@molecule/api-ai-tools 1.5.19 → 1.6.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 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-16T12:22:32.984Z
6
+ Generated: 2026-10-16T15:16:34.345Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -58,6 +58,24 @@ npm install @molecule/api-ai-tools @molecule/api-ai
58
58
 
59
59
  ### Interfaces
60
60
 
61
+ #### `CommandSegment`
62
+
63
+ One statement or pipeline stage of a shell command: what it runs, and with what.
64
+
65
+ ```typescript
66
+ interface CommandSegment {
67
+ /**
68
+ * The word in command position, its directory removed (`/usr/bin/hurl` → `hurl`): the program
69
+ * the shell would run once whatever wrappers stood in front of it are taken away.
70
+ */
71
+ program: string
72
+ /** The words after it, as written — quotes kept, so a quoted `"a|b"` is one word. */
73
+ args: readonly string[]
74
+ /** The program and its arguments as one line — what a runner pattern is matched against. */
75
+ text: string
76
+ }
77
+ ```
78
+
61
79
  #### `NormalizableSchema`
62
80
 
63
81
  The JSON-Schema-ish shape every tool in this package declares.
@@ -358,13 +376,44 @@ A cause repeating among a running suite's failures.
358
376
 
359
377
  ```typescript
360
378
  interface RepeatingFailure {
361
- /** How many failures share the signature. */
379
+ /**
380
+ * How many DISTINCT tests share the signature — a test printed twice (two reporters in the
381
+ * log) or failed twice (a retry) is one test.
382
+ */
362
383
  count: number
363
384
  /** The shared error line, when the log carries the failures' bodies (`line` reporter). */
364
385
  message: string | null
365
386
  /** The shared duration in seconds, when the failures line up by how long they ran. */
366
387
  seconds: number | null
367
- /** The failures that share it, in log order. */
388
+ /** The distinct tests that share it, in log order. */
389
+ tests: FailedTest[]
390
+ }
391
+ ```
392
+
393
+ #### `UniversalCause`
394
+
395
+ A repeating cause that is failing the WHOLE run — see {@link universalCause}.
396
+
397
+ ```typescript
398
+ interface UniversalCause {
399
+ /** How many distinct tests share it. */
400
+ count: number
401
+ /** The distinct spec files they live in, in the order the log named them. */
402
+ files: string[]
403
+ }
404
+ ```
405
+
406
+ #### `FailureCause`
407
+
408
+ One cause of failure in a run, and the tests it hit.
409
+
410
+ ```typescript
411
+ interface FailureCause {
412
+ /** The error line its tests all reported, or null when only the duration ties them together. */
413
+ message: string | null
414
+ /** The duration they shared in seconds, or null when the error line is the signature. */
415
+ seconds: number | null
416
+ /** The distinct tests it hit, in the order the log named them. */
368
417
  tests: FailedTest[]
369
418
  }
370
419
  ```
@@ -419,6 +468,37 @@ function createSandboxBackend(sandbox: SandboxLike, projectRoot?: string): Execu
419
468
 
420
469
  **Returns:** A backend that proxies all filesystem calls into the sandbox.
421
470
 
471
+ #### `commandSegments(command)`
472
+
473
+ The segments of a command, each with its program and arguments — the answer every classifier
474
+ that reads a command as "this runs the suite" is asking for.
475
+
476
+ ```typescript
477
+ function commandSegments(command: string): CommandSegment[]
478
+ ```
479
+
480
+ - `command` — The shell command.
481
+
482
+ **Returns:** One entry per statement or pipeline stage that runs something, in order.
483
+
484
+ #### `runsInCommandPosition(command, pattern)`
485
+
486
+ Whether any segment of the command runs a program whose command-position text matches
487
+ `pattern` — the one question a runner-kind table asks.
488
+
489
+ The pattern is matched against `program` and its arguments as one line, so it must be
490
+ anchored at `^`; it must not carry the `g` flag (a pattern shared between calls would then
491
+ carry its own match position from one segment to the next).
492
+
493
+ ```typescript
494
+ function runsInCommandPosition(command: string, pattern: RegExp): boolean
495
+ ```
496
+
497
+ - `command` — The shell command.
498
+ - `pattern` — An anchored pattern for the runner's invocation.
499
+
500
+ **Returns:** True when a segment in command position matches.
501
+
422
502
  #### `normalizeToolInput(schema, raw)`
