@molecule/api-ai-tools 1.10.1 → 1.10.3

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-18T22:07:04.913Z
6
+ Generated: 2026-10-19T03:15:38.119Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -586,6 +586,16 @@ interface TraceMissingText {
586
586
  method: string
587
587
  /** The path of the response that carried it (`/api/tags`). */
588
588
  path: string
589
+ /**
590
+ * Whether the trace PROVES the page dropped the text: the named response is a successful GET
591
+ * of the collection that started after the last navigation, and the text is absent from the
592
+ * page's own failure snapshot ({@link traceArtifactsScript}).
593
+ *
594
+ * Absent when the evidence is weaker — a creation POST carried the text (m194's button failure
595
+ * was not a missing article), the snapshot still showed it, or the trace recorded no clock —
596
+ * and the note then says only that a response contained the text.
597
+ */
598
+ dropped?: true
589
599
  }
590
600
  ```
591
601
 
@@ -616,6 +626,43 @@ interface TraceReadContradiction {
616
626
  }
617
627
  ```
618
628
 
629
+ #### `TraceHiddenBox`
630
+
631
+ A visibility failure whose locator RESOLVED: Playwright calls an element with a zero-size box
632
+ "hidden" even when it is painted, so the useful fact is why the box is empty. The trace's own
633
+ failure snapshot carries the element's children — read here, never guessed.
634
+
635
+ ```typescript
636
+ interface TraceHiddenBox {
637
+ /** The locator the assertion printed (`locator('a.navbar-brand')`), or the element it names. */
638
+ locator: string
639
+ /** The element's tag, lowercased (`a`). */
640
+ tag: string
641
+ /** The element's own inline style, when the snapshot carries one. */
642
+ style?: string
643
+ /**
644
+ * The `<img>` the element wraps when that image is its only element child and the element has
645
+ * no text of its own: the size the box would collapse to.
646
+ */
647
+ image?: {
648
+ /** The image's `src`, bounded. */
649
+ src?: string
650
+ /** Its `width` attribute. */
651
+ width?: string
652
+ /** Its `height` attribute. */
653
+ height?: string
654
+ /** Its inline `style`. */
655
+ style?: string
656
+ /** Its `class`. */
657
+ className?: string
658
+ /** Its `alt` text. */
659
+ alt?: string
660
+ }
661
+ /** True when the element has no text and no element children at all. */
662
+ empty?: true
663
+ }
664
+ ```
665
+
619
666
  #### `TraceArtifact`
620
667
 
621
668
  What one trace says: the test it belongs to, the requests it made, and its console lines.
@@ -705,6 +752,13 @@ interface TraceArtifact {
705
752
  * and on traces read before this field existed.
706
753
  */
707
754
  readContradiction?: TraceReadContradiction
755
+ /**
756
+ * A visibility failure whose locator RESOLVED into an empty box — Playwright's "hidden" — with
757
+ * the element's children read from the failure snapshot ({@link TraceHiddenBox}). The note uses
758
+ * it to name why the box is empty (an `<img>` with no CSS size, a textless element) instead of
759
+ * leaving the run to probe computed styles (xr-molecule-flash-m197).
760
+ */
761
+ hiddenBox?: TraceHiddenBox
708
762
  }
709
763
  ```
710
764
 
@@ -843,14 +897,31 @@ interface SuiteBudgetPin {
843
897
  /**
844
898
  * The Playwright flags that carry the suite's own settings onto the command
845
899
  * (`--timeout=15000`, `--workers=1`), as the consumer's own scan reads them. Empty when the
846
- * difference is in settings no flag carries — the pin is then the sentence alone.
900
+ * difference is in settings no flag carries.
847
901
  */
848
902
  flags: string[]
903
+ /** One sentence for the run's result: which of the suite's own settings this run uses. */
904
+ note: string
849
905
  /**
850
- * One sentence for the run's result: this run uses the suite's own budget, and which settings
851
- * a config keeps because Playwright has no flag for them.
906
+ * A config file the consumer wrote OUTSIDE the supplied suite, which re-imports the config the
907
+ * run selected and pins the settings no Playwright flag can carry (`expect.timeout`,
908
+ * `use.actionTimeout`, `use.navigationTimeout`). When present, the run is launched with
909
+ * `--config <this path>` in place of the config it named, so those settings are the suite's own
910
+ * rather than the executor's. Absent when the consumer could not write one.
852
911
  */
853
- note: string
912
+ config?: string
913
+ /**
914
+ * Every setting the derived config ({@link config}) pins at the suite's own value, by the scan's
915
+ * key (`timeout`, `expect.timeout`, `actionTimeout`, `navigationTimeout`), in milliseconds as
916
+ * digits. Empty when no derived config was placed.
917
+ */
918
+ values?: Record<string, string>
919
+ /**
920
+ * Every setting the run's own config makes different from the suite's, as the consumer's scan
921
+ * read them: the value this run's config writes and the suite's own. The tool reports the ones
922
+ * its placement did NOT put back, so a result never claims a drift it carried.
923
+ */
924
+ drift?: Record<string, { used: string; suite: string }>
854
925
  }
855
926
  ```
