mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-16 21:14:50 +08:00
Make ACP/Zed status obvious before users go source-diving
ROADMAP #21, #22, and #23 were already closed on current main, so the next real repo-local backlog item was the ACP/Zed discoverability gap. This adds a local `claw acp` status surface plus aliases, updates help/docs, and separates the shipped discoverability fix from the still-open daemon/protocol follow-up so editor-first users get a crisp answer immediately. Constraint: No ACP/Zed daemon or protocol server exists in claw-code yet, so the new surface must be explicit status guidance rather than a fake implementation Rejected: Add a pretend `acp serve` daemon path | would imply supported protocol behavior that does not exist Rejected: Docs-only clarification | still leaves `claw --help` unable to answer the editor-launch question directly Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep ROADMAP discoverability fixes separate from future ACP daemon/protocol work so help text and backlog IDs stay unambiguous Tested: cargo fmt --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; cargo run -q -p rusty-claude-cli -- acp; cargo run -q -p rusty-claude-cli -- --output-format json acp; architect review APPROVED Not-tested: Real ACP/Zed daemon launch because no protocol-serving surface exists yet
This commit is contained in:
@@ -33,6 +33,8 @@ The canonical implementation lives in [`rust/`](./rust), and the current source
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Start with [`USAGE.md`](./USAGE.md) for build, auth, CLI, session, and parity-harness workflows. Make `claw doctor` your first health check after building, use [`rust/README.md`](./rust/README.md) for crate-level details, read [`PARITY.md`](./PARITY.md) for the current Rust-port checkpoint, and see [`docs/container.md`](./docs/container.md) for the container-first workflow.
|
||||
>
|
||||
> **ACP / Zed status:** `claw-code` does not ship an ACP/Zed daemon entrypoint yet. Run `claw acp` (or `claw --acp`) for the current status instead of guessing from source layout; `claw acp serve` is currently a discoverability alias only, and real ACP support remains tracked separately in `ROADMAP.md`.
|
||||
|
||||
## Current repository shape
|
||||
|
||||
|
||||
@@ -1145,9 +1145,10 @@ Model name prefix now wins unconditionally over env-var presence. Regression tes
|
||||
|
||||
63. **Droid session completion semantics broken: code arrives after "status: completed"** — dogfooded 2026-04-12. Ultraclaw droid sessions (use-droid via acpx) report `session.status: completed` before file writes are fully flushed/synced to the working tree. Discovered +410 lines of "late-arriving" droid output that appeared after I had already assessed 8 sessions as "no code produced." This creates false-negative assessments and duplicate work. **Fix shape:** (a) droid agent should only report completion after explicit file-write confirmation (fsync or existence check); (b) or, claw-code should expose a `pending_writes` status that indicates "agent responded, disk flush pending"; (c) lane orchestrators should poll for file changes for N seconds after completion before final assessment. **Blocker:** none. Source: Jobdori ultraclaw dogfood 2026-04-12.
|
||||
|
||||
64. **ACP/Zed editor integration entrypoint is too implicit** — dogfooded 2026-04-13 from a user request for a `-acp` parameter to support ACP protocol integration in editor-first workflows such as Zed. The gap is not generic "please add another integration" churn; it is a **discoverability and launch-contract problem**. Right now the product surface does not make it obvious whether ACP is already supported, how an editor should invoke claw-code, or whether a dedicated flag/mode exists at all. That forces evaluators into repo archaeology instead of giving them a crisp editor-facing invocation contract. **Fix shape:** either (a) add an explicit ACP/editor entrypoint such as `--acp` / `acp serve` with help text that states the contract, or (b) if the protocol path already exists, surface it prominently in CLI help/README with a concrete Zed/editor integration example so users do not have to guess. **Acceptance bar:** an editor-first user can answer "how do I launch claw-code for ACP/Zed?" from `claw --help` or the first screen of docs without reading source. **Blocker:** none; currently recorded as a roadmap follow-up because the repo-local entrypoint was not obvious during dogfood.
|
||||
64a. **ACP/Zed editor integration entrypoint is too implicit** — **done (verified 2026-04-16):** `claw` now exposes a local `acp` discoverability surface (`claw acp`, `claw acp serve`, `claw --acp`, `claw -acp`) that answers the editor-first question directly without starting the runtime, and `claw --help` / `rust/README.md` now surface the ACP/Zed status in first-screen command/docs text. The current contract is explicit: claw-code does **not** ship an ACP/Zed daemon entrypoint yet; `claw acp serve` is only a status alias, while real ACP protocol support is tracked separately as #76. Fresh proof: parser coverage for `acp`/`acp serve`/flag aliases, help rendering coverage, and JSON output coverage for `claw --output-format json acp`.
|
||||
Original filing (2026-04-13): user requested a `-acp` parameter to support ACP protocol integration in editor-first workflows such as Zed. The gap was a **discoverability and launch-contract problem**: the product surface did not make it obvious whether ACP was supported, how an editor should invoke claw-code, or whether a dedicated flag/mode existed at all.
|
||||
|
||||
64. **Artifact provenance is post-hoc narration, not structured events** — **done (verified 2026-04-12):** completed lane persistence in `rust/crates/tools/src/lib.rs` now attaches structured `artifactProvenance` metadata to `lane.finished`, including `sourceLanes`, `roadmapIds`, `files`, `diffStat`, `verification`, and `commitSha`, while keeping the existing `lane.commit.created` provenance event intact. Regression coverage locks a successful completion payload that carries roadmap ids, file paths, diff stat, verification states, and commit sha without relying on prose re-parsing. **Original filing below.**
|
||||
64b. **Artifact provenance is post-hoc narration, not structured events** — **done (verified 2026-04-12):** completed lane persistence in `rust/crates/tools/src/lib.rs` now attaches structured `artifactProvenance` metadata to `lane.finished`, including `sourceLanes`, `roadmapIds`, `files`, `diffStat`, `verification`, and `commitSha`, while keeping the existing `lane.commit.created` provenance event intact. Regression coverage locks a successful completion payload that carries roadmap ids, file paths, diff stat, verification states, and commit sha without relying on prose re-parsing. **Original filing below.**
|
||||
|
||||
65. **Backlog-scanning team lanes emit opaque stops, not structured selection outcomes** — **done (verified 2026-04-12):** completed lane persistence in `rust/crates/tools/src/lib.rs` now recognizes backlog-scan selection summaries and records structured `selectionOutcome` metadata on `lane.finished`, including `chosenItems`, `skippedItems`, `action`, and optional `rationale`, while preserving existing non-selection and review-lane behavior. Regression coverage locks the structured backlog-scan payload alongside the earlier quality-floor and review-verdict paths. **Original filing below.**
|
||||
|
||||
@@ -1169,3 +1170,5 @@ Model name prefix now wins unconditionally over env-var presence. Regression tes
|
||||
74. **Poisoned test locks cascade into unrelated Rust regressions** — **done (verified 2026-04-12):** test-only env/cwd lock acquisition in `rust/crates/tools/src/lib.rs`, `rust/crates/plugins/src/lib.rs`, `rust/crates/commands/src/lib.rs`, and `rust/crates/rusty-claude-cli/src/main.rs` now recovers poisoned mutexes via `PoisonError::into_inner`, and new regressions lock that behavior so one panic no longer causes later tests to fail just by touching the shared env/cwd locks. Source: Jobdori dogfood 2026-04-12.
|
||||
|
||||
75. **`claw init` leaves `.clawhip/` runtime artifacts unignored** — **done (verified 2026-04-12):** `rust/crates/rusty-claude-cli/src/init.rs` now treats `.clawhip/` as a first-class local artifact alongside `.claw/` paths, and regression coverage locks both the create and idempotent update paths so `claw init` adds the ignore entry exactly once. The repo `.gitignore` now also ignores `.clawhip/` for immediate dogfood relief, preventing repeated OMX team merge conflicts on `.clawhip/state/prompt-submit.json`. Source: Jobdori dogfood 2026-04-12.
|
||||
|
||||
76. **Real ACP/Zed daemon contract is still missing after the discoverability fix** — follow-up filed 2026-04-16. ROADMAP #64 made the current status explicit via `claw acp`, but editor-first users still cannot actually launch claw-code as an ACP/Zed daemon because there is no protocol-serving surface yet. **Fix shape:** add a real ACP entrypoint (for example `claw acp serve`) only when the underlying protocol/transport contract exists, then document the concrete editor wiring in `claw --help` and first-screen docs. **Acceptance bar:** an editor can launch claw-code for ACP/Zed from a documented, supported command rather than a status-only alias. **Blocker:** protocol/runtime work not yet implemented; current `acp serve` spelling is intentionally guidance-only.
|
||||
|
||||
@@ -135,6 +135,7 @@ Top-level commands:
|
||||
version
|
||||
status
|
||||
sandbox
|
||||
acp [serve]
|
||||
dump-manifests
|
||||
bootstrap-plan
|
||||
agents
|
||||
@@ -144,6 +145,8 @@ Top-level commands:
|
||||
init
|
||||
```
|
||||
|
||||
`claw acp` is a local discoverability surface for editor-first users: it reports the current ACP/Zed status without starting the runtime. As of April 16, 2026, claw-code does **not** ship an ACP/Zed daemon entrypoint yet, and `claw acp serve` is only a status alias until the real protocol surface lands.
|
||||
|
||||
The command surface is moving quickly. For the canonical live help text, run:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -95,6 +95,8 @@ const CLI_OPTION_SUGGESTIONS: &[&str] = &[
|
||||
"--allowedTools",
|
||||
"--allowed-tools",
|
||||
"--resume",
|
||||
"--acp",
|
||||
"-acp",
|
||||
"--print",
|
||||
"--compact",
|
||||
"--base-commit",
|
||||
@@ -248,6 +250,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
cli.run_turn_with_output(&effective_prompt, output_format, compact)?;
|
||||
}
|
||||
CliAction::Doctor { output_format } => run_doctor(output_format)?,
|
||||
CliAction::Acp { output_format } => print_acp_status(output_format)?,
|
||||
CliAction::State { output_format } => run_worker_state(output_format)?,
|
||||
CliAction::Init { output_format } => run_init(output_format)?,
|
||||
CliAction::Export {
|
||||
@@ -337,6 +340,9 @@ enum CliAction {
|
||||
Doctor {
|
||||
output_format: CliOutputFormat,
|
||||
},
|
||||
Acp {
|
||||
output_format: CliOutputFormat,
|
||||
},
|
||||
State {
|
||||
output_format: CliOutputFormat,
|
||||
},
|
||||
@@ -368,6 +374,7 @@ enum LocalHelpTopic {
|
||||
Status,
|
||||
Sandbox,
|
||||
Doctor,
|
||||
Acp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -547,6 +554,10 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
||||
rest.push(flag[9..].to_string());
|
||||
index += 1;
|
||||
}
|
||||
"--acp" | "-acp" => {
|
||||
rest.push("acp".to_string());
|
||||
index += 1;
|
||||
}
|
||||
"--allowedTools" | "--allowed-tools" => {
|
||||
let value = args
|
||||
.get(index + 1)
|
||||
@@ -661,6 +672,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
||||
}
|
||||
}
|
||||
"system-prompt" => parse_system_prompt_args(&rest[1..], output_format),
|
||||
"acp" => parse_acp_args(&rest[1..], output_format),
|
||||
"login" | "logout" => Err(removed_auth_surface_error(rest[0].as_str())),
|
||||
"init" => Ok(CliAction::Init { output_format }),
|
||||
"export" => parse_export_args(&rest[1..], output_format),
|
||||
@@ -715,6 +727,7 @@ fn parse_local_help_action(rest: &[String]) -> Option<Result<CliAction, String>>
|
||||
"status" => LocalHelpTopic::Status,
|
||||
"sandbox" => LocalHelpTopic::Sandbox,
|
||||
"doctor" => LocalHelpTopic::Doctor,
|
||||
"acp" => LocalHelpTopic::Acp,
|
||||
_ => return None,
|
||||
};
|
||||
Some(Ok(CliAction::HelpTopic(topic)))
|
||||
@@ -785,6 +798,16 @@ fn removed_auth_surface_error(command_name: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_acp_args(args: &[String], output_format: CliOutputFormat) -> Result<CliAction, String> {
|
||||
match args {
|
||||
[] => Ok(CliAction::Acp { output_format }),
|
||||
[subcommand] if subcommand == "serve" => Ok(CliAction::Acp { output_format }),
|
||||
_ => Err(String::from(
|
||||
"unsupported ACP invocation. Use `claw acp`, `claw acp serve`, `claw --acp`, or `claw -acp`.",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_resolve_bare_skill_prompt(cwd: &Path, trimmed: &str) -> Option<String> {
|
||||
let bare_first_token = trimmed.split_whitespace().next().unwrap_or_default();
|
||||
let looks_like_skill_name = !bare_first_token.is_empty()
|
||||
@@ -5175,6 +5198,13 @@ fn render_help_topic(topic: LocalHelpTopic) -> String {
|
||||
Output local-only health report; no provider request or session resume required
|
||||
Related /doctor · claw --resume latest /doctor"
|
||||
.to_string(),
|
||||
LocalHelpTopic::Acp => "ACP / Zed
|
||||
Usage claw acp [serve]
|
||||
Aliases claw --acp · claw -acp
|
||||
Purpose explain the current editor-facing ACP/Zed launch contract without starting the runtime
|
||||
Status discoverability only; `serve` is a status alias and does not launch a daemon yet
|
||||
Related ROADMAP #64a (discoverability) · ROADMAP #76 (real ACP support) · claw --help"
|
||||
.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5182,6 +5212,39 @@ fn print_help_topic(topic: LocalHelpTopic) {
|
||||
println!("{}", render_help_topic(topic));
|
||||
}
|
||||
|
||||
fn print_acp_status(output_format: CliOutputFormat) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let message = "ACP/Zed editor integration is not implemented in claw-code yet. `claw acp serve` is only a discoverability alias today; it does not launch a daemon or Zed-specific protocol endpoint. Use the normal terminal surfaces for now and track ROADMAP #76 for real ACP support.";
|
||||
match output_format {
|
||||
CliOutputFormat::Text => {
|
||||
println!(
|
||||
"ACP / Zed\n Status discoverability only\n Launch `claw acp serve` / `claw --acp` / `claw -acp` report status only; no editor daemon is available yet\n Today use `claw prompt`, the REPL, or `claw doctor` for local verification\n Tracking ROADMAP #76\n Message {message}"
|
||||
);
|
||||
}
|
||||
CliOutputFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"kind": "acp",
|
||||
"status": "discoverability_only",
|
||||
"supported": false,
|
||||
"serve_alias_only": true,
|
||||
"message": message,
|
||||
"launch_command": serde_json::Value::Null,
|
||||
"aliases": ["acp", "--acp", "-acp"],
|
||||
"discoverability_tracking": "ROADMAP #64a",
|
||||
"tracking": "ROADMAP #76",
|
||||
"recommended_workflows": [
|
||||
"claw prompt TEXT",
|
||||
"claw",
|
||||
"claw doctor"
|
||||
],
|
||||
}))?
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_config_report(section: Option<&str>) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let cwd = env::current_dir()?;
|
||||
let loader = ConfigLoader::default_for(&cwd);
|
||||
@@ -8148,6 +8211,11 @@ fn print_help_to(out: &mut impl Write) -> io::Result<()> {
|
||||
out,
|
||||
" Diagnose local auth, config, workspace, and sandbox health"
|
||||
)?;
|
||||
writeln!(out, " claw acp [serve]")?;
|
||||
writeln!(
|
||||
out,
|
||||
" Show ACP/Zed editor integration status (currently unsupported; aliases: --acp, -acp)"
|
||||
)?;
|
||||
writeln!(out, " Source of truth: {OFFICIAL_REPO_SLUG}")?;
|
||||
writeln!(
|
||||
out,
|
||||
@@ -9200,6 +9268,34 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_acp_command_surfaces() {
|
||||
assert_eq!(
|
||||
parse_args(&["acp".to_string()]).expect("acp should parse"),
|
||||
CliAction::Acp {
|
||||
output_format: CliOutputFormat::Text,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_args(&["acp".to_string(), "serve".to_string()]).expect("acp serve should parse"),
|
||||
CliAction::Acp {
|
||||
output_format: CliOutputFormat::Text,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_args(&["--acp".to_string()]).expect("--acp should parse"),
|
||||
CliAction::Acp {
|
||||
output_format: CliOutputFormat::Text,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_args(&["-acp".to_string()]).expect("-acp should parse"),
|
||||
CliAction::Acp {
|
||||
output_format: CliOutputFormat::Text,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_command_help_flags_stay_on_the_local_parser_path() {
|
||||
assert_eq!(
|
||||
@@ -9217,6 +9313,10 @@ mod tests {
|
||||
.expect("doctor help should parse"),
|
||||
CliAction::HelpTopic(LocalHelpTopic::Doctor)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_args(&["acp".to_string(), "--help".to_string()]).expect("acp help should parse"),
|
||||
CliAction::HelpTopic(LocalHelpTopic::Acp)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -10125,6 +10225,7 @@ mod tests {
|
||||
assert!(help.contains("claw status"));
|
||||
assert!(help.contains("claw sandbox"));
|
||||
assert!(help.contains("claw init"));
|
||||
assert!(help.contains("claw acp [serve]"));
|
||||
assert!(help.contains("claw agents"));
|
||||
assert!(help.contains("claw mcp"));
|
||||
assert!(help.contains("claw skills"));
|
||||
|
||||
@@ -46,6 +46,24 @@ fn status_and_sandbox_emit_json_when_requested() {
|
||||
assert!(sandbox["filesystem_mode"].as_str().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_guidance_emits_json_when_requested() {
|
||||
let root = unique_temp_dir("acp-json");
|
||||
fs::create_dir_all(&root).expect("temp dir should exist");
|
||||
|
||||
let acp = assert_json_command(&root, &["--output-format", "json", "acp"]);
|
||||
assert_eq!(acp["kind"], "acp");
|
||||
assert_eq!(acp["status"], "discoverability_only");
|
||||
assert_eq!(acp["supported"], false);
|
||||
assert_eq!(acp["serve_alias_only"], true);
|
||||
assert_eq!(acp["discoverability_tracking"], "ROADMAP #64a");
|
||||
assert_eq!(acp["tracking"], "ROADMAP #76");
|
||||
assert!(acp["message"]
|
||||
.as_str()
|
||||
.expect("acp message")
|
||||
.contains("discoverability alias"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inventory_commands_emit_structured_json_when_requested() {
|
||||
let root = unique_temp_dir("inventory-json");
|
||||
|
||||
Reference in New Issue
Block a user