423
503
 
424
504
  Move known aliases onto the declared parameter names and report what is still
@@ -499,6 +579,33 @@ function guardAITool(tool: T, options?: GuardOptions): T
499
579
 
500
580
  **Returns:** A copy with a guarded `execute`.
501
581
 
582
+ #### `commandRunsNoTests(command)`
583
+
584
+ Whether a shell command invokes a test runner and EVERY such invocation only prints — so the
585
+ command runs no test at all.
586
+
587
+ A runner counts only where a shell would RUN it ({@link commandSegments}): the word in command
588
+ position of a segment, after its wrapper prefixes. A runner named in an argument is not an
589
+ invocation — `head -30 run-api-tests-hurl.sh` and `grep -n playwright e2e/README.md` name a
590
+ file to read, and a `--list` written inside a file the command writes is not a flag anyone runs
591
+ with (flash-m81).
592
+
593
+ A command that invokes no runner is NOT one of these (false): "the command runs no test" is
594
+ then true for `ls`, which is not the question any caller is asking. The question is whether
595
+ the runner invocation each caller read as a run is one.
596
+
597
+ A command that also runs tests is false however it is spelled: `npx playwright install &&
598
+ npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
599
+ reading a real run.
600
+
601
+ ```typescript
602
+ function commandRunsNoTests(command: string): boolean
603
+ ```
604
+
605
+ - `command` — The shell command as the executor sent it.
606
+
607
+ **Returns:** True when the command invokes a runner only to print.
608
+
502
609
  #### `discoverSkills(backend)`
503
610
 
504
611
  Discover skills from a project directory.
@@ -732,16 +839,15 @@ killed (X1 run flash-m15 lost 20 of its 60 minutes this way, 2026-10-15):
732
839
  often `html`, which prints nothing per test. `--reporter=list` is added
733
840
  when the playwright run is the command's last step and nothing is piped or
734
841
  redirected from it: it writes each result as it finishes, and it is the
735
- shape the platform reads a background suite back in (`suite-fail-fast.ts`'s
736
- fail-fast note and the Tests bar both parse `list` lines). A run that names
737
- `--reporter=line` is switched to `list` for the same reason — `line` writes
738
- `[n/N] <title>` with no ✓/✘ mark and no duration, so on X1 flash-m62 an
739
- 8.3-minute walk of one suite could never trip the fail-fast note.
842
+ shape the platform reads a background suite back in.
740
843
 
741
- A run being ITERATED on (capped, narrowed, or by file) gets `list,line` instead
742
- of `list` alone. `list` holds every failure's error for the summary at the end,
743
- so mid-run its log says only how long each failure took; `line` writes the body
744
- as the failure happens, and a look at a running suite (`wait_for_task`'s
844
+ The run ends up with exactly ONE reporter that prints each failure — `list`. A run that names
845
+ any other printing reporter has it REPLACED, never joined: two of them write every test result
846
+ into the log twice, which on X1 flash-m83 (2026-10-16) made a `--reporter=list,line` run (its
847
+ `line` added by this function one run earlier) report two distinct failures as "4 of this run's
848
+ tests have already failed the same way", and the executor stopped a 139-test walk at test 21
849
+ with a single cause in hand. `line` alone is no use either: it writes `[n/N] <title>` with no
850
+ ✓/✘ mark and no duration, so on X1 flash-m62 an 8.3-minute walk of one suite could never trip
745
851
 
746
852
  _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
747
853
 
@@ -758,6 +864,10 @@ function streamingBackgroundCommand(command: string): { command: string; changes
758
864
  Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
759
865
  — rather than a build, an install or a one-off script.
760
866
 
867
+ A command that invokes a runner only to print is not one of these
868
+ ({@link commandRunsNoTests}): `npx playwright test --list` prints the tests it would run and
869
+ exits, and `npx playwright install`, `vitest list` and `hurl --help` start no test either.
870
+
761
871
  ```typescript
762
872
  function isSuiteRunCommand(command: string): boolean
