HDR

Integrations

Harnesses

How HDR plugs into Claude Code, Codex, opencode and pi, and what each one can and cannot enforce.

Every harness runs the same checker, hdr hook, which reads one event as JSON on standard input and answers in Claude Code’s hook format. Claude Code and Codex call it directly as a command hook. opencode and pi load code in-process instead, so HDR installs a small generated plugin that converts their events, runs the checker, and turns its answer into that harness’s way of blocking.

At a glance

HarnessInstalled asProject locationBlocks tool callsFailing Stop check
Claude CodeCommand hooks in settings.claude/settings.jsonyesAgent is asked to continue once
CodexCommand hooks.codex/hooks.jsonyes, see status belowAgent is asked to continue once
opencodeGenerated plugin.opencode/plugins/hdr.tsyesWarning only
piGenerated extension.pi/extensions/hdr.tsyesOne follow-up message, then control returns

User-level installs go to ~/.claude/settings.json, $CODEX_HOME/hooks.json, $XDG_CONFIG_HOME/opencode/plugins/hdr.ts and $PI_CODING_AGENT_DIR/extensions/hdr.ts, each falling back to the harness’s usual home directory.

How file tools are recognised

The append-only guard needs to know when a tool call edits a file. Each harness’s native tools are mapped to the same two operations:

HarnessTreated as an editTreated as a write
Claude CodeEdit, MultiEdit, NotebookEditWrite
Codexapply_patch update, delete or moveapply_patch add
opencodeedit, apply_patch update, delete or movewrite, apply_patch add
pieditwrite

Shell tools are not parsed. Changes made through a shell are caught afterwards by the session baseline, as described in Decision history. tool_denied decisions use each harness’s own tool names, so a rule for Claude’s Bash does not cover pi’s bash.

Claude Code

hdr install claude --project

The reference integration. Hooks are added to the settings file for SessionStart, PreToolUse, PostToolUse and Stop, keeping any existing settings and a one-time .hdr-backup copy. HDR also reads the user, project and local settings and flags disableAllHooks and a configured bypassPermissions mode. A session running in bypass mode has every tool call denied.

Codex

hdr install codex --project

Codex uses the same hook protocol and event names as Claude Code, so the integration is the same command written to .codex/hooks.json. apply_patch envelopes are parsed so each file they touch is checked.

Status: not yet verified live

The installer and payload handling are covered by tests, but enforcement has not yet been confirmed in a real Codex session. Codex loads project hooks only when the project is trusted, and skips any non-managed hook until someone approves it in /hooks. Approval is tied to the hook’s hash, so reinstalling HDR asks for approval again. hdr check can only see that the hooks are on disk, not whether Codex approved them.

Hosted tools such as web search do not trigger Codex hooks.

opencode

hdr install opencode --project

HDR writes a plugin to .opencode/plugins/hdr.ts. It uses tool.execute.before to block a call by throwing the HDR reason, tool.execute.after to append feedback to the tool’s output, and session events for start and stop.

  • A failing Stop check only shows a warning toast: a plugin cannot make an idle session continue.
  • At the time of writing, opencode does not run tool.execute.before for tool calls made by subagents (opencode#5894). Those calls are only caught afterwards by the session baseline.

pi

hdr install pi --project

HDR writes an extension to .pi/extensions/hdr.ts. It blocks through tool_call, appends feedback to tool results through tool_result, gives the agent the session-start context through before_agent_start, and checks Stop through agent_end.

  • pi loads project extensions only after you trust the project. Non-interactive runs need --approve or a saved trust decision.
  • A failing Stop check sends one follow-up message asking for a correction, then returns control, mirroring Claude Code’s behaviour.

Plugins fail closed

The opencode and pi plugins deny the tool call if the checker cannot run at all, for example if Python is missing or the hook times out. HDR counts a plugin as installed only while it matches the generated file byte for byte, so a hand-edited plugin is reported as missing. Reinstall rather than edit it.