Skip to content
open tomato/ rafa
GitHub
rafa pages
  1. rafa
  2. rafa plan

rafa plan

Create a plan from a spec, then list, show, validate and read the risk and needs of plans, before any loop runs them.

rafa plan turns a spec into a plan, a markdown checklist the loop can run, and reads plans before they run. Only create starts a session; every other action reads files and starts none.

Spends: 🪙 create, one planning session. Changes: create writes .rafa/plans/PLAN-<stub>.md, and a PREREQUISITES-<stub>.md beside it when something must be true before the run; the other actions write nothing. Needs: Claude Code signed in for create; gh for a spec that is an issue or the roadmap; see Prerequisites.

Things to know before you use it:

  • Read the plan before you run it. It is plain markdown: edit a task, drop one or add one.
  • rafa plan risk and rafa plan needs answer "what may this run do here, and what does it need" without starting anything. rafa loop start refuses a plan whose agents or skills do not resolve, before any session is paid for; validate tells you first.

Used by the workflows The cycle, rafa next (its plan step) and Stretches (stretch item).

NOTE

In the target layout this code lives in the private package rafa-run, bundled into @open-tomato/rafa (Repository layout). In 0.47.0 it is already a package, @open-tomato/rafa-run in packages/rafa-run, but only as a facade: it re-exports this code from the root package's src/, where the files still live until their move. You install and type the same either way. [verify: the release the move lands in]

Actions

ActionDoes
creategenerate a plan, and its prerequisites, from a spec 🪙
listlist the plans, with the tasks done in each
showshow one plan, or its tracker, stage by stage
validatecheck that a plan file reads as written and routes to agents and skills that resolve, starting no session
riskread what a run of a plan may do on this machine and under your accounts, starting no session
needslist the agents, skills, MCP servers and programs a plan or spec needs, starting no session

rafa plan create

Generate a plan, and its prerequisites, from a spec.

rafa plan create [--spec=<string>] [--issue=<number>] [--next] [--roadmap]
  [--claim-ahead] [--refresh] [--dry-run] [--skip-review] [--accept-refs]
  [--no-comment] [--stub=<string>] [--no-progress] [--no-hint]

Hands the spec to a Claude Code session that writes PLAN-<stub>.md in plan.dir, .rafa/plans unless the config names another, and PREREQUISITES-<stub>.md beside it when the spec needs setup no task can do. The spec is a file (--spec), a GitHub issue (--issue) or the first undone line of the roadmap issue (--next); the two board routes write the issue body to a snapshot under specs.dir, plan from that file, and record issue: <n> in the plan. The stub is the basename of the spec unless --stub names one, and the command refuses when that plan already exists. On the two board routes the references the snapshot names are read against the stamps its saved copy keeps, and one missing or changed since the spec was read refuses the run before any session starts unless --accept-refs re-stamps them. The findings in progress.txt are handed to the session as advisory context unless --no-progress is given. The session loads the setting sources loop.settingSources names, and a config the loop cannot run on refuses the command before any session starts.

Spends: 🪙 one planning session

FlagType and defaultWhat it does
--specstringThe spec to plan from, relative to the project root, or under specs.dir (.rafa/specs unless the config names another) when the root holds no such file.
--issuenumberThe number of the type:spec issue to plan from. Its body is written to a snapshot under specs.dir, with rafa-<n>-notes.md beside it appended, and the planner reads that file.
--nextbooleanPlans the first line of the roadmap issue that is neither done nor taken, then as --issue. The roadmap is roadmap.issue in the config, else the pinned issue titled Roadmap; --next=<n> reads issue <n> as the roadmap instead.
--roadmapbooleanWith --next, plans the target of the rafa next --roadmap hop under way, the blocker it went to another epic or board for, through the same checks as any line; with no hop away it picks as --next does. Refused with exit 2 without --next.
--claim-aheadbooleanWith --next, also claims the line after the one it picks, on the same board, in the same push as the pick's own claim: both claims or neither. claims.ahead: allow in the config does it on every --next run. Refused with exit 2 without --next.
--refreshbooleanRebuilds a snapshot that no longer matches the issue without asking; the old copy moves under previous/.
--dry-runbooleanReads and refuses everything and writes nothing: no snapshot, no session. Prints what a real run would have planned from.
--skip-reviewbooleanPlans without the planner judging the spec first. The label and the code checks still run, and the plan records review: skipped.
--accept-refsbooleanRe-stamps every reference the spec names as reviewed and plans past the dangling and suspect ones check 4 would refuse, on this run only; dangerous.acceptStaleRefs: true in the config does it on every run.
--commentboolean, default truePosts the gaps of a spec judged not ready on its issue; --no-comment prints them and writes nothing on the board. Whatever the verdict does to the labels is unchanged by it.
--stubstringThe plan stub, which names PLAN-<stub>.md. Defaults to the basename of the spec.
--progressboolean, default trueHands the findings in progress.txt to the session; --no-progress leaves them out.
--hintboolean, default trueEnds by naming the one step that follows, asking it where there is a terminal to answer on; --no-hint reads nothing and prints nothing.

