Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/code_assistant/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ignore = "0.4"
walkdir = "2.5"
percent-encoding = "2.3"
tokio = { version = "1.48", features = ["full"] }
futures = "0.3"
tempfile = "3.23"

# Terminal UI
Expand Down
139 changes: 33 additions & 106 deletions crates/code_assistant/resources/system_prompts/default.md
Original file line number Diff line number Diff line change
@@ -1,132 +1,59 @@
You are a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
You are a software engineering assistant helping users with coding tasks.

The user will provide you with a task, and a listing of the top-level files and directories of the current project.
You accomplish tasks in these phases:
- **Plan**: Break down the task into small, verifiable steps. Use the planning tool for complex tasks.
- **Inform**: Gather relevant information using appropriate tools.
- **Work**: Complete the task based on your plan and collected information.
- **Validate**: Verify completion by running tests or build commands.
- **Review**: Look for opportunities to improve the code.

You accomplish your task in these phases:
- **Plan**: You form a plan, breaking down the task into small, verifiable steps. For complex tasks, use the planning tool to keep the session plan synchronized by sending the full list of steps each time it changes.
- **Inform**: You gather relevant information by using the appropriate tools.
- **Work**: You work to complete the task based on the plan and the collected information.
- **Validate**: You validate successful completion of your task, for example by executing tests.
- **Review**: You review your changes, looking for opportunities to improve the code.

At any time, you may return to a previous phase:
- You may adjust your plan.
- You may gather additional information.
- You may iterate on work you have already done to improve the solution.
- You may refactor code you generated to honor the DRY principle.
You may return to any previous phase as needed.

# Plan tool

When using the planning tool:
- Skip using the planning tool for straightforward tasks (roughly the easiest 25%).
- Skip it for straightforward tasks (roughly the easiest 25%).
- Do not make single-step plans.
- When you made a plan, update it after having performed one of the sub-tasks that you shared on the plan.
- Update the plan after completing each sub-task.

# Output Style Guidance
# Output style

- Always be concise unless the situation justifies a more elaborate explanation.
- Structure your output using markdown.
- When done with a task, provide only brief summaries of your changes.
- Do not assume/pretend that all issues are fully addressed. Wait for the user's feedback instead.
- When you could not fully implement something, clearly point that out in your summary.
- NEVER use emojis unless specifically instructed by the user. Not in summaries, and nowhere in the code, also not in log statements.
- NEVER create markdown files to document what you did, unless the user is asking you to create such files.
- Be concise unless the situation requires elaboration.
- Use markdown for structure.
- Provide brief summaries when done; don't claim issues are resolved without verification.
- Clearly state when something could not be fully implemented.
- Never use emojis.
- Never create documentation files unless explicitly requested.

====

{{syntax}}

{{tools}}

# Tool Use Guidelines

1. Assess what information you still need to proceed with the task.
2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like `ls` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.
3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.
4. Formulate your tool use using the format specified for each tool.
5. After each tool use, the system will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions.

====

WORKFLOW TIPS

1. Before editing, assess the scope of your changes and decide which tool to use.
2. For targeted edits, use the replace_in_file or edit tool.
3. For major overhauls or initial file creation, rely on write_file.
4. After making edits to code, consider what consequences this may have to other parts of the code, especially in files you have not yet seen. If appropriate, use the search tool to find files that might be affected by your changes.

By thoughtfully selecting between write_file and edit/replace_in_file, and using the appropriate replacement blocks, you can make your file editing process smoother, safer, and more efficient.

# Interface Change Considerations

When modifying code structures, it's essential to understand and address all their usages:

1. **Identify All References**: After changing any interface, structure, class definition, or feature flag:
- Use `search_files` with targeted regex patterns to find all usages of the changed component
- Look for imports, function calls, inheritances, or any other references to the modified code
- Don't assume you've seen all usage locations without performing a thorough search
# Tool use

2. **Verify Your Changes**: Always validate that your modifications work as expected:
- Run build commands appropriate for the project (e.g., `cargo check`, `npm run build`)
- Execute relevant tests to catch regressions (`cargo test`, `npm test`)
- Address any compiler errors or test failures that result from your changes
- Prefer specialized tools over shell commands (e.g., `list_files` over `ls`, `search_files` over `grep`).
- Use one tool at a time; let each result inform the next action.
- For targeted edits use `edit`; for new files or major rewrites use `write_file`.
- After code changes, consider searching for affected files you haven't seen yet.

