Skip to content

feat(pi): ctx setup pi - Pi coding-agent CLI integration - #161

Open
bilersan wants to merge 10 commits into
ActiveMemory:mainfrom
bilersan:feat/pi-cli-integration
Open

feat(pi): ctx setup pi - Pi coding-agent CLI integration#161
bilersan wants to merge 10 commits into
ActiveMemory:mainfrom
bilersan:feat/pi-cli-integration

Conversation

@bilersan

Copy link
Copy Markdown
Contributor

feat(pi): ctx setup pi — Pi coding-agent CLI integration

New AI-tool integration: ctx setup pi [--write] for the Pi coding-agent CLI
(earendil-works/pi, pi.dev). Pi has no built-in MCP by design, so the
integration is a thin TypeScript extension + Agent-Skills-standard skills +
AGENTS.md; all logic stays in the ctx binary via ctx system subcommands.

Spec: specs/pi-cli-integration.md (committed on-branch).

What ships

  • Embedded extensioninternal/assets/integrations/pi/extension/ctx.ts:
    flat .pi/extensions/ctx.ts shim wiring Pi lifecycle events to ctx system:
    • session_start: warm-up ctx agent --budget 4000 off the prompt path
    • before_agent_start: injects the packet as a persistent
      customType: "ctx-context" message (display: true) only when no ctx
      message exists in the live context — compaction-aware scan of
      sessionManager.buildContextEntries() (summary + kept tail +
      post-compaction entries), so kept-tail packets suppress re-injection and
      folded-away packets trigger it; fail-safe across /new /resume /fork
      /reload
    • tool_result (bash, git commit regex, !isError): ctx system post-commit
      with the hook-JSON envelope on piped+closed stdin (node:child_process
      is mandatory — Pi's exec helper hardcodes stdio: ["ignore","pipe","pipe"])
    • tool_result (edit/write, !isError): check-task-completion;
      agent_settled: check-persistence; session_compact: cache drop
  • ctx setup pi — deploy core (internal/cli/setup/core/pi/) mirroring
    the OpenCode integration: extension fatal, AGENTS.md + 10 bundled skills
    warn; refresh-in-place, symlink/non-regular refusal; dry-run prints the
    hook.pi blueprint; case cfgHook.ToolPi in setup root (no new
    subcommand, no new Use* constant)
  • CI gatetools/typecheck/pi/ (strict tsc --noEmit against
    @earendil-works/pi-coding-agent@0.84.2 types, committed lockfile) +
    typecheck-pi-extension job in .github/workflows/ci.yml
  • Docs — Pi entry in supported-tools reference, docs/home/pi.md
    quickstart, multi-tool recipe, zensical nav

Validation

  • go build ./..., golangci-lint (0 issues), targeted go test green
  • Full-suite delta vs clean main: zero new failures (all failing packages
    pre-exist on this Windows machine: CRLF render drift, .exe exec, audit
    path exemptions)
  • Scratch project: init + dry-run + --write + idempotent re-run +
    tamper-refresh (byte-identical to embedded)
  • Live pi: extension loads clean (A/B run with/without .pi identical);
    full LLM round-trip blocked by local pi provider config — left for a
    working-provider machine
  • tsc --noEmit passes against pi-coding-agent 0.84.2 types

Reviews

  • Independent sub-agent review: approve-with-nits — all nits addressed
    (32c55041: docstring, frontmatter coverage, spec naming)
  • claude -p fable-5 effort-max branch audit: ship-ready after one
    decision
    — the one decision (kept-tail duplicate-injection gap, MAJOR 1)
    resolved by switching the predicate to a buildContextEntries() scan
    (694f6d37, with a one-line stdin stream hardening)

Notes / follow-ups

  • site/ HTML rebuild deferred: this machine's zensical toolchain renders
    entity-escape drift vs the committed artifacts; rebuild on the canonical
    build machine (documented in PI.5 / commit 03a7e776)
  • TASKS.md PI.8 tracks the remaining low-priority audit follow-ups
    (post-compaction re-warm location, embedded-asset EOL pinning, unused
    tool_result nudge channel, tsconfig paths hardening)

PI.1 of specs/pi-cli-integration.md: the thin shim extension (the
.pi/extensions/ctx.ts deploy source) wiring Pi lifecycle events to
ctx system subcommands - hook-JSON envelope on piped stdin, isError
gating, session_start warm-up cache, branch-scan context re-injection
after compaction - plus the 10-skill bundled set copied from the
OpenCode integration (frontmatter conforms to the Agent Skills
standard; no harness-specific content inside the skills).

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
PI.2-PI.4 of specs/pi-cli-integration.md: the ctx setup pi surface.
internal/cli/setup/core/pi implements the deploy contract mirroring
the OpenCode integration (extension fatal, AGENTS.md + skills warn;
validateManagedTarget refuses symlink and non-regular targets; refresh-
in-place on drift):

- PI.2: PiExtension/PiSkills accessors (internal/assets/read/agent/pi.go),
  config/hook pi path constants (.pi/extensions/ctx.ts, .pi/skills),
  asset.go directory constants, embed.go directives.
- PI.3: core/pi package - pi.go Deploy (extension, AGENTS.md, skills,
  summary), extension.go, skill.go, validate.go, plus the deploy test
  suite (refresh, symlink refusal, non-regular target refusal;
  windows-skipped where needed).
- PI.4: case cfgHook.ToolPi in setup root Run() - dry-run prints the
  hook.pi blueprint, --write deploys; hooks.yaml hook.pi + supported-tools
  entry, write.yaml write.hook-pi-created/skipped/summary, config/embed/text
  DescKeys, write/setup InfoPi* (TestDescKeyYAMLLinkage green; no new Use*
  constant, no new subcommand).

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
PI.5 of specs/pi-cli-integration.md: pi entries in the supported-tools
reference (operations/integrations.md + cli/setup.md), a pi quickstart
(docs/home/pi.md, registered in zensical.toml nav), and the
multi-tool-setup recipe.

site/ HTML rebuild is deferred: this machine's zensical 0.0.51 venv renders
entity-escape drift (120 files, &ActiveMemory#39; vs literal ') against the committed
artifacts - unpinned zensical transitive deps make the build
non-reproducible off the canonical toolchain. Rebuild on the canonical
machine and stage site/ with a follow-up commit.

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
PI.7 of specs/pi-cli-integration.md: tools/typecheck/pi/ (tsconfig strict
noEmit over the embedded ctx.ts, devDeps on @earendil-works/pi-coding-agent
types + @types/node + typescript, committed lockfile, README) and the
typecheck-pi-extension CI job mirroring typecheck-opencode-plugin.
Verified locally: tsc --noEmit passes against pi-coding-agent 0.84.2.

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
Validation summary: go build + golangci-lint clean; full-suite delta vs
clean main shows zero new failures (all failing packages pre-exist on
this Windows machine: CRLF render drift, .exe exec, audit path
exemptions). Scratch project verified: init, dry-run, --write,
idempotent re-run, tamper refresh (byte-identical to embedded source),
unknown-tool listing. Live pi: extension loads clean (A/B run with and
without .pi identical); LLM round-trip blocked by local pi provider
config - left for a working-provider machine.

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
… rebuild drift)

Both surfaced during the pi integration: the Windows schannel/openssl
git TLS quirk and the non-reproducible zensical site rebuild (escape-
churn detection procedure).

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
…naming

Independent review (approve-with-nits) follow-ups:
- setup root package doc: add opencode + pi to the tool list
  (spec 'Files to create' required the docstring update)
- TestSkillFrontmatter: add the pi skill tree so the Agent Skills
  frontmatter contract is validated for the pi set
- new deploy tests: fresh-project full deploy (all 12 files,
  byte-identical to embedded; AGENTS.md marker) and deterministic
  skill ordering (deploy output sequence must be lexicographic)
- spec: FilePiExtension -> FilePiExtensionDeploy (matches the shipped
  constant; the FileOpenCodePluginDeploy convention)
- read/agent/pi.go: document the deliberate file-split deviation from
  agent.go's layout

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
fable-5 effort-max branch audit (0 blocker, 1 major, 4 minor, 3 nit):

- MAJOR 1: needsContextInjection scanned only branch entries after the last
  compaction, missing ctx packets that survive into the kept tail (a
  custom_message is a valid cut point, kept within keepRecentTokens) and
  causing the duplicate injection the spec explicitly rules out. Now scans
  sessionManager.buildContextEntries() - the compaction-aware live context
  (summary + kept tail + post-compaction entries); strictly smaller function,
  delivers the spec's no-duplicate guarantee. Spec updated to match (event
  map row, compaction interop paragraph, files-to-create comment).
