@open-tomato/rafa 0.44.0 → 0.46.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.
Files changed (142) hide show
  1. package/README.md +280 -42
  2. package/dist/PROMPT.md +18 -1
  3. package/dist/adapters/logger/settings.d.ts +132 -0
  4. package/dist/bugs/closest.d.ts +19 -0
  5. package/dist/bugs/fenced.d.ts +8 -0
  6. package/dist/bugs/places.d.ts +20 -0
  7. package/dist/bugs/record.d.ts +145 -0
  8. package/dist/bugs/title.d.ts +83 -0
  9. package/dist/bundled/agents/doc-updater.md +17 -3
  10. package/dist/bundled/agents/loop-implementer.md +14 -2
  11. package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +10 -3
  12. package/dist/bundled/operators/skills/rafa-stretch-sweep/SKILL.md +5 -0
  13. package/dist/bundled/skills/dev-planner/SKILL.md +120 -24
  14. package/dist/bundled/skills/documentation/SKILL.md +2 -12
  15. package/dist/cli.js +72387 -59411
  16. package/dist/config/entry.d.ts +119 -0
  17. package/dist/config/index.d.ts +36 -0
  18. package/dist/config/index.js +51 -0
  19. package/dist/config/shape.d.ts +232 -0
  20. package/dist/config-registry.d.ts +127 -0
  21. package/dist/config-schema-board-project.d.ts +157 -0
  22. package/dist/config-schema-bugs.d.ts +71 -0
  23. package/dist/config-schema-errors.d.ts +54 -0
  24. package/dist/config-schema-features.d.ts +64 -0
  25. package/dist/config-schema-hub.d.ts +150 -0
  26. package/dist/config-schema-issues.d.ts +95 -0
  27. package/dist/config-schema-logger.d.ts +91 -0
  28. package/dist/config-schema-loop-continue.d.ts +122 -0
  29. package/dist/config-schema-loop-retries.d.ts +94 -0
  30. package/dist/config-schema-release.d.ts +307 -0
  31. package/dist/config-schema-tests.d.ts +170 -0
  32. package/dist/config-schema-triage.d.ts +142 -0
  33. package/dist/config-schema-wrap-up.d.ts +96 -0
  34. package/dist/core/command/command.d.ts +228 -0
  35. package/dist/core/command/confirm.d.ts +67 -0
  36. package/dist/core/command/index.d.ts +112 -0
  37. package/dist/core/command/lazy.d.ts +54 -0
  38. package/dist/core/command/registry-types.d.ts +96 -0
  39. package/dist/core/command/running.d.ts +96 -0
  40. package/dist/core/command/spends.d.ts +39 -0
  41. package/dist/core/command/types.d.ts +92 -0
  42. package/dist/core/command/version.d.ts +4 -0
  43. package/dist/core/config/branch-template.d.ts +20 -0
  44. package/dist/core/config/config-items.d.ts +86 -0
  45. package/dist/core/config/config-load.d.ts +47 -0
  46. package/dist/core/config/config-origins.d.ts +57 -0
  47. package/dist/core/config/config-readers.d.ts +167 -0
  48. package/dist/core/config/config-schema.d.ts +207 -0
  49. package/dist/core/config/config-sections.d.ts +493 -0
  50. package/dist/core/config/config.d.ts +320 -0
  51. package/dist/core/config/core-section.d.ts +39 -0
  52. package/dist/core/config/dangerous.d.ts +58 -0
  53. package/dist/core/config/default-routing.d.ts +16 -0
  54. package/dist/core/config/index.d.ts +141 -0
  55. package/dist/core/config/scope.d.ts +42 -0
  56. package/dist/core/config/section.d.ts +206 -0
  57. package/dist/core/config/sections.d.ts +45 -0
  58. package/dist/core/config/standard-schema.d.ts +27 -0
  59. package/dist/core/git/commit.d.ts +232 -0
  60. package/dist/core/git/gh-runner.d.ts +24 -0
  61. package/dist/core/git/git.d.ts +22 -0
  62. package/dist/core/git/index.d.ts +85 -0
  63. package/dist/core/loop/claude.d.ts +388 -0
  64. package/dist/core/loop/hop-record.d.ts +52 -0
  65. package/dist/core/loop/index.d.ts +179 -0
  66. package/dist/core/loop/session-env.d.ts +20 -0
  67. package/dist/core/loop/session-record-parse.d.ts +63 -0
  68. package/dist/core/loop/sessions.d.ts +408 -0
  69. package/dist/core/loop/suite-failure.d.ts +25 -0
  70. package/dist/core/output/active.d.ts +100 -0
  71. package/dist/core/output/stream.d.ts +20 -0
  72. package/dist/core/output/text.d.ts +55 -0
  73. package/dist/core/plan/block-reader.d.ts +46 -0
  74. package/dist/core/plan/blocks.d.ts +139 -0
  75. package/dist/core/plan/declaration.d.ts +366 -0
  76. package/dist/core/plan/plan-stamp.d.ts +75 -0
  77. package/dist/core/runtime/identity.d.ts +21 -0
  78. package/dist/core/runtime/index.d.ts +46 -0
  79. package/dist/effort/store/bring-forward.d.ts +131 -0
  80. package/dist/effort/store/check-rows.d.ts +31 -0
  81. package/dist/effort/store/commit-stats.d.ts +42 -0
  82. package/dist/effort/store/copy.d.ts +35 -0
  83. package/dist/effort/store/development-build.d.ts +105 -0
  84. package/dist/effort/store/effort-levels.d.ts +13 -0
  85. package/dist/effort/store/findings.d.ts +214 -0
  86. package/dist/effort/store/fix-schema.d.ts +184 -0
  87. package/dist/effort/store/index.js +10 -7
  88. package/dist/effort/store/location.d.ts +44 -0
  89. package/dist/effort/store/merge-commit-gaps.d.ts +68 -0
  90. package/dist/effort/store/merge-conflicts.d.ts +91 -0
  91. package/dist/effort/store/merge-rules.d.ts +82 -0
  92. package/dist/effort/store/merge-store.d.ts +211 -0
  93. package/dist/effort/store/merge-union.d.ts +92 -0
  94. package/dist/effort/store/migrate.d.ts +146 -0
  95. package/dist/effort/store/migrations.d.ts +92 -0
  96. package/dist/effort/store/ndjson-files.d.ts +114 -0
  97. package/dist/effort/store/origins.d.ts +116 -0
  98. package/dist/effort/store/plan-stubs.d.ts +79 -0
  99. package/dist/effort/store/rebuild-aside.d.ts +172 -0
  100. package/dist/effort/store/schema-plan.d.ts +102 -0
  101. package/dist/effort/store/session-row-types.d.ts +64 -0
  102. package/dist/effort/store/settings.d.ts +13 -0
  103. package/dist/effort/store/sqlite.d.ts +340 -0
  104. package/dist/effort/store/store-generation.d.ts +17 -0
  105. package/dist/effort/store/store-identity.d.ts +220 -0
  106. package/dist/effort/store/store-meta.d.ts +305 -0
  107. package/dist/effort/store/store-replaced.d.ts +72 -0
  108. package/dist/effort/store/types.d.ts +223 -0
  109. package/dist/errors/codes.d.ts +56 -0
  110. package/dist/errors/rafa-codes.d.ts +36 -0
  111. package/dist/index-2a7nb3an.js +125 -0
  112. package/dist/{index-57mmzkcn.js → index-39kbcw68.js} +516 -217
  113. package/dist/{index-nv34ptht.js → index-77ajfffn.js} +14 -9
  114. package/dist/index-cy98y87p.js +2597 -0
  115. package/dist/index-gqz4jn6j.js +13447 -0
  116. package/dist/index-gvek4r8m.js +710 -0
  117. package/dist/index-j4k2vdz3.js +1125 -0
  118. package/dist/index-jh1r4kcn.js +1067 -0
  119. package/dist/index-ykggr043.js +540 -0
  120. package/dist/index-z1w83f81.js +4 -0
  121. package/dist/index.js +16091 -22798
  122. package/dist/learning/index.js +5 -6
  123. package/dist/plan/index.js +12 -11
  124. package/dist/plan-prompt.md +11 -2
  125. package/dist/ports/index.d.ts +732 -0
  126. package/dist/ports/learning.d.ts +101 -0
  127. package/dist/ports/log-level.d.ts +13 -0
  128. package/dist/ports/spec-review.d.ts +66 -0
  129. package/dist/pr/checks.d.ts +133 -0
  130. package/dist/pr/types.d.ts +422 -0
  131. package/dist/ready-4f8edezt.js +2016 -0
  132. package/dist/release-templates/release-bot-fragment.yml +153 -0
  133. package/dist/release-templates/release-publish.yml +110 -0
  134. package/dist/release-templates/release-settle.yml +122 -0
  135. package/dist/release-templates/release-tag.yml +102 -0
  136. package/dist/report/parse.d.ts +415 -0
  137. package/dist/schema/project-id.d.ts +79 -0
  138. package/dist/schema/tiers.d.ts +107 -0
  139. package/package.json +42 -4
  140. package/dist/index-6q30dtgm.js +0 -6342
  141. package/dist/index-cknayrpc.js +0 -2414
  142. package/dist/index-hf2k497d.js +0 -16
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The `text` Output adapter: a command's output as lines a person reads.
3
+ *
4
+ * Copied from `createTextOutput` in open-tomato's
5
+ * `packages/shared/cli-core/src/output.ts` at commit
6
+ * `18f94c843fff1bce65ed2245f8712ed9b64a2d51` (2026-06-25), and retyped
7
+ * against rafa's `Output` port, which `packages/rafa/src/ports/index.ts` copies from
8
+ * the same file's `CliOutput`.
9
+ *
10
+ * ## What the copy changes
11
+ *
12
+ * `info` is written at every verbosity, with no prefix. The source writes
13
+ * it at verbosity 1 and above, as `info: <message>`. The loop prints its
14
+ * progress lines at every verbosity, so under the source's threshold
15
+ * each would vanish at verbosity 0, and under its prefix each one left
16
+ * would change. `info(message)` writes `message`
17
+ * and a newline, the bytes `console.log(message)` writes for a single
18
+ * string: `text.test.ts` holds the two equal in a child process, over
19
+ * messages holding `%s`, `%%`, `%d`, `%o`, `%c` and `%j`, none of which
20
+ * `console.log` formats when it is handed one string.
21
+ *
22
+ * The rendering is otherwise the source's. `debug` is written at
23
+ * verbosity 2 and above as `debug: <message>`, `warn` and `error` at
24
+ * every verbosity as `warn: ` and `error: `, and `result` as `result: `
25
+ * followed by a string payload as it is or any other payload as JSON.
26
+ * `emit` renders a `log` event through the function of its level, a
27
+ * named `event` as nothing, and each other kind on a line of its own. The stream's `write` is a
28
+ * property, as `stream.ts` says, and the output answered is frozen.
29
+ *
30
+ * ## One stream
31
+ *
32
+ * Every line goes to the one stream the adapter is made with, `warn` and
33
+ * `error` included, as in the source. A `console.warn` or `console.error`
34
+ * line goes to stderr and carries no prefix, so the same message written
35
+ * through `warn` or `error` differs from it in both. `emit` also writes a
36
+ * line for a `start` event and a `result` event, `start: <command>` and
37
+ * `result: ok`, whatever the verbosity.
38
+ */
39
+ import type { OutputStream } from './stream.js';
40
+ import type { Output } from '../../ports/index.js';
41
+ /** What {@link createTextOutput} makes an adapter with. */
42
+ export interface CreateTextOutputOptions {
43
+ /** How much is written: `debug` lines from 2, every other line at any verbosity. */
44
+ verbosity: number;
45
+ /** Where every line is written. */
46
+ stream: OutputStream;
47
+ }
48
+ /**
49
+ * Makes a `text` Output writing each line to `stream`.
50
+ *
51
+ * The output answered is frozen, so no caller replaces one of its
52
+ * functions on an output `packages/rafa/src/core/output/active.ts` may be handing
53
+ * every module of the process.
54
+ */
55
+ export declare function createTextOutput({ verbosity, stream }: CreateTextOutputOptions): Output;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The barrel over the block reader: every name `blocks.ts` exports, and
3
+ * nothing else.
4
+ *
5
+ * A file outside `packages/rafa/src/core/` imports this file and never the one behind
6
+ * it (`scripts/survey/core-barrels.ts` measures that, and
7
+ * `src/tests/core-barrels.sweep.test.ts` holds it against the tree). A
8
+ * file inside `packages/rafa/src/core/` imports `blocks.ts` directly and never this
9
+ * file.
10
+ *
11
+ * ## Why one file has a barrel of its own
12
+ *
13
+ * `packages/rafa/src/core/plan/` has two barrels, both listed under `CORE_BARRELS` in
14
+ * `scripts/survey/core-moves.ts`. `index.ts` holds the plan parser and
15
+ * the plan-file helpers. This one holds `blocks.ts` alone, because
16
+ * `index.ts` cannot take it.
17
+ *
18
+ * A barrel that groups a file with a file outside `packages/rafa/src/core/` that
19
+ * imports the barrel back by value is a runtime cycle.
20
+ * `src/utils/tracker.ts` imports `readRafaBlocks` by value, and
21
+ * `parse.ts`, behind `index.ts`, imported that file by value when the
22
+ * split was made. So `blocks.ts` behind `index.ts` closed a cycle
23
+ * through the tracker. `blocks.ts` imports no file, so a barrel that
24
+ * holds it alone is imported back by nothing it reaches.
25
+ *
26
+ * `parse.ts` now reads the blocker comment from `blocker-comment.ts`,
27
+ * and no file of `packages/rafa/src/core/` imports the tracker, so that cycle is
28
+ * gone. The split stands as it was made.
29
+ *
30
+ * `index.ts` records the measurement: six cycles before the move and
31
+ * the same six after it, and a seventh in the control that puts all
32
+ * three files behind one barrel.
33
+ *
34
+ * ## What it exports
35
+ *
36
+ * Each name is spelled, types apart from values, as the other core
37
+ * barrels spell theirs. `src/plan/index.ts`, the `./plan` entry,
38
+ * re-exports all six, so this barrel is exactly as wide as the published
39
+ * surface it feeds.
40
+ *
41
+ * `block-reader.test.ts` holds the list to the file: every name
42
+ * `blocks.ts` exports is here once, as the same binding, no other name
43
+ * is here, and no name here is on the other plan barrel.
44
+ */
45
+ export type { LineSpan, RafaBlock, RafaBlockKind } from './blocks.js';
46
+ export { isRafaBlockKind, RAFA_BLOCK_KINDS, readRafaBlocks } from './blocks.js';
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Reading fenced `rafa:*` blocks out of a markdown document.
3
+ *
4
+ * A plan stays markdown a person can read, and the parts the loop acts
5
+ * on are fenced code blocks whose info string opens with `rafa:`:
6
+ *
7
+ * ````markdown
8
+ * ```rafa:plan
9
+ * stub: my-feature
10
+ * ```
11
+ * ````
12
+ *
13
+ * The rest of that first word is the block's KIND, `plan` above. This
14
+ * module answers every such block in source order with its kind, its
15
+ * body and the lines it spans, and nothing more: which kinds to act on,
16
+ * and what a body means, belong to whoever reads the document.
17
+ * {@link RAFA_BLOCK_KINDS} names the four kinds this phase defines.
18
+ *
19
+ * ## Fences are read the way a renderer shows them
20
+ *
21
+ * The checklist grammar is line-anchored and fence-blind: `findNextTask`
22
+ * dispatches a `- [ ] ` at column 0 inside a code fence, skipping one
23
+ * only inside a closed block this module answers. This reader is
24
+ * deliberately the opposite. A document
25
+ * that ILLUSTRATES the format puts its example inside a longer fence of
26
+ * its own, as the one above does, and a reader taking every line that
27
+ * opens with a `rafa:` fence for a block would hand the illustration to
28
+ * every task. So every fence is tracked, whatever its info string, and
29
+ * a `rafa:` line inside one is body text.
30
+ *
31
+ * The fence rules are CommonMark's, from its fenced code block section:
32
+ *
33
+ * - An opening fence is three or more backticks or tildes after at
34
+ * most three spaces. Four spaces, or a tab anywhere before it, and
35
+ * the line opens no fence; nor does a fence that does not open its
36
+ * line, which is prose quoting the syntax.
37
+ * - A backtick fence's info string holds no backtick, so a line such
38
+ * as ` ```rafa:plan``` ` is an inline code span and opens nothing.
39
+ * - A closing fence is the SAME character, at least as many of it,
40
+ * after at most three spaces, with nothing after it but spaces and
41
+ * tabs. It carries no info string, so ` ```ts ` inside a block is
42
+ * body; a block that holds a fence of its own opens with a longer
43
+ * one, as the illustration above does.
44
+ * - A fence never closed runs to the end of the document. The block
45
+ * is still answered, with {@link RafaBlock.closed} false: a session
46
+ * cut off mid-report leaves exactly that, and only the caller can
47
+ * judge whether a truncated body is usable.
48
+ * - Each body line loses up to as many leading spaces as the opening
49
+ * fence carried.
50
+ *
51
+ * What is NOT parsed is container structure. Measured against micromark
52
+ * (the CommonMark parser `@eslint/markdown` lints this repo's prose
53
+ * with), that costs three shapes: a fence inside a block quote, and one
54
+ * inside a list item indented four columns or more, are fences to a
55
+ * renderer and not here; a fence inside a multi-line HTML comment is
56
+ * raw HTML to a renderer and a block here. Outside those shapes the two
57
+ * agree: with every info string in 499 markdown files (this repo's, its
58
+ * sibling's and a skill library, on 2026-09-11) rewritten to open with
59
+ * `rafa:`, they answered the same kind, body and span for all 2,177
60
+ * fences. A plan puts its blocks at the top level.
61
+ *
62
+ * ## Lines
63
+ *
64
+ * A line is what `split('\n')` yields, the split `utils/tracker.ts`
65
+ * reads a tracker with, less one trailing carriage return, so a CRLF
66
+ * document reads exactly like its LF twin. A final newline ends the
67
+ * last line rather than opening an empty one. A LONE carriage return
68
+ * breaks no line here, where CommonMark breaks one on it; that is the
69
+ * fourth shape the measurement above found.
70
+ *
71
+ * {@link LineSpan} numbers lines from ONE, fences included, the way an
72
+ * editor and `grep -n` number them, so `sed -n '<first>,<last>p'`
73
+ * prints a block whole. `TaskInfo.lineNum` counts from ZERO: the line
74
+ * at index `i` of that split is line `i + 1` here.
75
+ *
76
+ * ## Unknown kinds
77
+ *
78
+ * A `rafa:` fence whose kind is none of {@link RAFA_BLOCK_KINDS} is
79
+ * answered like any other, never refused and never dropped. That is the
80
+ * rule `core/plan/declaration.ts` applies to an unrecognised key, for the
81
+ * same reason: a plan written for a later phase names kinds this one has
82
+ * never heard of, and refusing them would make every new kind a
83
+ * breaking change for older installs.
84
+ *
85
+ * The kind is the first word of the info string less its `rafa:`
86
+ * prefix, taken as written: `rafa:` alone answers the empty kind, and
87
+ * `rafa:Plan` answers `Plan`, which is unknown. The prefix is matched
88
+ * case-sensitively, as the declaration keys are, so `RAFA:plan` is an
89
+ * ordinary code block and no rafa block at all.
90
+ *
91
+ * Nothing here throws. A document with no block — empty, prose only, or
92
+ * fenced only in other languages — answers an empty list.
93
+ */
94
+ /**
95
+ * The kinds this phase defines. `plan`, `context` and `stage-context`
96
+ * are a plan's; `report` ends a task session's final message.
97
+ *
98
+ * Each document's reader acts on its own kinds and ignores the rest.
99
+ * {@link readRafaBlocks} answers every kind; see the module note.
100
+ */
101
+ export declare const RAFA_BLOCK_KINDS: readonly ["plan", "context", "stage-context", "report"];
102
+ /** One of the four kinds this phase defines. */
103
+ export type RafaBlockKind = (typeof RAFA_BLOCK_KINDS)[number];
104
+ /** The lines one block covers: from one, inclusive, fences included. */
105
+ export interface LineSpan {
106
+ /** The opening fence's line. */
107
+ readonly first: number;
108
+ /**
109
+ * The closing fence's line, or the document's last line when the
110
+ * block was never closed.
111
+ */
112
+ readonly last: number;
113
+ }
114
+ /** One fenced `rafa:*` block, as the document wrote it. */
115
+ export interface RafaBlock {
116
+ /** The info string's first word less `rafa:`, as written. */
117
+ readonly kind: string;
118
+ /**
119
+ * Every line between the fences, each less the opening fence's
120
+ * indentation, joined with `\n` and carrying no final newline. Empty
121
+ * for a block with no line between its fences.
122
+ */
123
+ readonly body: string;
124
+ /** Where the block sits in the document. */
125
+ readonly span: LineSpan;
126
+ /** False when the document ended before a closing fence. */
127
+ readonly closed: boolean;
128
+ }
129
+ /** True when `kind` is one of the four kinds this phase defines. */
130
+ export declare function isRafaBlockKind(kind: string): kind is RafaBlockKind;
131
+ /**
132
+ * Every `rafa:*` block in a markdown document, in source order.
133
+ *
134
+ * Takes any string and answers a new list, empty when the document
135
+ * holds no rafa block. An unknown kind is answered like a known one and
136
+ * an unclosed block is answered with `closed` false; see the module
137
+ * note for what counts as a fence and how lines are numbered.
138
+ */
139
+ export declare function readRafaBlocks(markdown: string): readonly RafaBlock[];
@@ -0,0 +1,366 @@
1
+ /**
2
+ * Reading a task's routing declaration off the end of its own line.
3
+ *
4
+ * A plan's task lines may carry a trailing brace block naming what the
5
+ * loop should dispatch the task WITH:
6
+ *
7
+ * ```text
8
+ * - [ ] Update the skill's cap sentence {agent=doc-updater effort=low}
9
+ * ```
10
+ *
11
+ * The block is the planner's half of a decision the loop otherwise has
12
+ * no way to make. A task carrying none is dispatched exactly as every
13
+ * task was before this module existed — one model, one effort, every
14
+ * tool — so a prose task and a schema migration cost the same, and
15
+ * the only place that knows they should not is the document that
16
+ * wrote them. `start/dispatch.ts` reads the block off each task on its
17
+ * way to the prompt, which is what makes a declared task the one that
18
+ * runs differently.
19
+ *
20
+ * Two parsers were never told about the block and both had to keep
21
+ * working unchanged, which is what shapes the grammar. `findNextTask`
22
+ * matches `^- \[ \] (.+)` and trims the capture, so the block arrives
23
+ * inside `taskInfo.task` and would otherwise reach the injected prompt
24
+ * verbatim; `updateTrackerLine` rewrites the checkbox prefix and
25
+ * nothing before a blocker comment, so the block survives a tick
26
+ * byte-identical. Neither needed a change for the block — the
27
+ * stripping happens here, at the point the text is used. Both halves
28
+ * are driven over real trackers in `tests/tracker-declarations.test.ts`,
29
+ * which is where that claim is measured rather than stated.
30
+ *
31
+ * A blocker comment is the one thing a line may trail after the block,
32
+ * and it comes off in `findNextTask` rather than here
33
+ * (`utils/tracker.ts`). It has to come off first: the block is anchored
34
+ * at end of text, so with the comment still on it would read as task
35
+ * text.
36
+ *
37
+ * The strip rule reaches every `rafa:*` block too, but not from here.
38
+ * A declaration sits ON a task line, so the text alone says where it
39
+ * starts; a block sits AROUND lines, and only a reader holding the
40
+ * document can tell that a line is its body. `findNextTask` is that
41
+ * reader, and every site quoting a task back quotes what it answered,
42
+ * so a task line inside a closed block is skipped there and reaches
43
+ * neither this module nor the prompt, the log or the commit.
44
+ *
45
+ * Four rules keep a brace that a task WROTE ABOUT from being read as a
46
+ * declaration, and each one is a shape that occurs in this repo's own
47
+ * plans:
48
+ *
49
+ * - The block is anchored at END OF TEXT. A task naming `{}` or a
50
+ * JSON literal mid-sentence has something after it.
51
+ * - It carries NO NESTED BRACES, so a task ending on a code span
52
+ * holding an object literal does not match.
53
+ * - It holds AT LEAST ONE RECOGNISED key, which is the rule the
54
+ * scoped grammar turns on. `{ ... }` prose, an empty block and a
55
+ * block of keys nobody here answers to are all task text.
56
+ * - It is not the WHOLE text. A line that is only a declaration
57
+ * leaves no task to dispatch, and stripping it would hand the loop
58
+ * an empty prompt — the one failure that reads as the loop having
59
+ * silently lost the task.
60
+ *
61
+ * When none of that holds the answer is today's behaviour exactly:
62
+ * `text` is the input string unchanged and `declaration` is null.
63
+ * Nothing is stripped and no flag is resolved.
64
+ *
65
+ * Priority inside a block is duplicated ON PURPOSE. With `agent`
66
+ * present the loop never passes `--model` or `--tools`, because an
67
+ * agent definition carries its own model and tool set and a flag beside
68
+ * it would be two authorities for one decision. `--effort` joins
69
+ * `--agent` unless the agent's definition declares an effort of its
70
+ * own, which the caller answers through an {@link AgentEffortLookup}
71
+ * (`utils/agent-definition.ts` reads it off the definition's
72
+ * frontmatter): effort is the cost lever a plan most needs to reach the
73
+ * session, and a definition silent on it leaves the plan's level the
74
+ * only one there is. `budget` is outranked by nothing: an agent
75
+ * definition supplies no budget, so `--max-budget-usd` joins whatever
76
+ * else the block resolved to, agent or none. Every key is still PARSED
77
+ * and still sits on the record whether or not it became a flag, and
78
+ * {@link ResolvedFlags.suppressed} names exactly the granular ones an
79
+ * agent took.
80
+ *
81
+ * `skills`, `tests` and `docs` are the three recognised keys that map to
82
+ * no flag under any block. There is no CLI flag for a skill, and what a
83
+ * plan names there is a hint for whoever reads the dispatch record rather
84
+ * than something the spawn can pass on. `tests` is read by the RUNNER,
85
+ * not the session: it names how much of the suite the loop runs after
86
+ * the task commits (`affected`, `module` or `full`, {@link TEST_SCOPES}),
87
+ * and {@link readTestScope} answers it with the `affected` default when
88
+ * the block is absent or its value unusable. `docs` marks the task's ROLE
89
+ * in its plan: its one value, `repair` ({@link DOCS_ROLES}), names the
90
+ * closing task that repairs the docs of what the plan changed, and
91
+ * {@link readDocsRole} answers it, with null for every other task. All
92
+ * three are recognised all the same, because that is what makes
93
+ * `{skills=bun-testing}`, `{tests=full}` or `{docs=repair}` a declaration
94
+ * on its own and what gets a misspelled value into
95
+ * {@link TaskDeclaration.issues} instead of through to the agent's prompt
96
+ * as task text. None is ever named in {@link ResolvedFlags.suppressed},
97
+ * which says what an AGENT outranked, and nothing outranks a key that
98
+ * was never a flag.
99
+ *
100
+ * Nothing here throws. A recognised key whose value this module cannot
101
+ * use lands in {@link TaskDeclaration.issues} and maps to no flag, so
102
+ * the task runs at the loop's defaults instead of stalling on a CLI
103
+ * that refuses `--effort medum`. An unrecognised KEY is not an issue at
104
+ * all: it is retained in {@link TaskDeclaration.extras} for telemetry,
105
+ * which is what lets a grammar grow without every older plan going red.
106
+ *
107
+ * `EFFORT_LEVELS` and `EffortLevel` are core's
108
+ * (`packages/rafa/src/effort/store/effort-levels.ts`), where the store's dispatch writer
109
+ * types a declared effort; this file re-exports them under the same names.
110
+ */
111
+ import type { EffortLevel } from '../../effort/store/effort-levels.js';
112
+ export type { EffortLevel } from '../../effort/store/effort-levels.js';
113
+ export { EFFORT_LEVELS } from '../../effort/store/effort-levels.js';
114
+ /**
115
+ * Keys this module answers to, in the order flags are emitted. `budget`
116
+ * sits ahead of `tools` because `--tools` is variadic and has to end the
117
+ * argument list (`core/loop/claude.ts`). `skills`, `tests` and `docs` come
118
+ * last because they emit no flag at all: each is recognised so that its value
119
+ * is PARSED and lands on the record, and a value the parser cannot use
120
+ * lands in {@link TaskDeclaration.issues} rather than passing silently.
121
+ */
122
+ export declare const DECLARATION_KEYS: readonly ["agent", "model", "effort", "budget", "tools", "skills", "tests", "docs"];
123
+ /** One of the keys the grammar recognises. */
124
+ export type DeclarationKey = (typeof DECLARATION_KEYS)[number];
125
+ /**
126
+ * The granular keys, each mapping to a flag of its own with no agent, and
127
+ * each one an agent can outrank. `budget` maps to a flag of its own too,
128
+ * and is not one of them: nothing outranks it.
129
+ */
130
+ export declare const GRANULAR_KEYS: readonly ["model", "effort", "tools"];
131
+ /** A key that maps to a flag of its own when no agent is named. */
132
+ export type GranularKey = (typeof GRANULAR_KEYS)[number];
133
+ /**
134
+ * The granular keys an `agent` outranks whenever it is named, its
135
+ * definition supplying a model and a tool set. `effort` is outranked
136
+ * only by a definition declaring one ({@link resolveDeclarationFlags}).
137
+ */
138
+ export declare const AGENT_OWNED_KEYS: readonly ["model", "tools"];
139
+ /**
140
+ * Model aliases the CLI documents, which is what a plan should spell.
141
+ *
142
+ * A FULL model name is accepted as well ({@link FULL_MODEL_NAME}) —
143
+ * `--model` takes either — but an alias is what survives a model
144
+ * generation changing underneath the plan.
145
+ */
146
+ export declare const MODEL_ALIASES: readonly ["opus", "sonnet", "haiku", "fable"];
147
+ /**
148
+ * How much of the suite the runner runs after a task commits, as a
149
+ * `tests=` value spells it: `affected` runs the test files the task's
150
+ * diff reaches, `module` the tests of every module the diff touches,
151
+ * and `full` the whole suite. Ordered narrowest first.
152
+ */
153
+ export declare const TEST_SCOPES: readonly ["affected", "module", "full"];
154
+ /** One of the three scopes a `tests=` value names. */
155
+ export type TestScope = (typeof TEST_SCOPES)[number];
156
+ /**
157
+ * The scope a task runs under when it declares none, or declares one
158
+ * {@link isTestScope} refuses: the narrowest, since a task that needs
159
+ * more is the one its plan marks.
160
+ */
161
+ export declare const DEFAULT_TEST_SCOPE: TestScope;
162
+ /**
163
+ * The roles a `docs=` value names. There is one: `repair` marks the task
164
+ * that repairs the docs of what its plan changed. A task declaring none
165
+ * has no docs role, so the key has no default value.
166
+ */
167
+ export declare const DOCS_ROLES: readonly ["repair"];
168
+ /** The one role a `docs=` value names. */
169
+ export type DocsRole = (typeof DOCS_ROLES)[number];
170
+ /**
171
+ * True when `value` is an agent name this module passes on: a bare
172
+ * file stem, so no name can reach a path outside `.claude/agents/`.
173
+ */
174
+ export declare function isAgentName(value: string): boolean;
175
+ /** The flag a declared budget is passed as, in the CLI's own spelling. */
176
+ export declare const BUDGET_FLAG = "--max-budget-usd";
177
+ /**
178
+ * Reads a `budget=` value as US dollars, or answers null when it is not
179
+ * one this module passes on: a shape {@link BUDGET_VALUE} refuses, or
180
+ * zero, which no session could run under.
181
+ */
182
+ export declare function parseBudgetUsd(value: string): number | null;
183
+ /** One `key=value` pair, exactly as it was written. */
184
+ export interface DeclarationEntry {
185
+ /** Key as written. Matched case-sensitively against the grammar. */
186
+ key: string;
187
+ /** Value as written, with no unescaping of any kind. */
188
+ value: string;
189
+ }
190
+ /** Why a token could not be used. */
191
+ export type DeclarationIssueReason =
192
+ /** A recognised key appearing twice. The first occurrence wins. */
193
+ 'duplicate-key'
194
+ /** A recognised key whose value this module cannot pass on. */
195
+ | 'unusable-value'
196
+ /** A token inside the block carrying no `=` at all. */
197
+ | 'stray-token';
198
+ /** One thing the block said that did not become a flag. */
199
+ export interface DeclarationIssue {
200
+ reason: DeclarationIssueReason;
201
+ /** Key it concerns, or the empty string for a stray token. */
202
+ key: string;
203
+ /** The token as written, so a log line can quote it. */
204
+ text: string;
205
+ }
206
+ /** What one block declared. */
207
+ export interface TaskDeclaration {
208
+ /** The block as it appeared, braces included. */
209
+ raw: string;
210
+ /** Every `key=value` token, in source order, recognised or not. */
211
+ entries: readonly DeclarationEntry[];
212
+ /** Entries whose key is none of {@link DECLARATION_KEYS}. */
213
+ extras: readonly DeclarationEntry[];
214
+ /** What was dropped, and why. Empty on a clean block. */
215
+ issues: readonly DeclarationIssue[];
216
+ /** Agent to dispatch under, or null. */
217
+ agent: string | null;
218
+ /** Model alias or full name, or null. */
219
+ model: string | null;
220
+ /** Effort level, or null. */
221
+ effort: EffortLevel | null;
222
+ /** Budget in US dollars, above zero, or null. */
223
+ budget: number | null;
224
+ /** Tool names, deduped in first-seen order, or null. */
225
+ tools: readonly string[] | null;
226
+ /**
227
+ * Skill names, deduped in first-seen order, or null. Maps to no flag:
228
+ * the key is recognised so a plan's skill hint is parsed and kept,
229
+ * and a reader takes it off the record rather than off the CLI.
230
+ */
231
+ skills: readonly string[] | null;
232
+ /**
233
+ * Test scope the runner runs after the task commits, or null when the
234
+ * block names none or names one outside {@link TEST_SCOPES}. Maps to
235
+ * no flag; {@link readTestScope} answers the default for null.
236
+ */
237
+ tests: TestScope | null;
238
+ /**
239
+ * The task's docs role, or null when the block names none or names one
240
+ * outside {@link DOCS_ROLES}. Maps to no flag; {@link readDocsRole}
241
+ * answers it.
242
+ */
243
+ docs: DocsRole | null;
244
+ }
245
+ /** A task text split from the declaration it carried. */
246
+ export interface ParsedTask {
247
+ /**
248
+ * The text the loop should dispatch. The input unchanged when no
249
+ * declaration was found, and the text before the block otherwise.
250
+ */
251
+ text: string;
252
+ /** What the block declared, or null when there was no block. */
253
+ declaration: TaskDeclaration | null;
254
+ }
255
+ /** What a declaration resolves to on the command line. */
256
+ export interface ResolvedFlags {
257
+ /** CLI arguments, in {@link DECLARATION_KEYS} order. */
258
+ args: readonly string[];
259
+ /**
260
+ * Keys present on the record that deliberately map to no flag.
261
+ * Non-empty only when an `agent` outranked them.
262
+ */
263
+ suppressed: readonly GranularKey[];
264
+ }
265
+ /** True when `value` is one of the five effort levels. */
266
+ export declare function isEffortLevel(value: string): value is EffortLevel;
267
+ /** True when `value` is one of the three {@link TEST_SCOPES}. */
268
+ export declare function isTestScope(value: string): value is TestScope;
269
+ /**
270
+ * The test scope a task's declaration yields: the one it declared, or
271
+ * {@link DEFAULT_TEST_SCOPE} when there is no block, the block carries
272
+ * no `tests=`, or its value was unusable (and so recorded in
273
+ * {@link TaskDeclaration.issues} rather than on the record).
274
+ */
275
+ export declare function readTestScope(declaration: TaskDeclaration | null): TestScope;
276
+ /** True when `value` is one of the {@link DOCS_ROLES}. */
277
+ export declare function isDocsRole(value: string): value is DocsRole;
278
+ /**
279
+ * The docs role a task's declaration yields: the one it declared, or null
280
+ * when there is no block, the block carries no `docs=`, or its value was
281
+ * unusable (and so recorded in {@link TaskDeclaration.issues} rather than
282
+ * on the record).
283
+ */
284
+ export declare function readDocsRole(declaration: TaskDeclaration | null): DocsRole | null;
285
+ /**
286
+ * True when `value` is a model this module will pass on: one of the
287
+ * documented aliases, or a full name spelled the way the CLI's own
288
+ * help spells one.
289
+ */
290
+ export declare function isModelValue(value: string): boolean;
291
+ /**
292
+ * Splits a `tools=` value into names, or answers null when it is not
293
+ * usable.
294
+ *
295
+ * Membership is deliberately NOT checked against a list of tool names.
296
+ * The built-in set lives in the CLI and moves with it, so a list kept
297
+ * here would eventually refuse a tool that exists — a failure that
298
+ * reads as a broken declaration rather than as a stale constant. The
299
+ * SHAPE is checked instead, which catches the mistakes a plan actually
300
+ * makes: an empty value, a doubled comma, a stray quote.
301
+ */
302
+ export declare function parseToolList(value: string): readonly string[] | null;
303
+ /**
304
+ * Splits a `skills=` value into names, or answers null when it is not
305
+ * usable.
306
+ *
307
+ * Membership is NOT checked here against the skills installed, for the
308
+ * reason {@link parseToolList} gives: the set moves with the machine,
309
+ * and this reads one declaration and no tier. `agents/roster.ts` checks
310
+ * it where the tiers are read, and `rafa plan validate` and `loop
311
+ * start`'s preflight refuse a name no loaded tier resolves. The SHAPE is
312
+ * checked here instead — a bare directory
313
+ * stem, so no name reaches a path outside `.claude/skills/` — which
314
+ * catches an empty value, a doubled comma and a stray quote.
315
+ */
316
+ export declare function parseSkillList(value: string): readonly string[] | null;
317
+ /**
318
+ * Splits a task's text from the declaration block it may carry.
319
+ *
320
+ * Takes the TEXT of a task rather than a whole tracker line, which is
321
+ * what `findNextTask` hands its caller. The anchor is therefore end of
322
+ * text, and a trailing space is tolerated because the tracker's own
323
+ * spelling puts two before the block.
324
+ */
325
+ export declare function parseTaskDeclaration(taskText: string): ParsedTask;
326
+ /** The task text alone, for a caller with no use for the record. */
327
+ export declare function stripTaskDeclaration(taskText: string): string;
328
+ /**
329
+ * Answers whether the named agent's definition declares an effort of
330
+ * its own. The loop's answer comes from the definition's frontmatter
331
+ * (`utils/agent-definition.ts`); a test hands in whatever its case is
332
+ * about.
333
+ */
334
+ export type AgentEffortLookup = (agent: string) => boolean;
335
+ /**
336
+ * Maps a declaration onto the flags the loop spawns Claude with.
337
+ *
338
+ * An `agent` outranks `model` and `tools` whenever it is named: the
339
+ * agent definition already names a model and a tool set, and passing
340
+ * a flag beside it would leave two authorities for one decision with
341
+ * no way to tell which won. `effort` is the exception. It joins
342
+ * `--agent` as `--effort` unless `agentDeclaresEffort` answers that
343
+ * the named agent's definition declares an effort of its own, because
344
+ * a definition silent on effort leaves the plan's level the only one
345
+ * there is. The lookup is asked only about a block carrying both keys,
346
+ * so every other block resolves without reading a definition. A name
347
+ * no definition answers for is the lookup's `false` and still passes
348
+ * `--effort`, leaving the CLI to refuse the name before any model
349
+ * call.
350
+ *
351
+ * Granular keys an agent outranked are named in
352
+ * {@link ResolvedFlags.suppressed} rather than dropped, which is what
353
+ * lets the dispatch say what it left to the agent. `skills`, `tests`
354
+ * and `docs` are not among them: they map to no flag under any block,
355
+ * so no agent ever took them.
356
+ *
357
+ * `budget` passes {@link BUDGET_FLAG} whatever else the block holds, an
358
+ * agent included, since a definition supplies no budget, and is never
359
+ * named as suppressed. It goes after `--effort` and ahead of `--tools`,
360
+ * whose variadic value has to end the list.
361
+ *
362
+ * A key whose value did not parse is absent from the record and so
363
+ * emits nothing here — the task runs at the loop's defaults, which is
364
+ * the failure worth having.
365
+ */
366
+ export declare function resolveDeclarationFlags(declaration: TaskDeclaration | null, agentDeclaresEffort: AgentEffortLookup): ResolvedFlags;