763
873
  ```
@@ -766,6 +876,27 @@ function isSuiteRunCommand(command: string): boolean
766
876
 
767
877
  **Returns:** True when the command starts one of those runners.
768
878
 
879
+ #### `isPlaywrightRunCommand(command)`
880
+
881
+ Whether a command starts a Playwright run — a `playwright test` in command position, in a form
882
+ that runs something.
883
+
884
+ `npx playwright test --list` matched every `\bplaywright\s+test\b` test in the platform and was
885
+ then treated as a run: held (flash-m79's config check), backgrounded for a run that outlasts
886
+ the tool's ceiling, and rewritten with `--reporter`. A list takes two seconds, prints its lines
887
+ and exits, so it is none of those; {@link commandRunsNoTests} is what decides.
888
+
889
+ A `playwright test` named as an argument is not a run either (`head -30 run-tests.sh`,
890
+ `grep -n "playwright test" e2e/README.md`): the command that runs is the reader.
891
+
892
+ ```typescript
893
+ function isPlaywrightRunCommand(command: string): boolean
894
+ ```
895
+
896
+ - `command` — The shell command.
897
+
898
+ **Returns:** True when the command starts a Playwright run.
899
+
769
900
  #### `suiteRunPriorityLaunch(command, launch)`
770
901
 
771
902
  Start a background suite run at lower CPU and IO priority, so the sandbox it
@@ -947,6 +1078,38 @@ function sliceLines(
947
1078
 
948
1079
  **Returns:** The window plus its position, or null when neither was given.
949
1080
 
1081
+ #### `serializedChars(text)`
1082
+
1083
+ A file's text as the request counts it: the length of its JSON string.
1084
+
1085
+ JSON escaping (`\n` → `\\n`, `"` → `\"`) costs characters, and the request
1086
+ side counts the SERIALIZED tool result. A window of 59,000 characters of
1087
+ source — 1,200 lines — arrives as ~60,200 characters of JSON, over the
1088
+ ceiling it was sized for, so a window is measured with this rather than by
1089
+ its raw text length.
1090
+
1091
+ ```typescript
1092
+ function serializedChars(text: string): number
1093
+ ```
1094
+
1095
+ - `text` — The text.
1096
+
1097
+ **Returns:** Its serialized length, without the surrounding quotes.
1098
+
1099
+ #### `linesWithinSerialized(content, chars)`
1100
+
1101
+ How many leading lines of a file fit inside a budget of SERIALIZED chars,
1102
+ the way `read_file` sizes a window (see {@link serializedChars}).
1103
+
1104
+ ```typescript
1105
+ function linesWithinSerialized(content: string, chars: number): number
1106
+ ```
1107
+
1108
+ - `content` — The file's full text.
1109
+ - `chars` — The budget in serialized chars.
1110
+
1111
+ **Returns:** The line count, at least one.
1112
+
950
1113
  #### `looksLikeHtml(text)`
951
1114
 
952
1115
  Whether `text` is an HTML document (a doctype or an `<html` root tag).
@@ -1069,41 +1232,117 @@ The cause repeating among a running suite's failures, or null.
1069
1232
 
1070
1233
  Playwright's `list` reporter prints one line per test as it finishes — its mark, its title
1071
1234
  path and how long it ran — and defers each failure's error to the summary at the end, so
1072
- mid-run the only signature those lines carry is the duration. A `line` reporter prints the
1073
- failure bodies as they happen, and there the repeated error message itself is the signature,
1074
- which is stronger (it needs no assumption about how long a failure should take), so the
1075
- message is looked for first.
1235
+ mid-run the only signature those lines carry is the duration. Where the log carries the
1236
+ failure bodies (a `line` reporter's output, or a finished run's summary) the repeated error
1237
+ message itself is the signature, which is stronger (it needs no assumption about how long a
1238
+ failure should take), so the message is looked for first.
1076
1239
 
1077
1240
  Either way the point is the same: a suite whose failures repeat one cause spends its whole
1078
1241
  runtime proving that one cause, and the executor watching it can be told so at the third
1079
1242
  failure instead of at the last. X1 flash-m76 walked 15 tests in 4.0 minutes where 15.6 s
1080
1243
  each was the test timeout and one missing `navigate('/')` was the cause of all of them.
1081
1244
 
1245
+ The quorum is of DISTINCT tests — {@link REPEATING_FAILURE_MIN} of them — because a log that
1246
+ prints each failure twice (a second reporter, a retry) had X1 flash-m83 told that four of its
1247
+ tests had failed the same way when two had, so the executor stopped a 139-test walk at test 21
1248
+ with one cause in hand (2026-10-16).
1249
+
1082
1250
  ```typescript
1083
1251
  function repeatingFailure(log: string): RepeatingFailure | null
1084
1252
  ```
