@open-tomato/rafa 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,6 +44,7 @@ import {
44
44
  announceRiskTotal,
45
45
  assessPlanRisk,
46
46
  blockedFaultMessage,
47
+ blockedLineSentence,
47
48
  boardId,
48
49
  branchClaims,
49
50
  checkbox,
@@ -86,6 +87,7 @@ import {
86
87
  ghHostOf,
87
88
  ghIssueReader,
88
89
  ghMissingMessage,
90
+ ghNextBoard,
89
91
  ghPreflightItems,
90
92
  ghPullRequestsIn,
91
93
  ghUnauthenticatedMessage,
@@ -139,6 +141,8 @@ import {
139
141
  readAgentDirectory,
140
142
  readAuthorTrust,
141
143
  readBlockedBy,
144
+ readBranch,
145
+ readBranchPlan,
142
146
  readChoice,
143
147
  readChoiceFlag,
144
148
  readCopyRefs,
@@ -148,7 +152,9 @@ import {
148
152
  readMergeRefusal,
149
153
  readNextState,
150
154
  readNonBlankFlag,
155
+ readOpenPull,
151
156
  readPlanChanges,
157
+ readPlans,
152
158
  readRecords,
153
159
  readRefsText,
154
160
  readRequiredFlag,
@@ -210,7 +216,7 @@ import {
210
216
  waitForChecks,
211
217
  writeFrontmatter,
212
218
  writeSession
213
- } from "./index-1vtd5fr7.js";
219
+ } from "./index-4qd537z6.js";
214
220
  import {
215
221
  FINDING_KINDS,
216
222
  FINDING_SIGNALS,
@@ -218,6 +224,7 @@ import {
218
224
  findNextTask,
219
225
  isStampableStub,
220
226
  loadPlanPrerequisites,
227
+ malformedPrerequisiteLines,
221
228
  mergePlanPrerequisites,
222
229
  parsePlan,
223
230
  parsePrerequisites,
@@ -225,7 +232,7 @@ import {
225
232
  runPreflight,
226
233
  splitBlockerComment,
227
234
  trackerPathFor
228
- } from "./index-krjzghpz.js";
235
+ } from "./index-64wec0k7.js";
229
236
  import {
230
237
  CORE_ADAPTER_REGISTRY,
231
238
  EFFORT_STORE_DIR,
@@ -243,7 +250,7 @@ import {
243
250
  spawnClaudeCaptured,
244
251
  sqliteStorePath,
245
252
  typeOfLabels
246
- } from "./index-q491ekzm.js";
253
+ } from "./index-7t0qz29r.js";
247
254
  import {
248
255
  CONFIG_DEFAULTS,
249
256
  CONFIG_FILE,
@@ -252,7 +259,7 @@ import {
252
259
  configFilePath,
253
260
  parseConfigText,
254
261
  readRafaBlocks
255
- } from "./index-6cw1n0qy.js";
262
+ } from "./index-ps5sac9p.js";
256
263
  import {
257
264
  createCommandRegistry,
258
265
  describeRegistry,
@@ -8355,6 +8362,17 @@ async function loadItems(plan2, config) {
8355
8362
  throw refusal2(["rafa doctor: the plan's prerequisites cannot be read:", ` ${messageOf(error)}`]);
8356
8363
  }
8357
8364
  }
8365
+ function refuseMalformedItems(plan2, items) {
8366
+ if (plan2 === null || items.malformed.length === 0)
8367
+ return;
8368
+ const file = basename6(prerequisitesPathForPlan(plan2) ?? plan2);
8369
+ const [head = "", ...rest] = malformedPrerequisiteLines(file, items.malformed);
8370
+ throw refusal2([
8371
+ `rafa doctor: ${head}`,
8372
+ ...rest,
8373
+ "rafa loop start would refuse here, before any probe. No run was started and nothing was stored."
8374
+ ]);
8375
+ }
8358
8376
  function mergedFile(plan2) {
8359
8377
  const file = plan2 === null ? null : prerequisitesPathForPlan(plan2);
8360
8378
  return file !== null && isFile(file) ? file : null;
@@ -8388,6 +8406,7 @@ async function checkPreflight(context, project, seams) {
8388
8406
  const { config } = resolved;
8389
8407
  const { plan: plan2, lookedFor } = choosePlan(project.root, config, named);
8390
8408
  const items = await loadItems(plan2, config);
8409
+ refuseMalformedItems(plan2, items);
8391
8410
  const automatic = readProvider(project.root, config, seams);
8392
8411
  const start2 = readStartTier(plan2, items);
8393
8412
  const tiers = {
@@ -9177,7 +9196,9 @@ var CONFIG_SETTINGS_LINES = Object.freeze([
9177
9196
  `# worktreeIdleDays: ${String(CONFIG_DEFAULTS.cleanupWorktreeIdleDays)} # days before rafa cleanup lists a worktree as idle`,
9178
9197
  "# keep: [] # glob patterns naming branches rafa cleanup never lists",
9179
9198
  "# dangerous:",
9180
- `# acceptStaleRefs: ${String(CONFIG_DEFAULTS.dangerousAcceptStaleRefs)} # true plans past dangling and suspect spec references on every run`
9199
+ `# acceptStaleRefs: ${String(CONFIG_DEFAULTS.dangerousAcceptStaleRefs)} # true plans past dangling and suspect spec references on every run`,
9200
+ "# status:",
9201
+ `# notice: ${String(CONFIG_DEFAULTS.statusNotice)} # false drops the one-line notice of what is new since the last command`
9181
9202
  ]);
9182
9203
  var VERSION_LINE = `version: ${String(CONFIG_DEFAULTS.version)}`;
9183
9204
  var PROJECT_HEADER = Object.freeze([
@@ -12758,7 +12779,7 @@ function openPrContext(context, seams = DEFAULT_PR_SEAMS) {
12758
12779
  readRemote: seams.readRemote
12759
12780
  }));
12760
12781
  const makeProvider = seams.pullRequests ?? ghPullRequestsIn;
12761
- const readBranch = seams.readBranch ?? gitBranch;
12782
+ const readBranch2 = seams.readBranch ?? gitBranch;
12762
12783
  return {
12763
12784
  project,
12764
12785
  reading,
@@ -12768,7 +12789,7 @@ function openPrContext(context, seams = DEFAULT_PR_SEAMS) {
12768
12789
  resolveBudget: config.prResolveBudget,
12769
12790
  trustedAuthors: config.boardTrustedAuthors,
12770
12791
  roadmapIssue: config.roadmapIssue,
12771
- readBranch: () => readBranch(project.root)
12792
+ readBranch: () => readBranch2(project.root)
12772
12793
  };
12773
12794
  }
12774
12795
  async function onProvider(doing, call) {
@@ -16543,7 +16564,7 @@ function readUntagged(root, configured, tags) {
16543
16564
  const released = changelogVersions(text);
16544
16565
  return { path: configured, released, versions: untaggedVersions(released, tags), problem: null };
16545
16566
  }
16546
- function readBranch(git) {
16567
+ function readBranch2(git) {
16547
16568
  const result = git(["rev-parse", "--abbrev-ref", "HEAD"]);
16548
16569
  const branch = result.stdout.trim();
16549
16570
  return result.ok && branch !== "" && branch !== "HEAD" ? branch : null;
@@ -16707,7 +16728,7 @@ function readStatus(context, seams = DEFAULT_RELEASE_SEAMS) {
16707
16728
  versionFile: readVersionFile(project.root, config.versionFile),
16708
16729
  tags,
16709
16730
  untagged: readUntagged(project.root, config.changelog, tags.tags),
16710
- plan: readPending(project.root, asked, readBranch(git), planStubsUnder(project.root, config.planDir), readNotes)
16731
+ plan: readPending(project.root, asked, readBranch2(git), planStubsUnder(project.root, config.planDir), readNotes)
16711
16732
  };
16712
16733
  return { reading, text: renderStatus2(reading) };
16713
16734
  }
@@ -16758,7 +16779,7 @@ var DEFAULT_RELEASE_BRANCH = "main";
16758
16779
  var DEFAULT_REGISTRY = "https://registry.npmjs.org";
16759
16780
  var RELEASE_REMOTE = "origin";
16760
16781
  var INDENT9 = " ";
16761
- function readBranch2(git) {
16782
+ function readBranch3(git) {
16762
16783
  const result = git(["rev-parse", "--abbrev-ref", "HEAD"]);
16763
16784
  if (!result.ok) {
16764
16785
  return { branch: null, problem: `the branch could not be read: ${gitSaid(result)}` };
@@ -16910,7 +16931,7 @@ function tagConfig(project, warn) {
16910
16931
  function readTagInputs(root, git, config) {
16911
16932
  return {
16912
16933
  releaseBranch: config.releaseBranch,
16913
- branch: readBranch2(git),
16934
+ branch: readBranch3(git),
16914
16935
  version: readVersion(root, config.versionFile),
16915
16936
  changelog: readNewestRelease(root, config.changelog),
16916
16937
  tags: readTags(git)
@@ -19411,6 +19432,335 @@ function createSkillShowCommand(seams = DEFAULT_SKILL_SHOW_SEAMS) {
19411
19432
  }
19412
19433
  var show_default6 = createSkillShowCommand();
19413
19434
 
19435
+ // src/status/render.ts
19436
+ var STATUS_SECTION_TITLES = Object.freeze({
19437
+ branch: "Branch",
19438
+ loops: "Loops",
19439
+ pull: "Pull request",
19440
+ board: "Board",
19441
+ housekeeping: "Housekeeping"
19442
+ });
19443
+ var INDENT10 = " ";
19444
+ var MERGEABILITY_WORDS = Object.freeze({
19445
+ mergeable: "mergeable",
19446
+ conflicting: "conflicting",
19447
+ unknown: "mergeability unknown"
19448
+ });
19449
+ var VERDICT_WORDS = Object.freeze({
19450
+ green: "checks green",
19451
+ red: "checks red",
19452
+ pending: "checks pending",
19453
+ none: "no checks"
19454
+ });
19455
+ function counted3(count, noun, plural5 = `${noun}s`) {
19456
+ return `${String(count)} ${count === 1 ? noun : plural5}`;
19457
+ }
19458
+ function info(text) {
19459
+ return { level: "info", text };
19460
+ }
19461
+ function branchText(reading) {
19462
+ const plan2 = reading.plan === null ? "no plan" : `plan \`${reading.plan.stub}\` (${formatCounts(reading.plan.tasks)})`;
19463
+ return `\`${reading.branch}\`, ${plan2}`;
19464
+ }
19465
+ function blockedTaskCount(blocked) {
19466
+ return blocked.reduce((sum, one) => sum + one.tasks.length, 0);
19467
+ }
19468
+ function loopsText(reading) {
19469
+ const tasks = blockedTaskCount(reading.blocked);
19470
+ return `${String(reading.live.length)} running, ${counted3(tasks, "task")} blocked`;
19471
+ }
19472
+ function loopsBody(reading) {
19473
+ const running = reading.live.map((record) => info(`${INDENT10}${sessionLine(record)}`));
19474
+ const blocked = reading.blocked.flatMap((one) => one.tasks.map((task) => {
19475
+ const blocker = task.blocker === null ? "" : ` (${task.blocker})`;
19476
+ return info(`${INDENT10}blocked: plan \`${planLabel(one.session)}\` line ${String(task.line)}: ${task.text}${blocker}`);
19477
+ }));
19478
+ return [...running, ...blocked];
19479
+ }
19480
+ function pullText(reading) {
19481
+ if (reading.pull === null)
19482
+ return "none open";
19483
+ const { summary, mergeable, verdict } = reading.pull;
19484
+ return `#${String(summary.number)} ${summary.title}, ${MERGEABILITY_WORDS[mergeable]}, ${VERDICT_WORDS[verdict]}`;
19485
+ }
19486
+ function boardText(reading) {
19487
+ const roadmap = `roadmap #${String(reading.roadmap)}`;
19488
+ const next = reading.next === null ? `${roadmap} has no line left` : `next is #${String(reading.next.line.issue)} on ${roadmap}, ${nextStanding(reading.next)}`;
19489
+ const blocked = reading.blockedIssues === null ? `the ${SPEC_BLOCKED_LABEL} issues were not read` : `${counted3(reading.blockedIssues, "issue")} labelled ${SPEC_BLOCKED_LABEL}`;
19490
+ return `${next}; ${blocked}`;
19491
+ }
19492
+ function nextStanding(next) {
19493
+ if (next.blocked !== null)
19494
+ return blockedLineSentence(next.blocked);
19495
+ return next.ready ? "ready" : "not ready";
19496
+ }
19497
+ function housekeepingText(reading) {
19498
+ const { merged: merged2, stale, notPushed, worktrees } = reading.counts;
19499
+ return `${String(merged2)} merged, ${String(stale)} stale, ${String(notPushed)} not pushed,` + ` ${counted3(worktrees, "worktree")} (${String(reading.idleWorktrees)} idle)`;
19500
+ }
19501
+ function sectionLines(key, section, text, body = () => []) {
19502
+ const title = STATUS_SECTION_TITLES[key];
19503
+ if (!section.read)
19504
+ return [{ level: "warn", text: `${title}: not read: ${section.problem}` }];
19505
+ return [info(`${title}: ${text(section)}`), ...body(section)];
19506
+ }
19507
+ function renderStatus3(sections) {
19508
+ return [
19509
+ ...sectionLines("branch", sections.branch, branchText),
19510
+ ...sectionLines("loops", sections.loops, loopsText, loopsBody),
19511
+ ...sectionLines("pull", sections.pull, pullText),
19512
+ ...sectionLines("board", sections.board, boardText),
19513
+ ...sectionLines("housekeeping", sections.housekeeping, housekeepingText)
19514
+ ];
19515
+ }
19516
+ function sectionData(section) {
19517
+ const copy = JSON.parse(JSON.stringify(section));
19518
+ const reading = Object.fromEntries(Object.entries(copy).filter(([key]) => key !== "read"));
19519
+ return { read: section.read, ...reading };
19520
+ }
19521
+ function statusData(sections) {
19522
+ return {
19523
+ branch: sectionData(sections.branch),
19524
+ loops: sectionData(sections.loops),
19525
+ pull: sectionData(sections.pull),
19526
+ board: sectionData(sections.board),
19527
+ housekeeping: sectionData(sections.housekeeping)
19528
+ };
19529
+ }
19530
+
19531
+ // src/status/sections.ts
19532
+ var STATUS_NETWORK_TIMEOUT_MS = 5000;
19533
+ function isIdleWorktree(row) {
19534
+ return !row.blockers.some((blocker) => blocker.kind === "recent");
19535
+ }
19536
+ function unread2(problem2) {
19537
+ return { read: false, problem: problem2 };
19538
+ }
19539
+ function read2(reading) {
19540
+ return { ...reading, read: true };
19541
+ }
19542
+ function localSection(work) {
19543
+ try {
19544
+ const reading = work();
19545
+ return isUnread(reading) ? reading : read2(reading);
19546
+ } catch (error) {
19547
+ return unread2(messageOf(error));
19548
+ }
19549
+ }
19550
+ function isUnread(value) {
19551
+ return typeof value === "object" && value !== null && value.read === false;
19552
+ }
19553
+ function openDeadline(ms) {
19554
+ let endsAt = null;
19555
+ return {
19556
+ ms,
19557
+ start: () => {
19558
+ endsAt = Date.now() + ms;
19559
+ },
19560
+ left: () => endsAt === null ? ms : endsAt - Date.now()
19561
+ };
19562
+ }
19563
+ function boundedRunner(open, root, deadline) {
19564
+ return (args) => {
19565
+ const left = deadline.left();
19566
+ if (left <= 0) {
19567
+ return Promise.resolve({
19568
+ ok: false,
19569
+ stdout: "",
19570
+ stderr: `gh ${args.join(" ")} was not run: the ${String(deadline.ms)}ms network deadline had passed`
19571
+ });
19572
+ }
19573
+ return open(root, left)(args);
19574
+ };
19575
+ }
19576
+ async function networkSection(what, deadline, work) {
19577
+ let timer;
19578
+ const expired = new Promise((resolve18) => {
19579
+ timer = setTimeout(() => {
19580
+ resolve18(unread2(`the ${what} was not read within the ${String(deadline.ms)}ms network deadline`));
19581
+ }, Math.max(0, deadline.left()));
19582
+ });
19583
+ try {
19584
+ return await Promise.race([work().then((reading) => read2(reading)), expired]);
19585
+ } catch (error) {
19586
+ return unread2(messageOf(error));
19587
+ } finally {
19588
+ clearTimeout(timer);
19589
+ }
19590
+ }
19591
+ function notGhProblem(what, provider) {
19592
+ const from = provider.source === "config" ? "as the config names it" : "as origin is no GitHub remote";
19593
+ return `pr.provider is ${provider.provider}, ${from}, and rafa status reads the ${what} through gh alone`;
19594
+ }
19595
+ function readBranchSection(sources) {
19596
+ let notes = [];
19597
+ const note = (problem2) => {
19598
+ notes = [...notes, problem2];
19599
+ };
19600
+ const branch = readBranch(sources, note);
19601
+ if (branch === "") {
19602
+ return unread2(notes.join("; ") || "the branch at the project root could not be read");
19603
+ }
19604
+ const plans = readPlans(sources, note);
19605
+ return { branch, plan: readBranchPlan(branch, plans), notes };
19606
+ }
19607
+ function blockedSessions(root, records) {
19608
+ let seen = new Set;
19609
+ let found = [];
19610
+ for (const session of [...records].reverse()) {
19611
+ const checklist = readSessionChecklist(root, session);
19612
+ if (checklist === null || seen.has(checklist.file))
19613
+ continue;
19614
+ seen = new Set([...seen, checklist.file]);
19615
+ const tasks = blockedTasks(checklist);
19616
+ if (tasks.length > 0)
19617
+ found = [...found, { session, checklist: checklist.file, tasks }];
19618
+ }
19619
+ return found;
19620
+ }
19621
+ function readLoopsSection(root, sources) {
19622
+ return localSection(() => {
19623
+ const records = sources.runs();
19624
+ return { live: records.filter((record) => isLive(record)), blocked: blockedSessions(root, records) };
19625
+ });
19626
+ }
19627
+ function pullSection(sources, branch, provider, deadline) {
19628
+ if (provider.provider !== "gh")
19629
+ return Promise.resolve(unread2(notGhProblem("pull request", provider)));
19630
+ if (!branch.read) {
19631
+ return Promise.resolve(unread2(`no pull request was looked for, as the branch was not read: ${branch.problem}`));
19632
+ }
19633
+ return networkSection("pull request", deadline, () => readPull(sources, branch.branch));
19634
+ }
19635
+ async function readPull(sources, branch) {
19636
+ let notes = [];
19637
+ const pull = await readOpenPull(sources, branch, (problem2) => {
19638
+ notes = [...notes, problem2];
19639
+ });
19640
+ return { pull, notes };
19641
+ }
19642
+ async function readBoard(board, blocked) {
19643
+ const [walk, report3] = await Promise.all([board.next(), blocked]);
19644
+ const { line } = walk;
19645
+ const next = line === null ? null : { line, ready: await board.isReady(line.issue), blocked: await board.blocking(line.issue) };
19646
+ const listed2 = report3.problem === null ? [] : [`the issues labelled ${SPEC_BLOCKED_LABEL} could not be read: ${report3.problem}`];
19647
+ return {
19648
+ roadmap: walk.roadmap,
19649
+ next,
19650
+ passed: walk.passed,
19651
+ blockedIssues: report3.problem === null ? report3.readings.length : null,
19652
+ notes: [...walk.problems, ...listed2]
19653
+ };
19654
+ }
19655
+ async function readHousekeeping(input, cleanup, now) {
19656
+ try {
19657
+ const settings = doctorCleanupSettings({ root: input.root, home: input.home, config: input.config, gh: null }, now);
19658
+ const reading = await readCleanup(cleanup, settings);
19659
+ if (!reading.ok)
19660
+ return unread2(reading.detail);
19661
+ const idle = reading.worktrees.filter((row) => isIdleWorktree(row));
19662
+ return read2({ counts: cleanupCounts(reading), idleWorktrees: idle.length, notes: reading.notes });
19663
+ } catch (error) {
19664
+ return unread2(messageOf(error));
19665
+ }
19666
+ }
19667
+ async function readStatusSections(input, seams = {}) {
19668
+ const { root, config } = input;
19669
+ const git = (seams.openGit ?? createGitRunner)(root);
19670
+ const openGh2 = seams.openGh ?? ((cwd, timeoutMs) => createGhRunner({ cwd, timeoutMs }));
19671
+ const deadline = openDeadline(seams.timeoutMs ?? STATUS_NETWORK_TIMEOUT_MS);
19672
+ const gh = boundedRunner(openGh2, root, deadline);
19673
+ const pulls = (seams.pullRequests ?? ((runner) => createGhPullRequests({ gh: runner })))(gh);
19674
+ const board = (seams.board ?? ghNextBoard)({ gh, git, configured: config.roadmapIssue });
19675
+ const sources = {
19676
+ base: config.prBase ?? DEFAULT_BASE_BRANCH,
19677
+ plans: plansDirAt(root, config.planDir),
19678
+ runs: () => readSessions(root, { isAlive: seams.isAlive }),
19679
+ git,
19680
+ pulls,
19681
+ board
19682
+ };
19683
+ const branch = localSection(() => readBranchSection(sources));
19684
+ const loops = readLoopsSection(root, sources);
19685
+ const provider = resolvePrProvider({ configured: config.prProvider, dir: root, readRemote: seams.readRemote });
19686
+ const isGh = provider.provider === "gh";
19687
+ const cleanup = (seams.cleanupSeams ?? defaultCleanupSeams)(root, isGh ? pulls : null);
19688
+ const now = (seams.now ?? (() => new Date))();
19689
+ const blockedIssues = seams.blockedIssues ?? ((runner) => readBlockedIssues({ gh: runner }));
19690
+ deadline.start();
19691
+ const pull = pullSection(sources, branch, provider, deadline);
19692
+ const boardSection = isGh ? networkSection("board", deadline, () => readBoard(board, blockedIssues(gh))) : Promise.resolve(unread2(notGhProblem("board", provider)));
19693
+ const housekeeping = readHousekeeping(input, cleanup, now);
19694
+ return {
19695
+ branch,
19696
+ loops,
19697
+ pull: await pull,
19698
+ board: await boardSection,
19699
+ housekeeping: await housekeeping
19700
+ };
19701
+ }
19702
+
19703
+ // src/commands/status.ts
19704
+ var COMMAND_NAME6 = "rafa status";
19705
+ var STATUS_USAGE = "rafa status";
19706
+ var STATUS_ARGUMENT_EXIT = 2;
19707
+ var DEFAULT_STATUS_SEAMS = Object.freeze({});
19708
+ function projectOf15(context) {
19709
+ if (context.project === null)
19710
+ throw new Error("rafa status runs inside a project, and was handed none");
19711
+ return context.project;
19712
+ }
19713
+ function expectNoWord(args) {
19714
+ if (args.length === 0)
19715
+ return;
19716
+ throw new CommandExit(STATUS_ARGUMENT_EXIT, `\u274C ${COMMAND_NAME6}: expected no argument, got ${String(args.length)}: ${args.join(" ")}
19717
+ Usage: ${STATUS_USAGE}`);
19718
+ }
19719
+ async function runStatus2(context, seams) {
19720
+ expectNoWord(context.args);
19721
+ const project = projectOf15(context);
19722
+ const config = resolveProjectConfig(project, COMMAND_NAME6, (message) => {
19723
+ context.output.warn(message);
19724
+ });
19725
+ const read3 = seams.read ?? ((input) => readStatusSections(input));
19726
+ const sections = await read3({ root: project.root, home: project.home, config });
19727
+ if (context.outputMode === "json") {
19728
+ context.output.result(statusData(sections));
19729
+ return;
19730
+ }
19731
+ for (const line of renderStatus3(sections)) {
19732
+ if (line.level === "warn")
19733
+ context.output.warn(line.text);
19734
+ else
19735
+ context.output.info(line.text);
19736
+ }
19737
+ }
19738
+ function createStatusCommand(seams = DEFAULT_STATUS_SEAMS) {
19739
+ const command = {
19740
+ name: "status",
19741
+ subject: "status",
19742
+ action: "status",
19743
+ summary: "where the project stands: branch and plan, loops, pull request, board and housekeeping",
19744
+ description: "Prints one line for each of five sections. Branch: the branch checked out at the project root" + " and the plan its `feat/<stub>` names, with its task counts. Loops: how many sessions are running and" + " how many tasks are blocked, with a line under it for each running loop and each blocked task. Pull" + " request: the branch's open pull request, whether it can be merged and its checks. Board: the" + " Roadmap's next issue and whether it is ready, and how many issues carry `spec:blocked`. Housekeeping:" + " what `rafa cleanup` would list, counted per group, and how many worktrees are idle. Git, the session" + " records and the plans are read first; the pull request and the board are read through `gh` under a" + " short deadline, and nothing is fetched. A section that cannot be read is one `warn` line saying why," + " and the others are printed all the same: it exits 1 only for a config that cannot be used. With" + " `--output=json` the five sections are the data of the terminal result event. Starts no session.",
19745
+ args: [],
19746
+ flags: [],
19747
+ examples: [
19748
+ {
19749
+ cmd: "rafa status",
19750
+ note: "Prints the five sections, one line each, with the running loops and blocked tasks under Loops."
19751
+ },
19752
+ {
19753
+ cmd: "rafa status --output=json",
19754
+ note: "Gives the five sections as data, each with `read` and its reading or the problem."
19755
+ }
19756
+ ],
19757
+ outputs: ["text", "json"],
19758
+ run: async (context) => runStatus2(context, seams)
19759
+ };
19760
+ return Object.freeze(command);
19761
+ }
19762
+ var status_default3 = createStatusCommand();
19763
+
19414
19764
  // src/commands/usage.ts
19415
19765
  var usage_default = wrapPhaseZeroCommand({
19416
19766
  name: "usage",
@@ -19489,6 +19839,7 @@ var CORE_COMMANDS = Object.freeze([
19489
19839
  show_default2,
19490
19840
  status_default2,
19491
19841
  tag_default,
19842
+ status_default3,
19492
19843
  next_default,
19493
19844
  roadmap_default,
19494
19845
  init_default,
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  start,
21
21
  usage,
22
22
  validateManifest
23
- } from "./index-1vtd5fr7.js";
23
+ } from "./index-4qd537z6.js";
24
24
  import {
25
25
  FINDING_KINDS,
26
26
  FINDING_SIGNALS,
@@ -38,7 +38,7 @@ import {
38
38
  renderInjection,
39
39
  runPreflight,
40
40
  runShellProbe
41
- } from "./index-krjzghpz.js";
41
+ } from "./index-64wec0k7.js";
42
42
  import {
43
43
  CORE_ADAPTER_REGISTRY,
44
44
  EFFORT_KEY_PROJECTIONS,
@@ -47,7 +47,7 @@ import {
47
47
  openNdjsonStore,
48
48
  openSqliteStore,
49
49
  selectEffortStore
50
- } from "./index-q491ekzm.js";
50
+ } from "./index-7t0qz29r.js";
51
51
  import {
52
52
  CONFIG_DEFAULTS,
53
53
  CONFIG_FILE,
@@ -58,7 +58,7 @@ import {
58
58
  parseConfigText,
59
59
  readRafaBlocks,
60
60
  resolveConfig
61
- } from "./index-6cw1n0qy.js";
61
+ } from "./index-ps5sac9p.js";
62
62
  import"./index-qzhpqmfy.js";
63
63
  import"./index-p901zs2p.js";
64
64
  import {
@@ -16,12 +16,12 @@ import {
16
16
  renderInjection,
17
17
  runPreflight,
18
18
  runShellProbe
19
- } from "../index-krjzghpz.js";
19
+ } from "../index-64wec0k7.js";
20
20
  import {
21
21
  RAFA_BLOCK_KINDS,
22
22
  isRafaBlockKind,
23
23
  readRafaBlocks
24
- } from "../index-6cw1n0qy.js";
24
+ } from "../index-ps5sac9p.js";
25
25
  import {
26
26
  INJECT_MODES
27
27
  } from "../index-n3rw8rhr.js";
@@ -66,7 +66,9 @@ criteria — **do not execute the plan**.
66
66
  as a checklist in the PREREQUISITES.md format of the dev-planner
67
67
  specification below. Only genuinely non-automatable steps; do not duplicate
68
68
  what `README.md`/`AGENTS.md` already require of every contributor. Link to
69
- it from the plan.
69
+ it from the plan. An `[auto]` item holds exactly one backticked span, and
70
+ that span is its command, ending the item after `: ` and run as written:
71
+ prove a tool with `<tool> --version`, `<tool> --help` or `which <tool>`.
70
72
  * Do not create or touch any tracker file — the loop derives it from the plan.
71
73
 
72
74
  {PLAN_FORMAT}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-tomato/rafa",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Alpha. Runs a plan through the ralph loop: one Claude Code session per task, one commit per task, then a pull request and a CI wait.",
5
5
  "private": false,
6
6
  "publishConfig": {