diff --git a/desktop/public/harness-logos/CREDITS.md b/desktop/public/harness-logos/CREDITS.md index 716c43e1ae3..9ac58fd900d 100644 --- a/desktop/public/harness-logos/CREDITS.md +++ b/desktop/public/harness-logos/CREDITS.md @@ -12,6 +12,7 @@ license permits redistribution. | `devin.svg` | [Cognition Devin documentation](https://docs.devin.ai/cli) | Retrieved 2026-07-27 | Cognition trademark; nominative use to identify the Devin harness | Official documentation `logo/favicon.svg` | Added the official black mark to a white square canvas so it remains legible in both app themes | | `hermes.png` | [NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent) | `6ad632b` | MIT © 2025 Nous Research | `website/static/img/logo.png` | Cropped the baked-in border frame, padded to square, resized to 64×64, quantised to a 16-colour palette | | `openclaw.svg` | [openclaw/openclaw](https://github.com/openclaw/openclaw) | `b06f40a` | MIT © 2026 OpenClaw Foundation | `ui/public/favicon.svg` | Removed the SMIL animation elements (renders the upstream rest pose statically — verified pixel-identical to the upstream frame at t=0); minified paths | +| `pi.svg` | [earendil-works/pi](https://github.com/earendil-works/pi) | Retrieved 2026-08-21 | MIT © 2025 Mario Zechner | [`https://pi.dev/logo-auto.svg`](https://pi.dev/logo-auto.svg) | None | | `omp.svg` | [can1357/oh-my-pi](https://github.com/can1357/oh-my-pi) | `667111575ebba136dadfd6989379e7f67e0d40d9` | MIT © 2025 Mario Zechner; © 2025–2026 Can Bölük | `assets/icon.svg` | None | | `kimi.png` | [MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli) | `4a550effdfcb29a25a5d325bf935296cc50cd417` | Apache-2.0; NOTICE: Kimi Code CLI © 2025 Moonshot AI | `web/public/logo.png` | None | | `grok.svg` | [SpaceXAI brand guidelines](https://x.ai/legal/brand-guidelines) | Retrieved 2026-07-25 | xAI Brand Guidelines: marks may be used to accurately refer to xAI or its services; logos must be used exactly as provided | `SpaceXAI_Grok_Assets.zip` → `Grok_Logomark_Dark.svg` | None | diff --git a/desktop/public/harness-logos/pi.svg b/desktop/public/harness-logos/pi.svg new file mode 100644 index 00000000000..c403a4ea012 --- /dev/null +++ b/desktop/public/harness-logos/pi.svg @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/desktop/src-tauri/src/managed_agents/discovery/presets.rs b/desktop/src-tauri/src/managed_agents/discovery/presets.rs index fd853094515..45b6c8cbfed 100644 --- a/desktop/src-tauri/src/managed_agents/discovery/presets.rs +++ b/desktop/src-tauri/src/managed_agents/discovery/presets.rs @@ -108,6 +108,15 @@ pub(super) const PRESET_HARNESSES: &[PresetHarness] = &[ install_hint: "Buzz talks to Cursor through the cursor-agent CLI's ACP mode.", underlying_cli: None, }, + PresetHarness { + id: "pi", + label: "Pi", + command: "pi-acp", + args: &[], + install_instructions_url: "https://github.com/svkozak/pi-acp", + install_hint: "Buzz talks to the standard Pi CLI through the pi-acp adapter. Follow the setup guide to install both pi and pi-acp so both commands are on your PATH.", + underlying_cli: Some("pi"), + }, PresetHarness { id: "omp", label: "Oh My Pi", @@ -347,6 +356,34 @@ mod tests { assert_eq!(entry.source, HarnessSource::Preset); } + #[test] + fn pi_preset_uses_standard_pi_acp_adapter() { + let preset = PRESET_HARNESSES + .iter() + .find(|preset| preset.id == "pi") + .expect("Pi preset should be present"); + + assert_eq!(preset.label, "Pi"); + assert_eq!(preset.command, "pi-acp"); + assert!(preset.args.is_empty()); + assert_eq!(preset.underlying_cli, Some("pi")); + assert_eq!( + preset.install_instructions_url, + "https://github.com/svkozak/pi-acp" + ); + + let entry = preset_catalog_entry(preset, |command| { + (command == "pi").then(|| PathBuf::from("/usr/local/bin/pi")) + }); + assert_eq!(entry.availability, AcpAvailabilityStatus::AdapterMissing); + assert!(entry.command.is_none()); + assert_eq!( + entry.underlying_cli_path.as_deref(), + Some("/usr/local/bin/pi") + ); + assert!(entry.install_hint.contains("standard Pi CLI")); + } + #[test] fn adapter_missing_when_underlying_cli_present() { let entry = preset_catalog_entry(&ADAPTER_PRESET, |command| { diff --git a/desktop/src/features/onboarding/ui/RuntimeIcon.tsx b/desktop/src/features/onboarding/ui/RuntimeIcon.tsx index 5b247c31f73..35fb5c7e835 100644 --- a/desktop/src/features/onboarding/ui/RuntimeIcon.tsx +++ b/desktop/src/features/onboarding/ui/RuntimeIcon.tsx @@ -18,6 +18,7 @@ const RUNTIME_LOGOS: Record = { // Keys match the preset `id` values emitted by the backend PRESET_HARNESSES. export const PRESET_LOGOS: Record = { devin: "/harness-logos/devin.svg", + pi: "/harness-logos/pi.svg", omp: "/harness-logos/omp.svg", grok: "/harness-logos/grok.svg", opencode: "/harness-logos/opencode.svg", diff --git a/desktop/src/features/settings/ui/harnessCatalogCopy.ts b/desktop/src/features/settings/ui/harnessCatalogCopy.ts index 9a71ff70f92..f0560465788 100644 --- a/desktop/src/features/settings/ui/harnessCatalogCopy.ts +++ b/desktop/src/features/settings/ui/harnessCatalogCopy.ts @@ -25,6 +25,9 @@ const HARNESS_DESCRIPTIONS: Record = { // Bundled presets — sources per RESEARCH/BYOH_CATALOG_IA.md. // Source: https://cursor.com/docs/cli/acp cursor: "Cursor's coding agent, connected to Buzz through its ACP server.", + // Sources: https://github.com/earendil-works/pi, + // https://github.com/svkozak/pi-acp + pi: "A minimal terminal coding harness, connected through the pi-acp adapter.", // Source: https://github.com/can1357/oh-my-pi omp: "A terminal coding agent with integrated development tools.", // Source: https://build.x.ai (docs unavailable during research; kept diff --git a/desktop/tests/e2e/harness-catalog-screenshots.spec.ts b/desktop/tests/e2e/harness-catalog-screenshots.spec.ts index c5613f41f2a..01d242d418b 100644 --- a/desktop/tests/e2e/harness-catalog-screenshots.spec.ts +++ b/desktop/tests/e2e/harness-catalog-screenshots.spec.ts @@ -79,6 +79,24 @@ const CATALOG = [ auth_status: { status: "not_applicable" }, source: "preset", }, + { + id: "pi", + label: "Pi", + avatar_url: "", + availability: "adapter_missing", + command: null, + binary_path: null, + default_args: [], + mcp_command: null, + install_hint: + "Buzz talks to the standard Pi CLI through the pi-acp adapter. Follow the setup guide to install both pi and pi-acp so both commands are on your PATH.", + install_instructions_url: "https://github.com/svkozak/pi-acp", + can_auto_install: false, + underlying_cli_path: "/usr/local/bin/pi", + node_required: false, + auth_status: { status: "not_applicable" }, + source: "preset", + }, { id: "omp", label: "Oh My Pi", @@ -160,7 +178,14 @@ test("after: consolidated harnesses panel + catalog dialog", async ({ path: `${SHOTS}/after-catalog-needs-setup.png`, }); - // 3. Ready entry detail (Ready state, no install action). Ready entries + // 3. Standard Pi uses its own preset and reports the missing ACP adapter. + await page.getByTestId("harness-catalog-list-item-pi").click(); + await page.waitForTimeout(300); + await page.getByTestId("harness-catalog-dialog").screenshot({ + path: `${SHOTS}/after-catalog-pi.png`, + }); + + // 4. Ready entry detail (Ready state, no install action). Ready entries // sit in the "Installed" accordion, collapsed by default — expand it. await page.getByTestId("harness-catalog-section-installed").click(); await page.getByTestId("harness-catalog-list-item-claude").click(); @@ -169,7 +194,7 @@ test("after: consolidated harnesses panel + catalog dialog", async ({ path: `${SHOTS}/after-catalog-ready-detail.png`, }); - // 4. Custom harness form — all fields inline. + // 5. Custom harness form — all fields inline. await page.getByTestId("harness-catalog-list-item-custom").click(); await expect(page.getByTestId("custom-harness-form")).toBeVisible(); await page.waitForTimeout(300);