- MINOR 2: child.stdin now swallows async 'error' emissions (EPIPE when the
  child exits before the write flushes, destroyed-stream writes on a failed
  spawn) so they cannot escape Pi's handler stack; spawn/exec failures still
  surface via the execFile callback.
- TASKS.md PI.8: follow-up for the remaining minor/nit findings (post-
  compaction inline re-warm, EOL pinning for embedded assets, unused
  tool_result nudge channel, tsconfig paths hardening). Audit NIT 6 (summary
  omits persistence behavior) was a false positive - all five behaviors are
  listed in write.hook-pi-summary.

Verified: tsc --noEmit (strict, pi-coding-agent 0.84.2 types), go build,
pi deploy suite + assets/read/skill green; remaining suite failures are
pre-existing on clean main.

Spec: specs/pi-cli-integration.md

Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
@bilersan
bilersan requested a review from josealekhine as a code owner August 23, 2026 19:31
@josealekhine

Copy link
Copy Markdown
Member

@bilersan looks good; give some fresh cookies to your minions.

Some nits:

Minor

  1. Stale re-injection predicate in user docsdocs/home/pi.md:85 and
    docs/operations/integrations.md:643 still say the packet is injected
    "when no ctx-injected message exists after the most recent
    compaction
    ". Commit 694f6d37 replaced that predicate with the
    live-context scan precisely because it caused duplicate injection when a
    packet survived into the kept tail; the docs now describe behavior the
    code deliberately no longer has. Suggest reusing the spec's wording:
    "when no ctx message exists in the live context (summary + kept tail +
    post-compaction entries)".

