gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.0fdacd524

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 (471) hide show
  1. package/README.md +8 -7
  2. package/dist/bundled-resource-path.d.ts +7 -0
  3. package/dist/bundled-resource-path.js +34 -2
  4. package/dist/claude-cli-check.js +77 -38
  5. package/dist/cli-policy.d.ts +13 -0
  6. package/dist/cli-policy.js +17 -0
  7. package/dist/cli.js +95 -55
  8. package/dist/headless-query.d.ts +22 -0
  9. package/dist/headless-query.js +43 -8
  10. package/dist/headless.d.ts +10 -0
  11. package/dist/headless.js +16 -1
  12. package/dist/loader.js +9 -13
  13. package/dist/onboarding.d.ts +10 -0
  14. package/dist/onboarding.js +2 -2
  15. package/dist/provider-migrations.d.ts +2 -2
  16. package/dist/provider-migrations.js +5 -2
  17. package/dist/resource-loader.d.ts +5 -2
  18. package/dist/resource-loader.js +30 -13
  19. package/dist/resources/.managed-resources-content-hash +1 -0
  20. package/dist/resources/extensions/claude-code-cli/readiness.js +90 -46
  21. package/dist/resources/extensions/google-search/index.js +2 -6
  22. package/dist/resources/extensions/gsd/auto/loop.js +23 -0
  23. package/dist/resources/extensions/gsd/auto/phases.js +5 -13
  24. package/dist/resources/extensions/gsd/auto/run-unit.js +26 -12
  25. package/dist/resources/extensions/gsd/auto/session.js +5 -6
  26. package/dist/resources/extensions/gsd/auto-dashboard.js +3 -2
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +55 -21
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +18 -6
  29. package/dist/resources/extensions/gsd/auto-prompts.js +69 -2
  30. package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
  31. package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
  32. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  33. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
  34. package/dist/resources/extensions/gsd/auto-worktree.js +60 -13
  35. package/dist/resources/extensions/gsd/auto.js +39 -14
  36. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
  37. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
  38. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  39. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
  40. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +112 -31
  41. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
  42. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +22 -0
  43. package/dist/resources/extensions/gsd/bootstrap/system-context.js +45 -8
  44. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +121 -3
  45. package/dist/resources/extensions/gsd/commands/catalog.js +76 -5
  46. package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -1
  47. package/dist/resources/extensions/gsd/commands/handlers/ops.js +8 -0
  48. package/dist/resources/extensions/gsd/commands-config.js +3 -2
  49. package/dist/resources/extensions/gsd/commands-extensions.js +46 -3
  50. package/dist/resources/extensions/gsd/commands-handlers.js +3 -2
  51. package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
  52. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
  53. package/dist/resources/extensions/gsd/commands-worktree.js +309 -0
  54. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  55. package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  56. package/dist/resources/extensions/gsd/doctor-providers.js +2 -1
  57. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
  58. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  59. package/dist/resources/extensions/gsd/forensics.js +10 -8
  60. package/dist/resources/extensions/gsd/git-service.js +12 -5
  61. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  62. package/dist/resources/extensions/gsd/guided-flow.js +25 -24
  63. package/dist/resources/extensions/gsd/home-dir.js +16 -0
  64. package/dist/resources/extensions/gsd/key-manager.js +2 -1
  65. package/dist/resources/extensions/gsd/memory-store.js +66 -31
  66. package/dist/resources/extensions/gsd/migrate/command.js +3 -2
  67. package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
  68. package/dist/resources/extensions/gsd/model-router.js +114 -9
  69. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
  70. package/dist/resources/extensions/gsd/preferences-models.js +91 -15
  71. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  72. package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
  73. package/dist/resources/extensions/gsd/preferences.js +5 -3
  74. package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
  75. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
  76. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -0
  77. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  78. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -0
  79. package/dist/resources/extensions/gsd/prompts/plan-slice.md +10 -0
  80. package/dist/resources/extensions/gsd/prompts/refine-slice.md +10 -0
  81. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +2 -0
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
  83. package/dist/resources/extensions/gsd/state.js +42 -0
  84. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  85. package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
  86. package/dist/resources/extensions/gsd/unit-context-manifest.js +29 -4
  87. package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
  88. package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
  89. package/dist/resources/extensions/gsd/worktree-command.js +26 -46
  90. package/dist/resources/extensions/gsd/worktree-manager.js +20 -1
  91. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -13
  92. package/dist/resources/extensions/gsd/worktree-root.js +124 -0
  93. package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
  94. package/dist/resources/extensions/gsd/worktree.js +4 -115
  95. package/dist/resources/extensions/mcp-client/index.js +6 -9
  96. package/dist/resources/extensions/ollama/index.js +15 -2
  97. package/dist/resources/extensions/ollama/model-capabilities.js +31 -0
  98. package/dist/resources/extensions/ollama/ollama-client.js +40 -4
  99. package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
  100. package/dist/resources/extensions/subagent/index.js +324 -178
  101. package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
  102. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  103. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  104. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  105. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  106. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  107. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  108. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  109. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  110. package/dist/resources/skills/lint/SKILL.md +4 -0
  111. package/dist/resources/skills/review/SKILL.md +4 -0
  112. package/dist/resources/skills/test/SKILL.md +3 -0
  113. package/dist/rtk-shared.d.ts +3 -0
  114. package/dist/rtk-shared.js +17 -0
  115. package/dist/rtk.d.ts +2 -5
  116. package/dist/rtk.js +3 -20
  117. package/dist/runtime-checks.d.ts +27 -0
  118. package/dist/runtime-checks.js +38 -0
  119. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  120. package/dist/web/standalone/.next/BUILD_ID +1 -1
  121. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  122. package/dist/web/standalone/.next/build-manifest.json +3 -3
  123. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  124. package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
  125. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  127. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
  146. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  156. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  158. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  160. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  161. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  162. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  163. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
  164. package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
  165. package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
  166. package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
  167. package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
  168. package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
  169. package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
  170. package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
  171. package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
  172. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
  173. package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
  174. package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
  175. package/dist/web/standalone/package.json +2 -1
  176. package/dist/welcome-screen.js +27 -1
  177. package/dist/worktree-cli.d.ts +1 -0
  178. package/dist/worktree-cli.js +9 -3
  179. package/dist/worktree-status-banner.d.ts +1 -0
  180. package/dist/worktree-status-banner.js +132 -0
  181. package/package.json +1 -3
  182. package/packages/daemon/package.json +2 -2
  183. package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
  184. package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
  185. package/packages/mcp-server/dist/alias-telemetry.js +30 -0
  186. package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
  187. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  188. package/packages/mcp-server/dist/workflow-tools.js +74 -46
  189. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  190. package/packages/mcp-server/package.json +2 -2
  191. package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
  192. package/packages/mcp-server/src/alias-telemetry.ts +30 -0
  193. package/packages/mcp-server/src/workflow-tools.test.ts +78 -0
  194. package/packages/mcp-server/src/workflow-tools.ts +93 -58
  195. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  196. package/packages/native/package.json +1 -1
  197. package/packages/native/tsconfig.tsbuildinfo +1 -1
  198. package/packages/pi-agent-core/package.json +1 -1
  199. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  200. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
  201. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
  202. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
  203. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
  204. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  205. package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
  206. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  207. package/packages/pi-ai/dist/types.d.ts +13 -0
  208. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  209. package/packages/pi-ai/dist/types.js.map +1 -1
  210. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  211. package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
  212. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  213. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
  214. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  215. package/packages/pi-ai/package.json +1 -1
  216. package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
  217. package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
  218. package/packages/pi-ai/src/types.ts +13 -0
  219. package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
  220. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
  221. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  222. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
  224. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/messages.js +4 -0
  227. package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  229. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
  231. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  232. package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
  233. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  234. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
  235. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
  237. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  238. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
  239. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
  240. package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
  241. package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
  242. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
  243. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
  244. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
  245. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
  246. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
  247. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  248. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
  249. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  250. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
  251. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
  252. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
  253. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
  254. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
  255. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
  256. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
  257. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
  258. package/packages/pi-coding-agent/package.json +1 -1
  259. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
  260. package/packages/pi-coding-agent/src/core/messages.ts +4 -0
  261. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
  262. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
  263. package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
  264. package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
  266. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
  268. package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
  269. package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
  270. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  271. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
  272. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  273. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
  274. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
  275. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
  276. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
  277. package/packages/pi-tui/package.json +1 -1
  278. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
  279. package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
  280. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  281. package/packages/rpc-client/package.json +1 -1
  282. package/pkg/package.json +1 -1
  283. package/src/resources/extensions/claude-code-cli/readiness.ts +92 -47
  284. package/src/resources/extensions/google-search/index.ts +2 -9
  285. package/src/resources/extensions/gsd/auto/loop.ts +24 -2
  286. package/src/resources/extensions/gsd/auto/phases.ts +6 -14
  287. package/src/resources/extensions/gsd/auto/run-unit.ts +26 -12
  288. package/src/resources/extensions/gsd/auto/session.ts +5 -6
  289. package/src/resources/extensions/gsd/auto/types.ts +1 -0
  290. package/src/resources/extensions/gsd/auto-dashboard.ts +3 -2
  291. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +60 -24
  292. package/src/resources/extensions/gsd/auto-dispatch.ts +18 -6
  293. package/src/resources/extensions/gsd/auto-prompts.ts +66 -2
  294. package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
  295. package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
  296. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  297. package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
  298. package/src/resources/extensions/gsd/auto-worktree.ts +82 -12
  299. package/src/resources/extensions/gsd/auto.ts +37 -10
  300. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
  301. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
  302. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  303. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
  304. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +121 -31
  305. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
  306. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +20 -0
  307. package/src/resources/extensions/gsd/bootstrap/system-context.ts +50 -8
  308. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +141 -11
  309. package/src/resources/extensions/gsd/commands/catalog.ts +82 -5
  310. package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -1
  311. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  312. package/src/resources/extensions/gsd/commands-config.ts +3 -2
  313. package/src/resources/extensions/gsd/commands-extensions.ts +43 -3
  314. package/src/resources/extensions/gsd/commands-handlers.ts +3 -2
  315. package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
  316. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
  317. package/src/resources/extensions/gsd/commands-worktree.ts +383 -0
  318. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  319. package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  320. package/src/resources/extensions/gsd/doctor-providers.ts +2 -1
  321. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
  322. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  323. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  324. package/src/resources/extensions/gsd/forensics.ts +12 -7
  325. package/src/resources/extensions/gsd/git-service.ts +13 -5
  326. package/src/resources/extensions/gsd/gsd-db.ts +12 -2
  327. package/src/resources/extensions/gsd/guided-flow.ts +27 -26
  328. package/src/resources/extensions/gsd/home-dir.ts +19 -0
  329. package/src/resources/extensions/gsd/journal.ts +4 -1
  330. package/src/resources/extensions/gsd/key-manager.ts +2 -1
  331. package/src/resources/extensions/gsd/memory-store.ts +81 -28
  332. package/src/resources/extensions/gsd/migrate/command.ts +3 -2
  333. package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
  334. package/src/resources/extensions/gsd/model-router.ts +172 -9
  335. package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
  336. package/src/resources/extensions/gsd/preferences-models.ts +101 -15
  337. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  338. package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
  339. package/src/resources/extensions/gsd/preferences.ts +16 -2
  340. package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
  341. package/src/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
  342. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -0
  343. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  344. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -0
  345. package/src/resources/extensions/gsd/prompts/plan-slice.md +10 -0
  346. package/src/resources/extensions/gsd/prompts/refine-slice.md +10 -0
  347. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +2 -0
  348. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
  349. package/src/resources/extensions/gsd/state.ts +42 -0
  350. package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  351. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +179 -1
  352. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
  353. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +24 -5
  354. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
  355. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
  356. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
  357. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +50 -27
  358. package/src/resources/extensions/gsd/tests/commands-extensions-version-compare.test.ts +58 -0
  359. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +48 -0
  360. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
  361. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
  362. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
  363. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
  364. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
  365. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
  366. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
  367. package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
  368. package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
  369. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
  370. package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
  371. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
  372. package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
  373. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
  374. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
  375. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
  376. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
  377. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
  378. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +25 -65
  379. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
  380. package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
  381. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
  382. package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
  383. package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
  384. package/src/resources/extensions/gsd/tests/home-dir.test.ts +52 -0
  385. package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
  386. package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
  387. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +72 -1
  388. package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
  389. package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
  390. package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
  391. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
  392. package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
  393. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +18 -1
  394. package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
  395. package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
  396. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
  397. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
  398. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
  399. package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
  400. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
  401. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  402. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
  403. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
  404. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +34 -0
  405. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
  406. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
  407. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
  408. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +17 -1
  409. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
  410. package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
  411. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
  412. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +38 -3
  413. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
  414. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
  415. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
  416. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +235 -0
  417. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +34 -33
  418. package/src/resources/extensions/gsd/tests/worktree.test.ts +8 -0
  419. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +131 -1
  420. package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
  421. package/src/resources/extensions/gsd/unit-context-manifest.ts +44 -12
  422. package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
  423. package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
  424. package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
  425. package/src/resources/extensions/gsd/worktree-command.ts +31 -44
  426. package/src/resources/extensions/gsd/worktree-manager.ts +40 -1
  427. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -14
  428. package/src/resources/extensions/gsd/worktree-root.ts +144 -0
  429. package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
  430. package/src/resources/extensions/gsd/worktree.ts +8 -119
  431. package/src/resources/extensions/mcp-client/index.ts +6 -10
  432. package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
  433. package/src/resources/extensions/ollama/index.ts +16 -2
  434. package/src/resources/extensions/ollama/model-capabilities.ts +34 -0
  435. package/src/resources/extensions/ollama/ollama-client.ts +41 -4
  436. package/src/resources/extensions/ollama/tests/model-capabilities.test.ts +96 -0
  437. package/src/resources/extensions/ollama/tests/ollama-client-timeout-env.test.ts +147 -0
  438. package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
  439. package/src/resources/extensions/subagent/index.ts +165 -7
  440. package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
  441. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  442. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  443. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  444. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  445. package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  446. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  447. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  448. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
  449. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
  450. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
  451. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  452. package/src/resources/skills/lint/SKILL.md +4 -0
  453. package/src/resources/skills/review/SKILL.md +4 -0
  454. package/src/resources/skills/test/SKILL.md +3 -0
  455. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
  456. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
  457. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
  458. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
  459. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
  460. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
  461. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
  462. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  463. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
  464. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
  465. package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
  466. package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
  467. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
  468. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
  469. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
  470. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → 4iu6IYeYfxOq8OidlDqp6}/_buildManifest.js +0 -0
  471. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → 4iu6IYeYfxOq8OidlDqp6}/_ssgManifest.js +0 -0
