Skip to content

feat(install): detect and configure pi coding agent - #534

Closed
Tensorboyalive wants to merge 2 commits into
DeusData:mainfrom
Tensorboyalive:feat/pi-adapter
Closed

feat(install): detect and configure pi coding agent#534
Tensorboyalive wants to merge 2 commits into
DeusData:mainfrom
Tensorboyalive:feat/pi-adapter

Conversation

@Tensorboyalive

Copy link
Copy Markdown
Contributor

Add pi (@earendil-works/pi-coding-agent) to the installer

install/uninstall auto-detect and configure 11 agents today, but not
pi. This adds pi as a
first-class target.

Why pi is wired differently (no mcpServers entry)

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>'):

  1. a pi extension~/.pi/agent/extensions/cbmem.ts — registers
    cbm_search_graph, cbm_trace_path, cbm_get_architecture,
    cbm_semantic_query, cbm_detect_changes, cbm_query_graph, cbm_index,
    cbm_list_projects as native pi tools that shell out to the binary;
  2. the codebase-memory skill~/.config/agents/skills/codebase-memory/;
  3. an AGENTS.md graph-first reminder, via the existing
    cbm_upsert_instructions sentinel upsert.

Changes

  • cbm_detected_agents_t gains bool pi; cbm_detect_agents sets it.
  • cbm_pi_config_dir honors $PI_CODING_AGENT_DIR, falls back to ~/.pi/agent
    (mirrors how cbm_claude_config_dir honors $CLAUDE_CONFIG_DIR).
  • install_pi_config / uninstall_pi: write/remove the extension + skill +
    AGENTS.md block; respect --force and --dry-run; recorded in install --plan.
  • README Multi-Agent Support table gains a pi row.
  • Tests (tests/test_cli.c): detection (incl. $PI_CODING_AGENT_DIR),
    install writes extension/skill/AGENTS, uninstall reverses it.

Validation

  • Clean build (-Wall -Wextra -Werror).
  • install --planinstalluninstall lifecycle verified against a fake
    HOME: extension/skill/AGENTS written then fully removed (sentinel gone).
  • Memory handling matches the existing Codex/OpenCode upsert patterns
    (malloc/read_file_str/write_file_str, freed on every path).

No existing agent behavior is changed; this is purely additive.

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
@DeusData

Copy link
Copy Markdown
Owner

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.

@DeusData DeusData added enhancement New feature or request editor/integration Editor compatibility and CLI integration priority/backlog Valuable contribution, lower scheduling urgency; review when maintainer capacity opens. labels Jun 29, 2026
@DeusData DeusData added this to the 0.9.2-rc milestone Jul 8, 2026
@DeusData

Copy link
Copy Markdown
Owner

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 cbm_* tools that shell out to the binary's CLI mode. That is a different and more ambitious shape than any other client surface we carry, and it deserves a real answer rather than being filed alongside the rest.

Security-wise the extension is careful, and I checked the parts that would matter: spawn(BINARY, ["cli", tool, JSON.stringify(args)]) uses array argv with no shell, so there is no command-injection surface; stdio is piped and the abort-signal kill handling is correct; there are no network calls, no filesystem writes and no credential access anywhere in the TypeScript. Config writes are equally careful — AGENTS.md is a sentinel-block merge through the existing cbm_upsert_instructions and uninstall removes only that block, while cbmem.ts and the skill file are created only when absent unless --force. No new cbm dependencies, no build or CI changes.

The thing that must change before this could ever ship. Two shipped files — the extension header comment and the user-facing SKILL.md — contain this line:

"the same graph-first discipline the brain's velocity server enforces over the Obsidian vault"

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 main by a3903ca (12 July), and the write_file_str helper it relies on has since been removed, so the branch no longer compiles against current main. Rebasing it is real work — which is why I do not want you to spend that effort until the maintainer has answered the actual question:

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 CBM_BINARY is an env-controlled exec path. That is normal for a user-owned environment, but it is the extension's only override knob and worth documenting.

