Conversation
🌱 graft blast radius3 areas changed → 6 areas can be affected. 18 dependent symbols, depth 2. flowchart TB
A0(("Host Integration<br/>6 symbols"))
A1(("CLI Wiring<br/>6 symbols"))
A2(("Claude Initialization<br/>2 symbols"))
A3(("Brain Connection<br/>2 symbols"))
A4(("MCP Server<br/>1 symbol"))
AX(("1 smaller area<br/>1 symbol"))
classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
class A0,A1,A2,A3,A4 reached;
classDef tail fill:#EEF2F3,stroke:#9AA4A9,stroke-width:1px,color:#3A4247;
class AX tail;
Who knows this code — 2 people across 9 areas
Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no All 18 dependent symbols, grouped by areaHost Integration — 6 symbols in 6 files
CLI Wiring — 6 symbols in 4 files
Claude Initialization — 2 symbols in 2 files
Brain Connection — 2 symbols in 2 files
MCP Server — 1 symbol in 1 file
Event Tracking — 1 symbol in 1 file
Test signal per changed area — 1 ✓ · 1 ✗ · 1 –Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.
9 test suites also reference this code11 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.
Open the interactive graph → — click an area to see its dependent symbols at file:line. |
pi and omp already get two of graft's four channels for free: both load AGENTS.md natively (the `agents` host covers orientation), and both read project .mcp.json (omp natively, pi via pi-mcp-adapter). What was missing is per-prompt retrieval and the statusline - both fit the extension API the two hosts share (omp's hook runner loads .omp/hooks/pre/*.ts factories, pi auto-discovers .pi/extensions/*.ts). - src/pi/graft-extension.ts: the extension. before_agent_start runs "graft ask --json --source" on the prompt and injects a pointers-only pack gated on the same floors as relevantRetrieval() (STRONG 0.1 / HIGH 0.5, nudges capped at 2, injected pointers never repeat); tool_result on edit/write flips a stale flag whose note ships on the next turn; turn_end paints the statusline bar (stats.json preferred, wiring.json fallback - same posture as resolveStats()) via ctx.ui.setStatus. No-ops outside graft/ repos; never blocks a turn on a slow or missing graft. - src/hosts/pi-omp.ts: wiring + targets + detection (~/.pi, ~/.omp), reusing writeOwned so re-init reports 'unchanged' instead of overwriting. The extension installs BOTH ways, mirroring how the other hosts already split it: repo-local (.pi/extensions + .omp/hooks/pre, the Cursor posture - --no-global keeps it) and user-level in the agent dir ($PI_CODING_AGENT_DIR, else ~/.pi/agent / ~/.omp/agent - the Codex/Claude posture, --no-global suppresses it). The user-level copy governs every project safely because the extension itself no-ops in a project without graft/INDEX.md; per-project state lives under that project's graft/, so sessions never cross. - registry: pi and omp entries render AGENTS.md via the existing instructionBody. - telemetry: 'pi' | 'omp' added to the AgentHost enum - the allowlist stays closed. - init reports every write as ConfigWrite so --dry-run, the picker, and the actual run can't drift apart. Verified end-to-end: pi 0.85.1 and omp 18.2.0 in a clean repo - packs inject, savedTokens accrues per project, statusline renders; full suite green (1223 pass, 0 fail). Fixes trailhq#402
|
Pushed an update: the extension now installs both repo-locally and user-level (mirroring how Codex/Claude Code already split it):
The user-level copy governs every project safely because the extension gates on |
cb47c37 to
0ed89e9
Compare
Self-hosting, same as the .claude/ wiring the README documents: the graft:init extension bytes copied into the paths pi (0.85.1) and omp (18.2.0) auto-discover. AGENTS.md carries the instruction section.
What
Adds pi (pi.dev,
@earendil-works/pi-coding-agent) and oh-my-pi / omp as instruction hosts, closing #402.pi and omp already get two of graft's four channels for free:
AGENTS.mdnatively (candidate order verified in pi-coding-agent 0.85.1).mcp.json)pi-mcp-adapteror host-config discoveryHow
pi and omp share one extension API (omp is a pi fork; omp's hook runner loads
.omp/hooks/pre/*.tsfactories, pi auto-discovers.pi/extensions/*.ts). One extension file serves both hosts:src/pi/graft-extension.ts(new, single source of truth —graft initcopies it to both repo-local paths):before_agent_start(≈ Claude'sUserPromptSubmit): runsgraft ask --json --sourceon the prompt (8s budget, never blocks the turn) and injects a pointers-only pack behind the same gates as graft's ownrelevantRetrieval()—STRONG_FLOOR 0.1/HIGH_FLOOR 0.5on lexical coverage, weak matches get at most 2 nudges per session, and an injected pointer is never injected twice (novelty memory capped at 40).tool_resulton edit/write: flips a stale flag; the[graft] graph is stale — run graft buildnote ships on the next turn's injection rather than clobbering the tool result. Same one-line posture as the Claude PostToolUse hook.turn_end→ctx.ui.setStatus("graft", …): the statusline bar —graft · N nodes / M edges · ✓ synced · ~X tok saved— preferringgraft/.cache/stats.jsonand falling back tograft/.graph/wiring.json, the same posture asresolveStats()in the Claude statusline.baselineChars/4 − pack, never the full baseline; a no-op outsidegraft/repos.src/hosts/pi-omp.ts(new): targets, detection (~/.pi,~/.omp, mirroring the other CLIs' config-dir probes), and the install, which reuseswriteOwnedso a re-init reportsunchangedinstead of overwriting. Two copies, no symlinks: omp's scanner reads only.omp/hooks/pre/, pi only.pi/extensions/, and symlinks break repo packaging.Registry:
piandompentries renderAGENTS.mdvia the existinginstructionBody— no new instruction format.Telemetry:
'pi' | 'omp'added to theAgentHostenum. The allowlist stays closed — without this, host attribution for the new surfaces would have to ship a free-form string, which the contract exists to prevent.Init: the extension writes land as
ConfigWritein the hooks result, so--dry-run, the picker, and the actual run can't drift apart (the plan-vs-run consistency test enforces it).Verification
npm test),tsc --noEmitclean.graft init --no-build --agents pi --agents omp: AGENTS.md section + both extension paths written;pi -p(0.85.1) andomp -p --no-session(18.2.0) both inject correct packs,savedTokensaccrues in the session file, and the statusline renders from thewiring.jsonfallback.spawnSync("graft", …)plus local reads, nothing else.Closes #402