@@ -1,601 +0,0 @@
1
- /**
2
- * browser-tools — Playwright integration tests
3
- *
4
- * Exercises browser-side evaluate scripts against real DOM:
5
- * - EVALUATE_HELPERS_SOURCE (window.__pi utilities)
6
- * - Intent scoring scripts from intent.ts
7
- * - Form analysis scripts from forms.ts
8
- *
9
- * Uses Playwright Chromium for real page.evaluate() against HTML fixtures.
10
- */
11
-
12
- import { describe, it, before, after } from "node:test";
13
- import assert from "node:assert/strict";
14
- import { chromium } from "playwright";
15
- import { dirname } from "node:path";
16
- import { fileURLToPath } from "node:url";
17
-
18
- const __dirname = dirname(fileURLToPath(import.meta.url));
19
-
20
- // ---------------------------------------------------------------------------
21
- // Source loading — import the IIFE builders directly via jiti.
22
- // The test-only named exports in tools/intent.ts and tools/forms.ts exist
23
- // exactly so this test can call the real, in-tree builders. No brace
24
- // walking, no regex stripping — a refactor of the signatures just updates
25
- // the import surface, not the test.
26
- // ---------------------------------------------------------------------------
27
-
28
- import { createRequire } from "node:module";
29
- const require = createRequire(import.meta.url);
30
- const jiti = require("jiti")(__dirname, { interopDefault: true, debug: false });
31
- const { EVALUATE_HELPERS_SOURCE } = jiti("../evaluate-helpers.ts");
32
- const { buildIntentScoringScript } = jiti("../tools/intent.ts");
33
- const { buildFormAnalysisScript } = jiti("../tools/forms.ts");
34
-
35
- // ---------------------------------------------------------------------------
36
- // Browser lifecycle
37
- // ---------------------------------------------------------------------------
38
-
39
- let browser;
40
- let page;
41
-
42
- before(async () => {
43
- browser = await chromium.launch({ headless: true });
44
- const context = await browser.newContext({ viewport: { width: 1280, height: 800 }, deviceScaleFactor: 2 });
45
- page = await context.newPage();
46
- });
47
-
48
- after(async () => {
49
- if (browser) await browser.close();
50
- });
51
-
52
- /** Inject window.__pi helpers into the current page */
53
- async function injectHelpers() {
54
- await page.evaluate(EVALUATE_HELPERS_SOURCE);
55
- }
56
-
57
- // =========================================================================
58
- // 1. window.__pi utility tests
59
- // =========================================================================
60
-
61
- describe("window.__pi utilities", () => {
62
- it("simpleHash — deterministic output for same input", async () => {
63
- await page.setContent("<p>test</p>");
64
- await injectHelpers();
65
- const h1 = await page.evaluate(() => window.__pi.simpleHash("hello world"));
66
- const h2 = await page.evaluate(() => window.__pi.simpleHash("hello world"));
67
- assert.equal(h1, h2);
68
- assert.equal(typeof h1, "string");
69
- assert.ok(h1.length > 0);
70
- });
71
-
72
- it("simpleHash — different output for different input", async () => {
73
- await page.setContent("<p>test</p>");
74
- await injectHelpers();
75
- const h1 = await page.evaluate(() => window.__pi.simpleHash("hello"));
76
- const h2 = await page.evaluate(() => window.__pi.simpleHash("world"));
77
- assert.notEqual(h1, h2);
78
- });
79
-
80
- it("isVisible — visible element returns true", async () => {
81
- await page.setContent('<div id="vis" style="width:100px;height:100px;">visible</div>');
82
- await injectHelpers();
83
- const result = await page.evaluate(() => window.__pi.isVisible(document.getElementById("vis")));
84
- assert.equal(result, true);
85
- });
86
-
87
- it("isVisible — display:none returns false", async () => {
88
- await page.setContent('<div id="hidden" style="display:none;">hidden</div>');
89
- await injectHelpers();
90
- const result = await page.evaluate(() => window.__pi.isVisible(document.getElementById("hidden")));
91
- assert.equal(result, false);
92
- });
93
-
94
- it("isVisible — visibility:hidden returns false", async () => {
95
- await page.setContent('<div id="inv" style="visibility:hidden;width:100px;height:100px;">inv</div>');
96
- await injectHelpers();
97
- const result = await page.evaluate(() => window.__pi.isVisible(document.getElementById("inv")));
98
- assert.equal(result, false);
99
- });
100
-
101
- it("isEnabled — enabled input returns true", async () => {
102
- await page.setContent('<input id="en" type="text" />');
103
- await injectHelpers();
104
- const result = await page.evaluate(() => window.__pi.isEnabled(document.getElementById("en")));
105
- assert.equal(result, true);
106
- });
107
-
108
- it("isEnabled — disabled input returns false", async () => {
109
- await page.setContent('<input id="dis" type="text" disabled />');
110
- await injectHelpers();
111
- const result = await page.evaluate(() => window.__pi.isEnabled(document.getElementById("dis")));
112
- assert.equal(result, false);
113
- });
114
-
115
- it("isEnabled — aria-disabled returns false", async () => {
116
- await page.setContent('<button id="adis" aria-disabled="true">Click</button>');
117
- await injectHelpers();
118
- const result = await page.evaluate(() => window.__pi.isEnabled(document.getElementById("adis")));
119
- assert.equal(result, false);
120
- });
121
-
122
- it("inferRole — button element → button", async () => {
123
- await page.setContent('<button id="btn">Go</button>');
124
- await injectHelpers();
125
- const role = await page.evaluate(() => window.__pi.inferRole(document.getElementById("btn")));
126
- assert.equal(role, "button");
127
- });
128
-
129
- it("inferRole — anchor with href → link", async () => {
130
- await page.setContent('<a id="lnk" href="/page">Link</a>');
131
- await injectHelpers();
132
- const role = await page.evaluate(() => window.__pi.inferRole(document.getElementById("lnk")));
133
- assert.equal(role, "link");
134
- });
135
-
136
- it("inferRole — input[type=text] → textbox", async () => {
137
- await page.setContent('<input id="txt" type="text" />');
138
- await injectHelpers();
139
- const role = await page.evaluate(() => window.__pi.inferRole(document.getElementById("txt")));
140
- assert.equal(role, "textbox");
141
- });
142
-
143
- it("inferRole — input[type=search] → searchbox", async () => {
144
- await page.setContent('<input id="srch" type="search" />');
145
- await injectHelpers();
146
- const role = await page.evaluate(() => window.__pi.inferRole(document.getElementById("srch")));
147
- assert.equal(role, "searchbox");
148
- });
149
-
150
- it("inferRole — explicit role attribute overrides tag", async () => {
151
- await page.setContent('<div id="d" role="button">Click me</div>');
152
- await injectHelpers();
153
- const role = await page.evaluate(() => window.__pi.inferRole(document.getElementById("d")));
154
- assert.equal(role, "button");
155
- });
156
-
157
- it("accessibleName — button with text content", async () => {
158
- await page.setContent('<button id="b">Submit Form</button>');
159
- await injectHelpers();
160
- const name = await page.evaluate(() => window.__pi.accessibleName(document.getElementById("b")));
161
- assert.equal(name, "Submit Form");
162
- });
163
-
164
- it("accessibleName — input with aria-label", async () => {
165
- await page.setContent('<input id="i" aria-label="Search query" />');
166
- await injectHelpers();
167
- const name = await page.evaluate(() => window.__pi.accessibleName(document.getElementById("i")));
168
- assert.equal(name, "Search query");
169
- });
170
-
171
- it("accessibleName — input with label[for]", async () => {
172
- await page.setContent('<label for="email">Email Address</label><input id="email" type="email" />');
173
- await injectHelpers();
174
- // accessibleName checks aria-label/labelledby/placeholder/alt/value/textContent —
175
- // but NOT label[for]. That's by design — it's a lightweight heuristic, not full ARIA.
176
- // For label[for], the accessible name falls back to textContent (empty for input).
177
- // Test what it actually returns.
178
- const name = await page.evaluate(() => window.__pi.accessibleName(document.getElementById("email")));
179
- // Input has no aria-label, no labelledby, no placeholder, no alt, no value, no textContent
180
- // So it returns empty string
181
- assert.equal(typeof name, "string");
182
- });
183
-
184
- it("accessibleName — input with aria-labelledby", async () => {
185
- await page.setContent('<span id="lbl">Username</span><input id="u" aria-labelledby="lbl" />');
186
- await injectHelpers();
187
- const name = await page.evaluate(() => window.__pi.accessibleName(document.getElementById("u")));
188
- assert.equal(name, "Username");
189
- });
190
-
191
- it("accessibleName — input with placeholder as fallback", async () => {
192
- await page.setContent('<input id="p" placeholder="Enter name" />');
193
- await injectHelpers();
194
- const name = await page.evaluate(() => window.__pi.accessibleName(document.getElementById("p")));
195
- assert.equal(name, "Enter name");
196
- });
197
-
198
- it("isInteractiveEl — button → true", async () => {
199
- await page.setContent('<button id="b">Go</button>');
200
- await injectHelpers();
201
- const result = await page.evaluate(() => window.__pi.isInteractiveEl(document.getElementById("b")));
202
- assert.equal(result, true);
203
- });
204
-
205
- it("isInteractiveEl — div → false", async () => {
206
- await page.setContent('<div id="d">Just text</div>');
207
- await injectHelpers();
208
- const result = await page.evaluate(() => window.__pi.isInteractiveEl(document.getElementById("d")));
209
- assert.equal(result, false);
210
- });
211
-
212
- it("isInteractiveEl — input → true", async () => {
213
- await page.setContent('<input id="i" type="text" />');
214
- await injectHelpers();
215
- const result = await page.evaluate(() => window.__pi.isInteractiveEl(document.getElementById("i")));
216
- assert.equal(result, true);
217
- });
218
-
219
- it("isInteractiveEl — anchor with href → true", async () => {
220
- await page.setContent('<a id="a" href="/page">Link</a>');
221
- await injectHelpers();
222
- const result = await page.evaluate(() => window.__pi.isInteractiveEl(document.getElementById("a")));
223
- assert.equal(result, true);
224
- });
225
-
226
- it("isInteractiveEl — div with tabindex → true", async () => {
227
- await page.setContent('<div id="t" tabindex="0">Focusable</div>');
228
- await injectHelpers();
229
- const result = await page.evaluate(() => window.__pi.isInteractiveEl(document.getElementById("t")));
230
- assert.equal(result, true);
231
- });
232
-
233
- it("cssPath — returns valid selector that resolves back to element", async () => {
234
- await page.setContent('<div><span><button id="target">Click</button></span></div>');
235
- await injectHelpers();
236
- const selector = await page.evaluate(() => window.__pi.cssPath(document.getElementById("target")));
237
- assert.equal(typeof selector, "string");
238
- assert.ok(selector.length > 0);
239
- // Verify round-trip: querySelector with that selector finds the element
240
- const roundTrip = await page.evaluate((sel) => {
241
- const el = document.querySelector(sel);
242
- return el ? el.id : null;
243
- }, selector);
244
- assert.equal(roundTrip, "target");
245
- });
246
-
247
- it("cssPath — element with id uses #id shortcut", async () => {
248
- await page.setContent('<div id="myid">content</div>');
249
- await injectHelpers();
250
- const selector = await page.evaluate(() => window.__pi.cssPath(document.getElementById("myid")));
251
- assert.equal(selector, "#myid");
252
- });
253
-
254
- it("cssPath — nested element without id uses tag path", async () => {
255
- await page.setContent('<main><section><p class="test">hello</p></section></main>');
256
- await injectHelpers();
257
- const selector = await page.evaluate(() => {
258
- const el = document.querySelector("p.test");
259
- return window.__pi.cssPath(el);
260
- });
261
- assert.ok(selector.startsWith("body >"));
262
- // Verify it resolves
263
- const text = await page.evaluate((sel) => document.querySelector(sel)?.textContent, selector);
264
- assert.equal(text, "hello");
265
- });
266
- });
267
-
268
- // =========================================================================
269
- // 2. Intent scoring tests
270
- // =========================================================================
271
-
272
- describe("intent scoring", () => {
273
- it("submit_form — submit button inside form scores higher than outside", async () => {
274
- await page.setContent(`
275
- <form>
276
- <input type="text" name="q" />
277
- <button type="submit" id="inside">Submit</button>
278
- </form>
279
- <button id="outside">Random Button</button>
280
- `);
281
- await injectHelpers();
282
-
283
- const script = buildIntentScoringScript("submit_form");
284
- const result = await page.evaluate(script);
285
-
286
- assert.ok(!result.error, `Unexpected error: ${result.error}`);
287
- assert.ok(result.candidates.length >= 1, "Expected at least 1 candidate");
288
-
289
- // The submit button inside the form should be top-ranked
290
- const inside = result.candidates.find(c => c.selector.includes("inside") || c.text.includes("submit"));
291
- const outside = result.candidates.find(c => c.selector.includes("outside") || c.text.includes("random"));
292
-
293
- assert.ok(inside, "Should find the inside submit button");
294
- if (outside) {
295
- assert.ok(inside.score > outside.score, `Inside score (${inside.score}) should exceed outside (${outside.score})`);
296
- }
297
- });
298
-
299
- it("close_dialog — × button in dialog scores highest", async () => {
300
- await page.setContent(`
301
- <div role="dialog" aria-modal="true" style="width:400px;height:300px;position:relative;">
302
- <button id="close-x" aria-label="close" style="position:absolute;top:5px;right:5px;">×</button>
303
- <p>Dialog content</p>
304
- <button id="cancel">Cancel</button>
305
- </div>
306
- <button id="other">Other</button>
307
- `);
308
- await injectHelpers();
309
-
310
- const script = buildIntentScoringScript("close_dialog");
311
- const result = await page.evaluate(script);
312
-
313
- assert.ok(!result.error, `Unexpected error: ${result.error}`);
314
- assert.ok(result.candidates.length >= 1, "Expected at least 1 candidate");
315
-
316
- // The × button should score high due to text match + aria-label + inside-dialog + top-right
317
- const closeBtn = result.candidates[0];
318
- assert.ok(
319
- closeBtn.text.includes("×") || closeBtn.name.toLowerCase().includes("close"),
320
- `Top candidate should be the × button, got: ${closeBtn.text} / ${closeBtn.name}`
321
- );
322
- });
323
-
324
- it("search_field — input[type=search] scores higher than input[type=text]", async () => {
325
- await page.setContent(`
326
- <header>
327
- <nav>
328
- <input id="search" type="search" placeholder="Search..." />
329
- <input id="text" type="text" placeholder="Username" />
330
- </nav>
331
- </header>
332
- `);
333
- await injectHelpers();
334
-
335
- const script = buildIntentScoringScript("search_field");
336
- const result = await page.evaluate(script);
337
-
338
- assert.ok(!result.error, `Unexpected error: ${result.error}`);
339
- assert.ok(result.candidates.length >= 1, "Expected at least 1 candidate");
340
-
341
- const searchInput = result.candidates.find(c => c.tag === "input" && c.name.toLowerCase().includes("search"));
342
- assert.ok(searchInput, "Should find the search input");
343
-
344
- // It should be the top candidate or at least higher than the text input
345
- const textInput = result.candidates.find(c => c.name.toLowerCase().includes("username"));
346
- if (textInput) {
347
- assert.ok(
348
- searchInput.score > textInput.score,
349
- `Search score (${searchInput.score}) should exceed text (${textInput.score})`
350
- );
351
- }
352
- });
353
-
354
- it("primary_cta — large button in main scores higher than small nav link", async () => {
355
- await page.setContent(`
356
- <nav>
357
- <a id="nav-link" href="/about" style="font-size:12px;padding:2px 4px;">About</a>
358
- </nav>
359
- <main>
360
- <button id="cta" style="font-size:24px;padding:20px 60px;width:300px;height:80px;">Get Started</button>
361
- </main>
362
- `);
363
- await injectHelpers();
364
-
365
- const script = buildIntentScoringScript("primary_cta");
366
- const result = await page.evaluate(script);
367
-
368
- assert.ok(!result.error, `Unexpected error: ${result.error}`);
369
- assert.ok(result.candidates.length >= 1, "Expected at least 1 candidate");
370
-
371
- // The large button in main should outrank the small nav link
372
- const cta = result.candidates.find(c => c.text.includes("get started"));
373
- const navLink = result.candidates.find(c => c.text.includes("about"));
374
-
375
- assert.ok(cta, "Should find the CTA button");
376
- if (navLink) {
377
- assert.ok(cta.score > navLink.score, `CTA score (${cta.score}) should exceed nav link (${navLink.score})`);
378
- }
379
- });
380
-
381
- it("submit_form — returns correct result structure", async () => {
382
- await page.setContent(`
383
- <form>
384
- <button type="submit">Save</button>
385
- </form>
386
- `);
387
- await injectHelpers();
388
-
389
- const script = buildIntentScoringScript("submit_form");
390
- const result = await page.evaluate(script);
391
-
392
- assert.equal(result.intent, "submit_form");
393
- assert.equal(result.normalized, "submitform");
394
- assert.equal(typeof result.count, "number");
395
- assert.ok(Array.isArray(result.candidates));
396
-
397
- const c = result.candidates[0];
398
- assert.equal(typeof c.score, "number");
399
- assert.equal(typeof c.selector, "string");
400
- assert.equal(typeof c.tag, "string");
401
- assert.equal(typeof c.role, "string");
402
- assert.equal(typeof c.name, "string");
403
- assert.equal(typeof c.text, "string");
404
- assert.equal(typeof c.reason, "string");
405
- });
406
-
407
- it("unknown intent returns error", async () => {
408
- await page.setContent("<p>test</p>");
409
- await injectHelpers();
410
-
411
- const script = buildIntentScoringScript("nonexistent_intent");
412
- const result = await page.evaluate(script);
413
- assert.ok(result.error, "Should return an error for unknown intent");
414
- assert.ok(result.error.includes("Unknown intent"));
415
- });
416
-
417
- it("missing window.__pi returns error", async () => {
418
- // Navigate to about:blank and clear window.__pi to simulate missing helpers
419
- await page.setContent("<p>test</p>");
420
- await page.evaluate(() => { delete window.__pi; });
421
- const script = buildIntentScoringScript("submit_form");
422
- const result = await page.evaluate(script);
423
- assert.ok(result.error, "Should return an error when __pi not injected");
424
- assert.ok(result.error.includes("__pi"));
425
- });
426
- });
427
-
428
- // =========================================================================
429
- // 3. Form analysis tests
430
- // =========================================================================
431
-
432
- describe("form analysis", () => {
433
- const COMPLEX_FORM = `
434
- <form id="testform" action="/submit">
435
- <!-- label[for] association -->
436
- <label for="fname">First Name</label>
437
- <input id="fname" name="first_name" type="text" required />
438
-
439
- <!-- wrapping label -->
440
- <label>Last Name <input id="lname" name="last_name" type="text" /></label>
441
-
442
- <!-- aria-label -->
443
- <input id="email" name="email" type="email" aria-label="Email Address" required />
444
-
445
- <!-- aria-labelledby -->
446
- <span id="phone-label">Phone Number</span>
447
- <input id="phone" name="phone" type="tel" aria-labelledby="phone-label" />
448
-
449
- <!-- placeholder as fallback -->
450
- <input id="city" name="city" type="text" placeholder="Enter your city" />
451
-
452
- <!-- hidden input -->
453
- <input id="token" name="csrf_token" type="hidden" value="abc123" />
454
-
455
- <!-- select with options -->
456
- <label for="country">Country</label>
457
- <select id="country" name="country">
458
- <option value="">Select...</option>
459
- <option value="us" selected>United States</option>
460
- <option value="uk">United Kingdom</option>
461
- </select>
462
-
463
- <!-- checkbox -->
464
- <label><input id="agree" name="agree" type="checkbox" /> I agree to terms</label>
465
-
466
- <!-- submit button -->
467
- <button type="submit" id="submit-btn">Register</button>
468
- </form>
469
- `;
470
-
471
- it("label via label[for] resolves correctly", async () => {
472
- await page.setContent(COMPLEX_FORM);
473
- const script = buildFormAnalysisScript("#testform");
474
- const result = await page.evaluate(script);
475
-
476
- assert.ok(!result.error, `Unexpected error: ${result.error}`);
477
- const fname = result.fields.find(f => f.name === "first_name");
478
- assert.ok(fname, "Should find first_name field");
479
- assert.equal(fname.label, "First Name");
480
- });
481
-
482
- it("label via wrapping label resolves correctly", async () => {
483
- await page.setContent(COMPLEX_FORM);
484
- const script = buildFormAnalysisScript("#testform");
485
- const result = await page.evaluate(script);
486
-
487
- const lname = result.fields.find(f => f.name === "last_name");
488
- assert.ok(lname, "Should find last_name field");
489
- assert.equal(lname.label, "Last Name");
490
- });
491
-
492
- it("label via aria-label resolves correctly", async () => {
493
- await page.setContent(COMPLEX_FORM);
494
- const script = buildFormAnalysisScript("#testform");
495
- const result = await page.evaluate(script);
496
-
497
- const email = result.fields.find(f => f.name === "email");
498
- assert.ok(email, "Should find email field");
499
- assert.equal(email.label, "Email Address");
500
- });
501
-
502
- it("label via aria-labelledby resolves correctly", async () => {
503
- await page.setContent(COMPLEX_FORM);
504
- const script = buildFormAnalysisScript("#testform");
505
- const result = await page.evaluate(script);
506
-
507
- const phone = result.fields.find(f => f.name === "phone");
508
- assert.ok(phone, "Should find phone field");
509
- assert.equal(phone.label, "Phone Number");
510
- });
511
-
512
- it("label via placeholder as fallback", async () => {
513
- await page.setContent(COMPLEX_FORM);
514
- const script = buildFormAnalysisScript("#testform");
515
- const result = await page.evaluate(script);
516
-
517
- const city = result.fields.find(f => f.name === "city");
518
- assert.ok(city, "Should find city field");
519
- assert.equal(city.label, "Enter your city");
520
- });
521
-
522
- it("hidden input is flagged as hidden", async () => {
523
- await page.setContent(COMPLEX_FORM);
524
- const script = buildFormAnalysisScript("#testform");
525
- const result = await page.evaluate(script);
526
-
527
- const token = result.fields.find(f => f.name === "csrf_token");
528
- assert.ok(token, "Should find csrf_token field");
529
- assert.equal(token.hidden, true);
530
- assert.equal(token.type, "hidden");
531
- });
532
-
533
- it("submit button is discovered", async () => {
534
- await page.setContent(COMPLEX_FORM);
535
- const script = buildFormAnalysisScript("#testform");
536
- const result = await page.evaluate(script);
537
-
538
- assert.ok(result.submitButtons.length >= 1, "Should find at least 1 submit button");
539
- const btn = result.submitButtons[0];
540
- assert.equal(btn.text, "Register");
541
- assert.equal(btn.type, "submit");
542
- });
543
-
544
- it("returns correct result structure", async () => {
545
- await page.setContent(COMPLEX_FORM);
546
- const script = buildFormAnalysisScript("#testform");
547
- const result = await page.evaluate(script);
548
-
549
- assert.equal(typeof result.formSelector, "string");
550
- assert.ok(Array.isArray(result.fields));
551
- assert.ok(Array.isArray(result.submitButtons));
552
- assert.equal(typeof result.fieldCount, "number");
553
- assert.equal(typeof result.visibleFieldCount, "number");
554
- assert.ok(result.fieldCount > 0);
555
- });
556
-
557
- it("required fields are correctly identified", async () => {
558
- await page.setContent(COMPLEX_FORM);
559
- const script = buildFormAnalysisScript("#testform");
560
- const result = await page.evaluate(script);
561
-
562
- const fname = result.fields.find(f => f.name === "first_name");
563
- assert.equal(fname.required, true, "first_name should be required");
564
-
565
- const lname = result.fields.find(f => f.name === "last_name");
566
- assert.equal(lname.required, false, "last_name should not be required");
567
- });
568
-
569
- it("select options are included", async () => {
570
- await page.setContent(COMPLEX_FORM);
571
- const script = buildFormAnalysisScript("#testform");
572
- const result = await page.evaluate(script);
573
-
574
- const country = result.fields.find(f => f.name === "country");
575
- assert.ok(country, "Should find country field");
576
- assert.equal(country.type, "select");
577
- assert.ok(Array.isArray(country.options));
578
- assert.ok(country.options.length >= 3);
579
- const selected = country.options.find(o => o.selected);
580
- assert.equal(selected.value, "us");
581
- });
582
-
583
- it("auto-detects single form when no selector given", async () => {
584
- await page.setContent(COMPLEX_FORM);
585
- const script = buildFormAnalysisScript();
586
- const result = await page.evaluate(script);
587
-
588
- assert.ok(!result.error, "Should auto-detect the form");
589
- assert.ok(result.fields.length > 0, "Should find fields");
590
- assert.ok(result.formSelector.includes("testform") || result.formSelector.includes("form"));
591
- });
592
-
593
- it("returns error for non-existent selector", async () => {
594
- await page.setContent("<p>no form</p>");
595
- const script = buildFormAnalysisScript("#doesnotexist");
596
- const result = await page.evaluate(script);
597
-
598
- assert.ok(result.error, "Should return error for missing form");
599
- assert.ok(result.error.includes("not found"));
600
- });
601
- });