Turn a GitHub issue into a merged PR autonomously, up to a human-approval checkpoint.
Harness-agnostic by design. The same dev loop runs three ways over one shared core (packages/core): as a Claude Code plugin, as a Pi extension, and as a standalone CLI. Routing, gates, and phases are defined once in the core; the plugin and extension are thin integrations over that shared workflow.
A dev loop takes a GitHub issue through seven lifecycle phases — from intake to merge — with deterministic routing, self-correcting review gates, and autonomous execution until a human approves. The phase is always consultable from the deterministic state model in packages/core/src/loop/lifecycle-state.mjs.
| Phase | What happens |
|---|---|
issue_intake |
Normalize the issue, confirm scope, detect linked PRs |
refinement |
Elaborate the spec, run a bounded audit, harden acceptance criteria |
implementation |
Build the accepted scope on a feature branch or via Copilot |
draft_gate |
Gate review at the draft→ready boundary before marking the PR ready |
feedback_resolution |
Fix, reply to, and resolve review threads on GitHub |
pre_approval_gate |
Final gate review: verify evidence, CI, and unresolved threads |
merge |
Merge the PR and write the retrospective checkpoint |
The public routing contract is the Public Dev Loop Contract.
dev-loop is the single public entrypoint. Drive it in natural language — it resolves the authoritative current state, picks the correct internal strategy, and routes deterministically. You never name internal strategies.
start dev loop on issue 112 # start work on an issue
auto dev loop on issue 112 # run autonomously to the human-approval checkpoint
continue dev loop on PR 88 # continue follow-up on an open PR
See the canonical shorthand mapping in the Public Dev Loop Contract.
dev-loop (natural language) is the catch-all router — reach for the named commands below only when you want a direct, unrouted entrypoint. The syntax differs by harness:
- Claude Code exposes each as a plugin slash command:
/loop-start,/loop-auto, … - Pi exposes the core entrypoints as subcommands of one command:
/dev-loops start,/dev-loops auto, …
| Command | Claude Code | Pi | Does |
|---|---|---|---|
| start | /loop-start <issue> |
/dev-loops start <issue> |
Start a dev loop on an issue |
| auto | /loop-auto <issue> |
/dev-loops auto <issue> |
Run autonomously to the human-approval checkpoint |
| continue | /loop-continue [issue|pr] |
/dev-loops continue [issue|pr] |
Continue; bare resumes the in-progress board item |
| start-spike | /loop-start-spike <question> |
/dev-loops start-spike <question> |
Time-boxed spike (or --file <path>) |
| info | /loop-info <issue|pr> |
/dev-loops info <issue|pr> |
Read-only state summary |
| status | /loop-status |
/dev-loops status |
Readiness check (gh auth, git repo, subagent) |
| enqueue | /loop-enqueue <issue|pr|text> |
— | Queue an issue/PR, or capture an idea as a grilled issue |
| grill | /loop-grill <issue|plan> [--auto] |
— | Socratic Q&A grill of an issue or plan before the loop |
| queue-status | /loop-queue-status |
— | Show the queue board grouped by column |
Beyond the per-issue loop entrypoints, the Pi /dev-loops command and the dev-loops CLI also expose standalone utilities: help, status, doctor, gates (status is the same readiness check as loop-status above). hide works only inside Pi — the dev-loops hide CLI subcommand is recognized but intentionally exits non-zero (session-local Pi UI behavior). Everything above is also available as a skill/agent — inside Pi, hand work to the dev-loop skill rather than calling internal routed skills (local-implementation, copilot-pr-followup, final-approval) directly.
All examples pin to <version> = the dev-loops version you installed; keep the CLI, plugin, and extension on the same version so behavior can't drift.
The repo ships a plugin rooted at .claude/ (manifest at .claude/.claude-plugin/plugin.json) exposing the dev-loop agents, skills, and hooks. Install it from the bundled marketplace catalog:
/plugin marketplace add mfittko/dev-loops # register the marketplace
/plugin install dev-loops@dev-loops # install the plugin
Or load it directly for a single session without installing:
claude --plugin-dir .claudeInstalled from npm, point at the bundled copy: claude --plugin-dir node_modules/dev-loops/.claude.
The hooks provide the gh pr ready draft-gate guard and an opt-in read-only boundary for the main agent (DEVLOOPS_MAIN_AGENT_READONLY=1). Skill references to a consumer repo's own PLAN.md / AGENTS.md resolve against that repo, by design.
pi install npm:dev-loops@<version> # global, pinned npm package
pi install -l npm:dev-loops@<version> # project-local (.pi/settings.json)You can also install from git:
pi install git:github.com/mfittko/dev-loops@<tag-or-sha> # global
pi install -l git:github.com/mfittko/dev-loops@<tag-or-sha> # project-localOnce a project is trusted, Pi auto-installs missing packages on startup. Install pi-subagents the same way when the repo relies on async loop behavior.
Run the CLI version-pinned:
npx dev-loops@<version> --helpA global npm install -g dev-loops is an optional bare-shell convenience only — it is not version-pinned, updates independently of the plugin/extension, and can silently drift out of sync (#833/#1036). Prefer the pinned npx invocation.
Universal:
- Node
>=24 ghinstalled and authenticated for GitHub/Copilot workflows
Pi-harness only:
pi-subagentsfor async workflow behavior- A Pi host satisfying the peer dependencies
@earendil-works/pi-coding-agentand@earendil-works/pi-tui
Defaults ship in packages/core/src/config/extension-defaults.yaml. A repo-local .pi/dev-loop/defaults.* layer merges on top of them when present, then consumer repos override everything in a .devloops file at the repo root (.devloops.yaml / .yml / .json are also accepted); .devloops is authoritative when present. The legacy .pi/dev-loop/settings.* and overrides.* files load only as a fallback when no .devloops is present; when .devloops exists they are ignored (a deprecation warning still fires if they are present).
npx dev-loops@<version> gates # print what reviewers will checkKey surfaces:
- Gate angles — which review lenses run at the draft and pre-approval gates
- Personas — focused per-angle prompts (DRY, KISS, YAGNI, SRP, SoC, and more)
- Refinement — fan-out count and mode for parallel review variants;
refinement.maxCopilotRoundscaps Copilot re-review rounds (default5). SetmaxCopilotRounds: 0to disable the Copilot gate entirely — local-harness-only review (draft_gate → pre_approval_gate), useful when the repo has no Copilot reviewer configured. - Autonomy — which gates require operator confirmation
- Workflow defaults — retrospective enforcement, draft-first posture, dev-mode policy
Full details: the shipped defaults in packages/core/src/config/extension-defaults.yaml and the loader in packages/core/src/config/config.mjs.
A deterministic container image with all required tooling for dev-loop operation.
docker build -t dev-loops .| Variable | Purpose | Required |
|---|---|---|
GH_TOKEN |
GitHub token for gh CLI and API calls |
Yes |
OPENAI_API_KEY |
LLM provider key (only for pi / LLM-backed operations) |
No |
Smoke test the image with a minimal read-only info call:
docker run --rm -e GH_TOKEN="$GH_TOKEN" dev-loops dev-loops loop info --repo mfittko/dev-loops --issue 1Verify the toolchain is reachable:
docker run --rm dev-loops node --version
docker run --rm dev-loops pi --version
docker run --rm dev-loops dev-loops --version
docker run --rm dev-loops gh --versionThe Dockerfile pins exact versions for Node.js (base image), the pi CLI, pi extensions, and gh CLI; paired with the committed package-lock.json, repeat builds produce functionally identical toolchains.
Runtime patterns:
# Interactive Pi sharing host config (sessions, models, settings write back to ~/.pi)
docker run -it --rm -e GH_TOKEN="$GH_TOKEN" -v "$HOME/.pi:/home/node/.pi" dev-loops pi
# Interactive Pi, clean (ephemeral ~/.pi; provider auth via env)
docker run -it --rm -e GH_TOKEN="$GH_TOKEN" -e OPENAI_API_KEY="$OPENAI_API_KEY" dev-loops pi
# Full dev-loop over a live repo worktree mounted at /workspace
git clone --mirror git@github.com:owner/repo.git /tmp/mirror
git --git-dir=/tmp/mirror worktree add /tmp/run /tmp/mirror/main
docker run -it --rm -e GH_TOKEN="$GH_TOKEN" -v "$HOME/.pi:/home/node/.pi" -v /tmp/run:/workspace dev-loops pinpm run verify # canonical root verification (tests + dev-loop tests)CI splits into a small changed-files gate plus a parallel verify-suite matrix (one leg per test:* suite) gated by a fail-closed verify job, and a conditional viewer-smoke job. On every change, npm ci runs and the verify suites run as parallel matrix legs; the Playwright/WebKit viewer smoke runs only when the bounded viewer surface or its smoke-path dependencies change.
Upgrading from before the rename to dev-loops? The package name, repo slug, and all PI_* environment variables changed (the env vars are a clean break — no aliases). See the migration guide.
- Docs Index — active docs and canonical-owner pointers
- README Audit Rubric — semantic properties this README is audited against (on-demand LLM judge)
- Public Dev Loop Contract — public routing contract
- Extension Documentation — README-driven extension spec
- Scripts Documentation — deterministic script contracts
- UI Smoke Harness — reusable local Playwright/WebKit smoke baseline
- UI Artifact Contract — screenshot/state artifact contract and CI-promotion rules
- UI Designer Review Loop — designer + vision (
uiReviewMode: vision) review loop - Slides Story Review Loop — bounded slides content & storytelling reviewer