pi-warden runs inside Pi today. This issue tracks running its guards in Claude Code, Codex and OpenCode as well. Pi stays the main host and the product keeps its name.
Approach
- A layer that does not depend on Pi. Most guards are already plain functions (
evaluateRules, evaluateDone, evaluateStuck, evaluateAction, ...). Add a normalized event type (session start, prompt, after tool, end of turn), a decide(event) that calls the existing guards, and one small adapter per host that turns the host's hook payload into an event and the decision back into the host's output.
- A CLI:
pi-warden hook <agent> <event> (reads the hook payload on stdin, always exits 0, fails open), pi-warden init <agent> [--project], pi-warden uninstall <agent>.
- State between hook processes: other hosts start a new process per event, and parallel tool calls run hooks at the same time. State lives under
~/.pi/agent/pi-warden/state/<agent>/<session>/, one file per record, created exclusively. No shared file is read and then rewritten.
- One config and one key for every host:
~/.pi/agent/pi-warden/. Consent and key come from PI_WARDEN_ENABLED=1 and the backend key variable on hosts without Pi's UI.
- A stable hook command: the command written into a host's settings never contains a version. Codex trusts hooks by their hash, so a changing command would switch the guards off after every upgrade.
What each host supports
| Guard |
Claude Code |
Codex |
OpenCode |
| Rules |
full (PostToolUse) |
full (apply_patch needs a patch parser) |
full (tool.execute.after) |
| Done-check |
full (Stop with decision: block) |
full |
partial (no stop hook; session.idle plus a new prompt) |
| Code slop, security in written code |
full |
full |
full |
| Stuck, call waste |
full, state in files |
full, state in files |
full |
| Action holds |
partial (deny works; no retry after approval, no plan text) |
partial (ask is not supported, so confirm becomes deny) |
partial (block by throwing) |
| Context saver, output masking |
partial (replacement must match each tool's output shape) |
partial (all-or-nothing replacement) |
full |
| Runaway |
not possible (no stream hook that can abort) |
not possible |
partial |
warden_* tools |
needs an MCP server |
needs an MCP server |
plugin tools |
Suggested order
- CLI skeleton, state store, and the Claude Code adapter with the rules guard and the done-check.
- Codex adapter (payload mapping,
apply_patch parser, a note on re-trusting hooks).
- OpenCode plugin (in-process, own deadlines, continuation on
session.idle).
- Action guard on all three, after measuring how many requests it adds without Pi's sibling batching.
Risks
- A hook that times out does not block the call. Every hook needs its own deadline well under the host's.
- Pi's plan text and message dedupe have no equivalent on the other hosts, so intent checks will be weaker there.
- OpenCode's turn continuation is not documented as a contract, and its
experimental.* hooks may change.
References
pi-warden runs inside Pi today. This issue tracks running its guards in Claude Code, Codex and OpenCode as well. Pi stays the main host and the product keeps its name.
Approach
evaluateRules,evaluateDone,evaluateStuck,evaluateAction, ...). Add a normalized event type (session start, prompt, after tool, end of turn), adecide(event)that calls the existing guards, and one small adapter per host that turns the host's hook payload into an event and the decision back into the host's output.pi-warden hook <agent> <event>(reads the hook payload on stdin, always exits 0, fails open),pi-warden init <agent> [--project],pi-warden uninstall <agent>.~/.pi/agent/pi-warden/state/<agent>/<session>/, one file per record, created exclusively. No shared file is read and then rewritten.~/.pi/agent/pi-warden/. Consent and key come fromPI_WARDEN_ENABLED=1and the backend key variable on hosts without Pi's UI.What each host supports
PostToolUse)apply_patchneeds a patch parser)tool.execute.after)Stopwithdecision: block)session.idleplus a new prompt)askis not supported, so confirm becomes deny)warden_*toolsSuggested order
apply_patchparser, a note on re-trusting hooks).session.idle).Risks
experimental.*hooks may change.References