@molecule/api-ai-tools 1.10.2 → 1.10.4

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 CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-19T01:32:03.421Z
6
+ Generated: 2026-10-19T06:38:37.559Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -216,6 +216,12 @@ interface CutRun {
216
216
  noun: 'files' | 'tests'
217
217
  /** The last thing the run printed — the line a reader looks at first. */
218
218
  doing: string | null
219
+ /**
220
+ * The interpreter of an inline program that only touches local files (`python3`, `node`, …), or
221
+ * null. Such a program is stopped by its short budget because it is stuck, never because the work
222
+ * is slow, so the sentence must not send it to the background (xr-molecule-flash-m200 +03:45).
223
+ */
224
+ inlineScript?: string | null
219
225
  }
220
226
  ```
221
227
 
@@ -626,6 +632,43 @@ interface TraceReadContradiction {
626
632
  }
627
633
  ```
628
634
 
635
+ #### `TraceHiddenBox`
636
+
637
+ A visibility failure whose locator RESOLVED: Playwright calls an element with a zero-size box
638
+ "hidden" even when it is painted, so the useful fact is why the box is empty. The trace's own
639
+ failure snapshot carries the element's children — read here, never guessed.
640
+
641
+ ```typescript
642
+ interface TraceHiddenBox {
643
+ /** The locator the assertion printed (`locator('a.navbar-brand')`), or the element it names. */
644
+ locator: string
645
+ /** The element's tag, lowercased (`a`). */
646
+ tag: string
647
+ /** The element's own inline style, when the snapshot carries one. */
648
+ style?: string
649
+ /**
650
+ * The `<img>` the element wraps when that image is its only element child and the element has
651
+ * no text of its own: the size the box would collapse to.
652
+ */
653
+ image?: {
654
+ /** The image's `src`, bounded. */
655
+ src?: string
656
+ /** Its `width` attribute. */
657
+ width?: string
658
+ /** Its `height` attribute. */
659
+ height?: string
660
+ /** Its inline `style`. */
661
+ style?: string
662
+ /** Its `class`. */
663
+ className?: string
664
+ /** Its `alt` text. */
665
+ alt?: string
666
+ }
667
+ /** True when the element has no text and no element children at all. */
668
+ empty?: true
669
+ }
670
+ ```
671
+
629
672
  #### `TraceArtifact`
630
673
 
631
674
  What one trace says: the test it belongs to, the requests it made, and its console lines.
@@ -715,6 +758,13 @@ interface TraceArtifact {
715
758
  * and on traces read before this field existed.
716
759
  */
717
760
  readContradiction?: TraceReadContradiction
761
+ /**
762
+ * A visibility failure whose locator RESOLVED into an empty box — Playwright's "hidden" — with
763
+ * the element's children read from the failure snapshot ({@link TraceHiddenBox}). The note uses
764
+ * it to name why the box is empty (an `<img>` with no CSS size, a textless element) instead of
765
+ * leaving the run to probe computed styles (xr-molecule-flash-m197).
766
+ */
767
+ hiddenBox?: TraceHiddenBox
718
768
  }
719
769
  ```
720
770
 
@@ -1026,6 +1076,17 @@ interface ToolBuildConfig {
1026
1076
  */
1027
1077
  suiteBudget?: (command: string, cwd: string) => Promise<SuiteBudgetPin | null | undefined>
1028
1078
 
1079
+ /**
1080
+ * A whole walk's duration in ms, as the consumer measured it. A suite run the executor detached
1081
+ * whose own `timeout` is shorter than this is lifted (m200); unset lifts only a bound no longer
1082
+ * than this tool's ceiling.
1083
+ *
1084
+ * @param command - The suite command.
1085
+ * @param cwd - The run's working directory.
1086
+ * @returns The duration in ms, or null.
1087
+ */
1088
+ suiteRunMs?: (command: string, cwd: string) => Promise<number | null | undefined>
1089
+
1029
1090
  /** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
1030
1091
  onAfterWrite?: (path: string) => Promise<void>
1031
1092
 
@@ -1659,7 +1720,9 @@ Written to be quotable by the model in one sentence and to leave no room for "ne
1659
1720
  the counts come from the runner's own output ({@link runProgress}), and a `total` the caller
1660
1721
  read elsewhere (the suite's own file count) fills in the "of N" it can then state. The sentence
1661
1722
  says how many tests the stop left unrun — a number a reader cannot round up to "the suite
1662
- passed" — and hands over the one move that survives a long run: run it in the background.
1723
+ passed" — and hands over the one move that survives a long run: run it in the background. An
1724
+ inline program that only touches local files is the exception ({@link CutRun.inlineScript}): it
1725
+ is not slow, it is stuck, and the background is the one place it must not be sent.
1663
1726
 
1664
1727
  ```typescript
