@molecule/app-ide-react 1.13.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +578 -1
  2. package/dist/command-metadata.d.ts.map +1 -1
  3. package/dist/command-metadata.js +7 -2
  4. package/dist/command-metadata.js.map +1 -1
  5. package/dist/components/ChatPanel.d.ts +9 -1
  6. package/dist/components/ChatPanel.d.ts.map +1 -1
  7. package/dist/components/ChatPanel.js +177 -18
  8. package/dist/components/ChatPanel.js.map +1 -1
  9. package/dist/components/TestsBar.d.ts +55 -0
  10. package/dist/components/TestsBar.d.ts.map +1 -0
  11. package/dist/components/TestsBar.js +296 -0
  12. package/dist/components/TestsBar.js.map +1 -0
  13. package/dist/components/TestsCard.d.ts +78 -0
  14. package/dist/components/TestsCard.d.ts.map +1 -0
  15. package/dist/components/TestsCard.js +247 -0
  16. package/dist/components/TestsCard.js.map +1 -0
  17. package/dist/components/index.d.ts +3 -0
  18. package/dist/components/index.d.ts.map +1 -1
  19. package/dist/components/index.js +2 -0
  20. package/dist/components/index.js.map +1 -1
  21. package/dist/components/tests-bar-utilities.d.ts +129 -0
  22. package/dist/components/tests-bar-utilities.d.ts.map +1 -0
  23. package/dist/components/tests-bar-utilities.js +236 -0
  24. package/dist/components/tests-bar-utilities.js.map +1 -0
  25. package/dist/components/tests-card-utilities.d.ts +175 -0
  26. package/dist/components/tests-card-utilities.d.ts.map +1 -0
  27. package/dist/components/tests-card-utilities.js +333 -0
  28. package/dist/components/tests-card-utilities.js.map +1 -0
  29. package/dist/index.d.ts +19 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +19 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/types.d.ts +136 -0
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/package.json +2 -2
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-09-13T10:48:48.475Z
6
+ Generated: 2026-09-15T18:15:48.521Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-ide-react
@@ -479,9 +479,33 @@ interface ChatPanelProps {
479
479
  * `null`/`undefined` (the default) to render the nudge text with no button.
480
480
  * `requiresSignup`, when set, is the backend's flag that the user must sign up
481
481
  * rather than upgrade an existing plan; when unset the host's own auth state decides.
482
+ *
483
+ * The rest of the context is the backend's own description of the limit that
484
+ * fired, forwarded verbatim so the host never has to guess the right button:
485
+ *
486
+ * - `limitType` — the RULE the backend enforced (e.g. `usage_balance`,
487
+ * `spend_cap`, `ai_cost`, `max_tool_loops`).
488
+ * - `billingAction` — the REMEDY the backend resolved (e.g. `sign_up`,
489
+ * `upgrade`, `add_payment_method`, `add_funds`, `raise_spend_cap`,
490
+ * `enable_extra_usage`, `contact_support`, `owner_only`, `none`). Prefer this
491
+ * over `limitType` when both are present: one limit can need different
492
+ * buttons per account (an empty balance is "Add funds" with a card on file
493
+ * and "Add a payment method" without one), and `none` means there is nothing
494
+ * the user can do — render no button.
495
+ * - `upgradeTier` — the plan an upgrade would move to, explicitly `null` when
496
+ * there is no higher tier, so a top-tier user is never sent to a plans page
497
+ * with nothing to sell them.
498
+ *
499
+ * Both vocabularies belong to the host's API, so they are plain strings here —
500
+ * the shared IDE never interprets them. Every field is optional and may be
501
+ * absent (a backend that sends none of them keeps the old behavior), so the
502
+ * host must have a fallback.
482
503
  */
483
504
  buildUpgradeCta?: (context: {
484
505
  requiresSignup?: boolean
506
+ limitType?: string
507
+ billingAction?: string
508
+ upgradeTier?: string | null
485
509
  }) => ChatEventCardAction | ChatEventCardAction[] | null | undefined
486
510
  /**
487
511
  * Optional app-specific section appended to the `/help` output — e.g. a plan /
@@ -544,6 +568,45 @@ interface ChatPanelProps {
544
568
  * modal — which POSTs to the project's own backend — is unaffected.
545
569
  */
546
570
  feedbackUrl?: string
571
+ /**
572
+ * Lists the project's tests for the `/test` browser. Omit it (the default)
573
+ * and `/test` has nothing to show — the shared IDE owns no test-discovery
574
+ * route of its own.
575
+ *
576
+ * molecule.dev implements it over `GET /projects/:id/tests`. It is called
577
+ * on every `/test` invocation and once more when a run finishes, so a spec
578
+ * the agent just wrote shows up the next time the browser is opened.
579
+ */
580
+ listTests?: () => Promise<TestList>
581
+ /**
582
+ * Runs the selected tests, streaming {@link TestRunEvent}s back as they
583
+ * happen. Required alongside {@link ChatPanelProps.listTests} for the
584
+ * browser's run controls to work.
585
+ *
586
+ * The returned handle's `cancel()` must stop the run (molecule.dev aborts the
587
+ * SSE request, and the server kills the process tree on disconnect). The host
588
+ * is responsible for running the e2e specs through the preview bond chain —
589
+ * in a sandbox that means `npx playwright test` with
590
+ * `@molecule/app-e2e-preview` as the browser, so the spec drives the live
591
+ * preview rather than a browser binary that is not installed there.
592
+ *
593
+ * The run is owned by `ChatPanel`, not by the card, so it keeps streaming
594
+ * while the browser is closed and is still there when it is re-opened.
595
+ */
596
+ runTests?: (selection: TestSelection, onEvent: (event: TestRunEvent) => void) => TestRunHandle
597
+ /**
598
+ * Whether this viewer may RUN tests. `false` still lets them open `/test`
599
+ * and read what the project tests (the platform serves the listing to
600
+ * viewers) but disables every run control and shows why. Defaults to
601
+ * `canEdit !== false`.
602
+ */
603
+ canRunTests?: boolean
604
+ /**
605
+ * Whether the environment the tests run in is up — a running sandbox.
606
+ * `false` makes `/test` say to start the project instead of listing an empty
607
+ * browser or letting a Run click fail. Defaults to `true`.
608
+ */
609
+ testsAvailable?: boolean
547
610
  className?: string
548
611
  }
