This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
hyperclaude is a Claude Code plugin that pairs Claude (builder) with Codex (critic) via a thin Node script. Claude implements, Codex reviews — that role split is the design invariant. Codex is always invoked with read-only sandbox; never let it author patches.
The plugin is meant to be installed into Claude Code (/plugin install hyperclaude); when you're editing this repo, you're editing the plugin source itself.
Prerequisites: Node 18+, codex-cli >= 0.130.0 on PATH, git. No npm install step — stdlib only.
node --test tests/*.mjs # unit tests (shared fixtures live in tests/helpers/,
# outside the glob). NOTE: `node --test tests/` (dir
# form) does NOT work — it interprets the path as a
# test name and fails.
node --test tests/codex-args.test.mjs --test-name-pattern "<regex>"
# single test by name
bash scripts/test/smoke.sh # acceptance smoke: dry-runs, manifest, hook, codex probes
node scripts/codex-bridge.mjs <mode> --dry-run [flags]
# exercise the bridge without spawning codexBoth test commands must pass before tagging a release. Zero npm dependencies — do not introduce any.
To exercise skill/agent edits live inside Claude Code, symlink the repo into the plugin cache. Use the version from .claude-plugin/plugin.json as the leaf:
version=$(node -e 'console.log(require("./.claude-plugin/plugin.json").version)')
ln -s "$(pwd)" ~/.claude/plugins/cache/hyperclaude/hyperclaude/"$version"Restart Claude Code (or /plugin reload if available) to pick up edits.
scripts/codex-bridge.mjs is the only Codex-spawning code in the plugin. Hook scripts under hooks/ are also executable but pure orchestration — they never spawn codex. The bridge is a CLI entry that owns mode dispatch; leaf modules in scripts/codex/ (args, paths, resume, templates, frontmatter, slug, git, codex, failure) are pure-ish helpers re-exported from the entry file for test access.
Four modes, exposed as positional subcommands. The mode name maps 1:1 to the artifact directory under .hyperclaude/:
| Bridge subcommand | Output dir | Codex invocation |
|---|---|---|
research |
.hyperclaude/research/ |
codex exec --sandbox read-only - (stdin prompt) |
plan-review |
.hyperclaude/plan-reviews/ |
codex exec --sandbox read-only - (stdin prompt) |
code-review |
.hyperclaude/code-reviews/ |
codex exec --sandbox read-only - (stdin prompt) |
docs-review |
.hyperclaude/docs-reviews/ |
codex exec --sandbox read-only - (stdin prompt) |
--resume is supported by plan-review, docs-review, and code-review — not research (deferred; would re-upload context resume is meant to avoid).
Naming note: the bridge's code-review mode (args.mode === 'code-review') is unrelated to the bridge's plan-review mode (args.mode === 'plan-review'). The native codex exec review subcommand is no longer used — fresh code-review is a plain codex exec --sandbox read-only - spawn with the templates/codex/code-review.md prompt, identical in shape to the other fresh modes. (Historical context: code-review used the native exec review subcommand from v0.4 until the 2026-05-18 reversal to a custom prompt — see docs/decisions.md.)
Every Codex spawn must be read-only:
- Fresh
codex exec(research/plan-review/docs-review/code-review) →--sandbox read-onlyflag. Freshcode-reviewis a regularcodex exec --sandbox read-only -spawn (NOT the nativeexec reviewsubcommand); the read-only sandbox still lets Codex run the target git commands the prompt instructs it to. codex exec resume(any mode with--resume) →-c sandbox_mode=read-onlyconfig override (resume doesn't inherit the original session's sandbox; this was empirically verified).
If you add a new spawn path, re-check both argv shapes. New flags must be added to ALLOWED_FLAGS_PER_MODE in scripts/codex/args.mjs; the parser rejects unknown flags per mode and tests cover this.
- Skills (
skills/<name>/SKILL.md) — what Claude reads on the matching trigger. Each skill is one markdown file with YAML frontmatter (name,description). Skills call the bridge viaBashand dispatch agents via theAgenttool. A skill MAY also spawn an agent as a persistent team teammate for stateful multi-turn loops (hyper-plan-loop,hyper-implement-loop,hyper-docs-loop); this uses Claude Code's experimental agent-teams feature (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1).hyper-setupis an invoke-only skill — its frontmatter carriesdisable-model-invocation: trueso it only ever runs on an explicit/hyperclaude:hyper-setup(never auto-triggered by its description); it is a local prerequisite probe that runsscripts/setup-doctor.mjsand spawns no Codex or agents. (Claude Code merged plugincommands/into skills; hyper-setup was the plugin's onecommands/*.mdentry and is now a skill with model-invocation disabled — the invoke-only guard depends on a Claude Code that honors that frontmatter, present since ~v2.1.126.)hyper-memoryis orchestration-only — no Codex, no agent dispatch — it runsscripts/memory/extract.mjsviaBashto extract repo-local knowledge candidates from accumulated.hyperclaude/artifacts to.hyperclaude/memory/candidates/; it joins the non-Codex-spawning set alongside thehyper-setupskill,hyper-recap, and the hooks. - Agents (
agents/<name>.md) — sub-Claude personas with restrictedtools:lists. Used by skills, never the other way around. - Hooks (
hooks/*.mjs, registered inhooks/hooks.json) — two: (1) a SessionStart reminder that injects a workflow-router template —templates/hooks/session-start-reminder.mdby default, or the loop-firstsession-start-reminder-loop.mdwhenCLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1(so the*-loop/hyper-autoskills become the default recommendation only when agent-teams is actually available) — plus an optional.hyperclaude/snapshot footer; (2) a PostToolUse(Write) stamp hook (hooks/stamp-artifact.mjs) that injects aplugin-versionline into every Claude-written.hyperclaude/**/*.mdartifact lacking one — deterministic provenance for plans/epics/research-claude that does NOT depend on the model authoring the line (bridge artifacts already carry it and are written via fs, so the hook skips them). Both are pure orchestration — they never spawn codex. - Templates (
templates/codex/*.md,templates/hooks/*.md) — prompt bodies loaded at runtime with{{UPPERCASE_KEY}}substitution. - Shared protocol references (
references/loop-protocol.md) — plugin-wide reference content loaded by skills' Step 0 alongside their loop-specificfailure-protocol.md. Used byhyper-plan-loop,hyper-implement-loop, andhyper-docs-loop. A second shared reference,references/review-brief.md, carries the--review-briefcomposition rules (source/omission/bound/shell-safety) pointed at byhyper-plan-review,hyper-code-review,hyper-plan-loop, andhyper-implement-loopat their brief-compose step (not Step 0). A third,references/bridge-review-calls.md, carries the shared bridge-invocation contract (stdout JSON envelope + strict-parse rule;--resumesemantics +template-versionprecondition) pointed at by every skill that invokes a review mode of the bridge (plan-review/code-review/docs-review) where it parses the bridge's output or describes resume behavior.
All four modes use a fresh prompt template (code-review uses templates/codex/code-review.md) and bump template-version when changing them; the resumed variants (*-resumed.md) are unversioned in frontmatter.
Template-version: each fresh template (templates/codex/{research,plan-review,code-review,docs-review}.md) declares its template-version in its own leading YAML frontmatter — that's the single source of truth. The bridge reads it via readTemplateWithVersion() and propagates it into the artifact frontmatter; every resumable mode's --resume (plan-review / docs-review / code-review) compares the prior artifact's version against the current template's and rejects a mismatch (auto → fresh fallback, explicit → resume rejected). When you change a fresh template's prompt body, bump its frontmatter version in the same file — no other source needs editing. The *-resumed.md continuation prompts stay frontmatter-less by design (they inherit the fresh template's version on artifact emission).
| Code | Docs |
|---|---|
skills/hyper-setup/SKILL.md (invoke-only setup skill) |
docs/gates-and-agents.md, docs/architecture.md, docs/workflow.md, README.md, site/index.html |
scripts/setup-doctor.mjs |
docs/architecture.md, docs/development.md (non-bridge probe; never spawns Codex) |
scripts/codex-bridge.mjs, scripts/codex/*.mjs |
docs/architecture.md, docs/decisions.md |
skills/<any>/SKILL.md |
docs/gates-and-agents.md, docs/workflow.md |
agents/<any>.md |
docs/gates-and-agents.md |
references/loop-protocol.md |
docs/architecture.md, docs/gates-and-agents.md, docs/decisions.md |
references/review-brief.md |
docs/gates-and-agents.md, docs/architecture.md |
references/bridge-review-calls.md |
docs/gates-and-agents.md, docs/architecture.md |
hooks/*.mjs, templates/hooks/*.md |
docs/architecture.md (SessionStart hook section) |
templates/codex/*.md (incl. code-review.md) |
docs/architecture.md, docs/development.md (template-version section); a code-review prompt/spawn change also touches docs/decisions.md, docs/workflow.md, docs/gates-and-agents.md, README.md, and skills/hyper-implement-loop/* (the loop parses the code-review contract) |
scripts/test/smoke.sh, tests/*.mjs (incl. tests/memory-extract.test.mjs) |
docs/development.md |
.claude-plugin/plugin.json |
site/index.html (the vMAJOR.MINOR status-banner line — search the design has converged; the README header carries no version line), docs/development.md (release flow) |
scripts/memory/extract.mjs |
docs/architecture.md, docs/development.md, docs/gates-and-agents.md |
skills/hyper-memory/SKILL.md |
docs/gates-and-agents.md, docs/workflow.md, README.md |
skills/hyper-recap/SKILL.md |
docs/gates-and-agents.md, docs/workflow.md, docs/architecture.md, README.md, site/index.html |
Behavioral surface changes (CLI flags, frontmatter keys, output paths, mode names) should also propagate to README.md and docs/workflow.md if the change is user-visible.
Docs edits — yours or any agent's — should be as terse as accuracy allows: prefer amending existing prose over appending new paragraphs. A docs/decisions.md entry is the decision plus its non-obvious why, typically one paragraph — not a forensic changelog.
.hyperclaude/ (gitignored by consumer convention) holds per-run artifacts. Naming: <YYYYMMDD-HHMM>-<slug>.md (UTC). The slug propagates as the trace key — research → plan → plan-review share one slug end-to-end (extracted from the plan filename). An optional hyper-interview spec (.hyperclaude/specs/<ts>-<slug>.md, Claude-only, no Codex — clarity not review) can sit in front of research: it mints the slug from the idea text the same deterministic way, so carrying the idea forward keeps the trio linked. specs/ accumulates like research/ (matched by slug, never archived). A research slug may now resolve to a Codex + Claude artifact PAIR (<ts>-<slug>.md + <ts>-<slug>-claude.md) that share the same frontmatter slug:; the slug remains the single trace key for research → plan → plan-review. code-review slug comes from the diff target (vs-main, uncommitted, commit-<sha7>); docs-review slug from the docs target basename. These are release-level, not feature-level — don't try to align them with the research/plan trio.
Plan files (Claude-authored) live in .hyperclaude/plans/ and are the input to plan-review. Never write <file>-v2.md siblings when revising a plan — --resume keys on the plan path, so a new path breaks resume continuity. On full completion (every task executed + final acceptance green) hyper-implement archives the executed canonical plan (direct child of .hyperclaude/plans/) to .hyperclaude/plans/done/ (plain mv) so it stops being the newest-plan auto-pick and the SessionStart "Active plan" — the non-recursive *.md globs and the snapshot's readdir filter ignore the done/ subdir for free. Archival is the plan-implemented signal, independent of any code-review findings (review fixes are downstream hardening). It applies to nested hyper-implement-loop runs too — the loop's later review/fix rounds harden already-implemented code and never re-read the plan. Only plans/ is archived. research/ accumulates (matched by slug, not newest) and *-reviews/ accumulate by design — the snapshot shows newest-only there, and --resume auto walks newest-first gated on identity match, so prior review artifacts must stay in place for resume continuity.
Plan tiers. Most plans are detailed (## Task N: blocks, no planner-authored frontmatter) in .hyperclaude/plans/ — the executable input to hyper-implement. For an oversized task hyper-plan instead emits an epic roadmap (## Milestone N: blocks) carrying a tier: epic frontmatter line, written to its own .hyperclaude/epics/ dir (NOT plans/, so hyper-implement's newest-plan auto-pick never selects it), then auto-expands Milestone 1 into a detailed plan at the canonical .hyperclaude/plans/<ts>-<slug>.md — NOT -m1: the roadmap's epics/ location means no collision, so Milestone 1 keeps the shared <slug> (a -mN suffix would leak into slug.mjs's extraction and break the research → plan → plan-review trace). Vocabulary: epic → milestone → task. The tier: epic marker is the only planner-authored frontmatter (a PostToolUse stamp hook additionally adds a plugin-version line to every .hyperclaude/ artifact post-write — see the Hooks layer); hyper-implement (and therefore hyper-implement-loop, which delegates execution to it) refuses a tier: epic file. hyper-plan-loop has no epic path — it always produces a detailed plan. Later milestones (M2+) are expanded with hyper-plan milestone <K> — epic-aware: it reads the newest epics/ roadmap, carries Milestone K's Depends on: context, and writes a detailed plan slugged from the milestone's own title (no -mN or epic-slug in the filename; slug.mjs untouched). M1 keeps the canonical epic <slug>; M2+ carry their own milestone-title slug — the epic linkage rides in the plan content, not the slug.
.hyperclaude/ is gitignored by convention, so git mv .hyperclaude/<a> .hyperclaude/<b> fails with "source directory is empty" — use plain mv. Tracked files (templates, scripts) still use git mv.
.hyperclaude/memory/ is a new artifact area, written on-demand by hyper-memory (scripts/memory/extract.mjs), not part of the research→ship cycle. candidates/ holds proposed knowledge candidates mined from the plans/done/ + plan-reviews/ + research/ corpus; they accumulate, deduped idempotently by compound-key filename checked across BOTH candidates/ AND promoted/ (so an already-promoted candidate is never resurrected by a later extraction run). Promotion is a plain mv from candidates/ to promoted/, gated on the curator first adding a real, live anchors: repo path to the candidate — there is no archival step.
When the user asks to release, run the whole flow end to end — don't stop after the commit.
- Tests green — re-run immediately before committing, not "earlier this session":
node --test tests/*.mjsandbash scripts/test/smoke.sh. Unit must reportfail 0; smoke must reportfailed: 0. Either red → stop and fix first. - Bump
versionin.claude-plugin/plugin.json. Pre-adoption policy (no installed user base): a breaking change or a new feature rides a MINOR bump, not a major — breaking (bridge subcommand renames, frontmatter key changes, artifact directory renames, layer/command/skill removals) and new features (a new skill/command/mode/flag) both count; only fix/docs/internal work is a patch. (We reached1.0.0as a maturity marker — the design has converged — not as a strict-semver stability contract. Revisit major-on-break once there's a real user base.) The bump rides in the release commit, not a separate one. When the bump changes thevMAJOR.MINORshown in the status banner, also update it inREADME.mdandsite/index.html(search forthe design has converged) so the user-visible docs match. - Commit + push to
main. Conventional-commit subject; breaking changes take a!(feat!:/refactor!:) and aBREAKING CHANGE:footer spelling out migration steps. Reviewgit statusand stage the release's files explicitly — don't blanketgit add -A. (.hyperclaude/is gitignored so its artifacts never appear, but any other untracked file would be swept into the release commit.) - Tag:
git tag -a vX.Y.Z -m "vX.Y.Z: <one-line>"thengit push origin vX.Y.Z. - GitHub release:
gh release create vX.Y.Z --title "..." --notes "...". When breaking, the notes must carry a Migration section whose steps match the commit'sBREAKING CHANGE:footer.
- docs/architecture.md — bridge details, mode table, output contract.
- docs/gates-and-agents.md — per-skill / per-agent mechanics.
- docs/workflow.md — research → ship cycle, skip rules,
--resumesemantics. - docs/decisions.md — non-obvious "why" notes and active deferrals.
- docs/development.md — local install, tests, full release checklist.