@blackbelt-technology/pi-agent-dashboard 0.3.0 → 0.4.1

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 (216) hide show
  1. package/AGENTS.md +87 -114
  2. package/README.md +408 -430
  3. package/docs/architecture.md +465 -12
  4. package/package.json +10 -5
  5. package/packages/extension/package.json +14 -4
  6. package/packages/extension/src/__tests__/ask-user-tool.test.ts +40 -8
  7. package/packages/extension/src/__tests__/bridge-entry-id-pi-070.test.ts +174 -0
  8. package/packages/extension/src/__tests__/enrich-model-metadata.test.ts +201 -0
  9. package/packages/extension/src/__tests__/event-forwarder.test.ts +30 -0
  10. package/packages/extension/src/__tests__/fork-entryid-timing.test.ts +64 -76
  11. package/packages/extension/src/__tests__/git-info.test.ts +67 -55
  12. package/packages/extension/src/__tests__/multiselect-list.test.ts +137 -0
  13. package/packages/extension/src/__tests__/no-session-replacement-calls.test.ts +99 -0
  14. package/packages/extension/src/__tests__/openspec-poller.test.ts +101 -96
  15. package/packages/extension/src/__tests__/process-scanner-kill.test.ts +61 -0
  16. package/packages/extension/src/__tests__/provider-register-reload.test.ts +394 -0
  17. package/packages/extension/src/__tests__/server-auto-start.test.ts +95 -4
  18. package/packages/extension/src/__tests__/server-launcher.test.ts +16 -0
  19. package/packages/extension/src/ask-user-tool.ts +5 -4
  20. package/packages/extension/src/bridge.ts +171 -17
  21. package/packages/extension/src/dev-build.ts +1 -1
  22. package/packages/extension/src/git-info.ts +9 -19
  23. package/packages/extension/src/multiselect-list.ts +146 -0
  24. package/packages/extension/src/multiselect-polyfill.ts +43 -0
  25. package/packages/extension/src/pi-env.d.ts +1 -0
  26. package/packages/extension/src/process-scanner.ts +72 -38
  27. package/packages/extension/src/provider-register.ts +304 -16
  28. package/packages/extension/src/server-auto-start.ts +27 -1
  29. package/packages/extension/src/server-launcher.ts +83 -27
  30. package/packages/server/package.json +16 -2
  31. package/packages/server/src/__tests__/bootstrap-queue.test.ts +120 -0
  32. package/packages/server/src/__tests__/bootstrap-routes.test.ts +125 -0
  33. package/packages/server/src/__tests__/bootstrap-state.test.ts +119 -0
  34. package/packages/server/src/__tests__/browse-endpoint.test.ts +17 -0
  35. package/packages/server/src/__tests__/cli-parse.test.ts +11 -0
  36. package/packages/server/src/__tests__/concurrent-launch.test.ts +110 -0
  37. package/packages/server/src/__tests__/config-api.test.ts +68 -0
  38. package/packages/server/src/__tests__/crash-recovery.test.ts +88 -0
  39. package/packages/server/src/__tests__/directory-service.test.ts +234 -8
  40. package/packages/server/src/__tests__/editor-registry.test.ts +28 -15
  41. package/packages/server/src/__tests__/extension-register-appimage.test.ts +5 -1
  42. package/packages/server/src/__tests__/extension-register.test.ts +3 -1
  43. package/packages/server/src/__tests__/find-port-holders.test.ts +94 -0
  44. package/packages/server/src/__tests__/fixtures/fork-jsonl-roundtrip.jsonl +8 -0
  45. package/packages/server/src/__tests__/force-kill-handler.test.ts +57 -8
  46. package/packages/server/src/__tests__/fork-jsonl-roundtrip.test.ts +49 -0
  47. package/packages/server/src/__tests__/home-lock-escape-hatch.test.ts +60 -0
  48. package/packages/server/src/__tests__/home-lock-release.test.ts +85 -0
  49. package/packages/server/src/__tests__/home-lock.test.ts +308 -0
  50. package/packages/server/src/__tests__/is-pi-process.test.ts +36 -0
  51. package/packages/server/src/__tests__/node-guard.test.ts +85 -0
  52. package/packages/server/src/__tests__/package-manager-wrapper-resolve.test.ts +5 -1
  53. package/packages/server/src/__tests__/package-manager-wrapper.test.ts +45 -10
  54. package/packages/server/src/__tests__/pi-version-skew.test.ts +237 -0
  55. package/packages/server/src/__tests__/preferences-store.test.ts +73 -4
  56. package/packages/server/src/__tests__/process-manager.test.ts +45 -18
  57. package/packages/server/src/__tests__/provider-probe.test.ts +287 -0
  58. package/packages/server/src/__tests__/provider-test-route.test.ts +149 -0
  59. package/packages/server/src/__tests__/restart-helper.test.ts +111 -0
  60. package/packages/server/src/__tests__/session-action-handler-headless-reload.test.ts +467 -0
  61. package/packages/server/src/__tests__/session-action-handler-reload-predicate.test.ts +73 -0
  62. package/packages/server/src/__tests__/session-action-handler-spawn-error.test.ts +74 -0
  63. package/packages/server/src/__tests__/terminal-manager.test.ts +41 -1
  64. package/packages/server/src/__tests__/tool-routes.test.ts +277 -0
  65. package/packages/server/src/__tests__/trusted-networks-config.test.ts +19 -0
  66. package/packages/server/src/__tests__/trusted-networks-no-oauth-roundtrip.test.ts +126 -0
  67. package/packages/server/src/__tests__/tunnel-cleanup.test.ts +90 -0
  68. package/packages/server/src/__tests__/tunnel.test.ts +13 -7
  69. package/packages/server/src/__tests__/wsl-tmux-probe-cache.test.ts +44 -0
  70. package/packages/server/src/bootstrap-queue.ts +130 -0
  71. package/packages/server/src/bootstrap-state.ts +131 -0
  72. package/packages/server/src/browse.ts +8 -3
  73. package/packages/server/src/browser-handlers/directory-handler.ts +23 -8
  74. package/packages/server/src/browser-handlers/session-action-handler.ts +213 -79
  75. package/packages/server/src/browser-handlers/session-action-helpers.ts +36 -0
  76. package/packages/server/src/cli.ts +310 -39
  77. package/packages/server/src/config-api.ts +16 -0
  78. package/packages/server/src/directory-service.ts +270 -39
  79. package/packages/server/src/editor-detection.ts +12 -9
  80. package/packages/server/src/editor-manager.ts +19 -4
  81. package/packages/server/src/editor-pid-registry.ts +9 -8
  82. package/packages/server/src/editor-registry.ts +22 -25
  83. package/packages/server/src/git-operations.ts +1 -1
  84. package/packages/server/src/headless-pid-registry.ts +7 -20
  85. package/packages/server/src/home-lock-release.ts +72 -0
  86. package/packages/server/src/home-lock.ts +389 -0
  87. package/packages/server/src/node-guard.ts +52 -0
  88. package/packages/server/src/package-manager-wrapper.ts +207 -47
  89. package/packages/server/src/pi-core-checker.ts +1 -1
  90. package/packages/server/src/pi-core-updater.ts +7 -1
  91. package/packages/server/src/pi-resource-scanner.ts +5 -8
  92. package/packages/server/src/pi-version-skew.ts +207 -0
  93. package/packages/server/src/preferences-store.ts +17 -3
  94. package/packages/server/src/process-manager.ts +403 -222
  95. package/packages/server/src/provider-probe.ts +234 -0
  96. package/packages/server/src/restart-helper.ts +141 -0
  97. package/packages/server/src/routes/bootstrap-routes.ts +88 -0
  98. package/packages/server/src/routes/openspec-routes.ts +25 -1
  99. package/packages/server/src/routes/pi-core-routes.ts +24 -1
  100. package/packages/server/src/routes/provider-auth-routes.ts +8 -8
  101. package/packages/server/src/routes/provider-routes.ts +43 -0
  102. package/packages/server/src/routes/recommended-routes.ts +10 -12
  103. package/packages/server/src/routes/system-routes.ts +20 -33
  104. package/packages/server/src/routes/tool-routes.ts +153 -0
  105. package/packages/server/src/server-pid.ts +5 -9
  106. package/packages/server/src/server.ts +211 -10
  107. package/packages/server/src/session-api.ts +77 -8
  108. package/packages/server/src/session-bootstrap.ts +17 -3
  109. package/packages/server/src/session-diff.ts +21 -21
  110. package/packages/server/src/terminal-manager.ts +61 -20
  111. package/packages/server/src/tunnel.ts +42 -28
  112. package/packages/shared/package.json +10 -3
  113. package/packages/shared/src/__tests__/{tool-resolver.test.ts → binary-lookup.test.ts} +32 -12
  114. package/packages/shared/src/__tests__/bootstrap/README.md +133 -0
  115. package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +370 -0
  116. package/packages/shared/src/__tests__/bootstrap/assertions.ts +136 -0
  117. package/packages/shared/src/__tests__/bootstrap/cube.test.ts +47 -0
  118. package/packages/shared/src/__tests__/bootstrap/cube.ts +66 -0
  119. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +83 -0
  120. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +89 -0
  121. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +33 -0
  122. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/d-overrides.test.ts.snap +20 -0
  123. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +61 -0
  124. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +33 -0
  125. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +46 -0
  126. package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/j-path-gui-minimal.test.ts.snap +12 -0
  127. package/packages/shared/src/__tests__/bootstrap/families/a-electron.test.ts +156 -0
  128. package/packages/shared/src/__tests__/bootstrap/families/b-npm-global.test.ts +157 -0
  129. package/packages/shared/src/__tests__/bootstrap/families/c-dev-monorepo.test.ts +102 -0
  130. package/packages/shared/src/__tests__/bootstrap/families/d-overrides.test.ts +76 -0
  131. package/packages/shared/src/__tests__/bootstrap/families/e-stale-partial.test.ts +94 -0
  132. package/packages/shared/src/__tests__/bootstrap/families/f-cwd-variants.test.ts +87 -0
  133. package/packages/shared/src/__tests__/bootstrap/families/g-windows-specifics.test.ts +143 -0
  134. package/packages/shared/src/__tests__/bootstrap/families/h-home-drift.test.ts +64 -0
  135. package/packages/shared/src/__tests__/bootstrap/families/i-malformed-settings.test.ts +77 -0
  136. package/packages/shared/src/__tests__/bootstrap/families/index.ts +19 -0
  137. package/packages/shared/src/__tests__/bootstrap/families/j-path-gui-minimal.test.ts +61 -0
  138. package/packages/shared/src/__tests__/bootstrap/families/k-dashboard-absent.test.ts +50 -0
  139. package/packages/shared/src/__tests__/bootstrap/families/l-instance-coordination.test.ts +272 -0
  140. package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +58 -0
  141. package/packages/shared/src/__tests__/bootstrap/fixtures/electron-layout.ts +84 -0
  142. package/packages/shared/src/__tests__/bootstrap/fixtures/index.ts +9 -0
  143. package/packages/shared/src/__tests__/bootstrap/fixtures/managed-install.ts +85 -0
  144. package/packages/shared/src/__tests__/bootstrap/fixtures/npm-global-layout.ts +122 -0
  145. package/packages/shared/src/__tests__/bootstrap/fixtures/pi-versions.ts +36 -0
  146. package/packages/shared/src/__tests__/bootstrap/fixtures/settings-json.ts +39 -0
  147. package/packages/shared/src/__tests__/bootstrap/harness.smoke.test.ts +220 -0
  148. package/packages/shared/src/__tests__/bootstrap/harness.ts +413 -0
  149. package/packages/shared/src/__tests__/bootstrap/scenarios-skipped.ts +125 -0
  150. package/packages/shared/src/__tests__/bootstrap/scenarios.ts +132 -0
  151. package/packages/shared/src/__tests__/bridge-register.test.ts +29 -6
  152. package/packages/shared/src/__tests__/config-openspec.test.ts +106 -0
  153. package/packages/shared/src/__tests__/config.test.ts +56 -0
  154. package/packages/shared/src/__tests__/detached-spawn.test.ts +243 -0
  155. package/packages/shared/src/__tests__/managed-paths.test.ts +60 -0
  156. package/packages/shared/src/__tests__/no-direct-child-process.test.ts +112 -0
  157. package/packages/shared/src/__tests__/no-direct-platform-branch.test.ts +174 -0
  158. package/packages/shared/src/__tests__/no-direct-process-kill.test.ts +105 -0
  159. package/packages/shared/src/__tests__/no-hardcoded-node-modules-paths.test.ts +176 -0
  160. package/packages/shared/src/__tests__/no-raw-node-import.test.ts +146 -0
  161. package/packages/shared/src/__tests__/node-spawn.test.ts +210 -0
  162. package/packages/shared/src/__tests__/platform-commands.test.ts +108 -0
  163. package/packages/shared/src/__tests__/platform-exec.test.ts +103 -0
  164. package/packages/shared/src/__tests__/platform-git.test.ts +194 -0
  165. package/packages/shared/src/__tests__/platform-npm.test.ts +137 -0
  166. package/packages/shared/src/__tests__/platform-openspec.test.ts +92 -0
  167. package/packages/shared/src/__tests__/platform-paths.test.ts +284 -0
  168. package/packages/shared/src/__tests__/platform-process-scan.test.ts +55 -0
  169. package/packages/shared/src/__tests__/platform-process.test.ts +160 -0
  170. package/packages/shared/src/__tests__/platform-runner.test.ts +173 -0
  171. package/packages/shared/src/__tests__/platform-shell.test.ts +74 -0
  172. package/packages/shared/src/__tests__/process-identify.test.ts +113 -0
  173. package/packages/shared/src/__tests__/recommended-extensions.test.ts +40 -7
  174. package/packages/shared/src/__tests__/resolve-jiti.test.ts +43 -7
  175. package/packages/shared/src/__tests__/resolve-tool-cli.test.ts +105 -0
  176. package/packages/shared/src/__tests__/semaphore.test.ts +119 -0
  177. package/packages/shared/src/__tests__/spawn-mechanism.test.ts +131 -0
  178. package/packages/shared/src/__tests__/state-replay-entry-id.test.ts +69 -0
  179. package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +239 -0
  180. package/packages/shared/src/__tests__/tool-registry-overrides.test.ts +137 -0
  181. package/packages/shared/src/__tests__/tool-registry-registry.test.ts +343 -0
  182. package/packages/shared/src/bootstrap-install.ts +212 -0
  183. package/packages/shared/src/bridge-register.ts +87 -20
  184. package/packages/shared/src/browser-protocol.ts +71 -1
  185. package/packages/shared/src/config.ts +87 -15
  186. package/packages/shared/src/managed-paths.ts +31 -4
  187. package/packages/shared/src/openspec-poller.ts +63 -46
  188. package/packages/shared/src/{tool-resolver.ts → platform/binary-lookup.ts} +125 -25
  189. package/packages/shared/src/platform/commands.ts +100 -0
  190. package/packages/shared/src/platform/detached-spawn.ts +305 -0
  191. package/packages/shared/src/platform/exec.ts +220 -0
  192. package/packages/shared/src/platform/git.ts +155 -0
  193. package/packages/shared/src/platform/index.ts +16 -0
  194. package/packages/shared/src/platform/node-spawn.ts +154 -0
  195. package/packages/shared/src/platform/npm.ts +162 -0
  196. package/packages/shared/src/platform/openspec.ts +91 -0
  197. package/packages/shared/src/platform/paths.ts +276 -0
  198. package/packages/shared/src/platform/process-identify.ts +126 -0
  199. package/packages/shared/src/platform/process-scan.ts +94 -0
  200. package/packages/shared/src/platform/process.ts +168 -0
  201. package/packages/shared/src/platform/runner.ts +369 -0
  202. package/packages/shared/src/platform/shell.ts +44 -0
  203. package/packages/shared/src/platform/spawn-mechanism.ts +124 -0
  204. package/packages/shared/src/platform/subprocess-adapter.ts +124 -0
  205. package/packages/shared/src/protocol.ts +23 -0
  206. package/packages/shared/src/recommended-extensions.ts +18 -2
  207. package/packages/shared/src/resolve-jiti.ts +62 -3
  208. package/packages/shared/src/rest-api.ts +26 -0
  209. package/packages/shared/src/semaphore.ts +83 -0
  210. package/packages/shared/src/state-replay.ts +9 -0
  211. package/packages/shared/src/tool-registry/definitions.ts +434 -0
  212. package/packages/shared/src/tool-registry/index.ts +56 -0
  213. package/packages/shared/src/tool-registry/overrides.ts +118 -0
  214. package/packages/shared/src/tool-registry/registry.ts +262 -0
  215. package/packages/shared/src/tool-registry/strategies.ts +198 -0
  216. package/packages/shared/src/tool-registry/types.ts +180 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Cube sweep — fail-closed check that every cell is either registered