Nothing to redo yet — please wait for the direction answer rather than rebasing on spec. I will come back to you either way.

@DeusData

Copy link
Copy Markdown
Owner

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, cbmem.ts exposes seven tools against a registry that currently holds fifteen, so it is a partial, hand-synchronised mirror of TOOLS[] in a second language. Every tool added afterwards would either be hand-copied across or silently missing for pi users. We already have two open bugs of exactly that shape, from other hand-maintained copies of the tool list, and we would rather not create a third.

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. install_pi_durable_context in src/cli/cli.c now writes ~/.pi/agent/AGENTS.md and installs the skill to ~/.pi/agent/skills. That arrived through a framework rebuild, independently, and it means the detection-and-configuration half of your PR has been overtaken. Six weeks in a queue did that, not you.

I went looking for pieces worth salvaging with Co-Authored-By credit, because that is usually the right outcome for a PR in this position. This time I could not find one, and I would rather say so than manufacture something:

  • Marker-delimited AGENTS.md editing — main does this already (CMM_MARKER_START / CMM_MARKER_END at cli.c:3115), and pi's install path already routes through it. Your instinct was right; main simply reached the same answer while you were waiting.
  • The XDG skills path (~/.config/agents/skills) — main installs to ~/.pi/agent/skills. Adopting yours as well would put two copies of one skill in two locations, which is a future bug rather than an improvement.
  • The uninstall path and the tests — good work, but they cover the extension and the paths above, so they travel with whichever of those is kept.

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 fopen() — a repo rule that catches most first contributions. The bridge itself is defensive where it counts: it scans stdout upward for the last parseable JSON line rather than trusting the first, and it honours pi's abort signal by killing the child process. Those are the habits of someone who has debugged this kind of shim before.

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.

@DeusData

Copy link
Copy Markdown
Owner

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 (install_pi_durable_context writes ~/.pi/agent/AGENTS.md and installs the skill), and the cbmem.ts extension — the one genuinely novel piece — is not something we want, because it would be a second, hand-synchronised tool surface in a project whose whole shape is an MCP server. I looked for pieces to salvage with Co-Authored-By credit and could not honestly find one: main already does marker-delimited AGENTS.md editing, and your XDG skills path would have meant two copies of one skill in two places.

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 fopen() in 727 lines. Those are the habits of someone who has debugged this kind of bridge before.

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.

@DeusData

Copy link
Copy Markdown
Owner

Following up because the decision changed, and in your favour.

Your idea is being implemented — #1392, with Co-Authored-By: Tensorboyalive on the commit.

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 .ts file lives in our repository, so the extension is now generated at install time from the live registry.

That change dissolves the objection completely. cbm_client_adapter_pi walks cbm_mcp_tool_count()/cbm_mcp_tool_name() and registers every tool — so pi gets all fifteen instead of a hand-picked subset, and adding a tool to TOOLS[] adds it to pi with no second edit. The drift I was worried about is now structurally impossible rather than merely discouraged.

Three things from your PR are carried forward directly, and they are the reason the implementation went as quickly as it did:

  • Marker-delimited editing, so a user's own module in that auto-loaded directory is never clobbered.
  • Your uninstall test's shape — asserting the markers are gone afterwards, rather than just that the call returned success.
  • Your defensive stdout parse — scanning upward for the last line that parses as JSON rather than trusting the first. A stray log line on stdout would otherwise break the bridge, and that is not an obvious failure to anticipate.

I also kept the install path you chose (~/.pi/agent/extensions/cbmem.ts).

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.

pull Bot pushed a commit to KornaAI/codebase-memory-mcp that referenced this pull request Aug 4, 2026
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>
pull Bot pushed a commit to KornaAI/codebase-memory-mcp that referenced this pull request Aug 4, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

editor/integration Editor compatibility and CLI integration enhancement New feature or request priority/backlog Valuable contribution, lower scheduling urgency; review when maintainer capacity opens.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants