feat(pi): add a memsearch plugin for the pi coding agent - #650
Open
jenkins5 wants to merge 14 commits into
Open
Conversation
- add plugins/pi with a session_start hook that ensures the onnx default config and runs an initial background index - copy derive-collection.sh byte-identical from claude-code so pi shares collections with the other agent plugins - add package.json declaring the pi extension and skill resources
- register memory_search and memory_get tools wrapping the memsearch CLI - inject recent daily journals into the system prompt via before_agent_start - add a memory-recall skill guiding the search-then-expand flow - extract journal preview helpers into context.ts and memoize the CLI and collection lookups
- add a `pi` field to PluginsConfig plus entries in both plugin key/field maps, so `--plugin pi` resolves its config instead of raising KeyError - mention pi in the two `--plugin` help strings - sync shared prompt templates into plugins/pi
- capture the settled turn via agent_settled, summarize it, and append to today's journal with a session anchor - fall back from the memsearch-managed LLM to `pi -p`, then to truncated raw text - invoke pi through the running process's node binary and entry script, since the PATH entry is a Volta shim that fails outside a project install - close child stdin when nothing is piped, otherwise `pi -p` waits for an EOF that never arrives - add MEMSEARCH_DEBUG to surface failures that the best-effort catch blocks swallow
- add transcript.py, which resolves one root-to-leaf branch via id/parentId so abandoned branches are excluded from pi's tree-structured sessions - register the memory_transcript tool wrapping it - record the session leaf id as `turn:` in the journal anchor so drill-down can target the exact tree position - extend the memory-recall skill to describe all three layers
- add memory-config covering diagnosis, plugins.pi.* keys, provider routing, prompt overrides, and troubleshooting - add memory-to-skill covering candidate capture, review, install, and history mining - point history mining at memory_transcript, since pi stores transcripts as JSONL rather than in a database - document pi's own skill discovery paths for install targets
- reset the session heading flag and last-captured leaf on session_start, since /new, /resume and /fork reuse the same process and would otherwise file new turns under the previous session's heading - skip capture when the leaf is unchanged, so an agent_settled that fires without a new exchange does not append the turn twice - serialize captures through a queue, because summarizing takes long enough for the next turn to settle mid-write and interleave the journal
- resolve plugins.pi.summarize.provider once per session and only shell out to `memsearch summarize` when it names a real provider - an unset or "native" provider means the host agent summarizes, so the previous unconditional attempt paid a CLI start-up on every capture just to fail
- add plugins/pi/README.md and docs/platforms/pi/ covering install, architecture, and the three memory tools - add a pi job to the test workflow, mirroring opencode plus a transcript.py syntax check - add unit tests for turn extraction, journal previews, and noise filtering - register pi across the mkdocs nav, root README, platform comparison, CONTRIBUTING, and the version table - note that project-scope installs stay inert until the project is trusted, which surprised us during testing
- pi's git source resolves whole repositories, not subdirectories, so `git:…#plugins/pi` fails to clone; document clone-then-local-path instead - note that a local-path install records the path rather than copying, so `git pull` updates the plugin in place
- derive the collection from the repository root rather than the working directory; starting pi in a subdirectory produced a different collection than the other plugins, silently breaking cross-agent sharing - honor MEMSEARCH_DIR as a global scope, matching the claude-code and codex plugins; the memory-config skill already documented it while the code ignored it - resolve scope once per working directory, since the git lookup spawns a process
The pi plugin documented plugins.pi.project_review / user_profile / memory_to_skill but shipped no runner, so enabling them did nothing. Add a pi branch to the shared maintenance runner and wake it after each capture, the same way the OpenClaw and OpenCode plugins do. Native tasks re-invoke pi in print mode with everything stripped off but the prompt, using the invocation the plugin already resolves for summarization — resolving `pi` from PATH breaks under a version-manager shim. Skill distillation gets no shell here: pi's allowlist is tool-level, so the Claude Code plugin's narrow Bash(memsearch transcript:*) hole has no equivalent, and an unattended run is the wrong place for an unscoped one.
`config init` builds the plugins table from a hardcoded list and save_config overwrites the file, so a user who had configured [plugins.pi] lost it the next time they ran the wizard.
Summarization spawned `pi -p` with no flags, so every captured turn was saved as a real session. `pi -r` filled up with summarizer runs whose first message is the note-taker prompt, crowding out the user's actual conversations. Add the flags each sibling plugin already has an equivalent of (claude-code's --no-session-persistence/--tools "", codex's --ephemeral, opencode's isolated config): --no-session --no-tools --no-skills --no-context-files, shared by summarization and native maintenance. The summarizer had full read/bash/edit/write access it never needed. Extensions stay enabled — custom providers are registered by extensions, so --no-extensions would break summarizing behind a proxy or on a self-hosted deployment. Guard session_start with IS_CHILD_PROCESS instead, which was the one hook still doing work in children.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds pi (earendil-works/pi) as a fifth platform plugin. It follows the existing plugin contract exactly — journals in
<project>/.memsearch/memory/, collection name from the sharedderive-collection.sh, the sharedsummarize.txtprompt, and the sharedmaintenance-runner.py— so memories written from pi are searchable from Claude Code / Codex / OpenCode / OpenClaw and vice versa.plugins/pi/is a TypeScript pi extension (registerTool+ lifecycle hooks), closest in shape to the OpenClaw plugin.memory_search(L1),memory_get(L2),memory_transcript(L3)session_start,before_agent_start,agent_settledmemory-recall,memory-config,memory-to-skilltranscript.py— tree-aware pi JSONL parserpi -p→ truncated raw textproject_review,user_profile,memory_to_skillvia the shared runnerDesign decisions worth a look
Capture on
agent_settled, notagent_end. pi may auto-retry, auto-compact, or drain queued messages afteragent_endfires, which would record the same exchange more than once.agent_settledfires only when pi will not continue on its own. Capture is additionally skipped when the session leaf has not moved, and serialized through a queue so a slow summary can't interleave the journal with the next turn.Tree-aware transcripts. pi sessions are a tree (
id/parentId), and/forkor/cloneput several branches in one file. Reading in line order would interleave abandoned branches with the live conversation, sotranscript.pywalks parents from a target entry to yield exactly one root-to-leaf path. Anchors recordturn:<leafId>to pin the tree position for drill-down.pi is re-invoked through
process.argv[1], not PATH. Under a version manager thepion PATH is a shim that refuses to run when the working directory has no project-local install — the common case. The plugin uses the running process's own node binary and entry script, and hands that invocation to the maintenance runner viaMEMSEARCH_PI_BIN.No shell for maintenance, including skill distillation. pi's
--toolsis a tool-level allowlist, so there is no equivalent of the Claude Code plugin's narrowBash(memsearch transcript:*)hole. Native maintenance therefore runs with--no-tools; distilled skills lose exact-command fidelity, which seemed the better trade for an unattended background run. Happy to revisit if you'd rather match Claude Code's behaviour.No file watcher, matching the OpenCode and OpenClaw plugins — the index is refreshed at session start and after each capture.
Changes outside
plugins/pi/Kept as small as I could. Two are worth calling out:
src/memsearch/cli.py— adds pi to theconfig initwizard.config initbuilds the plugins table from a hardcoded platform list andsave_configoverwrites the file, so without this a user who had configured[plugins.pi]would silently lose it the next time they ran the wizard.plugins/_shared/scripts/maintenance-runner.py— apibranch inrun_native_providerplus the argparse choice. Copies re-synced to all five plugins; the existing byte-equality test now covers pi.The rest is registration and counts:
config.py(PluginsConfig+ key maps),scripts/sync-prompts.sh, README badge and platform lists,docs/platforms/comparison table, mkdocs nav, CONTRIBUTING, CLAUDE.md.Testing
pytest— 287 passed, 7 skippedplugins/pinpm test— 12 passed (turn extraction, cold-start preview, noise filtering)node --checkon both entrypoints, plus a real module import;npm pack --dry-run--platform pi(all three tasks correctly reportdisabledby default), and its native command construction verified against paths containing spacespijob added to.github/workflows/test.ymlNotes
Not published to npm — the README installs from a local clone, because pi's git source resolves whole repositories rather than subdirectories. Version left at
0.1.0; happy to align it with whatever you'd prefer for a first release.