@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,67 @@
1
+ /**
2
+ * The line prompter: the one prompt that reads whole lines, not keys as
3
+ * `src/cli/prompt/`'s do, so it needs no raw mode and works on a pipe as
4
+ * well as on a terminal. Every yes-or-no question rafa asks, and the root
5
+ * `rafa init` reads, goes through a {@link Prompter}; the commands take
6
+ * one from {@link createLinePrompter} over `process.stdin` and
7
+ * `process.stderr`, and tests pass a scripted one.
8
+ *
9
+ * ## The prompter
10
+ *
11
+ * {@link createLinePrompter} reads lines from a stream with `readline`'s
12
+ * `line` and `close` events, and writes each question and each thing it
13
+ * says to another stream. It does not use `readline/promises`: measured
14
+ * on bun 1.3.14, a `question` pending when its input ends never settles,
15
+ * where the `close` event fires. The final line of an input that ends
16
+ * without a line break is still delivered as a line, measured the same
17
+ * way. readline is given no output and `terminal: false`, so on a
18
+ * terminal the terminal's own line discipline echoes and edits what is
19
+ * typed.
20
+ *
21
+ * Two line prompters over one input both receive every line, so a
22
+ * command handing a question to another closes its own first.
23
+ *
24
+ * ## The lazy prompter
25
+ *
26
+ * {@link lazyPrompter} wraps the opening of a prompter so a run that
27
+ * never asks opens nothing: the prompter is opened at the first
28
+ * question, kept for the next, and closed by `close`, after which the
29
+ * next question opens a fresh one. Its `ask` answers whether the line
30
+ * typed means yes: `y` or `yes`, trimmed and in any case, the reading
31
+ * every `[y/N]` question in rafa makes. Anything else declines, the empty
32
+ * answer and the null of an input that ended included. `issue ready`,
33
+ * `next`, `cleanup` and `plan create`'s three offers ask through it.
34
+ */
35
+ import type { Readable } from 'node:stream';
36
+ /** Asks an operator, and tells them what went wrong. */
37
+ export interface Prompter {
38
+ /** Writes `text` and a line break. */
39
+ readonly say: (text: string) => void;
40
+ /** Writes `question` and answers the next line typed, or null once the input has ended. */
41
+ readonly ask: (question: string) => Promise<string | null>;
42
+ /** Stops reading the input. */
43
+ readonly close: () => void;
44
+ }
45
+ /** A prompter opened at its first question, see the module note. */
46
+ export interface LazyPrompter {
47
+ /** Opens the prompter if it is not open, asks `question` and answers whether the line typed means yes. */
48
+ readonly ask: (question: string) => Promise<boolean>;
49
+ /** Closes the prompter if one is open; the next question opens another. */
50
+ readonly close: () => void;
51
+ }
52
+ /** A stream the prompter writes to: `process.stderr`, or a test's own. */
53
+ export interface PromptOutput {
54
+ readonly write: (chunk: string) => unknown;
55
+ }
56
+ /**
57
+ * A {@link Prompter} reading lines from `input` and writing to `output`;
58
+ * see the module note. A line typed before it is asked for is kept for
59
+ * the next question.
60
+ */
61
+ export declare function createLinePrompter(input: Readable, output: PromptOutput): Prompter;
62
+ /**
63
+ * A {@link LazyPrompter} over `open`, which is called at the first
64
+ * question and again at the first one after each `close`; see the
65
+ * module note.
66
+ */
67
+ export declare function lazyPrompter(open: () => Prompter): LazyPrompter;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * The barrel over the command contract: every name `command.ts`,
3
+ * `confirm.ts`, `lazy.ts`, `registry-types.ts`, `running.ts`,
4
+ * `spends.ts`, `types.ts` and `version.ts` export, and nothing else.
5
+ *
6
+ * A file outside `packages/rafa/src/core/` imports this file and never the eight
7
+ * behind it (`scripts/survey/core-barrels.ts` measures that, and
8
+ * `src/tests/core-barrels.sweep.test.ts` holds it against the tree). A
9
+ * file inside `packages/rafa/src/core/` imports the eight directly and never this
10
+ * file.
11
+ *
12
+ * ## What sits behind it
13
+ *
14
+ * - `command.ts`: `RafaCommand`, the `RafaContext` it runs with,
15
+ * `CommandExit`, the routing words and the shape check;
16
+ * - `confirm.ts`: the `Prompter` every yes-or-no question goes
17
+ * through, the line prompter that reads one from a stream, and the
18
+ * lazy prompter that opens one only to ask;
19
+ * - `lazy.ts`: `CommandDeclaration`, a command less its `run`, and
20
+ * `lazyCommand`, the command over a declaration whose `run` imports
21
+ * the command's module and delegates to its default export;
22
+ * - `registry-types.ts`: the contract of the dispatcher's registry,
23
+ * `CommandRegistry` and the five shapes it names, and
24
+ * `CommandFeature`, what one feature contributes to it, whose
25
+ * builder stays in `src/cli/registry.ts`;
26
+ * - `running.ts`: the command this process is running with its parsed
27
+ * flags, recorded by the dispatcher and read by the spend guard;
28
+ * - `spends.ts`: a command's `spends` declaration, `CommandSpend`, its
29
+ * shape check and the mark help ends a spender's roster line with;
30
+ * - `types.ts`: the contracts copied from open-tomato's `cli-core`,
31
+ * `CliContext`, `CliCommand`, `ArgSpec` and `FlagSpec`, which
32
+ * `RafaCommand` extends;
33
+ * - `version.ts`: the version the running build answers with, and the
34
+ * line that spells it.
35
+ *
36
+ * The eight hold no name in common, so each name is exported once and
37
+ * from the file that declares it.
38
+ *
39
+ * ## Why one barrel can hold all eight
40
+ *
41
+ * A barrel that groups a file with a file outside `packages/rafa/src/core/` that
42
+ * imports the barrel back by value is a runtime cycle. None of the eight
43
+ * imports a file outside `packages/rafa/src/core/` by value. `command.ts` takes
44
+ * `spendsProblem` from `spends.ts` beside it, and both take
45
+ * `describeValue` from `packages/rafa/src/core/config/config-sections.ts`.
46
+ * `lazy.ts` takes the shape check, `CommandExit` and `commandSpelling`
47
+ * from `command.ts`, and `messageOf` from the same config file; the
48
+ * module a lazy command loads is the caller's `load()`, imported when
49
+ * the command runs, never by `lazy.ts` itself.
50
+ * `confirm.ts` and `running.ts` import `node:` modules alone by value,
51
+ * `running.ts` naming `command.ts` and `types.ts` with `import type`.
52
+ * `version.ts` imports the root `package.json`, and `types.ts` nothing
53
+ * by value: it names `packages/rafa/src/core/config/config-sections.ts` and the
54
+ * `Output` port with `import type`. `registry-types.ts` imports nothing
55
+ * by value either: it names `command.ts` with `import type`, and this
56
+ * file exports its names with `export type`, so loading the barrel
57
+ * never loads it.
58
+ *
59
+ * `command.ts` also names `src/project/scope.ts` with `import type`,
60
+ * which is erased before anything loads; it named `src/cli/registry.ts`
61
+ * the same way until the registry's contract moved into
62
+ * `registry-types.ts`. `src/cli/registry.ts` imports this barrel by
63
+ * value, for the shape check and the routing words, so that edge is the
64
+ * one a value import in `command.ts` would turn into a cycle.
65
+ *
66
+ * Measured when the three moved: the value imports of the non-test files
67
+ * under `src/` and `packages/` held six cycles before the move and the
68
+ * same six after it, none through a file of this folder. The control,
69
+ * the same tree with `command.ts` taking one value (`HELP_WORD`) from
70
+ * `src/cli/registry.ts`, held a seventh: `src/cli/registry.ts`,
71
+ * `command.ts` and this file.
72
+ *
73
+ * Measured again when `types.ts` moved in, over every strongly connected
74
+ * set of the same value imports: five cycles at the base and five after
75
+ * the move, none through a file of this folder or of `src/cli/core/`.
76
+ * The same `HELP_WORD` control held a sixth, `src/cli/registry.ts`,
77
+ * `command.ts` and this file.
78
+ *
79
+ * Measured again when `spends.ts` moved in, the same way: five cycles at
80
+ * the base and five after the move, none through a file of this folder
81
+ * or of `src/cli/`. The same `HELP_WORD` control held a sixth,
82
+ * `src/cli/registry.ts`, `command.ts` and this file.
83
+ *
84
+ * Measured again when the registry's contract moved into
85
+ * `registry-types.ts`, the same way: five cycles at the base and five
86
+ * after the move, none through a file of this folder or of `src/cli/`.
87
+ * The same `HELP_WORD` control held a sixth, `src/cli/registry.ts`,
88
+ * `command.ts` and this file.
89
+ *
90
+ * ## What it exports
91
+ *
92
+ * Each name is spelled, types apart from values, as the config barrels
93
+ * spell theirs. No name here is part of the published surface:
94
+ * `src/index.ts` re-exports nothing from this file.
95
+ *
96
+ * `index.test.ts` holds the list to the eight files: every name any of
97
+ * them exports is here once, as the same binding, and no other name is
98
+ * here.
99
+ */
100
+ export type { CommandDeprecation, CommandExample, CommandOutput, FlagDeprecation, RafaCommand, RafaContext, RafaFlagSpec, } from './command.js';
101
+ export type { LazyPrompter, PromptOutput, Prompter } from './confirm.js';
102
+ export type { CommandDeclaration } from './lazy.js';
103
+ export type { CommandAlias, CommandFeature, CommandRegistry, CommandRegistryInput, ModuleMount, RosterOptions, SubjectSpec, } from './registry-types.js';
104
+ export type { RunningCommand } from './running.js';
105
+ export type { CommandSpend, SpendsWhen } from './spends.js';
106
+ export type { ArgSpec, CliCommand, CliContext, FlagSpec, } from './types.js';
107
+ export { COMMAND_OUTPUTS, CommandExit, commandProblem, commandSpelling, isRoutingWord, isTopLevel, } from './command.js';
108
+ export { createLinePrompter, lazyPrompter } from './confirm.js';
109
+ export { lazyCommand } from './lazy.js';
110
+ export { restoreRunningCommand, runAsRunningCommand, runningCommand, setRunningCommand, } from './running.js';
111
+ export { SPENDS_GLYPH, SPENDS_WHEN, spendsCondition, spendsMark, spendsProblem, } from './spends.js';
112
+ export { RAFA_VERSION, versionLine } from './version.js';
@@ -0,0 +1,54 @@
1
+ /**
2
+ * A command whose module is imported when it runs, not when the roster
3
+ * is built: {@link lazyCommand} over a {@link CommandDeclaration}.
4
+ *
5
+ * ## What is declared and what is loaded
6
+ *
7
+ * A declaration is a `RafaCommand` less its `run`: every field routing,
8
+ * help, `describe` and the spend guard read, so a roster of lazy
9
+ * commands is built, routed and rendered without importing one command
10
+ * module. The lazy command is the declaration spread under a `run` of
11
+ * its own, so it passes `commandProblem` (`command.ts` beside this
12
+ * file) as the declaration's fields stand.
13
+ *
14
+ * Its `run` awaits `load()`, typically `() => import('./x.js')`, and
15
+ * holds the module's default export to a command: one `commandProblem`
16
+ * accepts, whose subject and action are the declaration's. It then hands
17
+ * the context to that command's `run` and settles as that `run`
18
+ * settles, a `CommandExit` it throws included. The loaded command's
19
+ * other fields are not compared with the declaration's: the dispatcher
20
+ * routed, parsed and guarded by the declaration's before `run` was
21
+ * called.
22
+ *
23
+ * ## Refusing
24
+ *
25
+ * A load that rejects, a default export `commandProblem` refuses, and a
26
+ * default export spelled as another command each refuse with
27
+ * `CommandExit(1, message)`, the message reading
28
+ * `feature "<feature>": command "<spelling>" failed to load: <reason>`.
29
+ * The spelling is `commandSpelling`'s, so a top-level command reads as
30
+ * its one word, as every other refusal naming a command spells it. The
31
+ * reason is the rejection's message, the shape check's sentence, or the
32
+ * spelling the export has against the one declared.
33
+ *
34
+ * Nothing is cached here: a second run calls `load()` again, and the
35
+ * module loader's own cache answers it.
36
+ */
37
+ import type { RafaCommand } from './command.js';
38
+ /** A command less its `run`: what a feature declares for a command it loads when run. */
39
+ export type CommandDeclaration = Omit<RafaCommand, 'run'>;
40
+ /** What a command module's import resolves to: the command is its default export. */
41
+ type CommandModule = {
42
+ readonly default?: unknown;
43
+ } | null | undefined;
44
+ /**
45
+ * A command spreading `declaration` under a `run` that loads the
46
+ * command and delegates to it. See the module note.
47
+ *
48
+ * @param declaration - Every field of the command but its `run`.
49
+ * @param load - Imports the command's module, whose default export is the command.
50
+ * @param feature - The name of the feature the command belongs to, which a refusal names.
51
+ * @returns The lazy command.
52
+ */
53
+ export declare function lazyCommand(declaration: CommandDeclaration, load: () => Promise<CommandModule>, feature: string): RafaCommand;
54
+ export {};
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The registry's contract: the {@link CommandRegistry} the dispatcher
3
+ * routes through, what it is built from, the subjects, aliases and
4
+ * mounts it answers, and the {@link CommandFeature} one feature hands
5
+ * the registry its subjects and commands in.
6
+ *
7
+ * The registry itself, `createCommandRegistry` and the refusals it
8
+ * throws, stays the CLI's, in `src/cli/registry.ts`, which imports
9
+ * these names from here and re-exports them under the same names. Core
10
+ * holds the contract alone, so `RafaContext` can name the registry a
11
+ * command runs with without importing a file above core.
12
+ *
13
+ * Every name here is a type: the file imports `RafaCommand` from
14
+ * `command.ts` beside it with `import type`, and nothing by value.
15
+ */
16
+ import type { RafaCommand } from './command.js';
17
+ /** A subject: the first routing word, and what its roster line says. */
18
+ export interface SubjectSpec {
19
+ /** The routing word, singular: `issue`. */
20
+ readonly name: string;
21
+ /** One line for the root roster. */
22
+ readonly summary: string;
23
+ }
24
+ /** The commands one module's entry exports, mounted under `module/<name>`. */
25
+ export interface ModuleMount {
26
+ /** The word typed after an `exec` action: `linear` in `rafa module exec linear next`. */
27
+ readonly name: string;
28
+ /** The file the commands were imported from. */
29
+ readonly entry: string;
30
+ /** The commands, each routed by its action. */
31
+ readonly commands: readonly RafaCommand[];
32
+ }
33
+ /** One alias: the words it is typed as, the command it routes to, and whether it is kept for good. */
34
+ export interface CommandAlias {
35
+ readonly words: readonly string[];
36
+ readonly command: RafaCommand;
37
+ /** True for one of the command's `lastingAliases`, which prints no deprecation line. */
38
+ readonly lasting: boolean;
39
+ }
40
+ /** Whether a roster keeps hidden commands. */
41
+ export interface RosterOptions {
42
+ /** Keeps the commands declaring `hidden`. Off by default. */
43
+ readonly includeHidden?: boolean;
44
+ }
45
+ /** What a registry is built from. */
46
+ export interface CommandRegistryInput {
47
+ /** The subjects, in roster order. */
48
+ readonly subjects: readonly SubjectSpec[];
49
+ /** The core commands, in roster order. */
50
+ readonly commands: readonly RafaCommand[];
51
+ }
52
+ /**
53
+ * What one feature contributes to a registry: its name, the subjects it
54
+ * declares and the commands under them, each list in roster order. The
55
+ * same two lists as a {@link CommandRegistryInput}, under the name that
56
+ * says which feature they came from.
57
+ */
58
+ export interface CommandFeature {
59
+ /** The feature's name, which a load failure of one of its commands names. */
60
+ readonly name: string;
61
+ /** The subjects the feature declares, in roster order. */
62
+ readonly subjects: readonly SubjectSpec[];
63
+ /** The feature's commands, in roster order. */
64
+ readonly commands: readonly RafaCommand[];
65
+ }
66
+ /** The subjects, commands, aliases and mounts the dispatcher routes through. Never changes once made. */
67
+ export interface CommandRegistry {
68
+ /** The subjects, in the order declared. */
69
+ readonly subjects: () => readonly SubjectSpec[];
70
+ /** The subject a word names, as its name or its plural, or undefined. */
71
+ readonly subjectOf: (word: string) => SubjectSpec | undefined;
72
+ /** The core commands in the order declared, top-level ones included. */
73
+ readonly commands: (options?: RosterOptions) => readonly RafaCommand[];
74
+ /**
75
+ * The commands under a subject's name, or under a mount's key
76
+ * (`module/<name>`), in the order declared. Empty for a name holding
77
+ * none, a top-level command's included.
78
+ */
79
+ readonly actionsOf: (subject: string, options?: RosterOptions) => readonly RafaCommand[];
80
+ /** The command under a subject's name or a mount's key, and an action, or undefined. */
81
+ readonly find: (subject: string, action: string) => RafaCommand | undefined;
82
+ /** The top-level command a word names, or undefined. */
83
+ readonly topLevel: (word: string) => RafaCommand | undefined;
84
+ /** Every alias, longest first, in the order declared among equals. */
85
+ readonly aliases: () => readonly CommandAlias[];
86
+ /** The mounts, in the order mounted. */
87
+ readonly mounts: () => readonly ModuleMount[];
88
+ /** The mount a module name names, or undefined. */
89
+ readonly mountOf: (name: string) => ModuleMount | undefined;
90
+ /**
91
+ * A new registry holding this one's mounts followed by `mount`, leaving
92
+ * this one unchanged. Throws, answering no registry, on a mount the
93
+ * module note refuses.
94
+ */
95
+ readonly mount: (mount: ModuleMount) => CommandRegistry;
96
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The command this process is running, and the flags its line was parsed
3
+ * into.
4
+ *
5
+ * A reader deep below a command's `run` — the spend guard in
6
+ * `packages/rafa/src/core/loop/claude.ts` — asks {@link runningCommand} which command it
7
+ * runs under, without a context threaded down to it. The record is held
8
+ * in two places, and the reader answers the first that has one.
9
+ *
10
+ * ## The scope
11
+ *
12
+ * {@link runAsRunningCommand} runs a function with a record in an async
13
+ * scope of its own, an `AsyncLocalStorage` (`node:async_hooks`). Inside
14
+ * that function, and in everything it awaits or starts, the reader
15
+ * answers that record; once the function ends, however it ends, nothing
16
+ * of it is left. The dispatcher runs every command this way
17
+ * (`src/cli/dispatch.ts`), so nothing it records outlives the
18
+ * invocation.
19
+ *
20
+ * A scope belongs to one run, so runs need not nest to stay apart. Two
21
+ * runs that overlap in one process, started together and awaited with
22
+ * `Promise.all`, each read their own record whichever ends first. A run
23
+ * started inside another reads the inner record inside and the outer one
24
+ * after. The module-level value below keeps overlapping runs apart only
25
+ * while they nest: set and put back in a `finally` it is a stack, and
26
+ * two that end out of order leave the record of one of them for the
27
+ * rest of the process (#927).
28
+ *
29
+ * ## The module-level value
30
+ *
31
+ * {@link setRunningCommand} records a command with its parsed flags at
32
+ * module level and answers the record it replaced, null when nothing was
33
+ * recorded. {@link restoreRunningCommand} puts back a record
34
+ * {@link setRunningCommand} answered, so a caller that sets one restores
35
+ * it in a `finally`. Outside every scope the reader answers this value,
36
+ * or null. No module but a test sets it: a case stands in for a dispatch
37
+ * with it, as `src/tests/core-coupled/core/loop/claude.test.ts` does ahead of a door.
38
+ *
39
+ * ## A set made inside a scope
40
+ *
41
+ * The reader prefers the scope, and the setters write the module-level
42
+ * value alone, inside a scope as outside one. So a set made inside a
43
+ * scope changes nothing that scope reads, answers the module-level
44
+ * record it replaced, and is what the reader answers once the scope has
45
+ * ended. A scope's record cannot be replaced from inside it.
46
+ *
47
+ * ## What reads outside the scope
48
+ *
49
+ * A scope follows the awaits and the callbacks started inside it. A
50
+ * callback the runtime calls from elsewhere does not carry it. Measured
51
+ * under bun 1.3.14, three listeners registered inside a scope read no
52
+ * store: a `process.on` signal listener when the signal arrived, an
53
+ * abort listener that handler fired, and an abort listener an
54
+ * `AbortSignal.timeout` fired. A timer, a child process's `data`
55
+ * listener, a `readline` `line` listener and the code after an await
56
+ * such a handler settled all read the scope's. A reader outside the
57
+ * scope gets the module-level value; one that needs the scope's is
58
+ * wrapped with `AsyncResource.bind` where it is registered.
59
+ *
60
+ * The flags are copied and frozen when recorded, so a caller changing
61
+ * its own flags object afterwards changes nothing recorded.
62
+ *
63
+ * Bun runs every test file in one process, and the module-level value is
64
+ * module state: a case that sets a record restores the one it replaced
65
+ * after it.
66
+ */
67
+ import type { RafaCommand } from './command.js';
68
+ import type { CliContext } from './types.js';
69
+ /** A running command and the flags its line was parsed into. */
70
+ export interface RunningCommand {
71
+ /** The command running. */
72
+ readonly command: RafaCommand;
73
+ /** Its parsed flags by name, as the context carries them. */
74
+ readonly flags: CliContext['flags'];
75
+ }
76
+ /**
77
+ * Runs `run` with `command` and `flags` recorded as running in an async
78
+ * scope of its own, and answers what `run` answers. The record is read
79
+ * inside `run` and whatever it awaits, and nowhere after it; see the
80
+ * module note.
81
+ */
82
+ export declare function runAsRunningCommand<T>(command: RafaCommand, flags: CliContext['flags'], run: () => T): T;
83
+ /**
84
+ * Records `command` as running with `flags` at module level, and answers
85
+ * the module-level record it replaced for {@link restoreRunningCommand}.
86
+ * Inside a scope it changes nothing that scope reads; see the module
87
+ * note.
88
+ */
89
+ export declare function setRunningCommand(command: RafaCommand, flags: CliContext['flags']): RunningCommand | null;
90
+ /**
91
+ * The command running now with its flags: the record of the scope this
92
+ * is read in, else the module-level one, else null.
93
+ */
94
+ export declare function runningCommand(): RunningCommand | null;
95
+ /** Puts back `previous`, a module-level record {@link setRunningCommand} answered. */
96
+ export declare function restoreRunningCommand(previous: RunningCommand | null): void;
@@ -0,0 +1,39 @@
1
+ /** The mark help puts on a command that spends Claude usage. */
2
+ export declare const SPENDS_GLYPH = "\uD83E\uDE99";
3
+ /** The four values `when` takes. */
4
+ export declare const SPENDS_WHEN: readonly ["always", "with", "unless", "through"];
5
+ /** One of the four forms of a declaration. */
6
+ export type SpendsWhen = (typeof SPENDS_WHEN)[number];
7
+ /** What a command spends, and when; see the module note. */
8
+ export type CommandSpend = {
9
+ readonly when: 'always';
10
+ readonly what: string;
11
+ } | {
12
+ readonly when: 'with';
13
+ readonly flag: string;
14
+ readonly what: string;
15
+ } | {
16
+ readonly when: 'unless';
17
+ readonly flag: string;
18
+ readonly what: string;
19
+ } | {
20
+ readonly when: 'through';
21
+ readonly what: string;
22
+ };
23
+ /**
24
+ * What keeps `value` from being a `spends` declaration, as a sentence
25
+ * naming the field, or null when nothing does. Absent passes: a command
26
+ * that declares nothing spends nothing.
27
+ */
28
+ export declare function spendsProblem(value: unknown): string | null;
29
+ /**
30
+ * The condition a declaration spends under, as help prints it:
31
+ * `with --resolve`, `unless --no-model`, or null for `always` and
32
+ * `through`.
33
+ */
34
+ export declare function spendsCondition(spend: CommandSpend): string | null;
35
+ /**
36
+ * The mark ending a spender's roster line: `🪙`, `🪙 with --resolve` or
37
+ * `🪙 unless --no-model`.
38
+ */
39
+ export declare function spendsMark(spend: CommandSpend): string;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The contracts a rafa command is written against: the context it runs
3
+ * with, the positional arguments and flags it declares, and the command.
4
+ *
5
+ * Copied from open-tomato's `packages/shared/cli-core/src/types.ts` at
6
+ * commit `6c774b81fc62489f31748c47e251f11448e3904a` (2026-06-25). The
7
+ * package is not on the public registry, so rafa copies it rather than
8
+ * depending on it, as it copies `parseArgs.ts` and `assembleContext.ts`,
9
+ * which stay in `src/cli/core/`, from the same package.
10
+ *
11
+ * The copy sat beside those two until it moved into `packages/rafa/src/core/command/`,
12
+ * so core's `command.ts` reads it without importing the CLI.
13
+ * `packages/rafa/src/core/command/index.ts` exports its names to every
14
+ * file outside core; the re-export `src/cli/core/types.ts` kept for the
15
+ * CLI was deleted when core moved into its package (#1090).
16
+ *
17
+ * ## What the copy changes
18
+ *
19
+ * `CliContext.output` is rafa's `Output` port, which `packages/rafa/src/ports/index.ts`
20
+ * copies from the source's `CliOutput`. The source imports that type from
21
+ * its own `output.ts`, whose two renderers rafa ported to
22
+ * `packages/rafa/src/core/output/text.ts` and `src/adapters/output/json.ts`. Every member is otherwise the source's, with a
23
+ * TSDoc line added to each.
24
+ *
25
+ * `ArgSpec` and `FlagSpec` are still two names for one shape. The source
26
+ * declares `default` and `aliases` on both and reads neither. The copy's
27
+ * `parseArgs` reads a flag's `default` and `aliases` and an argument's
28
+ * `default`, and refuses an argument that declares `aliases`, as its
29
+ * module note says.
30
+ */
31
+ import type { Output } from '../../ports/index.js';
32
+ import type { OutputMode } from '../config/config-sections.js';
33
+ /** Everything a command runs with, built and frozen by `assembleContext`. */
34
+ export interface CliContext {
35
+ /** The positional words of the line, in order. */
36
+ args: readonly string[];
37
+ /** The flags of the line by name: a value, `true` for a bare flag, `false` for `--no-<name>`. */
38
+ flags: Readonly<Record<string, string | boolean>>;
39
+ /** How the command's output is rendered: lines a person reads, NDJSON events, or one line per loop event. */
40
+ outputMode: OutputMode;
41
+ /** How much the command writes, from 0, the least, to 3. */
42
+ verbosity: 0 | 1 | 2 | 3;
43
+ /** Where the command writes, rendered in its `outputMode`. */
44
+ output: Output;
45
+ /** Aborted when the command is asked to stop. */
46
+ signal: AbortSignal;
47
+ /** The environment the context was built from, as a frozen copy. */
48
+ env: Readonly<Record<string, string | undefined>>;
49
+ }
50
+ /** A positional argument a command declares. */
51
+ export interface ArgSpec {
52
+ /** What the argument is called. */
53
+ name: string;
54
+ /** What the argument holds, in a sentence. */
55
+ description: string;
56
+ /** The value's type. `parseArgs` converts no word to it: every word is a string. */
57
+ type: 'string' | 'boolean' | 'number';
58
+ /** Whether the command needs the argument. `parseArgs` refuses no line leaving it out. */
59
+ required?: boolean;
60
+ /** The word filled in when the line leaves the argument out, as a string. */
61
+ default?: string | boolean | number;
62
+ /** Refused by `parseArgs` when it names any: a positional word has no spelling but its place. */
63
+ aliases?: readonly string[];
64
+ }
65
+ /** A flag a command declares. */
66
+ export interface FlagSpec {
67
+ /** The flag's name, which `CliContext.flags` is keyed by: `plan` for `--plan`. */
68
+ name: string;
69
+ /** What the flag does, in a sentence. */
70
+ description: string;
71
+ /** The value's type. `parseArgs` converts no value to it: a value is a string. */
72
+ type: 'string' | 'boolean' | 'number';
73
+ /** Whether the command needs the flag. `parseArgs` refuses no line leaving it out. */
74
+ required?: boolean;
75
+ /** The value filled in when the line gives the flag none: a number as its string, anything else as it is. */
76
+ default?: string | boolean | number;
77
+ /** Other spellings of the flag, each read as its name: `p` reads `-p` and `--p` as `plan`. */
78
+ aliases?: readonly string[];
79
+ }
80
+ /** A command: what it declares, and what runs. */
81
+ export interface CliCommand {
82
+ /** The command's name. */
83
+ name: string;
84
+ /** What the command does. */
85
+ description: string;
86
+ /** The positional arguments, in the order the line gives them. */
87
+ args: ArgSpec[];
88
+ /** The flags. */
89
+ flags: FlagSpec[];
90
+ /** Runs the command with the context built for it. */
91
+ run: (context: CliContext) => Promise<void>;
92
+ }
@@ -0,0 +1,4 @@
1
+ /** The `version` of `package.json`, inlined into the build that reads it. */
2
+ export declare const RAFA_VERSION: string;
3
+ /** The line `rafa --version` prints and `rafa doctor` opens with: `rafa <version>`. */
4
+ export declare function versionLine(): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The glob matching every plan branch the template names: the template
3
+ * with `{stub}` replaced by `rafa-*`.
4
+ *
5
+ * @param template - A `naming.branch` template, such as `feat/{stub}`.
6
+ * @returns The glob, `feat/rafa-*` for the default template.
7
+ */
8
+ export declare function branchGlob(template: string): string;
9
+ /**
10
+ * The stub a branch carries under the template: what lies between the
11
+ * template's part before `{stub}` and its part after, when the branch
12
+ * opens with the one, closes with the other, and the stub between opens
13
+ * with `rafa-`.
14
+ *
15
+ * @param template - A `naming.branch` template, such as `feat/{stub}`.
16
+ * @param branch - A branch name, without `refs/heads/`.
17
+ * @returns The stub, `rafa-12-x` for `feat/rafa-12-x` under the default
18
+ * template; null for a branch the template does not name.
19
+ */
20
+ export declare function stubOfBranch(template: string, branch: string): string | null;