Nit

  1. Spec latency claim overstates the warm-up guarantee
    specs/pi-cli-integration.md:129-131 says "the prompt path never waits
    on ctx agent". On a cache miss — a first prompt that outruns the
    session_start warm-up, or the first prompt after compaction (the
    session_compact handler drops the cache) — before_agent_start awaits
    fetchPacket on the prompt path, bounded by the 15s timeout. PI.8
    already tracks the post-compaction re-warm; soften the spec sentence to
    match ("the first prompt normally injects from cache").

  2. "Plugin hook" terminology in the Pi skill tree
    pi/skills/ctx-agent/SKILL.md:16 says "The plugin hook already runs
    ctx agent on session start". Pi has extensions, not plugins; the
    wording is inherited byte-for-byte from the OpenCode tree. Since the
    trees are intentionally identical, fix it neutrally in both (e.g. "the
    tool integration already runs…") or accept it.

  3. No drift guard on the intentionally-identical skill trees — the
    pi/opencode skill trees and the skill.go/validate.go deploy files are
    exact mirrors, but nothing enforces that. A cheap byte-identity test (or
    an eventual shared deploy helper — this is now the second full copy)
    would freeze the mirror; without it the trees will drift silently the
    first time someone edits one side. Candidate for PI.8.

  4. Caret range in the typecheck gate's package.json
    tools/typecheck/pi/package.json:9 declares
    "@earendil-works/pi-coding-agent": "^0.84.2" while the PR, README, and
    extension header all speak of pinning 0.84.2. npm ci + the committed
    lockfile pin CI today, but any lockfile regeneration silently floats to
    the newest 0.x minor. An exact 0.84.2 makes the gate's intent
    self-enforcing.

Brings the Codex integration (ActiveMemory#163), the hook robustness sweep, and
the dependabot bumps under the Pi branch. Every conflict was a shared
insertion point where both branches add a tool: the setup dispatch
imports and docstring, the write/setup Info* helpers, the skill
frontmatter test comment, the setup docs table and examples, and the
TASKS/LEARNINGS context files. Both sides kept; Pi placed after Codex
so the branch diff against main stays a pure addition.

Spec: specs/pi-cli-integration.md
Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
…r guard

Maintainer nits on PR ActiveMemory#161 (2026-09-05):

- docs: the re-injection predicate in the Pi quickstart and the
  integrations reference now describes the live-context scan that
  694f6d3 shipped (summary + kept tail + post-compaction entries)
  instead of the retired "after the most recent compaction" rule
- spec: soften the latency claim; the first prompt normally injects
  from cache, and a cache miss (first prompt outrunning the warm-up,
  or the first prompt after session_compact drops the cache) runs
  the fetch on the prompt path, bounded by the 15s subprocess timeout
- skills: "plugin hook" -> "tool integration" in ctx-agent, in both
  the Pi and OpenCode trees (Pi has extensions, not plugins)
- test: TestPiSkillsMirrorOpenCode freezes the intentionally
  identical Pi/OpenCode skill trees byte-for-byte
- typecheck: pin @earendil-works/pi-coding-agent to exactly 0.84.2 in
  package.json and the lockfile root so a lockfile regeneration cannot
  float the gate

Independent review findings folded in:

- DECISIONS.md records the three Pi design choices the spec cites as
  decided 2026-08-23 (display:true, live-context predicate, flat
  single-file extension)
- flag-era wording ("flag loss can only cause an extra injection")
  replaced in the spec and the extension header: only the packet
  cache lives in extension memory; the injection decision is derived
  from live session state
- spec: tests bullet names the real guards; line-count estimate
  dropped. Quickstart: compaction sentence made conditional. README:
  integrations row lists Pi
- tasks: PI.9 records this round; PI.1 wording updated; PI.8 gains
  (a) cache-the-promise note and (e) shared deploy helper for the
  pi/opencode twins
- learnings: golangci-lint exits 7 with 0 issues while
  tools/typecheck/*/node_modules exists on this machine
- chore (bundled): refresh go.work.sum with the grpc v1.83.1 sums the
  go 1.26.3 workspace load adds after main's 1.83.2 bump; a clean
  upstream/main build leaves the same two-line drift

Spec: specs/pi-cli-integration.md
Signed-off-by: Ersan Bilik <ersanbilik@gmail.com>
@bilersan

bilersan commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the read. Two commits since your comment:

  • 9520e17 merges main (Codex + hook sweep + dependabot). All seven conflicts were the shared tool insertion points; both sides kept, Pi placed after Codex so the branch diff stays a pure addition.
  • 6288c22 addresses the nits:
  1. Docs predicatedocs/home/pi.md and the Pi section of docs/operations/integrations.md now use the spec wording: injected "when no ctx message exists in the live context (summary + kept tail + post-compaction entries)". Grep for the old "after the most recent compaction" phrase is empty.
  2. Spec latency — softened to "the first prompt normally injects from cache", with the cache-miss path spelled out (first prompt outrunning the warm-up, or first prompt after session_compact drops the cache) and the 15s subprocess bound. The session_compact re-warm stays in PI.8(a), now with a note to cache the fetch promise so a cold-start prompt can't spawn a second ctx agent.
  3. "Plugin hook" — now "The tool integration already runs ctx agent on session start" in both the Pi and OpenCode ctx-agent skills.
  4. Drift guardinternal/assets/pi_test.go / TestPiSkillsMirrorOpenCode: walks both embedded trees, byte-equal in both directions, fails naming the file (verified by mutating one skill). The shared deploy helper is parked as PI.8(e); skill.go/validate.go differ only by package name and constants today.
  5. Exact pin"@earendil-works/pi-coding-agent": "0.84.2" in package.json and the lockfile root entry; npm ci + tsc --noEmit green.

Also folded in from an independent review pass: three DECISIONS.md entries for the choices the spec cited as "decided 2026-08-23" (display:true, live-context predicate, flat single-file extension); flag-era "flag loss can only cause an extra injection" wording replaced in the spec and the extension header comment (comment-only, typecheck green); README integrations row lists Pi.

One bundled chore: go.work.sum +2 lines (grpc v1.83.1 sums) — a clean main build on go 1.26.3 leaves the same drift. Happy to drop it from the branch if you'd rather take it separately.

Still pending: the live Pi round-trip (spec verification step 3) needs a machine with a working Pi provider; PI.6 records the block. Locally on Windows the full suite shows the same 28 pre-existing failing packages as clean main, zero new; lint 0 issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants