@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.
- package/README.md +280 -42
- package/dist/PROMPT.md +18 -1
- package/dist/adapters/logger/settings.d.ts +132 -0
- package/dist/bugs/closest.d.ts +19 -0
- package/dist/bugs/fenced.d.ts +8 -0
- package/dist/bugs/places.d.ts +20 -0
- package/dist/bugs/record.d.ts +145 -0
- package/dist/bugs/title.d.ts +83 -0
- package/dist/bundled/agents/doc-updater.md +17 -3
- package/dist/bundled/agents/loop-implementer.md +14 -2
- package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +10 -3
- package/dist/bundled/operators/skills/rafa-stretch-sweep/SKILL.md +5 -0
- package/dist/bundled/skills/dev-planner/SKILL.md +120 -24
- package/dist/bundled/skills/documentation/SKILL.md +2 -12
- package/dist/cli.js +72387 -59411
- package/dist/config/entry.d.ts +119 -0
- package/dist/config/index.d.ts +36 -0
- package/dist/config/index.js +51 -0
- package/dist/config/shape.d.ts +232 -0
- package/dist/config-registry.d.ts +127 -0
- package/dist/config-schema-board-project.d.ts +157 -0
- package/dist/config-schema-bugs.d.ts +71 -0
- package/dist/config-schema-errors.d.ts +54 -0
- package/dist/config-schema-features.d.ts +64 -0
- package/dist/config-schema-hub.d.ts +150 -0
- package/dist/config-schema-issues.d.ts +95 -0
- package/dist/config-schema-logger.d.ts +91 -0
- package/dist/config-schema-loop-continue.d.ts +122 -0
- package/dist/config-schema-loop-retries.d.ts +94 -0
- package/dist/config-schema-release.d.ts +307 -0
- package/dist/config-schema-tests.d.ts +170 -0
- package/dist/config-schema-triage.d.ts +142 -0
- package/dist/config-schema-wrap-up.d.ts +96 -0
- package/dist/core/command/command.d.ts +228 -0
- package/dist/core/command/confirm.d.ts +67 -0
- package/dist/core/command/index.d.ts +112 -0
- package/dist/core/command/lazy.d.ts +54 -0
- package/dist/core/command/registry-types.d.ts +96 -0
- package/dist/core/command/running.d.ts +96 -0
- package/dist/core/command/spends.d.ts +39 -0
- package/dist/core/command/types.d.ts +92 -0
- package/dist/core/command/version.d.ts +4 -0
- package/dist/core/config/branch-template.d.ts +20 -0
- package/dist/core/config/config-items.d.ts +86 -0
- package/dist/core/config/config-load.d.ts +47 -0
- package/dist/core/config/config-origins.d.ts +57 -0
- package/dist/core/config/config-readers.d.ts +167 -0
- package/dist/core/config/config-schema.d.ts +207 -0
- package/dist/core/config/config-sections.d.ts +493 -0
- package/dist/core/config/config.d.ts +320 -0
- package/dist/core/config/core-section.d.ts +39 -0
- package/dist/core/config/dangerous.d.ts +58 -0
- package/dist/core/config/default-routing.d.ts +16 -0
- package/dist/core/config/index.d.ts +141 -0
- package/dist/core/config/scope.d.ts +42 -0
- package/dist/core/config/section.d.ts +206 -0
- package/dist/core/config/sections.d.ts +45 -0
- package/dist/core/config/standard-schema.d.ts +27 -0
- package/dist/core/git/commit.d.ts +232 -0
- package/dist/core/git/gh-runner.d.ts +24 -0
- package/dist/core/git/git.d.ts +22 -0
- package/dist/core/git/index.d.ts +85 -0
- package/dist/core/loop/claude.d.ts +388 -0
- package/dist/core/loop/hop-record.d.ts +52 -0
- package/dist/core/loop/index.d.ts +179 -0
- package/dist/core/loop/session-env.d.ts +20 -0
- package/dist/core/loop/session-record-parse.d.ts +63 -0
- package/dist/core/loop/sessions.d.ts +408 -0
- package/dist/core/loop/suite-failure.d.ts +25 -0
- package/dist/core/output/active.d.ts +100 -0
- package/dist/core/output/stream.d.ts +20 -0
- package/dist/core/output/text.d.ts +55 -0
- package/dist/core/plan/block-reader.d.ts +46 -0
- package/dist/core/plan/blocks.d.ts +139 -0
- package/dist/core/plan/declaration.d.ts +366 -0
- package/dist/core/plan/plan-stamp.d.ts +75 -0
- package/dist/core/runtime/identity.d.ts +21 -0
- package/dist/core/runtime/index.d.ts +46 -0
- package/dist/effort/store/bring-forward.d.ts +131 -0
- package/dist/effort/store/check-rows.d.ts +31 -0
- package/dist/effort/store/commit-stats.d.ts +42 -0
- package/dist/effort/store/copy.d.ts +35 -0
- package/dist/effort/store/development-build.d.ts +105 -0
- package/dist/effort/store/effort-levels.d.ts +13 -0
- package/dist/effort/store/findings.d.ts +214 -0
- package/dist/effort/store/fix-schema.d.ts +184 -0
- package/dist/effort/store/index.js +10 -7
- package/dist/effort/store/location.d.ts +44 -0
- package/dist/effort/store/merge-commit-gaps.d.ts +68 -0
- package/dist/effort/store/merge-conflicts.d.ts +91 -0
- package/dist/effort/store/merge-rules.d.ts +82 -0
- package/dist/effort/store/merge-store.d.ts +211 -0
- package/dist/effort/store/merge-union.d.ts +92 -0
- package/dist/effort/store/migrate.d.ts +146 -0
- package/dist/effort/store/migrations.d.ts +92 -0
- package/dist/effort/store/ndjson-files.d.ts +114 -0
- package/dist/effort/store/origins.d.ts +116 -0
- package/dist/effort/store/plan-stubs.d.ts +79 -0
- package/dist/effort/store/rebuild-aside.d.ts +172 -0
- package/dist/effort/store/schema-plan.d.ts +102 -0
- package/dist/effort/store/session-row-types.d.ts +64 -0
- package/dist/effort/store/settings.d.ts +13 -0
- package/dist/effort/store/sqlite.d.ts +340 -0
- package/dist/effort/store/store-generation.d.ts +17 -0
- package/dist/effort/store/store-identity.d.ts +220 -0
- package/dist/effort/store/store-meta.d.ts +305 -0
- package/dist/effort/store/store-replaced.d.ts +72 -0
- package/dist/effort/store/types.d.ts +223 -0
- package/dist/errors/codes.d.ts +56 -0
- package/dist/errors/rafa-codes.d.ts +36 -0
- package/dist/index-2a7nb3an.js +125 -0
- package/dist/{index-57mmzkcn.js → index-39kbcw68.js} +516 -217
- package/dist/{index-nv34ptht.js → index-77ajfffn.js} +14 -9
- package/dist/index-cy98y87p.js +2597 -0
- package/dist/index-gqz4jn6j.js +13447 -0
- package/dist/index-gvek4r8m.js +710 -0
- package/dist/index-j4k2vdz3.js +1125 -0
- package/dist/index-jh1r4kcn.js +1067 -0
- package/dist/index-ykggr043.js +540 -0
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index.js +16091 -22798
- package/dist/learning/index.js +5 -6
- package/dist/plan/index.js +12 -11
- package/dist/plan-prompt.md +11 -2
- package/dist/ports/index.d.ts +732 -0
- package/dist/ports/learning.d.ts +101 -0
- package/dist/ports/log-level.d.ts +13 -0
- package/dist/ports/spec-review.d.ts +66 -0
- package/dist/pr/checks.d.ts +133 -0
- package/dist/pr/types.d.ts +422 -0
- package/dist/ready-4f8edezt.js +2016 -0
- package/dist/release-templates/release-bot-fragment.yml +153 -0
- package/dist/release-templates/release-publish.yml +110 -0
- package/dist/release-templates/release-settle.yml +122 -0
- package/dist/release-templates/release-tag.yml +102 -0
- package/dist/report/parse.d.ts +415 -0
- package/dist/schema/project-id.d.ts +79 -0
- package/dist/schema/tiers.d.ts +107 -0
- package/package.json +42 -4
- package/dist/index-6q30dtgm.js +0 -6342
- package/dist/index-cknayrpc.js +0 -2414
- package/dist/index-hf2k497d.js +0 -16
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The task report: the `rafa:report` block a task session ends its final
|
|
3
|
+
* message with, read out of the session's captured output.
|
|
4
|
+
*
|
|
5
|
+
* `runClaudeCaptured` answers a session's stdout, which under `claude -p`
|
|
6
|
+
* is the session's final message. The message ends with one fenced block
|
|
7
|
+
* the loop acts on:
|
|
8
|
+
*
|
|
9
|
+
* ````markdown
|
|
10
|
+
* ```rafa:report
|
|
11
|
+
* status: done
|
|
12
|
+
* feedback: |
|
|
13
|
+
* What was done and how it went, one block.
|
|
14
|
+
* findings:
|
|
15
|
+
* - trigger: "when running bun test under a fresh worktree"
|
|
16
|
+
* kind: gotcha
|
|
17
|
+
* what: "node_modules is absent after fork"
|
|
18
|
+
* cause: "worktree creation does not run bun install"
|
|
19
|
+
* resolution: "run bun install before the first test"
|
|
20
|
+
* artifact: "Cannot find package"
|
|
21
|
+
* signal: loud
|
|
22
|
+
* domain: testing
|
|
23
|
+
* skills_used: [git-workflow]
|
|
24
|
+
* blockers:
|
|
25
|
+
* - what: "LINEAR_API_KEY unset"
|
|
26
|
+
* artifact: "401 Unauthorized"
|
|
27
|
+
* out_of_scope_bugs:
|
|
28
|
+
* - what: "..."
|
|
29
|
+
* artifact: "..."
|
|
30
|
+
* security: false
|
|
31
|
+
* kind: bug
|
|
32
|
+
* changes:
|
|
33
|
+
* - level: patch
|
|
34
|
+
* area: "loop"
|
|
35
|
+
* summary: "the loop waits for the running task's commit"
|
|
36
|
+
* stale_docs:
|
|
37
|
+
* - where: "context/triage.md:12"
|
|
38
|
+
* what: "says triage has three channels"
|
|
39
|
+
* ```
|
|
40
|
+
* ````
|
|
41
|
+
*
|
|
42
|
+
* {@link parseReport} is the whole interface. It takes any string, never
|
|
43
|
+
* throws, and answers either the report or an explicit record of why
|
|
44
|
+
* there is none. The prompt asks for the block and the loop has to live
|
|
45
|
+
* without it, so a missing report is an answer here, not an exception.
|
|
46
|
+
*
|
|
47
|
+
* ## Which block is read
|
|
48
|
+
*
|
|
49
|
+
* Blocks are found by `readRafaBlocks`, so fences follow its rules: a
|
|
50
|
+
* `rafa:report` fence that does not open its line is prose quoting the
|
|
51
|
+
* syntax, and a report illustrated inside a longer fence is that fence's
|
|
52
|
+
* body. Blocks of every other kind are ignored.
|
|
53
|
+
*
|
|
54
|
+
* Only the LAST `rafa:report` block counts. Anything earlier is a draft
|
|
55
|
+
* the final block replaces, or the session quoting the format while it
|
|
56
|
+
* works. When the last block cannot be read, no earlier one is read in
|
|
57
|
+
* its place: that would record a report the session had already replaced.
|
|
58
|
+
*
|
|
59
|
+
* ## Absence
|
|
60
|
+
*
|
|
61
|
+
* {@link ReportAbsent.reason} says why there is no report:
|
|
62
|
+
*
|
|
63
|
+
* - `no-block`: the output holds no `rafa:report` block.
|
|
64
|
+
* - `unclosed-block`: the last block is never closed. It is not read,
|
|
65
|
+
* because a body cut short can still be valid YAML, just with the
|
|
66
|
+
* later findings missing. Reading it would shorten the findings list
|
|
67
|
+
* without anyone noticing.
|
|
68
|
+
* - `malformed-block`: the last block's body is not valid YAML, or is
|
|
69
|
+
* not a mapping. An empty body is not a mapping either.
|
|
70
|
+
*
|
|
71
|
+
* Every reason but `no-block` carries the block, so its raw body reaches
|
|
72
|
+
* whoever records the absence.
|
|
73
|
+
*
|
|
74
|
+
* ## Fields
|
|
75
|
+
*
|
|
76
|
+
* A readable block answers a {@link TaskReport}, plus
|
|
77
|
+
* {@link ReportPresent.issues} for whatever it did not read as written.
|
|
78
|
+
* The rules are the ones `core/plan/parse.ts` applies to a plan header.
|
|
79
|
+
*
|
|
80
|
+
* - An UNKNOWN key, at the top level or inside an entry, is retained in
|
|
81
|
+
* that level's `extras` and acts on nothing. This is the rule
|
|
82
|
+
* `core/plan/declaration.ts` applies to an unrecognised key, for the same
|
|
83
|
+
* reason: a report written for a later phase must not break this one.
|
|
84
|
+
* - A known key that is absent, or set to null, says nothing. When the
|
|
85
|
+
* field is required, that is a `missing-field` issue.
|
|
86
|
+
* - A known key whose value is unusable is left null and reported as
|
|
87
|
+
* `unusable-field`. That covers a string field holding something
|
|
88
|
+
* other than a string or only whitespace, a closed-set field holding
|
|
89
|
+
* a value outside {@link REPORT_STATUSES}, {@link FINDING_KINDS},
|
|
90
|
+
* {@link FINDING_SIGNALS}, {@link FINDING_DOMAINS},
|
|
91
|
+
* {@link CHANGE_LEVELS} or {@link BUG_KINDS}, and a `security`
|
|
92
|
+
* flag that is not a boolean. The sets are closed because stored rows
|
|
93
|
+
* are keyed on them. A later phase promotes a `silent` finding on its
|
|
94
|
+
* first sighting, for example, so a signal spelled any other way must
|
|
95
|
+
* not reach the store looking meaningful.
|
|
96
|
+
* - A list field holding something other than a list reads as empty,
|
|
97
|
+
* and is reported. A list entry that is not a mapping, or a
|
|
98
|
+
* `skills_used` entry that is not a usable string, is dropped and
|
|
99
|
+
* reported under its index.
|
|
100
|
+
*
|
|
101
|
+
* Required fields: `status`; a finding's `trigger`, `kind`, `what` and
|
|
102
|
+
* `signal`; a blocker's `what`; an out-of-scope bug's `what` and
|
|
103
|
+
* `security`; a change note's `level` and `summary`; a stale doc's `what`.
|
|
104
|
+
* A missing `security` flag stays null rather than defaulting to false,
|
|
105
|
+
* because this parser does not get to decide that a bug has no security
|
|
106
|
+
* impact.
|
|
107
|
+
*
|
|
108
|
+
* ## An out-of-scope entry's kind
|
|
109
|
+
*
|
|
110
|
+
* An `out_of_scope_bugs` entry's `kind` says what sort of leftover it
|
|
111
|
+
* is, one of {@link BUG_KINDS}. It is a different field from a
|
|
112
|
+
* finding's `kind`, with its own list.
|
|
113
|
+
*
|
|
114
|
+
* It is the one closed-set field that never reads as null. The key was
|
|
115
|
+
* added after reports of three keys per entry were already being
|
|
116
|
+
* written, so an entry with no `kind` reads as `bug` with no issue: a
|
|
117
|
+
* session on an older prompt changes nothing. A value outside the list
|
|
118
|
+
* is reported as `unusable-field` and also reads as `bug`, the kind an
|
|
119
|
+
* entry had before the key existed.
|
|
120
|
+
*
|
|
121
|
+
* ## An out-of-scope entry's bug fields
|
|
122
|
+
*
|
|
123
|
+
* Four more keys of an `out_of_scope_bugs` entry carry what a bug record
|
|
124
|
+
* needs, each optional and read as a plain string, null when absent:
|
|
125
|
+
*
|
|
126
|
+
* - `module`: the part of the code the bug sits in.
|
|
127
|
+
* - `action`: what was being done when it showed.
|
|
128
|
+
* - `reproduce`: the command or steps that show it again.
|
|
129
|
+
* - `cause`: why it happens, as a cause code or in words.
|
|
130
|
+
*
|
|
131
|
+
* Whether `cause` is a code or a sentence is the cause rule's business,
|
|
132
|
+
* never this parser's: it is kept as written either way. A value holding
|
|
133
|
+
* `: ` has to be double-quoted, as every string value does. A report on
|
|
134
|
+
* the prompt before the keys existed, with none of them, reads with no
|
|
135
|
+
* issue.
|
|
136
|
+
*
|
|
137
|
+
* ## Change notes
|
|
138
|
+
*
|
|
139
|
+
* `changes` is the list of changelog lines a task returns for its own
|
|
140
|
+
* diff. A note's `summary` is one line a user of the project would
|
|
141
|
+
* understand, and its `area` is the short free-text heading the changelog
|
|
142
|
+
* groups it under. Its `level` says how much of a release the change is
|
|
143
|
+
* worth, one of {@link CHANGE_LEVELS}: a task whose diff a user would
|
|
144
|
+
* notice nothing of writes `level: none`, and a task with nothing to say
|
|
145
|
+
* at all omits the list. An absent list is exactly that — empty, with no
|
|
146
|
+
* issue — because a note the task did not write is not one to invent.
|
|
147
|
+
*
|
|
148
|
+
* {@link CHANGE_LEVELS} is spelled in the order the spec writes it and
|
|
149
|
+
* carries no ranking. Whatever computes a release level out of a plan's
|
|
150
|
+
* notes orders them itself; reading precedence off this list's indices
|
|
151
|
+
* would make `none` the highest of the four.
|
|
152
|
+
*
|
|
153
|
+
* ## Stale docs
|
|
154
|
+
*
|
|
155
|
+
* `stale_docs` is the list of documentation lines a task saw that no
|
|
156
|
+
* longer match the code and that its own change did not falsify. An
|
|
157
|
+
* entry's `where` is the file, with the line when known, as one string
|
|
158
|
+
* such as `context/triage.md:12`; its `what` says what no longer matches.
|
|
159
|
+
*
|
|
160
|
+
* This is the one list whose entry is dropped for a field: an entry with
|
|
161
|
+
* no usable `what` is reported and left out of {@link TaskReport.staleDocs},
|
|
162
|
+
* because a place with nothing said about it is not a row anyone can
|
|
163
|
+
* settle. An entry with no usable `where` is kept, with `where` null.
|
|
164
|
+
*
|
|
165
|
+
* The key is the eighth, added after reports of seven were already being
|
|
166
|
+
* written. A report with no `stale_docs` key reads as an empty list with
|
|
167
|
+
* no issue, like every other absent list, so a session on an older prompt
|
|
168
|
+
* is neither failed nor flagged for writing seven fields.
|
|
169
|
+
*
|
|
170
|
+
* Strings are kept exactly as written, never trimmed. `artifact` is the
|
|
171
|
+
* byte string a recurrence is matched on. A blank one is refused, because
|
|
172
|
+
* every blank artifact would dedupe into one finding.
|
|
173
|
+
*
|
|
174
|
+
* Nothing is deduped or summarised. Every usable entry is carried, in
|
|
175
|
+
* order, duplicates included. Deduping is the findings writer's job: by
|
|
176
|
+
* `artifact` when present, otherwise by exact `trigger` plus `what`.
|
|
177
|
+
*
|
|
178
|
+
* An issue's `field` is a path in the report's own key names
|
|
179
|
+
* (`findings[2].signal`, `skills_used[0]`), so it can be found in the
|
|
180
|
+
* block as written. Issues come in the order {@link TaskReport} lists its
|
|
181
|
+
* fields, with each entry's fields in the order its type lists them.
|
|
182
|
+
*
|
|
183
|
+
* ## The parser, measured on bun 1.3.14
|
|
184
|
+
*
|
|
185
|
+
* `Bun.YAML.parse` reads the body. A report is written by a model and
|
|
186
|
+
* quotes error messages, which is where these readings bite:
|
|
187
|
+
*
|
|
188
|
+
* - A `#` after a space opens a comment, so `artifact: error #42 here`
|
|
189
|
+
* reads as `"error"`. That loses bytes without an error and cannot be
|
|
190
|
+
* detected here. Only quoting prevents it.
|
|
191
|
+
* - An unquoted value that opens with a backtick, `@`, `%` or `[`, or
|
|
192
|
+
* that holds `: `, throws. One unquoted error message makes the whole
|
|
193
|
+
* block `malformed-block`, losing every finding in it.
|
|
194
|
+
* - The `SyntaxError` message names no line, so the block's `span` is
|
|
195
|
+
* the only location an absence can give.
|
|
196
|
+
* - `artifact: 404` and `artifact: 0042` read as numbers (the second as
|
|
197
|
+
* 42), and `.inf` as `Infinity`. A number is refused rather than
|
|
198
|
+
* turned back into a string that may differ from the one written.
|
|
199
|
+
* - `security: yes` reads as the string `"yes"`, which is refused, and
|
|
200
|
+
* `security: False` reads as the boolean false.
|
|
201
|
+
* - A `feedback: |` block keeps one trailing newline. Feedback is kept
|
|
202
|
+
* as parsed.
|
|
203
|
+
* - A body with a `---` separator reads as a LIST of documents, so it
|
|
204
|
+
* is `malformed-block`. A duplicated key keeps its last value and
|
|
205
|
+
* leaves no trace, so nothing here can report it.
|
|
206
|
+
* - A key spelled `__proto__` or `constructor` is an ordinary own key.
|
|
207
|
+
* Keys are matched against a list of names, never looked up by
|
|
208
|
+
* object index, so such a key lands in `extras`.
|
|
209
|
+
*/
|
|
210
|
+
import type { RafaBlock } from '../core/plan/block-reader.js';
|
|
211
|
+
/** What a session says of its own task. The loop's outcome is separate. */
|
|
212
|
+
export declare const REPORT_STATUSES: readonly ["done", "blocked"];
|
|
213
|
+
/** One of the two statuses a report can claim. */
|
|
214
|
+
export type ReportStatus = (typeof REPORT_STATUSES)[number];
|
|
215
|
+
/** What a finding is about. */
|
|
216
|
+
export declare const FINDING_KINDS: readonly ["gotcha", "pattern", "location", "skill-suggestion"];
|
|
217
|
+
/** One of the four finding kinds. */
|
|
218
|
+
export type FindingKind = (typeof FINDING_KINDS)[number];
|
|
219
|
+
/** Whether a finding surfaced as a failure, or passed while wrong. */
|
|
220
|
+
export declare const FINDING_SIGNALS: readonly ["loud", "silent"];
|
|
221
|
+
/** One of the two finding signals. */
|
|
222
|
+
export type FindingSignal = (typeof FINDING_SIGNALS)[number];
|
|
223
|
+
/**
|
|
224
|
+
* What area of work a finding's lesson belongs to. Optional on a
|
|
225
|
+
* finding; the lesson made from it defaults an absent one.
|
|
226
|
+
*
|
|
227
|
+
* continuous-learning-v2's domains: the five its `SKILL.md` names, and
|
|
228
|
+
* the three its `agents/observer.md` calls global-friendly, measured
|
|
229
|
+
* on 2026-09-18. Its `instinct-cli.py` defaults an absent domain to
|
|
230
|
+
* `general`, which is not one of these and is not accepted here; an
|
|
231
|
+
* importer meeting one has to map it.
|
|
232
|
+
*
|
|
233
|
+
* Declared here rather than in `schema/instinct.ts`, which re-exports it
|
|
234
|
+
* as `INSTINCT_DOMAINS`: that module already imports this one, and this
|
|
235
|
+
* one importing it back fails at load with `Cannot access
|
|
236
|
+
* 'FINDING_KINDS' before initialization`.
|
|
237
|
+
*/
|
|
238
|
+
export declare const FINDING_DOMAINS: readonly ["code-style", "testing", "git", "debugging", "workflow", "security", "general-best-practices"];
|
|
239
|
+
/** One of the finding domains. */
|
|
240
|
+
export type FindingDomain = (typeof FINDING_DOMAINS)[number];
|
|
241
|
+
/** How much of a release one change note is worth. */
|
|
242
|
+
export declare const CHANGE_LEVELS: readonly ["patch", "minor", "major", "none"];
|
|
243
|
+
/** One of the four change levels. */
|
|
244
|
+
export type ChangeLevel = (typeof CHANGE_LEVELS)[number];
|
|
245
|
+
/**
|
|
246
|
+
* What sort of leftover an `out_of_scope_bugs` entry is: `bug`, code
|
|
247
|
+
* doing the wrong thing that this plan did not cause; `own-fallout`,
|
|
248
|
+
* something an earlier task of this plan broke or left stale; `own-gap`,
|
|
249
|
+
* something this plan built and left unfinished; `convention`, a file
|
|
250
|
+
* too long or a name off the pattern; `docs`, a stale doc line;
|
|
251
|
+
* `machine`, this machine's fault.
|
|
252
|
+
*/
|
|
253
|
+
export declare const BUG_KINDS: readonly ["bug", "own-fallout", "own-gap", "convention", "docs", "machine"];
|
|
254
|
+
/** One of the six out-of-scope entry kinds. */
|
|
255
|
+
export type BugKind = (typeof BUG_KINDS)[number];
|
|
256
|
+
/** One key the parser does not know, at whatever level it was written. */
|
|
257
|
+
export interface ReportExtra {
|
|
258
|
+
/** The key as written. */
|
|
259
|
+
readonly key: string;
|
|
260
|
+
/**
|
|
261
|
+
* The value exactly as the parser returned it. Never serialised here:
|
|
262
|
+
* a YAML alias can share it with another value.
|
|
263
|
+
*/
|
|
264
|
+
readonly value: unknown;
|
|
265
|
+
}
|
|
266
|
+
/** One `findings` entry. */
|
|
267
|
+
export interface ReportFinding {
|
|
268
|
+
/** When the finding surfaced. Required. */
|
|
269
|
+
readonly trigger: string | null;
|
|
270
|
+
/** What the finding is about. Required. */
|
|
271
|
+
readonly kind: FindingKind | null;
|
|
272
|
+
/** The finding itself. Required. */
|
|
273
|
+
readonly what: string | null;
|
|
274
|
+
/** Why it happens. */
|
|
275
|
+
readonly cause: string | null;
|
|
276
|
+
/** What resolves it. */
|
|
277
|
+
readonly resolution: string | null;
|
|
278
|
+
/** The byte string a recurrence would match on. */
|
|
279
|
+
readonly artifact: string | null;
|
|
280
|
+
/** Whether it failed loudly or passed silently. Required. */
|
|
281
|
+
readonly signal: FindingSignal | null;
|
|
282
|
+
/** What area of work its lesson belongs to. */
|
|
283
|
+
readonly domain: FindingDomain | null;
|
|
284
|
+
/** Every key that names no field. */
|
|
285
|
+
readonly extras: readonly ReportExtra[];
|
|
286
|
+
}
|
|
287
|
+
/** One `blockers` entry. */
|
|
288
|
+
export interface ReportBlocker {
|
|
289
|
+
/** What blocked the task. Required. */
|
|
290
|
+
readonly what: string | null;
|
|
291
|
+
/** The byte string that showed it. */
|
|
292
|
+
readonly artifact: string | null;
|
|
293
|
+
/** Every key that names no field. */
|
|
294
|
+
readonly extras: readonly ReportExtra[];
|
|
295
|
+
}
|
|
296
|
+
/** One `out_of_scope_bugs` entry. */
|
|
297
|
+
export interface ReportBug {
|
|
298
|
+
/** The bug. Required. */
|
|
299
|
+
readonly what: string | null;
|
|
300
|
+
/** The byte string that showed it. */
|
|
301
|
+
readonly artifact: string | null;
|
|
302
|
+
/** Whether the bug has a security impact. Required; never defaulted. */
|
|
303
|
+
readonly security: boolean | null;
|
|
304
|
+
/**
|
|
305
|
+
* What sort of leftover it is. Never null: an absent key reads as
|
|
306
|
+
* `bug` with no issue, and a value outside {@link BUG_KINDS} as `bug`
|
|
307
|
+
* with an `unusable-field` issue.
|
|
308
|
+
*/
|
|
309
|
+
readonly kind: BugKind;
|
|
310
|
+
/** The part of the code the bug sits in. */
|
|
311
|
+
readonly module: string | null;
|
|
312
|
+
/** What was being done when the bug showed. */
|
|
313
|
+
readonly action: string | null;
|
|
314
|
+
/** The command or steps that show the bug again. */
|
|
315
|
+
readonly reproduce: string | null;
|
|
316
|
+
/** Why the bug happens, a cause code or words, kept as written. */
|
|
317
|
+
readonly cause: string | null;
|
|
318
|
+
/** Every key that names no field. */
|
|
319
|
+
readonly extras: readonly ReportExtra[];
|
|
320
|
+
}
|
|
321
|
+
/** One `changes` entry: a changelog line, as the task wrote it. */
|
|
322
|
+
export interface ReportChange {
|
|
323
|
+
/** How much of a release the change is worth. Required. */
|
|
324
|
+
readonly level: ChangeLevel | null;
|
|
325
|
+
/** The short heading the changelog groups the line under. */
|
|
326
|
+
readonly area: string | null;
|
|
327
|
+
/** One line a user of the project would understand. Required. */
|
|
328
|
+
readonly summary: string | null;
|
|
329
|
+
/** Every key that names no field. */
|
|
330
|
+
readonly extras: readonly ReportExtra[];
|
|
331
|
+
}
|
|
332
|
+
/** One `stale_docs` entry: a documentation line that no longer matches. */
|
|
333
|
+
export interface ReportStaleDoc {
|
|
334
|
+
/** The file, with the line when known: `context/triage.md:12`. */
|
|
335
|
+
readonly where: string | null;
|
|
336
|
+
/** What no longer matches. Required; an entry without one is dropped. */
|
|
337
|
+
readonly what: string;
|
|
338
|
+
/** Every key that names no field. */
|
|
339
|
+
readonly extras: readonly ReportExtra[];
|
|
340
|
+
}
|
|
341
|
+
/** A `rafa:report` block, read. */
|
|
342
|
+
export interface TaskReport {
|
|
343
|
+
/** The session's claim for its task. Required. */
|
|
344
|
+
readonly status: ReportStatus | null;
|
|
345
|
+
/** What was done and how it went, as parsed. */
|
|
346
|
+
readonly feedback: string | null;
|
|
347
|
+
/** Every usable `findings` entry, in order, none merged. */
|
|
348
|
+
readonly findings: readonly ReportFinding[];
|
|
349
|
+
/** Every usable `skills_used` entry, in order. */
|
|
350
|
+
readonly skillsUsed: readonly string[];
|
|
351
|
+
/** Every usable `blockers` entry, in order. */
|
|
352
|
+
readonly blockers: readonly ReportBlocker[];
|
|
353
|
+
/** Every usable `out_of_scope_bugs` entry, in order. */
|
|
354
|
+
readonly outOfScopeBugs: readonly ReportBug[];
|
|
355
|
+
/** Every usable `changes` entry, in order. */
|
|
356
|
+
readonly changes: readonly ReportChange[];
|
|
357
|
+
/** Every `stale_docs` entry that says what is stale, in order. */
|
|
358
|
+
readonly staleDocs: readonly ReportStaleDoc[];
|
|
359
|
+
/** Every top-level key that names no field. */
|
|
360
|
+
readonly extras: readonly ReportExtra[];
|
|
361
|
+
}
|
|
362
|
+
/** Why a field was not read as written. */
|
|
363
|
+
export type ReportIssueReason =
|
|
364
|
+
/** A required field that is absent or null. */
|
|
365
|
+
'missing-field'
|
|
366
|
+
/** A value the field cannot hold. Left null, or dropped from its list. */
|
|
367
|
+
| 'unusable-field';
|
|
368
|
+
/** One thing the report said that the model does not hold as written. */
|
|
369
|
+
export interface ReportIssue {
|
|
370
|
+
/** What went unread, and why. */
|
|
371
|
+
readonly reason: ReportIssueReason;
|
|
372
|
+
/** Where, in the report's own key names: `findings[2].signal`. */
|
|
373
|
+
readonly field: string;
|
|
374
|
+
/** One sentence for an operator to read. */
|
|
375
|
+
readonly text: string;
|
|
376
|
+
}
|
|
377
|
+
/** Why a session output answers no report. */
|
|
378
|
+
export type ReportAbsenceReason =
|
|
379
|
+
/** The output holds no `rafa:report` block. */
|
|
380
|
+
'no-block'
|
|
381
|
+
/** The last `rafa:report` block is never closed. */
|
|
382
|
+
| 'unclosed-block'
|
|
383
|
+
/** The last `rafa:report` block is not YAML, or not a mapping. */
|
|
384
|
+
| 'malformed-block';
|
|
385
|
+
/** A session output whose last `rafa:report` block was read. */
|
|
386
|
+
export interface ReportPresent {
|
|
387
|
+
readonly present: true;
|
|
388
|
+
/** The report. */
|
|
389
|
+
readonly report: TaskReport;
|
|
390
|
+
/** The block it was read from: the last `rafa:report` in the output. */
|
|
391
|
+
readonly block: RafaBlock;
|
|
392
|
+
/** What was not read as written. Empty for a clean report. */
|
|
393
|
+
readonly issues: readonly ReportIssue[];
|
|
394
|
+
}
|
|
395
|
+
/** A session output that answers no report, and why. */
|
|
396
|
+
export interface ReportAbsent {
|
|
397
|
+
readonly present: false;
|
|
398
|
+
/** Why there is no report. */
|
|
399
|
+
readonly reason: ReportAbsenceReason;
|
|
400
|
+
/** The last `rafa:report` block, raw body included, or null for `no-block`. */
|
|
401
|
+
readonly block: RafaBlock | null;
|
|
402
|
+
/** One sentence for an operator to read. */
|
|
403
|
+
readonly text: string;
|
|
404
|
+
}
|
|
405
|
+
/** What {@link parseReport} answers: a report, or why there is none. */
|
|
406
|
+
export type ReportReading = ReportPresent | ReportAbsent;
|
|
407
|
+
/**
|
|
408
|
+
* Reads the last `rafa:report` block out of a session's captured output.
|
|
409
|
+
*
|
|
410
|
+
* Takes any string, normally `CapturedSession.stdout`. Answers the report
|
|
411
|
+
* with an issue for everything not read as written, or an absence naming
|
|
412
|
+
* why there is none. Never throws. See the module note for which block
|
|
413
|
+
* counts and how each field is read.
|
|
414
|
+
*/
|
|
415
|
+
export declare function parseReport(output: string): ReportReading;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `project_id`: the twelve hex characters an instinct record carries to
|
|
3
|
+
* say which repository it was learned in, derived from that
|
|
4
|
+
* repository's `origin` remote.
|
|
5
|
+
*
|
|
6
|
+
* This is somebody else's algorithm, reproduced on purpose.
|
|
7
|
+
* `~/.claude/skills/continuous-learning-v2/scripts/detect-project.sh`
|
|
8
|
+
* files its own instincts under exactly this hash, so a record rafa
|
|
9
|
+
* writes and a record that skill writes for the same repository have
|
|
10
|
+
* to carry the SAME id or the two stores cannot be read together. That
|
|
11
|
+
* makes the NORMALISATION matter as much as the hash: the same
|
|
12
|
+
* repository is spelled `git@github.com:o/r.git`,
|
|
13
|
+
* `https://github.com/O/R.git` and `https://token@github.com/o/r`
|
|
14
|
+
* by three different checkouts, and all three have to land on one id.
|
|
15
|
+
*
|
|
16
|
+
* {@link normalizeRemote} is that script's four `sed -E` lines in
|
|
17
|
+
* order — strip embedded credentials, strip the scheme, rewrite
|
|
18
|
+
* `git@host:path` as `host/path`, drop a trailing `.git` and slashes —
|
|
19
|
+
* followed by its lowercasing of a network URL only. A local path
|
|
20
|
+
* keeps its case because a case-sensitive filesystem makes
|
|
21
|
+
* `/Users/Marcos` and `/users/marcos` different directories.
|
|
22
|
+
*
|
|
23
|
+
* Six remotes were run through the shell function's own text and
|
|
24
|
+
* `shasum -a 256` on 2026-09-18, and their normalised forms and
|
|
25
|
+
* digests are pinned as literals in `project-id.test.ts`. That pinning
|
|
26
|
+
* is the ONLY thing that can catch this implementation drifting from
|
|
27
|
+
* the shell one: nothing else in the repository compares them, and a
|
|
28
|
+
* drift produces two stores that quietly disagree rather than an
|
|
29
|
+
* error.
|
|
30
|
+
*
|
|
31
|
+
* ## What is deliberately not reproduced
|
|
32
|
+
*
|
|
33
|
+
* That script has a fallback chain past the remote: it hashes the main
|
|
34
|
+
* worktree root when a repository has no `origin`, and answers the
|
|
35
|
+
* literal `global` outside a repository. {@link projectId} answers null
|
|
36
|
+
* for both, because a path-derived id is machine-specific and a record
|
|
37
|
+
* carrying one would claim a cross-machine identity it does not have.
|
|
38
|
+
* `project_id` on a record is optional for exactly this reason.
|
|
39
|
+
*
|
|
40
|
+
* Nothing here throws. {@link gitRemoteUrl} answers null for a missing
|
|
41
|
+
* remote, a directory in no repository and a machine with no git
|
|
42
|
+
* alike, because the caller's next move is the same in all three: omit
|
|
43
|
+
* the field.
|
|
44
|
+
*/
|
|
45
|
+
/** Characters of the remote's SHA-256 a `project_id` keeps. */
|
|
46
|
+
export declare const PROJECT_ID_LENGTH = 12;
|
|
47
|
+
/** The shape {@link projectIdFromRemote} produces: lowercase hex. */
|
|
48
|
+
export declare const PROJECT_ID_PATTERN: RegExp;
|
|
49
|
+
/**
|
|
50
|
+
* `url` as continuous-learning-v2 hashes it: credentials stripped, the
|
|
51
|
+
* scheme dropped, `git@host:path` rewritten `host/path`, a trailing
|
|
52
|
+
* `.git` and slashes removed, and a network URL lowercased while a
|
|
53
|
+
* local path keeps its case. Like `sed` without `g`, each step
|
|
54
|
+
* replaces only the first match.
|
|
55
|
+
*/
|
|
56
|
+
export declare function normalizeRemote(url: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* The first {@link PROJECT_ID_LENGTH} hex characters of the SHA-256 of
|
|
59
|
+
* {@link normalizeRemote}'s answer, or null when `url` says nothing.
|
|
60
|
+
* The same id `detect-project.sh` derives for the same repository.
|
|
61
|
+
*/
|
|
62
|
+
export declare function projectIdFromRemote(url: string): string | null;
|
|
63
|
+
/**
|
|
64
|
+
* `origin`'s URL for the repository holding `dir`, or null when there
|
|
65
|
+
* is no origin, no repository or no git. Never throws: see the module
|
|
66
|
+
* note on why all three are one answer.
|
|
67
|
+
*/
|
|
68
|
+
export declare function gitRemoteUrl(dir: string): string | null;
|
|
69
|
+
/** What {@link projectId} reads the world through. */
|
|
70
|
+
export interface ProjectIdSeams {
|
|
71
|
+
/** The remote probe. {@link gitRemoteUrl} when absent. */
|
|
72
|
+
readonly readRemote?: (dir: string) => string | null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The `project_id` a record written for the repository holding `dir`
|
|
76
|
+
* carries, or null when that repository has no `origin` — which is the
|
|
77
|
+
* whole reason the field is optional.
|
|
78
|
+
*/
|
|
79
|
+
export declare function projectId(dir: string, seams?: ProjectIdSeams): string | null;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** The skill tiers, nearest the work first. */
|
|
2
|
+
export declare const SKILL_TIERS: readonly ["project", "rafa", "user"];
|
|
3
|
+
/** One of {@link SKILL_TIERS}. */
|
|
4
|
+
export type SkillTier = typeof SKILL_TIERS[number];
|
|
5
|
+
/** The instinct scopes, nearest the work first. There is no rafa scope. */
|
|
6
|
+
export declare const INSTINCT_SCOPES: readonly ["project", "user"];
|
|
7
|
+
/** One of {@link INSTINCT_SCOPES}. */
|
|
8
|
+
export type InstinctScope = typeof INSTINCT_SCOPES[number];
|
|
9
|
+
/** The skills directory Claude Code loads, under a project root or the home. */
|
|
10
|
+
export declare const CLAUDE_SKILLS_PATH: string;
|
|
11
|
+
/** The instincts directory a scope holds, under a project root or the home. */
|
|
12
|
+
export declare const RAFA_INSTINCTS_PATH: string;
|
|
13
|
+
/** The directory rafa's own skills sit in, relative to the running `cli.js`. */
|
|
14
|
+
export declare const BUNDLED_SKILLS_DIR: string;
|
|
15
|
+
/** The directory rafa's own programs sit in, relative to the running `cli.js`. */
|
|
16
|
+
export declare const BUNDLED_BIN_DIR: string;
|
|
17
|
+
/** What a tier is resolved against. Each seam is given; none is read from the environment. */
|
|
18
|
+
export interface TierSeams {
|
|
19
|
+
/** The home directory, an absolute path. */
|
|
20
|
+
readonly home: string;
|
|
21
|
+
/** The project root, or null when the caller stands in no project. */
|
|
22
|
+
readonly projectRoot: string | null;
|
|
23
|
+
/** This process's entry, which the rafa tier sits beside. Defaults to `Bun.main`. */
|
|
24
|
+
readonly entry?: string;
|
|
25
|
+
}
|
|
26
|
+
/** One tier and the directory it resolves to. */
|
|
27
|
+
export interface SkillTierLocation {
|
|
28
|
+
/** Which tier. */
|
|
29
|
+
readonly tier: SkillTier;
|
|
30
|
+
/** The directory it resolves to, absolute. */
|
|
31
|
+
readonly dir: string;
|
|
32
|
+
}
|
|
33
|
+
/** One instinct scope and the directory it resolves to. */
|
|
34
|
+
export interface InstinctScopeLocation {
|
|
35
|
+
/** Which scope. */
|
|
36
|
+
readonly scope: InstinctScope;
|
|
37
|
+
/** The directory it resolves to, absolute. */
|
|
38
|
+
readonly dir: string;
|
|
39
|
+
}
|
|
40
|
+
/** Whether `value` names a tier, for a `rafa skill list --source` a line typed. */
|
|
41
|
+
export declare function isSkillTier(value: string): value is SkillTier;
|
|
42
|
+
/** Whether `value` names an instinct scope. */
|
|
43
|
+
export declare function isInstinctScope(value: string): value is InstinctScope;
|
|
44
|
+
/** `entry` with its links resolved, or as it was when nothing is there. */
|
|
45
|
+
export declare function realEntry(entry: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* The directory rafa's own skills sit in: `bundled/skills` beside the
|
|
48
|
+
* running `cli.js`, links resolved. See the module note.
|
|
49
|
+
*/
|
|
50
|
+
export declare function bundledSkillsDirectory(entry?: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* The directory rafa's own programs sit in: `bundled/bin` beside the
|
|
53
|
+
* running `cli.js`, links resolved, as {@link bundledSkillsDirectory}
|
|
54
|
+
* resolves its skills. A build writes `ts-symbols` there; a checkout's
|
|
55
|
+
* `src/bundled/` holds no `bin/`, so run as `bun src/rafa.ts` it names
|
|
56
|
+
* a directory that is not there.
|
|
57
|
+
*/
|
|
58
|
+
export declare function bundledBinDirectory(entry?: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* The directory `tier` resolves to, or null for the project tier with
|
|
61
|
+
* no project root: a tier with no directory is not an empty one, and
|
|
62
|
+
* a caller has to be able to tell them apart.
|
|
63
|
+
*/
|
|
64
|
+
export declare function skillTierDirectory(tier: SkillTier, seams: TierSeams): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Every tier that has a directory, in {@link SKILL_TIERS} order. The
|
|
67
|
+
* directory need not be there: an absent one is {@link tierExists}'s
|
|
68
|
+
* question, and a tier is left out here only when it resolves to no
|
|
69
|
+
* path at all.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveSkillTiers(seams: TierSeams): readonly SkillTierLocation[];
|
|
72
|
+
/** The directory `scope` resolves to, or null for the project scope with no project root. */
|
|
73
|
+
export declare function instinctScopeDirectory(scope: InstinctScope, seams: TierSeams): string | null;
|
|
74
|
+
/** Every instinct scope that has a directory, in {@link INSTINCT_SCOPES} order. */
|
|
75
|
+
export declare function resolveInstinctScopes(seams: TierSeams): readonly InstinctScopeLocation[];
|
|
76
|
+
/** What {@link shadowLessonsById} ranks: a record, the scope holding it, and its id. */
|
|
77
|
+
export interface ScopedLesson {
|
|
78
|
+
/** The scope that holds it. */
|
|
79
|
+
readonly scope: InstinctScope;
|
|
80
|
+
/** The id a nearer scope shadows it by. */
|
|
81
|
+
readonly id: string;
|
|
82
|
+
}
|
|
83
|
+
/** A record that lost to a nearer one of the same id. */
|
|
84
|
+
export interface ShadowedLesson<T extends ScopedLesson> {
|
|
85
|
+
/** The record that lost. */
|
|
86
|
+
readonly record: T;
|
|
87
|
+
/** The record that shadows it, in a nearer scope or first in the same one. */
|
|
88
|
+
readonly by: T;
|
|
89
|
+
}
|
|
90
|
+
/** What {@link shadowLessonsById} answers. */
|
|
91
|
+
export interface LessonShadowing<T extends ScopedLesson> {
|
|
92
|
+
/** One record per id, in {@link INSTINCT_SCOPES} order, then the order handed. */
|
|
93
|
+
readonly winners: readonly T[];
|
|
94
|
+
/** Every other record, each beside the winner that shadows it. */
|
|
95
|
+
readonly shadowed: readonly ShadowedLesson<T>[];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The record a reader takes for each `id` across the instinct scopes:
|
|
99
|
+
* the project scope's before the user scope's. Records may come in any
|
|
100
|
+
* order and are taken in scope order, stably, so a second record of
|
|
101
|
+
* one id inside one scope is shadowed by the first that scope was
|
|
102
|
+
* handed. Throws on a record whose scope is not an instinct scope:
|
|
103
|
+
* the rafa tier holds no lessons (see the module note).
|
|
104
|
+
*/
|
|
105
|
+
export declare function shadowLessonsById<T extends ScopedLesson>(records: readonly T[]): LessonShadowing<T>;
|
|
106
|
+
/** Whether a resolved tier or scope directory is there at all. */
|
|
107
|
+
export declare function tierExists(dir: string): boolean;
|