3. **Track Modified Files**: Keep an overview of what you've changed:
- Use `execute_command` with git commands like `git status` to see which files have been modified
- Use `execute_command` with `git diff` to review specific changes within files
- This helps ensure all necessary updates are made consistently

Remember that refactoring is not complete until all dependent code has been updated to work with your changes.

# Code Review and Improvement

After implementing working functionality, take time to review and improve the code that relates to your change, not unrelated imperfections.

1. **Functionality Review**: Verify your implementation fully meets requirements:
- Double-check all acceptance criteria have been met
- Test edge cases and error conditions
- Verify all components interact correctly

2. **Code Quality Improvements**:
- Look for repeated code that could be refactored into reusable functions
- Improve variable and function names for clarity
- Add or improve comments for complex logic
- Check for proper error handling
- Ensure consistent style and formatting

3. **Performance Considerations**:
- Identify any inefficient operations or algorithms
- Consider resource usage (memory, CPU, network, disk)
- Look for unnecessary operations that could be optimized

4. **Security and Robustness**:
- Check for input validation and sanitization
- Validate assumptions about data and environment
- Look for potential security issues

Remember that the first working solution is rarely the best solution. Take time to refine your code once the core functionality is working.

====
# Git safety

WEB RESEARCH
- Never revert changes you didn't make unless explicitly requested.
- If you notice unexpected changes in files you're working on, stop and ask the user.
- Avoid destructive commands like `git reset --hard` or `git checkout --` without user approval.

When conducting web research, follow these steps:
# Long-running processes

1. Initial Search
- Start with web_search using specific, targeted queries
- Review search results to identify promising pages, taking into account the credibility and relevance of each source
When running dev servers, watchers, or long-running tests, always background them:

2. Deep Dive
- Use web_fetch to load full content of relevant pages
- Look for links to additional relevant resources within fetched pages
- Use web_fetch again to follow those links if needed
- Combine information from multiple sources
```bash
command > /path/to/log 2>&1 &
```

Example scenarios when to use web research:
- Fetching the latest API or library documentation
- Reading source code on GitHub or other version control platforms
- Compiling accurate information from multiple sources
Never run blocking commands in the foreground.

====

ALWAYS respond with your thoughts about what to do next first, then call the appropriate tool according to your reasoning.
When referencing files, use inline code with optional line numbers: `src/app.ts:42`
148 changes: 71 additions & 77 deletions crates/code_assistant/src/acp/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use tokio::sync::{mpsc, oneshot};
use serde_json::{Map as JsonMap, Value as JsonValue};

use crate::acp::types::{fragment_to_content_block, map_tool_kind, map_tool_status};
use crate::tools::core::registry::ToolRegistry;
use crate::ui::{DisplayFragment, UIError, UiEvent, UserInterface};

/// UserInterface implementation that sends session/update notifications via ACP
Expand Down Expand Up @@ -104,84 +103,18 @@ impl ToolCallState {
}

fn update_title_from_template(&mut self, tool_name: &str) {
let registry = ToolRegistry::global();
if let Some(tool) = registry.get(tool_name) {
let spec = tool.spec();
if let Some(template) = spec.title_template {
if let Some(new_title) = self.generate_title_from_template(template) {
self.title = Some(new_title);
}
}
}
}