549
612
  ```
@@ -1407,6 +1470,183 @@ interface TabBarProps {
1407
1470
  }
1408
1471
  ```
1409
1472
 
1473
+ #### `TestFailure`
1474
+
1475
+ One failing test and the output that explains it.
1476
+
1477
+ ```typescript
1478
+ interface TestFailure {
1479
+ item: TestItem
1480
+ output?: string | undefined
1481
+ }
1482
+ ```
1483
+
1484
+ #### `TestGroup`
1485
+
1486
+ One rendered group of rows: a kind within a project directory.
1487
+
1488
+ ```typescript
1489
+ interface TestGroup {
1490
+ kind: TestKind
1491
+ workspace: TestWorkspace
1492
+ /** The directory's name for the heading; `null` for the workspace root. */
1493
+ label: string | null
1494
+ items: TestItem[]
1495
+ }
1496
+ ```
1497
+
1498
+ #### `TestItem`
1499
+
1500
+ One test file the host discovered in the project.
1501
+
1502
+ ```typescript
1503
+ interface TestItem {
1504
+ /** Stable id, unique across workspaces — used as the row key and to select by. */
1505
+ id: string
1506
+ /** Path relative to its workspace, e.g. `e2e/home.spec.ts`. */
1507
+ file: string
1508
+ kind: TestKind
1509
+ workspace: TestWorkspace
1510
+ /**
1511
+ * The group's project name, ready to render: the same path as `workspace`,
1512
+ * or `null` for the workspace root, which has no name of its own. Hosts that
1513
+ * omit it get the path itself (and the translated "Project" for the root).
1514
+ */
1515
+ workspaceLabel?: string | null
1516
+ /** Human label for the row; the bar falls back to the file path without one. */
1517
+ title?: string
1518
+ }
1519
+ ```
1520
+
1521
+ #### `TestList`
1522
+
1523
+ What {@link ChatPanelProps.listTests} resolves with.
1524
+
1525
+ ```typescript
1526
+ interface TestList {
1527
+ tests: TestItem[]
1528
+ /**
1529
+ * Per project directory (keyed like {@link TestItem.workspace}), the runners
1530
+ * the host found. Only directories that hold a listed test appear.
1531
+ */
1532
+ runners: Record<string, TestRunners>
1533
+ }
1534
+ ```
1535
+
1536
+ #### `TestResultEntry`
1537
+
1538
+ What one test file ended as in the last run.
1539
+
1540
+ ```typescript
1541
+ interface TestResultEntry {
1542
+ status: TestStatus
1543
+ durationMs?: number
1544
+ passed: number
1545
+ failed: number
1546
+ skipped: number
1547
+ /** The runner's output, kept for a FAILURE so the row can keep showing it. */
1548
+ output?: string
1549
+ }
1550
+ ```
1551
+
1552
+ #### `TestRunHandle`
1553
+
1554
+ Handle to a run in flight, so the bar can stop it.
1555
+
1556
+ ```typescript
1557
+ interface TestRunHandle {
1558
+ /** Stop the run — the host aborts its stream, which cancels the work. */
1559
+ cancel(): void
1560
+ }
1561
+ ```
1562
+
1563
+ #### `TestRunners`
1564
+
1565
+ Which runner drives each kind in one workspace (`null` = none installed).
1566
+
1567
+ ```typescript
1568
+ interface TestRunners {
1569
+ e2e: string | null
1570
+ unit: string | null
1571
+ }
1572
+ ```
1573
+
1574
+ #### `TestsCardProps`
1575
+
1576
+ Props for {@link TestsCard}.
1577
+
1578
+ ```typescript
1579
+ interface TestsCardProps {
1580
+ /** Every discovered test (unfiltered). */
1581
+ tests: TestItem[]
1582
+ /** How the listing went. */
1583
+ status: TestsStatus
1584
+ /** The run this card is showing — owned by the panel, so it survives a close. */
1585
+ run: TestsRunState
1586
+ /** Seeds the search box from `/test <query>`. */
1587
+ initialQuery: string
1588
+ /** Whether this viewer may run tests at all (a viewer may not). */
1589
+ canRun: boolean
1590
+ /** Runs a selection. */
1591
+ onRun: (selection: TestSelection) => void
1592
+ /** Stops the run in flight. */
1593
+ onCancel: () => void
1594
+ /**
1595
+ * Hands the failing tests to the agent as ONE chat message — a real turn it
1596
+ * answers, exactly like the editor’s “Fix with AI”.
1597
+ */
1598
+ onFix: (failures: TestFailure[]) => void
1599
+ /**
1600
+ * Why fixing is unavailable (a viewer, a turn already streaming), already
1601
+ * translated by the host — `null` when it is available. The card states the
1602
+ * reason on the disabled button rather than letting a click do nothing.
1603
+ */
1604
+ fixDisabledReason: string | null
1605
+ /** Light theme (drives the same row border + field inset the sibling cards use). */
1606
+ isLight: boolean
1607
+ /** Chrome-less inside the command overlay; full card chrome in the timeline. */
1608
+ embedded?: boolean
1609
+ }
1610
+ ```
1611
+
1612
+ #### `TestSelection`
1613
+
1614
+ What the bar asks the host to run.
1615
+
1616
+ ```typescript
1617
+ interface TestSelection {
1618
+ /** Specific {@link TestItem.id}s. Takes precedence over `kind`. */
1619
+ ids?: string[]
1620
+ /** Everything of this kind when no `ids` are given. */
1621
+ kind?: TestKind | 'all'
1622
+ }
1623
+ ```
1624
+
1625
+ #### `TestsRunState`
1626
+
1627
+ Everything the card knows about the run it is showing.
1628
+
1629
+ ```typescript
1630
+ interface TestsRunState {
1631
+ runId: string | null
1632
+ running: boolean
1633
+ /** The ids this run covers, in run order. */
1634
+ queued: string[]
1635
+ /** The id whose output is streaming right now, when the host says which. */
1636
+ currentId: string | null
1637
+ /** The live output lines, newest last, capped at {@link MAX_OUTPUT_LINES}. */
1638
+ output: string[]
1639
+ /** Per-id outcome from this run (and from earlier runs, until re-run). */
1640
+ results: Record<string, TestResultEntry>
1641
+ /** How the run ended, once it has. */
1642
+ outcome: TestRunOutcome | null
1643
+ /** A run-level failure message (timeout, transport error) shown in the card. */
1644
+ error: string | null
1645
+ startedAt: number | null
1646
+ durationMs: number | null
1647
+ }
1648
+ ```
1649
+
1410
1650
  #### `ToolCallCardProps`