3
+ * or explicitly skipped with a reason.
4
+ *
5
+ * See openspec/changes/bootstrap-resolution-harness/design.md §5.
6
+ */
7
+ import {
8
+ REGISTERED_SCENARIOS,
9
+ SKIPPED_SCENARIOS,
10
+ cellKey,
11
+ enumerateCube,
12
+ type ScenarioCell,
13
+ } from "./scenarios.js";
14
+
15
+ export interface CubeReport {
16
+ total: number;
17
+ registered: number;
18
+ skipped: number;
19
+ unclassified: ScenarioCell[];
20
+ }
21
+
22
+ /**
23
+ * Sweep the cube. Returns a report with the list of cells that have
24
+ * no test and no skip marker.
25
+ */
26
+ export function sweepCube(): CubeReport {
27
+ const all = enumerateCube();
28
+ const unclassified: ScenarioCell[] = [];
29
+ let registered = 0;
30
+ let skipped = 0;
31
+ for (const cell of all) {
32
+ const key = cellKey(cell);
33
+ if (REGISTERED_SCENARIOS.has(key)) {
34
+ registered++;
35
+ } else if (SKIPPED_SCENARIOS.has(key)) {
36
+ skipped++;
37
+ } else {
38
+ unclassified.push(cell);
39
+ }
40
+ }
41
+ return { total: all.length, registered, skipped, unclassified };
42
+ }
43
+
44
+ /**
45
+ * Build a human-readable error message for unclassified cells.
46
+ * Truncates at a sensible length; full list printed to stderr.
47
+ */
48
+ export function formatUnclassifiedError(report: CubeReport): string {
49
+ const lines: string[] = [];
50
+ lines.push(
51
+ `Cube sweep: ${report.unclassified.length} unclassified cell(s) ` +
52
+ `(${report.registered} registered, ${report.skipped} skipped, ${report.total} total).`,
53
+ );
54
+ lines.push("");
55
+ lines.push("Add each to either REGISTERED_SCENARIOS (via a family-test file) or");
56
+ lines.push('SKIPPED_SCENARIOS with a reason (via scenarios-skipped.ts).');
57
+ lines.push("");
58
+ const preview = report.unclassified.slice(0, 20);
59
+ for (const cell of preview) {
60
+ lines.push(` ${cellKey(cell)}`);
61
+ }
62
+ if (report.unclassified.length > preview.length) {
63
+ lines.push(` ... and ${report.unclassified.length - preview.length} more`);
64
+ }
65
+ return lines.join("\n");
66
+ }
@@ -0,0 +1,83 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family A — electron-packaged > A1 — electron-fresh (bundled dashboard, no pi) > resolves nothing for pi (darwin) 1`] = `
4
+ "name: pi
5
+ ok: false
6
+ source: —
7
+ path: —
8
+ tried:
9
+ override no override set
10
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
11
+ where not found on PATH"
12
+ `;
13
+
14
+ exports[`Family A — electron-packaged > A1 — electron-fresh (bundled dashboard, no pi) > resolves nothing for pi (linux) 1`] = `
15
+ "name: pi
16
+ ok: false
17
+ source: —
18
+ path: —
19
+ tried:
20
+ override no override set
21
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
22
+ where not found on PATH"
23
+ `;
24
+
25
+ exports[`Family A — electron-packaged > A1 — electron-fresh (bundled dashboard, no pi) > resolves nothing for pi (win32) 1`] = `
26
+ "name: pi
27
+ ok: false
28
+ source: —
29
+ path: —
30
+ tried:
31
+ override no override set
32
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
33
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
34
+ managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
35
+ managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
36
+ npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
37
+ npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
38
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
39
+ where not found on PATH"
40
+ `;
41
+
42
+ exports[`Family A — electron-packaged > A2 — electron-prewarmed (bundled + managed pi) > resolves pi via managed (darwin) 1`] = `
43
+ "name: pi
44
+ ok: true
45
+ source: managed
46
+ path: <HOME>/.pi-dashboard/node_modules/.bin/pi
47
+ tried:
48
+ override no override set
49
+ managed ok"
50
+ `;
51
+
52
+ exports[`Family A — electron-packaged > A2 — electron-prewarmed (bundled + managed pi) > resolves pi via managed (linux) 1`] = `
53
+ "name: pi
54
+ ok: true
55
+ source: managed
56
+ path: <HOME>/.pi-dashboard/node_modules/.bin/pi
57
+ tried:
58
+ override no override set
59
+ managed ok"
60
+ `;
61
+
62
+ exports[`Family A — electron-packaged > A2 — electron-prewarmed (bundled + managed pi) > resolves pi via managed (win32) 1`] = `
63
+ "name: pi
64
+ ok: true
65
+ source: managed
66
+ path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
67
+ tried:
68
+ override no override set
69
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
70
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
71
+ managed ok"
72
+ `;
73
+
74
+ exports[`Family A — electron-packaged > A3 — electron + pre-existing global pi > global npm pi takes precedence over managed-bin fallback (linux) 1`] = `
75
+ "name: pi
76
+ ok: true
77
+ source: system
78
+ path: /usr/local/bin/pi
79
+ tried:
80
+ override no override set
81
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
82
+ where ok"
83
+ `;
@@ -0,0 +1,89 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family B — npm-global > B1 — npm-g dash-only (⚠ captures current Windows bug) > pi unresolved on darwin 1`] = `
4
+ "name: pi
5
+ ok: false
6
+ source: —
7
+ path: —
8
+ tried:
9
+ override no override set
10
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
11
+ where not found on PATH"
12
+ `;
13
+
14
+ exports[`Family B — npm-global > B1 — npm-g dash-only (⚠ captures current Windows bug) > pi unresolved on linux 1`] = `
15
+ "name: pi
16
+ ok: false
17
+ source: —
18
+ path: —
19
+ tried:
20
+ override no override set
21
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
22
+ where not found on PATH"
23
+ `;
24
+
25
+ exports[`Family B — npm-global > B1 — npm-g dash-only (⚠ captures current Windows bug) > pi unresolved on win32 1`] = `
26
+ "name: pi
27
+ ok: false
28
+ source: —
29
+ path: —
30
+ tried:
31
+ override no override set
32
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
33
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
34
+ managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
35
+ managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
36
+ npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
37
+ npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
38
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
39
+ where not found on PATH"
40
+ `;
41
+
42
+ exports[`Family B — npm-global > B2 — npm-g full (pi + openspec via global npm) > pi resolves via npm-global on darwin 1`] = `
43
+ "name: pi
44
+ ok: true
45
+ source: system
46
+ path: /usr/local/bin/pi
47
+ tried:
48
+ override no override set
49
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
50
+ where ok"
51
+ `;
52
+
53
+ exports[`Family B — npm-global > B2 — npm-g full (pi + openspec via global npm) > pi resolves via npm-global on linux 1`] = `
54
+ "name: pi
55
+ ok: true
56
+ source: system
57
+ path: /usr/local/bin/pi
58
+ tried:
59
+ override no override set
60
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
61
+ where ok"
62
+ `;
63
+
64
+ exports[`Family B — npm-global > B2 — npm-g full (pi + openspec via global npm) > pi resolves via npm-global on win32 (with node.exe toArgv) 1`] = `
65
+ "name: pi
66
+ ok: true
67
+ source: npm-global
68
+ path: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
69
+ tried:
70
+ override no override set
71
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
72
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
73
+ managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
74
+ managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
75
+ npm-global ok
76
+ argv:
77
+ - C:/Program Files/nodejs/node.exe
78
+ - <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js"
79
+ `;
80
+
81
+ exports[`Family B — npm-global > B3 — npm-g pi-installed-first (bridge needs registration) > settings.json present but lacks bridge entry (linux) 1`] = `
82
+ "settings-delta:
83
+ added:
84
+ (none)
85
+ removed:
86
+ (none)
87
+ preserved:
88
+ (none)"
89
+ `;
@@ -0,0 +1,33 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family C — dev monorepo > C1 — posix (managed/where chain, no bare-import for pi) > pi chain runs on darwin 1`] = `
4
+ "name: pi
5
+ ok: false
6
+ source: —
7
+ path: —
8
+ tried:
9
+ override no override set
10
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
11
+ where not found on PATH"
12
+ `;
13
+
14
+ exports[`Family C — dev monorepo > C1 — posix (managed/where chain, no bare-import for pi) > pi chain runs on linux 1`] = `
15
+ "name: pi
16
+ ok: false
17
+ source: —
18
+ path: —
19
+ tried:
20
+ override no override set
21
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
22
+ where not found on PATH"
23
+ `;
24
+
25
+ exports[`Family C — dev monorepo > C2 — win32 (bare-import from workspace) > resolves pi via workspace bare-import 1`] = `
26
+ "name: pi
27
+ ok: true
28
+ source: bare-import
29
+ path: dist/cli.js
30
+ tried:
31
+ override no override set
32
+ bare-import ok"
33
+ `;
@@ -0,0 +1,20 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family D — overrides > D1 — override-valid: pi resolves via override 1`] = `
4
+ "name: pi
5
+ ok: true
6
+ source: override
7
+ path: /opt/custom/bin/pi
8
+ tried:
9
+ override ok"
10
+ `;
11
+
12
+ exports[`Family D — overrides > D2 — override-invalid: path doesn't exist, chain falls through 1`] = `
13
+ "name: pi
14
+ ok: true
15
+ source: managed
16
+ path: <HOME>/.pi-dashboard/node_modules/.bin/pi
17
+ tried:
18
+ override invalid: path does not exist: /nonexistent/broken/pi
19
+ managed ok"
20
+ `;
@@ -0,0 +1,61 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family E — stale / partial > E1 — stale managed pi (old version) > current strategies resolve without version gating (darwin) 1`] = `
4
+ "name: pi
5
+ ok: true
6
+ source: managed
7
+ path: <HOME>/.pi-dashboard/node_modules/.bin/pi
8
+ tried:
9
+ override no override set
10
+ managed ok"
11
+ `;
12
+
13
+ exports[`Family E — stale / partial > E1 — stale managed pi (old version) > current strategies resolve without version gating (linux) 1`] = `
14
+ "name: pi
15
+ ok: true
16
+ source: managed
17
+ path: <HOME>/.pi-dashboard/node_modules/.bin/pi
18
+ tried:
19
+ override no override set
20
+ managed ok"
21
+ `;
22
+
23
+ exports[`Family E — stale / partial > E1 — stale managed pi (old version) > current strategies resolve without version gating (win32) 1`] = `
24
+ "name: pi
25
+ ok: true
26
+ source: managed
27
+ path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
28
+ tried:
29
+ override no override set
30
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
31
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
32
+ managed ok"
33
+ `;
34
+
35
+ exports[`Family E — stale / partial > E2 — partial managed install (package.json, no dist) > strategy skips when entry file absent (linux) 1`] = `
36
+ "name: pi
37
+ ok: false
38
+ source: —
39
+ path: —
40
+ tried:
41
+ override no override set
42
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
43
+ where not found on PATH"
44
+ `;
45
+
46
+ exports[`Family E — stale / partial > E2 — partial managed install (package.json, no dist) > strategy skips when entry file absent (win32) 1`] = `
47
+ "name: pi
48
+ ok: false
49
+ source: —
50
+ path: —
51
+ tried:
52
+ override no override set
53
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
54
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
55
+ managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
56
+ managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
57
+ npm-global missing: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
58
+ npm-global missing: <NPM_ROOT>/@oh-my-pi/pi-coding-agent/dist/cli.js
59
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi.cmd
60
+ where not found on PATH"
61
+ `;
@@ -0,0 +1,33 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family F — cwd variants > F1 — resolves normally with Program Files (x86) cwd (win32) 1`] = `
4
+ "name: pi
5
+ ok: true
6
+ source: managed
7
+ path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
8
+ tried:
9
+ override no override set
10
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
11
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
12
+ managed ok"
13
+ `;
14
+
15
+ exports[`Family F — cwd variants > F1 — resolves normally with spaces in cwd (linux) 1`] = `
16
+ "name: pi
17
+ ok: true
18
+ source: managed
19
+ path: <HOME>/.pi-dashboard/node_modules/.bin/pi
20
+ tried:
21
+ override no override set
22
+ managed ok"
23
+ `;
24
+
25
+ exports[`Family F — cwd variants > F2 — resolves with Greek/Cyrillic/emoji in cwd 1`] = `
26
+ "name: pi
27
+ ok: true
28
+ source: managed
29
+ path: <HOME>/.pi-dashboard/node_modules/.bin/pi
30
+ tried:
31
+ override no override set
32
+ managed ok"
33
+ `;
@@ -0,0 +1,46 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family G — Windows specifics > G1 — pi.cmd resolved + toArgv prepends node.exe (no-cmd-flash) 1`] = `
4
+ "name: pi
5
+ ok: true
6
+ source: managed
7
+ path: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
8
+ tried:
9
+ override no override set
10
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
11
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
12
+ managed ok
13
+ argv:
14
+ - C:/Program Files/nodejs/node.exe
15
+ - <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js"
16
+ `;
17
+
18
+ exports[`Family G — Windows specifics > G2 — npm-g at %APPDATA%\\Roaming\\npm (argv prepends node.exe) 1`] = `
19
+ "name: pi
20
+ ok: true
21
+ source: npm-global
22
+ path: <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js
23
+ tried:
24
+ override no override set
25
+ bare-import cannot resolve @mariozechner/pi-coding-agent/package.json
26
+ bare-import cannot resolve @oh-my-pi/pi-coding-agent/package.json
27
+ managed missing: <HOME>/.pi-dashboard/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
28
+ managed missing: <HOME>/.pi-dashboard/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js
29
+ npm-global ok
30
+ argv:
31
+ - C:/Program Files/nodejs/node.exe
32
+ - <NPM_ROOT>/@mariozechner/pi-coding-agent/dist/cli.js"
33
+ `;
34
+
35
+ exports[`Family G — Windows specifics > G4 — node.exe at C:\\Program Files\\nodejs\\node.exe 1`] = `
36
+ "name: node
37
+ ok: true
38
+ source: system
39
+ path: C:/Program Files/nodejs/node.exe
40
+ tried:
41
+ override no override set
42
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/node.cmd
43
+ where ok
44
+ argv:
45
+ - C:/Program Files/nodejs/node.exe"
46
+ `;
@@ -0,0 +1,12 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Family J — minimal PATH > J1 — GUI-launched minimal PATH: pi does NOT resolve on posix (limitation) 1`] = `
4
+ "name: pi
5
+ ok: false
6
+ source: —
7
+ path: —
8
+ tried:
9
+ override no override set
10
+ managed missing: <HOME>/.pi-dashboard/node_modules/.bin/pi
11
+ where not found on PATH"
12
+ `;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Family A — Electron-packaged dashboard scenarios.
3
+ *
4
+ * A1: electron-fresh — bundled dashboard, no pi, no settings.
5
+ * A2: electron-prewarmed — bundled dashboard, managed pi+openspec, valid settings.
6
+ *
7
+ * Full family (A3, A4) lands as cells are activated in tasks.md §6.
8
+ */
9
+ import { describe, expect, it } from "vitest";
10
+ import { withFakeEnv, layer } from "../harness.js";
11
+ import { registerDefaultTools } from "../../../tool-registry/definitions.js";
12
+ import * as fixtures from "../fixtures/index.js";
13
+ import { snapshotTrail } from "../assertions.js";
14
+ import { register, SKIPPED_SCENARIOS, cellKey } from "../scenarios.js";
15
+
16
+ // Register the cells this file covers. Overrides the bulk-skip from
17
+ // scenarios-skipped.ts. Runs at module import.
18
+ const A1: Parameters<typeof register>[0][] = [
19
+ { platform: "linux", dash: "electron", pi: "absent", settings: "missing", env: "normal" },
20
+ { platform: "darwin", dash: "electron", pi: "absent", settings: "missing", env: "normal" },
21
+ { platform: "win32", dash: "electron", pi: "absent", settings: "missing", env: "normal" },
22
+ ];
23
+ const A2: Parameters<typeof register>[0][] = [
24
+ { platform: "linux", dash: "electron", pi: "present-valid", settings: "valid", env: "normal" },
25
+ { platform: "darwin", dash: "electron", pi: "present-valid", settings: "valid", env: "normal" },
26
+ { platform: "win32", dash: "electron", pi: "present-valid", settings: "valid", env: "normal" },
27
+ ];
28
+ // A3: electron bundled + global pi (user had pi before installing dashboard).
29
+ const A3: Parameters<typeof register>[0][] = [
30
+ { platform: "linux", dash: "electron", pi: "present-no-ext", settings: "valid", env: "normal" },
31
+ ];
32
+ // A4: linux AppImage first run (temp mount; bridge registration skipped).
33
+ const A4: Parameters<typeof register>[0][] = [
34
+ { platform: "linux", dash: "electron", pi: "appimage-tmp", settings: "missing", env: "normal" },
35
+ ];
36
+ for (const cell of [...A1, ...A2, ...A3, ...A4]) {
37
+ register(cell, "families/a-electron.test.ts");
38
+ SKIPPED_SCENARIOS.delete(cellKey(cell));
39
+ }
40
+
41
+ describe("Family A — electron-packaged", () => {
42
+ describe("A1 — electron-fresh (bundled dashboard, no pi)", () => {
43
+ it.each(["linux", "darwin", "win32"] as const)(
44
+ "resolves nothing for pi (%s)",
45
+ async (platform) => {
46
+ const homedir = platform === "win32" ? "C:\\Users\\R" : "/home/r";
47
+ await withFakeEnv(
48
+ {
49
+ platform,
50
+ homedir,
51
+ env: { PATH: platform === "win32" ? "C:\\Windows\\System32" : "/usr/bin" },
52
+ fs: layer(fixtures.electronPackaged({ platform })),
53
+ },
54
+ (ctx) => {
55
+ const registry = ctx.createRegistry();
56
+ registerDefaultTools(registry, ctx.createStrategyDeps());
57
+ const res = registry.resolve("pi");
58
+ expect(res.ok).toBe(false);
59
+ expect(snapshotTrail(res, ctx)).toMatchSnapshot();
60
+ },
61
+ );
62
+ },
63
+ );
64
+ });
65
+
66
+ describe("A2 — electron-prewarmed (bundled + managed pi)", () => {
67
+ it.each(["linux", "darwin", "win32"] as const)(
68
+ "resolves pi via managed (%s)",
69
+ async (platform) => {
70
+ const homedir = platform === "win32" ? "C:\\Users\\R" : "/home/r";
71
+ await withFakeEnv(
72
+ {
73
+ platform,
74
+ homedir,
75
+ env: { PATH: platform === "win32" ? "C:\\Windows\\System32" : "/usr/bin" },
76
+ fs: layer(
77
+ fixtures.electronPackaged({ platform }),
78
+ fixtures.managedInstall({ homedir, platform }),
79
+ fixtures.settingsJson({
80
+ homedir,
81
+ platform,
82
+ packages: [
83
+ platform === "win32"
84
+ ? "C:\\Program Files\\PI Dashboard\\resources\\server\\packages\\extension"
85
+ : "/usr/lib/pi-dashboard/resources/server/packages/extension",
86
+ ],
87
+ }),
88
+ ),
89
+ },
90
+ (ctx) => {
91
+ const registry = ctx.createRegistry();
92
+ registerDefaultTools(registry, ctx.createStrategyDeps());
93
+ const res = registry.resolve("pi");
94
+ expect(res.ok).toBe(true);
95
+ expect(res.source).toBe("managed");
96
+ expect(snapshotTrail(res, ctx)).toMatchSnapshot();
97
+ },
98
+ );
99
+ },
100
+ );
101
+ });
102
+
103
+ describe("A3 — electron + pre-existing global pi", () => {
104
+ it("global npm pi takes precedence over managed-bin fallback (linux)", async () => {
105
+ const homedir = "/home/r";
106
+ await withFakeEnv(
107
+ {
108
+ platform: "linux",
109
+ homedir,
110
+ env: { PATH: "/usr/local/bin" },
111
+ fs: layer(
112
+ fixtures.electronPackaged({ platform: "linux" }),
113
+ fixtures.npmGlobalUnix({}),
114
+ fixtures.settingsJson({ homedir, platform: "linux", packages: [] }),
115
+ ),
116
+ },
117
+ (ctx) => {
118
+ const registry = ctx.createRegistry();
119
+ registerDefaultTools(registry, ctx.createStrategyDeps());
120
+ const res = registry.resolve("pi");
121
+ // pi chain on posix: override → managed-bin → where.
122
+ // No managed install present; `where` finds /usr/local/bin/pi.
123
+ expect(res.ok).toBe(true);
124
+ expect(res.source).toBe("system");
125
+ expect(snapshotTrail(res, ctx)).toMatchSnapshot();
126
+ },
127
+ );
128
+ });
129
+ });
130
+
131
+ describe("A4 — AppImage first run (temp mount)", () => {
132
+ it("findBundledExtension rejects /tmp/.mount_* paths", async () => {
133
+ const homedir = "/home/r";
134
+ await withFakeEnv(
135
+ {
136
+ platform: "linux",
137
+ homedir,
138
+ fs: fixtures.electronPackaged({ platform: "linux", appimage: true }),
139
+ },
140
+ (ctx) => {
141
+ // The bundled extension resolved by findBundledExtension when
142
+ // running from an AppImage temp mount would be under
143
+ // `/tmp/.mount_*/...`. The real implementation rejects those
144
+ // paths explicitly (bridge-register.ts). Here we just verify
145
+ // the fixture places files where the temp-mount detection
146
+ // would fire.
147
+ const paths = fixtures.electronPaths({ platform: "linux", appimage: true });
148
+ expect(paths.extensionDir).toContain("/tmp/.mount_");
149
+ expect(ctx.fs.existsSync(paths.extensionDir + "/package.json")).toBe(true);
150
+ // Full round-trip rejection lives in bridge-register unit
151
+ // tests; this family cell confirms only the fixture shape.
152
+ },
153
+ );
154
+ });
155
+ });
156
+ });