The instantprd CLI hands an approved plan to a coding agent one task at a time. It pulls the next
task with its full specification, claims it, submits evidence for it, and stops where you told it to
stop.
Everything on this page is the behaviour of the published package and the API it talks to. If a command or a response here disagrees with what you see, the code is right and this page is a bug.
The package is published to npm; npx is enough, there is nothing to install globally.
npx instantprd login
login opens your browser once per machine. The CLI starts a loopback callback server, sends a
random state with the request, and the web app posts the freshly minted token back to it. The
token is then verified against GET /api/cli/v1/doctor before anything is written, and stored in
~/.instantprd/config.json with mode 0600.
In CI, skip the browser:
npx instantprd login --token "$INSTANTPRD_TOKEN"
Two companions:
npx instantprd whoami # account, token scope, and the plans this token can reach
npx instantprd logout # removes the stored token (a token in the environment survives)
Create and revoke tokens in Settings → Access tokens. A token is iprd_ followed by 40 characters,
and it is shown once — the server stores only a hash and a prefix.
Highest wins:
environment > flag > .instantprd.json (this directory or a parent) > ~/.instantprd/config.json > default
| Variable | Meaning |
|---|
The token never comes from .instantprd.json. That file is committed to your repository, and a
committed file is not a place for a credential.
.instantprd.json carries the appUrl, and it is committed — which means a repository you clone
chooses where the CLI would send your token. So the CLI does not send it. A saved token travels only
to the origin it was issued for, or to a host you named yourself with --url or
INSTANTPRD_API_URL.
When the origins disagree you get a refusal rather than a bare 401:
Your saved token was issued for https://instantprd.web.id, but this command targets
https://elsewhere.example (set by /repo/.instantprd.json).
It was NOT sent — a repository you clone can set that URL.
If you trust the host, say so explicitly: instantprd login --url https://elsewhere.example, or
pass --token for a single command. instantprd doctor prints the same explanation.
npx instantprd init --plan <planId>
init writes the agent configuration pack into the current directory:
Running it twice leaves every byte identical: the marker block is replaced in place, never appended
a second time, and text you wrote outside the markers is not touched. Edit inside the markers and
the next init will overwrite it.
The skill itself is fetched from GET /api/cli/v1/agent-skill, which serves one reviewed constant
identically to every caller, so a repository picks up a reviewed update. Offline or unauthenticated,
init falls back to the copy bundled with the package — the same content.
If .instantprd.json already points at a different plan, init leaves it alone and says so; re-run
with --force to repoint it.
The workspace has this whole sequence, plus the first prompt for your agent, behind Start implementation → One-click start — one copy-paste block per supported tool, with the plan id already filled in.
npx instantprd task next --json
npx instantprd task start T-014
npx instantprd task complete T-014 --evidence evidence.json
task nextPrints the next task in server execution order with everything needed to implement it: description,
acceptance criteria (numbered from 0 — those indices are what evidence refers to), test plan, layer,
page, estimate, dependencies, the rejection note if a human sent it back, plan progress, and whether
a checkpoint is blocking. --json prints the raw response and nothing else.
Selection is deterministic: a task already doing comes back first, so a restarted agent resumes
where it stopped; otherwise the first todo or failed task by sort key whose dependencies are all
done. Tasks in needs_review are never handed out — a human is holding them.
task start <ref>Claims the task (doing). Refused at an open checkpoint — see below. --notes stores a note in the
task log; --force crosses a checkpoint deliberately and records that it was forced.
task complete <ref>Finishes the task with evidence. Evidence is not optional: a completion without it is refused.
With no --evidence, the CLI writes instantprd-evidence-<ref>.json next to you, pre-filled with
one entry per acceptance criterion, and exits 4 so you can fill it in and re-run. Use --yes only
when you really ran the tests; it marks every criterion passed on your behalf.
task fail <ref> --reason "<why>"Gives the task back with a concrete reason. The reason is required — "it failed" is useless to the person who picks it up. It is stored on the task and shown on the board.
{
"summary": "Added POST /bookings with validation and its tests.",
"checks": [
{ "criterion_index": 0, "passed": true, "note": "bookings.test.ts covers the 201 case" },
{ "criterion_index": 1, "passed": true, "note": "returns 422 on an overlapping slot" }
],
"test_output": "PASS src/bookings/__tests__/create.test.ts (6 tests)",
"files_changed": ["src/app/api/bookings/route.ts", "src/bookings/__tests__/create.test.ts"]
}
What the server does with it:
done, marked
verified, and each criterion ticked;needs_review, with the missing and failed
indices in the response;needs_review too, whatever the checks say. A human signs it off.A completion with no checks answers 422 evidence_required and includes the expected payload in
details.expected, so an agent can correct itself without reading this page. An index that does not
exist on that task answers 422 evidence_invalid.
A rejected task comes back with the human's note in rejection_note on the next task next.
By default the agent stops at every phase and layer boundary. That is the design, not an error.
Starting a task across an unfinished boundary answers 409 checkpoint_required:
{
"error": "checkpoint_required",
"message": "Phase 1 backend tasks are finished — check them before the agent continues.",
"details": {
"reason": "phase_boundary",
"message": "…",
"continue_url": "https://instantprd.web.id/workspace/<planId>/board"
}
}
reason is phase_boundary or layer_boundary. The CLI exits 3.
Only a human can clear it, by pressing Continue on the board. That action is web-session only —
there is no bearer-token path to it at all, so a token can never clear the checkpoint it is blocked
by. --force on task start crosses the boundary without clearing it and leaves a forced-crossing
entry in the log; use it only when the human asked for it.
Finishing the last task of a phase or layer raises the banner on the board immediately, so the person watching sees the boundary as it is reached rather than when the agent next bumps into it.
The plan's owner can turn autonomous mode on once the board is ready, and the agent then runs the plan end to end. Two fields say so:
checkpoint.autonomous on GET /tasks/next and PATCH /tasks/:ref — checkpoint.required is
never true on such a plan, and autonomous says that this is the owner's choice rather than the
absence of a boundary;plan.autonomous_mode on GET /plans/:id.Boundaries are still computed and every crossing is still recorded on the task log — the mode changes who is asked, not what is tracked. Turning it off is never gated, on any plan, at any time.
One exception worth knowing: enabling the mode does not clear a banner that is already open. A checkpoint already raised is a real stop and is reported as one.
instantprd syncHands an existing checkout to InstantPRD so the plan can be written against the code that is already there.
npx instantprd sync --dry-run # scan locally, print exactly what would be uploaded, send nothing
npx instantprd sync # metadata only: paths, sizes, languages
npx instantprd sync --full-code # opt in to uploading the code in 120-line chunks
The gate runs on your machine, before any request. .env files in every shape, key material,
node_modules, build output and .git are never opened; .instantprdignore (gitignore syntax,
negations included) narrows it further but can never re-include something the hard block caught.
With --full-code, every file is scanned for secrets first, and one match withholds that file's
contents entirely — the path is still listed, marked, so you can see what was held back. The server
re-runs the same rules on arrival, so a patched CLI cannot push a secret in either.
Other flags: --name, --branch, --plan, --json, --no-analyze (skip the architecture map),
-y to skip the confirmation before uploading code.
Repositories belong to an account, not to a plan, so the repos endpoints refuse a plan-scoped
token with 403:
This token is scoped to a single plan — repositories belong to the account. Create an account-scoped token to sync a repo.
instantprd doctorPrints the resolved app URL and where it came from, whether a token is present and from which source, the selected plan, whether the API answers, whether the token is valid, and whether it can reach the selected plan. It prints the token's source, never the token.
Run it first whenever something is refused and the reason is not obvious.
A token is either account-scoped (every plan its owner can edit) or plan-scoped (exactly one). Two refusals look surprising until you know why:
404, not 403. A plan that does not exist and a plan belonging
to someone else produce identical responses. 403 would confirm the plan exists, which turns a
token into a probe for which plans exist. The same applies to a plan you can only view: the agent
handoff writes, so a viewer is refused exactly like a stranger.403. That decision is made before any
database read, so every foreign plan id gets the same answer.120 requests per minute, per token. Every verified response carries X-RateLimit-Limit and
X-RateLimit-Remaining; a 429 adds Retry-After in seconds. The public API counts in a separate
bucket, so a busy dashboard cannot starve a running agent.
The CLI and the MCP server both speak this API and nothing else. Bearer token on every request:
Authorization: Bearer iprd_….
Every failure is the same envelope, so a client can branch on the code rather than parse prose:
{ "error": "evidence_required", "message": "…", "details": { "expected": { } } }
Meant to be branched on in a script:
If your agent speaks MCP rather than a shell, read MCP server — same token, same contract, no subprocess. To read plans, tasks and progress from your own tooling, read Public API.
INSTANTPRD_TOKEN | access token (iprd_…) |
INSTANTPRD_API_URL | app URL, e.g. https://instantprd.web.id |
INSTANTPRD_PLAN | plan id |
| Path | What it is |
|---|
.claude/skills/instantprd/SKILL.md | the reviewed agent skill, verbatim |
.cursor/rules/instantprd.mdc | the same skill as a Cursor rule |
.agents/skills/instantprd/SKILL.md | the same skill for Antigravity |
AGENTS.md, CLAUDE.md | a managed marker block between <!-- instantprd:start --> and <!-- instantprd:end --> |
.instantprd.json | { "planId": …, "appUrl": … } — committed, and holds no token |
| Flag | Effect |
|---|
--evidence <file> | JSON file with the payload below |
--test-output <file> | file whose contents are sent as test_output |
--summary <text> | summary used when the CLI generates the payload |
--notes <notes> | note stored in the task log |
-y, --yes | generate a payload with every criterion marked passed |
| Field | Rule |
|---|
summary | required, 5–2 000 characters |
checks | required, at least one entry; criterion_index is 0-based, note ≤ 500 characters |
test_output | optional, ≤ 20 000 characters |
files_changed | optional, ≤ 200 paths of ≤ 300 characters |
screenshot | optional base64 PNG/JPEG, ≤ 500 KB decoded |
| Method and path | What it does |
|---|
GET /api/cli/v1/doctor | the identity behind the token, its scope, and the plans it may act on |
GET /api/cli/v1/agent-skill | the reviewed skill file init installs |
GET /api/cli/v1/plans/:planId | plan, latest PRD, feature structure, progress |
GET /api/cli/v1/tasks/next?plan=:planId | the next task with its full spec, progress and checkpoint |
PATCH /api/cli/v1/tasks/:ref?plan=:planId | doing / done (with evidence) / failed |
POST /api/cli/v1/checkpoints/:planId/report | tell the board you reached a boundary (it can never clear one) |
POST /api/cli/v1/checkpoints/:planId/continue | clear a checkpoint — web session only, never a token |
POST /api/cli/v1/repos | register a repository (account-scoped token) |
POST /api/cli/v1/repos/:repoId/files | one batch of the file listing, and code chunks with --full-code |
POST /api/cli/v1/repos/:repoId/analyze | run the architecture map step |
GET /api/cli/v1/repos/:repoId | the repository and its architecture map |
error | HTTP |
|---|
unauthorized | 401 |
forbidden | 403 |
not_found | 404 |
validation, evidence_required, evidence_invalid | 422 |
checkpoint_required, conflict | 409 |
rate_limited | 429 |
server_error | 500 |
| Code | Meaning |
|---|
0 | ok |
1 | error |
2 | authentication required |
3 | checkpoint required — stop and let the human continue |
4 | evidence required or invalid |