1411
1651
 
1412
1652
  Properties for tool call card.
@@ -1649,6 +1889,86 @@ A role granted by a share link.
1649
1889
  type ShareRole = (typeof SHARE_ROLES)[number]
1650
1890
  ```
1651
1891
 
1892
+ #### `TestKind`
1893
+
1894
+ What a test file is: an end-to-end spec driven against the LIVE PREVIEW (the
1895
+ `@molecule/app-e2e-preview` bond every scaffolded app carries), or a plain
1896
+ unit test run by the project's own runner.
1897
+
1898
+ ```typescript
1899
+ type TestKind = 'e2e' | 'unit'
1900
+ ```
1901
+
1902
+ #### `TestRunEvent`
1903
+
1904
+ One event from a run in progress. The host streams these to the bar (over SSE
1905
+ in molecule.dev) in the order the run produces them: one `start`, then
1906
+ interleaved `output`/`result`, then exactly one `done`.
1907
+
1908
+ ```typescript
1909
+ type TestRunEvent =
1910
+ | { type: 'start'; runId: string; ids: string[]; startedAt?: string }
1911
+ | { type: 'output'; id?: string; stream?: 'stdout' | 'stderr'; chunk: string }
1912
+ | {
1913
+ type: 'result'
1914
+ id: string
1915
+ status: TestStatus
1916
+ durationMs?: number
1917
+ passed?: number
1918
+ failed?: number
1919
+ skipped?: number
1920
+ /** The runner's output for a FAILURE, which the bar keeps visible. */
1921
+ output?: string
1922
+ }
1923
+ | {
1924
+ type: 'done'
1925
+ runId?: string
1926
+ outcome: TestRunOutcome
1927
+ passed?: number
1928
+ failed?: number
1929
+ skipped?: number
1930
+ durationMs?: number
1931
+ error?: string
1932
+ }
1933
+ ```
1934
+
1935
+ #### `TestRunOutcome`
1936
+
1937
+ How a whole run ended.
1938
+
1939
+ ```typescript
1940
+ type TestRunOutcome = 'completed' | 'cancelled' | 'timeout' | 'error'
1941
+ ```
1942
+
1943
+ #### `TestsStatus`
1944
+
1945
+ Discovery status for the tests list — mirrors {@link ScriptsCard}'s.
1946
+
1947
+ ```typescript
1948
+ type TestsStatus = 'loading' | 'ready' | 'error' | 'unavailable'
1949
+ ```
1950
+
1951
+ #### `TestStatus`
1952
+
1953
+ How one test file ended.
1954
+
1955
+ ```typescript
1956
+ type TestStatus = 'passed' | 'failed' | 'skipped'
1957
+ ```
1958
+
1959
+ #### `TestWorkspace`
1960
+
1961
+ Which project directory a test file belongs to: the directory that OWNS the
1962
+ file (the nearest ancestor holding the runner's config or a `package.json`),
1963
+ as a path relative to the workspace root — `app`, `api`, `my-app/app`,
1964
+ `packages/web` — or `.` for the workspace root itself. Nothing guarantees
1965
+ `app/` or `api/` at the root: the executor names its project directory, so
1966
+ the value is a path, not an enum.
1967
+
1968
+ ```typescript
1969
+ type TestWorkspace = 'app' | 'api' | '.' | (string & {})
1970
+ ```
1971
+
1652
1972
  #### `TimestampsCommand`
1653
1973
 
1654
1974
  Parsed `/timestamps` invocation: toggle, explicit on/off, or an unrecognized argument.
@@ -1766,6 +2086,23 @@ function activityTypeLabel(type: ActivityType): string
1766
2086
 
1767
2087
  **Returns:** The translated channel label.
1768
2088
 
2089
+ #### `applyTestRunEvent(state, event)`
2090
+
2091
+ Fold one streamed event into the run state.
2092
+
2093
+ Deliberately total: an event for an id the card no longer lists is recorded
2094
+ anyway (a re-list may be in flight), and an unknown event type leaves the
2095
+ state untouched rather than throwing inside a stream handler.
2096
+
2097
+ ```typescript
2098
+ function applyTestRunEvent(state: TestsRunState, event: TestRunEvent): TestsRunState
2099
+ ```
2100
+
2101
+ - `state` — The current state.
2102
+ - `event` — The event just received.
2103
+
2104
+ **Returns:** The next state (a new object whenever anything changed).
2105
+
1769
2106
  #### `autoCommitReducer(state, action)`
1770
2107
 
1771
2108
  Pure reducer for the auto-commit countdown. Deterministic and side-effect
@@ -1824,6 +2161,23 @@ function buildShareUrl(result: ShareLinkResult, origin: string): string
1824
2161
 
1825
2162
  **Returns:** The absolute, copyable share URL.
1826
2163
 
2164
+ #### `buildTestFixMessage(failures)`
2165
+
2166
+ Compose the ONE user message the card's “Fix with Synthase” action sends.
2167
+
2168
+ It is a real turn the executor answers, so it reads like something a person
2169
+ would type: which test failed, where it lives, what the runner said, and to
2170
+ re-run it. This is a PROMPT, not UI copy — it is not translated, exactly like
2171
+ the auto-fix loop’s own `Fix these issues:` message.
2172
+
2173
+ ```typescript
2174
+ function buildTestFixMessage(failures: readonly TestFailure[]): string
2175
+ ```
2176
+
2177
+ - `failures` — The failing tests, in the order the card lists them.
2178
+
2179
+ **Returns:** The message, or `` when there is nothing to fix.
2180
+
1827
2181
  #### `ChatPanel(props)`
1828
2182
 
1829
2183
  AI chat panel with conversation history dropdown and Claude Code-style tool display.
@@ -1885,6 +2239,10 @@ function ChatPanel({
1885
2239
  version,
1886
2240
  extraCommands,
1887
2241
  feedbackUrl,
2242
+ listTests,
2243
+ runTests,
2244
+ canRunTests,
2245
+ testsAvailable,
1888
2246
  className,
1889
2247
  }: ChatPanelProps): JSX.Element
1890
2248
  ```