1665
1728
  function cutRunSentence(run: CutRun): string
@@ -1699,6 +1762,37 @@ function allConnectionFailures(failures: readonly (RunFailureClass | null)[]): b
1699
1762
 
1700
1763
  **Returns:** True when there is at least one and none of them is the app's.
1701
1764
 
1765
+ #### `inlineLocalScriptCommand(command)`
1766
+
1767
+ The interpreter of an INLINE program that only reads and writes local files — the shape that gets
1768
+ {@link INLINE_SCRIPT_BUDGET_MS} instead of the tool's command budget.
1769
+
1770
+ An inline program with any external effect keeps the full budget: no network, install, test
1771
+ runner, build or server verb may appear anywhere in the command ({@link EXTERNAL_EFFECT}), and
1772
+ the command must not be a suite run or a foreground server (the platform's own classifiers).
1773
+
1774
+ ```typescript
1775
+ function inlineLocalScriptCommand(command: string): string | null
1776
+ ```
1777
+
1778
+ - `command` — The command as the executor sent it.
1779
+
1780
+ **Returns:** The interpreter's name (`python3`, `node`, …), or null when this is not the shape.
1781
+
1782
+ #### `inlineScriptRunawaySentence(interpreter, seconds)`
1783
+
1784
+ What a cut inline program's result says instead of the background advice: the work never was
1785
+ slow, it is stuck, and re-running it — here or in the background — cannot help.
1786
+
1787
+ ```typescript
1788
+ function inlineScriptRunawaySentence(interpreter: string, seconds: number): string
1789
+ ```
1790
+
1791
+ - `interpreter` — The interpreter the program ran under (`python3`, `node`, …).
1792
+ - `seconds` — The short budget it was stopped at, in seconds.
1793
+
1794
+ **Returns:** The sentence the result carries.
1795
+
1702
1796
  #### `normalizeToolInput(schema, raw)`
1703
1797
 
1704
1798
  Move known aliases onto the declared parameter names and report what is still
@@ -1818,7 +1912,9 @@ the runner invocation each caller read as a run is one.
1818
1912
 
1819
1913
  A command that also runs tests is false however it is spelled: `npx playwright install &&
1820
1914
  npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
1821
- reading a real run.
1915
+ reading a real run. So is a command carrying a segment this reader cannot place (a script it
1916
+ does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
1917
+ table can be accounted for, and "only prints" is a claim about the WHOLE command.
1822
1918
 
1823
1919
  ```typescript
1824
1920
  function commandRunsNoTests(command: string): boolean
@@ -2409,6 +2505,26 @@ function isCredentialExchangePath(path: string, authEndpoints?: readonly string[
2409
2505
 
2410
2506
  **Returns:** True when the path names a credential exchange.
2411
2507
 
2508
+ #### `hiddenBoxNote(items)`
2509
+
2510
+ The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
2511
+
2512
+ Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
2513
+ is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
2514
+ only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
2515
+ width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
2516
+ (`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
2517
+ note said only "the cause is in the page", and the run spent over four minutes rasterising an
2518
+ SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
2519
+
2520
+ ```typescript
2521
+ function hiddenBoxNote(items: readonly TraceArtifact[]): string | null
2522
+ ```
2523
+
2524
+ - `items` — The traces {@link parseTraceArtifacts} read.
2525
+
2526
+ **Returns:** The sentence, or null when no failure carries a hidden-box reading.
2527
+
2412
2528
  #### `failureTraceNote(items, target, earlierFailures, apiPrefixes, report)`
2413
2529
 
2414
2530
  The note a failed suite run's result carries: what each failing test's page actually sent, and
@@ -3020,6 +3136,32 @@ function isValidGlob(pattern: string): boolean
3020
3136
 
3021
3137
  **Returns:** `true` when the pattern contains only allowed characters.
3022
3138
 
3139
+ #### `expandBraceGlob(pattern)`
3140
+
3141
+ Expand ONE level of brace alternatives in a glob pattern, validating every
3142
+ expanded pattern.
3143
+
3144
+ Neither search tool expands braces itself: `grep --include` and `find` take
3145
+ the pattern as a single shell-quoted argument, so no shell ever sees it.
3146
+ `find_files {pattern:"*.{ts,tsx}"}` and `search_files {include:"*.{ts,tsx}"}`
3147
+ were both refused outright (xr-molecule-flash-m199 and m189), costing a round
3148
+ trip and pushing the agent to a narrower search. The group is expanded here
3149
+ instead — `search_files` passes one `--include` per alternative and
3150
+ `find_files` ORs one `-name`/`-path` predicate per alternative.
3151
+
3152
+ Bounded on purpose: one level of braces, alternatives split on `,`, at most
3153
+ {@link MAX_BRACE_GLOB_ALTERNATIVES}, none empty, and each EXPANDED pattern
3154
+ must still pass {@link isValidGlob} — so a shell metacharacter in any
3155
+ alternative (`*.{ts,$(rm)}`) is refused exactly as a bare one is.
3156
+
3157
+ ```typescript
3158
+ function expandBraceGlob(pattern: string): string[] | null
3159
+ ```
3160
+
3161
+ - `pattern` — The user-supplied glob.
3162
+
3163
+ **Returns:** Every pattern the value expands to, or null when it is not accepted (the caller reports its tool's glob error).
3164
+
3023
3165
  #### `findPatternArgs(pattern)`
3024
3166
 
3025
3167
  The `find` predicate for a `find_files` pattern.
@@ -3827,6 +3969,18 @@ run that passed, and no reader may treat it as one.
3827
3969
  const CUT_AT_BUDGET: 'cut_at_budget'
3828
3970
  ```
3829
3971
 
3972
+ #### `INLINE_SCRIPT_BUDGET_MS`
3973
+
3974
+ How long an inline interpreter program that only reads and writes local files may run.
3975
+
3976
+ Such work finishes in milliseconds; if it has not finished in twenty seconds it never will
3977
+ (a backtracking regex on a large file, a loop waiting on something that is not coming), so this
3978
+ is a runaway detector, not a performance budget.
3979
+
3980
+ ```typescript
3981
+ const INLINE_SCRIPT_BUDGET_MS: 20000
3982
+ ```
3983
+
3830
3984
  #### `TOOL_SCHEMAS`
3831
3985
 
3832
3986
  Canonical tool schemas shared by the agent runtime and documentation.
@@ -4019,6 +4173,26 @@ edited `app/src` while the run was still walking — which voids the run.
4019
4173
  const PLAYWRIGHT_FAILURES_INLINE_ENV: 'PLAYWRIGHT_LIST_PRINT_FAILURES_INLINE'
4020
4174
  ```
4021
4175
 
4176
+ #### `MAX_BRACE_GLOB_ALTERNATIVES`
4177
+
4178
+ The most alternatives one brace group may expand to. The point is to accept
4179
+ the ordinary multi-extension form a model writes from habit
4180
+ (`*.{ts,tsx,yml,json,md}`), not to grow a glob engine: a group is bounded so
4181
+ a pattern cannot turn one search into hundreds.
4182
+
4183
+ ```typescript
4184
+ const MAX_BRACE_GLOB_ALTERNATIVES: 16
4185
+ ```
4186
+
4187
+ #### `BRACE_GLOB_HINT`
4188
+
4189
+ The accepted brace form, named in every refusal so a refusal teaches the
4190
+ syntax rather than only reporting that the value was wrong.
4191
+
4192
+ ```typescript
4193
+ const BRACE_GLOB_HINT: string
4194
+ ```
4195
+
4022
4196
  #### `MAX_READ_SIZE`
4023
4197
 
4024
4198
  Max file size for read_file (5MB).
@@ -1 +1 @@
1
- {"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AA8RD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAwkBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAIjE;AAmCD;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAC9B,0FAA0F;IAC1F,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAE/E;AAmDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GACjB,MAAM,GAAG,IAAI,CAyDf;AA2FD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAC1C;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAoD7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}
1
+ {"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAgSD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAmoBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAIjE;AAmCD;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAC9B,0FAA0F;IAC1F,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAE/E;AAmDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GACjB,MAAM,GAAG,IAAI,CAyDf;AA4GD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAC1C;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAoD7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}
@@ -62,6 +62,7 @@ const LAUNCHERS = new Set([
62
62
  'nice',
63
63
  'nohup',
64
64
  'setsid',
65
+ 'stdbuf',
65
66
  'sudo',
66
67
  'time',
67
68
  'timeout',
@@ -268,6 +269,7 @@ const WRAPPER_VALUE_FLAGS = {
268
269
  env: ['-S', '--split-string', '-u', '--unset', '-C', '--chdir'],
269
270
  ionice: ['-c', '--class', '-n', '--classdata', '-p', '--pid', '-P', '--pgid', '-u', '--uid'],
270
271
  nice: ['-n', '--adjustment'],
272
+ stdbuf: ['-e', '-i', '-o'],
271
273
  sudo: [
272
274
  '-C',
273
275
  '--chdir',
@@ -408,22 +410,40 @@ function nestedCommand(words, from) {
408
410
  return null;
409
411
  }
410
412
  /**
411
- * Where the program starts in one segment's words, and the bound the segment puts on itself.
413
+ * Where the program starts in one segment's words, the bound the segment puts on itself, and the
414
+ * environment it binds for the program it runs.
415
+ *
416
+ * `bound` is every `NAME=value` word the walk steps over between the segment's start and its
417
+ * program: the leading words, and the ones behind an `env` wrapper (`env -i PATH=$PATH
418
+ * BASE_URL=https://… npx playwright test`). A shell passes those to the program in exactly the
419
+ * same way — `env` is the POSIX form of the leading-word form — so a reader that only saw the
420
+ * leading ones called a deployed walk local (X0 xr-molecule-flash-m204 +38:15: two green deployed
421
+ * walks were refused, 5.2 min).
412
422
  *
413
423
  * @param words - The segment's words, quotes kept.
414
424
  * @returns The index of the word in command position (one past the end when the segment runs
415
- * nothing), the command string of a `sh -c '<command>'` this segment turns out to be, and the
416
- * seconds its own `timeout` wrapper allows that command (`timeout 280 hurl …` → 280) or null.
425
+ * nothing), the command string of a `sh -c '<command>'` this segment turns out to be, the
426
+ * seconds its own `timeout` wrapper allows that command (`timeout 280 hurl …` → 280) or null,
427
+ * every `NAME=value` word bound for the program in the order the shell binds them, and the word
428
+ * index an assignment for the program belongs at when an `env` wrapper sits in front of it
429
+ * (`env -i … <here>npx`; a value written before `env -i` is cleared, so it must go after).
417
430
  * `program` names what runs when that is not the word itself's name — `node <path>/playwright`
418
431
  * is a `playwright` invocation, not a `node` one ({@link nodeScriptRunner}).
419
432
  */
420
433
  function commandStart(words) {
421
434
  let at = 0;
422
435
  let timeoutSeconds = null;
436
+ const bound = [];
437
+ let envInsert = null;
438
+ let envReset = false;
423
439
  for (let guard = 0; guard < MAX_WRAPPERS && at < words.length; guard++) {
424
440
  const word = words[at] ?? '';
425
441
  if (ENV_ASSIGNMENT.test(word)) {
442
+ bound.push(word);
426
443
  at += 1;
444
+ // Still the env wrapper's own region: an assignment for the program goes after it.
445
+ if (envInsert !== null)
446
+ envInsert = at;
427
447
  continue;
428
448
  }
429
449
  const name = programName(word);
@@ -435,14 +455,22 @@ function commandStart(words) {
435
455
  if (SHELLS.has(name)) {
436
456
  const nested = nestedCommand(words, at + 1);
437
457
  if (nested !== null)
438
- return { at, nested, timeoutSeconds, program: null };
458
+ return { at, nested, timeoutSeconds, program: null, bound, envInsert, envReset };
439
459
  at += 1 + flagWords(words, at + 1, name);
440
460
  continue;
441
461
  }
442
462
  if (PACKAGE_RUNNERS.has(name)) {
443
463
  const runner = packageRunnerOptions(words, at + 1, name);
444
464
  if (runner.nested !== null)
445
- return { at, nested: runner.nested, timeoutSeconds, program: null };
465
+ return {
466
+ at,
467
+ nested: runner.nested,
468
+ timeoutSeconds,
469
+ program: null,
470
+ bound,
471
+ envInsert,
472
+ envReset,
473
+ };
446
474
  at += 1 + runner.count;
447
475
  continue;
448
476
  }
@@ -451,7 +479,15 @@ function commandStart(words) {
451
479
  const subcommand = unquoted(words[at + 1] ?? '');
452
480
  const runner = packageRunnerOptions(words, at + 2, `${name} ${subcommand}`);
453
481
  if (runner.nested !== null)
454
- return { at, nested: runner.nested, timeoutSeconds, program: null };
482
+ return {
483
+ at,
484
+ nested: runner.nested,
485
+ timeoutSeconds,
486
+ program: null,
487
+ bound,
488
+ envInsert,
489
+ envReset,
490
+ };
455
491
  at += 2 + runner.count;
456
492
  continue;
457
493
  }
@@ -468,18 +504,38 @@ function commandStart(words) {
468
504
  const script = nodeScriptIndex(words, at + 1);
469
505
  const runner = script === null ? null : nodeScriptRunner(words[script] ?? '');
470
506
  return runner === null
471
- ? { at, nested: null, timeoutSeconds, program: null }
472
- : { at: script ?? at, nested: null, timeoutSeconds, program: runner };
507
+ ? { at, nested: null, timeoutSeconds, program: null, bound, envInsert, envReset }
508
+ : {
509
+ at: script ?? at,
510
+ nested: null,
511
+ timeoutSeconds,
512
+ program: runner,
513
+ bound,
514
+ envInsert,
515
+ envReset,
516
+ };
473
517
  }
474
518
  if (!LAUNCHERS.has(name))
475
- return { at, nested: null, timeoutSeconds, program: null };
519
+ return { at, nested: null, timeoutSeconds, program: null, bound, envInsert, envReset };
520
+ const from = at;
476
521
  at += 1 + flagWords(words, at + 1, name);
522
+ // Past `env` and its own options: an assignment for the program written here survives
523
+ // `env -i`, which clears everything before it.
524
+ if (name === 'env') {
525
+ envInsert = at;
526
+ // `env -i` clears the environment it inherited: the words bound before it are NOT the
527
+ // environment the program runs in, so a reader must not attribute them to the run.
528
+ if (words.slice(from + 1, at).some((flag) => flag === '-i' || flag === '--ignore-environment'))
529
+ envReset = true;
530
+ }
531
+ if (envReset)
532
+ bound.length = 0;
477
533
  if (name === 'timeout' && DURATION.test(words[at] ?? '')) {
478
534
  timeoutSeconds = durationSeconds(words[at] ?? '');
479
535
  at += 1;
480
536
  }
481
537
  }
482
- return { at, nested: null, timeoutSeconds, program: null };
538
+ return { at, nested: null, timeoutSeconds, program: null, bound, envInsert, envReset };
483
539
  }
484
540
  /**
485
541
  * A command with every heredoc BODY blanked to spaces, so its length and every offset in it are
@@ -1073,11 +1129,24 @@ function locatedSegmentsOf(command, depth) {
1073
1129
  const words = segmentWords(part.text);
1074
1130
  const start = commandStart(words);
1075
1131
  const leading = leadingAssignments(part.text);
1132
+ // Every `NAME=value` bound for the program the statement runs: its leading words AND the ones
1133
+ // an `env` wrapper carries (`env -i PATH=$PATH BASE_URL=… npx playwright test`). A shell binds
1134
+ // both to the program the same way, so one list holds both — a reader that saw only the
1135
+ // leading ones read a deployed walk as local (X0 xr-molecule-flash-m204 +38:15). `env -i`
1136
+ // clears what it inherited, so after it the wrapper's own words are the whole environment.
1137
+ const assignments = [
1138
+ ...new Set(start.envReset ? start.bound : [...leading.words, ...start.bound]),
1139
+ ];
1076
1140
  /** Every offset in this statement is an offset in the command it was written in. */
1077
1141
  const placed = {
1078
1142
  start: part.start,
1079
1143
  end: part.start + part.text.length,
1080
- assignmentOffset: part.start + leading.end,
1144
+ // After the statement's own leading `NAME=value` words — or, when an `env` wrapper stands
1145
+ // there, after that wrapper and its own options/assignments, because `env -i` clears what
1146
+ // comes before it (`env -i … <here>npx playwright test`).
1147
+ assignmentOffset: start.envInsert === null
1148
+ ? part.start + leading.end
1149
+ : part.start + (wordOffset(part.text, start.envInsert) ?? leading.end),
1081
1150
  programOffset: words[start.at] === undefined ? null : part.start + (wordOffset(part.text, start.at) ?? 0),
1082
1151
  };
1083
1152
  // The runner the word RUNS names, when that is not the word's own name (`node <path>/playwright`
@@ -1093,6 +1162,9 @@ function locatedSegmentsOf(command, depth) {
1093
1162
  continue;
1094
1163
  out.push({
1095
1164
  ...entry,
1165
+ // The variables this statement bound before the shell reached it are the nested
1166
+ // command's too: `env BASE_URL=… sh -c '… npx playwright test …'` passes them down.
1167
+ assignments: [...new Set([...assignments, ...entry.assignments])],
1096
1168
  // A nested statement's positions are positions in the nested string, not in this
1097
1169
  // command. The FIRST one keeps this statement's own separator: the shell reads the
1098
1170
  // nested string where this statement stands.
@@ -1115,10 +1187,10 @@ function locatedSegmentsOf(command, depth) {
1115
1187
  if (word === undefined) {
1116
1188
  // Only wrappers and assignments: nothing this statement runs, but the variables it sets
1117
1189
  // persist in the shell the rest of the command runs in.
1118
- if (leading.words.length > 0)
1190
+ if (assignments.length > 0)
1119
1191
  out.push({
1120
1192
  segment: null,
1121
- assignments: leading.words,
1193
+ assignments,
1122
1194
  ...placed,
1123
1195
  programOffset: null,
1124
1196
  separator: part.before,
@@ -1134,7 +1206,7 @@ function locatedSegmentsOf(command, depth) {
1134
1206
  text: [program, ...args].join(' '),
1135
1207
  timeoutSeconds: start.timeoutSeconds,
1136
1208
  },
1137
- assignments: leading.words,
1209
+ assignments,
1138
1210
  ...placed,
1139
1211
  separator: part.before,
1140
1212
  terminator: part.after,
package/dist/cut-run.d.ts CHANGED
@@ -161,6 +161,12 @@ export interface CutRun {
161
161
  noun: 'files' | 'tests';
162
162
  /** The last thing the run printed — the line a reader looks at first. */
163
163
  doing: string | null;
164
+ /**
165
+ * The interpreter of an inline program that only touches local files (`python3`, `node`, …), or
166
+ * null. Such a program is stopped by its short budget because it is stuck, never because the work
167
+ * is slow, so the sentence must not send it to the background (xr-molecule-flash-m200 +03:45).
168
+ */
169
+ inlineScript?: string | null;
164
170
  }
165
171
  /**
166
172
  * The run a command's result records as stopped by a budget, or null when the result is a run that
@@ -213,7 +219,9 @@ export declare function cutRunFacts(result: unknown): CutRun | null;
213
219
  * the counts come from the runner's own output ({@link runProgress}), and a `total` the caller
214
220
  * read elsewhere (the suite's own file count) fills in the "of N" it can then state. The sentence
215
221
  * says how many tests the stop left unrun — a number a reader cannot round up to "the suite
216
- * passed" — and hands over the one move that survives a long run: run it in the background.
222
+ * passed" — and hands over the one move that survives a long run: run it in the background. An
223
+ * inline program that only touches local files is the exception ({@link CutRun.inlineScript}): it
224
+ * is not slow, it is stuck, and the background is the one place it must not be sent.
217
225
  *
218
226
  * @param run - The stopped run.
219
227
  * @returns The sentence.
@@ -1 +1 @@
1
- {"version":3,"file":"cut-run.d.ts","sourceRoot":"","sources":["../src/cut-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH;;;GAGG;AACH,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAE5C,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,aAAa,CAAA;AAE1D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CAGhE;AAiBD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAkDD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA4B9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,IAAI,CAIf;AAED,0BAA0B;AAC1B,MAAM,MAAM,KAAK;AACf,sEAAsE;AACpE,aAAa;AACf,oDAAoD;GAClD,iBAAiB;AACnB;;;;GAIG;GACD,cAAc,CAAA;AAElB,wDAAwD;AACxD,MAAM,WAAW,MAAM;IACrB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,kFAAkF;IAClF,SAAS,EAAE,KAAK,CAAA;IAChB;;;;;;OAMG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,qCAAqC;IACrC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,yEAAyE;IACzE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,aAAa,SAAI,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAuFf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAiB1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiClD"}
1
+ {"version":3,"file":"cut-run.d.ts","sourceRoot":"","sources":["../src/cut-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAMH;;;GAGG;AACH,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAE5C,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,aAAa,CAAA;AAE1D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CAGhE;AAiBD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAkDD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA4B9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,IAAI,CAIf;AAED,0BAA0B;AAC1B,MAAM,MAAM,KAAK;AACf,sEAAsE;AACpE,aAAa;AACf,oDAAoD;GAClD,iBAAiB;AACnB;;;;GAIG;GACD,cAAc,CAAA;AAElB,wDAAwD;AACxD,MAAM,WAAW,MAAM;IACrB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,kFAAkF;IAClF,SAAS,EAAE,KAAK,CAAA;IAChB;;;;;;OAMG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,qCAAqC;IACrC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,yEAAyE;IACzE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,aAAa,SAAI,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CA4Ff;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkB1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqClD"}
package/dist/cut-run.js CHANGED
@@ -33,6 +33,7 @@
33
33
  * @module
34
34
  */
35
35
  import { commandTimeoutSeconds } from './command-position.js';
36
+ import { inlineLocalScriptCommand } from './inline-script.js';
36
37
  import { isSuiteRunCommand } from './utilities.js';
37
38
  /**
38
39
  * The state a result carries when a budget stopped its command: a run that did not finish is not a
@@ -287,6 +288,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
287
288
  if (summary?.finished)
288
289
  return null;
289
290
  const suite = isSuiteRunCommand(command);
291
+ const inlineScript = inlineLocalScriptCommand(command);
290
292
  const withheld = /\|\s*(?:tail|head|grep|egrep|fgrep|sort|uniq|wc|jq)\b/.test(command);
291
293
  const progress = runProgress(detected, suite, summary);
292
294
  const doing = lastPrinted(text);
@@ -315,6 +317,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
315
317
  exitCode,
316
318
  ...progress,
317
319
  doing,
320
+ inlineScript,
318
321
  };
319
322
  }
320
323
  if (declared !== null && ownBoundStops && elapsedMs >= atBoundMs(declared)) {
@@ -326,6 +329,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
326
329
  exitCode: withheld || exitCode === 0 ? null : exitCode,
327
330
  ...progress,
328
331
  doing,
332
+ inlineScript,
329
333
  };
330
334
  }
331
335
  // The tool's budget with its code masked: the run used the whole budget and the pipeline left
@@ -338,6 +342,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
338
342
  exitCode: exitCode === 0 ? null : exitCode,
339
343
  ...progress,
340
344
  doing,
345
+ inlineScript,
341
346
  };
342
347
  }
343
348
  // The runner printed a summary that does not account for the total it announced, and neither
@@ -354,6 +359,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
354
359
  exitCode: withheld || exitCode === 0 ? null : exitCode,
355
360
  ...progress,
356
361
  doing,
362
+ inlineScript,
357
363
  };
358
364
  }
359
365
  return null;
@@ -390,6 +396,7 @@ export function cutRunFacts(result) {
390
396
  total: typeof c.total === 'number' ? c.total : null,
391
397
  noun: c.noun === 'tests' ? 'tests' : 'files',
392
398
  doing: typeof c.doing === 'string' && c.doing ? c.doing : null,
399
+ inlineScript: typeof c.inlineScript === 'string' && c.inlineScript ? c.inlineScript : null,
393
400
  };
394
401
  }
395
402
  /**
@@ -400,7 +407,9 @@ export function cutRunFacts(result) {
400
407
  * the counts come from the runner's own output ({@link runProgress}), and a `total` the caller
401
408
  * read elsewhere (the suite's own file count) fills in the "of N" it can then state. The sentence
402
409
  * says how many tests the stop left unrun — a number a reader cannot round up to "the suite
403
- * passed" — and hands over the one move that survives a long run: run it in the background.
410
+ * passed" — and hands over the one move that survives a long run: run it in the background. An
411
+ * inline program that only touches local files is the exception ({@link CutRun.inlineScript}): it
412
+ * is not slow, it is stuck, and the background is the one place it must not be sent.
404
413
  *
405
414
  * @param run - The stopped run.
406
415
  * @returns The sentence.
@@ -417,11 +426,15 @@ export function cutRunSentence(run) {
417
426
  // from being sent to the background again — when its own `timeout` is what stopped it, that
418
427
  // prefix is the thing to drop (X1 xr-molecule-flash-m143 +24:45: three cut sentences in one run
419
428
  // told a background walk to "run it in the background", which is where it already was).
420
- const background = run.background
421
- ? run.stoppedBy === 'command-timeout'
422
- ? `drop the \`timeout\` prefix and send it again — in the background a run is bounded by \`wait_for_task\` and the turn's deadline, not by this tool's ceiling, so the bound you wrote is what killed it`
423
- : 'send it again when you are ready'
424
- : 'run it in the background (run_in_background + wait_for_task)';
429
+ const background = run.inlineScript
430
+ ? // An inline local-file program finished in milliseconds or is stuck; backgrounding it only
431
+ // moves the hang (xr-molecule-flash-m200 +03:45). The full rewrite advice rides the error.
432
+ 'do not run it in the background — the transform is stuck, not slow'
433
+ : run.background
434
+ ? run.stoppedBy === 'command-timeout'
435
+ ? `drop the \`timeout\` prefix and send it again — in the background a run is bounded by \`wait_for_task\` and the turn's deadline, not by this tool's ceiling, so the bound you wrote is what killed it`
436
+ : 'send it again when you are ready'
437
+ : 'run it in the background (run_in_background + wait_for_task)';
425
438
  const missing = run.ran !== null && run.total !== null ? run.total - run.ran : null;
426
439
  const lead = missing !== null
427
440
  ? `Ran ${run.ran} of ${run.total} ${run.noun} in ${run.seconds} s and was stopped by ${by} — the ${missing} it never reached ${missing === 1 ? 'has' : 'have'} not run; ${background}`
package/dist/index.d.ts CHANGED
@@ -135,6 +135,7 @@ export * from './browser-guard.js';
135
135
  export * from './command-position.js';
136
136
  export * from './cut-run.js';
137
137
  export * from './failure-kind.js';
138
+ export * from './inline-script.js';
138
139
  export * from './input-normalizer.js';
139
140
  export * from './line-diff.js';
140
141
  export * from './no-test-run.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
package/dist/index.js CHANGED
@@ -135,6 +135,7 @@ export * from './browser-guard.js';
135
135
  export * from './command-position.js';
136
136
  export * from './cut-run.js';
137
137
  export * from './failure-kind.js';
138
+ export * from './inline-script.js';
138
139
  export * from './input-normalizer.js';
139
140
  export * from './line-diff.js';
140
141
  export * from './no-test-run.js';