1085
1253
 
1086
1254
  - `log` — The log so far.
1087
1255
 
1088
- **Returns:** The repeating cause, or null when no signature has {@link REPEATING_FAILURE_MIN} failures behind it.
1256
+ **Returns:** The repeating cause, or null when no signature has {@link REPEATING_FAILURE_MIN} distinct tests behind it.
1257
+
1258
+ #### `universalCause(log, found)`
1259
+
1260
+ Whether a repeating cause is failing the whole run — the one case where stopping it is the
1261
+ right call.
1262
+
1263
+ The test is: the cause accounts for every failure the log names, it has hit
1264
+ {@link UNIVERSAL_CAUSE_FILES} spec files or more, and nothing has passed since its first
1265
+ failure. An app that is not up, a 5xx on every request, or a selector that is missing on every
1266
+ page all look like that, and every remaining test will fail the same way — so the run's
1267
+ remaining minutes measure nothing. Anything less is NOT this: a cause that stops at one file,
1268
+ or that leaves other tests passing, or that shares the run with failures of another shape,
1269
+ leaves the run with something to say, and the run is the only thing that will say it. X1
1270
+ flash-m83 (2026-10-16) stopped eight local runs at their first repeating cause over 14.5
1271
+ minutes and never finished one; three of its deploys each fixed a single cause that a finished
1272
+ run would have listed alongside the others.
1273
+
1274
+ ```typescript
1275
+ function universalCause(log: string, found: RepeatingFailure): UniversalCause | null
1276
+ ```
1277
+
1278
+ - `log` — The log so far.
1279
+ - `found` — The repeating cause {@link repeatingFailure} found in it.
1280
+
1281
+ **Returns:** The cause's count and files, or null when the run is not failing whole.
1282
+
1283
+ #### `failuresByCause(log)`
1284
+
1285
+ Every failure a run names, grouped by the cause behind it — one group per cause, with the
1286
+ tests it hit.
1287
+
1288
+ The point is that ONE run yields EVERY cause. A Playwright report prints its failures one
1289
+ after another with no statement of which of them share a cause, so a run of 19 failures is 19
1290
+ bodies to read; X1 flash-m83 (2026-10-16) spent three deploys (6.7 minutes) each fixing a
1291
+ single cause, two of them found by runs stopped before they walked the rest.
1292
+
1293
+ The signature is the failures' own error line where the log carries their bodies (a finished
1294
+ run, or a `line` reporter), and how long they took where it does not (a still-running `list`
1295
+ log). A test the log printed twice, or failed twice, is one test.
1296
+
1297
+ ```typescript
1298
+ function failuresByCause(log: string): FailureCause[]
1299
+ ```
1300
+
1301
+ - `log` — The log of a finished or running run.
1302
+
1303
+ **Returns:** The causes, the one with the most tests first.
1304
+
1305
+ #### `failureCausesNote(log)`
1306
+
1307
+ The one-line-per-cause summary of a finished run's failures, or null when the run named fewer
1308
+ than two of them.
1309
+
1310
+ This is what makes a run worth finishing: every cause the run found, in the result the
1311
+ executor reads, so one round of fixes covers all of them rather than the one cause a run
1312
+ stopped early happened to reach.
1313
+
1314
+ ```typescript
1315
+ function failureCausesNote(log: string): string | null
1316
+ ```
1317
+
1318
+ - `log` — The finished run's output.
1319
+
1320
+ **Returns:** The note, or null.
1089
1321
 
1090
1322
  #### `repeatingFailureNote(found, o)`
1091
1323
 
1092
- The sentence that names a cause repeating among a running suite's failures, and the command
1093
- that stops the run so the cause can be fixed first.
1324
+ The sentence that names a cause repeating among a running suite's failures, mid-run.
1094
1325
 
1095
1326
  Kept here, beside {@link repeatingFailure}, so the two stay in step: every word below is about
