Reference manual for CLI v2: architecture, pipeline, addons, template, harness, FIA — the IAI Agent Factory and how to extend it. Getting started: README.
v2 (
impactus) adds the FIA: Pi package (.pi/), Node runner (imp/), deterministic FDAs (fda_*.mjs), observability SQLite (imp/data/fia.db) and subscription-based authentication (Claude CLI + Pi/Codex).
Contents
- The three pieces
- Access: sign-in, guest mode and tokens
- Install modes, stack paths and the pipeline
- Philosophy: maximal template, guided removal
- Addons
- Integrations: skills, CLIs and service keys
- The Documents page (storage)
- The harness
- FIA — the IAI Agent Factory
- The durable planning layer
- The design-system layer
- Pi command reference
- The web UI (
--ui) - Maintenance:
--verify,--update-runtime,imp update - Recipes — worked examples
- Flag reference
- Environment variables
- Extending: a new addon, a new template
- CLI development
| Piece | Source | What it is |
|---|---|---|
| CLI | this repo (impactus on npm) |
Interactive Node.js installer (ESM + @clack/prompts) |
| Harness | private repo (community API) | Agent-workflow scaffold (/start, /dev, /sv, 9 agents, skills) — the base, always installed |
| Template | private repo (community gated API) | Next.js 16 + Convex + Clerk + shadcn/Tailwind v4 app, with EVERYTHING implemented — optional |
The CLI does not bundle the template or the harness: both are downloaded at
install time exclusively through the community API — there is no direct
GitHub clone. The templates require the paying-student token; the harness is
also served without one (guest mode installs harness + FIA only — see §2).
The FIA/Pi runtime (imp/ + .pi/) IS bundled in the npm package
(fia-templates/, pi-templates/) and stamped locally.
Tenancy: --tenancy single (default) downloads live1; --tenancy multi
downloads live2 — multi-tenant with organizations owned by the
app itself (Convex tables; Clerk only authenticates identity): data and
billing per organization, roles (admin/member) with a per-module permission
matrix, email invites, mandatory onboarding and an /admin panel with user and
organization management. The convex JWT template is the SAME simple one in
both templates (no org claims) and the webhook subscribes only to user.* (and
is optional — users.ensure syncs on first login). Both templates ship the
/admin panel; in live2 the FIRST user to sign in becomes super-admin, and the
CLI additionally sets SUPERADMIN_EMAILS in Convex with the Clerk account
email during installation (extra bootstrap). Spec/roadmap: live2-spec.md
in the private impactus-internal-docs repo.
The overall shape of an install:
npx impactus
│
├─► ACCESS — sign in (optional; valid session skips the question)
│
├─► name/folder + MODE (harness only | harness + template)
│
├─ full mode ──► DECISIONS: template (live1 | live2), addons, shadcn,
│ │ deps, storage, webhook, GitHub, deploy, FIA —
│ │ all at once, then one summary + confirm
│ ├─► DOWNLOADS the template to a tmp (community gated API)
│ ├─► install ──► reconciles the choice against the
│ │ downloaded template.addons.json, then REMOVES
│ │ everything that was not chosen
│ ├─► Convex + Clerk + webhook + storage + skills/CLIs
│ └─► GitHub + Vercel (optional)
│
├─► harness merge (community download — always, in both modes)
├─► stack manifest + docs + tooling (ai-docs/stack.md)
├─► FIA stamp (imp/ + .pi/ from the bundled templates)
├─► Impeccable design skill (optional)
└─► final summary + next steps
Signing in is optional and is the first question of the installer
(steps/auth.js). What it decides:
| State | What the installer delivers |
|---|---|
| Signed in, active subscription | Everything: templates + the automated template pipeline (Convex, Clerk, keys, webhooks, GitHub, deploy) and the harness + FIA. |
| Guest (declined the sign-in) | Harness + FIA only. Every template path is locked (steps/mode.js), announced up front and again in the final summary. |
| Inactive subscription | Same as guest, after a renewal warning with the checkout URL. The saved token is kept — renewing reactivates it. |
The decision tree on every run:
CREATE_IAI_TOKENset (CI/automation) → verified; an invalid token fails loudly (automation never silently degrades to a harness-only install).- A saved token exists (
~/.create-iai/auth.json) → verified. Valid → "Welcome back" and no questions. Expired/revoked (401) → cleared, the offer below includes a fresh login. Community unreachable (5xx/network) → the run exits WITHOUT deleting the credential ("your login is still saved"). - Interactive → the "Sign in (optional)" note + a two-option select:
Sign in (recommended) (device flow) or Continue without signing in
(guest mode,
ctx.guest = true). --yeswith no token → guest mode with a warning (there are no prompts to ask with);--mode full/--stack recomendadathen error out instead of silently downgrading.
Device flow (OAuth 2.0 Device Authorization Grant, RFC 8628): the CLI
calls POST /api/cli/device/start on the community API, opens the browser at
the community's /cli page with a user code, and polls
POST /api/cli/device/poll until approved (10-minute deadline, slow_down
honored). The token is saved to ~/.create-iai/auth.json (mode 600) with
the API base and a device label (<hostname> (<platform>)) shown in the
student's token list. During an install, a DENIED authorization falls back to
guest mode (the run continues); --login standalone still exits 1.
Access is revalidated on every use (GET /api/cli/verify re-checks the
enrollment), and the template download re-checks it again server-side. The
harness tarball is served without a token (PUBLIC_TEMPLATES on the
community server) — harness + FIA are the free tier; live1/live2 answer
401 missing_token to anonymous requests.
Subcommands (each authenticates/reports and exits):
npx impactus --login # device flow now; errors out if denied
npx impactus --whoami # "Authenticated as <name> — subscription active ✓" | inactive | expired
npx impactus --logout # revokes server-side (best-effort) + deletes auth.jsonState on disk — ~/.create-iai/ (dir mode 700):
| Path | What |
|---|---|
auth.json |
{ token, apiBase, savedAt, label }, mode 600. |
keys/<slug>.env |
Service keys pasted in the web UI (--keys reads them; mode 600, machine-local only). |
logs/run-<timestamp>.log |
Full log of each installer run (secrets redacted) — what students attach to bug reports. |
Dev/testing: --api <url> or CREATE_IAI_API point the CLI at another
community deployment; a custom base prints a one-time warning (the saved token
is sent to that host).
Right after name/folder, the CLI asks how to start (stored in
ctx.mode + ctx.stackPath, set in steps/mode.js):
| Path | Trigger | Mode | What happens |
|---|---|---|---|
template |
"Recommended stack" pick, --stack recomendada, --mode full, the --yes default in a new folder |
full |
Downloads the ready-made template and runs the full pipeline. The harness still comes in (last). |
custom |
"Build my own stack" pick, --stack propria/custom, or --stack cat=opt pairs |
harness |
The layer-by-layer wizard (§3.3). May switch to full mid-wizard when the choices match the recommended stack. |
discover |
"Not sure yet" pick, --stack depois, --mode harness in a new folder |
harness |
Everything pending — Pi (/idea) extracts PRD + stack later. |
brownfield |
Existing project detected (folder has package.json or .git and was not created by the CLI) |
harness |
/absorb maps the real stack and fills the manifest. --stack depois on an existing project also lands here. |
Flag precedence: --stack > --mode/--harness-only > --yes. A --mode
that conflicts with the mode --stack implies is a hard error. In guest
mode (§2) any flag that forces the template (--mode full,
--stack recomendada) throws a "needs the community sign-in" error rather
than silently downgrading, and interactive menus simply don't offer the
template path. A leftover install-state marker (a previous full install died
midway) is treated as a resume of the full install — never as an existing
project — and requires the sign-in.
The harness is always installed; the mode only decides whether the template comes along.
The prelude (steps 1–5) always runs; the [i/n] counter only knows the exact
total after the mode is resolved (main.js builds the list in two phases — the
prelude, then the tail for the chosen mode).
Prelude (always):
| # | Step | File | Notes |
|---|---|---|---|
| 1 | Access — sign in (optional) | steps/auth.js |
Valid session → "Welcome back"; otherwise offers the device-flow login. Declining sets guest mode (§2) |
| 2 | Engines — Claude Code and Codex | steps/preflight.js |
Status only — NEVER blocks. Missing engine → install/login guidance and the run continues; the final summary repeats the roster with the exact commands |
| 3 | Name and folder | steps/project.js |
. installs in the current folder |
| 4 | How to start — mode + stack path | steps/mode.js |
Sets ctx.mode + ctx.stackPath; agent-files conflict policy asked here too |
| 5 | Your stack — layer by layer | steps/stack.js |
Only the custom path asks; may switch ctx.mode to full (never for guests) |
full mode (harness + template): each step declares a CAPABILITY
(core always runs; convex/clerk/shadcn/storage/mcps only when the
template declares them in requires — see §4.4 and src/lib/pipeline.js).
| # | Step | File | Notes |
|---|---|---|---|
| 6 | Decisions — assemble the installation | steps/decisions.js |
ALL questions at once (template variant, addons, shadcn, deps, storage, webhook, GitHub, deploy, FIA) + summary/confirm |
| 7 | CLIs (git, gh, vercel) | steps/preflight.js |
Auto-install (git: PM, or the official installer on a PM-less Windows / the native CLT dialog on a brew-less Mac); logins only when push/deploy was chosen. NEVER blocks: availability is re-probed on the machine (no "did you install it?" self-reporting) and anything still missing is skipped with the person's consent — the fix lands in the final summary and ai-docs/inbox.md, and the dependent steps (commit/push, publish, deploy) degrade to manual notes |
| 8 | FIA — Pi install/update | steps/preflight.js |
Codex login stays for AFTER the install; a failed Pi install degrades (continues without FIA) |
| 9 | Template download | steps/project.js |
Community gated API (tarball) → tmp; reads the manifest |
| 10 | Install + prune + npm install | steps/project.js |
Copies from tmp; applies addons BEFORE the install |
| 11 | Update deps (optional) | steps/deps.js |
none / safe (patch/minor) |
| 12 | MCPs (Playwright, Convex) | steps/project.js |
claude mcp add |
| 13 | shadcn preset + block | steps/shadcn.js |
Optional |
| 14 | Convex — cloud project | steps/convex.js |
Login + env; 1st push fails on purpose (issuer missing) |
| 15 | Clerk — app, keys, JWT | steps/clerk.js |
convex JWT template (the SAME simple one in single and multi — no org claims) + issuer in Convex |
| 16 | Convex — publish functions | steps/convex.js |
Also regenerates _generated |
| 17 | Clerk → Convex webhook | steps/webhook.js |
Optional; subscribes only to user.* events (multi included) |
| 18 | Storage — Convex or R2 | steps/storage.js |
R2 with wrangler assistant (bucket + CORS); consumes keys from --keys |
| 19 | Keys — activate integrations | steps/service-keys.js |
See §6.1 — AI prompts, --keys, webhooks via API |
| 20 | Integrations — skills and CLIs | steps/integrations.js |
See §6 |
| 21 | Git + GitHub | steps/github.js |
Private/public repo, push |
| 22 | Vercel deploy | steps/deploy.js |
Optional (demo with dev creds) |
| 23 | Harness | steps/harness.js |
Merge without overwriting anything (always) |
| 24 | Stack — manifest and docs | steps/stack-docs.js |
ai-docs/stack.md + stack block in AGENTS.md |
| 25 | FIA — Pi + FDAs | steps/fia.js |
Stamps imp/ + .pi/, npm scripts, SQLite + the runtime manifest (§14.2) |
| 26 | Impeccable — design skill | steps/impeccable.js |
Optional, default on; requires Node ≥ 22.12 |
| 27 | Final summary | steps/finish.js |
URLs + integrations report + pending items |
harness mode (harness only):
| # | Step | File | Notes |
|---|---|---|---|
| 6 | CLIs (git) | steps/preflight.js |
Binaries only — no gh, no Vercel (the harness always comes through the community API, token or not). Same never-blocks contract: git that can't be installed is skipped on request and the commit degrades |
| 7 | Harness | steps/harness.js |
Merge into the folder; runs git init if missing |
| 8 | Stack — manifest, docs and tooling | steps/stack-docs.js |
Manifest + AGENTS.md block + skills/CLIs/MCPs of the chosen techs (incl. Neon/Supabase dev DB) |
| 9 | FIA — Pi + FDAs | steps/fia.js |
Stamps imp/ + .pi/ |
| 10 | Impeccable — design skill | steps/impeccable.js |
Optional, default on |
| 11 | Final summary | steps/finish.js |
Harness next steps (no npm run dev) |
Logs of each run: ~/.create-iai/logs/run-<timestamp>.log.
src/stack-catalog.js is the single data source for everything the
installer knows about stack layers: the wizard, the manifest renderer and the
tooling step are all driven by it. Adding a technology = one catalog entry
(docs/skills/CLI/MCP/envs), no logic changes. depois ("decide later") is a
valid answer in ANY category — the literal is a stored/CLI value kept for
compatibility.
The eight categories, in question order, and every option:
| Category | Options (default first) | Conditional logic |
|---|---|---|
frontend |
nextjs (Next.js App Router) |
— |
backend |
convex (database + backend together, no API layer) · hono (own API — a route handler inside Next.js, single deploy) |
— |
database |
neon (serverless Postgres, instant no-account provisioning) · supabase (Postgres + platform) · convex (built into the backend) |
Only asked when backend=hono; backend=convex forces convex; backend=depois drags it to pending |
orm |
drizzle · prisma · none |
Only asked when backend=hono (an ORM is mandatory there); backend=convex forces none |
auth |
clerk · better-auth (open source, lives in YOUR SQL database — not offered with the Convex backend) |
— |
blob |
r2 (Cloudflare R2) · convex-storage (Convex backend only) · supabase-storage (Supabase database only) |
— |
automations |
none (in-app scheduling: Convex scheduled functions / Vercel crons) · modal (external compute: cron, queues, GPU, long-running Python — a SECOND deploy target, pip install modal, modal setup) |
— |
deploy |
vercel (the only supported path today) |
Still a real question — a --stack pair list that omits it leaves it pending |
The pure rule engine (applyStackRules in src/lib/stack.js) enforces the
combinations and never silently drops an explicit choice — an overridden
choice produces a printed warning (e.g. ORM: "drizzle" ignored — the chosen backend already defines this layer (not applicable).). The golden rule: the
Convex backend means no API layer and no ORM; the Hono backend means SQL
database (Neon/Supabase) + ORM (Drizzle recommended, Prisma alternative).
Wizard behavior worth knowing: categories decided via --stack are not asked
(flags win); "Adjust the choices…" at the review panel reopens ALL layers,
including flag-set ones; if the final choices are exactly the recommended
stack (matchesTemplateStack: nextjs + convex + clerk + r2/convex-storage +
vercel — automations is ignored and survives as a manifest override), the
wizard offers to switch to the ready-made template (signed-in only; guests get
an informative note and keep building from the manifest). Under --yes the
flag choices stand, the rest stays pending, and the template-switch offer
never happens.
npx impactus my-app --stack recomendada # full mode: the ready-made template
npx impactus my-app --stack depois # harness only, all layers pending
npx impactus my-app --stack propria # harness, interactive wizard (alias: custom)
npx impactus my-app --stack backend=hono,db=neon,orm=drizzle
npx impactus my-app --stack banco=supabase,storage=supabase-storage,jobs=modal
npx impactus my-app --yes --stack backend=hono,db=neon,orm=drizzle,auth=clerk,blob=r2,frontend=nextjs,deploy=vercel,automations=noneCanonical categories: frontend, backend, database, orm, auth, blob, automations, deploy. Aliases: db/banco → database, arquivos/storage
→ blob, autenticacao → auth, jobs/automacoes → automations. A
value with no = that isn't a known shortcut aborts (an unrecognized
value must never silently change the install type); an invalid pair warns and
that layer is asked again (or stays pending under --yes); a known category
with an unknown option is dropped with a warning at validation time.
Runs inside the stack-docs step, only when the chosen database is Neon or
Supabase and .env.local does not already carry a DATABASE_URL (an existing
one is always kept — the database is yours). Everything is fail-soft: a
network/CLI failure warns and prints the manual command; nothing aborts.
- Neon, instant (recommended, no account) — Neon Launchpad:
POST https://neon.new/api/v1/database(20 s timeout)..env.localgetsDATABASE_URL(+DATABASE_URL_DIRECTwhen present) plus a claim-URL comment; the claim URL also lands in the manifest and as a terminal warning: claim the database into your Neon account within ~72h or it expires. A marker comment prevents a re-run from creating a second database. - Neon, in your account — installs/logs into the
neonCLI (neon auth, browser) and runsneon projects create --name <slug> --output json(2-minute timeout; retried once after a login). Secrets are redacted from the run log. - Supabase — installs the
supabaseCLI (brew tap), probes login withsupabase projects list, runssupabase loginif needed, thensupabase projects create <slug>inherited: the Supabase CLI itself asks organization, region and the database password — the password is typed into the CLI's hidden prompt, never passes through argv and never stays with the installer. The connection string comes from the dashboard (Connect) and is pasted into the installer (or added to.env.locallater). - Later — a manual hint is printed (e.g.
npx neon-new@latest --yesfor a claimable database, orneon projects create).
ai-docs/stack.md is the source of truth agents read before planning,
implementing and launching. Written once by the installer (never overwritten
if present — /stack updates it), it carries: the source line
(template/installer/discover/brownfield), a per-layer Summary table with
local-docs pointers (ai-docs/apis/<tech>.md, generated by /stack), a
"Pending — decide before implementing" checkbox list with the decision rules,
a "Layers" detail section per decided technology (role, official docs,
llms.txt, CLI/MCP/skills, notes, test users for auth, Neon provision +
claim URL), and an "Environments — development × production" table (golden
rule: .env.local is development ONLY; every production env lives on Vercel
or the matching service — /launch guides the promotion).
AGENTS.md gets a compact mirror between <!-- stack-start/end --> markers —
decided layers, pending layers ("do NOT invent: stop and decide with the
engineer"), and the pointer to the manifest. Idempotent: marker present ⇒
never rewritten by the installer.
Outside template mode, the stack-docs step equips the project for every decided technology (deduped — Convex appears as backend AND database but its tooling runs once):
- Skills via skills.sh (see §6.2) — e.g. Neon installs
neon,neon-postgres,claimable-postgres,neon-postgres-branchesfromneondatabase/agent-skills; Convex/Clerk/Better Auth/Cloudflare install their official sources. - Official CLI via
ensureIntegrationCli— offered once per binary: brew → npm -g → pip fallback (Modal is pip), 5-minute timeout, then an optional browser login (default no). Convex isviaNpx— never installed globally. Under--yes: hint only, no install, no login. - Official MCP via
claude mcp add …— e.g. Neon (--transport http https://mcp.neon.tech/mcp), Vercel (https://mcp.vercel.com), Convex (add-jsonwithnpx convex mcp start). Supabase and Clerk MCPs need a key/PAT, so the installer prints the note and/stackguides them later.
Pending layers install nothing — whoever decides later (Pi /idea or
/stack) gets the tools at decision time. The installer catalog is the
bootstrap; the first /stack pass re-verifies everything through the research
ledger (§10).
Decisions (steps/decisions.js) follow the professional-scaffolder
pattern: ALL questions first (template, addons, shadcn preset+block, deps,
storage, webhook, GitHub push + repo name + visibility, deploy, FIA,
Impeccable), then one summary panel, then a Yes — start now / Adjust the choices… / Cancel confirm. "Adjust" reopens every question pre-filled with
the prior answers. Whatever a flag decided is never asked; capabilities the
template does not declare never become a question. Execution then runs end to
end with no further decision questions (logins/keys for chosen services may
still interact — the summary says so).
The --yes defaults, exactly: deps none · shadcn no preset + block
sidebar-07 · storage convex (unless --storage r2 + all four R2_* keys
in --keys, which configures R2 non-interactively) · webhook off (it needs a
manual dashboard action, so automation never turns it on) · push off (creating
a remote repo is irreversible) · visibility private · deploy off · FIA on ·
Impeccable on.
Step behaviors worth knowing:
- Deps —
saferunsnpm update --save(patch/minor inside the template's ranges). There is deliberately nolatestmode (major bumps broke projects before the firstnpm run dev); the manual path isnpx npm-check-updates -i, package by package, with the app running. - shadcn — a pasted "Get Code" command runs verbatim; a bare code becomes
npx shadcn@latest apply <code>. Blocks install withnpx shadcn@latest add -y -o <block…>(deterministic, no mid-install prompts) and a guard-rail restores application code the block demo would clobber: modified tracked app files are checked out back to the baseline commit, unused new demo files are removed — the block's real value (components/ui/*,hooks/*,lib/*, deps) stays.hooks/use-mobile.tsis backed up/restored (the registry version fails the template's eslint and would block every commit). A warn-onlynpx tsc --noEmitcloses the step. - Convex — two phases.
setupConvexlogs in (npx convex login --device-name "create-iai installer") and provisions withnpx convex dev --once --configure=new --project <slug> --dev-deployment cloud --tail-logs disable; success is judged by the env vars, NOT the exit code — the very first push fails ON PURPOSE (auth.config.tsreadsCLERK_JWT_ISSUER_DOMAIN, which Clerk sets next). A crashed re-run that findsCONVEX_DEPLOYMENT+NEXT_PUBLIC_CONVEX_URLin.env.localreconnects with a plaindev --onceinstead of creating a SECOND cloud project.NEXT_PUBLIC_CONVEX_SITE_URLis derived (.convex.cloud→.convex.site).finalizeConvexpublishes the functions after Clerk (also regeneratesconvex/_generated); its failure is only a warning ("runnpx convex dev"). - Clerk — 100% automatic: resolves the
clerkCLI (global →npm i -g clerk→npx -y clerk@latest, agent mode viaCLERK_MODE=agent), logs in, creates or reuses an app, pulls the dev-instance keys into.env.local(normalized toNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY+CLERK_SECRET_KEY), creates theconvexJWT template ({"name":"convex","claims":{"aud": "convex"},"lifetime":3600}—convex/auth.config.tspins that exact name/aud; API fallback + manual note if the CLI path fails), derives the issuer FROM the publishable key (base64 payload — no API round-trip) and setsCLERK_JWT_ISSUER_DOMAINon the Convex deployment. The Clerk account email becomes the super-admin (SUPERADMIN_EMAILSon Convex). - Webhook — semi-automatic by necessity (endpoint + signing secret are
dashboard-only in Clerk): the CLI mints the Svix dashboard URL via the
Clerk API, prints exactly what to paste
(
https://<name>.convex.site/clerk-users-webhook, eventsuser.created,user.updated,user.deleted), captures thewhsec_secret (written to.env.localFIRST, thennpx convex env set CLERK_WEBHOOK_SECRET). - GitHub — commit
feat: project configured by create-iai (Convex + Clerk + shadcn/ui); a lefthook/eslint rejection shows the lint output and the documented escape hatchLEFTHOOK=0 git commit -m "initial setup"(never misblamed on git identity). Push runsgh repo create <name> --private|--public --source=. --remote=origin --push. - Deploy —
vercel link --yes --project <slug>, then an additive env sync (only missing keys are added; existing Vercel values are kept, with a note when they differ from.env.local; values travel via stdin, never argv;CONVEX_DEPLOYMENTis deliberately not copied — CLI-only var), thenvercel deploy --prod --yes. The URL uses the DEV Convex backend and DEV Clerk keys — a demo; the real production promotion is/launch(FIA) or the printed manual checklist.
Instead of the CLI generating code (fragile, hard to test), the template
ships with all integrations implemented and working together — and the CLI
removes what the user did not choose. The order is: decide (all addon
questions in the decisions phase) → download → reconcile (against the
downloaded manifest) → prune → npm install. Three mechanisms, all
described in the template.addons.json manifest (at the template root):
Snippets belonging to an addon sit between markers:
// live1:addon:sentry:start
import { withSentryConfig } from "@sentry/nextjs"
// live1:addon:sentry:endStripper rules (src/lib/addons.js — pure, tested):
-
Addon not chosen → the whole block is deleted.
-
Addon chosen → the code stays; the marker lines disappear.
-
Inverse block
live1:addon!:<id>→ only exists when the addon was NOT chosen. Real example fromnext.config.ts— the minimal baseline CSP vs. the full CSP of thecspaddon:// live1:addon!:csp:start { key: "Content-Security-Policy", value: "frame-ancestors 'self';" }, // live1:addon!:csp:end // live1:addon:csp:start { key: "Content-Security-Policy", value: contentSecurityPolicy }, // live1:addon:csp:end
-
Works in any text file — the marker is matched anywhere in the line, so any comment syntax works:
//(TS/JS),{/* */}(JSX),#(YAML,.env.example),<!-- -->(md). Nested blocks are supported; stray/extraendmarkers are tolerated instead of corrupting output. -
The scan skips
node_modules,.git,.next,_generated,public, files over 1 MiB, and any file without thelive1:addonsubstring; only text extensions are touched. The literallive1:prefix is the fixed protocol string in EVERY template (live2 included) — not per-template. -
Virtual ids (
manifest.virtual) join the keep-set when ANY member addon was chosen; they exist only for markers shared by several addons — e.g.billing-ui(stripe|asaas|clerk-billing → the "Subscription" sidebar item) andsignup-hooks(notifications|resend → the new-signup hooks import).
template.addons.json lists, per addon: files (deleted), dependencies/
devDependencies and scripts (pruned from package.json). A file listed by
more than one addon (e.g. app/dashboard/billing, shared by stripe/
asaas/clerk-billing) is only deleted when none of them was chosen.
Emptied parent folders are removed too. A representative real entry:
"sentry": {
"label": "Sentry (+ Spotlight no dev)",
"files": ["sentry.server.config.ts", "sentry.edge.config.ts",
"instrumentation-client.ts", "app/global-error.tsx"],
"dependencies": ["@sentry/nextjs"],
"devDependencies": ["@spotlightjs/spotlight"],
"scripts": ["dev:spotlight"]
}The generated project gets imp/iai.config.json (older versions kept it at
the root):
{ "createdWith": "create-iai", "addons": ["analyzer", "commitlint", "csp", "knip", "rate-limit", "sentry"] }and the manifest is removed. npm install runs only after the pruning (never
downloads what was cut) and convex dev --once (the Convex step) regenerates
convex/_generated for the reduced set. After the install the CLI runs
npx prettier --write . and amends the initial commit
(chore: initial template (create-iai), with LEFTHOOK=0 so the project's
fresh git hooks don't fire on the internal amend) — stripping blocks can leave
formatting Prettier would rewrite, and the amend keeps format:check green in
the generated project's CI. .env.local is seeded from the already-pruned
.env.example (born clean — no dead config for unchosen addons) and the four
Clerk routing defaults are always upserted.
TEMPLATES (src/config.js) is the single source of installable templates
(live1 single-tenant, live2 multi-tenant). Each entry declares repo
(documentational — the real allowlist lives on the community server),
available (publication gate: false hides it from the select AND
hard-errors an explicit --template-id), strip (folders deleted right after
extraction — both templates strip packages/), tenancy and requires — the
capabilities the pipeline turns on (convex, clerk, shadcn, storage,
mcps; pure helper in src/lib/pipeline.js). The choice comes from
--template-id (or the legacy --tenancy shortcut, or an interactive
question). There is no direct fork/clone flag: the download is always the
gated one, by the catalog id; --template-ref <branch|tag> tests a template
branch through the same gate.
The addon CHOICE happens in the decisions phase, against the CLI's built-in
catalog — but the downloaded template.addons.json may declare its own
groups/presets fields (same format as ADDON_GROUPS/ADDON_PRESETS).
When it does, reconcileAddons makes the TEMPLATE'S catalog win after the
download: flag-driven selections are re-resolved from scratch against it;
interactive selections are intersected (ids the template doesn't know are
dropped with a warning; template options the CLI never asked about stay out,
with a pointer to the project README). A new template can expose its own
options without a CLI release. Today neither live template declares them, so
the built-in catalog applies end to end.
To ADD a template: an entry in TEMPLATES + repo on the community backend's
allowlist (cli-paid-gate.md, private impactus-internal-docs repo) +
TEMPLATE_GITHUB_TOKEN scope. See §18.
installTemplatewrites.create-iai-state.jsoninto the target FIRST ({ version, startedAt, mode }); the finish step removes it. A leftover marker therefore means a full install died midway.- A folder with the marker is a resume, never an "existing project":
re-running the installer there warns, recommends continuing the full
install (the pipeline is idempotent — it reuses the folder,
.env.localand any cloud resources instead of duplicating Convex/Clerk), and skips the agent-files question (the agent files present are the template's own half-copied tree). Guest mode + marker is a hard error — only the gated template flow can resume safely. - On a fatal error or Ctrl-C, the CLI cleans the tmp download and — only for
folders it CREATED itself — offers to delete the partial project (default
no; never under
--yes). If.env.localalready points at cloud services, it warns first that deleting the folder does NOT delete them and that.env.localis the only local record (dashboard.convex.dev, dashboard.clerk.com, console.neon.tech).
A target folder that already has agent files (.claude, .cursor,
.cursorrules, .agents, .pi, .windsurf, .windsurfrules, CLAUDE.md,
HARNESS.md, imp/HARNESS.md) gets the question up front (or
--agent-files add|replace):
- add (default) — only what's missing is copied; every pre-existing file wins. In full mode the template copy protects those subtrees and merges them per-file.
- replace — the current agent files are MOVED (never deleted) to
.agents-backup-<YYYY-MM-DD_HH-mm-ss>/inside the project, then the incoming ones land.
App code, ai-docs/, docs/, AGENTS.md (always append-merged), root
.mcp.json and imp/ are never part of the policy.
| Group (flag) | Options | Default (padrao) |
|---|---|---|
--addons (Quality/DX) |
commitlint, knip, analyzer |
commitlint, knip, analyzer |
--observability |
sentry, logging |
sentry |
--analytics (single) |
none, posthog, vercel-analytics |
none |
--security |
csp, rate-limit |
csp, rate-limit |
--emails (single) |
none, resend |
none |
--platform |
notifications |
none |
--payments (single) |
none, stripe, asaas, clerk-billing |
none |
Presets (--preset): minimo (nothing), padrao (the recommended set),
saas (padrao + logging, posthog, notifications, resend, stripe). completo
predates the english-first rename and is a deprecated alias of saas
(accepted with a warning; no longer listed in pickers).
Precedence: group flag > --preset > default (padrao). A group flag
fully overrides its group; the other groups keep their preset values. Lists
accept none and all. The three single groups are validated at parse time
(a bad --payments value is a fatal error); multi-group values are validated
at resolve time (bad ones warn and are ignored). Examples:
npx impactus my-saas --preset saas --payments asaas # saas set, stripe swapped for asaas
npx impactus my-mvp --preset minimo --observability sentry --yes
npx impactus my-app --yes --addons none --security csp # padrao base, quality emptied, security reducedInteractively, the decisions phase offers the presets (Default (recommended) / Minimal / SaaS / Customize… — one question per group,
recommended options pre-selected), then one summary with an
"Adjust the choices…" loop before anything executes. Under --yes the
padrao preset applies and the summary is informational.
Always included (not a choice): TypeScript strict, T3 Env (env vars
validated at build), Vitest + convex-test, Playwright, ESLint + Prettier,
Lefthook, SEO (sitemap/robots/OG), CI, Dependabot, .vscode, i18n pt-BR/en,
Documents page (upload → Convex Storage or R2, decided at runtime).
| Addon | Key files | Envs (where) |
|---|---|---|
| commitlint | commitlint.config.mjs + commit-msg hook in lefthook.yml |
— |
| knip | knip.json, check:deps script |
— |
| analyzer | wrapper in next.config.ts, build-stats script |
— |
| sentry | sentry.*.config.ts, instrumentation*.ts, app/global-error.tsx |
NEXT_PUBLIC_SENTRY_DSN (+ ORG/PROJECT/AUTH_TOKEN for sourcemaps) |
| logging | lib/logger.ts (LogTape, JSON in prod) |
— |
| posthog | components/analytics/posthog-provider.tsx |
NEXT_PUBLIC_POSTHOG_KEY |
| vercel-analytics | <Analytics /> in components/analytics.tsx |
— |
| csp | full CSP in next.config.ts (inverse block removes the baseline) |
— |
| rate-limit | convex/lib/rateLimiter.ts + calls in mutations |
— |
| notifications | convex/notifications.ts, convex/lib/notificationKinds.ts registry, bell in the header; email channel when resend is also present (signup-hooks virtual) |
— |
| resend | convex/emails.ts + single template convex/lib/emailTemplate.ts + scheduling in users.upsertFromClerk |
RESEND_API_KEY (Convex) |
| stripe | convex/stripe.ts, convex/subscriptions.ts, /stripe-webhook webhook, billing page |
STRIPE_* + SITE_URL (Convex) |
| asaas | convex/asaas.ts, convex/assinaturasAsaas.ts, /asaas-webhook webhook, billing page, skill |
ASAAS_* (Convex) |
| clerk-billing | components/billing/clerk-pricing.tsx (PricingTable) |
plans in the Clerk dashboard |
All degrade gracefully without their key (no-op/warning), so the app runs immediately after the install and each service is activated whenever you want.
For each chosen addon with official tooling, the CLI installs the agent
skills into the project (via skills.sh, recorded in
skills-lock.json) and offers to install/log into the official CLI
(pipeline step 20):
| Addon | Skills (npx skills add …) |
Official CLI | Login | Keys/dashboard |
|---|---|---|---|---|
| stripe | https://docs.stripe.com (all) |
stripe (brew stripe/stripe-cli/stripe) |
stripe login |
https://dashboard.stripe.com/apikeys |
| sentry | getsentry/sentry-for-ai — only the 4 relevant of ~35: sentry-nextjs-sdk, sentry-get-started, sentry-debug-issue, sentry-fix-issues |
sentry-cli (npm @sentry/cli) |
sentry-cli login |
https://sentry.io/settings/auth-tokens/ |
| resend | resend/resend-skills (all) |
resend (npm resend-cli) |
resend login |
https://resend.com/api-keys |
| asaas | — (no official skills; PROJECT skill in .claude/skills/asaas) |
— (no CLI; REST API) | — | https://sandbox.asaas.com · https://www.asaas.com |
| r2 (storage) | cloudflare/skills (cloudflare + wrangler) |
wrangler |
wrangler login |
dash.cloudflare.com → R2 |
ensureIntegrationCli is shared with the stack-tooling step (§3.7): each
binary is offered ONCE per install; install order brew → npm -g → pip
(5-minute timeout each); the login confirm defaults to no; --yes prints
the hint and installs/logs nothing. Everything is best-effort: a network
failure on skills never aborts the installation, and the final summary lists
the commands to redo it manually.
Central catalog: SERVICES in src/config.js — for each external service
(Clerk, Convex, Stripe, Asaas, Resend, Sentry, PostHog, R2) it declares what
is automatic, the envs (exact name, convex/local destination, format
regex) and the AI prompt steps. Three consumers:
- Web UI (
--ui) — "Integrations & keys" section: each service becomes a card with its status (✅ automatic at install · optional key), a "Copy AI prompt" button (to paste into a browser-automation extension, e.g. Claude in Chrome — the agent finds/creates the keys in the dashboard and returnsKEY=valuelines), a paste box that fills the fields by itself, and regex-validated fields. Pasted keys are saved to~/.create-iai/keys/<slug>.env(permission 600, machine-local only) and the generated command references the path via--keys— no secret ever appears in the command/history. - Terminal (
steps/service-keys.js) — same flow without the UI: per service, a select offers "See the AI prompt and paste the keys" (default) / "I already have the keys — paste now" / "Skip — activate later (the feature stays dormant, nothing breaks)". Each env is validated against its regex (masked prompt for secrets) and written where the template reads it (npx convex env set+ mirror in.env.local, or.env.localonly for theNEXT_PUBLIC_*ones). - Webhooks via API — with the key in hand, the CLI creates the
Stripe webhook (
POST /v1/webhook_endpointswith the eventscheckout.session.completed,customer.subscription.updated,customer.subscription.deleted, theapi_versionpinned to the stripe-node version INSTALLED in the project, capturing thewhsec_) and the Asaas one (POST /v3/webhooks— production or sandbox base perASAAS_ENV— withauthToken= the locally generatedASAAS_WEBHOOK_TOKEN, the same value the backend validates in theasaas-access-tokenheader) pointing tohttps://<deployment>.convex.site/.... Failed? It prints the manual step.
The exact env catalog this step applies:
| Service | Env (destination) | Validation |
|---|---|---|
| stripe | STRIPE_SECRET_KEY (Convex, secret) |
^(sk|rk)_test_… — TEST keys only; restricted rk_test_ accepted, sk_live_ rejected |
STRIPE_PRICE_ID (Convex) |
^price_… |
|
STRIPE_WEBHOOK_SECRET (Convex, auto via API) |
^whsec_… |
|
SITE_URL (Convex, auto) |
http://localhost:3000 in dev |
|
| asaas | ASAAS_API_KEY (Convex, secret) |
^\$?aact_… |
ASAAS_ENV (Convex, user) |
production | sandbox |
|
ASAAS_VALUE (Convex, user) |
e.g. 49.90 |
|
ASAAS_WEBHOOK_TOKEN (Convex, generated locally) |
random 24-byte base64url | |
| resend | RESEND_API_KEY (Convex, secret) |
^re_… — test mode until a domain is verified (EMAIL_FROM, RESEND_TEST_MODE=false for prod) |
| sentry | NEXT_PUBLIC_SENTRY_DSN (.env.local) |
https://…@…/<id> |
| posthog | NEXT_PUBLIC_POSTHOG_KEY (.env.local) |
^phc_… |
NEXT_PUBLIC_POSTHOG_HOST (.env.local) |
https://us.i.posthog.com or https://eu.i.posthog.com |
Extra --keys-only keys (applied straight to Convex when the matching addon
was chosen): EMAIL_FROM, RESEND_TEST_MODE, ASAAS_DESCRIPTION.
Security rules: every secret is redacted from the run log; a dashboard env
left at its factory default activates nothing; everything is optional (without
a key the addon degrades as always); the final summary shows the report
(ctx.serviceReport) and offers to delete the keys file that was used.
Every official skill (this step, the storage step and the stack step) goes in
through the same door: src/lib/skills.js, which drives the
skills.sh CLI — the skills npm package, the standard the
vendors publish against. The sources are declared as data:
ADDON_TOOLING[*].skills and OPTIONAL_SKILLS in src/config.js, skills
per option in src/stack-catalog.js.
On disk, per engine:
| Engine | Path | How it gets there |
|---|---|---|
| Cursor | .agents/skills/<name>/SKILL.md |
the canonical store, read directly ("universal") |
| Claude Code | .claude/skills/<name> |
symlink to the store |
| Pi (FIA agents) | .agents/skills/<name> |
native .agents/ discovery — no copy of its own |
skillsAddArgs(spec, agents) builds the argv and installProjectSkills runs it
once, with -a claude-code cursor. Measured against skills@1.5.22
(Aug 2026): -a claude-code alone never creates .agents/skills/ (Cursor gets
nothing), and the comma form -a a,b is rejected and installs nothing. The
-a stays last in the argv so the variadic cannot swallow the -y. A failure
warns without failing the step (as ever, nothing about skills aborts an
install; the manual command is printed).
Pi needs no leg of its own: it scans the project's .agents/skills/ natively
(behind the same project-trust gate as .pi/skills/, in interactive and
headless runs alike), so one canonical copy serves the three engines. Older
CLI versions DID run a second skills add … -a pi per source — and since Pi
dedupes discovered skills by realpath, that real copy made every skill load
twice and opened each Pi session with a "Skill conflicts" panel listing all of
them. prunePiSkillCopies(dir) cleans those leftovers: lock-driven (so
harness-owned skills like .pi/skills/fia/ are never touched) and
conservative (a copy is only removed when its .agents/skills/ canonical
exists). It runs in the FIA step and in --update-runtime, which is how
projects stamped by the old flow heal on their next update.
The install is recorded in skills-lock.json (v1: {version, skills: {<name>: {source, sourceType, skillPath, computedHash}}}), which the project commits —
.agents/ and the vendor folders under .claude/skills/ are gitignored. Hence
the commands worth knowing in the generated project:
npx skills list # what is installed
npx skills find <query> # search the catalog (--owner <org> to narrow)
npx skills update [name] # update one skill, or all
npx skills experimental_install # restore everything from skills-lock.jsonnpx skills use <pkg>@<skill> prints a single skill's prompt without
installing anything — handy for a one-off. And --verify audits the coverage
(see §14.1).
Always present at /dashboard/documentos: multi-file upload with
drag-and-drop and progress, download via signed URL and deletion with
confirmation. The backend decides the destination at runtime:
- Convex File Storage (default, zero config):
ctx.storage.generateUploadUrl. - Cloudflare R2: activates when the four envs
R2_ACCOUNT_ID,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,R2_BUCKETexist in the Convex deployment — a partial fill just stays on Convex Storage until completed. Uses the official@convex-dev/r2component; the endpoint is derived from the Account ID.
Validation (MIME allowlist + 25 MB) is server-side in convex/documentos.ts;
the browser sends the binary STRAIGHT to storage via signed URL.
The storage step (pipeline step 18) wires R2 with a wrangler assistant:
installs/logs into wrangler if wanted, picks the Cloudflare account from
wrangler whoami, creates the bucket (suggested name <project>-files;
"already exists" counts as success) and applies the CORS policy — Cloudflare's
rules/allowed JSON shape, NOT AWS S3 style:
{ "rules": [ { "allowed": {
"origins": ["http://localhost:3000"],
"methods": ["GET", "PUT"],
"headers": ["Content-Type"] },
"maxAgeSeconds": 3600 } ] }(an extra production origin can be added during the prompt). The one thing
wrangler cannot do is the S3 API token — dashboard-only, Secret shown only
once: Cloudflare → R2 → Manage API Tokens → Create API Token, minimum scope
"Object Read & Write" on the project bucket. The four values are prompted
(masked for the secret), set with npx convex env set and mirrored into
.env.local. Fully non-interactive R2 is possible with
--yes --storage r2 --keys <file> when the file carries all four keys (the
R2 AI prompt returns exactly that). The installer also installs the official
Cloudflare agent skills (cloudflare + wrangler) so the coding agent knows
R2 by heart from day one.
The installer's base — always installed, in both modes. Downloads the
harness through the community API (the only path — with the student token, or
anonymously in guest mode: harness + FIA are the free tier)
and merges without overwriting anything — existing files
win, the harness README.md becomes imp/HARNESS.md, and its AGENTS.md is
appended to the project's between the <!-- harness-start/end --> markers.
For Claude Code AND Cursor it brings 21 slash commands, 9 specialist
agents, 6 skills and the whole ai-docs/ scaffold: PRD template, maps,
task roadmap (todos/), specs (specs/0000-example.md), milestones, inbox,
decisions/, ui/ (interaction patterns), components/ (140+ reference docs),
apis/, examples/ and start/map-start.yaml.
Skills shared with the templates (HARNESS.templateOwnedPaths — the four
professional ones, in .claude/skills and .cursor/skills): the harness is
the SINGLE SOURCE of this material. In full mode the harness copy of a path
is discarded only when the installed template actually shipped that path
(the merge runs after the template install, so the destination already
reflects it) — a template that brings its own variant wins in that path
(live2 ships only security, its multi-tenant variant), and a template
without the skill inherits the harness version. In harness mode (no
template) everything comes from the harness. The asaas skill belongs to an
addon and lives only in the templates.
In full mode it runs last (the git repo already exists, created during the
template install); in harness mode the folder may have no git — the step
runs git init before the best-effort commit. The --no-harness/
--skip-harness flags only apply in full mode (template WITHOUT the
harness); in harness mode they are ignored with a warning.
The harness also ships .claude/settings.json with
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 (required by /team) and the two
fda-lock hooks (SessionStart warn + PreToolUse gate — the read-only guard
while an FDA runs, §9.5), plus .mcp.json/.cursor/mcp.json with the
playwright and convex MCP servers.
The core build loop:
| Command | What it does |
|---|---|
/start [--components] [--restart] |
6-step project initialization: PRD check → screens-routes.md → task breakdown (vertical slices, one issue file per task with Blocked by:, specs + milestones) behind a mandatory approval checkpoint → map.yaml → component registry seeding → live /ui-components page. Resumable (workflow_progress in map.yaml persists after every step); --restart starts over; --components skips the step-5/6 confirmation. Missing PRD stops with a /grill suggestion. |
/dev [task?] |
Executes ONE dev task test-first. No argument = the next frontier task (pending, all blockers done). The task-sequencer writes a just-in-time brief in ai-docs/actual-todo/; four gates run before code (blocking questions, registry-only components, interaction patterns from ai-docs/ui/patterns.md, spec traceability markers); TDD loop (red → green at pre-agreed seams); npm run build mandatory for Kind: foundation/Kind: kit briefs; self code-review; closes the task and recomputes the frontier. Also enforces the theme gate and the env-preflight gate (§10). |
/sv [msg?] |
Save: npm run build gate → docs-sync check (schema/deps diffs must be reflected in ai-docs/stack.md/specs) → conventional commit → Convex export backup to ~/Documents/convex-backups/. |
/test-ui [flow?] |
Tests the UI in a real browser (Playwright MCP; Chrome DevTools MCP fallback) at http://localhost:3000: restarts the dev server if needed, walks the flow (default: sign-in), watches console errors and 4xx/5xx, fixes what it finds, re-tests, prints a fixed-format report. Credentials come from the ai-docs/test-credentials.md roster (Clerk test users +clerk_test / code 424242), map.yaml, or the command-file placeholders. |
/team <task> |
Multi-agent orchestration: splits the task into independent subtasks and launches the specialist agents in parallel (frontier tasks are independent by construction; each is claimed before dispatch). |
/restore [hash?] |
Destructive rollback with confirmations: git reset --hard <commit> and/or npx convex import --replace from a backup. --prod never by default. |
Planning, specs and scope:
| Command | What it does |
|---|---|
/grill [doc|topic?] |
Stress-tests the PRD (default) or any doc/decision — one question at a time, each with a recommendation, decisions recorded in the decision log and written back into the document. Hunts placeholders, missing actors/permissions/edge cases and a ## Launch criteria section. Run BEFORE /start. |
/stack [tech|layer?] |
Owns ai-docs/stack.md: decides pending layers by interview (IAI preference rules), researches each tech across the 4 mandatory dimensions (docs+llms.txt, skills, CLI, MCP) into the code-verified research ledger, writes ai-docs/apis/<tech>.md (9 required sections incl. the Production runbook /launch executes), and equips the project (skills in two invocations, CLIs, MCPs). |
/spec [capability|NNNN?] |
Creates/updates a durable spec ai-docs/specs/NNNN-<slug>.md (requirements FR/NFR, BDD scenarios S-n, traceability, gate log) via a short interview. Definition Gate flips Status: defined. |
/feature "what you want" |
New functionality on an EXISTING system: size triage (module-sized → /idea in Pi), delta mini-grill, delta spec, then ONLY the new tasks (numbering continues, Blocked by: real tasks), shown for approval before anything executes. Requires map.yaml (/absorb first on a never-onboarded system). |
/bug "the symptom" |
Registers the defect as an issue, then fixes it through the normal pipeline with a RED-for-the-right-reason gate: the reproduction test must fail on an assertion before the fix (a passing or broken test never counts). |
/quick "small change" |
Triage: SIMPLE only when blast radius ≤ ~3 files, one obvious shape, and none of: schema/migrations, auth/permissions, payments, new dependency, new route/page, new UI component, destructive data op. SIMPLE ships in one sitting with the guardrails on + one ## Q-NNN audit line in ai-docs/todos/quick-log.md; anything else routes to /feature//bug with the reason. Never touches the roadmap. |
/note "idea" |
Appends - [ ] YYYY-MM-DD — <idea> to ai-docs/inbox.md and stops — zero questions. Later /feature//quick//spec tick items with → spec NNNN / → Q-NNN / → task NN. |
/absorb [focus?] |
Onboards an EXISTING system: surveys the code, writes map.yaml, the as-built PRD (never overwriting a human PRD — PRD-as-built.md instead), stack.md from what was observed, conventions.md, the as-built component registry and a distilled project skill; short interview for what code can't reveal. Changes no code, creates no tasks; recommends /kit when the registry comes out empty/duplicated. |
Design system and references:
| Command | What it does |
|---|---|
/component <name + URL/cmd> | list | sync |
The legal entry path for a new UI component: duplicate check against the registry first (roles default/alternative resolved), research → ai-docs/components/<lib>/<name>.md, install, adapt to theme/i18n/a11y (semantic-domain fields ship with their canonical source wired), registry row, /ui-components section. sync reconciles registry ↔ code ↔ page; list prints the registry by category. |
/theme [hint|accept?] |
Visual identity behind a side-by-side preview: ~7-question interview (colors, dark/light, typography via next/font, shape, interaction patterns), generates the full token set (WCAG AA contrast is a blocker), renders Current × Proposed at /ui-components/preview with REAL registry components, and only applies to app/globals.css after explicit approval. accept fast-path records a conscious "keep the default" decision — enough to satisfy the theme gate. |
/design <images + description> |
Layout redesign from reference images: structure/hierarchy/density/motion come from the reference, colors/fonts/components stay OURS (theme + registry only). Contained scope applies directly; broad scope becomes roadmap tasks. Uses the Impeccable skill for motion when installed. |
/example <url> [notes] | list |
Registers an external reference on the example shelf: reads the source (never registers from a URL alone), pins license + commit, writes ai-docs/examples/<slug>/NOTES.md (mandatory ## What NOT to take) + a registry row. GPL-family/unknown licenses are never copied verbatim. |
/kit [focus?] [--report-only] |
Brownfield design-system audit: as-built registry rows → /ui-components page → gap report vs the core kit (kit-report.md: missing needs, below-contract items with file/line evidence — the DataTable contract audited item by item — duplicates without roles) → engineer approves → delta spec + Kind: kit design-only tasks with one checkbox per contract item. Changes no component and no screen itself. |
Going live and meta:
| Command | What it does |
|---|---|
/launch |
From "runs on my machine" to LIVE, in rungs: Ready gate (lint/typecheck/test/build green, pushed, CI green) → Safe gate (the security skill's checklist walked top to bottom; two hard stops: open data-ownership and any committed secret) → BETA (public vercel.app + production backend: Convex prod deploy key, env promotion, deploy, smoke test) → PRODUCTION (own domain, pk_live_/sk_live_, prod webhooks, live payments with one real tested charge). Uses node imp/scripts/fia-launch-check.mjs --json as the source of truth when the FIA is installed; everything logged in ai-docs/launch.md; every outward step needs an explicit "yes". |
/create-command [name] [description] |
Scaffolds a new professionally-structured slash command in .claude/commands/ and mirrors it to .cursor/commands/. |
Every command that generates durable docs commits them via
node imp/scripts/docs-commit.mjs (when the FIA is present) and guards its
imp/scripts/* usages with a manual fallback — the harness works with or
without the FIA runtime.
| Agent | Role |
|---|---|
task-master-generator |
Breaks the app into vertical-slice (tracer bullet) tasks: todos/task-master.md index + one issue file per task with explicit blocking deps; FULL mode also generates specs; DELTA mode (used by /feature, /kit) only adds new issues. |
task-sequencer |
Prepares (never implements) the next task: picks/claims the frontier task and writes the just-in-time brief with "Seams & First Tests"; enforces the theme and env-preflight gates for foundation tasks. |
screen-routes-generator |
PRD → ai-docs/screens-routes.md (screens, routes, navigation flows). |
start-mapper |
Reads the whole codebase → ai-docs/map.yaml (mapping only). |
start-scaffolding |
Folder structure + empty placeholders only — explicitly no implementation code. |
component-architect |
Seeds the component registry: core-kit planned rows in scaffold-less greenfield, as-built installed rows when code exists (/kit runs it in as-built mode). |
ui-component-page |
Creates/updates the live /ui-components design-system page (every registry component rendered for real, searchable, by category). |
ui-component-researcher |
Researches/documents a single UI component into ai-docs/components/<lib>/<name>.md. |
api-docs-researcher |
Researches an external API/technology and writes the project-tailored doc into ai-docs/apis/ (also logs the four research dimensions). |
.cursor/agents/ are symlinks to .claude/agents/ (canonical). Cursor
additionally ships router skills (project-workflow + workflow-* wrappers
for the original 8 pipelines) because Cursor routes by skill.
Six skills for both engines: tdd (the red→green loop /dev follows),
frontend-profissional, design-system (incl. references/core-kit.md
— the canonical component contracts — and references/semantic-fields.md —
known-domain data never becomes a free-text input), security (incl. the
/launch checklist and the multi-tenancy reference), backend-profissional
and examples (the reference-shelf matching rules). The four professional
ones are the template-ownable paths described above.
With the FIA installed, npm run plan opens the viewer's "Plan" tab
(http://127.0.0.1:4600#plan, 100% offline) with the screens/routes, tasks
with blockers/criteria, design system and every ai-docs/ file rendered —
/map opens it automatically when it finishes. npm run agents (or
/agents inside pi) opens the "Agents" tab to see engine login status and
edit each FDA agent's engine/model/reasoning and fallbacks: chain — saving
rewrites imp/fia.config.yaml preserving comments (locked while an FDA
runs). See §9.6.
The FIA is the agent runtime the CLI stamps into every project (unless
--no-fia): Pi as the interactive agent (§12), deterministic FDAs
(fully-automated dev agents, imp/fda_*.mjs) as the workforce, an
observability SQLite (imp/data/fia.db), quality gates and dashboards.
Everything runs inside the subscriptions the student already has — Claude
through the official claude CLI (Pro/Max), Codex through Pi (ChatGPT
Plus/Pro) — never API keys, never per-token billing.
- Migrates any legacy layout first (
fia/→imp/, rootHARNESS.md/iai.config.json→imp/— §14.2). - Installs/updates the Pi CLI (
npm install -g @earendil-works/pi-coding-agent) WITHOUT any interactive login — the Codex login is deliberately the last step, after the install. A failed Pi install degrades (warns, EACCES gets a dedicated explanation, the rest of the install continues without FIA). - Stamps
imp/(from the bundledfia-templates/) and.pi/(frompi-templates/.pi/) — copy-skip-existing, never overwrites; a partial stamp is a hard error telling you to re-run (it only adds what's missing). - Ensures the gitignore entries (
imp/node_modules/,imp/data/sessions/, the SQLite files, backups), merges the npm scripts below (a name conflict keeps YOUR script and ships ours as<name>:fia), and records the runtime manifest (imp/.runtime-manifest.json, template sha1 per stamped file — the--update-runtimebaseline; on re-runs existing baselines win). npm installinsideimp/; installs the three Pi packages (pi-subagents,pi-mcp-adapter,pi-web-access), each exact-pinned to the latest npm version — the pin is what keeps Pi's "Package Updates Available" banner away; a student-customized entry (fork/git/path) is never touched.- Installs the global
implauncher (npm install -g impactus) so the brand command exists on PATH — best-effort, never fails the install. - Prints the final note — including, when Codex isn't logged in yet: run
imp, type/login openai-codex, finish in the browser, and never log in to Anthropic inside Pi (there the Claude subscription bills per token as "extra usage"; Claude runs through theclaudeCLI in the FDAs).
npm scripts stamped into the project (FIA.npmScripts):
npm run fda:demo # node imp/fda_prompt.mjs "Summarize this repo…" --agent scout
npm run fda:quality # node imp/fda_quality.mjs "quality gate"
npm run fda:sessions # node imp/scripts/fia-query.mjs sessions
npm run fda:phases # node imp/scripts/fia-query.mjs phases
npm run fda:tail # node imp/scripts/fia-query.mjs tail
npm run fda:viewer # node imp/scripts/fia-viewer.mjs (web viewer)
npm run plan # viewer --view plan (Plan tab)
npm run agents # viewer --view agents (Agents tab)
npm run launch:check # node imp/scripts/fia-launch-check.mjs (read-only readiness)
npm run env:check # node imp/scripts/env-preflight.mjs (dev keys preflight)
npm run fda:status # node imp/scripts/fda-lock.mjs status (is an FDA running?)
npm run docs:commit # node imp/scripts/docs-commit.mjs (ai-docs-only commit)
npm run tui # node imp/scripts/fia-tui.mjs (terminal dashboard)
npm run handoff # node imp/scripts/handoff.mjs (continue the newest
# Pi conversation in `claude` — see §14.3, `imp handoff`)Every runner shares the same CLI contract:
node imp/fda_<name>.mjs "<prompt-or-brief-path>" [--config imp/fia.config.yaml]
[--fda-id <id>] [--resume] [--agent <name>] [--debug]
The prompt may be inline text or a file path (a brief file is inlined).
--resume requires --fda-id and may omit the prompt — it is reloaded from
the trace. --agent is only read by fda_prompt. --debug (or env
FIA_DEBUG) prints full stack traces. A failed run always prints the exact
resume command (node imp/fda_<name>.mjs --fda-id <id> --resume) — nothing
is lost. Exit codes: 0 accepted · 1 any failure (including "phases green but
not accepted") · 130/143 on SIGINT/SIGTERM (the session is marked failed
first, never left as an eternal running).
| Runner | Agents | Phases (code phases in italics) | Use it for |
|---|---|---|---|
fda_prompt |
any (default builder) | request → prompt | One agent, one prompt, traced end to end. npm run fda:demo is this with the scout. |
fda_plan |
planner | request → plan | A written plan, no code changes. |
fda_build |
builder | request → build | Implement from a plan/brief — no tests, no commit. |
fda_scout |
scout (read-only) | request → scout | Recon: "where is billing implemented?" — any repo change is rolled back. |
fda_document |
documenter | request → document | Write up recent changes (docs paths only). |
fda_quality |
none | request → quality | Lint + typecheck + build + test with no agent — works with nothing logged in. |
fda_plan_build_test |
planner, builder (+reviewer for the UI gate) | request → plan → build → test → up to 3 × (fix → test) → spec_coverage → checklist gate → UI gate → commit | The task workhorse (/task uses it via the sequencer). |
fda_sdlc |
planner, builder, reviewer, documenter | request → plan → build → test (single run, no fix loop) → spec_coverage → checklist gate → UI gate → review → commit_code → document → commit_docs | Full cycle with an independent review — the review runs even when tests failed, and acceptance requires green tests AND an approved review. |
fda_bug |
planner, builder (+reviewer) | request → plan → red_test → red_check → build → test → fix loop (≤3) → gates → commit | Defect fixing with a valid RED gate: the reproduction test must fail on an assertion BEFORE the fix (passing = "bug not reproduced"; module/syntax/env failures = invalid RED). |
fda_quick |
builder | request → build → quality_1 (lint+typecheck+focal test) → one fix round → quality_2 → quicklog → commit | Small guarded changes (/quick). Appends the ## Q-NNN audit entry, then stamps the commit sha into it as a separate one-line commit. |
Examples:
node imp/fda_plan_build_test.mjs ai-docs/todos/briefs/task-07.md
node imp/fda_bug.mjs "Deleting the last org member 500s instead of blocking"
node imp/fda_quick.mjs "Make the empty-state copy on /invoices friendlier"
node imp/fda_sdlc.mjs ai-docs/todos/briefs/task-12.md
node imp/fda_plan_build_test.mjs --fda-id 3fa9c21b --resume # resume a failed runAn engine exit without a parseable Report envelope fails fast with the last stderr lines and a matched recovery hint: login-shaped errors point at the right login command per engine; rate-limit/quota errors say to wait and re-run ("no extra payment is needed"). Malformed envelope JSON gets 2 re-asks before failing.
FDAs never name a model — they name an agent; imp/fia.config.yaml maps
agents to engines/models. Edited by hand or via the Agents tab (npm run agents / /agents in pi), which preserves comments. Never touched by
--update-runtime (it's yours). The essentials:
defaults:
coding_agent: pi # engine for agents that don't say otherwise
model: openai-codex/gpt-5.6-sol
thinking: high # minimal|low|medium|high (Pi engines)
relay: auto # engine death mid-run: auto|resume|off
tools: [read, bash, edit, write, grep, find, ls] # Pi tool allowlist
protected_files: # deny-list enforced for EVERY agent
- imp/modules/
- imp/fia.config.yaml
- imp/fda_*.mjs
- imp/scripts/
- imp/data/prompt_engineering/
data_dir: imp/data
permissions:
benign_paths: [] # extra globs reverted as build side effects
observability:
db: imp/data/fia.db
agents:
- name: planner
coding_agent: claude_code # official `claude` CLI — plan billing
model: opus # alias (sonnet|opus|haiku|fable) or full name
effort: high # low|medium|high|xhigh|max|ultracode
fallbacks: # up to 5; run start, mid-run, resume
- { coding_agent: pi, model: openai-codex/gpt-5.6-sol, thinking: high }
prompt_engineering: # REQUIRED per agent (never inherited)
system: imp/data/prompt_engineering/planner/system.md
user: imp/data/prompt_engineering/planner/user.md
writes: [specs/, ai-docs/] # allowlist; [] = read-only; omit = anywhereShipped roster: planner (claude_code/opus, writes specs+ai-docs), builder (pi/Codex, writes anywhere but protected), scout (pi/Codex, read-only), reviewer (claude_code/sonnet, read-only), documenter (pi/Codex, docs paths only). Each ships a fallback to the other engine.
Engines (coding_agent):
| Engine | Binary | Model examples | Notes |
|---|---|---|---|
claude_code |
claude (override CLAUDE_PATH) |
sonnet, opus, haiku, fable, or full names |
Runs on the Claude Pro/Max plan. effort sets reasoning depth; system prompt via --append-system-prompt (preserves the cacheable prefix). |
pi |
pi (override PI_PATH) |
openai-codex/gpt-5.6-sol, openrouter/…, xai/…, github-copilot/… |
Session continuity via a session FILE; thinking sets reasoning; per-agent tools and harness_engineering (Pi extensions). Subscription providers log in via /login openai-codex / github-copilot; API-key providers read their env var (OPENROUTER_API_KEY, XAI_API_KEY, …). |
cursor |
cursor-agent (override CURSOR_AGENT_PATH) |
picker ids like sonnet-4.5, gpt-5, composer-1 |
Cursor subscription; no token usage reported; system prompt is prepended to the first prompt. |
Fallbacks (up to 5 per agent) are walked in three stages. At run start,
for hard unavailability (binary missing; Pi provider with no login and no API
key) — traced as engine_fallback. Mid-run, when the engine dies inside a
phase (exits without a report, or its binary vanishes): the failure is
classified (login | limit | missing | crash), the run switches to the
next viable fallback in place and retries the phase — traced as engine_error
engine_relay(§9.5). On resume, the interrupted run's failure markers arm the chain even when the binary checks pass (skipped underrelay: off).defaults.relaychooses the policy:auto(the default when the key is absent — switch in-run and on resume),resume(fail fast mid-run; the fallbacks arm only under--resume),off(never auto-switch, mid-run OR on resume; the death is still recorded and traced). Every switch is printed and traced — never silent. The config header repeats the golden billing rule: Claude INSIDE Pi bills per token as "extra usage" — always usecoding_agent: claude_codeto stay on the plan.
Permissions: every agent phase snapshots the working tree (git diff +
hashed untracked files); writes outside the agent's writes allowlist (or in
protected_files) are rolled back and fail the phase as a PermissionBreach.
Benign build side effects (AGENTS.md, next-env.d.ts, **/*.tsbuildinfo,
.next/, node_modules/, coverage/, … + your benign_paths) are reverted
and logged as external_change — never blamed on the agent. Read-only agents
deposit their reports in the git-invisible session dir
(imp/data/sessions/<id>/context_handoff/), handed to prompts as
{{context_handoff_dir}}.
Prompt material lives in imp/data/prompt_engineering/<agent>/{system, user}.md — student-editable, never touched by --update-runtime. Templates
receive {{prompt}}, {{previous_envelope}} and {{context_handoff_dir}}.
Envelopes: every agent phase must end with a typed Report JSON
(status, summary, artifacts, notes_for_next_agent, plus per-type
fields like changed_files, approved/findings/blocking,
document_path). Gates validate them: artifactsExist/filesNonEmpty fail
on an empty artifacts list (an agent that produced nothing can no longer
count as a success); verdictConsistent refuses an "approved" review with
blocking items.
The code phases run the project's own scripts, sequentially, with verbatim
output tails fed to fix rounds: npm run test (600 s), npm run lint
(120 s), npm run typecheck/type-check (180 s), npm run build (600 s).
A brief carrying Kind: foundation or Kind: kit arms npm run build
alongside the suite in the test phase (in code, before any reviewer).
fda_quick runs lint + typecheck always and the focal test only when the
builder declared a *.test.* file — the full suite is deliberately skipped
as disproportionate.
- A failed FDA prints the exact resume command (
node imp/fda_<name>.mjs --fda-id <id> --resume) instead of a raw stack trace; pass--debugto see the full stack. With--fda-id … --resumethe original prompt is loaded from the session database — no need to retype it. - Resume replays what already succeeded — with the right exceptions.
Deterministic
codephases (tests, gates) re-run so they verify the CURRENT tree; succeeded agent phases are reused. Three deliberate exceptions:ui_verifyalways re-runs (a verdict about the current tree must not fossilize a rejection);fda_bug'sred_checkreplays its saved verdict once the fix has been built (a one-way gate — re-running would misread the fixed code as "bug not reproduced");fda_quick'squicklogreuses the entry it already appended (the append is not idempotent). - Agent phases retry once by default (
retries: 1) before failing the run. - An engine that dies mid-phase no longer takes the run with it. An exit
without a report, or a binary that vanished, is classified (
login|limit|missing|crash) and written toimp/data/sessions/<fda_id>/<agent>/engine_error.json; the run then switches to the next viable entry of the agent'sfallbacks:chain in place (the substitute owns that agent's later phases too) and retries the phase.login/limit/missingswitch on the first death — waiting cannot renew an expired login and a plan limit outlives the run; acrashretries the SAME engine once (a transient CLI death must not demote the chosen engine) and switches on the second consecutive crash. Traced asengine_error+engine_relay, printed as⚠ <agent>: <engine> died mid-run.defaults.relayinimp/fia.config.yamlpicks the policy:auto(default),resume(fail fast mid-run; the chain arms only on--resume) oroff(never auto-switch — the marker is still recorded). - Resume prefers the engine that can actually finish:
--resume --fda-id <id>reads the interrupted run's markers and walks the fallbacks even though the binary checks pass — that engine already proved it cannot finish. With no viable fallback the resumed run retries the primary out loud instead of blocking at the door (limits reset, outages end). - The substitute inherits the interrupted attempt. Whichever engine takes
over — a fallback, or the same engine retrying cold — gets a continuation
block prepended to its USER prompt (never the system prompt, which stays
byte-stable for caching): it points at the dead attempt's transcript
(
imp/data/sessions/<fda_id>/<agent>/raw_output.jsonl) as a read-only historical record with no authority over it, names the workspace (git status+ the files themselves) as the only authority on current state, and requires the new engine to state where the previous attempt stopped and continue from there instead of starting from scratch. Exception: Pi taking over from Pi resumes its own session file natively (--session pi_session.jsonl) and skips the preamble. Traced asengine_continuation. Everything stays inside the subscriptions — no API keys are involved. - Marker lifecycle: markers are per-run (scoped to the
fda_id, so a fresh run never reads them) and are cleared only when the engine that died later succeeds again — exact engine+model identity. A success on a fallback KEEPS the primary's marker, so a later resume of the same run keeps preferring the fallbacks instead of bouncing back onto the dead engine. - Seeing the switches:
npm run fda:sessionsadds arelayedcount per run (run-start fallbacks + mid-run relays); the TUI's run detail prints⚠ agent: engine (model) → engine (model) — kindlines; the viewer timeline showsengine_error/engine_fallback/engine_relay/engine_continuation(all included under theerrorfilter). - One FDA at a time per project: a best-effort
imp/data/.fda.lock(pid + fda_id + runner + started_at) blocks a second concurrent run — the permission gates of two parallel runs would revert each other's work. Acquisition is atomic and full-content (a complete temp file hard-linked into place — a competing reader can never observe a half-written lock), and a stale lock is stolen via an exclusive rename, so two runs started in the same instant cannot both proceed; a pid that answers EPERM counts as ALIVE (it exists under another user), and elapsed time alone never discards a lock — only a dead pid does. If a crash leaves a stale lock, deleting the file is the recovery. - Interactive sessions go read-only while an FDA runs: the same lock has
a reader side (
imp/scripts/fda-lock.mjs, alsonpm run fda:status). A Claude session opened in the project during a run gets a READ-ONLY notice at session start (SessionStart hook) and a PreToolUse hook blocks file edits and write-shaped bash commands (incl.git commit/add/…) aimed inside the repo — an external write mid-run would be attributed to the phase agent and rolled back. Interactive Pi gets the same guard via the.pi/extensions/fda-lock.tsextension, and Cursor via the.cursor/hooks.jsonbeforeShellExecutionhook (.cursor/hooks/fda-lock-cursor.mjs— Cursor has no before-edit hook, so shell commands are the guarded surface; stray agent edits are still swept by the run's own gate). The FDA's own child agents are exempt (the runner exportsFIA_FDA_RUNinto their environment), writes outside the repo stay allowed, everything fails open, and the block lifts the moment the run ends. - FIA commits are scoped: FDAs commit only the files declared in the
agent's envelope (never
git add -A), so rejected builder changes and your own uncommitted work stay out of FIA commits. - Run baseline (anti-contamination): when a run starts, the runtime takes
a pre-flight photo of the working tree (content fingerprint per dirty path,
persisted as
imp/data/sessions/<id>/baseline.json, reloaded verbatim on--resume). At commit time, a declared path whose content is identical to the baseline is dropped: it was already dirty before the run and the run never touched it — an over-declaring builder can no longer sweep another session's leftovers into a FIA commit. A pre-dirty file the run DID modify stays in. The trace logsexcluded_pre_existingandchanged_by_run_but_uncommittedso nothing disappears silently. - Foundation commits are widened: a
Kind: foundation(orKind: kit) brief makes the commit phase union the envelope-declared paths with everything the run itself changed (baseline-diffed) — a scaffold of hundreds of files no envelope can enumerate gets committed whole instead of leaking into the tree as permanent dirt. - Agents never commit: the builder/documenter task prompts carry explicit
git rules — no
git commit/add/push(committing is the FDA's own code phase, after review), and no declaring files the task did not touch. - Spec-coverage gate: a brief with a
Spec: NNNN (S-1, FR-2)line makes the run grep every promised id againstspec:NNNN covers:…markers in test files — missing ones fail the phase by name. NoSpec:line → skipped. - Checklist gate (C8): a brief's checkboxes (Objectives, Acceptance
Criteria, Quality Checklist) can no longer be left behind by a "finished"
run. The builder ticks what it verified (
[x], or[x] … — N/A (<reason>)for inapplicable items); after the suite is green,checklist_1re-reads the brief FROM DISK (the file path survives--resumevia a session marker), onefix_checklistbuilder round repairs a forgotten checklist, andchecklist_2fails the run if any- [ ]survives — the gate refuses, it never ticks a box itself. The second pass also compares box IDENTITIES against the first (checklistDrift): rewording, moving or swapping a box is refused the same as deleting it — only the tick and the— N/A (<reason>)annotation are legal edits, and an N/A tick without a parenthesized reason fails on its own. Infda_sdlcthis happens BEFORE review, so the reviewer audits the ticks against the diff. Prompts that are not brief files, and briefs without checkboxes, skip the gate. - UI-conformance gate: a run that changed frontend component files
(
.tsx/.jsx/.vue/.sveltevs the run baseline) gets a dedicated audit phase before it may close.ui_scope(code) decides deterministically whether the gate arms: an explicitSurface:line withoutuiin the brief stands it down, otherwise changed frontend files arm it.ui_check(reviewer agent) audits ONLY those files against the interaction-pattern rubric — field errors inline with the field (never only a banner/toast), success/failure toasts after mutations resolve, create/edit in aDialog,AlertDialogfor destructive actions, no nativealert()/confirm(), components from the registry, the shared DataTable for record lists, semantic components for known-domain fields — withai-docs/ui/patterns.mdoverriding the defaults when the project keeps one. Violations get ONEfix_uibuilder round,ui_verifyre-audits, the finalui_gate(code) throws if violations survive, andui_retestre-runs the suite (the repair touched production code after the test phase). The rubric lives inmodules/(not prompt material), so--update-runtimedelivers it to existing installs. - Observability: every phase, gate verdict, engine call, token count and
cost lands in
imp/data/fia.db.agent_endevents stamp{model, coding_agent, cost, cache_read, cache_write}— the per-LLM ledger groups by what actually ran, so later roster edits never re-attribute spend; a failed phase that burned tokens emits its ownagent_spendevent.
Web viewer — npm run fda:viewer (node imp/scripts/fia-viewer.mjs [--port 4600] [--db imp/data/fia.db] [--no-open] [--view plan|agents|pi] [--ai-docs ai-docs] [--detach]). A read-only local server on
http://127.0.0.1:4600 (localhost-only, DNS-rebinding guarded; the page is
self-contained — no CDN). Four views, each a URL hash that survives reload:
- FDAs (default) — run list + drill-down: status/duration/tokens/cost
KPIs, "tokens per model" chips, a Gantt timeline (one lane per phase owner,
roster colors, running bars dashed), per-phase detail (engine, model,
effort/thinking, context gauge, gates with expandable checks, typed
envelopes with syntax-highlighted JSON, the compiled system/user prompts)
and a live filtered event stream. A
runningsession with no event for 10 minutes and no live pid is tagged stale — it never animates forever. - Interactive Pi — this project's
~/.pi/agent/sessions/timelines: main lane + one lane per subagent run, messages, tool calls, tokens. - Plan (
npm run plan) — everything/map//startcreated: milestones (declared status, resolved task progress), specs with gate logs, the example library (license chips flag the GPL family), workflow progress, screens/routes, tasks with frontier navigation, the design system (component registry,/ui-componentsprobe) and everyai-docs/document rendered, plus aninbox · N openbadge. - Agents (
npm run agents) — engine cards with install/login state and fix hints per provider, the commands → phases → agents map, and the roster editor: engine/model/reasoning/fallbacks per agent, curated model pickers (livecursor-agent --list-modelswhen Cursor is installed), a billing guard banner foranthropic/…models on the Pi engine, and a save that edits the YAML preserving comments, backs up first (imp/data/backups/fia.config.<stamp>.yaml) and answers 409 while a live (non-stale) FDA runs.
The FDA-side of the same data is --view pi-free in the terminal:
TUI — npm run tui / imp tui (node imp/scripts/fia-tui.mjs [--tab 1-5] [--once] [--no-alt] [--db] [--ai-docs] [--config]). Read-only Ink 7
dashboard, five tabs: 1 Home (tasks/specs/milestone/inbox/all-runs cards +
the current run with per-phase chips and a context gauge), 2 Work
(tasks + specs with the traceability table — uncovered requirements in red),
3 Runs (table + drill-down with phases, retries, live event tail),
4 Plan, 5 Agents (roster + the per-LLM usage ledger, attributed at
spend time). Keys: 1-5/Tab tabs · ↑↓ j k move · Enter open ·
Esc back · t run the test suite in a pane (disabled while an FDA holds
the lock) · r refresh · v open the web viewer (detached, matching tab) ·
q quit. Mouse: clicks and wheel work (SGR reporting, restored on exit).
--once renders one settled frame and exits (CI/smoke — it also skips the
file watcher); non-TTY without --once exits 1 pointing at the query CLI.
Query CLI — node imp/scripts/fia-query.mjs:
npm run fda:sessions # 20 newest runs (id, status, request, tokens)
npm run fda:phases -- <fda_id> # phase list of one run
npm run fda:tail -- <fda_id> # last 20 raw JSONL events of a run
node imp/scripts/fia-query.mjs models # per-LLM lifetime ledger (engine, model,
# runs, tokens, cost, last used)
node imp/scripts/fia-query.mjs sessions --json # scripts/Pi consume JSONThe models ledger sums what actually ran (agent_end + failed-attempt
agent_spend events, stamped at spend time) — roster edits never
re-attribute history; tokens recorded before model stamping surface as an
explicit unattributed row, never silently dropped.
Launch readiness — npm run launch:check (node imp/scripts/fia-launch-check.mjs [--json] [--strict] [--dir <p>]). Read-only
red/green report — it never publishes anything. Detects the current rung
(local → beta when .vercel/project.json exists → production when a
pk_live_ Clerk key or an own-domain production URL is found) and runs ~29
stack-aware checks across six sections:
- Versioning: git repo (blocker), clean tree (blocker), remote, pushed,
CI green (via
gh), CI workflow present. - Work: open tasks,
stack_decided(blocker — pending manifest layers),production_runbooks(every namedai-docs/apis/<tech>.mdneeds its Production section),test_credentials(auth stacks need ≥1 filled roster row between the credential markers),quality_scripts(lint/typecheck/test/build present),docs_sync(schema-ish files committed afterstack.md/specs),theme_tokens(raw hex colors in components),registry_seeded/registry_planned(blind registry, planned rows at launch). - Secrets: tracked
.env*files (blocker — untrack AND rotate),.env.examplepresent, secret-shaped values inNEXT_PUBLIC_*(blocker). - Security: raw
query(/mutation(outsideconvex/lib(use the authed wrappers),dangerouslySetInnerHTML, webhook signature verification inconvex/http.ts(blocker). - Production: Vercel linked,
convex deployin the build command, dev deployment noted, Clerk dev vs live keys, production URL, and theautomations_runbookblocker when the manifest declares an external automations layer (e.g. Modal) without a Production runbook. - Operations: error monitoring (Sentry), a database backup existing (with the "rehearse a RESTORE once" reminder).
--strict exits 1 on blockers (CI-friendly); --json gives
{rung, checks[], summary}. /launch (in pi) uses this report as its
source of truth and walks you through each fix.
Shared conventions between the harness (Claude Code/Cursor) and Pi, all under
ai-docs/:
-
Specs —
ai-docs/specs/NNNN-<slug>.md(4-digit, numbering continues;0000-example.mdis the shipped format reference and never counts). Header:Status: draft | defined | in-progress | done, created/updated dates and the linked task numbers. Sections: Problem & Outcome, Scope (In/Out), Actors & Permissions, Requirements (FR-1/NFR-1, one obligation per ID), Scenarios (BDD,S-1— with mandatory classes for user-facing mutations: success, validation, authorization, cross-tenant isolation and idempotency where they apply), Traceability (requirement → scenario → test file), an append-only Gate log (Definition Gate/Delivery Gate) and dated Decisions. Lean on purpose: "Not applicable — " is a valid section body. Created by/feature(delta specs replacing the old mini-PRD),/spec(short interview, also for work not born from the PRD) and the full mapping. -
Test markers — a test file proving a spec carries
spec:NNNN covers:S-1,S-2,FR-2(one marker per spec, grep targetspec:NNNN). Tasks/briefs link back with aSpec: 0003 (S-1, S-4)line; when present, the FDAs run the spec-coverage gate (checkSpecCoverageinimp/modules/gates.mjs—git grepwith a recursive fallback): every listed ID must appear in some test'scovers:list, missing ones fail the phase by name. NoSpec:line → check skipped. -
RED validity —
fda_bug.mjswrites ONLY the failing reproduction test first, then ared_checkphase runs it andvalidateRedReasonclassifies the failure: an assertion/expectation failure is a valid RED; a passing test fails the gate as "bug not reproduced"; module-not-found, syntax, command/env errors fail it with the classification (unknown → invalid, conservative). Only then does the fix build run. -
Milestones —
ai-docs/milestones.md: 3–6 blocks (Goal, verifiableDone when:list,Tasks:,Status: pending | in-progress | done), the first being the MVP, generated by/map(Pi) and/start(harness) after the task breakdown and fed by the PRD's## Launch criteria(captured by/idea//grill). A milestone is done only when its exit conditions are verified — never by task count; the declared Status is never auto-flipped. -
Inbox —
ai-docs/inbox.md:- [ ] YYYY-MM-DD — <one-line idea> (context: …)appended by/notewith ZERO interview./feature,/quickand/mapcheck it for related items and tick them with a→ spec 0003/→ Q-012/→ task 07annotation. -
Decision logs —
ai-docs/decisions/NNN-<command>-<date>.md: every interview command (/idea,/grill,/stack,/spec,/feature,/theme,/design,/kit) records question/recommendation/answer as the interview happens, viaimp/scripts/decision-log.mjs(deterministic: the script owns naming, numbering, timestamps and lifecycle; a crash loses nothing already answered):node imp/scripts/decision-log.mjs open stack --topic "backend choice" node imp/scripts/decision-log.mjs log 3 --q "Which database?" --rec "Convex" --a "Convex" node imp/scripts/decision-log.mjs note 3 --text "constraint that surfaced mid-talk" node imp/scripts/decision-log.mjs close 3 --outcome "Convex + Clerk" --artifact ai-docs/stack.md node imp/scripts/decision-log.mjs list [--command theme] [--json] node imp/scripts/decision-log.mjs latest [command]
One file per run = versioning: re-running a command opens the next
NNN; a still-open log of the same command becomessuperseded, closed ones are history. Commands read the recent logs before interviewing and never re-ask a decided question. The log preserves the interview; the artifact (PRD/spec/manifest) stays the source of truth for WHAT was decided. -
Stack research —
ai-docs/research/<tech>.md: before/stackdocuments or equips a technology, it must research FOUR dimensions — docs (+llms.txt), agent skills (skills.sh registry), official CLI, official MCP — and log each finding with its source:node imp/scripts/stack-research.mjs open neon node imp/scripts/stack-research.mjs log neon --dim docs --found "https://neon.com/docs" --source "https://neon.com/docs/llms.txt" node imp/scripts/stack-research.mjs log neon --dim mcp --none --source "web search: neon MCP server" node imp/scripts/stack-research.mjs close neon # REFUSES while any dimension lacks an entry node imp/scripts/stack-research.mjs status [neon] [--json]
The
closeis the gate: only a closed record lets the tech be marked documented in the manifest (and equipped). "I didn't check" is not a representable state (--sourceis mandatory). One file per tech, script-owned; a re-open discards old findings on purpose (fresh evidence — history lives in git). The hardcoded tables (installer catalog,/stackhint table) are bootstrap hints: research that diverges from them wins, and the divergence is reported so the tables get updated. -
Docs commits —
imp/scripts/docs-commit.mjs(aliasnpm run docs:commit): pathspec-limited commit forai-docs/artifacts, called by the flows that generate durable documents (/stack,/mapand/start,/component,/design,/theme, decision-log close) right when the documents land. Uncommitted docs are a contamination reservoir — the next FDA's commit can sweep them into an unrelated change. Guardrails: onlyai-docs/paths are accepted (escapes resolved and refused — code is committed by FDAs, never by this script), it refuses while a FIA run is active (imp/data/.fda.lockwith a live pid), and a clean tree exits 0 with "nothing to commit".node imp/scripts/docs-commit.mjs --message "docs(stack): decide backend" [paths…] [--json] -
Env preflight —
imp/scripts/env-preflight.mjs(aliasnpm run env:check): derives, from the layersai-docs/stack.mddeclares, the dev keys the scaffold reads at build/boot time (Convex →NEXT_PUBLIC_CONVEX_URL+CONVEX_DEPLOYMENT; Clerk → publishable + secret; SQL →DATABASE_URL; Supabase → URL + anon key; Better Auth → secret) and checks.env.local, printing a copy-pastable fix per missing key (exit 1 when keys are missing;--jsonfor scripts). The task-sequencer runs it BEFORE writing the foundation brief (env gate — the twin of the theme gate), so a missing key costs one command, not a full scaffold FDA rejected in review becausenpm run buildcrashed on prerender. Two more layers back it up deterministically: foundation briefs carry the issue'sKind: foundationline, which makes the FDA test phase runnpm run buildalongside the suite (in code, before the reviewer), and the foundation task's fixed scope demands a hermetic build —npm run buildgreen with NO.env.local, plus a generated.env.example. No manifest → the preflight passes (it only enforces what the stack declares). -
Project mode —
imp/scripts/project-mode.mjs [--json]classifies the project deterministically so/ideacan branch:greenfield(no PRD, or a PRD template still carrying{{placeholders}}— the starter's code never counts),ideation(a real PRD but nothing built: re-running/ideameans revising the idea) orbrownfield(map.yaml,todos/task-master.mdorPRD-as-built.mdexist). In brownfield,/idearuns in module mode: deep interview about the new module and an APPENDED## Module: <name>chapter in the PRD (never rewriting the rest), stack delta only, then/featurebreaks the chapter into delta specs + tasks (/featureitself triages size and routes module-sized requests up to/idea). -
Guide —
/guide [goal?](Pi): the situational router. Probes the state with the same scripts the flows use (project-mode.mjs,decision-log.mjs list, the plan artifacts,fia-launch-check.mjswhen the goal is going live), asks ONE question to confirm the goal, then maps goal + state onto the Routing table in.pi/skills/fia/SKILL.md(single source of truth — the prompt keeps no command list of its own, so a new command registered in the table is routable for free) and answers with a numbered command sequence: each step, and each skipped rung, carries the criterion that decided it. Read-only by design: it opens no decision log and never executes the route — at most it offers to start step 1. -
Quick flow —
/quick <description>triages: SIMPLE only when the blast radius is ≤ ~3 files with one obvious shape and NO schema/migrations, auth/permissions, payments, new dependency, new route/page, new UI component or destructive data operation — anything else routes to/featureor/bugwith the reason. SIMPLE still enforces the guardrails (registry defaults, backend rules, one focal test when testable) and appends a## Q-012audit entry (files, verification, commit) toai-docs/todos/quick-log.md. In Pi it runsnode imp/fda_quick.mjs "…"(build → quality → one fix round → quick-log → commit); quick work never touches the task roadmap. -
Example shelf —
ai-docs/examples/:registry.mdis the index (same marker discipline as the component registry — real rows only between<!-- registry:start/end -->), each entry's detail in<slug>/NOTES.mdwith its images in<slug>/assets/. Row:| Example | Kind | Tags | Source | What to take | License | Status |, with fixed vocabularies — Kindrepo | code | docs | design, Statusreferenced | excerpted | archived, License an SPDX id (n/afor docs/design). Tags are the search key (functionality, not technology): theexamplesskill and the task briefs match a task's nouns against that column, and NOTES.md carries a mandatory, never-empty## What NOT to take. Written by/example(harness and Pi), which reads the source before registering anything and researches the license instead of asking. Two rules make it safe: it is a shelf, not a gate — nothing fails because no example matched, and an empty registry is a normal state — and examples teach shape, so the default is to reimplement in this project's conventions;AGPL-3.0, anyGPL-*andunknownare never copied verbatim, and any verbatim copy is called out in the task summary with its license. The0000-*entry is a format reference and never counts. -
Test credentials — the convention every auth flow feeds: ONE dev test user per profile/role, created by the auth task via the provider's native mechanism (Clerk dev instances: any
+clerk_testemail verifies with the fixed code424242, no real email sent; Better Auth: a dev-only seed script with the password inTEST_USER_PASSWORD), recorded in theai-docs/test-credentials.mdroster (env var names, never real passwords)./test-uireads the roster to sign in;launch:checkwarns when an auth stack has no filled roster row.
Observability follows along: the viewer's Plan tab (§9.6) renders
milestones, specs, the inbox badge and the example library; the TUI's
Work tab shows the spec traceability table with uncovered requirements in
red; and npm run launch:check adds the read-only docs sync warning when
schema-ish files changed more recently than ai-docs/stack.md/ai-docs/specs/.
The problem this layer kills: components being created on demand, by whichever task first needs one — the app reaches task 5 and grows its first (hand-rolled) table, task 9 grows a second, different one. Three pieces make the design system deterministic instead:
- The registry (
ai-docs/components/registry.md) is the source of truth the task briefs enforce: every UI need in a brief maps to a registry row, creating a component the registry already covers is forbidden (variations are props/composition), and the living/ui-componentspage renders everything in it. Two components for the same need carry roles — exactly onedefault, the restalternative, used only on explicit request. - The core kit (design-system skill,
references/core-kit.md) is the canonical always-needed set — buttons, inputs, MaskedInput, Select, Combobox, MultiSelect, the three date components (typed, calendar, date+time), menus incl. right-click ContextMenu, dialogs, toast, Skeleton, EmptyState — plus the FULL per-component contracts. The DataTable contract (TanStack Table asdefault; REUI Data Grid registeredalternative) is the big one: global fuzzy multi-word search, header menu on click AND right-click (sort/hide/filter), per-column filters adapted to the column type (text/enum-facet/date-range/number-range), active-filter chips + clear-all, column visibility, pagination, row selection + bulk-actions bar, row-click edit, skeleton/empty/no-results states. - Greenfield: Task 01 is always the fixed Foundation scaffold and Task
02 the fixed Core component kit (
Kind: kit), blocked by 01 and blocking every feature task — sequenced after the/themecheckpoint, so the demos render with the approved identity. Enforcement is code, not prose:Kind: kitarmsnpm run buildin the FDA test phase (isFoundationBriefinimp/modules/gates.mjs), the issue carries one checkbox per component (the checklist gate refuses to close with an open box), and the UI gate's rubric fails a run whose list of records bypasses the registry's default table.
Semantic fields ride along: known-domain data (state/UF, country,
address/CEP, phone, documents, money, dates, timezone, fixed categories)
never ships as a free-text input — the canonical catalog lives in the
design-system skill (references/semantic-fields.md), the sequencer adds a
Semantic fields table to briefs that touch such data, and both the C8
quality checklist and the UI gate's rubric audit it.
The theme checkpoint: on greenfield paths, after the Foundation task the
sequencer refuses to hand out any other task until a closed theme
decision log exists — either /theme ran (interview → side-by-side
preview → explicit approval) or the engineer consciously accepted the stack
default (/theme accept, recorded open+close in one breath). Any closed
theme log passes, whatever its outcome; the check uses decision-log.mjs list (never latest — an abandoned re-run must not cancel an earlier
approval).
Existing code gets the same layer through /kit (the brownfield
counterpart of Task 02): as-built inventory (installed rows with real
paths — no planned promises invented), the /ui-components page, then a
gap report against core-kit.md — missing needs, below-contract items
(with file/line evidence), duplicates without roles — an interview where the
engineer approves what improves (recorded in the kit decision log;
"nothing approved" is a valid outcome), and finally design-only tasks via a
delta spec: Kind: kit build/upgrade tasks with one checkbox per contract
item, expand–contract migrations per screen batch, duplicate removal last.
The command itself changes no component and no screen. /absorb recommends
/kit whenever the as-built registry comes out empty or duplicated, and
npm run launch:check backs it with two warnings: registry_seeded (a
BLIND registry — reusable component files in the code, zero registry rows)
and registry_planned (a row still planned at launch — a promise nobody
built). Both templates ship the layer pre-filled: as-built registry + living
/ui-components page committed in their own repos (the harness merge never
overwrites an existing file, so the template's registry wins).
The .pi/ package the CLI stamps (from pi-templates/.pi/) makes Pi the
FIA's interactive cockpit. imp (or pi) opens it in the project.
settings.json sets the subagent defaults (openai-codex/gpt-5.6-sol,
thinking high); APPEND_SYSTEM.md appends the FIA persona to every session
(prefer FDAs for repeatable work, pi-subagents for ad-hoc recon, never touch
the protected machinery); and .pi/skills/fia/SKILL.md carries the hard
rules + the Routing table — the single source of truth for the command
catalog (/guide routes exclusively from it; a command absent there "does
not exist"). Deep procedures live in 15 cookbooks
(.pi/skills/fia/cookbooks/): fia_overview, harness_bridge, install,
run_fda, create_fda, observability, decision-log, stack, specs, components,
theme, design, examples, launch, update_roster.
| Command | Arguments | What it does |
|---|---|---|
/fia |
— | Factory overview: FDA table, task counts, last run, command list. Read-only. |
/guide |
[goal?] |
Situational router: probes the state with the deterministic scripts, asks ONE confirming question, answers with a numbered command route (each step carries the criterion that decided it). Suggests, never executes — at most offers to run step 1. |
/idea |
[topic?] |
Interview → PRD + stack. Branches on project-mode.mjs: greenfield (full discovery), ideation (revise the idea), brownfield (module mode — appends a ## Module: <name> chapter, never rewrites the rest). Tags semantic field types in the data model and always adds ## Launch criteria. |
/stack |
[tech|layer?] |
Decide pending layers + the 4-dimension research gate + ai-docs/apis/<tech>.md docs + equip (skills/CLI/MCP). |
/grill |
[doc|topic?] |
Stress-test the PRD one question at a time; decisions recorded and written back. |
/prd |
[focus?] |
Quick reviewer opinion on the PRD — never edits it. |
/map |
[notes?] |
PRD → map.yaml + screens-routes + issues/task-master + specs + registry seed + /ui-components + milestones; ends by opening the Plan page (npm run plan -- --detach). Greenfield build order: /task (foundation) → /theme → /goal. |
/task |
[number|description?] |
ONE task: the task-sequencer writes the brief (enforcing the theme and env gates), then node imp/fda_plan_build_test.mjs <brief> (bigger/riskier work → fda_sdlc). On failure: npm run fda:phases -- <id>, resume with --fda-id <id> --resume. |
/goal |
[limit?] |
All unblocked tasks to done, one FDA per task (never batched), gates inside the loop, human-only steps handled MID-goal; ends with the app RUNNING + "How to test", then suggests /launch. |
/feature |
"request" |
Delta on an existing mapped system: size triage (module-sized routes UP to /idea), delta mini-grill, delta spec, DELTA tasks, approval before executing. Requires map.yaml (/absorb first otherwise). |
/bug |
"symptom" |
Issue + node imp/fda_bug.mjs with the RED-validity gate (assertion-failing reproduction before any fix). |
/quick |
"small change" |
Triage; SIMPLE runs node imp/fda_quick.mjs + the Q-NNN quick-log entry; COMPLEX routes to /feature//bug naming the failed criterion. |
/note |
"idea" |
One line into ai-docs/inbox.md, zero questions. |
/spec |
"capability"|NNNN |
Create/update a durable spec; Definition Gate flips Status: defined; ticks related inbox items. |
/launch |
[beta|production?] |
Go live by rungs, fia-launch-check.mjs --json as the fact source; confirms before every irreversible step; secrets never in chat. |
/component |
name + URL/cmd | list | sync |
Design-system entry path (dedupe → research → install → register → showcase). |
/theme |
[hint|accept?] |
Identity interview → FDA-built side-by-side preview at /ui-components/preview → explicit approval. accept records "keep the default" (satisfies the theme gate) with zero app changes. AA contrast is a blocker. |
/design |
images + scope |
Layout redesign from references — structure from the image, identity from OUR system. |
/example |
URL [notes] | list |
Register an external reference on the shelf (license researched, What NOT to take mandatory). |
/agents |
— | Opens the viewer's Agents tab (npm run agents -- --detach) to edit engines/models/fallbacks; Pi is forbidden from editing imp/fia.config.yaml itself. |
/absorb |
[focus?] |
Brownfield onboarding (as-built PRD/map/conventions/registry + project skill in .pi/skills/project/ AND .claude/skills/project/); recommends /kit when the registry comes out empty/duplicated. |
/kit |
[focus?] [--report-only] |
Brownfield design-system audit → gap report → approved design-only tasks. |
/status |
— | Read-only progress: tasks, milestones (status as declared), specs, inbox, latest runs and failed phases. |
14 pi-subagents (.pi/agents/*.md, all with
fallbackModels: openai-codex/gpt-5.5 and inheritProjectContext): the
interactive twins of the FDA roster (scout, planner, builder,
reviewer, documenter) plus the planning specialists shared with the
harness (task-sequencer, task-master-generator, start-mapper,
start-scaffolding, screen-routes-generator, component-architect,
ui-component-page, ui-component-researcher, api-docs-researcher). The
two researchers carry real web tools (web_search, fetch_content,
get_search_content) provided by the pi-web-access package — without it
the packaged researcher hard-fails; the installer (and imp update) installs
and exact-pins the three Pi packages (pi-subagents, pi-mcp-adapter,
pi-web-access); the exact pin is also what suppresses Pi's "Package Updates
Available" panel.
Chains (.pi/chains/): scout-planner.chain.md — scout surveys the
codebase for {task}, its context.md output feeds the planner.
Extensions (.pi/extensions/):
fda-lock.ts— interactive Pi goes read-only while an FDA runs (§9.5).fia-guard.ts— FIA-protected paths (imp/modules/**,imp/fia.config.yaml,imp/fda_*.mjs, incl.rm -rf imp) are never agent-writable in interactive sessions — the extension-side twin of the FDA permission gate. Block message: "…is FIA infrastructure (protected). Ask the engineer to change it manually or via impactus."fia-branding.ts— theFIA · IAI Cursosstatus-line slot.
npx impactus --ui (alias --web) starts a local server (default port
4599, walking forward up to 20 ports on conflict; bound to 127.0.0.1 only,
with a Host/Origin DNS-rebinding guard) that serves one self-contained page
where the installation is assembled by clicking. Execution does NOT
happen in the browser: at the end the page highlights the ready
npx impactus … command; the student copies it and runs it in the terminal
(an "Open the terminal for me" button opens the OS terminal app). The
terminal wizard remains the default entry point — --ui is opt-in;
--terminal/--no-ui are accepted for compatibility. The page still ships
with the historical create-iai branding (title/header).
What the page offers, top to bottom:
- Project folder — prefilled with the server's cwd, with a native OS folder dialog (AppleScript / PowerShell / zenity–kdialog; in-page modal fallback) and a live "folder already has files" warning. There is no name field: the project name is the folder's basename.
- What to install — "Harness + template" vs "Harness only". The
harness-only card has a "Your stack" section: "Decide by talking with Pi"
(→
--stack depois) or layer-by-layer selects driven by the stack catalog (→--stack cat=opt,…, with explicitcat=depoisentries so the terminal wizard doesn't re-ask a deliberately-pending layer). - Template, preset and addon groups — cards from the catalogs; picking a preset materializes every group flag, editing a group clears the preset.
- Services — storage pills, GitHub push + visibility, Vercel deploy
toggles (tri-state: unchecked emits
--no-push/--no-deploy— an unchecked box is a decision, so the preflight never prepares gh/vercel logins for nothing). - Service keys (optional) — per relevant service, the AI-assisted path:
"Copy instructions for the AI" (the same prompt catalog as the terminal —
paste it into a browser-automation agent, e.g. Claude in Chrome), a paste
box that parses the returned
KEY=valueblock and fills the regex-validated fields, and a collapsed manual path with the dashboard link. Keys autosave to~/.create-iai/keys/<slug>.env(mode 600, machine-local; a project rename deletes the old slug's file; the command references the path via--keys— no secret ever appears in the command or leaves the machine). Fully-automatic services (Clerk, Convex) are listed as "nothing to do". - Sign-in — the access bar checks
~/.create-iai/auth.jsonagainst the community API and, when needed, runs the whole device flow server-side (the browser only sees the code/link; the token never travels to the page). An inactive subscription is reported as such — a fresh sign-in won't fix it. - Run the installation — the live command (assembled by
src/lib/command.js; full mode always carries--mode full --template-id <id>, so a guest running the copied command gets the clear sign-in error), copy button, open-terminal button, and the 3-step run instructions.
Example generated commands:
# Full mode, page defaults:
npx impactus --dir /Users/ana/my-app --mode full --template-id live1 --preset padrao \
--addons commitlint,knip,analyzer --observability sentry --analytics none \
--security csp,rate-limit --emails none --platform none --payments none \
--storage convex --no-push --no-deploy
# Harness only, "decide with Pi":
npx impactus --dir /Users/ana/my-app --stack depois
# Harness only, layer by layer (Convex hides database/ORM):
npx impactus --dir /Users/ana/my-app --stack frontend=nextjs,backend=convex,auth=clerk,blob=r2,automations=none,deploy=vercel
# With pasted keys:
npx impactus --dir /Users/ana/my-app --mode full --template-id live1 … --keys /Users/ana/.create-iai/keys/my-app.envEndpoints, for the curious: GET / (the page), GET /api/catalog,
GET /api/browse + GET /api/dir-info + POST /api/pick-folder,
POST /api/keys, POST /api/command, GET /api/auth, POST /api/login
(NDJSON stream), POST /api/open-terminal. Bodies are capped at 1 MB;
everything degrades gracefully.
npx impactus --verify --dir <folder> audits an already-installed project
without touching anything (src/steps/verify.js). Exit 0 = no errors;
exit 1 = errors found — usable in the student's CI. --json replaces the
human report with { ok, errors, warnings } on stdout (same exit code).
Also reachable as imp init --verify --dir ..
The audit first detects the install type: harness-only projects (no
addons config, no convex/, but a harness footprint) skip the template
checks with explicit "skip" lines instead of false errors. The checks:
| Finding | Level |
|---|---|
package.json missing/invalid (full install — short-circuits the audit) |
error |
package.json missing/invalid (harness-only) / without name |
warn |
| addons config missing (project from an older CLI) | warn |
template.addons.json still present (pruning did not run) |
error |
orphan live1:addon markers anywhere in the tree |
error |
.env.local missing |
error |
core env keys empty/missing (NEXT_PUBLIC_CONVEX_URL, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY) |
error |
| chosen-addon service keys missing (everything degrades gracefully) | warn |
node_modules / convex/_generated missing |
warn |
skills-lock.json invalid |
warn |
a locked skill missing from .agents/skills/ (Cursor/Pi blind to it) |
warn |
a locked skill DUPLICATED in .pi/skills/ (Pi "Skill conflicts" panel — fix with imp init --update-runtime or delete the copy) |
warn |
npx impactus --verify --dir my-saas # human report
npx impactus --verify --dir . --json | jq .ok # CI gatenpx impactus --update-runtime --dir <folder> re-stamps the FIA/Pi RUNTIME of
an already-installed project from the impactus version currently running
(src/steps/update-runtime.js) — new FDAs, gates and prompts without a
re-install. The contract:
- Manifest —
setupFiarecordsimp/.runtime-manifest.json({ impactus, stamped_at, files: { <relpath>: <sha1> } }) covering every file stamped from both template trees. The shas are the TEMPLATE's: the stamp skips pre-existing files, so a file that differs from the template was never written by us and stays "modified" (consent + backup before any overwrite). - Updatable paths only (
FIA.runtimeUpdatablePaths):imp/modules/,imp/fda_*.mjs,imp/scripts/,imp/package.json,.pi/skills/fia/,.pi/prompts/,.pi/extensions/. Never touched:imp/fia.config.yaml,imp/data/,imp/node_modules/, anything outside the template trees. Files the template no longer ships are LEFT in place — additive + replace, never delete. - Per file: missing → add; byte-identical → skip; differs with the disk
sha matching the manifest (unmodified since the stamp) → overwrite; differs
otherwise (edited locally, or no manifest) → interactive runs ask per file —
Yes / Yes to all / No / No to all, the *-to-all answers stick for
the rest of the run — after one loud warning; non-interactive runs (
--yesor--json) skip and report unless--force(= yes to everything). Every overwrite is backed up toimp/.runtime-backup-<YYYYMMDD-HHmmss>/<relpath>first (gitignored). - It also: runs
migrateLegacyFiaLayoutfirst (see below); runsnpm installinimp/whenimp/package.jsonchanged (the ONLY failure that makes it exit 1 after a successful plan); re-pins the three Pi packages (student-customized entries skipped and reported); prunes stale.pi/skills/copies; ensures the FIA gitignore entries; merges newFIA.npmScripts(never clobbering yours — conflicts land as<name>:fia); rewrites the manifest — overwritten files move to the new baseline while skipped-modified ones KEEP the stamp sha, so the next run still flags them. --jsonreport:{ ok, dir, impactus, added[], updated[], skippedModified[], unchanged (a count), backupDir, npmScriptsAdded[], warnings[] }.
npx impactus --update-runtime --dir . # interactive per-file consent
npx impactus --update-runtime --dir . --force # overwrite everything (backed up)
npx impactus --update-runtime --json --force # CI: JSON report, no prompts
imp init --update-runtime # same, via the launcherLegacy layout migration (migrateLegacyFiaLayout — runs at the start of
BOTH the update and the install stamp; idempotent, crash-resumable via a
.fia-migration-pending marker): projects from older versions kept the
runtime at fia/ and HARNESS.md/iai.config.json at the root. The
migration renames/merges fia/ → imp/, moves the root files into imp/,
rewrites fia/… runtime paths across imp/, .pi/, .claude/, .cursor/,
package.json scripts and .gitignore (surgical — .pi/skills/fia/ and
unrelated fia-named folders are untouched), patches the fia-guard
extension's literals, and updates the manifest keys so OUR path fix never
shows up as a student edit.
The imp launcher (installed globally by the FIA stamp, or npm i -g impactus) is a thin brand wrapper over the real pi binary — NOT a fork:
| Invocation | Behavior |
|---|---|
imp |
Starts Pi in the current folder (installs Pi on demand). Banner only on a TTY; piped output is byte-identical to pi's. |
imp init [flags] |
The full impactus installer in place — every flag works (imp init --harness-only -y, imp init --verify, …). |
imp update |
npm install -g impactus@latest + pi update (or install) + re-pin of the three Pi extension packages. Exit code keyed to the impactus self-update; the extension refresh is best-effort. |
imp tui [args] |
Runs the project-stamped imp/scripts/fia-tui.mjs (errors with a imp init hint when the runtime is absent); imp tui --once passes through. |
imp doctor [--json] |
Read-only checkup — detection only, fixes nothing. Four sections: engines/subscriptions (Claude Code on PATH, the Codex login inside Pi, the Cursor CLI — all informative, never required), core CLIs (node floor, git, npm; gh/vercel as optional), Pi & imp (Pi version, the three pinned extension packages, the same update probe the launcher uses, timeboxed at 4 s), and — when run inside a project — the install: FIA runtime present, .mcp.json hygiene (an npx server without -y dies on a cold cache with "Connection closed"), the harness stamp state (missing/modified vs imp/.harness-manifest.json) and a summarized --verify audit (full report stays in npx impactus --verify). Every finding ends in the exact command that fixes it. Exit 0 = no error-level finding; --json prints { ok, sections } with no banner. |
imp fix [flags] |
The remediating sibling of doctor — restore-only: it recreates what is MISSING (deleted harness files via imp/.harness-manifest.json + a fresh API download, runtime files the stamp manifest recorded, skills from skills-lock.json, the AGENTS.md harness block) and adds missing -y flags to npx MCP servers; it NEVER overwrites a file that exists with different content (those are reported as notes — updating outdated files stays with --update-runtime, adopting harness versions with --agent-files replace). A bare run prints the plan and asks one y/N; project-touching fixes require a clean git tree. Flags: --dry-run / --json (plan only, never mutate), --yes (skip the ask; required in CI — a non-TTY run without it prints the plan and exits 1), --allow-dirty (skip the git gate), --commit (one git commit per applied fix). Ends by re-planning: exit 0 only when nothing is left. |
imp handoff [args] |
Runs the project-stamped imp/scripts/handoff.mjs: hands the newest interactive Pi conversation to the claude CLI with a continuation prompt pointing at the session transcript (same preamble the FDA relay uses). Works while Codex is down — that is the point. --list picks a session, --session <id> targets one, --full asks for a full transcript read, --print prints the prompt without launching. Also npm run handoff. |
imp help / imp --version |
Help / bare version. |
| anything else | Straight through to pi (e.g. imp -p "prompt", imp --continue). |
Version-notice choreography: imp launches Pi with PI_SKIP_VERSION_CHECK=1
(suppressing Pi's pi-branded update banner), probes npm in the background
during the session (TTY only), and prints ONE imp-branded "Updates available
… Run imp update" block AFTER Pi exits (racing a 400 ms timeout so a quick
one-shot session is never held hostage). Opt-outs: IMP_SKIP_VERSION_CHECK,
PI_OFFLINE. The three Pi packages are exact-pinned precisely so Pi's own
"Package Updates Available — run pi update --extensions" panel never shows;
imp update re-pins them.
npx impactus # pick "Build my own stack" (or "Not sure yet")
cd my-app
imp # open Pi
/login openai-codex # one time only (never Anthropic inside Pi)
/idea # interview → PRD + the best stack (ai-docs/)
/stack # research + docs per tech + CLIs/MCPs/skills
/grill # stress-test the PRD before building
/map # PRD → screens, tasks, milestones (opens the plan)
/task # Task 01 (Foundation) via FDA
/theme # the greenfield checkpoint (or `/theme accept`)
/goal # every remaining task, one FDA each
npm run tui # follow along in another terminalFully scripted variant (no prompts, guest-friendly):
npx impactus my-app --yes --stack backend=hono,db=neon,orm=drizzle,auth=clerk,blob=r2,frontend=nextjs,deploy=vercel,automations=nonenpx impactus # sign in; pick "Recommended stack (ready-made template)"
# the CLI provisions everything: Convex + Clerk + JWT + keys (+ webhook/GitHub/deploy if chosen)
cd my-app
npm run dev:convex # terminal 1 — backend (watch + codegen)
npm run dev # terminal 2 — Next.js → http://localhost:3000
# the app already runs — now shape it into YOUR product:
imp
/grill # sharpen the PRD (template features are the baseline)
/map # plan screens + tasks on top of the template
/goal # FDAs build it — or /dev in Claude Code, task by taskNon-interactive full install (CI or a second machine):
CREATE_IAI_TOKEN=<token> npx impactus my-saas --yes --preset saas --payments asaas --storage r2 --keys ~/.create-iai/keys/my-saas.envcd my-app
npx impactus --dir . # detects the project → harness + FIA only; nothing overwritten
imp
/absorb # as-built PRD + map + conventions + stack manifest + registry
/kit # (recommended when the registry came out empty) DS audit → tasks
/feature "CSV export on the reports page" # delta spec + tasks, approved before running
/bug "login loops after logout" # proven RED, then the fix
/quick "rename the Save button" # small change, one audit line
/task # execute — or /goal for everything approvednpm run fda:sessions # find the run id (status: failed)
npm run fda:phases -- 3fa9c21b # which phase failed
npm run fda:tail -- 3fa9c21b # last events (raw JSONL)
npm run fda:viewer # or drill down in the browser
node imp/fda_plan_build_test.mjs --fda-id 3fa9c21b --resume
# succeeded agent phases replay from saved results; tests re-run against the
# CURRENT tree — you can fix code by hand first and the resume re-tests it.npm run agents # opens the viewer's Agents tab (or /agents inside pi)
# pick engine (claude_code | pi | cursor), model, reasoning and a fallback chain
# per agent; Save preserves the YAML comments and backs the file up. Locked
# while an FDA runs; applies from the next run.Or edit imp/fia.config.yaml by hand — remember the billing rule: Claude
agents use coding_agent: claude_code (the plan); Claude INSIDE Pi bills per
token as extra usage.
# example: Stripe, after the install
npx convex env set STRIPE_SECRET_KEY sk_test_...
npx convex env set STRIPE_PRICE_ID price_...
# the webhook: dashboard.stripe.com (Test mode) → Developers → Webhooks →
# Add endpoint → https://<deployment>.convex.site/stripe-webhook, then:
npx convex env set STRIPE_WEBHOOK_SECRET whsec_...Every addon degrades gracefully until its keys exist — the final install
summary and --verify both list what is still pending, and the web UI's AI
prompts (§6.1) fetch keys for you.
imp doctor # read-only checkup: subscriptions, CLIs, Pi, project
imp fix # restore missing harness/runtime/skills files (plan + consent)
imp update # impactus + Pi + the pinned extension packages
npx impactus --update-runtime --dir . # new FDAs/gates/prompts into imp/ + .pi/
npx impactus --verify --dir . # audit that everything is still intactnpm run launch:check # read-only readiness (blockers/warnings, rung)
imp # then, inside pi:
/launch # Ready gate → Safe gate → BETA → PRODUCTIONnpx impactus [name] [options]
Project
--name <name> Name (same as the 1st positional argument)
--dir <path> Target folder ("." = current folder)
--mode <value> harness (harness only) | full (harness + template)
--harness-only Shortcut for --mode harness (does not install the template)
--stack <value> recomendada | propria | depois | category=option pairs
(e.g. backend=hono,db=neon,orm=drizzle; categories:
frontend, backend, database, orm, auth, blob,
automations, deploy — anything missing stays "decide
later"; aliases: db/banco, storage/arquivos,
autenticacao, jobs/automacoes)
--agent-files <mode> Folder already has agent files (.claude, CLAUDE.md…):
add (default, only what's missing) | replace (backup + replace)
--template-id <id> Catalog template: live1 (default) | live2
--template-ref <branch> Template branch/tag for the gated download
Stack (whatever is not chosen is REMOVED from the generated code)
--preset <name> minimo | padrao | saas (completo = deprecated alias of saas)
--addons <list> commitlint,knip,analyzer | none | all
--observability <list> sentry,logging | none
--analytics <value> none | posthog | vercel-analytics
--security <list> csp,rate-limit | none
--emails <value> none | resend
--platform <list> notifications | none | all
--payments <value> none | stripe | asaas | clerk-billing
Customization
--update-deps <mode> none | safe (patch/minor — there is no "latest" mode:
the template is tested with the pinned versions)
--shadcn-preset <val> Preset from ui.shadcn.com/create
--shadcn-block <blocks> shadcn blocks (comma-separated) or "none"
--skip-shadcn Skips the shadcn step
Services
--keys <file> .env file with service keys (generated by the --ui
web UI in ~/.create-iai/keys/; machine-local, mode 600)
--tenancy <value> single (Live 1, default) | multi (Live 2: your app's
own organizations in Convex — per-organization
data/billing, roles/permissions, /admin with org
management)
--skip-webhook No Clerk → Convex webhook (there is no positive
--webhook flag: it needs a dashboard action, so it
can only be turned on interactively)
--storage <value> convex | r2
--skip-storage Stays on Convex Storage without asking
--repo <name> GitHub repo name (default: slug)
--public | --private Visibility (default: private)
--push | --no-push Create remote repo and push (or not)
--skip-github Not even a local commit in that step
--deploy | --no-deploy Vercel deploy at the end
--skip-deploy Same as --no-deploy
--no-harness (full mode only) template WITHOUT the harness
--skip-harness Same as --no-harness
FIA and design
--fia | --no-fia Install (default) or skip FIA (Pi + FDAs + skill);
--skip-fia --skip-fia = --no-fia
--impeccable Impeccable design skill (impeccable.style) — free,
no API key; default on. Requires Node >= 22.12
--no-impeccable Skips Impeccable (--skip-impeccable is the same)
Access (sign-in is optional: it unlocks the templates + their automation;
without it the installer delivers the harness + agent only — see §2)
--login Authenticates this computer (browser) and exits
--logout Removes/revokes the CLI token and exits
--whoami Shows subscription status and exits
--api <url> Community API base (dev/testing; or CREATE_IAI_API)
General
--ui, --web Opens the local web UI to build the install command
--terminal, --no-ui Accepted for compatibility (terminal is the default)
--port <n> UI server port (default: 4599)
--verify Audits an ALREADY-installed project (--dir) and exits
--update-runtime Re-stamps the FIA/Pi runtime of an installed project
(--dir) from this impactus version and exits (§14.2);
config/data/local edits preserved, backups always
--force With --update-runtime: overwrite locally modified
runtime files too (after the backup)
--json With --verify / --update-runtime: JSON report on stdout
-y, --yes No prompts (safe defaults; full mode, addons = padrao preset)
-v, --version | -h, --help
--yes semantics: full mode, padrao addon preset (change with
--preset/groups), default shadcn block, no webhook/deploy, storage convex
(EXCEPT --storage r2 with all four R2_* keys in --keys, which
configures R2 non-interactively), harness installed, local commit without
a remote repo, no integration-CLI logins, FIA + Impeccable on. For harness
only without prompts: --harness-only (or --mode harness). Without a valid
login (or CREATE_IAI_TOKEN), --yes continues as guest — harness +
agent only — and --mode full/--stack recomendada error out instead of
silently downgrading.
Recognized by the installer:
| Variable | Effect |
|---|---|
CREATE_IAI_TOKEN |
CI/automation access token — the non-interactive way to run a full install. An invalid token fails loudly (never a silent guest downgrade). |
CREATE_IAI_API |
Community API base override (same as --api; a custom base warns once — the token is sent to that host). |
GH_TOKEN / GITHUB_TOKEN |
Accepted by the gh preflight as authentication (no gh auth login needed). |
VERCEL_TOKEN |
Accepted by the vercel preflight as authentication. |
LEFTHOOK=0 |
The documented one-time escape hatch when the template's pre-commit lint blocks the initial commit. |
Recognized by the FIA runtime inside a project:
| Variable | Effect |
|---|---|
FIA_DB |
Trace database path (default imp/data/fia.db) — fia-query, viewer, TUI. |
FIA_CONFIG |
Agent roster path (default imp/fia.config.yaml) — viewer, TUI. |
FIA_AI_DOCS |
ai-docs/ dir override — viewer/TUI Plan views, launch-check, env-preflight, quick-log. |
FIA_PROJECT_ROOT |
Project root override for the viewer. |
FIA_DEBUG |
Same as --debug on any FDA (full stack traces). |
FIA_FDA_RUN |
Exported BY the runner into its child agents — makes the fda-lock hooks/extension silent for the run's own process tree. Never set it yourself. |
ENGINEER_NAME |
Engineer identity stamped in the trace (fallback: git config user.name → $USER). |
PI_PATH / CLAUDE_PATH / CURSOR_AGENT_PATH |
Engine binary overrides (pi / claude / cursor-agent). |
PI_SESSIONS_DIR |
Override for the viewer's interactive-Pi session dir (~/.pi/agent/sessions/<slug>). |
IAI_DECISION_LOG_NOW |
Fixed timestamp for decision-log/stack-research (tests). |
OPENROUTER_API_KEY, XAI_API_KEY, GROQ_API_KEY, GEMINI_API_KEY, FIREWORKS_API_KEY, DEEPSEEK_API_KEY, CEREBRAS_API_KEY, MISTRAL_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY |
Pi API-key providers (per-token billing — the subscription providers openai-codex/github-copilot log in via /login instead). |
Recognized by the imp launcher:
| Variable | Effect |
|---|---|
PI_SKIP_VERSION_CHECK=1 |
Set by imp on every passthrough — suppresses Pi's own pi-branded update notice (imp prints its own after the session). |
IMP_SKIP_VERSION_CHECK / PI_OFFLINE |
Skip imp's post-session update probe entirely. |
State on disk, outside projects: ~/.create-iai/ (auth token, web-UI keys
files, run logs — §2) and ~/.pi/agent/ (Pi's own credential store,
settings and session logs; never imported from ~/.claude/~/.codex —
rotating refresh tokens invalidate each other).
- In the template (
live1): implement the complete feature; wrap snippets in shared files withlive1:addon:<id>:start/:end; add the entry totemplate.addons.json(files/deps/scripts); document the envs in.env.example(inside markers). Runnpm run type-check,npm run testand the build with everything on. - In the CLI: add the option to the right group in
ADDON_GROUPS(src/config.js); if there is official tooling, register it inADDON_TOOLING; if it needs a post-install instruction,ADDON_NOTES; if it needs keys, aSERVICESentry (envs + AI prompt). Validate the value insrc/lib/args.jsif it belongs to asinglegroup. - Test:
npm testin the CLI; apply the addon to a copy of the template (node -e "import('./src/steps/addons.js').then(m => m.applyAddons({dir, addons:[...]}))") and run type-check/test/build there.
A new TEMPLATES entry in src/config.js (id, repo, label/badge/hint/
description, available, strip, requires capabilities, tenancy) + the
repo on the community backend's allowlist (cli-paid-gate.md, private
impactus-internal-docs repo) + TEMPLATE_GITHUB_TOKEN scope. The
template's own template.addons.json may declare groups/presets — then
its catalog wins after the download (§4.4) and it can expose new addon
options without a CLI release. Capability steps the template does not
declare in requires are skipped with a stable step counter.
One entry in src/stack-catalog.js (label/hint/role, docs + llms.txt,
skills source, cli spec, mcp argv or mcpNote, envs dev×prod,
optional onlyWhen/forcedBy/testUsers) — the wizard, the manifest
renderer and the tooling step pick it up with no logic changes. The /stack
research ledger later re-verifies the entry against reality (the catalog is
the bootstrap, research wins).
Must be registered everywhere it is listed: the .pi/prompts/<name>.md
prompt file, the Routing table in .pi/skills/fia/SKILL.md (the single
source /guide routes from), the harness command file(s)
(.claude/commands/ + .cursor/commands/ mirror), the finish.js final
panels, the README and this DOCS.
node bin/create-iai.js my-test # runs the installer locally
npm test # node --test (CLI unit tests)
npm run lint && npm run format
npm run sync:skills # regenerate harness/.cursor/skillsMirror rule (single source of truth): shared skills are edited ONLY in
harness/.claude/skills/ — harness/.cursor/skills/ is GENERATED by
npm run sync:skills (Cursor-only skills like project-workflow and
workflow-* are untouched). test/consistency.test.js fails on drift and
sync:skills:check reports it without writing. Commands
(.claude/commands ↔ .cursor/commands) stay manual on purpose: their
diffs are intentional (frontmatter, .claude→.cursor paths, Cursor-only
extras like bugbot). Cross-runtime knowledge follows the same principle:
one canonical file + pointers (e.g. the semantic-fields catalog in the
design-system skill; test/semantic-fields.test.js is the tripwire that no
runtime loses its pointer).
Structure: bin/ (entrypoints: create-iai.js = npx impactus, imp.js =
the launcher) · src/main.js (pipeline) · src/config.js (catalogs:
community, template, harness, FIA, addons, tooling, services, shadcn, MCPs) ·
src/stack-catalog.js (the stack layers) · src/lib/ (args, addons/stripper,
auth-client, stack, skills, keys, clerk, command, ui, proc, log, util…) ·
src/steps/ (one file per step) · fia-templates/ + pi-templates/ (the
runtime stamped into projects) · test/ (node:test). The local checkouts
live1/, live2/ and harness/ are gitignored — each piece has its own
repo. lessons.md (local-only, gitignored) is the living log of recurring
problems; internal planning docs live in the private impactus-internal-docs
repo — never add them back here (this repo is public).
Publishing to npm: bump version in package.json + npm publish (the
package ships bin/, src/, fia-templates/, pi-templates/ and
README.md — the FIA/Pi templates are stamped into projects by the CLI
itself; only the SaaS template and the harness come from the community API).