@bluewombat/implementer 0.1.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 (55) hide show
  1. package/README.md +91 -0
  2. package/SPECS.md +349 -0
  3. package/dist/args/parse-args.d.ts +6 -0
  4. package/dist/args/parse-args.d.ts.map +1 -0
  5. package/dist/args/parse-args.js +330 -0
  6. package/dist/args/parse-args.js.map +1 -0
  7. package/dist/child/parse-child-output.d.ts +11 -0
  8. package/dist/child/parse-child-output.d.ts.map +1 -0
  9. package/dist/child/parse-child-output.js +49 -0
  10. package/dist/child/parse-child-output.js.map +1 -0
  11. package/dist/child/run-builder.d.ts +24 -0
  12. package/dist/child/run-builder.d.ts.map +1 -0
  13. package/dist/child/run-builder.js +92 -0
  14. package/dist/child/run-builder.js.map +1 -0
  15. package/dist/child/run-child.d.ts +36 -0
  16. package/dist/child/run-child.d.ts.map +1 -0
  17. package/dist/child/run-child.js +141 -0
  18. package/dist/child/run-child.js.map +1 -0
  19. package/dist/child/run-gate.d.ts +14 -0
  20. package/dist/child/run-gate.d.ts.map +1 -0
  21. package/dist/child/run-gate.js +72 -0
  22. package/dist/child/run-gate.js.map +1 -0
  23. package/dist/cli.d.ts +3 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +44 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/index.d.ts +12 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +9 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/loop/decide-after-attempt.d.ts +23 -0
  32. package/dist/loop/decide-after-attempt.d.ts.map +1 -0
  33. package/dist/loop/decide-after-attempt.js +26 -0
  34. package/dist/loop/decide-after-attempt.js.map +1 -0
  35. package/dist/progress/emit.d.ts +5 -0
  36. package/dist/progress/emit.d.ts.map +1 -0
  37. package/dist/progress/emit.js +24 -0
  38. package/dist/progress/emit.js.map +1 -0
  39. package/dist/run/run-implementer.d.ts +23 -0
  40. package/dist/run/run-implementer.d.ts.map +1 -0
  41. package/dist/run/run-implementer.js +274 -0
  42. package/dist/run/run-implementer.js.map +1 -0
  43. package/dist/status/announce.d.ts +14 -0
  44. package/dist/status/announce.d.ts.map +1 -0
  45. package/dist/status/announce.js +40 -0
  46. package/dist/status/announce.js.map +1 -0
  47. package/dist/status/make-status.d.ts +9 -0
  48. package/dist/status/make-status.d.ts.map +1 -0
  49. package/dist/status/make-status.js +38 -0
  50. package/dist/status/make-status.js.map +1 -0
  51. package/dist/types.d.ts +119 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +3 -0
  54. package/dist/types.js.map +1 -0
  55. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # Implementer
