Iterate on code with AI — one reviewable feature at a time.
Plan → Features → Red tests → Implement → Review, driven from a browser dashboard. Every piece of state is plain markdown in an Open Knowledge Format (OKF) bundle you can read, edit, and diff without any tooling.
The idea • The flow • The memory bundle • Installation • Configuration
The /iterator hub: the whole plan at a glance — every feature with its
status, size, and 🔴🟢 test badges — and buttons that dispatch each step.
Classical diff tools group changes by file. A developer's mental model is organized around what changed and why — a unit of work usually touches several files at once. iterator makes the feature the primary unit: a meaningful, vertical slice of implementation (code and its tests) of roughly 200 lines, in dependency order. Review-effectiveness research shows defect detection degrades past ~200–400 changed lines, so ~200 is a conservative, reviewable default.
You still lean on AI for planning and implementation — but the unit of change stays human-reviewable, dependency-ordered, and durable across sessions:
- The browser UI is the control plane. One local server renders every step's view — dashboard, plan review, feature breakdown, test plan, diff review — on one fixed port. No clipboard, no temp files.
- Everything mechanical is deterministic code, not prompt instructions.
gather.mjscomputes every step's payload (bundle state, parsed git diffs mapped to features, test-runner detection);write.mjsowns every bundle write (frontmatter, timestamps, topological indexes, the update log, cycle/reference validation). The model only supplies the semantic text: plan prose, feature descriptions, test cases, and the code itself. - All persistent state is an OKF v0.1 bundle in your
repo's
memory/directory — self-describing markdown with YAML frontmatter. Nothing is locked away in a database or a proprietary format.
Inspired by Plannotator.
/iterator dashboard hub — pick a feature, press Test / Implement / Review
▼
/iterator-plan create/revise the plan → memory/plan.md
│ (accept auto-continues)
▼
/iterator-feature break the plan into features → memory/features/<slug>.md
│
├─ (optional) /iterator-test write RED tests from the feature's contract
▼
/iterator-implement build the dependency-ready wave, drive tests GREEN,
│ auto-start one review over the wave
▼
/iterator-review feature-vs-git-diff review → outcome recorded in the feature file
/iterator-plan turns a goal into a plan you review in the browser:
click-to-edit markdown sections, per-section comments, editable dependency
chips. On accept it writes the memory/ bundle and auto-continues into the
feature breakdown.
/iterator-feature splits the plan into features — one OKF file each — in a
UI with a dependency-graph visualization, snippets, drag-to-move files, and
LLM-backed Split/Merge. Each feature is a vertical slice: one
user-visible capability including its own tests, implementable and reviewable
on its own.
| Size | Color | Meaning |
|---|---|---|
| small | 🟢 green | One focused change |
| medium | 🟡 yellow | A feature touching a few files |
| large | 🔴 red | Probably two features — should be split |
/iterator-test is decided by the feature's status: on a pending feature
it writes intentionally-failing tests from the feature's contract (red — the
implement step drives them green); on a done feature it writes passing
tests against the real code. Either way it commits the tests
(test(<slug>)) and records tests/tests_status in the feature file.
/iterator-implement builds the dependency-ready wave — every pending
feature whose dependencies are done — with each feature's tests as its
definition of done (implement → run → fix until green, never weakening a
test). On Accept and commit each feature is committed separately
(feature(<slug>)) and flipped to done, with its commit shas recorded.
/iterator-review shows the git diff grouped by feature, not by file,
with est-vs-actual line counts and amber cards for known pitfalls matched to
the touched files. It warns when a feature's actual diff exceeds ~200 changed
code lines — reviewability enforced where it can actually be measured. Done
features are reviewable too: the diff is rebuilt from the feature's recorded
commits.
The same bundle carries knowledge areas (architecture/, decisions/,
patterns/, pitfalls/, setup/). Knowledge is not write-only: pitfalls
resurface as cards during review, architecture notes feed the feature
breakdown, and relevant memories are injected into implementation.
/iterator-knowledge— the Knowledge view: area cards, every concept with itsfiles:anchors and stale flag, memorize status./iterator-init— analyze the repo and draft the initial memories per area, reviewed in the browser before anything is written./iterator-consolidate— re-review existing memories against the current code: stale anchors, dead concepts, merges./iterator-memorize— study commits made outside the iterator flow and draft create/update/delete cards, with conflict detection.
Knowledge also flows automatically: when /iterator-implement lands a wave,
the accepted diff is evaluated for durable knowledge, and proposed memory
creates/updates appear as reviewable cards in the same commit review — with
an explicit Apply/Skip toggle before anything is written.
All state lives in a memory/ directory in your project root — a
self-describing Open Knowledge Format (OKF) v0.1 bundle,
plain markdown with YAML frontmatter:
memory/
├── index.md # bundle root index (okf_version)
├── format.md # the metadata schema, copied into every bundle
├── plan.md # the plan (type: Plan)
├── design.md # optional — the project's design params (type: Design)
├── log.md # chronological history of what the AI did
├── features/
│ ├── index.md # feature listing with status
│ └── <slug>.md # one document per feature (type: Feature)
└── architecture/ decisions/ patterns/ pitfalls/ setup/
# knowledge areas (one concept per file)
An example feature document:
---
type: Feature
title: Auth middleware
description: JWT-based auth middleware for all protected routes.
status: pending
size: small
depends_on: [config-module]
files: ["src/auth.ts", "src/middleware/*.ts"]
tests: ["test/auth.test.ts"]
tests_status: red
---
# Implementation notes
Verify the token from the config secret; wrap protected routes.
# Blast radius
Every route behind the auth guard.The feature slug (the filename without .md) is the feature's identity:
its OKF concept ID, its depends_on key, and its commit-message name. One
file per feature means per-feature git history and no fragile line-number
indexes. The full schema is in templates/format.md;
the format itself is specified in docs/OKF_SPEC.md.
iterator currently supports two harnesses: pi (the full experience) and Claude Code (everything except the pi-only runtime features).
Install the repo as a pi package — the manifest in package.json points at
skills/ and extensions/, so the install registers the /iterator…
commands and the pi extension runtime:
pi install git:github.com/<user>/iterator@<tag> # or: pi -e … for one sessionThe pi extension unlocks the full feature set: auto mode (test → implement → review driven automatically, with a reviewer agent standing in for you), per-role models & thinking (planner/implementer/tester/reviewer each pin a model and thinking level), live pause (aborting the in-flight stream), a session dashboard with Work | Knowledge tabs, and token capture into the usage ledger.
For running iterator inside a Docker sandbox, use
pi-docker-sandbox-setup:
a pi sandbox image that installs iterator, sets ITERATOR_REMOTE=1, and
forwards port 7777 to the host via its pisbx script — open the printed URL
in the host browser and everything works as if local.
Load the plugin for a session with --plugin-dir:
claude --plugin-dir /path/to/iteratorTo install it persistently, add the directory as a local marketplace and install from it (inside Claude Code):
/plugin marketplace add /path/to/iterator
/plugin install iterator
All skills (/iterator + the six /iterator-* steps + the four knowledge
skills) are auto-discovered from skills/*/SKILL.md.
In Claude Code, iterator runs in Agent-Skills mode — a reduced feature set: the guided flow, all browser UIs, red/green testing, settings storage, the review guards, archive browsing, and the Usage view all work unchanged; the pi-only features (auto mode, per-role model/thinking switching, live pause, token capture) are stored in settings but inert.
- Node.js ≥ 18 (the servers use only Node built-ins — no
npm installneeded) - pi or Claude Code
- A git repository (the
memory/bundle is resolved relative to the git root)
Per-project behavior lives in memory/settings.md, edited from the dashboard
(gear icon, or /iterator-settings in pi) and written only by the
deterministic writer:
- auto mode (pi) — after the feature set is approved, the driver runs
test → implement → review automatically; a reviewer agent's verdicts are
recorded in each feature's
# Reviewhistory, and aftermax_review_iterationsneeds-work rounds it pauses and escalates to you. - per-role models & thinking (pi) — planner/implementer/tester/reviewer can each pin a model and thinking level.
- git flow —
branch_per_plancreatesiterator/<plan-slug>on plan approval, in a separate git worktree by default;block_commit_on_leftoversrefuses accept-commit while changed files are neither assigned to a feature nor explicitly skipped. - token ledger —
usage_ledgerrecords per-step × per-model token counts intomemory/usage.md(the Usage tab).
Every interactive step runs through one tiny local HTTP server bound to
127.0.0.1 that opens a browser UI and prints your response back to the
agent. The port defaults to 7777 (ITERATOR_PORT overrides) and is
stable by design: a lingering iterator server from an earlier run is shut
down and replaced, so back-to-back runs never drift to 7778.
ITERATOR_PORT— fixed port (default 7777)ITERATOR_MEMORY_DIR— relocate the bundle (relative to the git root)ITERATOR_NO_OPEN=1— print the URL without opening a browserITERATOR_REMOTE=1— force remote mode (see below)ITERATOR_DISPLAY_PORT— host-side port shown in printed URLs when a sandbox publish maps a different host port onto the listen port (display only; pi-docker-sandbox-setup'spisbxsets this per sandbox)ITERATOR_DISPLAY_HOST— hostname shown in printed URLs (defaultlocalhost)
The server rejects requests with a non-localhost Host header (DNS-rebinding
protection). Reloading the tab is safe — only closing it cancels.
When the agent runs inside a container or SSH session but your browser is on
the host, the server detects it (SSH/container markers, or explicit
ITERATOR_REMOTE=1) and switches to remote mode: binds :: (override
with ITERATOR_BIND_HOST), skips the browser opener, and prints a
http://localhost:<port>/ URL to open on the host. The sandbox must publish
the port:
sbx ports <sandbox> --publish 7777:7777 # Docker sandboxes (explicit host:container)
docker run -p 127.0.0.1:7777:7777 … # plain Docker — keep the host side on loopback
ssh -L 7777:localhost:7777 host # plain SSHWith several sandboxes open at once, each needs its own host port mapped onto
the sandbox's 7777 (e.g. --publish 7778:7777 for the second one). Set
ITERATOR_DISPLAY_PORT=<host port> inside that sandbox so the printed URL
matches — pisbx does both automatically.
MicroVM sandboxes have no container marker files, so set ITERATOR_REMOTE=1
in the sandbox image (pi-docker-sandbox-setup's image already does). Binding
:: exposes the UI to whatever network the sandbox is attached to —
keep the host-side publish on loopback.
:: is the dual-stack wildcard: it answers over both IPv4 and IPv6. That
matters because a sandbox with an IPv6 address gets two loopback forwards
(127.0.0.1:<host>->7777 and ::1:<host>->7777). Bound IPv4-only, the v6
forward has nothing behind it and resets — and a reset, unlike a refusal, stops
browsers falling back to IPv4, so http://localhost:<port>/ fails on the host
while http://127.0.0.1:<port>/ works. Where the kernel or container has no
IPv6 stack, the listener downgrades to 0.0.0.0 on its own; set
ITERATOR_BIND_HOST=0.0.0.0 to force IPv4 explicitly.
- A skill pipes a JSON payload to
skills/iterator/server.mjs— usually the one-command form{"gather":true,"step":"<step>", …}. Nothing is written to/tmp. - The server shuts down any lingering iterator server, binds the fixed port,
serves a self-contained page (data embedded inline and safely escaped),
and opens
http://127.0.0.1:<port>/. - On submit the browser POSTs structured JSON to
/submit; the server prints it to stdout and exits. Closing the tab cancels cleanly; a 2h idle times out. - The agent reads stdout, updates the
memory/bundle throughwrite.mjs, and re-runs the server for the next round.
See docs/ARCHITECTURE.md for the full design and docs/OKF_SPEC.md for the bundle format.
MIT