1096
- what that function could actually see in the log.
1327
+ what that function could actually see in the log — including the count, which is of DISTINCT
1328
+ tests, so the note never names one or two tests as a cause.
1329
+
1330
+ The note names the cause early so the executor can START FIXING while the run keeps walking,
1331
+ and it asks for the run to be stopped only when {@link universalCause} says the cause is
1332
+ failing the whole run. X1 flash-m76 (2026-10-16) spent 4.0 minutes walking 15 tests whose one
1333
+ cause was certain at the third, 47 s in, and the note answers exactly that; X1 flash-m83 then
1334
+ stopped eight runs at their first repeating cause in 14.5 minutes and never finished one, so
1335
+ the note must not say to stop when the run still has other causes to name.
1097
1336
 
1098
1337
  ```typescript
1099
1338
  function repeatingFailureNote(
1100
1339
  found: RepeatingFailure,
1101
- o: { taskId: string; log: string; stop: string },
1340
+ o: { taskId: string; log: string; stop: string; universal?: UniversalCause | null },
1102
1341
  ): string
1103
1342
  ```
1104
1343
 
1105
1344
  - `found` — What {@link repeatingFailure} found.
1106
- - `o` — The run's facts: its `taskId`, its `log` path, and `stop` — the shell command that stops it (the pids the platform's own pid file leads to).
1345
+ - `o` — The run's facts: its `taskId`, its `log` path, `stop` — the shell command that stops it, printed only for a {@link UniversalCause} — and `universal`, what {@link universalCause} found (absent means the run is not failing whole).
1107
1346
 
1108
1347
  **Returns:** The note.
1109
1348
 
@@ -1230,34 +1469,67 @@ const MAX_SEARCH_CONTEXT_LINES: 40
1230
1469
  #### `MAX_BATCH_READ_FILES`
1231
1470
 
1232
1471
  Most files one batched `read_file` call returns. The cap exists so a survey
1233
- of a large tree cannot swallow the turn's context in one result; the tool
1234
- reports where it stopped so the model continues from there.
1472
+ of a large tree cannot swallow the turn's context in one result; every path
1473
+ past it is named in the result's `note` and listed with its own `error`, so
1474
+ the model continues from there.
1235
1475
 
1236
1476
  ```typescript
1237
1477
  const MAX_BATCH_READ_FILES: 25
1238
1478
  ```
1239
1479
 
1240
- #### `MAX_BATCH_READ_BYTES`
1480
+ #### `MAX_READ_CALL_CHARS`
1481
+
1482
+ The most one `read_file` RESULT may carry, in chars of its JSON — every file
1483
+ the call returns, the envelope and the note included. That is how the request
1484
+ side counts a tool result, so it is how this tool counts one too
1485
+ ({@link serializedChars}).
1486
+
1487
+ One tool result is ONE block in the request, and the request side caps a
1488
+ block: molecule-dev cuts any block over `MAX_TAIL_BLOCK_CHARS` (60,000) in
1489
+ place, keeping ~30 KB of each end. A batched read that crossed it lost its
1490
+ MIDDLE — the cut lands between two `"content"` values, so whole files fell
1491
+ out and the note in their place said only "N characters cut here" — while
1492
+ this tool's own note had promised each of those files that it "fits in one
1493
+ call, so it is not paged": X0 flash-m81 (2026-10-16) read ten e2e specs in
1494
+ one call (121,467 chars), lost the middle six, and bought them again in six
1495
+ further calls (52,908 chars).
1496
+
1497
+ So the CALL gets the ceiling the request gives it. Whole files are returned
1498
+ up to it; the paths that did not fit are named in `note` and listed in
1499
+ `files` with their own `error`, the shape every other entry has. No file is
1500
+ ever cut, and no read result reaches the request-side cut. A test in
1501
+ molecule-dev's `context-fit` asserts the two ceilings still agree.
1502
+
1503
+ ```typescript
1504
+ const MAX_READ_CALL_CHARS: 60000
1505
+ ```
1506
+
1507
+ #### `READ_RESULT_ENVELOPE_CHARS`
1508
+
1509
+ Room inside one read result for what is not file content: the `path`, the
1510
+ offset/line fields, and the note.
1241
1511
 
1242
- Total content bytes one batched `read_file` call returns, across all files.
1243
- The per-file {@link MAX_READ_SIZE} still applies to each one.
1512
+ A batch sizes each file against what the call has left
1513
+ ({@link MAX_READ_CALL_CHARS} less this), so the entry that does not fit is
1514
+ caught here rather than by the request-side cut.
1244
1515
 