Examples:

  • rafa plan create --spec=.rafa/specs/my-feature.md: Writes .rafa/plans/PLAN-my-feature.md from the spec.
  • rafa plan create --spec=my-feature.md --stub=my-feature-v2 --no-progress: Reads .rafa/specs/my-feature.md when the project root holds no my-feature.md, and writes .rafa/plans/PLAN-my-feature-v2.md without the findings in progress.txt.
  • rafa plan create --issue=20: Snapshots the body of issue 20 to .rafa/specs/rafa-20-<slug>.md and plans from it, recording issue: "20" in the plan. Refuses an issue that is closed or unlabelled; one that is not marked spec:ready is offered the label where there is a terminal, and refused where there is not.
  • rafa plan create --next --dry-run: Prints the first roadmap line that is neither done nor taken, with what it skipped to reach it, and stops without writing a snapshot or starting a session.
  • rafa plan create --next --roadmap: While a rafa next --roadmap hop is away, plans the blocker it went for on that blocker's own board; with none away, plans as rafa plan create --next does.
  • rafa plan create --next --claim-ahead: Plans the first undone line of the roadmap and claims it and the undone line after it in one push, so the issue after this one is reserved too; neither claim lands when either is refused.
  • rafa plan create --issue=20 --refresh --skip-review --no-comment: Takes issue 20 as it reads now over the snapshot already there, plans without the planner judging the spec first, and keeps any gaps off the board.

Output formats: text, json.

rafa plan list

List the plans, with the tasks done in each.

rafa plan list [--open]

Lists each PLAN-<stub>.md in the plans directory plan.dir names, in stub order, with its tasks counted by checkbox: from its tracker, PLAN_TRACKER-<stub>.md, once there is one, and from the plan itself before then. A plan with no tracker is marked no tracker, and a plan the plan parser did not read as written shows how many issues rafa plan validate reports for it. The directory is read under the project root, and is .rafa/plans unless a config names another. With --open only the plans with a task still open, or with issues, are listed. With --output=json the list is the data of the terminal result event, each path absolute.

FlagType and defaultWhat it does
--openbooleanLists only the plans with a task still open, or with issues; takes no value.

Examples:

  • rafa plan list: Prints one row per plan: its stub, then its tasks done, blocked and open.
  • rafa plan list --open: Prints only the plans with open tasks or issues, hiding the done and all-blocked ones.
  • rafa plan list --output=json: Writes a start event, then a result event whose data is the list.

Output formats: text, json.

rafa plan show

Show one plan, or its tracker, stage by stage.

rafa plan show <stub> [--tracker]

Reads a plan file in the plans directory plan.dir names, under the project root, with the plan parser and prints its stub, the issue and spec its rafa:plan block names, and its tasks counted by checkbox, then each task under its stage heading and every issue the parser reported. With --tracker it reads the loop tracker copy instead, which the loop ticks as tasks finish. Type --tracker after the stub: typed before it, the stub is read as the value of the flag and refused. With --output=json the plan is the data of the terminal result event.

ArgumentTypeWhat it is
stubstring, requiredThe plan stub, which names the corresponding plan file.
FlagType and defaultWhat it does
--trackerbooleanReads the loop tracker copy instead of the plan, which it ticks as tasks finish.

Examples:

  • rafa plan show my-feature: Prints the plan stage by stage.
  • rafa plan show my-feature --tracker --output=json: Writes a start event, then a result event whose data is the tracker as the plan parser reads it.

Output formats: text, json.

rafa plan validate

Check that a plan file reads as written and routes to agents and skills that resolve, starting no session.

rafa plan validate <file>

Reads one plan file with the plan parser, the rafa:* blocks and the checklist, and starts no session. When the parser reads the whole file as written, and every agent= of its still-to-run tasks resolves for a session spawned under loop.settingSources, tiers.rafa and tiers.agents, it prints the stages and the tasks counted by checkbox. Otherwise it writes every issue the parser reported as an error line naming the file, the line and the reason, then one line per agent no loaded tier serves, saying why (held by no tier, switched off, held only by a tier the session does not load, or held by two tiers with different contents) and naming the paths and the pin line or setting that settles it, then one line per skills= name two loaded tiers hold with different contents, naming both paths and the tiers.skills pin line that settles it, then one line per skills= name no loaded tier resolves (held by no tier, switched off, or held only by a tier the session does not load), naming what settles it, then one line per effort-store rule the plan breaks (a store change pinned by number; and, in a plan whose task names migration <id>, a command running src/rafa.ts, dist/cli.js or bun run rafa without a leading RAFA_EFFORT_DIR=, or no [auto] rafa effort schema --check probe in its PREREQUISITES), then exits 1 — the same check rafa loop start halts on before it dispatches anything. A task that edits an agent or a skill rafa bundles, under src/bundled/ or through a .claude/ link into it, gets a note naming the item and the tiers.agents or tiers.skills pin a clone that merges the plan needs, never a refusal; when nothing refuses the plan the pin is written as a [human] item of the PREREQUISITES-<stub>.md beside it, once. The path is read relative to the working directory; the agents and the bundle are read from the project found from it and the config that resolves there, and are left unread when there is no project. With --output=json each issue and each missing agent is an error log event, each note an info log event, and a plan with neither is the data of the terminal result event, its bundledEdits listing the edits.