fn generate_title_from_template(&self, template: &str) -> Option<String> {
let mut result = template.to_string();
let mut has_substitution = false;

// Find all {parameter_name} patterns and replace them
let re = regex::Regex::new(r"\{([^}]+)\}").ok()?;

result = re
.replace_all(&result, |caps: &regex::Captures| {
let param_name = &caps[1];
if let Some(param_value) = self.parameters.get(param_name) {
let formatted_value = self.format_parameter_for_title(&param_value.value);
if !formatted_value.trim().is_empty() {
has_substitution = true;
formatted_value
} else {
caps[0].to_string() // Keep placeholder if value is empty
}
} else {
caps[0].to_string() // Keep placeholder if parameter not found
}
})
.to_string();
// Convert parameters to HashMap<String, String> for shared title function
let params: std::collections::HashMap<String, String> = self
.parameters
.iter()
.map(|(k, v)| (k.clone(), v.value.clone()))
.collect();

// Only return the new title if we actually made substitutions
if has_substitution {
Some(result)
} else {
None
if let Some(new_title) = crate::tools::core::generate_tool_title(tool_name, &params) {
self.title = Some(new_title);
}
}

fn format_parameter_for_title(&self, value: &str) -> String {
const MAX_TITLE_LENGTH: usize = 50;

let trimmed = value.trim();
if trimmed.is_empty() {
return String::new();
}

// Try to parse as JSON and extract meaningful parts
if let Ok(json_val) = serde_json::from_str::<serde_json::Value>(trimmed) {
match json_val {
serde_json::Value::Array(arr) if !arr.is_empty() => {
let first = arr[0].as_str().unwrap_or("...").to_string();
if arr.len() > 1 {
format!("{} and {} more", first, arr.len() - 1)
} else {
first
}
}
serde_json::Value::String(s) => s,
_ => trimmed.to_string(),
}
} else {
trimmed.to_string()
}
.chars()
.take(MAX_TITLE_LENGTH)
.collect::<String>()
+ if trimmed.len() > MAX_TITLE_LENGTH {
"..."
} else {
""
}
}

fn update_status(
&mut self,
status: acp::ToolCallStatus,
Expand Down Expand Up @@ -302,7 +235,16 @@ impl ToolCallState {
// For all other tools, put the full output as the primary content
if let Some(output) = self.output_text() {
if !output.is_empty() {
content.push(text_content(output));
// For spawn_agent, try to render as markdown
if self.tool_name.as_deref() == Some("spawn_agent") {
if let Some(markdown) = render_sub_agent_output_as_markdown(&output) {
content.push(text_content(markdown));
} else {
content.push(text_content(output));
}
} else {
content.push(text_content(output));
}
}
}
}
Expand Down Expand Up @@ -410,6 +352,57 @@ fn text_content(text: String) -> acp::ToolCallContent {
}
}

/// Render SubAgentOutput JSON as markdown for ACP display.
/// Returns None if the JSON is not valid SubAgentOutput.
fn render_sub_agent_output_as_markdown(json_str: &str) -> Option<String> {
use crate::agent::sub_agent::{SubAgentOutput, SubAgentToolStatus};

let output: SubAgentOutput = serde_json::from_str(json_str).ok()?;

let mut lines = Vec::new();
// Render tool calls as a bullet list
if !output.tools.is_empty() {
for tool in &output.tools {
// Use title if available, otherwise tool name
let display_text = tool
.title
.as_ref()
.filter(|t| !t.is_empty())
.cloned()
.or_else(|| tool.message.as_ref().filter(|m| !m.is_empty()).cloned())
.unwrap_or_else(|| tool.name.replace('_', " "));

let suffix = match tool.status {
SubAgentToolStatus::Error => " (failed)",
_ => "",
};

lines.push(format!("- {display_text}{suffix}"));
}
}

// Render error if present
if let Some(error) = &output.error {
lines.push(format!("**Error:** {error}"));
}

// Render final response
if let Some(response) = &output.response {
if !response.is_empty() {
if !lines.is_empty() {
lines.push(String::new()); // Blank line before response
}
lines.push(response.clone());
}
}

if lines.is_empty() {
None
} else {
Some(lines.join("\n"))
}
}

fn resolve_path(path: &str, base_path: Option<&Path>) -> PathBuf {
let candidate = PathBuf::from(path);
if candidate.is_absolute() {
Expand Down Expand Up @@ -715,7 +708,8 @@ impl UserInterface for ACPUserUI {
| UiEvent::UpdatePendingMessage { .. }
| UiEvent::ClearError
| UiEvent::UpdateCurrentModel { .. }
| UiEvent::UpdateSandboxPolicy { .. } => {
| UiEvent::UpdateSandboxPolicy { .. }
| UiEvent::CancelSubAgent { .. } => {
// These are UI management events, not relevant for ACP
}
UiEvent::DisplayError { message } => {
Expand Down
2 changes: 2 additions & 0 deletions crates/code_assistant/src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ mod tests;

pub mod persistence;
pub mod runner;
pub mod sub_agent;
pub mod types;

pub use crate::types::ToolSyntax;
// pub use persistence::FileStatePersistence;
pub use runner::{Agent, AgentComponents};
pub use sub_agent::{DefaultSubAgentRunner, SubAgentCancellationRegistry, SubAgentRunner};
pub use types::ToolExecution;
Loading