diff --git a/crates/buzz-acp/TESTING.md b/crates/buzz-acp/TESTING.md index 8c8fcbee49a..06fad051538 100644 --- a/crates/buzz-acp/TESTING.md +++ b/crates/buzz-acp/TESTING.md @@ -1,58 +1,29 @@ # Pi adapter integration -Buzz's Pi preset uses [salman1993/pi-acp](https://github.com/salman1993/pi-acp). -Requires Node.js 22 or newer. Install Pi and configure its model provider, -then install the adapter directly from the fork: +Buzz uses the [buzz-pi-acp fork](https://github.com/salman1993/buzz-pi-acp). +Install Node.js 22 or newer, configure Pi, and install the latest development +adapter from the fork's `main` branch: ```sh npm install -g @earendil-works/pi-coding-agent pi -npm install -g --install-links=true git+https://github.com/salman1993/pi-acp.git#main +npm install -g --install-links=true 'git+https://github.com/salman1993/buzz-pi-acp.git#main' ``` -Restart Buzz, then select **Pi** as the agent harness. Buzz starts `buzz-pi-acp` -automatically. Run the same adapter install command again to update it. -The unscoped `npm install -g pi-acp` command installs the upstream package, -without these extensions. Use fresh sessions to replace old user-framed -standing instructions. +This test setup intentionally tracks `main`; the Desktop runtime catalog pins a +reviewed adapter revision for users. -Buzz adds `-- --skill /.agents/skills` when launching `buzz-pi-acp`. -An existing separator and explicit Pi options are preserved. Managed agents -run from the Buzz nest (normally `~/.buzz`): Desktop sets the `buzz-acp` child -CWD through `default_agent_workdir()`, and adapters inherit it. The default -skill directory is that launch workspace's `.agents/skills`. Standalone CLI -launches use the caller's working directory. -The path is fixed at adapter launch and applies to every Pi subprocess. +Make sure `pi` and `buzz-pi-acp` are on PATH, then restart Buzz. -The full composed session prompt is sent as a replacement string through the -provisional `session/new.params._meta.systemPrompt` field. Buzz recognizes -`buzz-pi-acp` by the agent name returned during initialization, regardless of -protocol version. Upstream `pi-acp` does not receive this fork-specific metadata. -No custom capability negotiation or legacy Pi prompt fallback is used. -Session titles continue to use `_meta.sessionTitle`. - -## Validation - -Activate Hermit from the Buzz repository root, then run the package tests: +## Tests ```sh -. ./bin/activate-hermit cargo test -p buzz-acp ``` -To exercise the real adapter through Buzz's production session composer: +Run the ignored real-adapter test with a built fork checkout: ```sh -BUZZ_TEST_PI_ACP=/absolute/pi-acp/dist/index.js \ +BUZZ_TEST_PI_ACP=/absolute/buzz-pi-acp/dist/index.js \ cargo test -p buzz-acp real_pi_preserves -- --ignored ``` - -This test requires Node and Pi on PATH. It isolates HOME and Pi settings, -disables extensions and context files, and uses a synthetic transcript without -model calls. It inspects Pi's effective prompt through RPC HTML export after -switching sessions and restarting the adapter. Base, persona, team, core memory, -huddle, canvas, and the extra skill must each appear once, without another -session's instructions or Pi's default coding preamble. - -HTML export reports the exporting process's current system prompt. It cannot -recover a historical prompt from an old transcript alone. diff --git a/crates/buzz-acp/src/setup_mode.rs b/crates/buzz-acp/src/setup_mode.rs index 70b5a8dcb28..c924e26f0fd 100644 --- a/crates/buzz-acp/src/setup_mode.rs +++ b/crates/buzz-acp/src/setup_mode.rs @@ -116,6 +116,8 @@ pub(crate) enum RequirementPayload { }, /// Git for Windows is missing; open Agent runtimes for the installation guide. GitBash, + /// A custom harness command could not be resolved in the current PATH. + MissingBinary { command: String }, } impl RequirementPayload { @@ -190,6 +192,9 @@ impl RequirementPayload { RequirementPayload::GitBash => { "install Git for Windows (open Agent runtimes in Settings to diagnose)".to_string() } + RequirementPayload::MissingBinary { command } => { + format!("install `{command}` or add it to PATH") + } } } } @@ -262,6 +267,10 @@ impl SetupPayload { .requirements .iter() .all(|r| matches!(r, RequirementPayload::CliConfigInvalid { .. })); + let all_missing_binary = self + .requirements + .iter() + .all(|r| matches!(r, RequirementPayload::MissingBinary { .. })); let any_external = self .requirements .iter() @@ -269,6 +278,8 @@ impl SetupPayload { let footer = if has_doctor_requirement { "Open Agent runtimes in Settings, install Git for Windows, then re-check and restart the agent.".to_string() + } else if all_missing_binary { + "Install the missing binary or update PATH, then restart Buzz.".to_string() } else if all_external { // All requirements are external config files — Edit Agent cannot // help. Don't send the user there. @@ -739,6 +750,23 @@ mod tests { )); } + #[test] + fn setup_payload_deserializes_missing_binary_requirement() { + let payload = SetupPayload::from_raw_env_value(Some( + r#"{"agent_name":"Carol","agent_pubkey":"test","requirements":[{"surface":"missing_binary","command":"buzz-pi-acp"}]}"#.to_string(), + )) + .unwrap() + .expect("missing_binary payload must parse"); + assert!(matches!( + payload.requirements.as_slice(), + [RequirementPayload::MissingBinary { command }] if command == "buzz-pi-acp" + )); + let body = payload.nudge_body(); + assert!(body.contains("install `buzz-pi-acp` or add it to PATH")); + assert!(body.contains("restart Buzz")); + assert!(!body.contains("Open Edit Agent")); + } + #[tokio::test] async fn authorized_workflow_nudge_mentions_effective_owner_not_relay_signer() { let agent_keys = nostr::Keys::generate(); diff --git a/desktop/src-tauri/src/managed_agents/discovery/presets.rs b/desktop/src-tauri/src/managed_agents/discovery/presets.rs index 68763d9aaad..d46145dbd68 100644 --- a/desktop/src-tauri/src/managed_agents/discovery/presets.rs +++ b/desktop/src-tauri/src/managed_agents/discovery/presets.rs @@ -109,8 +109,8 @@ pub(super) const PRESET_HARNESSES: &[PresetHarness] = &[ label: "Pi", command: "buzz-pi-acp", args: &[], - install_instructions_url: "https://github.com/salman1993/pi-acp", - install_hint: "Requires Node.js 22 or newer. Install the Pi ACP adapter with `npm install -g --install-links=true git+https://github.com/salman1993/pi-acp.git#main`. Restart Buzz, then select Pi as the agent harness. Run the same install command again to update the adapter.", + install_instructions_url: "https://github.com/salman1993/buzz-pi-acp", + install_hint: "Requires Node.js 22 or newer. Install the Pi ACP adapter with `npm install -g --install-links=true 'git+https://github.com/salman1993/buzz-pi-acp.git#86b201e'`. Make sure `buzz-pi-acp` is on PATH, then restart Buzz.", underlying_cli: Some("pi"), underlying_cli_install_hint: Some( "Install Pi with `npm install -g @earendil-works/pi-coding-agent`, then run `pi` to configure its model provider.", @@ -434,11 +434,11 @@ mod tests { assert!(adapter_missing.default_args.is_empty()); assert_eq!( adapter_missing.install_hint, - "Requires Node.js 22 or newer. Install the Pi ACP adapter with `npm install -g --install-links=true git+https://github.com/salman1993/pi-acp.git#main`. Restart Buzz, then select Pi as the agent harness. Run the same install command again to update the adapter." + "Requires Node.js 22 or newer. Install the Pi ACP adapter with `npm install -g --install-links=true 'git+https://github.com/salman1993/buzz-pi-acp.git#86b201e'`. Make sure `buzz-pi-acp` is on PATH, then restart Buzz." ); assert_eq!( adapter_missing.install_instructions_url, - "https://github.com/salman1993/pi-acp" + "https://github.com/salman1993/buzz-pi-acp" ); let cli_missing = preset_catalog_entry(preset, |command| { @@ -462,7 +462,7 @@ mod tests { ); assert_eq!( not_installed.install_hint, - "Install Pi with `npm install -g @earendil-works/pi-coding-agent`, then run `pi` to configure its model provider. Requires Node.js 22 or newer. Install the Pi ACP adapter with `npm install -g --install-links=true git+https://github.com/salman1993/pi-acp.git#main`. Restart Buzz, then select Pi as the agent harness. Run the same install command again to update the adapter." + "Install Pi with `npm install -g @earendil-works/pi-coding-agent`, then run `pi` to configure its model provider. Requires Node.js 22 or newer. Install the Pi ACP adapter with `npm install -g --install-links=true 'git+https://github.com/salman1993/buzz-pi-acp.git#86b201e'`. Make sure `buzz-pi-acp` is on PATH, then restart Buzz." ); } diff --git a/desktop/src/shared/ui/config-nudge-attachment.test.mjs b/desktop/src/shared/ui/config-nudge-attachment.test.mjs index e526be34fe1..d823ecc5ec7 100644 --- a/desktop/src/shared/ui/config-nudge-attachment.test.mjs +++ b/desktop/src/shared/ui/config-nudge-attachment.test.mjs @@ -28,6 +28,7 @@ globalThis.window = { import { focusTargetForRequirement, + missingBinaryRecoveryMessage, shouldOpenDoctor, } from "./config-nudge-attachment.tsx"; import { @@ -60,6 +61,21 @@ test("shouldOpenDoctor_regularMixedRequirements_routesToEditAgent", () => { ); }); +test("shouldOpenDoctor_missingBinary_routesToAgentRuntimes", () => { + assert.equal( + shouldOpenDoctor([{ surface: "missing_binary", command: "buzz-pi-acp" }]), + true, + "a missing runtime binary must route to Agent runtimes", + ); +}); + +test("missingBinaryRecoveryMessage_requiresBuzzRestart", () => { + assert.equal( + missingBinaryRecoveryMessage(), + "not found in PATH — install it or update PATH, then restart Buzz", + ); +}); + // ── focusTargetForRequirement — pure function ───────────────────────────────── test("focusTargetForRequirement_envKey_returnsEnvKeyTarget", () => { diff --git a/desktop/src/shared/ui/config-nudge-attachment.tsx b/desktop/src/shared/ui/config-nudge-attachment.tsx index cc5931e23e3..738fc3ead12 100644 --- a/desktop/src/shared/ui/config-nudge-attachment.tsx +++ b/desktop/src/shared/ui/config-nudge-attachment.tsx @@ -43,11 +43,10 @@ function requirementKey( } /** - * Returns true when every requirement in the nudge is a `cli_login` surface. - * Non-authOnly all-cli_login cards (at least one install-state row) route to - * Agent runtimes — install/login problems can't be fixed in Edit Agent. AuthOnly cards - * (every row is `availability === "available"`) are purely informational and - * do not route anywhere. + * Requirements owned by runtime discovery route to Agent runtimes. This covers + * Git Bash, unresolved binaries, and all-CLI-login cards with installation work. + * Auth-only cards remain informational because Agent runtimes cannot log in to + * an external CLI for the user. */ function hasGitBashRequirement( reqs: ConfigNudgePayload["requirements"], @@ -55,6 +54,12 @@ function hasGitBashRequirement( return reqs.some((r) => r.surface === "git_bash"); } +function hasMissingBinaryRequirement( + reqs: ConfigNudgePayload["requirements"], +): boolean { + return reqs.some((r) => r.surface === "missing_binary"); +} + function isAllCliLogin(reqs: ConfigNudgePayload["requirements"]): boolean { return reqs.length > 0 && reqs.every((r) => r.surface === "cli_login"); } @@ -62,7 +67,15 @@ function isAllCliLogin(reqs: ConfigNudgePayload["requirements"]): boolean { export function shouldOpenDoctor( reqs: ConfigNudgePayload["requirements"], ): boolean { - return isAllCliLogin(reqs) || hasGitBashRequirement(reqs); + return ( + isAllCliLogin(reqs) || + hasGitBashRequirement(reqs) || + hasMissingBinaryRequirement(reqs) + ); +} + +export function missingBinaryRecoveryMessage(): string { + return "not found in PATH — install it or update PATH, then restart Buzz"; } /** @@ -165,9 +178,10 @@ export function focusTargetForRequirement( * the system. * * Routing: - * (A) Any card with a `git_bash` requirement, or one whose requirements are all - * install-state `cli_login`, opens Settings → Agent runtimes. A card-level - * Agent runtimes label in `AttachmentActions` confirms the action at rest. + * (A) Any card with a `git_bash` or `missing_binary` requirement, or one whose + * requirements are all install-state `cli_login`, opens Settings → Agent + * runtimes. A card-level Agent runtimes label in `AttachmentActions` confirms + * the action at rest. * (A-auth) A card whose requirements are all available `cli_login` surfaces is * purely informational: Agent runtimes cannot authenticate a CLI, and `setup_copy` * already gives the needed command. @@ -211,7 +225,7 @@ export function ConfigNudgeCard({ const handleOpen = () => { if (shouldOpenDoctor(nudge.requirements)) { - // Git Bash and install-state CLI requirements both resolve in Agent runtimes. + // Runtime installation and discovery requirements resolve in Agent runtimes. // Informational-only cards never mount this trigger. openDoctor(); } else { @@ -384,15 +398,16 @@ function RequirementRow({ ); case "missing_binary": { - // Missing-binary rows are purely informational — the user must install the - // binary or update their PATH. No in-app action can fix this. + // Agent runtimes owns installation guidance and the re-check action. The + // restart guidance remains visible here because a normal agent restart does + // not invalidate Desktop's negative command-resolution cache. return (
{requirement.command} {" "} - not found in PATH — install it or check your PATH settings + {missingBinaryRecoveryMessage()}
);