Detailed changes
@@ -12,6 +12,7 @@ When this command is invoked:
- Begin the research process
2. **If no parameters provided**, respond with:
+
```
I'll help you create a detailed implementation plan. Let me start by understanding what we're building.
@@ -43,11 +44,8 @@ Then wait for the user's input.
2. **Spawn initial research tasks to gather context**:
Before asking the user any questions, use specialized agents to research in parallel:
-
- Use the **codebase-locator** agent to find all files related to the ticket/task
- Use the **codebase-analyzer** agent to understand how the current implementation works
- - If relevant, use the **thoughts-locator** agent to find any existing thoughts documents about this feature
- - If a Linear ticket is mentioned, use the **linear-ticket-reader** agent to get full details
These agents will:
- Find relevant source files, configs, and tests
@@ -67,6 +65,7 @@ Then wait for the user's input.
- Determine true scope based on codebase reality
5. **Present informed understanding and focused questions**:
+
```
Based on the ticket and my research of the codebase, I understand we need to [accurate summary].
@@ -118,9 +117,10 @@ After getting initial clarifications:
- Return specific file:line references
- Find tests and examples
-3. **Wait for ALL sub-tasks to complete** before proceeding
+4. **Wait for ALL sub-tasks to complete** before proceeding
+
+5. **Present findings and design options**:
-4. **Present findings and design options**:
```
Based on my research, here's what I found:
@@ -144,6 +144,7 @@ After getting initial clarifications:
Once aligned on approach:
1. **Create initial plan outline**:
+
```
Here's my proposed plan structure:
@@ -167,7 +168,7 @@ After structure approval:
1. **Write the plan** to `thoughts/shared/plans/{descriptive_name}.md`
2. **Use this template structure**:
-```markdown
+````markdown
# [Feature/Task Name] Implementation Plan
## Overview
@@ -183,6 +184,7 @@ After structure approval:
[A Specification of the desired end state after this plan is complete, and how to verify it]
### Key Discoveries:
+
- [Important finding with file:line reference]
- [Pattern to follow]
- [Constraint to work within]
@@ -198,21 +200,25 @@ After structure approval:
## Phase 1: [Descriptive Name]
### Overview
+
[What this phase accomplishes]
### Changes Required:
#### 1. [Component/File Group]
+
**File**: `path/to/file.ext`
**Changes**: [Summary of changes]
```[language]
// Specific code to add/modify
```
+````
### Success Criteria:
#### Automated Verification:
+
- [ ] Migration applies cleanly: `make migrate`
- [ ] Unit tests pass: `make test-component`
- [ ] Type checking passes: `npm run typecheck`
@@ -220,6 +226,7 @@ After structure approval:
- [ ] Integration tests pass: `make test-integration`
#### Manual Verification:
+
- [ ] Feature works as expected when tested via UI
- [ ] Performance is acceptable under load
- [ ] Edge case handling verified manually
@@ -236,13 +243,16 @@ After structure approval:
## Testing Strategy
### Unit Tests:
+
- [What to test]
- [Key edge cases]
### Integration Tests:
+
- [End-to-end scenarios]
### Manual Testing Steps:
+
1. [Specific step to verify feature]
2. [Another verification step]
3. [Edge case to test manually]
@@ -260,6 +270,7 @@ After structure approval:
- Original ticket: `thoughts/allison/tickets/eng_XXXX.md`
- Related research: `thoughts/shared/research/[relevant].md`
- Similar implementation: `[file:line]`
+
```
### Step 5: Sync and Review
@@ -269,80 +280,83 @@ After structure approval:
- This ensures the plan is properly indexed and available
2. **Present the draft plan location**:
- ```
- I've created the initial implementation plan at:
- `thoughts/shared/plans/[filename].md`
-
- Please review it and let me know:
- - Are the phases properly scoped?
- - Are the success criteria specific enough?
- - Any technical details that need adjustment?
- - Missing edge cases or considerations?
- ```
+```
+
+I've created the initial implementation plan at:
+`thoughts/shared/plans/[filename].md`
+
+Please review it and let me know:
+
+- Are the phases properly scoped?
+- Are the success criteria specific enough?
+- Any technical details that need adjustment?
+- Missing edge cases or considerations?
+
+````
3. **Iterate based on feedback** - be ready to:
- - Add missing phases
- - Adjust technical approach
- - Clarify success criteria (both automated and manual)
- - Add/remove scope items
- - After making changes, run `humanlayer thoughts sync` again
+- Add missing phases
+- Adjust technical approach
+- Clarify success criteria (both automated and manual)
+- Add/remove scope items
+- After making changes, run `humanlayer thoughts sync` again
4. **Continue refining** until the user is satisfied
## Important Guidelines
1. **Be Skeptical**:
- - Question vague requirements
- - Identify potential issues early
- - Ask "why" and "what about"
- - Don't assume - verify with code
+- Question vague requirements
+- Identify potential issues early
+- Ask "why" and "what about"
+- Don't assume - verify with code
2. **Be Interactive**:
- - Don't write the full plan in one shot
- - Get buy-in at each major step
- - Allow course corrections
- - Work collaboratively
+- Don't write the full plan in one shot
+- Get buy-in at each major step
+- Allow course corrections
+- Work collaboratively
3. **Be Thorough**:
- - Read all context files COMPLETELY before planning
- - Research actual code patterns using parallel sub-tasks
- - Include specific file paths and line numbers
- - Write measurable success criteria with clear automated vs manual distinction
- - automated steps should use `make` whenever possible - for example `make -C humanlayer-wui check` instead of `cd humanalyer-wui && bun run fmt`
+- Read all context files COMPLETELY before planning
+- Research actual code patterns using parallel sub-tasks
+- Include specific file paths and line numbers
+- Write measurable success criteria with clear automated vs manual distinction
+- automated steps should use `make` whenever possible - for example `make -C humanlayer-wui check` instead of `cd humanalyer-wui && bun run fmt`
4. **Be Practical**:
- - Focus on incremental, testable changes
- - Consider migration and rollback
- - Think about edge cases
- - Include "what we're NOT doing"
+- Focus on incremental, testable changes
+- Consider migration and rollback
+- Think about edge cases
+- Include "what we're NOT doing"
5. **Track Progress**:
- - Use TodoWrite to track planning tasks
- - Update todos as you complete research
- - Mark planning tasks complete when done
+- Use TodoWrite to track planning tasks
+- Update todos as you complete research
+- Mark planning tasks complete when done
6. **No Open Questions in Final Plan**:
- - If you encounter open questions during planning, STOP
- - Research or ask for clarification immediately
- - Do NOT write the plan with unresolved questions
- - The implementation plan must be complete and actionable
- - Every decision must be made before finalizing the plan
+- If you encounter open questions during planning, STOP
+- Research or ask for clarification immediately
+- Do NOT write the plan with unresolved questions
+- The implementation plan must be complete and actionable
+- Every decision must be made before finalizing the plan
## Success Criteria Guidelines
**Always separate success criteria into two categories:**
1. **Automated Verification** (can be run by execution agents):
- - Commands that can be run: `make test`, `npm run lint`, etc.
- - Specific files that should exist
- - Code compilation/type checking
- - Automated test suites
+- Commands that can be run: `make test`, `npm run lint`, etc.
+- Specific files that should exist
+- Code compilation/type checking
+- Automated test suites
2. **Manual Verification** (requires human testing):
- - UI/UX functionality
- - Performance under real conditions
- - Edge cases that are hard to automate
- - User acceptance criteria
+- UI/UX functionality
+- Performance under real conditions
+- Edge cases that are hard to automate
+- User acceptance criteria
**Format example:**
```markdown
@@ -359,11 +373,12 @@ After structure approval:
- [ ] Performance is acceptable with 1000+ items
- [ ] Error messages are user-friendly
- [ ] Feature works correctly on mobile devices
-```
+````
## Common Patterns
### For Database Changes:
+
- Start with schema/migration
- Add store methods
- Update business logic
@@ -371,6 +386,7 @@ After structure approval:
- Update clients
### For New Features:
+
- Research existing patterns first
- Start with data model
- Build backend logic
@@ -378,6 +394,7 @@ After structure approval:
- Implement UI last
### For Refactoring:
+
- Document current behavior
- Plan incremental changes
- Maintain backwards compatibility
@@ -394,20 +411,16 @@ When spawning research sub-tasks:
- Which directories to focus on
- What information to extract
- Expected output format
-4. **Be EXTREMELY specific about directories**:
- - If the ticket mentions "WUI", specify `humanlayer-wui/` directory
- - If it mentions "daemon", specify `hld/` directory
- - Never use generic terms like "UI" when you mean "WUI"
- - Include the full path context in your prompts
-5. **Specify read-only tools** to use
-6. **Request specific file:line references** in responses
-7. **Wait for all tasks to complete** before synthesizing
-8. **Verify sub-task results**:
+4. **Specify read-only tools** to use
+5. **Request specific file:line references** in responses
+6. **Wait for all tasks to complete** before synthesizing
+7. **Verify sub-task results**:
- If a sub-task returns unexpected results, spawn follow-up tasks
- Cross-check findings against the actual codebase
- Don't accept results that seem incorrect
Example of spawning multiple tasks:
+
```python
# Spawn these tasks concurrently:
tasks = [
@@ -1,15 +0,0 @@
-you're working on an experimental feature that didn't get the proper ticketing and pr stuff set up.
-
-assuming you just made a commit, here are the next steps:
-
-
-1. get the sha of the commit you just made (if you didn't make one, read `.claude/commands/commit.md` and make one)
-
-2. read `.claude/commands/linear.md` - think deeply about what you just implemented, then create a linear ticket about what you just did, and put it in 'in dev' state - it should have ### headers for "problem to solve" and "proposed solution"
-3. fetch the ticket to get the recommended git branch name
-4. git checkout main
-5. git checkout -b 'BRANCHNAME'
-6. git cherry-pick 'COMMITHASH'
-7. git push -u origin 'BRANCHNAME'
-8. gh pr create --fill
-9. read '.claude/commands/describe_pr.md' and follow the instructions
@@ -39,7 +39,7 @@ If you encounter a mismatch:
## Verification Approach
After implementing a phase:
-- Run the success criteria checks (usually `make check test` covers everything)
+- Run the success criteria checks (usually `cargo test -p [crate_name]` covers everything)
- Fix any issues before proceeding
- Update your progress in both the plan and your todos
- Check off completed items in the plan file itself using Edit
@@ -1,11 +1,31 @@
{
"permissions": {
+ "allow": [
+ "Read(/Users/mikaylamaki/projects/zed-work/zed-monorepo-real/**)",
+ "Read(/Users/nathan/src/agent-client-protocol/rust/**)",
+ "Read(/Users/nathan/src/agent-client-protocol/rust/**)",
+ "Read(/Users/nathan/src/agent-client-protocol/rust/**)",
+ "Read(/Users/nathan/src/agent-client-protocol/rust/**)",
+ "Bash(git add:*)",
+ "Read(/Users/nathan/src/agent-client-protocol/rust/**)",
+ "Bash(./script/spec_metadata.sh:*)",
+ "Bash(npm run generate:*)",
+ "Bash(npm run typecheck:*)",
+ "Bash(npm run:*)",
+ "Bash(npm install)",
+ "Bash(grep:*)",
+ "Bash(find:*)",
+ "Bash(node:*)",
+ "Bash(cargo check:*)",
+ "Bash(cargo test)",
+ "Bash(npx tsc:*)"
+ ],
"additionalDirectories": [
"/Users/mikaylamaki/projects/zed-work/zed-monorepo-real/claude-code-acp/",
- "/Users/mikaylamaki/projects/zed-work/zed-monorepo-real/agentic-coding-protocol/"
- ],
- "allow": [
- "Read(/Users/mikaylamaki/projects/zed-work/zed-monorepo-real/**)"
+ "/Users/mikaylamaki/projects/zed-work/zed-monorepo-real/agentic-coding-protocol/",
+ "/Users/nathan/src/agent",
+ "/Users/nathan/src/agent-client-protocol/",
+ "/Users/nathan/src/claude-code-acp"
]
}
}
@@ -191,9 +191,7 @@ dependencies = [
[[package]]
name = "agent-client-protocol"
-version = "0.0.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "289eb34ee17213dadcca47eedadd386a5e7678094095414e475965d1bcca2860"
+version = "0.2.0-alpha.0"
dependencies = [
"anyhow",
"async-broadcast",
@@ -426,7 +426,7 @@ zlog_settings = { path = "crates/zlog_settings" }
# External crates
#
-agent-client-protocol = "0.0.31"
+agent-client-protocol = { path = "../agent-client-protocol" }
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", branch = "add-hush-login-flag" }
any_vec = "0.14"
@@ -868,6 +868,11 @@ impl AcpThread {
&self.connection
}
+ /// Returns true if the agent supports custom slash commands.
+ pub fn supports_custom_commands(&self) -> bool {
+ self.prompt_capabilities.supports_custom_commands
+ }
+
pub fn action_log(&self) -> &Entity<ActionLog> {
&self.action_log
}
@@ -76,6 +76,9 @@ pub trait AgentConnection {
None
}
+ fn list_commands(&self, session_id: &acp::SessionId, cx: &mut App) -> Task<Result<acp::ListCommandsResponse>>;
+ fn run_command(&self, request: acp::RunCommandRequest, cx: &mut App) -> Task<Result<()>>;
+
fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
}
@@ -440,6 +443,14 @@ mod test_support {
Some(Rc::new(StubAgentSessionEditor))
}
+ fn list_commands(&self, _session_id: &acp::SessionId, _cx: &mut App) -> Task<Result<acp::ListCommandsResponse>> {
+ Task::ready(Ok(acp::ListCommandsResponse { commands: vec![] }))
+ }
+
+ fn run_command(&self, _request: acp::RunCommandRequest, _cx: &mut App) -> Task<Result<()>> {
+ Task::ready(Ok(()))
+ }
+
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
@@ -1027,6 +1027,19 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
Some(Rc::new(self.clone()) as Rc<dyn acp_thread::AgentTelemetry>)
}
+ fn list_commands(&self, session_id: &acp::SessionId, _cx: &mut App) -> Task<Result<acp::ListCommandsResponse>> {
+ // Native agent doesn't support custom commands yet
+ let _session_id = session_id.clone();
+ Task::ready(Ok(acp::ListCommandsResponse {
+ commands: vec![],
+ }))
+ }
+
+ fn run_command(&self, _request: acp::RunCommandRequest, _cx: &mut App) -> Task<Result<()>> {
+ // Native agent doesn't support custom commands yet
+ Task::ready(Err(anyhow!("Custom commands not supported")))
+ }
+
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
@@ -588,6 +588,7 @@ impl Thread {
image,
audio: false,
embedded_context: true,
+ supports_custom_commands: false,
}
}
@@ -136,6 +136,7 @@ impl AcpConnection {
read_text_file: true,
write_text_file: true,
},
+ terminal: true,
},
})
.await?;
@@ -328,6 +329,23 @@ impl AgentConnection for AcpConnection {
.detach();
}
+ fn list_commands(&self, session_id: &acp::SessionId, cx: &mut App) -> Task<Result<acp::ListCommandsResponse>> {
+ let conn = self.connection.clone();
+ let session_id = session_id.clone();
+ cx.foreground_executor().spawn(async move {
+ conn.list_commands(acp::ListCommandsRequest { session_id }).await
+ .map_err(Into::into)
+ })
+ }
+
+ fn run_command(&self, request: acp::RunCommandRequest, cx: &mut App) -> Task<Result<()>> {
+ let conn = self.connection.clone();
+ cx.foreground_executor().spawn(async move {
+ conn.run_command(request).await
+ .map_err(Into::into)
+ })
+ }
+
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
@@ -244,6 +244,7 @@ impl AgentConnection for ClaudeAgentConnection {
image: true,
audio: false,
embedded_context: true,
+ supports_custom_commands: false,
}),
cx,
)
@@ -339,6 +340,19 @@ impl AgentConnection for ClaudeAgentConnection {
.log_err();
}
+ fn list_commands(&self, session_id: &acp::SessionId, _cx: &mut App) -> Task<Result<acp::ListCommandsResponse>> {
+ // Claude agent doesn't support custom commands yet
+ let _session_id = session_id.clone();
+ Task::ready(Ok(acp::ListCommandsResponse {
+ commands: vec![],
+ }))
+ }
+
+ fn run_command(&self, _request: acp::RunCommandRequest, _cx: &mut App) -> Task<Result<()>> {
+ // Claude agent doesn't support custom commands yet
+ Task::ready(Err(anyhow!("Custom commands not supported")))
+ }
+
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
@@ -928,6 +928,7 @@ impl MentionCompletion {
}
}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -65,6 +65,7 @@ pub struct MessageEditor {
prompt_store: Option<Entity<PromptStore>>,
prevent_slash_commands: bool,
prompt_capabilities: Rc<Cell<acp::PromptCapabilities>>,
+ completion_provider: Rc<ContextPickerCompletionProvider>,
_subscriptions: Vec<Subscription>,
_parse_slash_command_task: Task<()>,
}
@@ -99,13 +100,15 @@ impl MessageEditor {
},
None,
);
- let completion_provider = ContextPickerCompletionProvider::new(
+ let context_completion_provider = ContextPickerCompletionProvider::new(
cx.weak_entity(),
workspace.clone(),
history_store.clone(),
prompt_store.clone(),
prompt_capabilities.clone(),
);
+
+ let completion_provider = Rc::new(context_completion_provider);
let semantics_provider = Rc::new(SlashCommandSemanticsProvider {
range: Cell::new(None),
});
@@ -119,7 +122,7 @@ impl MessageEditor {
editor.set_show_indent_guides(false, cx);
editor.set_soft_wrap();
editor.set_use_modal_editing(true);
- editor.set_completion_provider(Some(Rc::new(completion_provider)));
+ editor.set_completion_provider(Some(completion_provider.clone()));
editor.set_context_menu_options(ContextMenuOptions {
min_entries_visible: 12,
max_entries_visible: 12,
@@ -170,11 +173,17 @@ impl MessageEditor {
prompt_store,
prevent_slash_commands,
prompt_capabilities,
+ completion_provider,
_subscriptions: subscriptions,
_parse_slash_command_task: Task::ready(()),
}
}
+ pub fn set_thread(&mut self, thread: WeakEntity<acp_thread::AcpThread>, _cx: &mut Context<Self>) {
+ // Update the completion provider with the thread reference
+ self.completion_provider.set_thread(thread);
+ }
+
pub fn insert_thread_summary(
&mut self,
thread: agent2::DbThreadMetadata,
@@ -551,10 +551,16 @@ impl AcpThreadView {
None
};
this.thread_state = ThreadState::Ready {
- thread,
+ thread: thread.clone(),
title_editor,
_subscriptions: subscriptions,
};
+
+ // Update the message editor with the thread reference for slash command completion
+ this.message_editor.update(cx, |editor, cx| {
+ editor.set_thread(thread.downgrade(), cx);
+ });
+
this.message_editor.focus_handle(cx).focus(window);
this.profile_selector = this.as_native_thread(cx).map(|thread| {
@@ -100,8 +100,8 @@ agent-client-protocol = "0.2.0-alpha.1" # or appropriate version
### **Cross-Repository Verification**
Before opening PRs:
-- [ ] ACP protocol extension compiles and tests pass
-- [ ] Zed compiles against local ACP changes
+- [x] ACP protocol extension compiles and tests pass
+- [x] Zed compiles against local ACP changes
- [ ] End-to-end slash command flow works locally
- [ ] Claude ACP adapter works with generated types
@@ -330,26 +330,26 @@ pub fn supports_custom_commands(&self, cx: &App) -> bool {
### Success Criteria:
#### Automated Verification:
-- [ ] Protocol crate compiles: `cd /Users/nathan/src/agent-client-protocol && cargo check`
-- [ ] Protocol tests pass: `cd /Users/nathan/src/agent-client-protocol && cargo test`
+- [x] Protocol crate compiles: `cd /Users/nathan/src/agent-client-protocol && cargo check`
+- [x] Protocol tests pass: `cd /Users/nathan/src/agent-client-protocol && cargo test`
- [ ] Schema generation works: `cd /Users/nathan/src/agent-client-protocol && cargo run --bin generate`
- [ ] Schema includes new methods: `grep -A5 -B5 "session/list_commands\|session/run_command" /Users/nathan/src/agent-client-protocol/schema/schema.json`
-- [ ] Zed compiles successfully: `./script/clippy`
-- [ ] No linting errors: `cargo clippy --package agent_servers --package acp_thread`
-- [ ] ACP thread capability method compiles: `cargo check --package acp_thread`
+- [x] Zed compiles successfully: `./script/clippy`
+- [x] No linting errors: `cargo clippy --package agent_servers --package acp_thread`
+- [x] ACP thread capability method compiles: `cargo check --package acp_thread`
#### Manual Verification:
-- [ ] New trait methods are properly defined in Agent trait (`/Users/nathan/src/agent-client-protocol/rust/agent.rs`)
+- [x] New trait methods are properly defined in Agent trait (`/Users/nathan/src/agent-client-protocol/rust/agent.rs`)
- Verify `list_commands()` method signature at line ~115
- Verify `run_command()` method signature at line ~127
-- [ ] Request/response enums updated in ClientRequest (`agent.rs:~423`) and AgentResponse enums
-- [ ] Method constants added (`SESSION_LIST_COMMANDS`, `SESSION_RUN_COMMAND`) after line 415
-- [ ] PromptCapabilities extended with `supports_custom_commands: bool` field
-- [ ] ClientSideConnection methods implemented with proper error handling
+- [x] Request/response enums updated in ClientRequest (`agent.rs:~423`) and AgentResponse enums
+- [x] Method constants added (`SESSION_LIST_COMMANDS`, `SESSION_RUN_COMMAND`) after line 415
+- [x] PromptCapabilities extended with `supports_custom_commands: bool` field
+- [x] ClientSideConnection methods implemented with proper error handling
- [ ] Message dispatch logic updated in `AgentSide::decode_request()`
-- [ ] AgentConnection trait extends with new methods (`crates/acp_thread/src/connection.rs`)
-- [ ] AcpConnection implements trait methods (`crates/agent_servers/src/acp.rs`)
-- [ ] AcpThread has `supports_custom_commands()` helper method
+- [x] AgentConnection trait extends with new methods (`crates/acp_thread/src/connection.rs`)
+- [x] AcpConnection implements trait methods (`crates/agent_servers/src/acp.rs`)
+- [x] AcpThread has `supports_custom_commands()` helper method
---
@@ -634,24 +634,27 @@ pub fn run_command(
### Success Criteria:
#### Automated Verification:
-- [ ] Code compiles successfully: `./script/clippy`
-- [ ] No linting errors: `cargo clippy --package agent_ui --package acp_thread`
-- [ ] Type checking passes: `cargo check --package agent_ui --package acp_thread`
-- [ ] Completion provider compiles: `cargo check --package agent_ui --lib`
-- [ ] Slash command parsing works: Test `SlashCommandCompletion::try_parse()` with various inputs
-
-#### Manual Verification:
-- [ ] SlashCommandCompletion struct added to `crates/agent_ui/src/acp/completion_provider.rs`
- - Verify `try_parse()` method implementation
- - Test parsing of "/", "/create", "/research_codebase" patterns
-- [ ] ContextPickerCompletionProvider updated:
- - `is_completion_trigger()` method extended (around line 763)
- - `completions()` method handles slash commands (around line 700)
- - `complete_slash_commands()` method added (around line 850)
-- [ ] SlashCommandConfirmation struct implements CompletionConfirm trait
- - Handles immediate execution for commands without arguments
- - Allows continued typing for commands requiring arguments
-- [ ] AcpThread has `run_command()` method for command execution
+- [x] Code compiles successfully: `./script/clippy`
+- [x] No linting errors: `cargo clippy --package agent_ui --package acp_thread`
+- [x] Type checking passes: `cargo check --package agent_ui --package acp_thread`
+- [x] Completion provider compiles: `cargo check --package agent_ui --lib`
+- [x] Slash command parsing works: Test `SlashCommandCompletion::try_parse()` with various inputs
+
+#### Manual Verification (REVISED - Simpler Approach):
+- [ ] **Refactored to Simpler Architecture**:
+ - [ ] Remove complex `CompositeCompletionProvider` and `AgentSlashCommandCompletionProvider`
+ - [ ] Extend existing `ContextPickerCompletionProvider` with optional thread field
+ - [ ] Add `set_thread()` method for lifecycle management
+ - [ ] Add slash command detection to `is_completion_trigger()`
+ - [ ] Add slash command completion to `completions()` method
+- [ ] **Slash Command Integration**:
+ - [ ] Parse slash commands using existing `SlashCommandLine` from assistant_slash_command
+ - [ ] Fetch commands via ACP `list_commands()` RPC when thread supports it
+ - [ ] Execute commands via ACP `run_command()` RPC with proper confirmation
+ - [ ] Only show slash completions when `supports_custom_commands = true`
+- [ ] **MessageEditor Integration**:
+ - [ ] Add `set_thread()` method to update completion provider when thread is ready
+ - [ ] Call `set_thread()` in ThreadView when thread transitions to Ready state
- [ ] Integration Testing:
- [ ] Typing "/" in agent panel triggers completion when `supports_custom_commands = true`
- [ ] No "/" completion appears when `supports_custom_commands = false`
@@ -1,14 +1,15 @@
---
date: 2025-08-28 15:34:28 PDT
researcher: Mikayla Maki
-git_commit: 565782a1c769c90e58e012a80ea1c2d0cfcdb837
+git_commit: 425291f0aed2abe148e1a8ea4eda74569e25c2b7
branch: claude-experiments
repository: zed
topic: "Custom Slash Commands for Agent Client Protocol"
tags: [research, codebase, acp, slash-commands, claude-code, protocol-extension]
status: complete
last_updated: 2025-08-28
-last_updated_by: Mikayla Maki
+last_updated_by: Nathan
+last_updated_note: "Added detailed findings from agent-client-protocol and claude-code-acp repositories"
---
# Research: Custom Slash Commands for Agent Client Protocol
@@ -86,28 +87,40 @@ The agent panel is **completely separate** from the assistant/text thread system
### Agent Client Protocol RPC Patterns
-**Core Structure** (`agentic-coding-protocol/`):
-
-- JSON-RPC based bidirectional communication
-- Type-safe request/response enums with static dispatch
-- Capability negotiation for feature opt-in
-- Auto-generated JSON Schema from Rust types
-
-**RPC Method Pattern**:
-
-1. Define request/response structs with `#[derive(Serialize, Deserialize, JsonSchema)]`
-2. Add method name constant: `const NEW_METHOD_NAME: &str = "new/method"`
-3. Add variants to `ClientRequest`/`AgentRequest` enums
-4. Update trait definition with async method signature
-5. Add to dispatch logic in `decode_request()` and `handle_request()`
-
-**Existing Methods**:
-
-- `initialize` - Capability negotiation and authentication
-- `session/new`, `session/load` - Session management
-- `session/prompt` - Message processing
-- `fs/read_text_file`, `fs/write_text_file` - File operations
-- `session/request_permission` - Permission requests
+**Core Structure** (`agent-client-protocol/rust/`):
+
+- JSON-RPC based bidirectional communication via symmetric `Agent`/`Client` traits
+- Type-safe request/response enums with `#[serde(untagged)]` routing
+- Capability negotiation via `AgentCapabilities` and `ClientCapabilities`
+- Auto-generated JSON Schema from Rust types via `JsonSchema` derives
+
+**Agent Trait Methods** (`agent.rs:18-108`):
+- `initialize()` - Connection establishment and capability negotiation
+- `authenticate()` - Authentication using advertised methods
+- `new_session()` - Creates conversation contexts
+- `load_session()` - Loads existing sessions (capability-gated)
+- `prompt()` - Processes user prompts with full lifecycle
+- `cancel()` - Cancels ongoing operations
+
+**Client Trait Methods** (`client.rs:19-114`):
+- `request_permission()` - Requests user permission for tool calls
+- `write_text_file()` / `read_text_file()` - File operations (capability-gated)
+- `session_notification()` - Handles session updates from agent
+
+**RPC Infrastructure Pattern**:
+
+1. **Method Constants** - Define at lines `agent.rs:395-415` / `client.rs:451-485`
+2. **Request/Response Structs** - With `#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]`
+3. **Schema Annotations** - `#[schemars(extend("x-side" = "agent", "x-method" = "method_name"))]`
+4. **Untagged Enums** - Add to `ClientRequest`/`AgentResponse` enums for message routing
+5. **Trait Methods** - Add to `Agent`/`Client` traits with `impl Future` signatures
+6. **Connection Methods** - Implement in `ClientSideConnection`/`AgentSideConnection`
+7. **Message Handling** - Update `MessageHandler` implementations for dispatch
+
+**Protocol Versioning** (`version.rs:4-20`):
+- Current: V1 with backward compatibility
+- Breaking changes require version bump
+- Non-breaking additions use capability flags
### .claude/commands Directory Structure
@@ -128,66 +141,78 @@ The agent panel is **completely separate** from the assistant/text thread system
[Instructions]
-## Important Guidelines
-
-[Constraints and behaviors]
-```
-
-**Metadata Extraction Points**:
-
-- H1 title for command name and description
-- "Initial Response" section for invocation behavior
-- Sequential process steps under "Process Steps"
-- Checkbox lists (`- [ ]`, `- [x]`) for progress tracking
-- Code blocks with executable commands
-
-**Command Categories**:
-
-- Development workflow: `create_plan.md`, `implement_plan.md`, `validate_plan.md`, `commit.md`
-- Research: `research_codebase.md`, `debug.md`
-- Project management: `ralph_plan.md`, `founder_mode.md`
-
### Claude Code ACP Adapter Implementation
**Architecture** (`claude-code-acp/src/`):
-- `acp-agent.ts` - Main `ClaudeAcpAgent` implementing ACP Agent interface
-- `mcp-server.ts` - Internal MCP server for file operations and permissions
-- `tools.ts` - Tool conversion between Claude and ACP formats
-- Session management with unique IDs and Claude SDK `Query` objects
-
-**Integration Pattern**:
-
-```typescript
-let q = query({
- prompt: input,
- options: {
- cwd: params.cwd,
- mcpServers: { acp: mcpServerConfig },
- allowedTools: ["mcp__acp__read"],
- disallowedTools: ["Read", "Write", "Edit", "MultiEdit"],
- },
-});
-```
-
-**Tool Execution Flow**:
-
-1. ACP client makes tool request
-2. Claude ACP agent converts to Claude SDK format
-3. Internal MCP server proxies to ACP client capabilities
-4. Results converted back to ACP format
+- `acp-agent.ts:51` - `ClaudeAcpAgent` class implementing complete ACP `Agent` interface
+- `mcp-server.ts:9` - Internal MCP proxy server for file operations and permissions
+- `tools.ts:22` - Tool format conversion between Claude SDK and ACP representations
+- Session management with UUID tracking and Claude SDK `Query` objects
+
+**Agent Interface Implementation** (`acp-agent.ts:51-218`):
+- `initialize()` at line 63: Declares capabilities (image, embedded_context) and auth methods
+- `newSession()` at line 84: Creates UUID sessions with MCP server integration
+- `prompt()` at line 140: Main query execution using Claude SDK with real-time streaming
+- `cancel()` at line 211: Properly handles session cancellation and cleanup
+
+**Session Lifecycle** (`acp-agent.ts:84-134`):
+1. Generate UUID session ID and create pushable input stream
+2. Configure MCP servers from ACP request parameters
+3. Start internal HTTP-based MCP proxy server on dynamic port
+4. Initialize Claude SDK query with working directory, MCP servers, tool permissions
+5. Enable `mcp__acp__read` while disabling direct file tools for security
+
+**Query Execution Flow** (`acp-agent.ts:140-209`):
+1. Convert ACP prompt to Claude format via `promptToClaude()` at line 237
+2. Push user message to Claude SDK input stream
+3. Iterate through Claude SDK responses with real-time streaming
+4. Handle system, result, user, and assistant message types
+5. Convert Claude messages to ACP format via `toAcpNotifications()` at line 312
+6. Stream session updates back to ACP client
+
+**MCP Proxy Architecture** (`mcp-server.ts:9-449`):
+- **Internal HTTP Server**: Creates MCP server for Claude SDK integration
+- **Tool Implementations**:
+ - `read` (lines 19-94): Proxies to ACP client's `readTextFile()`
+ - `write` (lines 96-149): Proxies to ACP client's `writeTextFile()`
+ - `edit` (lines 152-239): Text replacement with line tracking
+ - `multi-edit` (lines 241-318): Sequential edit operations
+- **Permission Integration**: Routes tool permission requests through ACP client
+
+**Current Command Support**:
+- **No existing slash command infrastructure** - all interactions use standard prompt interface
+- **No `.claude/commands` directory integration** currently implemented
+- **Command detection would require preprocessing** before Claude SDK integration
## Code References
+### Zed Integration Layer
- `crates/agent_ui/src/agent_panel.rs:24` - Main AgentPanel component
- `crates/agent_ui/src/acp/thread_view.rs:315` - AcpThreadView UI component
- `crates/agent_ui/src/acp/message_editor.rs` - Agent panel message input
- `crates/agent_servers/src/acp.rs:63-162` - ACP connection establishment
- `crates/acp_thread/src/acp_thread.rs:826` - ACP thread creation
-- `agentic-coding-protocol/rust/agent.rs:604-610` - ACP request enum pattern
-- `agentic-coding-protocol/rust/acp.rs:355-371` - Method dispatch logic
-- `claude-code-acp/src/acp-agent.ts:1-500` - ACP adapter implementation
-- `.claude/commands/*.md` - Command definition files
+
+### Agent Client Protocol
+- `agent-client-protocol/rust/agent.rs:18-108` - Agent trait with 6 core methods
+- `agent-client-protocol/rust/client.rs:19-114` - Client trait for bidirectional communication
+- `agent-client-protocol/rust/acp.rs:120` - ClientSideConnection implementation
+- `agent-client-protocol/rust/acp.rs:341` - AgentSideConnection implementation
+- `agent-client-protocol/rust/rpc.rs:30-367` - RPC connection infrastructure
+- `agent-client-protocol/rust/agent.rs:333-371` - AgentCapabilities and PromptCapabilities
+- `agent-client-protocol/rust/agent.rs:423-432` - ClientRequest/AgentResponse enum routing
+- `agent-client-protocol/rust/generate.rs:24-77` - JSON schema generation
+
+### Claude Code ACP Adapter
+- `claude-code-acp/src/acp-agent.ts:51-218` - ClaudeAcpAgent implementing Agent interface
+- `claude-code-acp/src/mcp-server.ts:9-449` - Internal MCP proxy server
+- `claude-code-acp/src/tools.ts:22-395` - Tool format conversion
+- `claude-code-acp/src/utils.ts:7-75` - Stream processing utilities
+
+### Command Infrastructure
+- `.claude/commands/*.md` - Command definition files (markdown format)
+- No existing slash command infrastructure in claude-code-acp currently
## Architecture Insights
@@ -203,33 +228,73 @@ let q = query({
### 1. Protocol Extension for Custom Commands
-Add new RPC methods to ACP schema following existing patterns in `agentic-coding-protocol/rust/`:
+Add new RPC methods following exact ACP patterns in `agent-client-protocol/rust/`:
+**Method Constants** (`agent.rs:395-415`):
```rust
-// New request types
-pub struct ListCommandsRequest {
- pub session_id: SessionId,
-}
+pub const SESSION_LIST_COMMANDS: &str = "session/list_commands";
+pub const SESSION_RUN_COMMAND: &str = "session/run_command";
+```
-pub struct RunCommandRequest {
+**Request/Response Types** (after `agent.rs:371`):
+```rust
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
+#[schemars(extend("x-side" = "agent", "x-method" = "session/list_commands"))]
+#[serde(rename_all = "camelCase")]
+pub struct ListCommandsRequest {
pub session_id: SessionId,
- pub command: String,
- pub args: Option<String>,
}
-// Response types
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
+#[schemars(extend("x-side" = "agent", "x-method" = "session/list_commands"))]
+#[serde(rename_all = "camelCase")]
pub struct ListCommandsResponse {
pub commands: Vec<CommandInfo>,
}
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "camelCase")]
pub struct CommandInfo {
pub name: String,
pub description: String,
pub requires_argument: bool,
}
+
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
+#[schemars(extend("x-side" = "agent", "x-method" = "session/run_command"))]
+#[serde(rename_all = "camelCase")]
+pub struct RunCommandRequest {
+ pub session_id: SessionId,
+ pub command: String,
+ pub args: Option<String>,
+}
+```
+
+**Trait Extension** (add to `Agent` trait after `cancel()` at line 107):
+```rust
+fn list_commands(
+ &self,
+ arguments: ListCommandsRequest,
+) -> impl Future<Output = Result<ListCommandsResponse, Error>>;
+
+fn run_command(
+ &self,
+ arguments: RunCommandRequest,
+) -> impl Future<Output = Result<(), Error>>;
```
-Add to request/response enums and implement in dispatch logic similar to existing ACP methods.
+**Enum Routing** (add to `ClientRequest` at line 423 and `AgentResponse`):
+```rust
+ListCommandsRequest(ListCommandsRequest),
+RunCommandRequest(RunCommandRequest),
+```
+
+**Capability Extension** (add to `PromptCapabilities` at line 358):
+```rust
+/// Agent supports custom slash commands via `list_commands` and `run_command`.
+#[serde(default)]
+pub supports_custom_commands: bool,
+```
### 2. Agent Panel UI Integration
@@ -245,19 +310,88 @@ Add to request/response enums and implement in dispatch logic similar to existin
### 3. ACP Agent Implementation
-In Claude Code ACP adapter (`claude-code-acp/src/acp-agent.ts`):
+In Claude Code ACP adapter, extend `ClaudeAcpAgent` class at `claude-code-acp/src/acp-agent.ts:51`:
+
+**Add Command Parser** (new module at `src/command-parser.ts`):
+```typescript
+export interface CommandInfo {
+ name: string;
+ description: string;
+ requires_argument: boolean;
+ content?: string;
+}
+
+export class CommandParser {
+ private commandsDir: string;
+ private cachedCommands?: CommandInfo[];
+
+ constructor(cwd: string) {
+ this.commandsDir = path.join(cwd, '.claude', 'commands');
+ }
+ async listCommands(): Promise<CommandInfo[]> {
+ // Parse *.md files, extract H1 titles and descriptions
+ }
+
+ async getCommand(name: string): Promise<CommandInfo | null> {
+ // Return specific command with full content for execution
+ }
+}
+```
+
+**Extend ClaudeAcpAgent** (add after line 218):
```typescript
+private commandParser?: CommandParser;
+
+// In constructor around line 60:
+if (options.cwd && fs.existsSync(path.join(options.cwd, '.claude', 'commands'))) {
+ this.commandParser = new CommandParser(options.cwd);
+}
+
+// Update initialize() around line 68 to advertise capability:
+agent_capabilities: {
+ prompt_capabilities: {
+ image: true,
+ audio: false,
+ embedded_context: true,
+ supports_custom_commands: !!this.commandParser,
+ },
+}
+
async listCommands(request: ListCommandsRequest): Promise<ListCommandsResponse> {
- // Read .claude/commands directory
- // Parse markdown files for metadata
- // Return CommandInfo array
+ if (!this.commandParser) return { commands: [] };
+
+ const commands = await this.commandParser.listCommands();
+ return {
+ commands: commands.map(cmd => ({
+ name: cmd.name,
+ description: cmd.description,
+ requires_argument: cmd.requires_argument,
+ }))
+ };
}
-async runCommand(request: RunCommandRequest): Promise<RunCommandResponse> {
- // Find command definition in .claude/commands/
- // Execute via Claude SDK query with command content
- // Stream results back via session notifications using existing session update mechanism
+async runCommand(request: RunCommandRequest): Promise<void> {
+ if (!this.commandParser) throw new Error('Commands not supported');
+
+ const command = await this.commandParser.getCommand(request.command);
+ if (!command) throw new Error(`Command not found: ${request.command}`);
+
+ // Execute command via existing session mechanism
+ const session = this.sessions.get(request.session_id);
+ if (!session) throw new Error('Session not found');
+
+ // Create system prompt from command content
+ let prompt = command.content;
+ if (command.requires_argument && request.args) {
+ prompt += `\n\nArguments: ${request.args}`;
+ }
+
+ // Inject as system message and process via existing prompt flow
+ session.input.push({ role: 'user', content: prompt });
+
+ // Stream results back via existing session update mechanism
+ // (handled automatically by query execution loop at line 150)
}
```
@@ -292,7 +426,28 @@ Execute commands by sending command content as system prompt to Claude SDK, simi
7. **UI Integration**: How should command execution progress and results be displayed within the `AcpThreadView` component?
-###### FINAL CLAUDE PROMPT:
+## Follow-up Research 2025-08-28 20:29:47 MDT
+
+After gaining access to the actual `agent-client-protocol` and `claude-code-acp` repositories, I updated this research document with concrete implementation details:
+
+### Key New Findings
+
+**Agent Client Protocol Structure**: The protocol uses a symmetric `Agent`/`Client` trait design with `#[serde(untagged)]` enum routing, JSON schema generation, and explicit capability negotiation. Adding new RPC methods requires specific patterns for method constants, request/response structs, trait extensions, and enum routing.
+
+**Claude Code ACP Adapter**: Implements the full Agent interface using Claude SDK integration via MCP proxy servers. Currently has no slash command infrastructure - all interactions use the standard prompt interface. Command detection would require preprocessing before Claude SDK integration.
+
+**Implementation Requirements**: More complex than initially understood - requires protocol extension, trait implementations, enum routing updates, capability advertisement, and coordination between three repositories (zed, agent-client-protocol, claude-code-acp).
+
+### Updated Implementation Approach
+
+1. **Protocol Extension**: Add `session/list_commands` and `session/run_command` methods following exact ACP patterns
+2. **Capability System**: Extend `PromptCapabilities` with `supports_custom_commands` flag
+3. **Agent Implementation**: Create command parser module in claude-code-acp to read `.claude/commands/*.md` files
+4. **UI Integration**: Use existing completion provider patterns in Zed's agent panel message editor
+
+The research now includes specific file:line references and concrete code examples for all three layers of the implementation.
+
+###### ORIGINAL CLAUDE PROMPT (2025-08-28 15:34:28 PDT):
⏺ I've completed comprehensive research on adding custom
slash commands to the Agent Client Protocol for the