@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,63 @@
|
|
|
1
|
+
import type { SessionDecision, SessionRecord, SessionStep } from './sessions.js';
|
|
2
|
+
/** The states a session record holds, in the spec's order. */
|
|
3
|
+
export declare const SESSION_STATES: readonly ['running', 'paused', 'stopped', 'done'];
|
|
4
|
+
/** One of {@link SESSION_STATES}. */
|
|
5
|
+
export type SessionState = (typeof SESSION_STATES)[number];
|
|
6
|
+
/**
|
|
7
|
+
* The phases of a run, in the order a run meets them: its tasks, the
|
|
8
|
+
* wrap-up, the pull request, the CI wait, and a repair of what CI failed.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SESSION_PHASES: readonly ['task', 'wrap-up', 'pull-request', 'ci', 'repair'];
|
|
11
|
+
/** One of {@link SESSION_PHASES}. */
|
|
12
|
+
export type SessionPhase = (typeof SESSION_PHASES)[number];
|
|
13
|
+
/** What a record's file name ends in. */
|
|
14
|
+
export declare const RECORD_EXTENSION = ".json";
|
|
15
|
+
/** The kinds of suite step a run records, in the order a run meets them. */
|
|
16
|
+
export declare const SESSION_STEP_KINDS: readonly ['baseline', 'task', 'stage', 'pre-wrap-up'];
|
|
17
|
+
/** One of {@link SESSION_STEP_KINDS}. */
|
|
18
|
+
export type SessionStepKind = (typeof SESSION_STEP_KINDS)[number];
|
|
19
|
+
/**
|
|
20
|
+
* Why a suite step ran at the scope it did: the task's own `tests=`
|
|
21
|
+
* declaration, a full-suite trigger, the `--changed` fallback taken when
|
|
22
|
+
* no `Owns:` folder narrows the run, a `tests=module` task whose module
|
|
23
|
+
* holds no test file, and a stage step's own scope.
|
|
24
|
+
*/
|
|
25
|
+
export declare const SESSION_STEP_REASONS: readonly ['declared', 'trigger', 'fallback', 'no-module-tests', 'stage'];
|
|
26
|
+
/** One of {@link SESSION_STEP_REASONS}. */
|
|
27
|
+
export type SessionStepReason = (typeof SESSION_STEP_REASONS)[number];
|
|
28
|
+
/** The strategies a saved decision may name: the two that pass over a task. */
|
|
29
|
+
export declare const SESSION_DECISION_STRATEGIES: readonly ['jump', 'defer'];
|
|
30
|
+
/** A record file that cannot be read, or holds no record this module accepts. */
|
|
31
|
+
export declare class SessionRecordError extends Error {
|
|
32
|
+
/** The record's path. */
|
|
33
|
+
readonly file: string;
|
|
34
|
+
constructor(file: string, problem: string);
|
|
35
|
+
}
|
|
36
|
+
/** True when the text can name a record file. */
|
|
37
|
+
export declare function isSessionId(value: string): boolean;
|
|
38
|
+
/** True for a whole number from 1. */
|
|
39
|
+
export declare function isPositiveWhole(value: unknown): value is number;
|
|
40
|
+
/** Every problem with one step, the `at` naming its place in the record. */
|
|
41
|
+
export declare function stepProblems(step: unknown, at: string): string[];
|
|
42
|
+
/** Every problem with a parsed record read from `file`. */
|
|
43
|
+
export declare function recordProblems(fields: object, file: string): string[];
|
|
44
|
+
/**
|
|
45
|
+
* A frozen record of fields already checked, in the order it is written;
|
|
46
|
+
* `phase`, `hop`, `worktree`, `steps` then `decisions` last, each only
|
|
47
|
+
* when there, `phase` only when it is one of {@link SESSION_PHASES}.
|
|
48
|
+
*/
|
|
49
|
+
export declare function freezeRecord(fields: object): SessionRecord;
|
|
50
|
+
/** The steps a record holds, oldest first; none for a record written before the field. */
|
|
51
|
+
export declare function sessionSteps(record: Pick<SessionRecord, 'steps'>): readonly SessionStep[];
|
|
52
|
+
/** The pass-over list a record saved; none for a record without the key. */
|
|
53
|
+
export declare function sessionDecisions(record: Pick<SessionRecord, 'decisions'>): readonly SessionDecision[];
|
|
54
|
+
/**
|
|
55
|
+
* The phase a record reads as: its own, or `task` for a record without
|
|
56
|
+
* one, as a record from a rafa older than the field is.
|
|
57
|
+
*/
|
|
58
|
+
export declare function sessionPhase(record: Pick<SessionRecord, 'phase'>): SessionPhase;
|
|
59
|
+
/**
|
|
60
|
+
* Reads a record out of the text of `file`, or throws
|
|
61
|
+
* {@link SessionRecordError} naming every problem. See the module note.
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseSessionRecord(text: string, file: string): SessionRecord;
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session records of `loop start`: one JSON file per run under
|
|
3
|
+
* `<root>/.rafa/runs/`, named by the session's id.
|
|
4
|
+
*
|
|
5
|
+
* `.rafa/specs/cli-surface.md`, "Sessions": `loop stop`, `pause`, `resume`,
|
|
6
|
+
* `status` and `list` act on a run through its record, so every
|
|
7
|
+
* `loop start` writes `.rafa/runs/<session-id>.json`. This module reads,
|
|
8
|
+
* judges and writes those files, and prints nothing. `start/session.ts`
|
|
9
|
+
* is where `loop start` calls it.
|
|
10
|
+
*
|
|
11
|
+
* ## The record
|
|
12
|
+
*
|
|
13
|
+
* A {@link SessionRecord}, written as indented JSON:
|
|
14
|
+
*
|
|
15
|
+
* - `sessionId`: the run's id, the file's name before `.json`.
|
|
16
|
+
* - `planStub`: the plan's stub, or null for a plan whose file name
|
|
17
|
+
* carries none, as `PLAN.md` does (`core/plan/plan-stamp.ts`).
|
|
18
|
+
* - `plan`: the plan's path relative to the project root. The spec lists
|
|
19
|
+
* the stub alone; the path is what tells two stubless plans apart.
|
|
20
|
+
* - `branch`: the branch the run was started on.
|
|
21
|
+
* - `pid`: the process running the loop.
|
|
22
|
+
* - `startedAt`: when the run began, as an ISO timestamp.
|
|
23
|
+
* - `state`: one of {@link SESSION_STATES}.
|
|
24
|
+
* - `task`: the task running, its 1-based tracker line and its sentence,
|
|
25
|
+
* or null.
|
|
26
|
+
* - `phase`: where the run is, one of {@link SESSION_PHASES}: `task`
|
|
27
|
+
* while it works its plan's tasks, then `wrap-up`, `pull-request`,
|
|
28
|
+
* `ci` and `repair`. The field is additive, as `steps` is: a record
|
|
29
|
+
* written before it, and a new record, carry no `phase` key, and
|
|
30
|
+
* {@link sessionPhase} reads such a record as `task`. A phase this
|
|
31
|
+
* rafa does not know, as a later rafa may write, is no problem on
|
|
32
|
+
* read and reads as `task` too; it is left out of the record read,
|
|
33
|
+
* so the next write of the record carries no `phase` key. It is
|
|
34
|
+
* written as its own `"phase": "<value>"` key, as `state` is, which
|
|
35
|
+
* the zsh prompt plugin's patterns over the file's text rely on
|
|
36
|
+
* (`extras/zsh/rafa-prompt/`). Once written, every later write keeps
|
|
37
|
+
* it until a change names another ({@link SessionChange}'s `phase`).
|
|
38
|
+
* - `hop`: only on a run started with `loop start --roadmap` while a
|
|
39
|
+
* `rafa next --roadmap` hop is away, the hop record as
|
|
40
|
+
* `.rafa/hop.json` held it when the run began
|
|
41
|
+
* (`src/next/hop-record.ts`, `start/session.ts`). A record of any
|
|
42
|
+
* other run carries no `hop` key at all, never one set to null, so
|
|
43
|
+
* such a record is written byte for byte as it was before the field.
|
|
44
|
+
* Once written, every later write of the record keeps it as it is.
|
|
45
|
+
* - `worktree`: only on a run whose checkout is a linked worktree rather
|
|
46
|
+
* than the project root, as a `loop start --as-worktree` run's is, the
|
|
47
|
+
* absolute path of that worktree (`start/session.ts`). A run in the
|
|
48
|
+
* main checkout carries no `worktree` key at all, as it carries no
|
|
49
|
+
* `hop`, and every later write keeps the path as it is.
|
|
50
|
+
* - `steps`: the suite steps the runner has recorded for the run, oldest
|
|
51
|
+
* first, each a {@link SessionStep}. The field is additive: a record
|
|
52
|
+
* written before it, and a run that has recorded no step, carry no
|
|
53
|
+
* `steps` key at all, and {@link sessionSteps} reads such a record as
|
|
54
|
+
* holding none. A stored `steps: []` reads the same way and is written
|
|
55
|
+
* back without the key. Steps are only ever appended
|
|
56
|
+
* ({@link SessionChange}'s `appendStep`); every later write keeps them.
|
|
57
|
+
* - `decisions`: the pass-over list of a `loop start --continue` run
|
|
58
|
+
* (`start/pass-over.ts`), each a {@link SessionDecision}: the task
|
|
59
|
+
* jumped or deferred, its `lineNum` counted from 0 as the tracker
|
|
60
|
+
* reader counts it, its text with the blocker comment and
|
|
61
|
+
* declaration off, and its `ordinal`, which copy of that text it is
|
|
62
|
+
* among the tracker's task lines (left out of a record from before
|
|
63
|
+
* the field, and read as 1), the strategy, `jump` or `defer`, the
|
|
64
|
+
* decision's reason, and on a defer alone the task it waits on, as
|
|
65
|
+
* `after`. A later run of the plan reads it back off the newest
|
|
66
|
+
* ended record, stopped or done, and matches each task by its text
|
|
67
|
+
* and ordinal.
|
|
68
|
+
* The field is additive, as `steps` is: a record carries no
|
|
69
|
+
* `decisions` key until a change hands a list, an empty list is
|
|
70
|
+
* written without the key, and
|
|
71
|
+
* {@link sessionDecisions} reads such a record as holding none. A
|
|
72
|
+
* change's list replaces the stored one whole
|
|
73
|
+
* ({@link SessionChange}'s `decisions`); every other write keeps it.
|
|
74
|
+
*
|
|
75
|
+
* ## Steps
|
|
76
|
+
*
|
|
77
|
+
* A {@link SessionStep} is one suite run the runner made for the run:
|
|
78
|
+
*
|
|
79
|
+
* - `kind`: one of {@link SESSION_STEP_KINDS}, `baseline` at the plan's
|
|
80
|
+
* first dispatch, `task` after a task commits, `stage` after a stage's
|
|
81
|
+
* last task, `pre-wrap-up` before the wrap-up.
|
|
82
|
+
* - `scope`: `affected`, `module` or `full` (`TEST_SCOPES`,
|
|
83
|
+
* `core/plan/declaration.ts`), or the list of files and folders run, which
|
|
84
|
+
* may be empty.
|
|
85
|
+
* - `reason`: optional, why the step ran at that scope, one of
|
|
86
|
+
* {@link SESSION_STEP_REASONS}: `declared` for the task's own `tests=`
|
|
87
|
+
* declaration, `trigger` for a full-suite trigger, `fallback` for the
|
|
88
|
+
* `--changed` run taken when no `Owns:` folder narrows it,
|
|
89
|
+
* `no-module-tests` for a `tests=module` task whose module holds no
|
|
90
|
+
* test file, and `stage` for a stage step's own scope. It is written
|
|
91
|
+
* as its own key right after `scope`. A step written by rafa 0.35.0
|
|
92
|
+
* or earlier carries no `reason` key and reads as having none; a
|
|
93
|
+
* value outside the set is left out on read, never refused, as an
|
|
94
|
+
* unknown `phase` is.
|
|
95
|
+
* - `command`: the argv spawned, `bun` first, never empty.
|
|
96
|
+
* - `exitCode`: the command's exit code, a whole number.
|
|
97
|
+
* - `summary`: Bun's `Ran N tests across M files.` line, or null when it
|
|
98
|
+
* printed none.
|
|
99
|
+
* - `failures`: the failing tests, each a file and a full test name
|
|
100
|
+
* (`SuiteFailure`, `suite-failure.ts`).
|
|
101
|
+
* - `newFailures`: those of `failures` that are new against the
|
|
102
|
+
* baseline; every one of them is also in `failures`, compared by file
|
|
103
|
+
* and name. A new failure also carries `errorLines`, after its
|
|
104
|
+
* `name`: the first lines of the error Bun printed for it
|
|
105
|
+
* (`suite/failure-lines.ts`), which the JUnit report does not hold.
|
|
106
|
+
* The key is left out of a failure Bun printed nothing for and of
|
|
107
|
+
* every step written before it; a value that is no non-empty list of
|
|
108
|
+
* strings is left out on read, never refused, as an unknown `reason`
|
|
109
|
+
* is.
|
|
110
|
+
* - `stepOnly`: only on a step that reran its newly red test files
|
|
111
|
+
* alone and found some green (`start/suite-retake-alone.ts`), each a
|
|
112
|
+
* {@link SessionStepOnly}: the file, the names of its tests that were
|
|
113
|
+
* red in the step, the error lines Bun printed for the first of
|
|
114
|
+
* them, its `position` in the order its `bun test` process ran the
|
|
115
|
+
* files, counted from 1, or null when that order did not hold it, and
|
|
116
|
+
* `before`, the few files run just before it, in run order. Such a
|
|
117
|
+
* file's tests stay in `failures` and are left out of `newFailures`:
|
|
118
|
+
* they were red only in the step's own file order. The key is
|
|
119
|
+
* written right after `newFailures`. Every other step carries no
|
|
120
|
+
* `stepOnly` key, and an entry outside that shape is left out on
|
|
121
|
+
* read, never refused, as an unknown `reason` is.
|
|
122
|
+
* - `outsideCase`: only on a step whose run exited nonzero and listed
|
|
123
|
+
* no failing case (`start/suite-outside-case.ts`), `true`, with
|
|
124
|
+
* `outputTail` right after it: the last non-blank lines of that
|
|
125
|
+
* run's stderr (`SuiteResult.outputTail`, `suite/run.ts`), which is
|
|
126
|
+
* the evidence of a run the JUnit report says nothing about; the
|
|
127
|
+
* list is empty when the run printed none. The pair is written
|
|
128
|
+
* after `stepOnly`. Every other step carries neither key, so it is
|
|
129
|
+
* written byte for byte as it was before them. On read an
|
|
130
|
+
* `outsideCase` that is not `true` is left out with the tail, never
|
|
131
|
+
* refused, as an unknown `reason` is, and a tail that is no list of
|
|
132
|
+
* strings reads as empty.
|
|
133
|
+
* - `interrupted`: only on a step the runner read as a stop rather than
|
|
134
|
+
* a suite's answer, `true`: its `bun test` ended on SIGINT, or the
|
|
135
|
+
* runner received SIGINT while it ran (`start/suite-step.ts`). Its
|
|
136
|
+
* failures are what Bun had reported by then, and none of them is new.
|
|
137
|
+
* Every other step carries no `interrupted` key at all, never one set
|
|
138
|
+
* to false, so it is written byte for byte as it was before the field.
|
|
139
|
+
*
|
|
140
|
+
* ## One session is one `loop start`
|
|
141
|
+
*
|
|
142
|
+
* A session's id is new for every run, and a rerun of a plan never takes
|
|
143
|
+
* over an earlier session's record. The preflight's run id is the session
|
|
144
|
+
* id, and the store refuses a second preflight write under a run id it
|
|
145
|
+
* already holds (`effort/store/preflight.ts`). Measured on 2026-09-15: the
|
|
146
|
+
* second `writePreflightChecks` under one id threw
|
|
147
|
+
* `UNIQUE constraint failed: preflight.run_id, preflight.position`, while
|
|
148
|
+
* a write under another id went in. A rerun reusing its plan's session
|
|
149
|
+
* would halt on that refusal whenever the plan names a prerequisite. So a
|
|
150
|
+
* plan run three times on its branch leaves three records.
|
|
151
|
+
*
|
|
152
|
+
* ## The state a record reads as
|
|
153
|
+
*
|
|
154
|
+
* A record stored as `running` or `paused` whose pid is gone reads as
|
|
155
|
+
* `stopped` ({@link readState}): the run ended without writing its end, as
|
|
156
|
+
* a killed process does. `stopped` and `done` read as stored. A pid is
|
|
157
|
+
* probed with signal 0, which sends nothing: a process that is gone
|
|
158
|
+
* answers `ESRCH`, and one that exists under another user `EPERM`, which
|
|
159
|
+
* reads as alive. A pid the system has handed to an unrelated process
|
|
160
|
+
* since reads as alive too, so such a record keeps refusing its plan until
|
|
161
|
+
* it is deleted; the refusal names its file.
|
|
162
|
+
*
|
|
163
|
+
* ## Uniqueness over plan, session and branch
|
|
164
|
+
*
|
|
165
|
+
* Two records name the same plan when both carry a stub and the stubs are
|
|
166
|
+
* equal, or neither carries one and the paths are equal
|
|
167
|
+
* ({@link samePlan}). A stub names the plan wherever its file sits, as
|
|
168
|
+
* effort attribution reads it. {@link sessionConflicts} refuses a run over
|
|
169
|
+
* each record of its plan that:
|
|
170
|
+
*
|
|
171
|
+
* - **names another branch**, in whatever state: a plan that has a
|
|
172
|
+
* branch does not create another. A revised plan is a new stub, and a
|
|
173
|
+
* new stub has no record.
|
|
174
|
+
* - **names the run's branch and reads `running` or `paused`**: that
|
|
175
|
+
* session is running the plan, and a second run on the same tracker
|
|
176
|
+
* would dispatch its tasks twice.
|
|
177
|
+
*
|
|
178
|
+
* A record of the plan on the run's branch that reads `stopped` or `done`
|
|
179
|
+
* refuses nothing. The rerun is a session of its own under the plan and
|
|
180
|
+
* branch that match, which is the one way a plan runs twice.
|
|
181
|
+
*
|
|
182
|
+
* ## Writes
|
|
183
|
+
*
|
|
184
|
+
* Every write lands whole: the text goes to a temporary file beside the
|
|
185
|
+
* record, whose name does not end in `.json`, and is then linked into
|
|
186
|
+
* place for a new record or renamed over an existing one. The link fails
|
|
187
|
+
* when the name is taken, so {@link beginSession} never overwrites a
|
|
188
|
+
* record. A record is checked as it would be read before it is written,
|
|
189
|
+
* so nothing this module writes is one it refuses to read.
|
|
190
|
+
*
|
|
191
|
+
* {@link beginSession} reads the records and writes the new one with no
|
|
192
|
+
* lock between the two, so two runs of one plan started within that
|
|
193
|
+
* window both go ahead. The loop is single-thread until phase 6, and the
|
|
194
|
+
* window is one directory read.
|
|
195
|
+
*
|
|
196
|
+
* {@link updateSession} reads the stored record again and changes only
|
|
197
|
+
* the fields it is handed, so a state another process wrote, as
|
|
198
|
+
* `loop pause` writes `paused`, survives the loop writing its running
|
|
199
|
+
* task. A key this module does not know is dropped by that write.
|
|
200
|
+
*
|
|
201
|
+
* A change handed `onlyFrom` is refused with {@link SessionStateError},
|
|
202
|
+
* writing nothing, when the state stored is none of those. `loop pause`
|
|
203
|
+
* moves only a `running` record and `loop resume` only a `paused` one, so
|
|
204
|
+
* a run that writes its end between such a command's read and its write
|
|
205
|
+
* keeps that end. The window left is the one between this module's own
|
|
206
|
+
* read and its rename, which holds no lock either.
|
|
207
|
+
*
|
|
208
|
+
* ## Reading a record
|
|
209
|
+
*
|
|
210
|
+
* The record's field readers and {@link parseSessionRecord} live in
|
|
211
|
+
* `session-record-parse.ts`, beside this file, whose note lists what is
|
|
212
|
+
* refused on read. This module re-exports what a caller reads from
|
|
213
|
+
* there, so `./sessions.js` stays the one import a record's reader
|
|
214
|
+
* needs: `isPositiveWhole` and `RECORD_EXTENSION` among them, which
|
|
215
|
+
* `src/stretch/folder.ts` and `src/cleanup/runs.ts` read.
|
|
216
|
+
*/
|
|
217
|
+
import type { HopRecord } from './hop-record.js';
|
|
218
|
+
import type { SessionPhase, SessionState, SessionStepKind, SessionStepReason } from './session-record-parse.js';
|
|
219
|
+
import type { SuiteFailure } from './suite-failure.js';
|
|
220
|
+
import type { TestScope } from '../plan/declaration.js';
|
|
221
|
+
export type { SessionPhase, SessionState, SessionStepKind, SessionStepReason, } from './session-record-parse.js';
|
|
222
|
+
export { isPositiveWhole, isSessionId, parseSessionRecord, RECORD_EXTENSION, SESSION_DECISION_STRATEGIES, SESSION_PHASES, SESSION_STATES, SESSION_STEP_KINDS, SESSION_STEP_REASONS, SessionRecordError, sessionDecisions, sessionPhase, sessionSteps, } from './session-record-parse.js';
|
|
223
|
+
/** The directory the records are kept in, under a project's `.rafa/`. */
|
|
224
|
+
export declare const RUNS_DIR = "runs";
|
|
225
|
+
/** The task a session is running. */
|
|
226
|
+
export interface SessionTask {
|
|
227
|
+
/** The task's line in the tracker, counted from 1. */
|
|
228
|
+
readonly line: number;
|
|
229
|
+
/** The task's sentence, its routing declaration left out. */
|
|
230
|
+
readonly text: string;
|
|
231
|
+
}
|
|
232
|
+
/** One test file red in a step and green when run alone. See the module note, "Steps". */
|
|
233
|
+
export interface SessionStepOnly {
|
|
234
|
+
/** The test file, relative to the checkout. */
|
|
235
|
+
readonly file: string;
|
|
236
|
+
/** The full names of its tests that were red in the step. */
|
|
237
|
+
readonly tests: readonly string[];
|
|
238
|
+
/** The error lines Bun printed for the first of them; none when it printed none. */
|
|
239
|
+
readonly errorLines: readonly string[];
|
|
240
|
+
/** Its place, from 1, in the order its `bun test` process ran the files; null when that order did not hold it. */
|
|
241
|
+
readonly position: number | null;
|
|
242
|
+
/** The files run just before it, in run order. */
|
|
243
|
+
readonly before: readonly string[];
|
|
244
|
+
}
|
|
245
|
+
/** One suite run the runner recorded for a session. See the module note. */
|
|
246
|
+
export interface SessionStep {
|
|
247
|
+
readonly kind: SessionStepKind;
|
|
248
|
+
/** A named scope, or the files and folders run. */
|
|
249
|
+
readonly scope: TestScope | readonly string[];
|
|
250
|
+
/** Why the step ran at {@link SessionStep.scope}; left out of a step from rafa 0.35.0 or earlier. See the module note. */
|
|
251
|
+
readonly reason?: SessionStepReason;
|
|
252
|
+
/** The argv spawned, `bun` first. */
|
|
253
|
+
readonly command: readonly string[];
|
|
254
|
+
readonly exitCode: number;
|
|
255
|
+
/** Bun's summary line, or null when it printed none. */
|
|
256
|
+
readonly summary: string | null;
|
|
257
|
+
readonly failures: readonly SuiteFailure[];
|
|
258
|
+
/** Those of {@link SessionStep.failures} new against the baseline. */
|
|
259
|
+
readonly newFailures: readonly SuiteFailure[];
|
|
260
|
+
/** The files red in the step and green alone; left out of a step with none. See the module note. */
|
|
261
|
+
readonly stepOnly?: readonly SessionStepOnly[];
|
|
262
|
+
/** `true` on a step whose run exited nonzero and listed no failing case; left out of every other. See the module note. */
|
|
263
|
+
readonly outsideCase?: true;
|
|
264
|
+
/** The last lines of that run's stderr; only beside {@link SessionStep.outsideCase}. */
|
|
265
|
+
readonly outputTail?: readonly string[];
|
|
266
|
+
/** `true` on a step read as a stop on SIGINT; left out of every other. See the module note. */
|
|
267
|
+
readonly interrupted?: true;
|
|
268
|
+
}
|
|
269
|
+
/** A task a saved decision names: its zero-based tracker line and its text. */
|
|
270
|
+
export interface SessionTaskRef {
|
|
271
|
+
/** Counted from 0, as `TaskInfo.lineNum`; a hint, the text and the ordinal being the key. */
|
|
272
|
+
readonly lineNum: number;
|
|
273
|
+
/** The task's text, its blocker comment and declaration off. */
|
|
274
|
+
readonly task: string;
|
|
275
|
+
/**
|
|
276
|
+
* Which copy of `task` it is among the tracker's task lines, ticked
|
|
277
|
+
* ones included, counted from 1 in tracker order. Left out of a record
|
|
278
|
+
* written before the field, which reads as 1.
|
|
279
|
+
*/
|
|
280
|
+
readonly ordinal?: number;
|
|
281
|
+
}
|
|
282
|
+
/** One task a `--continue` run passes over, as its record saves it. See the module note. */
|
|
283
|
+
export interface SessionDecision {
|
|
284
|
+
readonly task: SessionTaskRef;
|
|
285
|
+
readonly strategy: 'jump' | 'defer';
|
|
286
|
+
readonly reason: string;
|
|
287
|
+
/** On a defer alone: the task it waits on. */
|
|
288
|
+
readonly after?: SessionTaskRef;
|
|
289
|
+
}
|
|
290
|
+
/** One `loop start` run, as its record holds it. See the module note. */
|
|
291
|
+
export interface SessionRecord {
|
|
292
|
+
readonly sessionId: string;
|
|
293
|
+
readonly planStub: string | null;
|
|
294
|
+
readonly plan: string;
|
|
295
|
+
readonly branch: string;
|
|
296
|
+
readonly pid: number;
|
|
297
|
+
readonly startedAt: string;
|
|
298
|
+
readonly state: SessionState;
|
|
299
|
+
readonly task: SessionTask | null;
|
|
300
|
+
/** Where the run is; left out of a record from before the field. Read it with {@link sessionPhase}. */
|
|
301
|
+
readonly phase?: SessionPhase;
|
|
302
|
+
/** The away hop the run was started under; left out of every other run's record. See the module note. */
|
|
303
|
+
readonly hop?: HopRecord;
|
|
304
|
+
/** The linked worktree the run's checkout is; left out of a run in the main checkout. See the module note. */
|
|
305
|
+
readonly worktree?: string;
|
|
306
|
+
/** The suite steps recorded, oldest first; left out while there are none. Read it with {@link sessionSteps}. */
|
|
307
|
+
readonly steps?: readonly SessionStep[];
|
|
308
|
+
/** The pass-over list saved; left out while there is none. Read it with {@link sessionDecisions}. */
|
|
309
|
+
readonly decisions?: readonly SessionDecision[];
|
|
310
|
+
}
|
|
311
|
+
/** What a new record is made from; it opens `running`, with no task, no phase, no step and no decision. */
|
|
312
|
+
export type SessionDraft = Omit<SessionRecord, 'state' | 'task' | 'phase' | 'steps' | 'decisions'>;
|
|
313
|
+
/** What {@link updateSession} changes. A field left out keeps its stored value. */
|
|
314
|
+
export interface SessionChange {
|
|
315
|
+
readonly state?: SessionState;
|
|
316
|
+
readonly task?: SessionTask | null;
|
|
317
|
+
/** The phase the run is now in. */
|
|
318
|
+
readonly phase?: SessionPhase;
|
|
319
|
+
/** A step appended after the stored ones. */
|
|
320
|
+
readonly appendStep?: SessionStep;
|
|
321
|
+
/** The pass-over list, replacing the stored one whole; an empty one drops the key. */
|
|
322
|
+
readonly decisions?: readonly SessionDecision[];
|
|
323
|
+
/**
|
|
324
|
+
* The stored states the change acts on. Any other refuses it with
|
|
325
|
+
* {@link SessionStateError}, writing nothing. Every state when left out.
|
|
326
|
+
*/
|
|
327
|
+
readonly onlyFrom?: readonly SessionState[];
|
|
328
|
+
}
|
|
329
|
+
/** Answers whether a process with this pid exists. */
|
|
330
|
+
export type PidProbe = (pid: number) => boolean;
|
|
331
|
+
/** The seams a read goes through. */
|
|
332
|
+
export interface SessionReadSeams {
|
|
333
|
+
/** Whether a pid is alive. {@link isPidAlive} when left out. */
|
|
334
|
+
readonly isAlive?: PidProbe;
|
|
335
|
+
}
|
|
336
|
+
/** Why a record of the plan refuses a run: its branch, or its live session. */
|
|
337
|
+
export type SessionConflictReason = 'branch' | 'live';
|
|
338
|
+
/** A record refusing a run, and why. */
|
|
339
|
+
export interface SessionConflict {
|
|
340
|
+
readonly reason: SessionConflictReason;
|
|
341
|
+
readonly record: SessionRecord;
|
|
342
|
+
}
|
|
343
|
+
/** A run {@link beginSession} refused, over the records that refuse it. */
|
|
344
|
+
export declare class SessionConflictError extends Error {
|
|
345
|
+
readonly conflicts: readonly SessionConflict[];
|
|
346
|
+
constructor(conflicts: readonly SessionConflict[]);
|
|
347
|
+
}
|
|
348
|
+
/** A change {@link updateSession} refused: the record stores a state the change does not act on. */
|
|
349
|
+
export declare class SessionStateError extends Error {
|
|
350
|
+
/** The record's path. */
|
|
351
|
+
readonly file: string;
|
|
352
|
+
/** The state the record stores. */
|
|
353
|
+
readonly state: SessionState;
|
|
354
|
+
constructor(file: string, state: SessionState, onlyFrom: readonly SessionState[]);
|
|
355
|
+
}
|
|
356
|
+
/** `<root>/.rafa/runs`. */
|
|
357
|
+
export declare function runsDir(root: string): string;
|
|
358
|
+
/** `<root>/.rafa/runs/<sessionId>.json`. Throws on an id {@link isSessionId} refuses. */
|
|
359
|
+
export declare function sessionFilePath(root: string, sessionId: string): string;
|
|
360
|
+
/** The system's code on a thrown error, such as `ENOENT`, or null when it carries none. */
|
|
361
|
+
export declare function errorCode(error: unknown): string | null;
|
|
362
|
+
/**
|
|
363
|
+
* Whether a process with this pid exists, by signal 0; false for a pid
|
|
364
|
+
* that is no positive whole number, which is never signalled. See the
|
|
365
|
+
* module note.
|
|
366
|
+
*/
|
|
367
|
+
export declare function isPidAlive(pid: number): boolean;
|
|
368
|
+
/** The state a record reads as: `running` or `paused` with its pid gone reads `stopped`. */
|
|
369
|
+
export declare function readState(record: SessionRecord, isAlive?: PidProbe): SessionState;
|
|
370
|
+
/**
|
|
371
|
+
* Every record under `<root>/.rafa/runs/`, each with the state it reads as
|
|
372
|
+
* ({@link readState}), oldest first. Throws {@link SessionRecordError} for
|
|
373
|
+
* the first file that holds no record.
|
|
374
|
+
*/
|
|
375
|
+
export declare function readSessions(root: string, seams?: SessionReadSeams): readonly SessionRecord[];
|
|
376
|
+
/**
|
|
377
|
+
* The record of `sessionId` under `<root>/.rafa/runs/`, with the state it
|
|
378
|
+
* reads as ({@link readState}). Throws {@link SessionRecordError} when its
|
|
379
|
+
* file is not there or holds no record, and the error
|
|
380
|
+
* {@link sessionFilePath} throws for an id it refuses.
|
|
381
|
+
*/
|
|
382
|
+
export declare function readSession(root: string, sessionId: string, seams?: SessionReadSeams): SessionRecord;
|
|
383
|
+
/** Whether two records name the same plan. See the module note. */
|
|
384
|
+
export declare function samePlan(a: Pick<SessionRecord, 'planStub' | 'plan'>, b: Pick<SessionRecord, 'planStub' | 'plan'>): boolean;
|
|
385
|
+
/**
|
|
386
|
+
* The records refusing a run of `candidate`'s plan on `candidate`'s
|
|
387
|
+
* branch, in the order handed in. `records` carry the state they read as,
|
|
388
|
+
* as {@link readSessions} answers them. See the module note.
|
|
389
|
+
*/
|
|
390
|
+
export declare function sessionConflicts(records: readonly SessionRecord[], candidate: Pick<SessionRecord, 'planStub' | 'plan' | 'branch'>): readonly SessionConflict[];
|
|
391
|
+
/**
|
|
392
|
+
* Opens a session: refuses it with {@link SessionConflictError} when a
|
|
393
|
+
* record of its plan does ({@link sessionConflicts}), and otherwise writes
|
|
394
|
+
* its record, `running` with no task, and answers it. Throws
|
|
395
|
+
* {@link SessionRecordError} when a record cannot be read, and the
|
|
396
|
+
* system's error when the new one cannot be written, its id's file
|
|
397
|
+
* already existing included.
|
|
398
|
+
*/
|
|
399
|
+
export declare function beginSession(root: string, draft: SessionDraft, seams?: SessionReadSeams): SessionRecord;
|
|
400
|
+
/**
|
|
401
|
+
* Changes the stored record of a session, reading it again first, and
|
|
402
|
+
* answers the record written; a change's `appendStep` goes after the
|
|
403
|
+
* stored steps. Throws {@link SessionRecordError} when the
|
|
404
|
+
* record cannot be read or the change would make it one that cannot be,
|
|
405
|
+
* and {@link SessionStateError} when it stores a state the change's
|
|
406
|
+
* `onlyFrom` leaves out.
|
|
407
|
+
*/
|
|
408
|
+
export declare function updateSession(root: string, sessionId: string, change: SessionChange): SessionRecord;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The record of one failing test, as `src/suite/run.ts` reads it from a
|
|
3
|
+
* JUnit report and a session record keeps it among a suite step's
|
|
4
|
+
* failures (`sessions.ts`).
|
|
5
|
+
*
|
|
6
|
+
* It sits in core so `sessions.ts` reads it without importing above
|
|
7
|
+
* core; `src/suite/run.ts` re-exports it under the same name.
|
|
8
|
+
*/
|
|
9
|
+
/** One failing test: its file, relative to the run's directory, and its full name. */
|
|
10
|
+
export interface SuiteFailure {
|
|
11
|
+
readonly file: string;
|
|
12
|
+
/** Its describe names, outermost first, and its own, joined by ` > `. */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/**
|
|
15
|
+
* The first line of its JUnit failure's message; absent when the
|
|
16
|
+
* report gave none. Compared by `src/triage/inherited.ts` when present to
|
|
17
|
+
* identify inherited failures.
|
|
18
|
+
*/
|
|
19
|
+
readonly message?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The first lines of the error Bun printed for it on stderr, trimmed;
|
|
22
|
+
* absent when it printed none. See the module note of `src/suite/run.ts`.
|
|
23
|
+
*/
|
|
24
|
+
readonly errorLines?: readonly string[];
|
|
25
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Output this process writes through, and the mode it renders in.
|
|
3
|
+
*
|
|
4
|
+
* A function that prints asks {@link activeOutput} for the output rather
|
|
5
|
+
* than taking one as an argument, as `src/start/stamp.ts` holds a run's
|
|
6
|
+
* plan stub: an `Output` threaded through every function that prints
|
|
7
|
+
* would add an argument to call sites that have nothing to say about
|
|
8
|
+
* output. The output is held in two places, and the readers answer the
|
|
9
|
+
* first that has one.
|
|
10
|
+
*
|
|
11
|
+
* ## The scope
|
|
12
|
+
*
|
|
13
|
+
* {@link runWithActiveOutput} runs a function with an output and a mode
|
|
14
|
+
* in an async scope of its own, an `AsyncLocalStorage`
|
|
15
|
+
* (`node:async_hooks`). Inside that function, and in everything it
|
|
16
|
+
* awaits or starts, the readers answer that output and that mode; once
|
|
17
|
+
* the function ends, however it ends, nothing of them is left. The
|
|
18
|
+
* dispatcher runs every command this way (`src/cli/dispatch.ts`), with
|
|
19
|
+
* the command's context output in the invocation's mode.
|
|
20
|
+
*
|
|
21
|
+
* A scope belongs to one run, so runs need not nest to stay apart. Two
|
|
22
|
+
* runs that overlap in one process, started together and awaited with
|
|
23
|
+
* `Promise.all`, each write through their own output whichever ends
|
|
24
|
+
* first. A run started inside another reads the inner output inside and
|
|
25
|
+
* the outer one after. The module-level value below keeps overlapping
|
|
26
|
+
* runs apart only while they nest: set and put back in a `finally` it is
|
|
27
|
+
* a stack, and two that end out of order leave the output of one of them
|
|
28
|
+
* for the rest of the process (#927).
|
|
29
|
+
*
|
|
30
|
+
* ## The module-level value
|
|
31
|
+
*
|
|
32
|
+
* Outside every scope the readers answer one module-level value, which
|
|
33
|
+
* no module but a test and its fixtures sets. Until one sets another, it
|
|
34
|
+
* is the `text` adapter at verbosity 0 writing to `process.stdout`.
|
|
35
|
+
* {@link setActiveOutput} with `null` puts that default back. The
|
|
36
|
+
* default is made once, when this module is first imported, so
|
|
37
|
+
* {@link activeOutput} answers the same object on every read while
|
|
38
|
+
* nothing is set. It holds `process.stdout` itself and calls its `write`
|
|
39
|
+
* on each line, so a spy set on `process.stdout.write` after the import
|
|
40
|
+
* sees every line.
|
|
41
|
+
*
|
|
42
|
+
* ## A set made inside a scope
|
|
43
|
+
*
|
|
44
|
+
* The readers prefer the scope, and {@link setActiveOutput} writes the
|
|
45
|
+
* module-level value alone, inside a scope as outside one. So a set made
|
|
46
|
+
* inside a scope changes nothing that scope reads, and is what the
|
|
47
|
+
* readers answer once the scope has ended. A scope's output cannot be
|
|
48
|
+
* replaced from inside it.
|
|
49
|
+
*
|
|
50
|
+
* ## What reads outside the scope
|
|
51
|
+
*
|
|
52
|
+
* A scope follows the awaits and the callbacks started inside it. A
|
|
53
|
+
* callback the runtime calls from elsewhere does not carry it. Measured
|
|
54
|
+
* under bun 1.3.14, three listeners registered inside a scope read no
|
|
55
|
+
* store: a `process.on` signal listener when the signal arrived, an
|
|
56
|
+
* abort listener that handler fired, and an abort listener an
|
|
57
|
+
* `AbortSignal.timeout` fired. A timer, a child process's `data`
|
|
58
|
+
* listener, a `readline` `line` listener and the code after an await
|
|
59
|
+
* such a handler settled all read the scope's. A reader outside the
|
|
60
|
+
* scope writes through the module-level value; one that needs the
|
|
61
|
+
* scope's output reads it where the listener is registered, or is
|
|
62
|
+
* wrapped with `AsyncResource.bind` there.
|
|
63
|
+
*
|
|
64
|
+
* ## The mode
|
|
65
|
+
*
|
|
66
|
+
* An Output carries no mode of its own, and two writers need one, so the
|
|
67
|
+
* mode is held beside the output, in the scope and at module level
|
|
68
|
+
* alike. `core/loop/claude.ts` echoes a session's stdout as the bytes it
|
|
69
|
+
* wrote in `text` mode and as one `log` event per line in `json` mode.
|
|
70
|
+
* `start/dispatch.ts` emits a `step` event per task in `json` mode
|
|
71
|
+
* alone, where the `text` adapter would render it as a `step: ` line
|
|
72
|
+
* beside the line announcing the task. The dispatcher hands each
|
|
73
|
+
* invocation's mode to its scope with its output. The default, and an
|
|
74
|
+
* output set at module level with no mode, render in `text`.
|
|
75
|
+
*
|
|
76
|
+
* Bun runs every test file in one process, and the module-level value is
|
|
77
|
+
* module state: a case that sets an output sets `null` after it, or each
|
|
78
|
+
* file bun runs later writes through that case's output, in that case's
|
|
79
|
+
* mode.
|
|
80
|
+
*/
|
|
81
|
+
import type { Output } from '../../ports/index.js';
|
|
82
|
+
import type { OutputMode } from '../config/config-sections.js';
|
|
83
|
+
/**
|
|
84
|
+
* Runs `run` with `output` as the active output, rendering in `mode`, in
|
|
85
|
+
* an async scope of its own, and answers what `run` answers. Both are
|
|
86
|
+
* read inside `run` and whatever it awaits, and nowhere after it; see
|
|
87
|
+
* the module note.
|
|
88
|
+
*/
|
|
89
|
+
export declare function runWithActiveOutput<T>(output: Output, mode: OutputMode, run: () => T): T;
|
|
90
|
+
/**
|
|
91
|
+
* Sets the module-level output and the mode it renders in, `text`
|
|
92
|
+
* unless one is named. `null` puts the default back, in `text` whatever
|
|
93
|
+
* mode is named. Inside a scope it changes nothing that scope reads; see
|
|
94
|
+
* the module note.
|
|
95
|
+
*/
|
|
96
|
+
export declare function setActiveOutput(output: Output | null, mode?: OutputMode): void;
|
|
97
|
+
/** The output to write through: the one of the scope this is read in, else the module-level one. */
|
|
98
|
+
export declare function activeOutput(): Output;
|
|
99
|
+
/** The mode the active output renders in, read as {@link activeOutput} is; see the module note. */
|
|
100
|
+
export declare function activeOutputMode(): OutputMode;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an Output adapter writes.
|
|
3
|
+
*
|
|
4
|
+
* Copied from open-tomato's `packages/shared/cli-core/src/output.ts`,
|
|
5
|
+
* where it is named `CliOutputStream`, at commit
|
|
6
|
+
* `18f94c843fff1bce65ed2245f8712ed9b64a2d51` (2026-06-25). Every output
|
|
7
|
+
* adapter writes through it, `text.ts` beside this module and `json.ts`
|
|
8
|
+
* and `events.ts` under `src/adapters/output/`, so it lives in none of
|
|
9
|
+
* them.
|
|
10
|
+
*
|
|
11
|
+
* The source spells `write` as a method. It is a function-typed property
|
|
12
|
+
* here, as every port function is in `packages/rafa/src/ports/index.ts`, whose note
|
|
13
|
+
* gives the reason. `process.stdout` is assignable to it, which
|
|
14
|
+
* `src/adapter-registry.ts` relies on when a context names no stream.
|
|
15
|
+
*/
|
|
16
|
+
/** A stream an Output adapter writes whole lines to. */
|
|
17
|
+
export interface OutputStream {
|
|
18
|
+
/** Writes one chunk. What it answers is never read. */
|
|
19
|
+
write: (chunk: string) => unknown;
|
|
20
|
+
}
|