2
+
3
+ Transformer: runs **one Task** in an existing workspace. It launches the
4
+ Builder, then the Gate sequence it was given (possibly empty), and loops Attempts
5
+ until the run is `validated`, `escalated`, or `interrupted`.
6
+
7
+ Behavioural contract: [SPECS.md](./SPECS.md).
8
+
9
+ ## Stack
10
+
11
+ Repository stack — TypeScript, Node 24.20, Biome, `node:test`. See [docs/DEVELOPMENT.md](../../../docs/DEVELOPMENT.md).
12
+
13
+ ## Setup
14
+
15
+ From the repository root:
16
+
17
+ ```bash
18
+ nvm use
19
+ npm install
20
+ ```
21
+
22
+ ## Commands
23
+
24
+ | Task | Command |
25
+ | --------- | ---------------- |
26
+ | Lint | `npm run lint` |
27
+ | Format | `npm run format` |
28
+ | Typecheck | `npm run tsc` |
29
+ | Test | `npm test` |
30
+ | Build | `npm run build` |
31
+
32
+ Binary after build: `dist/cli.js` (package bin name `implementer`).
33
+
34
+ ## Run
35
+
36
+ Workspace must already exist. Example with no Gates:
37
+
38
+ ```bash
39
+ npm run build
40
+ node dist/cli.js \
41
+ --id task-1 \
42
+ --intention "do the work" \
43
+ --definition-of-done "checks pass" \
44
+ --workspace /absolute/path/to/workspace \
45
+ --builder -- node /path/to/builder.js \
46
+ --builder-timeout-ms 60000 \
47
+ --max-attempts 3
48
+ ```
49
+
50
+ Each child has its own ceiling: `--builder-timeout-ms` (required), and `--gate-timeout-ms` before each `--gate`. There is no Attempt clock and no Task clock.
51
+
52
+ Progress is JSON lines on stdout (`status`, `attempt-started`, `builder-finished`,
53
+ `gate-finished`, `attempt-finished`, `run-finished`). Exit codes: `0` validated,
54
+ `1` escalated, `2` invalid-invocation, `130` interrupted.
55
+
56
+ ## Import
57
+
58
+ Same Transformer, in process. `cli.ts` is an adapter over this function — it reads argv
59
+ and stdin, calls `runImplementer`, and maps the outcome to an exit code. Nothing the CLI
60
+ can do is unreachable from here.
61
+
62
+ ```ts
63
+ import { runImplementer } from "@bluewombat/implementer";
64
+
65
+ const result = await runImplementer({
66
+ invocation, // same shape the CLI builds from argv
67
+ write: (line) => events.push(line), // progress as values, not stdout
68
+ });
69
+ ```
70
+
71
+ `src/index.ts` exports `runImplementer`, its `RunOptions` / `RunResult`, `ProgressWriter`,
72
+ and this Transformer's domain types.
73
+
74
+ ## Layout
75
+
76
+ ```
77
+ SPECS.md behavioural contract
78
+ README.md how to build and run
79
+ src/
80
+ cli.ts entry
81
+ types.ts Transformer-owned types
82
+ args/ CLI parsing
83
+ loop/ pure Attempt-loop decisions
84
+ status/ Status labels and --on-status
85
+ progress/ JSON-line progress writer
86
+ child/ spawn, Builder, Gate
87
+ run/ invocation orchestration
88
+ fixtures/ fake Builder / Gate scripts for tests
89
+ ```
90
+
91
+ Sources stay colocated: helpers used once live next to their caller.
package/SPECS.md ADDED
@@ -0,0 +1,349 @@
1
+ # Implementer
2
+
3
+ Independent Transformer: it has a binary, a contract, and nothing else. This paragraph
4
+ is the only place this spec talks about the rest of the system. From here on,
5
+ Implementer is alone. Its code, identifiers, comments, and messages name only
6
+ what it owns: the Task, its arguments, the workspace path, Builder, Gates,
7
+ Attempts, Traces, Status, clocks, and the run outcome. No other component is
8
+ mentioned there, because none of them exist inside this perimeter.
9
+
10
+ Implementer runs **one Task** in a directory it did not create. It
11
+ launches the Builder, then the Gate sequence it was given (that sequence may be
12
+ empty), and loops Attempts until the run is validated, escalated, or
13
+ interrupted.
14
+
15
+ The workspace is the only place files belong: that is where the Builder writes.
16
+ Intention, definition of done, reports, bounds — arguments, never files.
17
+
18
+ Stack and binary layout: [README.md](./README.md).
19
+
20
+ ## 1. Job
21
+
22
+ ```
23
+ arguments Implementer children
24
+ --------- ----------- --------
25
+ Task one invocation Builder, once per Attempt
26
+ workspace Attempt loop each Gate, in order, per Attempt
27
+ Builder until a run outcome
28
+ ordered Gates (may be none)
29
+ bounds
30
+ ← run outcome + Traces + Status
31
+ ```
32
+
33
+ **In:** arguments. **Out:** one run outcome, a Trace per Attempt that ran, and
34
+ a live Status each time it changes. Implementer does not persist. It does not
35
+ wait for the listener to acknowledge. It does not write files.
36
+
37
+ ## 2. Options
38
+
39
+ Implementer is invoked with arguments. It does not read an input file. It does
40
+ not read configuration from the workspace.
41
+
42
+ | Argument | Content |
43
+ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
44
+ | `--id` | Task id. Copied onto every Trace. |
45
+ | `--intention` | Passed through to Builder and Gates. Implementer does not interpret it. |
46
+ | `--definition-of-done` | Passed through to Builder and Gates. Implementer does not evaluate it. |
47
+ | `--workspace` | Absolute path. Must already exist. Working directory of Builder and Gates. The only directory this process treats as files. |
48
+ | `--builder -- <argv…>` | Builder command. Non-empty. **May be omitted**: an Attempt is then its Gate sequence alone, and nothing is produced. |
49
+ | `--builder-timeout-ms` | Positive integer. **Required.** Wall clock of one producer run. Nothing else bounds it. |
50
+ | `--repair-builder -- <argv…>` | Optional producer for a pass that has something to resolve. Needs a `--builder` to fall back to. |
51
+ | `--repair-builder-timeout-ms` | Optional. Wall clock of that producer. Absent: `--builder-timeout-ms`. |
52
+ | `--gate <id> -- <argv…>` | One Gate. Repeatable; order is the sequence. **May be omitted** (empty sequence). Each `id` unique. Command non-empty. |
53
+ | `--gate-timeout-ms` | Positive integer. **Required before each `--gate`.** Wall clock of that Gate. |
54
+ | `--max-attempts` | Positive integer. Attempts this invocation may start. |
55
+ | `--report` | Optional. Failure report passed to the producer on Attempt 1 (and kept for retries). |
56
+ | `--report-from` | Optional. Which Gate produced `--report`, when a Gate did. |
57
+ | `--context` | Optional. Larger piece of work this Task belongs to, passed through to the producer. |
58
+ | `--stage` | Optional. `unit` or `assembly`. Passed to Gates. Absent: Gates receive `unit`. |
59
+ | `--on-status -- <argv…>` | Optional. Command to run each time Status changes. Omitted: Status still goes to stdout. |
60
+
61
+ No other argument is read.
62
+
63
+ Malformed arguments, missing workspace, empty command, duplicate Gate ids, or a
64
+ non-positive bound → run outcome `invalid-invocation`. No Attempt.
65
+
66
+ A Task with neither a Builder nor a Gate has nothing to do and nothing to judge:
67
+ that is an empty invocation, and it validates.
68
+
69
+ ## 3. Workflow
70
+
71
+ One invocation runs the whole Attempt loop.
72
+
73
+ An Attempt is one pass: Builder, then Gates from the start of the list, stopping
74
+ at the first non-pass. Numbered from 1. It starts when the Builder is spawned.
75
+
76
+ The run is `validated` only when **every** Gate passes in the **same** Attempt.
77
+ An empty list after a completed Builder is validation: Implementer adds no Gate
78
+ of its own.
79
+
80
+ ```
81
+ Start Attempt 1.
82
+ Run Builder.
83
+ If Builder does not complete → end Attempt (§5).
84
+ Else run Gates in list order.
85
+ Empty list → Attempt `validated`.
86
+ First non-pass → Attempt ends with that verdict.
87
+ All pass → Attempt `validated`.
88
+ After an Attempt:
89
+ `validated` → run `validated`.
90
+ `fail-blocking` → run `escalated` (do not start another Attempt).
91
+ `interrupted` → run `interrupted`.
92
+ `fail-retryable` and no Attempts left
93
+ → run `escalated`.
94
+ `fail-retryable` otherwise
95
+ → next Attempt.
96
+ Builder receives `--report` with the failing Gate's
97
+ report (or the Builder failure detail if no Gate ran).
98
+ Gates restart from the first in the list.
99
+ ```
100
+
101
+ ```mermaid
102
+ flowchart TD
103
+ START[Start Attempt] -->|a producer| B[Builder]
104
+ START -->|no producer: Gates alone| G
105
+ B -->|completed| G{Gates remaining?}
106
+ G -->|none| OK[Attempt validated]
107
+ G -->|yes| NEXT[Next Gate]
108
+ NEXT -->|pass| G
109
+ NEXT -->|fail-retryable| R{Attempts left?}
110
+ NEXT -->|fail-blocking| ESC[Run escalated]
111
+ B -->|fail-retryable, timed out| R
112
+ B -->|refused, or its own fail-blocking| ESC
113
+ B -->|interrupted| INT[Run interrupted]
114
+ NEXT -->|interrupted| INT
115
+ R -->|yes| AGAIN[Next Attempt: Builder with --report]
116
+ AGAIN --> B
117
+ R -->|no| ESC
118
+ OK --> DONE[Run validated]
119
+ ```
120
+
121
+ Rules of the loop are a pure function of verdicts, Attempt count, and clocks:
122
+ no process spawn inside that function, so it is testable without a Builder.
123
+
124
+ ## 4. Ceilings and interrupt
125
+
126
+ **Every child carries its own, and no other.** The producer takes
127
+ `--builder-timeout-ms` (or `--repair-builder-timeout-ms` when that producer
128
+ runs); each Gate takes the `--gate-timeout-ms` written before it. Reaching one
129
+ kills that child and ends the Attempt as `fail-retryable`, which the loop then
130
+ decides on like any other retryable failure.
131
+
132
+ **Nothing is derived from what another child left behind.** There is no Attempt
133
+ clock and no Task clock. A budget computed from a deadline is how a producer
134
+ that overran left its Gates a millisecond to answer in — they were killed on
135
+ the clock instead of judging the work, and the judgement was lost, not just the
136
+ build.
137
+
138
+ **What bounds a whole invocation** is therefore what the Project wrote, and it
139
+ is arithmetic anyone can do: `--max-attempts` times the producer's ceiling plus
140
+ the sum of the Gates'. Add the status hook, which is capped at 5s per call.
141
+
142
+ **Interrupt.** SIGINT and SIGTERM stop this invocation. Kill the child, end the
143
+ in-flight Attempt as `interrupted`, return `interrupted`. Do **not** delete
144
+ `--workspace`.
145
+
146
+ ## 5. Builder
147
+
148
+ Working directory: `--workspace`. Implementer appends arguments to the Builder
149
+ command. It sets no extra environment variables of its own (the process still
150
+ inherits the environment).
151
+
152
+ | Argument | Content |
153
+ | ---------------------- | ----------------------------------------------------------------------------------------------------- |
154
+ | `--id` | Task id |
155
+ | `--attempt` | Attempt number, decimal |
156
+ | `--intention` | The `--intention` given to Implementer |
157
+ | `--definition-of-done` | The `--definition-of-done` given to Implementer |
158
+ | `--report` | Previous Attempt's failure report (JSON `report` from that child's stdout), or `--report` given to Implementer. **Omitted** when neither exists. |
159
+ | `--report-from` | Present when a Gate produced that report. |
160
+ | `--context` | Present when `--context` was given to Implementer. |
161
+
162
+ Exit 0: the Builder finished its pass. Implementer does not inspect the
163
+ workspace.
164
+
165
+ | What happened | Attempt `ended` | `--report` on the next Builder, if any |
166
+ | ----------------------------------------------------------------- | -------------------- | -------------------------------------- |
167
+ | Exit 0 | completed; run Gates | — |
168
+ | Stdout is JSON `{ "outcome": "fail-blocking", "report": "..." }` | `fail-blocking` | that `report` |
169
+ | Stdout is JSON `{ "outcome": "fail-retryable", "report": "..." }` | `fail-retryable` | that `report` |
170
+ | Non-zero exit, no such JSON | `fail-retryable` | the end of stdout and stderr (8 KiB) |
171
+ | Killed by a clock | `fail-retryable` | which clock |
172
+ | Stop signal | `interrupted` | none |
173
+ | Cannot spawn (not found, not executable) | `fail-blocking` | why |
174
+
175
+ `outcome` on that optional JSON is only `fail-retryable` or `fail-blocking`.
176
+ Exit 0 is completion; there is no `pass` object. Other fields are ignored.
177
+
178
+ A Builder that cannot proceed returns `fail-blocking` with the reason in
179
+ `report`.
180
+
181
+ ## 6. Gates
182
+
183
+ The sequence is the `--gate` arguments, in that order. Implementer does not add
184
+ Gates, does not reorder them, and does not skip one because an earlier Attempt
185
+ passed it.
186
+
187
+ Working directory: `--workspace`. Implementer appends arguments to the Gate
188
+ command. No `--report`: the Gate judges the workspace.
189
+
190
+ | Argument | Content |
191
+ | ---------------------- | ----------------------- |
192
+ | `--id` | Task id |
193
+ | `--attempt` | Attempt number, decimal |
194
+ | `--gate-id` | This Gate's `id` |
195
+ | `--intention` | Same as the Builder |
196
+ | `--definition-of-done` | Same as the Builder |
197
+ | `--stage` | `unit` or `assembly` (default `unit`) |
198
+
199
+ Stdout: one JSON object.
200
+
201
+ ```json
202
+ {
203
+ "verdict": "pass",
204
+ "report": "optional; required when not pass"
205
+ }
206
+ ```
207
+
208
+ | `verdict` | Meaning |
209
+ | ---------------- | -------------------------------------------------------------------------------------------------- |
210
+ | `pass` | Next Gate, or validate if this was the last. |
211
+ | `fail-retryable` | Stop the sequence. New Attempt if budget remains; next Builder gets `--report` with this `report`. |
212
+ | `fail-blocking` | Stop the sequence. Escalate. No further Attempt. |
213
+
214
+ | What happened | Verdict used |
215
+ | --------------------------------- | ------------------------------------------------------------ |
216
+ | Valid JSON with a known `verdict` | that verdict; `report` as given (empty on `pass` if omitted) |
217
+ | Stdout is not that JSON | `fail-blocking` |
218
+ | Cannot spawn | `fail-blocking` |
219
+ | Killed by a clock | `fail-retryable`, report names the clock |
220
+ | Stop signal | Attempt `interrupted` |
221
+
222
+ A Gate that cannot return a verdict on the work returns `fail-blocking`. If it
223
+ must wait on something, it waits inside its own process until it can return
224
+ one of the three verdicts, or until `--gate-timeout-ms` kills it. Implementer
225
+ does not hold an Attempt open without consuming it.
226
+
227
+ ## 7. Status
228
+
229
+ Status is the live snapshot of this invocation. It changes at phase boundaries
230
+ (Builder about to run, a Gate about to run, run outcome known). It is not a
231
+ history — that is the Trace. The `label` is meant to be shown as-is.
232
+
233
+ ### Shape
234
+
235
+ | Field | Content |
236
+ | -------------- | --------------------------------------------------------------------------- |
237
+ | `phase` | `building`, `gating`, `validated`, `escalated`, `interrupted`, or `invalid` |
238
+ | `attempt` | Current Attempt number. Absent on `invalid`. |
239
+ | `max_attempts` | From `--max-attempts`. Absent on `invalid`. |
240
+ | `gate_id` | The Gate about to run. Present only when `phase` is `gating`. |
241
+ | `label` | Canonical display string. See below. |
242
+
243
+ ### Label
244
+
245
+ Built by Implementer. Do not invent another spelling.
246
+
247
+ | `phase` | `label` | Example |
248
+ | ------------- | ------------------------------------ | ------------------------- |
249
+ | `building` | `building:attempt-<n>:<max>` | `building:attempt-1:3` |
250
+ | `gating` | `gating:<gate_id>:attempt-<n>:<max>` | `gating:lint:attempt-1:3` |
251
+ | `validated` | `validated:attempt-<n>:<max>` | `validated:attempt-2:3` |
252
+ | `escalated` | `escalated:attempt-<n>:<max>` | `escalated:attempt-3:3` |
253
+ | `interrupted` | `interrupted:attempt-<n>:<max>` | `interrupted:attempt-1:3` |
254
+ | `invalid` | `invalid` | `invalid` |
255
+
256
+ `<n>` and `<max>` are decimal, no padding. `<gate_id>` is the id from `--gate`.
257
+
258
+ ### When it is announced
259
+
260
+ | Moment | `phase` |
261
+ | ----------------------------------------------------- | ------------ |
262
+ | Arguments or workspace unusable | `invalid` |
263
+ | Builder about to spawn | `building` |
264
+ | A Gate about to spawn | `gating` |
265
+ | Run outcome `validated` / `escalated` / `interrupted` | that outcome |
266
+
267
+ Announced on stdout as a JSON line (`event`: `status`) carrying the fields
268
+ above. If `--on-status` was given, Implementer also runs that command, cwd
269
+ unchanged (not the workspace), and appends:
270
+
271
+ | Argument | Content |
272
+ | ---------------- | ------------------------------------- |
273
+ | `--id` | Task id |
274
+ | `--label` | The label |
275
+ | `--phase` | The phase |
276
+ | `--attempt` | Present when the phase has an Attempt |
277
+ | `--max-attempts` | Present when the phase has an Attempt |
278
+ | `--gate-id` | Present when `phase` is `gating` |
279
+
280
+ `--on-status` does not count against Attempt or Task clocks. A non-zero exit,
281
+ a missing command, or a hang that Implementer must kill does **not** change
282
+ the run outcome. Visibility must not take the Task down.
283
+
284
+ ## 8. Trace and progress
285
+
286
+ ### Trace
287
+
288
+ What an Attempt leaves so the result can be understood without replaying.
289
+
290
+ | Field | Content |
291
+ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
292
+ | `task_id` | From `--id`. |
293
+ | `attempt` | Number, from 1. |
294
+ | `builder` | Input (intention, definition of done, previous `--report` if any) and result (completed / failed / timed out / interrupted / refused, plus detail). |
295
+ | `gates` | One entry per Gate **that ran**, in order: `id`, verdict, report. Gates not reached are absent. |
296
+ | `ended` | `validated`, `fail-retryable`, `fail-blocking`, or `interrupted`. |
297
+
298
+ `escalated` after at least one Attempt **must** carry that Attempt's Trace.
299
+ An escalation with no Trace when an Attempt existed is invalid.
300
+ `invalid-invocation` has no Trace.
301
+
302
+ ### Progress
303
+
304
+ JSON lines on stdout. A `status` line is the live snapshot (§7). The other
305
+ lines rebuild the Trace.
306
+
307
+ | `event` | When |
308
+ | ------------------ | --------------------------- |
309
+ | `status` | Each time Status changes |
310
+ | `attempt-started` | Builder about to spawn |
311
+ | `builder-finished` | Builder ended or was killed |
312
+ | `gate-finished` | A Gate ended or was killed |
313
+ | `attempt-finished` | Attempt `ended` is known |
314
+ | `run-finished` | Run outcome is known |
315
+
316
+ Each line carries `task_id`, `attempt` when an Attempt exists, and enough
317
+ to rebuild the Trace without reading the workspace.
318
+
319
+ Implementer creates no file under `--workspace`. The Builder (and Gates, if
320
+ they must) may write there — that is the work. Implementer deletes nothing
321
+ there.
322
+
323
+ ## 9. Run outcomes
324
+
325
+ Exactly one per invocation. The meaning is entirely inside this process.
326
+
327
+ | Outcome | Meaning |
328
+ | -------------------- | --------------------------------------------------------------------------------- |
329
+ | `invalid-invocation` | Arguments or workspace unusable. No Attempt. |
330
+ | `validated` | An Attempt ended `validated`. |
331
+ | `escalated` | `fail-blocking`, or a retryable failure with no Attempts left. |
332
+ | `interrupted` | Stop signal. |
333
+
334
+ No partial validation. Implementer never deletes `--workspace`.
335
+
336
+ ## 10. Acceptance
337
+
338
+ 1. No `--gate`, Builder exits 0 → `validated` after Attempt 1.
339
+ 2. A `fail-retryable` Gate → new Attempt from the first Gate; Builder receives `--report` with that `report`.
340
+ 3. A Gate that passed on Attempt 1 runs again on Attempt 2.
341
+ 4. `fail-blocking` → `escalated`, no further Attempt, with a Trace.
342
+ 5. `--max-attempts` exhausted on retryable failure → `escalated`, with a Trace.
343
+ 6. SIGTERM during Builder or a Gate → `interrupted`, workspace still on disk.
344
+ 7. Implementer never deletes the workspace, never requires a Gate, never writes an intention or report file.
345
+ 8. The loop's decisions are unit-testable with no child process.
346
+ 9. Entering the Builder on Attempt 1 of `--max-attempts 3` announces `label` `building:attempt-1:3` on stdout, and via `--on-status` when that argument is set.
347
+ 10. `--on-status` exiting non-zero does not change a `validated` run.
348
+
349
+ How to build and run this Transformer: [README.md](./README.md).
@@ -0,0 +1,6 @@
1
+ import type { ParseResult } from "../types.js";
2
+ /**
3
+ * Parse Implementer CLI argv (without node/script). Malformed input → not ok.
4
+ */
5
+ export declare function parseArgs(argv: string[]): ParseResult;
6
+ //# sourceMappingURL=parse-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-args.d.ts","sourceRoot":"","sources":["../../src/args/parse-args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAwB,WAAW,EAAE,MAAM,aAAa,CAAC;AAqErE;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,CA0RrD"}