1245
1516
  ```typescript
1246
- const MAX_BATCH_READ_BYTES: number
1517
+ const READ_RESULT_ENVELOPE_CHARS: 1000
1247
1518
  ```
1248
1519
 
1249
1520
  #### `MAX_READ_RETURN_CHARS`
1250
1521
 
1251
1522
  The most one read_file call returns for ONE file when no window was asked
1252
- for, in chars. Larger files come back as their first window with a note
1253
- saying how to read the rest.
1523
+ for, in serialized chars: larger files come back as their first window, with
1524
+ a note saying how to read the rest.
1254
1525
 
1255
1526
  MAX_READ_SIZE (5 MB) is a bound on what the tool will open, not on what it
1256
1527
  should hand a model: a single batched read that returned a 1.2 MB README
1257
1528
  put the next three provider calls at 366k tokens (~330k of them fresh)
1258
- against a 120k-token context cap (X0 run x5, call 26). ~80 KB is ~20–25k
1259
- tokens — a fifth of that cap, and more than any one file read usually
1260
- needs.
1529
+ against a 120k-token context cap (X0 run x5, call 26). This is the call's
1530
+ own ceiling ({@link MAX_READ_CALL_CHARS}) less the room its envelope takes,
1531
+ so one read result — one file or twenty-five — fits inside what the request
1532
+ side carries without being cut.
1261
1533
 
1262
1534
  ```typescript
1263
1535
  const MAX_READ_RETURN_CHARS: number
@@ -1380,7 +1652,8 @@ const REPEATING_CAUSE_TAIL_BYTES: 40000
1380
1652
 
1381
1653
  #### `REPEATING_FAILURE_MIN`
1382
1654
 
1383
- Failures that must share one signature before a running suite's repeating cause is named.
1655
+ DISTINCT tests that must share one signature before a running suite's repeating cause is named.
1656
+ A test the log prints twice, or a test that failed twice, is still one test.
1384
1657
 
1385
1658
  ```typescript
1386
1659
  const REPEATING_FAILURE_MIN: 3
@@ -1394,6 +1667,35 @@ How many of a repeating cause's failures the note lists before it says "and N mo
1394
1667
  const REPEATING_FAILURE_LISTED: 5