856
927
 
@@ -986,7 +1057,8 @@ interface ToolBuildConfig {
986
1057
  * different settings, asked BEFORE the run executes. The consumer knows where the suite and its
987
1058
  * base config live; this shared package does not, so the reading is the consumer's and the
988
1059
  * mechanics — putting the flags on the runner's own arguments, before the pipeline and outside
989
- * any redirect or group closer — are this tool's.
1060
+ * any redirect or group closer, and pointing `--config` at the derived config that carries the
1061
+ * settings no flag can — are this tool's.
990
1062
  *
991
1063
  * Called for a Playwright suite command only, and only the LAST suite step is touched. A run
992
1064
  * that already keeps the suite's settings, or a sandbox with no supplied suite, answers `null`.
@@ -994,7 +1066,7 @@ interface ToolBuildConfig {
994
1066
  *
995
1067
  * @param command - The shell command, as the model sent it.
996
1068
  * @param cwd - The resolved working directory it will run in.
997
- * @returns The flags and the sentence, or null when nothing is put back.
1069
+ * @returns The flags, the derived config and the sentence, or null when nothing is put back.
998
1070
  */
999
1071
  suiteBudget?: (command: string, cwd: string) => Promise<SuiteBudgetPin | null | undefined>
1000
1072
 
@@ -1227,6 +1299,19 @@ interface CausePageErrors {
1227
1299
  where: string
1228
1300
  /** The alert / error items the page showed at the moment it failed. */
1229
1301
  items: readonly string[]
1302
+ /**
1303
+ * The landmark roles the test's page snapshot printed (`navigation`, `banner`), read from the
1304
+ * YAML block of its own `error-context.md`. Present ONLY when the caller has that test's
1305
+ * snapshot; an empty array means the snapshot was read and showed no landmark, while an absent
1306
+ * field means nothing was read and no claim may be made ({@link missingHeaderNote}).
1307
+ */
1308
+ landmarks?: readonly string[]
1309
+ /**
1310
+ * Whether the test's snapshot showed populated page content (headings, paragraphs, links,
1311
+ * list items, buttons), the other half of the missing-header reading: a page that painted its
1312
+ * main content but no navigation.
1313
+ */
1314
+ populated?: boolean
1230
1315
  }
1231
1316
  ```
1232
1317
 
@@ -1296,6 +1381,14 @@ from any shell runner.
1296
1381
  type InstalledPackageProbe = (path: string) => Promise<InstalledPackageFacts>
1297
1382
  ```
1298
1383
 
1384
+ #### `RequiredLandmark`
1385
+
1386
+ A landmark role a failed selector requires: `<nav>`/`role=navigation`, `<header>`/`role=banner`.
1387
+
1388
+ ```typescript
1389
+ type RequiredLandmark = 'navigation' | 'banner'
1390
+ ```
1391
+
1299
1392
  ### Classes
1300
1393
 
1301
1394
  #### `ToolInputError`
@@ -1769,7 +1862,9 @@ the runner invocation each caller read as a run is one.
1769
1862
 
1770
1863
  A command that also runs tests is false however it is spelled: `npx playwright install &&
1771
1864
  npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
1772
- reading a real run.
1865
+ reading a real run. So is a command carrying a segment this reader cannot place (a script it
1866
+ does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
1867
+ table can be accounted for, and "only prints" is a claim about the WHOLE command.
1773
1868
 
1774
1869
  ```typescript
1775
1870
  function commandRunsNoTests(command: string): boolean
@@ -2360,6 +2455,26 @@ function isCredentialExchangePath(path: string, authEndpoints?: readonly string[
2360
2455
 
2361
2456
  **Returns:** True when the path names a credential exchange.
2362
2457
 
2458
+ #### `hiddenBoxNote(items)`
2459
+
2460
+ The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
2461
+
2462
+ Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
2463
+ is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
2464
+ only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
2465
+ width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
2466
+ (`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
2467
+ note said only "the cause is in the page", and the run spent over four minutes rasterising an
2468
+ SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
2469
+
2470
+ ```typescript
2471
+ function hiddenBoxNote(items: readonly TraceArtifact[]): string | null
2472
+ ```
2473
+
2474
+ - `items` — The traces {@link parseTraceArtifacts} read.
2475
+
2476
+ **Returns:** The sentence, or null when no failure carries a hidden-box reading.
2477
+
2363
2478
  #### `failureTraceNote(items, target, earlierFailures, apiPrefixes, report)`
2364
2479
 
2365
2480
  The note a failed suite run's result carries: what each failing test's page actually sent, and
@@ -2741,6 +2856,33 @@ function suiteRunBudgetCommand(
2741
2856
 
2742
2857
  **Returns:** The command to run, the flags placed on it, and the ones it could not carry.
2743
2858
 
2859
+ #### `suiteRunConfigCommand(command, config)`
2860
+
2861
+ A command with the derived config put on the LAST suite runner's own arguments, replacing the
2862
+ config the run names or added when it names none.
2863
+
2864
+ Some of the suite's settings have no Playwright flag (`expect.timeout`, `use.actionTimeout`,
2865
+ `use.navigationTimeout`), so a config the executor wrote cannot be corrected from the command
2866
+ line. The consumer writes a derived config OUTSIDE the supplied suite that re-imports the one
2867
+ the run selected and pins those settings ({@link SuiteBudgetPin.config}); pointing the runner at
2868
+ it is the one way to carry them, and it belongs on the runner itself for the same reasons
2869
+ {@link suiteRunBudgetCommand}'s flags do (last suite step, before a pipe or group closer).
2870
+
2871
+ A command whose runner cannot be placed is returned exactly as written with `placed: false`, so
2872
+ the caller never claims a config it did not point the run at.
2873
+
2874
+ ```typescript
2875
+ function suiteRunConfigCommand(
2876
+ command: string,
2877
+ config: string,
2878
+ ): { command: string; placed: boolean }
2879
+ ```
2880
+
2881
+ - `command` — The shell command about to run.
2882
+ - `config` — The config to run, as the runner takes it (`--config <path>`).
2883
+
2884
+ **Returns:** The command to run and whether the config was placed.
2885
+
2744
2886
  #### `withoutSuiteTimeout(command)`
2745
2887
 
2746
2888
  The command with the `timeout <N>` wrapper lifted off the statement that runs the suite, and the
@@ -3327,6 +3469,31 @@ function repeatingFailure(log: string): RepeatingFailure | null
3327
3469
 
3328
3470
  **Returns:** The repeating cause, or null when no signature has {@link REPEATING_FAILURE_MIN} distinct tests behind it.
3329
3471
 
3472
+ #### `logShowsInlineFailureBodies(log)`
3473
+
3474
+ Whether the log read so far carries a failure's own body under a `✘` row — the evidence that a
3475
+ run prints each failure's error as that test fails ({@link PLAYWRIGHT_FAILURES_INLINE_ENV}).
3476
+
3477
+ `wait_for_task` promises the executor that each failure's report sits right under its `✘` line
3478
+ only when the log proves it. The command's shape cannot answer: the launch sets the switch on
3479
+ every background command (the executor's wrapper script inherits it), so a wrapper this reader
3480
+ cannot place is exactly as covered as `npx playwright test` ({@link streamingBackgroundCommand}).
3481
+ The log is the evidence — a `1) [project] › spec › title ───` block whose test the log also
3482
+ printed with `✘` ({@link failureBlocks}).
3483
+
3484
+ A block alone is not enough: a FINISHED run's end-of-run summary carries the same blocks, and a
3485
+ running wait must not be told "as it fails" about a log whose bodies only appear at the end.
3486
+ During a wait the run has not ended (the exit file is read first), so a block beside a `✘` row
3487
+ is one the reporter printed inline.
3488
+
3489
+ ```typescript
3490
+ function logShowsInlineFailureBodies(log: string): boolean
3491
+ ```
3492
+
3493
+ - `log` — A log, or the slice of one a wait reads.
3494
+
3495
+ **Returns:** True when a `✘` row's own test has a failure body under it.
3496
+
3330
3497
  #### `specFilesSelected(command)`
3331
3498
 
3332
3499
  How many test FILES a Playwright run's own command narrows it to, or null when it selects the
@@ -3481,6 +3648,50 @@ function failuresByCause(log: string, excluded?: readonly string[]): FailureCaus
3481
3648
 
3482
3649
  **Returns:** The causes, the one with the most tests first.
3483
3650
 
3651
+ #### `requiredLandmark(selector)`
3652
+
3653
+ The landmark a selector requires as an ANCESTOR (or names as its own role), or null when it
3654
+ names no landmark at all (`h1`, `.article-preview`, `a[href="/profile/x"]`).
3655
+
3656
+ ```typescript
3657
+ function requiredLandmark(selector: string): RequiredLandmark | null
3658
+ ```
3659
+
3660
+ - `selector` — One CSS selector or `getByRole` role from a failure's call log.
3661
+
3662
+ **Returns:** The landmark, or null.
3663
+
3664
+ #### `missingHeaderNote(log, pageErrors)`
3665
+
3666
+ The sentence for a run whose navigational elements are all missing from pages that otherwise
3667
+ painted — the missing shared header the executor must check BEFORE rewriting auth.
3668
+
3669
+ xr-molecule-flash-m193 (+13:54 → +20:00): four failures on `/` — `a[href="/profile/…"]`,
3670
+ `a.navbar-brand`, `nav a[href="/"]`, `nav .user-pic` — while every `error-context.md` snapshot
3671
+ showed the feed, sidebar and footer with no navigation at all. `Home.tsx` imported and
3672
+ re-exported `Navbar` but never rendered it, and `App.tsx` mounted `<Home />` in a bare route.
3673
+ The cause list named four unrelated symptoms, so the run rewrote `Navbar`'s auth logic and then
3674
+ the HTTP bond across two whole walks (~6 minutes) instead of mounting the header.
3675
+
3676
+ The reading is per FAILURE and per SNAPSHOT: each failure's own selector must require the
3677
+ landmark, its own snapshot must have been read AND show populated page content, and that
3678
+ snapshot must not contain the landmark. Two or more such failures on ONE route (the route a
3679
+ selector names, e.g. `nav a[href="/"]` → `/`; failures whose selector names none ride along)
3680
+ are the group. A page that shows the navigation and merely misses a signed-in link is not this
3681
+ shape — its snapshots contain the landmark, so nothing is said.
3682
+
3683
+ This never stops the run and never claims the page is blank: it is one sentence prepended to
3684
+ the causes note ({@link failureCausesNote}).
3685
+
3686
+ ```typescript
3687
+ function missingHeaderNote(log: string, pageErrors?: readonly CausePageErrors[]): string | null
3688
+ ```
3689
+
3690
+ - `log` — The run's output.
3691
+ - `pageErrors` — The page snapshot facts the caller read per failing test.
3692
+
3693
+ **Returns:** The sentence, or null when the failures do not share a missing landmark.
3694
+
3484
3695
  #### `failureCausesNote(log, excluded, run)`
3485
3696
 
3486
3697
  The one-line-per-cause summary of a finished run's failures, or null when the run named fewer
@@ -3514,6 +3725,7 @@ function failureCausesNote(
3514
3725
  env?: readonly RunEnvSetting[]
3515
3726
  },
3516
3727
  pageErrors?: readonly CausePageErrors[],
3728
+ regressedSpecs?: readonly string[],
3517
3729
  ): string | null
3518
3730
  ```
3519
3731
 
@@ -3862,8 +4074,9 @@ the summary at the end: `list` reads it in its constructor (`ListReporterOptions
3862
4074
  inline the end-of-run repeat is suppressed with it (`epilogue(!printFailuresInline)`), so one
3863
4075
  reporter still prints each failure exactly once.
3864
4076
 
3865
- A background suite is put on `list` (see {@link streamingBackgroundCommand}) and this with it, so
3866
- the log names its causes WHILE it walks. Without it the log's only failure lines are `✘` rows:
4077
+ Every background command is given this (see {@link streamingBackgroundCommand}), so a suite the
4078
+ executor starts through its own wrapper is covered too — the wrapper passes the environment on
4079
+ to the Playwright process it spawns. Without it the log's only failure lines are `✘` rows:
3867
4080
  X1 xr-molecule-flash-m99 (+19:27 → +21:00, 2026-10-16) was told to `grep -n -A 20 '<test name>'`
3868
4081
  the log, got nothing back through four greps, inferred causes from the spec prose instead, and
3869
4082
  edited `app/src` while the run was still walking — which voids the run.
@@ -4174,11 +4387,12 @@ a test whose result is thrown away, and a blank page costs its per-test budget e
4174
4387
  (m177: 18 tests, ~4.2 minutes, on a page whose thrown error was visible in the first
4175
4388
  `pageerror`). Three is the smallest count that is not one page's own bug: the cause additionally
4176
4389
  needs three DISTINCT spec files (`UNIVERSAL_CAUSE_FILES`, or the files a narrowed run selected),
4177
- each spec waiting for its OWN first element, and no test passing after the first failure. A page
4178
- that renders misses elements, but three unrelated specs in three files failing at three
4179
- different first locators with nothing passing in between is the page, not three bugs. At the
4180
- suite's own 5 s action timeout that verdict lands ~20 s into the walk, so the early stop costs
4181
- about one test's worth of time instead of fifteen.
4390
+ no failure showing a locator RESOLVING ({@link ELEMENT_FOUND} — a blank page cannot resolve
4391
+ one), and no test passing after the first failure. A page that renders misses elements, but
4392
+ three unrelated specs in three files failing at three different locators that never resolved,
4393
+ with nothing passing in between, is the page, not three bugs. At the suite's own 5 s action
4394
+ timeout that verdict lands ~20 s into the walk, so the early stop costs about one test's worth
4395
+ of time instead of fifteen.
4182
4396
 
4183
4397
  ```typescript
4184
4398
  const EMPTY_PAGE_CAUSE_TESTS: 3
@@ -49,7 +49,9 @@
49
49
  *
50
50
  * A command that also runs tests is false however it is spelled: `npx playwright install &&
51
51
  * npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
52
- * reading a real run.
52
+ * reading a real run. So is a command carrying a segment this reader cannot place (a script it
53
+ * does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
54
+ * table can be accounted for, and "only prints" is a claim about the WHOLE command.
53
55
  *
54
56
  * @param command - The shell command as the executor sent it.
55
57
  * @returns True when the command invokes a runner only to print.
@@ -1 +1 @@
1
- {"version":3,"file":"no-test-run.d.ts","sourceRoot":"","sources":["../src/no-test-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA0EH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAU3D"}
1
+ {"version":3,"file":"no-test-run.d.ts","sourceRoot":"","sources":["../src/no-test-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAiLH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAiB3D"}
@@ -75,6 +75,108 @@ const NO_RUN_FLAGS = {
75
75
  ],
76
76
  hurl: [/(?:^|\s)--help(?=\s|$)/, /(?:^|\s)-h(?=\s|$)/, /(?:^|\s)--version(?=\s|$)/],
77
77
  };
78
+ /**
79
+ * The programs that start no test — the shell's own bookkeeping (`cd`, `export`, `echo`), and the
80
+ * readers, printers and file commands an executor uses to look at what is already there (`cat`,
81
+ * `ls`, `cp`, `tail`, …).
82
+ *
83
+ * A segment whose program is neither a runner nor one of these is a program this reader cannot
84
+ * account for: the suite's own script (`./run-api-tests-hurl.sh`), a wrapper the executor wrote,
85
+ * anything that may itself start a suite. {@link commandRunsNoTests} answers false for a command
86
+ * carrying one — "every invocation only prints" is not a claim an unknown step can support.
87
+ * X1 xr-molecule-flash-m197 (+05:18) sent
88
+ * `hurl --version | head -2 && HOST=http://localhost:4000 ./run-api-tests-hurl.sh 2>&1 | tail -60`,
89
+ * the suite passed 13/13, and the supplied script was the one segment nobody could place: the
90
+ * whole command read as a version probe, so every "has the suite run?" reader said no for the
91
+ * rest of the run and the early deploy was held to the end of the list.
92
+ */
93
+ const NO_TEST_PROGRAMS = new Set([
94
+ '[',
95
+ '[[',
96
+ ':',
97
+ 'awk',
98
+ 'base64',
99
+ 'basename',
100
+ 'case',
101
+ 'cat',
102
+ 'cd',
103
+ 'chmod',
104
+ 'chown',
105
+ 'column',
106
+ 'comm',
107
+ 'cp',
108
+ 'cut',
109
+ 'date',
110
+ 'df',
111
+ 'diff',
112
+ 'dirname',
113
+ 'do',
114
+ 'done',
115
+ 'du',
116
+ 'echo',
117
+ 'egrep',
118
+ 'else',
119
+ 'env',
120
+ 'esac',
121
+ 'export',
122
+ 'expr',
123
+ 'false',
124
+ 'fgrep',
125
+ 'fi',
126
+ 'file',
127
+ 'fold',
128
+ 'for',
129
+ 'grep',
130
+ 'head',
131
+ 'hostname',
132
+ 'id',
133
+ 'if',
134
+ 'jq',
135
+ 'kill',
136
+ 'less',
137
+ 'ln',
138
+ 'ls',
139
+ 'md5sum',
140
+ 'mkdir',
141
+ 'mktemp',
142
+ 'more',
143
+ 'mv',
144
+ 'nl',
145
+ 'od',
146
+ 'paste',
147
+ 'printf',
148
+ 'pwd',
149
+ 'readlink',
150
+ 'realpath',
151
+ 'rev',
152
+ 'rm',
153
+ 'rmdir',
154
+ 'seq',
155
+ 'set',
156
+ 'sha256sum',
157
+ 'sleep',
158
+ 'sort',
159
+ 'stat',
160
+ 'tac',
161
+ 'tail',
162
+ 'tee',
163
+ 'test',
164
+ 'then',
165
+ 'touch',
166
+ 'tr',
167
+ 'true',
168
+ 'type',
169
+ 'uname',
170
+ 'uniq',
171
+ 'unset',
172
+ 'until',
173
+ 'wait',
174
+ 'wc',
175
+ 'which',
176
+ 'while',
177
+ 'whoami',
178
+ 'xxd',
179
+ ]);
78
180
  /**
79
181
  * A heredoc body is data a command writes, not what it runs: a `playwright test --list` inside
80
182
  * the config a command writes is not a run of this command either way. The pattern consumes the
@@ -113,7 +215,9 @@ function informational(runner, after) {
113
215
  *
114
216
  * A command that also runs tests is false however it is spelled: `npx playwright install &&
115
217
  * npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
116
- * reading a real run.
218
+ * reading a real run. So is a command carrying a segment this reader cannot place (a script it
219
+ * does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
220
+ * table can be accounted for, and "only prints" is a claim about the WHOLE command.
117
221
  *
118
222
  * @param command - The shell command as the executor sent it.
119
223
  * @returns True when the command invokes a runner only to print.
@@ -123,9 +227,16 @@ export function commandRunsNoTests(command) {
123
227
  return false;
124
228
  let invocations = 0;
125
229
  for (const segment of commandSegments(command.replace(HEREDOC, ' '))) {
126
- const runner = RUNNERS[segment.program];
127
- if (!runner)
230
+ // An own key only: `constructor` is a program name (`constructor --version`) and a
231
+ // prototype key is not a runner.
232
+ const runner = Object.hasOwn(RUNNERS, segment.program) ? RUNNERS[segment.program] : undefined;
233
+ if (runner === undefined) {
234
+ // A program this reader cannot place may start a suite of its own; only a known
235
+ // non-test program lets the command keep reading as "prints only".
236
+ if (!NO_TEST_PROGRAMS.has(segment.program))
237
+ return false;
128
238
  continue;
239
+ }
129
240
  invocations += 1;
130
241
  if (!informational(runner, segment.args.join(' ')))
131
242
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AA4K9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AA6ZD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAqFnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AA2ElF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AA2GD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAyuFxF"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AA4K9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AA6ZD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAsHnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AA2ElF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AA2GD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAyvFxF"}