From 3b806702e7a0e90bbfd8b316b9d14e1597ca91d7 Mon Sep 17 00:00:00 2001 From: Yeachan-Heo Date: Sun, 12 Apr 2026 04:50:03 +0000 Subject: [PATCH] Make the CLI point users at the real install source The next repo-local backlog item was ROADMAP #70: users could mistake third-party pages or the deprecated `cargo install claw-code` path for the official install route. The CLI now surfaces the source of truth directly in `claw doctor` and `claw --help`, and the roadmap closeout records the change. Constraint: Keep the fix inside repo-local Rust CLI surfaces instead of relying on docs alone Rejected: Close #70 with README-only wording | the bug was user-facing CLI ambiguity, so the warning needed to appear in runtime help/doctor output Confidence: high Scope-risk: narrow Reversibility: clean Directive: If install guidance changes later, update both the doctor check payload and the help-text warning together Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE Not-tested: Third-party websites outside this repo that may still present stale install instructions --- ROADMAP.md | 2 +- rust/crates/rusty-claude-cli/src/main.rs | 46 +++++++++++++++++++ .../tests/output_format_contract.rs | 24 +++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 2e2e990..7cbfffa 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -512,4 +512,4 @@ Model name prefix now wins unconditionally over env-var presence. Regression tes 69. **Lane stop summaries have no minimum quality floor** — **done (verified 2026-04-12):** completed lane persistence in `rust/crates/tools/src/lib.rs` now normalizes vague/control-only stop summaries into a contextual fallback that includes the lane target and status, while preserving structured metadata about whether the quality floor fired (`qualityFloorApplied`, `rawSummary`, `reasons`, `wordCount`). Regression coverage locks both the pass-through path for good summaries and the fallback path for mushy summaries like `commit push everyting, keep sweeping $ralph`. **Original filing below.** -70. **Install-source ambiguity misleads real users** — community observation 2026-04-12. User treated `claw-code.io` as official, then hit `clawcode` vs deprecated `claw-code` naming collision and concluded install story was inconsistent. Source-of-truth is not obvious when website/repo/crates naming diverges. **Fix shape:** canonical repo docs should explicitly state which site is official; installation guidance should visibly warn against deprecated `claw-code` crate and ambiguous third-party pages. Blocker: none. Source: gaebal-gajae community watch 2026-04-12. +70. **Install-source ambiguity misleads real users** — **done (verified 2026-04-12):** repo-local Rust guidance now makes the source of truth explicit in `claw doctor` and `claw --help`, naming `ultraworkers/claw-code` as the canonical repo and warning that `cargo install claw-code` installs a deprecated stub rather than the `claw` binary. Regression coverage locks both the new doctor JSON check and the help-text warning. **Original filing below.** diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 06ea162..4bdf873 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -78,6 +78,9 @@ const INTERNAL_PROGRESS_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(3); const POST_TOOL_STALL_TIMEOUT: Duration = Duration::from_secs(10); const PRIMARY_SESSION_EXTENSION: &str = "jsonl"; const LEGACY_SESSION_EXTENSION: &str = "json"; +const OFFICIAL_REPO_URL: &str = "https://github.com/ultraworkers/claw-code"; +const OFFICIAL_REPO_SLUG: &str = "ultraworkers/claw-code"; +const DEPRECATED_INSTALL_COMMAND: &str = "cargo install claw-code"; const LATEST_SESSION_REFERENCE: &str = "latest"; const SESSION_REFERENCE_ALIASES: &[&str] = &[LATEST_SESSION_REFERENCE, "last", "recent"]; const CLI_OPTION_SUGGESTIONS: &[&str] = &[ @@ -1477,6 +1480,7 @@ fn render_doctor_report() -> Result> { checks: vec![ check_auth_health(), check_config_health(&config_loader, config.as_ref()), + check_install_source_health(), check_workspace_health(&context), check_sandbox_health(&context.sandbox_status), check_system_health(&cwd, config.as_ref().ok()), @@ -1764,6 +1768,36 @@ fn check_config_health( } } +fn check_install_source_health() -> DiagnosticCheck { + DiagnosticCheck::new( + "Install source", + DiagnosticLevel::Ok, + format!( + "official source of truth is {OFFICIAL_REPO_SLUG}; avoid `{DEPRECATED_INSTALL_COMMAND}`" + ), + ) + .with_details(vec![ + format!("Official repo {OFFICIAL_REPO_URL}"), + "Recommended path build from this repo or use the upstream binary documented in README.md" + .to_string(), + format!( + "Deprecated crate `{DEPRECATED_INSTALL_COMMAND}` installs a deprecated stub and does not provide the `claw` binary" + ) + .to_string(), + ]) + .with_data(Map::from_iter([ + ("official_repo".to_string(), json!(OFFICIAL_REPO_URL)), + ( + "deprecated_install".to_string(), + json!(DEPRECATED_INSTALL_COMMAND), + ), + ( + "recommended_install".to_string(), + json!("build from source or follow the upstream binary instructions in README.md"), + ), + ])) +} + fn check_workspace_health(context: &StatusContext) -> DiagnosticCheck { let in_repo = context.project_root.is_some(); DiagnosticCheck::new( @@ -8111,6 +8145,11 @@ fn print_help_to(out: &mut impl Write) -> io::Result<()> { out, " Diagnose local auth, config, workspace, and sandbox health" )?; + writeln!(out, " Source of truth: {OFFICIAL_REPO_SLUG}")?; + writeln!( + out, + " Warning: do not `{DEPRECATED_INSTALL_COMMAND}` (deprecated stub)" + )?; writeln!(out, " claw dump-manifests [--manifests-dir PATH]")?; writeln!(out, " claw bootstrap-plan")?; writeln!(out, " claw agents")?; @@ -8200,6 +8239,11 @@ fn print_help_to(out: &mut impl Write) -> io::Result<()> { writeln!(out, " claw mcp show my-server")?; writeln!(out, " claw /skills")?; writeln!(out, " claw doctor")?; + writeln!(out, " source of truth: {OFFICIAL_REPO_URL}")?; + writeln!( + out, + " do not run `{DEPRECATED_INSTALL_COMMAND}` — it installs a deprecated stub" + )?; writeln!(out, " claw init")?; writeln!(out, " claw export")?; writeln!(out, " claw export conversation.md")?; @@ -10082,6 +10126,8 @@ mod tests { assert!(help.contains("claw mcp")); assert!(help.contains("claw skills")); assert!(help.contains("claw /skills")); + assert!(help.contains("ultraworkers/claw-code")); + assert!(help.contains("cargo install claw-code")); assert!(!help.contains("claw login")); assert!(!help.contains("claw logout")); } diff --git a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs index 33d25cd..1c9bc93 100644 --- a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs +++ b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs @@ -209,7 +209,7 @@ fn doctor_and_resume_status_emit_json_when_requested() { assert!(summary["failures"].as_u64().is_some()); let checks = doctor["checks"].as_array().expect("doctor checks"); - assert_eq!(checks.len(), 5); + assert_eq!(checks.len(), 6); let check_names = checks .iter() .map(|check| { @@ -221,7 +221,27 @@ fn doctor_and_resume_status_emit_json_when_requested() { .collect::>(); assert_eq!( check_names, - vec!["auth", "config", "workspace", "sandbox", "system"] + vec![ + "auth", + "config", + "install source", + "workspace", + "sandbox", + "system" + ] + ); + + let install_source = checks + .iter() + .find(|check| check["name"] == "install source") + .expect("install source check"); + assert_eq!( + install_source["official_repo"], + "https://github.com/ultraworkers/claw-code" + ); + assert_eq!( + install_source["deprecated_install"], + "cargo install claw-code" ); let workspace = checks