@@ -1963,6 +2321,18 @@ function CommandPalette({ commands, onDismiss }: CommandPaletteProps): JSX.Eleme
1963
2321
 
1964
2322
  **Returns:** The command palette element.
1965
2323
 
2324
+ #### `countByKind(tests)`
2325
+
2326
+ How many tests there are of each kind.
2327
+
2328
+ ```typescript
2329
+ function countByKind(tests: readonly TestItem[]): Record<TestKind, number>
2330
+ ```
2331
+
2332
+ - `tests` — Every discovered test.
2333
+
2334
+ **Returns:** The per-kind counts.
2335
+
1966
2336
  #### `DeviceFrameSelector(props)`
1967
2337
 
1968
2338
  A dropdown that selects the preview device frame and hosts the Rotate +
@@ -2021,6 +2391,37 @@ function EditorPanel({
2021
2391
 
2022
2392
  **Returns:** The rendered editor panel element.
2023
2393
 
2394
+ #### `failedTests(tests, results)`
2395
+
2396
+ Every listed test whose last verdict was a failure, with its output — what
2397
+ the card's fix actions send.
2398
+
2399
+ ```typescript
2400
+ function failedTests(
2401
+ tests: readonly TestItem[],
2402
+ results: Record<string, TestResultEntry>,
2403
+ ): TestFailure[]
2404
+ ```
2405
+
2406
+ - `tests` — The currently listed (and filtered) tests.
2407
+ - `results` — The per-id outcomes.
2408
+
2409
+ **Returns:** The failures, in list order.
2410
+
2411
+ #### `failRun(state, message)`
2412
+
2413
+ The state after a run that never produced a `done` event — the host's stream
2414
+ died, or its request failed before the server could answer.
2415
+
2416
+ ```typescript
2417
+ function failRun(state: TestsRunState, message: string): TestsRunState
2418
+ ```
2419
+
2420
+ - `state` — The current state.
2421
+ - `message` — What to show in the card.
2422
+
2423
+ **Returns:** The next state, no longer running.
2424
+
2024
2425
  #### `FileExplorer(props)`
2025
2426
 
2026
2427
  Tree-view file explorer component with multi-select, keyboard navigation, and drag-and-drop.
@@ -2064,6 +2465,21 @@ function filterActivitiesByType(activities: Activity[], type: ActivityType | nul
2064
2465
 
2065
2466
  **Returns:** The filtered list (a new array unless `type` is null).
2066
2467
 
2468
+ #### `filterTests(tests, query)`
2469
+
2470
+ Filters tests by a free-text query, matching (case-insensitively) against the
2471
+ file path, the row title, and the project directory. A blank query returns
2472
+ every test in input order — the same contract as `filterScripts`.
2473
+
2474
+ ```typescript
2475
+ function filterTests(tests: readonly TestItem[], query: string): TestItem[]
2476
+ ```
2477
+
2478
+ - `tests` — The tests to filter.
2479
+ - `query` — The search query.
2480
+
2481
+ **Returns:** The matching tests, in input order.
2482
+
2067
2483
  #### `formatAutoCommitBadge(state)`
2068
2484
 
2069
2485
  Formats the countdown's remaining seconds for the compact badge (e.g. `"12s"`).
@@ -2169,6 +2585,20 @@ function groupCommandsByCategory(
2169
2585
 
2170
2586
  **Returns:** One {@link CommandGroup} per non-empty category, in category order.
2171
2587
 
2588
+ #### `groupTests(tests)`
2589
+
2590
+ Group tests for display: by kind (end-to-end first), then by project
2591
+ directory (whatever directories are present — `app`, `my-app/app`,
2592
+ `packages/web`, the root last), with the files sorted inside each group.
2593
+
2594
+ ```typescript
2595
+ function groupTests(tests: readonly TestItem[]): TestGroup[]
2596
+ ```
2597
+
2598
+ - `tests` — Every discovered test.
2599
+
2600
+ **Returns:** The groups, in display order. Empty groups are never produced.
2601
+
2172
2602
  #### `hasRenderableAvatar(avatar)`
2173
2603
 
2174
2604
  Whether a stored avatar value is renderable (vs. needing the icon fallback).
@@ -2298,6 +2728,20 @@ function isReportFormValid(form: ReportFormState): boolean
2298
2728
 
2299
2729
  **Returns:** `true` when the form can be submitted.
2300
2730
 
2731
+ #### `isRowRunning(state, id)`
2732
+
2733
+ Whether a row should render as "running": the run is live and either the host
2734
+ named this row as current, or it is in the queue and has no verdict yet.
2735
+
2736
+ ```typescript
2737
+ function isRowRunning(state: TestsRunState, id: string): boolean
2738
+ ```
2739
+
2740
+ - `state` — The run state.
2741
+ - `id` — The row's test id.
2742
+
2743
+ **Returns:** True when the row is part of the live run and still undecided.
2744
+
2301
2745
  #### `isShareRole(value)`
2302
2746
 
2303
2747
  Type guard for a valid {@link ShareRole} (case-insensitive callers should
@@ -2429,6 +2873,24 @@ function parseShareCommand(input: string): ShareCommand | null
2429
2873
 
2430
2874
  **Returns:** The parsed {@link ShareCommand}, or `null`.
2431
2875
 
2876
+ #### `parseTestCommand(input)`
2877
+
2878
+ Parses a `/test [query | all]` command (`/tests` is the registered alias).
2879
+
2880
+ `all` is the one argument that ACTS: it runs everything immediately. Every
2881
+ other argument only filters the list, exactly the way `/scripts <query>` seeds
2882
+ the scripts browser — running ONE test by name is what the per-row Run button
2883
+ is for, and giving the argument a second meaning would make `/test <thing>`
2884
+ sometimes list and sometimes execute.
2885
+
2886
+ ```typescript
2887
+ function parseTestCommand(input: string): { query: string; runAll: boolean } | null
2888
+ ```
2889
+
2890
+ - `input` — The raw chat input.
2891
+
2892
+ **Returns:** `{ query, runAll }` when it is a `/test` command, else `null`.
2893
+
2432
2894
  #### `parseTimestampsCommand(input)`
2433
2895
 
2434
2896
  Parses `/timestamps [on | off]`. `show`/`hide` are accepted as synonyms.
@@ -2767,6 +3229,23 @@ function SidebarTabs({ activeTab, onTabChange, children, className }: SidebarTab
2767
3229
 
2768
3230
  **Returns:** The sidebar tabs element.
2769
3231
 
3232
+ #### `summarizeResults(tests, results)`
3233
+
3234
+ The tallies of the last run, over the tests that are still listed. Counted
3235
+ per FILE (one row, one verdict) so the collapsed summary matches the rows.
3236
+
3237
+ ```typescript
3238
+ function summarizeResults(
3239
+ tests: readonly TestItem[],
3240
+ results: Record<string, TestResultEntry>,
3241
+ ): { passed: number; failed: number; skipped: number; reported: number }
3242
+ ```
3243
+
3244
+ - `tests` — The currently listed tests.
3245
+ - `results` — The per-id outcomes.
3246
+
3247
+ **Returns:** Passed/failed/skipped file counts.
3248
+
2770
3249
  #### `TabBar(props)`
2771
3250
 
2772
3251
  Horizontally scrollable tab bar for open editor files.
@@ -2787,6 +3266,42 @@ function TabBar({
2787
3266
 
2788
3267
  **Returns:** The rendered tab bar element, or null if no tabs are open.
2789
3268
 
3269
+ #### `testRowLabel(item)`
3270
+
3271
+ The label for a row: the host's title when it gave one, else the file path.
3272
+
3273
+ ```typescript
3274
+ function testRowLabel(item: TestItem): string
3275
+ ```
3276
+
3277
+ - `item` — The test.
3278
+
3279
+ **Returns:** The label to render.
3280
+
3281
+ #### `TestsCard(props)`
3282
+
3283
+ The tests browser shown by `/test`.
3284
+
3285
+ ```typescript
3286
+ function TestsCard({
3287
+ tests,
3288
+ status,
3289
+ run,
3290
+ initialQuery,
3291
+ canRun,
3292
+ onRun,
3293
+ onCancel,
3294
+ onFix,
3295
+ fixDisabledReason,
3296
+ isLight,
3297
+ embedded,
3298
+ }: TestsCardProps): JSX.Element
3299
+ ```
3300
+
3301
+ - `props` — See {@link TestsCardProps}.
3302
+
3303
+ **Returns:** The rendered tests card.
3304
+
2790
3305
  #### `useChatTimestampsVisible()`
2791
3306
 
2792
3307
  Whether chat timestamps are turned on for every item on this device. Re-renders
@@ -3043,6 +3558,14 @@ An empty report form (the modal's initial state).
3043
3558
  const EMPTY_REPORT_FORM: ReportFormState
3044
3559
  ```
3045
3560
 
3561
+ #### `EMPTY_RUN_STATE`
3562
+
3563
+ A run that has not started.
3564
+
3565
+ ```typescript
3566
+ const EMPTY_RUN_STATE: TestsRunState
3567
+ ```
3568
+
3046
3569
  #### `IS_MAC`
3047
3570
 
3048
3571
  Detect macOS / iOS for Cmd vs Ctrl.
@@ -3059,6 +3582,30 @@ Maximum avatar source length we will render inline (~256 KB data-URI).
3059
3582
  const MAX_AVATAR_SRC_LENGTH: 262144
3060
3583
  ```
3061
3584
 
3585
+ #### `MAX_FIX_MESSAGE_OUTPUT_CHARS`
3586
+
3587
+ Total output kept across a batched fix message, so one click cannot send a novel.
3588
+
3589
+ ```typescript
3590
+ const MAX_FIX_MESSAGE_OUTPUT_CHARS: 12000
3591
+ ```
3592
+
3593
+ #### `MAX_FIX_OUTPUT_CHARS`
3594
+
3595
+ Output kept per failure when several are batched into one fix message.
3596
+
3597
+ ```typescript
3598
+ const MAX_FIX_OUTPUT_CHARS: 2000
3599
+ ```
3600
+
3601
+ #### `MAX_OUTPUT_LINES`
3602
+
3603
+ Live output lines retained — enough to read, bounded so a chatty run cannot grow forever.
3604
+
3605
+ ```typescript
3606
+ const MAX_OUTPUT_LINES: 400
3607
+ ```
3608
+
3062
3609
  #### `MAX_PANEL_PERCENT`
3063
3610
 
3064
3611
  Largest a panel may grow to, as a percentage of the container.
@@ -3232,6 +3779,36 @@ Peer dependencies:
3232
3779
  - A custom card is a critical event only when its factory sets
3233
3780
  `critical: true` (a failure, a blocked action, a limit that stopped work) —
3234
3781
  the package never infers it from tone or copy.
3782
+ - `ChatPanel` owns no pricing or auth routes, so every limit/upgrade button
3783
+ comes from the host's `buildUpgradeCta(context)`. The context forwards the
3784
+ backend's whole description of the refusal: `requiresSignup`, `limitType`
3785
+ (the rule that fired), `billingAction` (the remedy it resolved — e.g.
3786
+ `add_funds`, `add_payment_method`, `raise_spend_cap`, `upgrade`, `none`) and
3787
+ `upgradeTier` (`null` = no higher plan). Branch on `billingAction` first;
3788
+ every field is optional, so keep a fallback. A live limit banner and a
3789
+ recorded card that declares the same `coversLimitType` state one fact, so
3790
+ the card steps aside while the banner is up — which only reads as one
3791
+ consistent message if the banner's button is resolved from the same
3792
+ `billingAction` the card used.
3793
+ - **`/test` lists and runs the project's tests** (`TestsCard`, opened by the
3794
+ `/test` command — `/tests` is a registered alias — in the same closeable
3795
+ overlay as `/scripts` and `/skills`, and renderable in the timeline as the
3796
+ `tests` system card). It owns no routes: pass `listTests` and `runTests`
3797
+ and the command works; omit either and it has nothing to run.
3798
+ `runTests(selection, onEvent)` returns a handle whose `cancel()` must
3799
+ really stop the run, and it must deliver exactly one `done` event however
3800
+ the run ends — including when the request never opened — or the card spins
3801
+ forever. `canRunTests` (a viewer: false) and `testsAvailable` (the
3802
+ environment is up) each disable the run controls and state their own
3803
+ reason in the card. The RUN is owned by `ChatPanel`, not the card, so it
3804
+ keeps streaming while the overlay is closed. `/test all` runs everything;
3805
+ any other argument only filters the list.
3806
+ - **End-to-end specs are meant to run against the LIVE PREVIEW.** The host
3807
+ should drive them through `@molecule/app-e2e-preview` (what every
3808
+ `mlcl create` app already bonds in `e2e/bonds.ts`), not a browser binary —
3809
+ in a sandbox there is none. That bond drives the page the person is
3810
+ actually looking at, so **a preview must be open** somewhere or the driver
3811
+ waits and fails; the card says so next to the end-to-end group.
3235
3812
  - Text routes through `t('ide.*')` — `@molecule/app-locales-ide` supplies
3236
3813
  translations.
3237
3814