mirror of
https://github.com/block/buzz.git
synced 2026-10-29 21:41:33 +00:00
fix(acp): improve hints in pi agent setup guide (#7594)
## Summary Handle missing Pi adapter setup and update its install guidance. ### Related issue None found. ### Testing No manual testing. Generated with Amp --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -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 <harness-cwd>/.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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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({
|
||||
</div>
|
||||
);
|
||||
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 (
|
||||
<div className="flex items-center gap-2 text-xs leading-4 text-muted-foreground">
|
||||
<span className="flex-1 [overflow-wrap:anywhere]">
|
||||
<code className="rounded bg-muted px-1 py-0.5 font-mono text-xs text-foreground">
|
||||
{requirement.command}
|
||||
</code>{" "}
|
||||
not found in PATH — install it or check your PATH settings
|
||||
{missingBinaryRecoveryMessage()}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user