feat(install): detect and configure pi coding agent - #534
Conversation
pi has no native MCP, so wire it up via the binary's CLI mode: install a pi extension (cbmem.ts, registers cbm_* tools), the codebase-memory skill, and an AGENTS.md graph-first reminder. Honors $PI_CODING_AGENT_DIR; falls back to ~/.pi/agent. Adds detection, install/uninstall, install-plan entries, and tests. Co-authored-by: builder-pi-adapter (gpt-5.5 via Overstory) Session-Id: 019ee430-dc1a-7575-a0ed-f2f2cf2f1379 Signed-off-by: Manav Gupta <manav.tensorboy@gmail.com> Session-Id: 019ee430-dc1a-7575-a0ed-f2f2cf2f1379
The pi skill frontmatter description was unquoted but contained ': ' and inner double-quotes, which YAML parses as a nested mapping -> pi's skill loader rejected it. Wrap the value in double quotes and use single quotes for the inline trigger examples, matching how the other skills quote descriptions. Signed-off-by: Manav Gupta <manav.tensorboy@gmail.com> Session-Id: 019ee430-dc1a-7575-a0ed-f2f2cf2f1379
|
Huge thanks for opening this PR and for the work you put into it. The maintainer shop is currently full, so this may sit for a bit before it gets a proper review. We will come back to this as soon as possible with real feedback; I wanted to make sure it did not sit unacknowledged in the meantime. |
|
Thank you for this, and apologies for the long wait. Reviewed in full, including the entire embedded TypeScript payload. There is a genuinely interesting idea here, one thing that must not ship as written, and a decision that is the maintainer's. The idea is not a duplicate, and that is worth stating up front. Most "add client X" PRs are a config-registration exercise. pi has no native MCP support, so you wrote a ~400-line pi extension registering eight native Security-wise the extension is careful, and I checked the parts that would matter: The thing that must change before this could ever ship. Two shipped files — the extension header comment and the user-facing
That is context from your own agent setup that has bled into files we would install on every user's machine. It references infrastructure that does not exist for them, inside a file an AI agent reads as guidance. To be clear about what I am and am not saying: I checked for directive-style prompt injection and there is none — no "ignore previous instructions", no goal redirection. It is unreviewed machine-generated text (the commit trailer shows a co-authoring agent) that leaked through. But shipped instruction content has to be written deliberately, so this would need removing regardless of anything else. Where it stands. The install mechanism this builds on was superseded on do we want to carry a bespoke TypeScript extension for a client that lacks MCP? That is a different maintenance commitment from a config entry: it couples us to pi's internal extension API, which can change under us, and it means shipping and maintaining TypeScript inside a lean C project. It might well be worth it — pi users otherwise get nothing — but it is a direction call and it is his, not mine. I have put it to him with the full picture. One smaller note for whenever it resumes: the binary resolution order ends at a bare PATH lookup, and Nothing to redo yet — please wait for the direction answer rather than rebasing on spec. I will come back to you either way. |
|
Coming back to you with the maintainer's decision, and with a piece of context that changes the picture. The decision: we are deferring the extension. The reasoning is about what this project is rather than about what you built — codebase-memory-mcp is an MCP server, and the extension would create a second tool surface that we would then own indefinitely. Concretely, That is a judgement about maintenance direction, not about your code. And there is something I have to tell you that is not your fault: main implemented pi support while this PR waited. I went looking for pieces worth salvaging with
What deserves saying regardless of the outcome: this is careful work. You used marker-delimited editing so a user's instructions file is never clobbered, you wrote an uninstall path and a test asserting the markers are gone afterwards, you added 191 lines of tests including install-plan assertions, and you introduced no raw If the direction changes — most plausibly if we decide to generate client adapters from the tool registry rather than hand-write them, which is what would dissolve the drift problem blocking this — I will come back to this PR, and to you, first. Thank you, and I am sorry the answer took as long as it did. |
|
Closing this, so it is not left sitting open with nothing that can move it. The reasoning is in my earlier comment and has not changed: main implemented pi support independently while this waited ( An open pull request that cannot move is worse than a closed one, so this is a clean close rather than an indefinite park. What I said before stands: this was careful work — the uninstall test asserting the markers are gone, scanning stdout upward for the last parseable JSON line rather than trusting the first, honouring pi's abort signal, and not a single raw And the condition that would reopen it is real, not a formality: if we ever generate client adapters from the tool registry rather than hand-writing them, the drift objection that blocks this dissolves entirely. If that happens I will come back to this PR and to you first. Thank you, and sorry again that the answer took as long as it did. |
|
Following up because the decision changed, and in your favour. Your idea is being implemented — #1392, with When I closed this I said the one thing that would reopen it was "if we ever generate client adapters from the tool registry rather than hand-write them, the drift objection that blocks this dissolves entirely." That is exactly what happened: the maintainer asked for pi support on the condition that no That change dissolves the objection completely. Three things from your PR are carried forward directly, and they are the reason the implementation went as quickly as it did:
I also kept the install path you chose ( Thank you for the original work, and for taking the close well. You were right that pi needed a bridge; the only thing that had to change was where the tool list comes from. |
pi has no MCP client, and OpenCode has no declarative hook configuration -- verified against OpenCode's own plugin documentation, which states hooks are available only through JavaScript/TypeScript plugin modules. For those two clients a module is the only extension point. We do not want to ship such a module as a repository asset. DeusData#534 proposed one embedding 380 lines of TypeScript as C string literals and registering 7 of the 15 registry tools; every tool added afterwards would have been silently missing for that client. Hand-maintained copies of the tool list have already produced defects here (DeusData#1361, and the smoke-invariants count). So the module is GENERATED from the live registry instead. cbm_client_adapter_pi walks cbm_mcp_tool_count()/cbm_mcp_tool_name() and registers every tool, which makes drift structurally impossible rather than merely discouraged: adding a tool to TOOLS[] adds it to every generated adapter with no second edit. Nothing in the repository is a .ts file. Both emitters wrap their output in ownership markers so a caller can rewrite its own block and leave a user-authored file alone. Two defects from the prior proposals are closed by construction: - Path escaping. DeusData#616's template rejected the double quote but not the backslash, so a Windows home like C:\Users\urs\bin produced an invalid unicode escape and the whole auto-loaded plugin failed to parse, which is worse than an absent plugin. cbm_client_adapter_escape_js escapes backslash, quote, newline and CR, and fails closed rather than emitting a truncated literal; generation aborts if the path cannot be escaped. - The silent no-op. DeusData#616's payload omitted hook_event_name, which hook-augment requires and without which it accepts nothing, so the plugin emitted zero bytes for six weeks. The generated payload carries it and a test pins it. Recorded honestly: the OpenCode emitter hooks tool.execute.after, whose ability to modify a tool's output is NOT part of OpenCode's documented plugin contract (only tool.execute.before's argument mutation is). If they change it the augmentation stops with no error. That risk is accepted deliberately and the emitter says so in a comment, so a future reader does not have to rediscover it. This commit adds the generator and its tests only; wiring into the install and uninstall routines follows separately so the two are reviewable apart. Tests: every registry tool appears in the pi module (revert-checked by simulating the 7-of-15 subset, which fails); Windows/quote/newline escaping and its truncation boundary; the OpenCode payload carries hook_event_name and registers no tools; NULL/empty binary paths generate nothing. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Wires the registry-driven generators into install, plan and uninstall. pi gets ~/.pi/agent/extensions/cbmem.ts, its only route to the graph -- pi has no MCP client. OpenCode gets ~/.config/opencode/plugins/cbm-augment.ts, which adds no tools (it already reaches all of them over MCP) and supplies only the automatic graph lookup before a grep/glob that other clients get from their own hook configuration. OpenCode has none; its plugin system is the only extension point, verified against their plugin documentation. Both go in as a MARKED BLOCK rather than a whole-file write. Those directories are auto-loaded, so a user may legitimately keep their own module there, and an install routine that clobbers it is destroying user content. Uninstall removes only our block and leaves the rest of the file, matching every other uninstall path here. A generator returning NULL is a hard error rather than a skip. A silently absent extension is precisely the failure that left the earlier proposal a no-op for six weeks, so it must surface as an install error instead of a quiet success. Note for reviewers: the generated body deliberately carries NO ownership markers. cbm_text_upsert_managed_block adds them itself and rejects content that already contains them -- an existing guard test (test_cli.c:6018) caught that integration mistake before CI did, which is the test doing exactly its job. Reimplements the ideas from two community PRs, both closed as superseded by this work, with credit to their authors: - DeusData#534 (@Tensorboyalive) proposed the pi extension. It embedded 380 lines of TypeScript as C string literals and registered 7 of the 15 registry tools; this generates from the registry so the surface cannot drift, and ships no .ts in the repository. Their marker-delimited editing, their uninstall test asserting the markers are GONE, and their defensive stdout parsing (take the last line that parses, not the first) are all carried forward here. - DeusData#616 (@ZeR020) proposed the OpenCode plugin. Its payload omitted hook_event_name, which hook-augment requires, so it produced zero bytes; and its path template rejected the double quote but not the backslash, so a Windows home like C:\Users\urs\bin produced an invalid unicode escape and the auto-loaded plugin failed to parse. Both are closed by construction here and pinned by tests. Their hook-point research is what made the OpenCode side possible at all. Tests: install writes both extensions and the dry run does not; uninstall removes our block while preserving a user's own content in the same file. Co-Authored-By: Tensorboyalive <Tensorboyalive@users.noreply.github.com> Co-Authored-By: ZeR020 <ZeR020@users.noreply.github.com> Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Add pi (
@earendil-works/pi-coding-agent) to the installerinstall/uninstallauto-detect and configure 11 agents today, but notpi. This adds pi as a
first-class target.
Why pi is wired differently (no
mcpServersentry)pi has no native MCP. Its README states: "No MCP. Build CLI tools with
READMEs (Skills), or build an extension that adds MCP support." So instead of an
MCP server entry, the pi adapter installs the three things pi actually consumes,
all driven by the existing CLI mode (
codebase-memory-mcp cli <tool> '<json>'):~/.pi/agent/extensions/cbmem.ts— registerscbm_search_graph,cbm_trace_path,cbm_get_architecture,cbm_semantic_query,cbm_detect_changes,cbm_query_graph,cbm_index,cbm_list_projectsas native pi tools that shell out to the binary;codebase-memoryskill →~/.config/agents/skills/codebase-memory/;cbm_upsert_instructionssentinel upsert.Changes
cbm_detected_agents_tgainsbool pi;cbm_detect_agentssets it.cbm_pi_config_dirhonors$PI_CODING_AGENT_DIR, falls back to~/.pi/agent(mirrors how
cbm_claude_config_dirhonors$CLAUDE_CONFIG_DIR).install_pi_config/uninstall_pi: write/remove the extension + skill +AGENTS.md block; respect
--forceand--dry-run; recorded ininstall --plan.pirow.tests/test_cli.c): detection (incl.$PI_CODING_AGENT_DIR),install writes extension/skill/AGENTS, uninstall reverses it.
Validation
-Wall -Wextra -Werror).install --plan→install→uninstalllifecycle verified against a fakeHOME: extension/skill/AGENTS written then fully removed (sentinel gone).
(malloc/
read_file_str/write_file_str, freed on every path).No existing agent behavior is changed; this is purely additive.