A Codex and Claude Code plugin for shaping an engineering epic, then running long autonomous implementation against it, without losing the plot.
An epic is the unit of planning: a large slice of work, such as a new area of functionality, a complex migration, or an open-ended investigation. The name comes from agile, where an epic is a large body of work broken into smaller tasks. Here, it's an area of project ownership with its own bounded context, one you can rejoin from any session and keep for as long as you need it.
epic-loop turns that epic into a durable workspace. You first shape it, capturing intent and decomposing the work into phases and tasks, then run implementation against it through a hook-driven loop. Explicit lifecycle modes and disk-backed state carry the epic across sessions.
Working with coding agents produces a second body of work alongside the code: the intent, specs, decisions, and task breakdowns you feed the agent. Today it has nowhere durable to live.
- context evaporates between sessions, machines, and teammates, or bloats a single
AGENTS.md/CLAUDE.md - you return to areas, not tasks: cross-cutting, revisited weeks apart, reconstructed by hand each time
- inside a long autonomous run, coordination falls back on you: is the last step done, what to scope next, what goes in each brief
- a compaction or interruption loses the thread
epic-loop gives that work a home and makes the orchestration explicit and durable.
As Codex Plugin:
in your terminal:
codex plugin marketplace add usulpro/epic-loop
codex plugin add epic-loop@epic-loop
codex plugin list | grep "epic-loop"
then in codex:
`$epic-loop start a new epic`
Shaping is a step-by-step dialogue, not a one-shot planning dump. The agent clarifies intent topic by topic, captures it in the epic's documentation pack, and decomposes the work into phases and tasks with concrete acceptance criteria. The tracker and docs it produces are exactly what the implementation loop later consumes: shaping is where the epic's roadmap and source of truth come from.
Implementation mode runs as a single agent session (Codex or Claude Code) with two visible roles. The Stop hook keeps the loop moving: each techlead turn uses a fixed governance prompt, and each engineer turn receives the task-specific brief the techlead wrote for that exact slice.
Techlead role. Owns the implementation loop. It reads compact epic state and live repository evidence, reviews the latest engineer report, decides whether work is honestly closed, chooses the next step, updates human-facing epic artifacts, makes commit decisions, and writes exactly one focused engineer brief when implementation should continue.
Engineer role. Owns execution of one concrete brief. It follows project patterns, keeps scope narrow, makes local implementation decisions inside the brief, verifies the result with real evidence, reports changed files, blockers, and gaps, then stops. Routing returns to techlead automatically.
Two ordered modes. First you shape the epic, then you implement it:
- Shaping → clarify the epic, capture intent, create docs, decompose into phases and tasks.
- Implementation → run the hook-driven techlead/engineer loop against those tasks.
Supporting workflows help keep long-lived epics healthy:
- Review → check completed work against the original intent and captured decisions.
- Reset → replace stale assumptions, architecture, or roadmap with a controlled new baseline.
- Resume → re-enter an existing epic from its disk-backed workspace.
Epics live inside the target project under .epic-loop/, each keyed by a <slug> that names its folder and is passed to helper scripts via --slug. Multiple epics can coexist, each self-contained.
.epic-loop/
epics/
<slug>/ # one self-contained epic
state-of-epic.md # compact, current source of truth
tracker.md # phases and tasks with their status
implementation-log.md # what each turn changed, with verdicts
decision-log.md # decisions by you and the agent, with rationale
risk-register.md # known risks and open concerns
docs/ # intent, specs, references captured during shaping
.runtime/ # hidden machine state for hook routing and replay
<another-slug>/ # another epic, same layout
These files are the epic: a durable, human-readable planning artifact you commit alongside the code and keep across sessions. Roles read the human-facing files; .runtime/ exists for debugging and replay without polluting role context.
- A supported host: Codex (with hooks enabled under
[features]in the active config or profile) or Claude Code. - Trusted project-local command hooks for the current session (
.codex/hooks.jsonfor Codex,.claude/settings.jsonfor Claude Code). - Node.js 18+ with
npx(the skill's helper scripts and theepic-loopnpm CLI). - A target repository where the epic workspace should be created.
Three supported ways to install, all ending in the same skill:
1. npx (project-local copy + hooks in one step). From the target repository:
npx epic-loop install --platform claude-code # -> .claude/skills/epic-loop + .claude/settings.json hooks
npx epic-loop install --platform codex # -> .codex/skills/epic-loop + .codex/hooks.json hooks2. Plugin. Codex:
codex plugin marketplace add usulpro/epic-loop --ref main # or --ref v<version> for a pinned release
codex plugin add epic-loop@epic-loopClaude Code:
claude plugin marketplace add usulpro/epic-loop
claude plugin install epic-loop@epic-loop3. Manual copy. Copy plugins/epic-loop/skills/epic-loop/ into the project's .claude/skills/ or .codex/skills/. This is the same end state as option 1, minus the hook setup.
The skill runs its deterministic operations through the epic-loop npm CLI: scripts/epic-loop.mjs inside the skill invokes npx epic-loop@<version> pinned to the skill's own version, so a skill copy and the CLI it uses can never drift apart.
doctor checks the npm registry once a day and reports when a newer version exists, with the exact update command for the install type:
| Install | Update |
|---|---|
| npx install or manual copy | npx epic-loop@<latest> update replaces the copy in place (atomically; local edits are not preserved) |
| Codex plugin | codex plugin marketplace upgrade epic-loop && codex plugin add epic-loop@epic-loop |
| Claude Code plugin | claude plugin marketplace update epic-loop && claude plugin update epic-loop@epic-loop, then restart |
After a plugin update, run doctor again: the plugin path changes, and doctor flags the stale hook command for reinstall.
To let doctor update a local copy automatically, without asking, enable it per machine (stored in .epic-loop/.runtime/config.json, never committed):
npx epic-loop config set autoupdate truePlugin installs are never auto-updated; they belong to the host.
After installing, start a new Codex or Claude Code session in the target repository and invoke the skill by name:
$epic-loop start a new epic for the following area of functionality:
[describe the problem area or functionality you need to solve]
$epic-loop <epic-slug|epic-path>
$epic-loop <epic-slug|epic-path> start implementation loop
$epic-loop <epic-slug|epic-path> start review mode
The first run checks whether the target project has the project-local hooks it needs.
Hook setup is intentionally performed from the target project after user approval:
# Check technical readiness for the chosen host (and whether a skill update exists).
node <skill-dir>/scripts/epic-loop.mjs doctor --platform codex|claude-code --json
# Install project-local hooks (.codex/hooks.json for Codex, .claude/settings.json for Claude Code).
node <skill-dir>/scripts/install-hooks.mjs
# Bind the current session to an epic after the user confirms implementation.
node <skill-dir>/scripts/bind-session.mjs \
--current --slug "<epic-slug>" --mode implementationUnbound sessions are silent no-ops. epic-loop only activates for sessions explicitly bound to an epic.
Experimental. The plugin is intended for sustained autonomous engineering work where preserving intent, state, and review discipline matters more than simply running a checklist.
For contributors working on the plugin itself.
.agents/plugins/marketplace.json # Codex marketplace
.claude-plugin/marketplace.json # Claude Code marketplace
plugins/
epic-loop/
.codex-plugin/plugin.json
.claude-plugin/plugin.json
skills/
epic-loop/
SKILL.md
agents/openai.yaml
scripts/ # scripts/epic-loop.mjs = pinned entry into the npm CLI
references/
assets/templates/
packages/
cli/ # `epic-loop` npm package (ships a copy of the skill)
scripts/
release.mjs
validate-epic-loop-package.mjs
The distributable plugin surface is plugins/epic-loop/, exposed to Codex by .agents/plugins/marketplace.json and to Claude Code by .claude-plugin/marketplace.json. The npm package in packages/cli/ carries the CLI and, from its build step, a copy of the same skill for npx epic-loop install|update.
# Validate this plugin repository.
pnpm run validate
# Sync the current skill into repo-local Codex and Claude Code skill folders.
pnpm run self-updateWhile developing, point the skill wrapper at the local CLI source instead of the published package:
EPIC_LOOP_CLI="$PWD/packages/cli/src/cli.mjs" node <skill-dir>/scripts/epic-loop.mjs doctor --platform claude-codeThe skill, both plugin manifests, and the npm CLI share one version; pnpm run validate fails if they drift.
In Claude Code, run /release-epic [x.y.z|patch|minor|major]. It fills in the changelog, prepares the release, tells you to run npm publish, waits for npm to serve the version, then tags, pushes, and syncs the runtime skill copies. The same steps by hand:
node scripts/release.mjs prepare minor # preflight, stamp, changelog, validate, tests, build, local release commit
cd packages/cli && npm publish # the only manual step
node scripts/release.mjs wait 0.2.0 # optional: block until npm serves the version
node scripts/release.mjs finish 0.2.0 # npx smoke test, tag, push main + tag, self-update, runtime doctor check
node scripts/release.mjs abort 0.2.0 # instead of publishing: drop the local release commitPublish before pushing: the skill on main pins the new version, so it must already exist on npm; prepare checks the npm login, never pushes, and finish refuses until npm has the version.
Release tags (v<version>, matching the npm version) and GitHub Releases exist only on main; finish creates both, with notes from the changelog. Feature branches may carry temporary tags during development, which go away with the squash merge. The repo stays on 0.x until most skill scripts run through the CLI; that milestone ships as 1.0.0. Planned: move publishing to CI, so a release is just pushing a version tag.
These scripts back the modes above. In normal use the agent invokes them through hooks; they are listed here for reference and manual debugging.
# List local epics in a target project.
node <skill-dir>/scripts/list-epics.mjs
# Create a new epic workspace.
node <skill-dir>/scripts/init-epic.mjs \
--description "<epic description>"
# Show compact role-facing state for an epic.
node <skill-dir>/scripts/role-summary.mjs --slug "<epic-slug>"
# Write an engineer handoff brief.
node <skill-dir>/scripts/write-engineer-brief.mjs \
--slug "<epic-slug>" --stdin
# Route the next implementation turn.
node <skill-dir>/scripts/set-next-role.mjs \
--slug "<epic-slug>" --role "<engineer|idle>" \
--reason "<short reason>"Task and phase scripts are available for deterministic status changes:
node <skill-dir>/scripts/start-task.mjs --slug "<epic-slug>" --task-id "<task-id>"
node <skill-dir>/scripts/close-task.mjs --slug "<epic-slug>" --task-id "<task-id>"
node <skill-dir>/scripts/start-phase.mjs --slug "<epic-slug>" --phase-id "<phase-id>"
node <skill-dir>/scripts/close-phase.mjs --slug "<epic-slug>" --phase-id "<phase-id>"
node <skill-dir>/scripts/append-implementation-log.mjs --slug "<epic-slug>" --task "<task>" --verdict "<verdict>"MIT