ArgumentTypeWhat it is
filestring, requiredThe plan file to read, relative to the working directory.

Examples:

  • rafa plan validate .rafa/plans/PLAN-my-feature.md: Prints one line when the plan reads as written, after a note per bundled agent or skill a task edits with its pin written to PREREQUISITES-my-feature.md, or one error line per issue and exits 1.
  • rafa plan validate .rafa/plans/PLAN-my-feature.md --output=json: Writes a start event, an error log event per issue, then a result event whose bundledEdits names each bundled item a task edits.

Output formats: text, json.

rafa plan risk

Read what a run of a plan may do on this machine and under your accounts, starting no session.

rafa plan risk [plan] [--strict]

Reads one plan in code, with no model, and reports what a run of it may do: per open task the tools it may use, its model and its budget; destructive commands on the command lines of the plan, its task lines' code spans and the shell fences of the agents and skills its tasks declare; paths outside the repository those commands name; the push remote and its visibility, the issue tracker and the pull request provider; and the NAMES of the environment variables that look like credentials, never their values. Each finding is high or note. It reads what the plan and its skills say, not what a session will decide to do: it is not a sandbox. The plan is read relative to the project root, as rafa loop start --plan= reads it, and is the default plan loop start would run when none is named. Exits 0 whatever it finds, unless --strict is typed and a finding is high. With --output=json the reading is the data of the terminal result event; under --strict each high finding is an error log event instead.

ArgumentTypeWhat it is
planstringThe plan file to read, relative to the project root; the default plan when left out.
FlagType and defaultWhat it does
--strictbooleanExits 1 when any finding is high, for scripts and CI. Type it after the plan.

Examples:

  • rafa plan risk .rafa/plans/PLAN-my-feature.md: Prints the high findings, then the notes, then the one-line total and what the reading is not.
  • rafa plan risk .rafa/plans/PLAN-my-feature.md --strict: Prints the same report, and exits 1 when any finding is high.
  • rafa plan risk --output=json: Writes a start event, then a result event whose data is the default plan's reading.

Output formats: text, json.

rafa plan needs

List the agents, skills, MCP servers and programs a plan or spec needs, starting no session.

rafa plan needs [plan] [--spec=<string>] [--issue=<string>] [--missing]
  [--source=<string>]

Reads one plan, or one spec, in code, with no model, and lists what it needs from this machine: for a plan, the agent= and skills= of every open task, the MCP server each mcp__<server>__… tool names, the programs its PREREQUISITES probes and its declared skills' shell fences call, and the symbol tool its stack needs; for a spec, every agent and skill name its text mentions. Each is present or missing, with its source, its MCP scope or its PATH directory, and whether a session the loop spawns under loop.settingSources sees it. The plan is read relative to the project root, as rafa loop start --plan= reads it, and is the default plan when none is named; --spec and --issue find the spec as rafa plan create does, an issue snapshotted under specs.dir as it would be. --missing keeps what a run would not have and exits 1 when anything is kept, printing nothing when all is provided; --source=<source> keeps the agents and skills held by one source. With --output=json the reading is the data of the terminal result event; under a failing --missing each kept row is an error log event instead.

ArgumentTypeWhat it is
planstringThe plan file to read, relative to the project root; the default plan when left out.
FlagType and defaultWhat it does
--specstringRead a spec file instead, found against the project root or under specs.dir.
--issuestringRead the spec of issue <n> instead, checked and snapshotted as plan create does.
--missingbooleanList only what a run would not have, and exit 1 when there is any. Type it after the plan.
--sourcestringList the agents and skills held by one source: project, rafa, user, plugin:<name> or addon:<name>.

Examples:

  • rafa plan needs .rafa/plans/PLAN-my-feature.md: Lists each agent, skill, MCP server and program the plan needs, present or missing.
  • rafa plan needs --missing: Lists what a run of the default plan would not have, and exits 1 when there is any.
  • rafa plan needs --spec=my-feature.md --source=project: Lists the project agents and skills the spec mentions.
  • rafa plan needs --issue=42: Reads issue 42 as plan create --issue=42 would, and lists what its spec mentions.
  • rafa plan needs --output=json: Writes a start event, then a result event whose data is the default plan's needs.

Output formats: text, json.