1395
1668
  ```
1396
1669
 
1670
+ #### `FAILURE_CAUSES_LISTED`
1671
+
1672
+ How many causes a finished run's summary lists before it says "and N more".
1673
+
1674
+ ```typescript
1675
+ const FAILURE_CAUSES_LISTED: 5
1676
+ ```
1677
+
1678
+ #### `FAILURE_CAUSE_TESTS_LISTED`
1679
+
1680
+ How many of one cause's tests that summary names before it says "and N more".
1681
+
1682
+ ```typescript
1683
+ const FAILURE_CAUSE_TESTS_LISTED: 6
1684
+ ```
1685
+
1686
+ #### `UNIVERSAL_CAUSE_FILES`
1687
+
1688
+ Distinct spec FILES a repeating cause must span — with every failure sharing it and nothing
1689
+ passing since — before a run is called broken for the whole suite.
1690
+
1691
+ One file's failures can be that file's own setup, so a cause confined to one file is never
1692
+ this. Across {@link UNIVERSAL_CAUSE_FILES} files, on the other hand, whatever it is is a
1693
+ property of the run rather than of a page.
1694
+
1695
+ ```typescript
1696
+ const UNIVERSAL_CAUSE_FILES: 3
1697
+ ```
1698
+
1397
1699
  #### `PARSE_CHECKABLE`
1398
1700
 
1399
1701
  Files a parse check can vouch for cheaply right after a write.
@@ -1428,9 +1730,13 @@ Peer dependencies:
1428
1730
  - **`read_file` takes `paths` as well as `path`.** An agent that issues one
1429
1731
  tool call per model round-trip pays a full round-trip per file, so a project
1430
1732
  survey costs minutes; batched, the same reads cost one call each 25 files
1431
- (`MAX_BATCH_READ_FILES` / `MAX_BATCH_READ_BYTES`, which report where they
1432
- stopped). Each entry carries its own content or its own error, so one bad
1433
- path never fails the batch.
1733
+ (`MAX_BATCH_READ_FILES`). One result is capped at `MAX_READ_CALL_CHARS`, the
1734
+ ceiling the request side puts on a single tool result: whole files are
1735
+ returned up to it and every path past that is named in `note` and listed as
1736
+ `not returned`, so a file is never handed back in part — the shape that lost
1737
+ the middle of a ten-file batch to the request-side cut (X0 flash-m81). Each
1738
+ entry carries its own content or its own error, so one bad path never fails
1739
+ the batch.
1434
1740
  - **`edit_file` shows the result, so the model need not re-read.** A
1435
1741
  successful edit returns `excerpt`: each edited region as it now reads, with
1436
1742
  1-based line numbers (`N: text`) and a few lines of context, capped at
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Command position: which program a shell segment actually RUNS, and which words are only
3
+ * arguments to something else.
4
+ *
5
+ * Every classifier that read a command as "this runs the suite" matched the runner's NAME
6
+ * anywhere in the text, so a runner named in an argument was read as an invocation: X1
7
+ * xr-molecule-flash-m81 (+20:03) sent
8
+ * `cd /workspace/fixtures/realworld/specs/api && ls && head -30 run-api-tests-hurl.sh` — two
9
+ * reads of a file that was already there — and the suite-overlap hold answered "a Playwright
10
+ * suite is already running against this app and database … wait for it", costing a minute of
11
+ * forced idling on a command that writes nothing and runs nothing. `cat`, `grep`, `sed -n`,
12
+ * `wc`, `ls` and `git show` of a runner script are the same shape.
13
+ *
14
+ * So this parser answers it in one place, for every caller: split the command at its pipeline
15
+ * and statement separators, take each segment's word in command position — after the wrapper
16
+ * prefixes a shell puts in front of it (`HOST=x sudo nice -n 10 npx`, `bash`, `timeout 280`) —
17
+ * and let the caller match its runner against THAT segment's text, never against the raw
18
+ * command. `{@link commandSegments}` returns the segments, and
19
+ * {@link runsInCommandPosition} asks one anchored pattern of them all.
20
+ *
21
+ * Not command position, and therefore not an invocation: a runner named as an argument
22
+ * (`head -30 run-api-tests-hurl.sh`, `grep -n playwright e2e/README.md`,
23
+ * `git show HEAD:run-api-tests.sh`), and a runner named in a heredoc body — a file a command
24
+ * WRITES is the caller's business, so a command with a heredoc goes through the caller's own
25
+ * stripper first (`withoutHeredocs` in molecule-dev's suite checks).
26
+ *
27
+ * @module
28
+ */
29
+ /** One statement or pipeline stage of a shell command: what it runs, and with what. */
30
+ export interface CommandSegment {
31
+ /**
32
+ * The word in command position, its directory removed (`/usr/bin/hurl` → `hurl`): the program
33
+ * the shell would run once whatever wrappers stood in front of it are taken away.
34
+ */
35
+ program: string;
36
+ /** The words after it, as written — quotes kept, so a quoted `"a|b"` is one word. */
37
+ args: readonly string[];
38
+ /** The program and its arguments as one line — what a runner pattern is matched against. */
39
+ text: string;
40
+ }
41
+ /**
42
+ * The segments of a command, each with its program and arguments — the answer every classifier
43
+ * that reads a command as "this runs the suite" is asking for.
44
+ *
45
+ * @param command - The shell command.
46
+ * @returns One entry per statement or pipeline stage that runs something, in order.
47
+ */
48
+ export declare function commandSegments(command: string): CommandSegment[];
49
+ /**
50
+ * Whether any segment of the command runs a program whose command-position text matches
51
+ * `pattern` — the one question a runner-kind table asks.
52
+ *
53
+ * The pattern is matched against `program` and its arguments as one line, so it must be
54
+ * anchored at `^`; it must not carry the `g` flag (a pattern shared between calls would then
55
+ * carry its own match position from one segment to the next).
56
+ *
57
+ * @param command - The shell command.
58
+ * @param pattern - An anchored pattern for the runner's invocation.
59
+ * @returns True when a segment in command position matches.
60
+ */
61
+ export declare function runsInCommandPosition(command: string, pattern: RegExp): boolean;
62
+ //# sourceMappingURL=command-position.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;CACb;AAqRD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAEjE;AA+BD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}