gsd-pi 2.20.0 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/README.md +12 -0
  2. package/dist/cli.js +21 -18
  3. package/dist/help-text.d.ts +2 -0
  4. package/dist/help-text.js +47 -0
  5. package/dist/loader.js +2 -16
  6. package/dist/mcp-server.d.ts +18 -0
  7. package/dist/mcp-server.js +53 -0
  8. package/dist/onboarding.d.ts +0 -6
  9. package/dist/onboarding.js +0 -28
  10. package/dist/resources/agents/javascript-pro.md +280 -0
  11. package/dist/resources/agents/typescript-pro.md +255 -0
  12. package/dist/resources/extensions/bg-shell/index.ts +14 -2
  13. package/dist/resources/extensions/bg-shell/utilities.ts +14 -0
  14. package/dist/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
  15. package/dist/resources/extensions/gsd/auto-dashboard.ts +2 -1
  16. package/dist/resources/extensions/gsd/auto-prompts.ts +42 -1
  17. package/dist/resources/extensions/gsd/auto-worktree.ts +35 -2
  18. package/dist/resources/extensions/gsd/auto.ts +116 -10
  19. package/dist/resources/extensions/gsd/claude-import.ts +656 -0
  20. package/dist/resources/extensions/gsd/collision-diagnostics.ts +332 -0
  21. package/dist/resources/extensions/gsd/commands.ts +60 -7
  22. package/dist/resources/extensions/gsd/diff-context.ts +220 -0
  23. package/dist/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -1
  25. package/dist/resources/extensions/gsd/doctor.ts +12 -4
  26. package/dist/resources/extensions/gsd/file-watcher.ts +97 -0
  27. package/dist/resources/extensions/gsd/files.ts +1 -1
  28. package/dist/resources/extensions/gsd/forensics.ts +596 -0
  29. package/dist/resources/extensions/gsd/git-service.ts +2 -1
  30. package/dist/resources/extensions/gsd/github-client.ts +235 -0
  31. package/dist/resources/extensions/gsd/gitignore.ts +1 -0
  32. package/dist/resources/extensions/gsd/marketplace-discovery.ts +507 -0
  33. package/dist/resources/extensions/gsd/mcp-server.ts +87 -0
  34. package/dist/resources/extensions/gsd/namespaced-registry.ts +467 -0
  35. package/dist/resources/extensions/gsd/namespaced-resolver.ts +307 -0
  36. package/dist/resources/extensions/gsd/plugin-importer.ts +410 -0
  37. package/dist/resources/extensions/gsd/preferences.ts +14 -3
  38. package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/forensics.md +71 -0
  40. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  41. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  42. package/dist/resources/extensions/gsd/prompts/system.md +12 -3
  43. package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
  44. package/dist/resources/extensions/gsd/session-forensics.ts +5 -5
  45. package/dist/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
  46. package/dist/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
  47. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
  48. package/dist/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
  49. package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
  50. package/dist/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
  51. package/dist/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
  52. package/dist/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
  53. package/dist/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
  54. package/dist/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
  55. package/dist/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
  56. package/dist/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
  57. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
  58. package/dist/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
  59. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
  60. package/dist/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
  61. package/dist/resources/extensions/gsd/token-counter.ts +45 -0
  62. package/dist/resources/extensions/gsd/visualizer-data.ts +2 -2
  63. package/dist/resources/extensions/gsd/worktree-manager.ts +29 -1
  64. package/dist/resources/extensions/gsd/worktree.ts +3 -0
  65. package/dist/resources/extensions/mcporter/index.ts +90 -7
  66. package/dist/resources/extensions/search-the-web/native-search.ts +2 -0
  67. package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
  68. package/dist/resources/extensions/search-the-web/url-utils.ts +35 -1
  69. package/dist/resources/extensions/shared/terminal.ts +1 -1
  70. package/dist/resources/extensions/universal-config/discovery.ts +4 -0
  71. package/dist/resources/extensions/universal-config/format.ts +35 -4
  72. package/dist/resources/extensions/universal-config/index.ts +5 -3
  73. package/dist/resources/extensions/universal-config/scanners.ts +65 -2
  74. package/dist/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
  75. package/dist/resources/extensions/universal-config/tests/format.test.ts +22 -6
  76. package/dist/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
  77. package/dist/resources/extensions/universal-config/types.ts +20 -1
  78. package/dist/resources/skills/lint/SKILL.md +141 -0
  79. package/dist/resources/skills/review/SKILL.md +214 -0
  80. package/dist/resources/skills/test/SKILL.md +201 -0
  81. package/dist/tool-bootstrap.js +2 -1
  82. package/dist/wizard.js +2 -0
  83. package/package.json +12 -7
  84. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -1
  86. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  87. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  88. package/packages/pi-ai/dist/providers/openai-responses.js +12 -1
  89. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  90. package/packages/pi-ai/node_modules/@smithy/node-http-handler/LICENSE +201 -0
  91. package/packages/pi-ai/node_modules/@smithy/node-http-handler/README.md +9 -0
  92. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-cjs/index.js +762 -0
  93. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js +19 -0
  94. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/constants.js +1 -0
  95. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js +9 -0
  96. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/index.js +3 -0
  97. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +230 -0
  98. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js +87 -0
  99. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js +32 -0
  100. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +169 -0
  101. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/readable.mock.js +21 -0
  102. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/server.mock.js +88 -0
  103. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js +36 -0
  104. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js +21 -0
  105. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js +22 -0
  106. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js +23 -0
  107. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/collector.js +8 -0
  108. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js +41 -0
  109. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/readable.mock.js +21 -0
  110. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/timing.js +4 -0
  111. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/write-request-body.js +63 -0
  112. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/build-abort-error.d.ts +10 -0
  113. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/constants.d.ts +5 -0
  114. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/get-transformed-headers.d.ts +4 -0
  115. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/index.d.ts +3 -0
  116. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +46 -0
  117. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-manager.d.ts +24 -0
  118. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-pool.d.ts +12 -0
  119. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +63 -0
  120. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/readable.mock.d.ts +13 -0
  121. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/server.mock.d.ts +12 -0
  122. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-connection-timeout.d.ts +2 -0
  123. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-request-timeout.d.ts +6 -0
  124. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-keep-alive.d.ts +6 -0
  125. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-timeout.d.ts +2 -0
  126. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/collector.d.ts +5 -0
  127. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/index.d.ts +6 -0
  128. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/readable.mock.d.ts +13 -0
  129. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/timing.d.ts +8 -0
  130. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/build-abort-error.d.ts +10 -0
  131. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/constants.d.ts +5 -0
  132. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/get-transformed-headers.d.ts +4 -0
  133. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/index.d.ts +3 -0
  134. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http-handler.d.ts +46 -0
  135. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-manager.d.ts +24 -0
  136. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-pool.d.ts +12 -0
  137. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-handler.d.ts +63 -0
  138. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/readable.mock.d.ts +13 -0
  139. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/server.mock.d.ts +12 -0
  140. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-connection-timeout.d.ts +2 -0
  141. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-request-timeout.d.ts +6 -0
  142. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-keep-alive.d.ts +6 -0
  143. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-timeout.d.ts +2 -0
  144. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/collector.d.ts +5 -0
  145. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/index.d.ts +6 -0
  146. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/readable.mock.d.ts +13 -0
  147. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/timing.d.ts +8 -0
  148. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/write-request-body.d.ts +12 -0
  149. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/write-request-body.d.ts +12 -0
  150. package/packages/pi-ai/node_modules/@smithy/node-http-handler/package.json +68 -0
  151. package/packages/pi-ai/package.json +3 -0
  152. package/packages/pi-ai/pnpm-lock.yaml +2022 -0
  153. package/packages/pi-ai/src/providers/azure-openai-responses.ts +12 -1
  154. package/packages/pi-ai/src/providers/openai-responses.ts +12 -1
  155. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  156. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/extensions/index.js +1 -1
  158. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +1 -0
  160. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/extensions/loader.js +12 -1
  162. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  163. package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts +2 -0
  164. package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts.map +1 -0
  165. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +113 -0
  166. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -0
  167. package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts +4 -0
  168. package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts.map +1 -0
  169. package/packages/pi-coding-agent/dist/core/extensions/project-trust.js +42 -0
  170. package/packages/pi-coding-agent/dist/core/extensions/project-trust.js.map +1 -0
  171. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts +1 -0
  172. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
  173. package/packages/pi-coding-agent/dist/core/resolve-config-value.js +18 -0
  174. package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
  175. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts +2 -0
  176. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts.map +1 -0
  177. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +114 -0
  178. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -0
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +8 -2
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  182. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts +2 -0
  183. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +23 -9
  185. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts +2 -0
  187. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts.map +1 -0
  188. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +67 -0
  189. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -0
  190. package/packages/pi-coding-agent/package.json +1 -1
  191. package/packages/pi-coding-agent/pnpm-lock.yaml +454 -0
  192. package/packages/pi-coding-agent/scripts/copy-assets.cjs +24 -0
  193. package/packages/pi-coding-agent/src/core/extensions/index.ts +3 -0
  194. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +141 -0
  195. package/packages/pi-coding-agent/src/core/extensions/loader.ts +14 -1
  196. package/packages/pi-coding-agent/src/core/extensions/project-trust.ts +51 -0
  197. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +132 -0
  198. package/packages/pi-coding-agent/src/core/resolve-config-value.ts +20 -0
  199. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +7 -2
  200. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +98 -0
  201. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +24 -9
  202. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  203. package/packages/pi-tui/dist/tui.js +2 -2
  204. package/packages/pi-tui/dist/tui.js.map +1 -1
  205. package/packages/pi-tui/package.json +3 -1
  206. package/packages/pi-tui/src/tui.ts +2 -2
  207. package/src/resources/agents/javascript-pro.md +280 -0
  208. package/src/resources/agents/typescript-pro.md +255 -0
  209. package/src/resources/extensions/bg-shell/index.ts +14 -2
  210. package/src/resources/extensions/bg-shell/utilities.ts +14 -0
  211. package/src/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
  212. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  213. package/src/resources/extensions/gsd/auto-prompts.ts +42 -1
  214. package/src/resources/extensions/gsd/auto-worktree.ts +35 -2
  215. package/src/resources/extensions/gsd/auto.ts +116 -10
  216. package/src/resources/extensions/gsd/claude-import.ts +656 -0
  217. package/src/resources/extensions/gsd/collision-diagnostics.ts +332 -0
  218. package/src/resources/extensions/gsd/commands.ts +60 -7
  219. package/src/resources/extensions/gsd/diff-context.ts +220 -0
  220. package/src/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
  221. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -1
  222. package/src/resources/extensions/gsd/doctor.ts +12 -4
  223. package/src/resources/extensions/gsd/file-watcher.ts +97 -0
  224. package/src/resources/extensions/gsd/files.ts +1 -1
  225. package/src/resources/extensions/gsd/forensics.ts +596 -0
  226. package/src/resources/extensions/gsd/git-service.ts +2 -1
  227. package/src/resources/extensions/gsd/github-client.ts +235 -0
  228. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  229. package/src/resources/extensions/gsd/marketplace-discovery.ts +507 -0
  230. package/src/resources/extensions/gsd/mcp-server.ts +87 -0
  231. package/src/resources/extensions/gsd/namespaced-registry.ts +467 -0
  232. package/src/resources/extensions/gsd/namespaced-resolver.ts +307 -0
  233. package/src/resources/extensions/gsd/plugin-importer.ts +410 -0
  234. package/src/resources/extensions/gsd/preferences.ts +14 -3
  235. package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
  236. package/src/resources/extensions/gsd/prompts/forensics.md +71 -0
  237. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  238. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  239. package/src/resources/extensions/gsd/prompts/system.md +12 -3
  240. package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
  241. package/src/resources/extensions/gsd/session-forensics.ts +5 -5
  242. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
  243. package/src/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
  244. package/src/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
  245. package/src/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
  246. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
  247. package/src/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
  248. package/src/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
  249. package/src/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
  250. package/src/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
  251. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
  252. package/src/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
  253. package/src/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
  254. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
  255. package/src/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
  256. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
  257. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
  258. package/src/resources/extensions/gsd/token-counter.ts +45 -0
  259. package/src/resources/extensions/gsd/visualizer-data.ts +2 -2
  260. package/src/resources/extensions/gsd/worktree-manager.ts +29 -1
  261. package/src/resources/extensions/gsd/worktree.ts +3 -0
  262. package/src/resources/extensions/mcporter/index.ts +90 -7
  263. package/src/resources/extensions/search-the-web/native-search.ts +2 -0
  264. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
  265. package/src/resources/extensions/search-the-web/url-utils.ts +35 -1
  266. package/src/resources/extensions/shared/terminal.ts +1 -1
  267. package/src/resources/extensions/universal-config/discovery.ts +4 -0
  268. package/src/resources/extensions/universal-config/format.ts +35 -4
  269. package/src/resources/extensions/universal-config/index.ts +5 -3
  270. package/src/resources/extensions/universal-config/scanners.ts +65 -2
  271. package/src/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
  272. package/src/resources/extensions/universal-config/tests/format.test.ts +22 -6
  273. package/src/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
  274. package/src/resources/extensions/universal-config/types.ts +20 -1
  275. package/src/resources/skills/lint/SKILL.md +141 -0
  276. package/src/resources/skills/review/SKILL.md +214 -0
  277. package/src/resources/skills/test/SKILL.md +201 -0
  278. package/dist/resources/extensions/browser-tools/core.d.ts +0 -205
  279. package/src/resources/extensions/browser-tools/core.d.ts +0 -205
@@ -4,7 +4,171 @@
4
4
  * Kept free of pi-specific imports so it can be exercised with node:test.
5
5
  */
6
6
 
7
- export function createActionTimeline(limit = 60) {
7
+ // ---------------------------------------------------------------------------
8
+ // Interfaces & Types
9
+ // ---------------------------------------------------------------------------
10
+
11
+ export interface ActionTimeline {
12
+ limit: number;
13
+ nextId: number;
14
+ entries: ActionEntry[];
15
+ }
16
+
17
+ export interface ActionEntry {
18
+ id: number;
19
+ tool: string;
20
+ paramsSummary: string;
21
+ startedAt: number;
22
+ finishedAt: number | null;
23
+ status: string;
24
+ beforeUrl: string;
25
+ afterUrl: string;
26
+ verificationSummary?: string;
27
+ warningSummary?: string;
28
+ diffSummary?: string;
29
+ changed?: boolean;
30
+ error?: string;
31
+ }
32
+
33
+ export interface ActionPartial {
34
+ tool: string;
35
+ paramsSummary?: string;
36
+ startedAt?: number;
37
+ beforeUrl?: string;
38
+ afterUrl?: string;
39
+ verificationSummary?: string;
40
+ warningSummary?: string;
41
+ diffSummary?: string;
42
+ changed?: boolean;
43
+ error?: string;
44
+ }
45
+
46
+ export interface ActionUpdates {
47
+ finishedAt?: number;
48
+ status?: string;
49
+ afterUrl?: string;
50
+ verificationSummary?: string;
51
+ warningSummary?: string;
52
+ diffSummary?: string;
53
+ changed?: boolean;
54
+ error?: string;
55
+ }
56
+
57
+ export interface DiffResult {
58
+ changed: boolean;
59
+ changes: Array<{ type: string; before: unknown; after: unknown }>;
60
+ summary: string;
61
+ }
62
+
63
+ export interface Threshold {
64
+ op: string;
65
+ n: number;
66
+ }
67
+
68
+ export interface PageRegistry {
69
+ pages: PageEntry[];
70
+ activePageId: number | null;
71
+ nextId: number;
72
+ }
73
+
74
+ export interface PageEntry {
75
+ id: number;
76
+ page: any;
77
+ title: string;
78
+ url: string;
79
+ opener: number | null;
80
+ }
81
+
82
+ export interface PageListEntry {
83
+ id: number;
84
+ title: string;
85
+ url: string;
86
+ opener: number | null;
87
+ isActive: boolean;
88
+ }
89
+
90
+ export interface SnapshotModeConfig {
91
+ tags: string[];
92
+ roles: string[];
93
+ selectors: string[];
94
+ ariaAttributes: string[];
95
+ useInteractiveFilter: boolean;
96
+ visibleOnly?: boolean;
97
+ containerExpand?: boolean;
98
+ }
99
+
100
+ export interface AssertionCheckResult {
101
+ name: string;
102
+ passed: boolean;
103
+ actual: unknown;
104
+ expected: unknown;
105
+ selector?: string;
106
+ text?: string;
107
+ }
108
+
109
+ export interface AssertionEvaluation {
110
+ verified: boolean;
111
+ checks: AssertionCheckResult[];
112
+ summary: string;
113
+ agentHint: string;
114
+ }
115
+
116
+ export interface WaitValidationError {
117
+ error: string;
118
+ }
119
+
120
+ export interface BatchStepResult {
121
+ ok: boolean;
122
+ stopReason: string | null;
123
+ failedStepIndex: number | null;
124
+ stepResults: unknown[];
125
+ summary: string;
126
+ }
127
+
128
+ export interface FormattedTimeline {
129
+ entries: Array<{
130
+ id: number | null;
131
+ tool: string;
132
+ status: string;
133
+ durationMs: number | null;
134
+ beforeUrl: string;
135
+ afterUrl: string;
136
+ line: string;
137
+ }>;
138
+ retained: number;
139
+ totalRecorded: number;
140
+ bounded: boolean;
141
+ summary: string;
142
+ }
143
+
144
+ export interface FailureHypothesis {
145
+ hasFailures: boolean;
146
+ categories: string[];
147
+ summary: string;
148
+ signals: Array<{ category: string; source: string; detail: string }>;
149
+ }
150
+
151
+ export interface SessionSummary {
152
+ counts: {
153
+ pages: number;
154
+ actions: { total: number; retained: number; success: number; error: number; running: number };
155
+ waits: { total: number; success: number; error: number; running: number };
156
+ assertions: { total: number; passed: number; failed: number; running: number };
157
+ consoleErrors: number;
158
+ failedRequests: number;
159
+ dialogs: number;
160
+ };
161
+ activePage: { id: number | null; title: string; url: string } | null;
162
+ caveats: string[];
163
+ failureHypothesis: FailureHypothesis;
164
+ summary: string;
165
+ }
166
+
167
+ // ---------------------------------------------------------------------------
168
+ // Action Timeline
169
+ // ---------------------------------------------------------------------------
170
+
171
+ export function createActionTimeline(limit = 60): ActionTimeline {
8
172
  return {
9
173
  limit,
10
174
  nextId: 1,
@@ -12,8 +176,8 @@ export function createActionTimeline(limit = 60) {
12
176
  };
13
177
  }
14
178
 
15
- export function beginAction(timeline, partial) {
16
- const entry = {
179
+ export function beginAction(timeline: ActionTimeline, partial: ActionPartial): ActionEntry {
180
+ const entry: ActionEntry = {
17
181
  id: timeline.nextId++,
18
182
  tool: partial.tool,
19
183
  paramsSummary: partial.paramsSummary ?? "",
@@ -35,7 +199,7 @@ export function beginAction(timeline, partial) {
35
199
  return entry;
36
200
  }
37
201
 
38
- export function finishAction(timeline, actionId, updates = {}) {
202
+ export function finishAction(timeline: ActionTimeline, actionId: number, updates: ActionUpdates = {}): ActionEntry | null {
39
203
  const entry = timeline.entries.find((item) => item.id === actionId);
40
204
  if (!entry) return null;
41
205
  Object.assign(entry, updates, {
@@ -51,14 +215,14 @@ export function finishAction(timeline, actionId, updates = {}) {
51
215
  return entry;
52
216
  }
53
217
 
54
- export function findAction(timeline, actionId) {
218
+ export function findAction(timeline: ActionTimeline, actionId: number): ActionEntry | null {
55
219
  return timeline.entries.find((item) => item.id === actionId) ?? null;
56
220
  }
57
221
 
58
- export function toActionParamsSummary(params) {
222
+ export function toActionParamsSummary(params: unknown): string {
59
223
  if (!params || typeof params !== "object") return "";
60
- const entries = [];
61
- for (const [key, value] of Object.entries(params)) {
224
+ const entries: string[] = [];
225
+ for (const [key, value] of Object.entries(params as Record<string, unknown>)) {
62
226
  if (value === undefined || value === null) continue;
63
227
  if (typeof value === "string") {
64
228
  entries.push(`${key}=${JSON.stringify(value.length > 60 ? `${value.slice(0, 57)}...` : value)}`);
@@ -77,8 +241,22 @@ export function toActionParamsSummary(params) {
77
241
  return entries.slice(0, 6).join(", ");
78
242
  }
79
243
 
80
- export function diffCompactStates(before, after) {
81
- const changes = [];
244
+ // ---------------------------------------------------------------------------
245
+ // Compact State Diffing
246
+ // ---------------------------------------------------------------------------
247
+
248
+ interface CompactStateForDiff {
249
+ url?: string;
250
+ title?: string;
251
+ focus?: string;
252
+ dialog?: { count?: number; title?: string };
253
+ counts?: Record<string, number>;
254
+ headings?: string[];
255
+ bodyText?: string;
256
+ }
257
+
258
+ export function diffCompactStates(before: CompactStateForDiff | null | undefined, after: CompactStateForDiff | null | undefined): DiffResult {
259
+ const changes: Array<{ type: string; before: unknown; after: unknown }> = [];
82
260
  if (!before || !after) {
83
261
  return {
84
262
  changed: false,
@@ -159,11 +337,15 @@ export function diffCompactStates(before, after) {
159
337
  return { changed, changes, summary };
160
338
  }
161
339
 
162
- function normalizeString(value) {
340
+ // ---------------------------------------------------------------------------
341
+ // String helpers
342
+ // ---------------------------------------------------------------------------
343
+
344
+ function normalizeString(value: unknown): string {
163
345
  return String(value ?? "").trim();
164
346
  }
165
347
 
166
- export function includesNeedle(haystack, needle) {
348
+ export function includesNeedle(haystack: string, needle: string): boolean {
167
349
  return normalizeString(haystack).toLowerCase().includes(normalizeString(needle).toLowerCase());
168
350
  }
169
351
 
@@ -173,10 +355,8 @@ export function includesNeedle(haystack, needle) {
173
355
 
174
356
  /**
175
357
  * Parse a threshold expression like ">=3", "==0", "<5", or bare "3" (defaults to ">=").
176
- * @param {string} value
177
- * @returns {{ op: string, n: number } | null} — null if malformed
178
358
  */
179
- export function parseThreshold(value) {
359
+ export function parseThreshold(value: string | null | undefined): Threshold | null {
180
360
  if (value == null) return null;
181
361
  const str = String(value).trim();
182
362
  if (str === "") return null;
@@ -189,11 +369,8 @@ export function parseThreshold(value) {
189
369
 
190
370
  /**
191
371
  * Evaluate whether a count meets a parsed threshold.
192
- * @param {number} count
193
- * @param {{ op: string, n: number }} threshold
194
- * @returns {boolean}
195
372
  */
196
- export function meetsThreshold(count, threshold) {
373
+ export function meetsThreshold(count: number, threshold: Threshold): boolean {
197
374
  switch (threshold.op) {
198
375
  case ">=": return count >= threshold.n;
199
376
  case "<=": return count <= threshold.n;
@@ -207,12 +384,12 @@ export function meetsThreshold(count, threshold) {
207
384
  /**
208
385
  * Filter entries that occurred at or after a given action's start time.
209
386
  * If sinceActionId is missing or the action isn't found, returns all entries.
210
- * @param {Array<{ timestamp?: number }>} entries
211
- * @param {number | undefined} sinceActionId
212
- * @param {{ entries: Array<{ id: number, startedAt: number }> }} timeline
213
- * @returns {Array}
214
387
  */
215
- export function getEntriesSince(entries, sinceActionId, timeline) {
388
+ export function getEntriesSince(
389
+ entries: Array<{ timestamp?: number }>,
390
+ sinceActionId: number | undefined,
391
+ timeline: ActionTimeline,
392
+ ): Array<{ timestamp?: number }> {
216
393
  if (!entries || !Array.isArray(entries)) return [];
217
394
  if (sinceActionId == null || !timeline) return entries;
218
395
  const action = findAction(timeline, sinceActionId);
@@ -221,8 +398,34 @@ export function getEntriesSince(entries, sinceActionId, timeline) {
221
398
  return entries.filter((e) => (e.timestamp ?? 0) >= since);
222
399
  }
223
400
 
224
- export function evaluateAssertionChecks({ checks, state }) {
225
- const results = [];
401
+ // ---------------------------------------------------------------------------
402
+ // Assertion Evaluation
403
+ // ---------------------------------------------------------------------------
404
+
405
+ interface AssertionCheckInput {
406
+ kind: string;
407
+ selector?: string;
408
+ value?: string;
409
+ text?: string;
410
+ checked?: boolean;
411
+ sinceActionId?: number;
412
+ }
413
+
414
+ interface AssertionState {
415
+ url?: string;
416
+ title?: string;
417
+ bodyText?: string;
418
+ focus?: string;
419
+ selectorStates?: Record<string, { visible?: boolean; value?: string; checked?: boolean | null }>;
420
+ consoleEntries?: Array<{ type?: string; text?: string; message?: string; timestamp?: number }>;
421
+ networkEntries?: Array<{ type?: string; url?: string; status?: number; failed?: boolean; timestamp?: number }>;
422
+ allConsoleEntries?: Array<{ type?: string; text?: string; message?: string; timestamp?: number }>;
423
+ allNetworkEntries?: Array<{ type?: string; url?: string; status?: number; failed?: boolean; timestamp?: number }>;
424
+ actionTimeline?: ActionTimeline | null;
425
+ }
426
+
427
+ export function evaluateAssertionChecks({ checks, state }: { checks: AssertionCheckInput[]; state: AssertionState }): AssertionEvaluation {
428
+ const results: AssertionCheckResult[] = [];
226
429
  const selectorStates = state.selectorStates ?? {};
227
430
  const consoleEntries = state.consoleEntries ?? [];
228
431
  const networkEntries = state.networkEntries ?? [];
@@ -233,29 +436,29 @@ export function evaluateAssertionChecks({ checks, state }) {
233
436
  for (const check of checks) {
234
437
  const selectorState = check.selector ? selectorStates[check.selector] ?? null : null;
235
438
  let passed = false;
236
- let actual;
237
- let expected;
439
+ let actual: unknown;
440
+ let expected: unknown;
238
441
 
239
442
  switch (check.kind) {
240
443
  case "url_contains":
241
444
  actual = state.url ?? "";
242
445
  expected = check.value ?? "";
243
- passed = includesNeedle(actual, expected);
446
+ passed = includesNeedle(actual as string, expected as string);
244
447
  break;
245
448
  case "title_contains":
246
449
  actual = state.title ?? "";
247
450
  expected = check.value ?? "";
248
- passed = includesNeedle(actual, expected);
451
+ passed = includesNeedle(actual as string, expected as string);
249
452
  break;
250
453
  case "text_visible":
251
454
  actual = state.bodyText ?? "";
252
455
  expected = check.text ?? "";
253
- passed = includesNeedle(actual, expected);
456
+ passed = includesNeedle(actual as string, expected as string);
254
457
  break;
255
458
  case "text_not_visible":
256
459
  actual = state.bodyText ?? "";
257
460
  expected = check.text ?? "";
258
- passed = !includesNeedle(actual, expected);
461
+ passed = !includesNeedle(actual as string, expected as string);
259
462
  break;
260
463
  case "selector_visible":
261
464
  actual = selectorState?.visible ?? false;
@@ -275,12 +478,12 @@ export function evaluateAssertionChecks({ checks, state }) {
275
478
  case "value_contains":
276
479
  actual = selectorState?.value ?? "";
277
480
  expected = check.value ?? "";
278
- passed = includesNeedle(actual, expected);
481
+ passed = includesNeedle(actual as string, expected as string);
279
482
  break;
280
483
  case "focused_matches":
281
484
  actual = state.focus ?? "";
282
485
  expected = check.value ?? "";
283
- passed = includesNeedle(actual, expected);
486
+ passed = includesNeedle(actual as string, expected as string);
284
487
  break;
285
488
  case "checked_equals":
286
489
  actual = selectorState?.checked ?? null;
@@ -301,8 +504,8 @@ export function evaluateAssertionChecks({ checks, state }) {
301
504
  // --- S02: New structured network/console assertion kinds ---
302
505
 
303
506
  case "request_url_seen": {
304
- const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline);
305
- const matches = filtered.filter((e) => includesNeedle(e.url ?? "", check.text ?? ""));
507
+ const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline!);
508
+ const matches = (filtered as typeof allNetworkEntries).filter((e) => includesNeedle(e.url ?? "", check.text ?? ""));
306
509
  actual = matches.length > 0;
307
510
  expected = true;
308
511
  passed = actual === true;
@@ -310,9 +513,9 @@ export function evaluateAssertionChecks({ checks, state }) {
310
513
  }
311
514
 
312
515
  case "response_status": {
313
- const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline);
314
- const statusNum = parseInt(check.value, 10);
315
- const matches = filtered.filter(
516
+ const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline!);
517
+ const statusNum = parseInt(check.value!, 10);
518
+ const matches = (filtered as typeof allNetworkEntries).filter(
316
519
  (e) => includesNeedle(e.url ?? "", check.text ?? "") && typeof e.status === "number" && e.status === statusNum
317
520
  );
318
521
  actual = matches.length > 0 ? `found (status=${matches[0].status})` : `not found`;
@@ -322,8 +525,8 @@ export function evaluateAssertionChecks({ checks, state }) {
322
525
  }
323
526
 
324
527
  case "console_message_matches": {
325
- const filtered = getEntriesSince(allConsoleEntries, check.sinceActionId, actionTimeline);
326
- const matches = filtered.filter((e) => includesNeedle(e.text ?? "", check.text ?? ""));
528
+ const filtered = getEntriesSince(allConsoleEntries, check.sinceActionId, actionTimeline!);
529
+ const matches = (filtered as typeof allConsoleEntries).filter((e) => includesNeedle(e.text ?? "", check.text ?? ""));
327
530
  actual = matches.length > 0;
328
531
  expected = true;
329
532
  passed = actual === true;
@@ -331,8 +534,8 @@ export function evaluateAssertionChecks({ checks, state }) {
331
534
  }
332
535
 
333
536
  case "network_count": {
334
- const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline);
335
- const matches = filtered.filter((e) => includesNeedle(e.url ?? "", check.text ?? ""));
537
+ const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline!);
538
+ const matches = (filtered as typeof allNetworkEntries).filter((e) => includesNeedle(e.url ?? "", check.text ?? ""));
336
539
  const threshold = parseThreshold(check.value);
337
540
  if (!threshold) {
338
541
  actual = `invalid threshold: ${check.value}`;
@@ -347,8 +550,8 @@ export function evaluateAssertionChecks({ checks, state }) {
347
550
  }
348
551
 
349
552
  case "console_count": {
350
- const filtered = getEntriesSince(allConsoleEntries, check.sinceActionId, actionTimeline);
351
- const matches = filtered.filter((e) => includesNeedle(e.text ?? "", check.text ?? ""));
553
+ const filtered = getEntriesSince(allConsoleEntries, check.sinceActionId, actionTimeline!);
554
+ const matches = (filtered as typeof allConsoleEntries).filter((e) => includesNeedle(e.text ?? "", check.text ?? ""));
352
555
  const threshold = parseThreshold(check.value);
353
556
  if (!threshold) {
354
557
  actual = `invalid threshold: ${check.value}`;
@@ -363,8 +566,8 @@ export function evaluateAssertionChecks({ checks, state }) {
363
566
  }
364
567
 
365
568
  case "no_console_errors_since": {
366
- const filtered = getEntriesSince(allConsoleEntries, check.sinceActionId, actionTimeline);
367
- const errors = filtered.filter((e) => e.type === "error" || e.type === "pageerror");
569
+ const filtered = getEntriesSince(allConsoleEntries, check.sinceActionId, actionTimeline!);
570
+ const errors = (filtered as typeof allConsoleEntries).filter((e) => e.type === "error" || e.type === "pageerror");
368
571
  actual = errors.length;
369
572
  expected = 0;
370
573
  passed = errors.length === 0;
@@ -372,8 +575,8 @@ export function evaluateAssertionChecks({ checks, state }) {
372
575
  }
373
576
 
374
577
  case "no_failed_requests_since": {
375
- const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline);
376
- const failures = filtered.filter((e) => e.failed || (typeof e.status === "number" && e.status >= 400));
578
+ const filtered = getEntriesSince(allNetworkEntries, check.sinceActionId, actionTimeline!);
579
+ const failures = (filtered as typeof allNetworkEntries).filter((e) => e.failed || (typeof e.status === "number" && e.status >= 400));
377
580
  actual = failures.length;
378
581
  expected = 0;
379
582
  passed = failures.length === 0;
@@ -417,11 +620,16 @@ export function evaluateAssertionChecks({ checks, state }) {
417
620
  // Wait-condition validation
418
621
  // ---------------------------------------------------------------------------
419
622
 
623
+ interface WaitConditionSpec {
624
+ needsValue: boolean;
625
+ valueLabel: string;
626
+ needsThreshold?: boolean;
627
+ }
628
+
420
629
  /**
421
630
  * All recognized wait conditions with their parameter requirements.
422
- * Each entry: { needsValue: bool, valueLabel: string, needsThreshold?: bool }
423
631
  */
424
- const WAIT_CONDITIONS = {
632
+ const WAIT_CONDITIONS: Record<string, WaitConditionSpec> = {
425
633
  // Existing 5 conditions
426
634
  selector_visible: { needsValue: true, valueLabel: "CSS selector" },
427
635
  selector_hidden: { needsValue: true, valueLabel: "CSS selector" },
@@ -440,10 +648,8 @@ const WAIT_CONDITIONS = {
440
648
 
441
649
  /**
442
650
  * Validate parameters for a browser_wait_for condition.
443
- * @param {{ condition: string, value?: string, threshold?: string }} params
444
- * @returns {null | { error: string }} — null if valid, structured error otherwise
445
651
  */
446
- export function validateWaitParams(params) {
652
+ export function validateWaitParams(params: { condition: string; value?: string; threshold?: string }): WaitValidationError | null {
447
653
  const { condition, value, threshold } = params ?? {};
448
654
 
449
655
  if (!condition) {
@@ -477,14 +683,8 @@ export function validateWaitParams(params) {
477
683
  /**
478
684
  * Generate a JS expression string for page.waitForFunction() that detects
479
685
  * DOM stability by comparing snapshot hashes across polling intervals.
480
- *
481
- * The script stores a snapshot on a namespaced window key. When the snapshot
482
- * matches the previous value, the region is considered stable.
483
- *
484
- * @param {string} selector — CSS selector for the target element
485
- * @returns {string} — self-contained JS function body suitable for waitForFunction
486
686
  */
487
- export function createRegionStableScript(selector) {
687
+ export function createRegionStableScript(selector: string): string {
488
688
  // Create a stable key from the selector (simple hash to avoid special chars)
489
689
  const safeKey = Array.from(selector).reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0) >>> 0;
490
690
  const windowKey = `__pw_region_stable_${safeKey}`;
@@ -504,40 +704,20 @@ export function createRegionStableScript(selector) {
504
704
  // Page Registry — pure-logic operations for multi-page/tab management
505
705
  // ---------------------------------------------------------------------------
506
706
 
507
- /**
508
- * Create a fresh page registry.
509
- * @returns {{ pages: Array, activePageId: number | null, nextId: number }}
510
- */
511
- export function createPageRegistry() {
707
+ export function createPageRegistry(): PageRegistry {
512
708
  return { pages: [], activePageId: null, nextId: 1 };
513
709
  }
514
710
 
515
- /**
516
- * @typedef {{ id: number, page: any, title: string, url: string, opener: number | null }} PageEntry
517
- */
518
-
519
- /**
520
- * Add a page to the registry. Assigns an auto-incrementing ID.
521
- * @param {ReturnType<typeof createPageRegistry>} registry
522
- * @param {{ page: any, title?: string, url?: string, opener?: number | null }} info
523
- * @returns {PageEntry}
524
- */
525
- export function registryAddPage(registry, { page, title = "", url = "", opener = null }) {
526
- const entry = { id: registry.nextId++, page, title, url, opener };
711
+ export function registryAddPage(
712
+ registry: PageRegistry,
713
+ { page, title = "", url = "", opener = null }: { page: unknown; title?: string; url?: string; opener?: number | null },
714
+ ): PageEntry {
715
+ const entry: PageEntry = { id: registry.nextId++, page, title, url, opener };
527
716
  registry.pages.push(entry);
528
717
  return entry;
529
718
  }
530
719
 
531
- /**
532
- * Remove a page from the registry by ID.
533
- * If the removed page was active, falls back to the opener (if still present)
534
- * or the last remaining page.
535
- * Orphans any pages whose opener was the removed page (sets their opener to null).
536
- * @param {ReturnType<typeof createPageRegistry>} registry
537
- * @param {number} pageId
538
- * @returns {{ removed: PageEntry, newActiveId: number | null }}
539
- */
540
- export function registryRemovePage(registry, pageId) {
720
+ export function registryRemovePage(registry: PageRegistry, pageId: number): { removed: PageEntry; newActiveId: number | null } {
541
721
  const idx = registry.pages.findIndex((p) => p.id === pageId);
542
722
  if (idx === -1) {
543
723
  const available = registry.pages.map((p) => p.id);
@@ -571,12 +751,7 @@ export function registryRemovePage(registry, pageId) {
571
751
  return { removed, newActiveId };
572
752
  }
573
753
 
574
- /**
575
- * Set the active page by ID. Throws if the page is not in the registry.
576
- * @param {ReturnType<typeof createPageRegistry>} registry
577
- * @param {number} pageId
578
- */
579
- export function registrySetActive(registry, pageId) {
754
+ export function registrySetActive(registry: PageRegistry, pageId: number): void {
580
755
  const entry = registry.pages.find((p) => p.id === pageId);
581
756
  if (!entry) {
582
757
  const available = registry.pages.map((p) => p.id);
@@ -589,12 +764,7 @@ export function registrySetActive(registry, pageId) {
589
764
  registry.activePageId = pageId;
590
765
  }
591
766
 
592
- /**
593
- * Get the active page entry. Throws if no active page or active page not found.
594
- * @param {ReturnType<typeof createPageRegistry>} registry
595
- * @returns {PageEntry}
596
- */
597
- export function registryGetActive(registry) {
767
+ export function registryGetActive(registry: PageRegistry): PageEntry {
598
768
  if (registry.activePageId === null) {
599
769
  throw new Error(
600
770
  `registryGetActive: no active page. ` +
@@ -613,22 +783,11 @@ export function registryGetActive(registry) {
613
783
  return entry;
614
784
  }
615
785
 
616
- /**
617
- * Get a page entry by ID, or null if not found.
618
- * @param {ReturnType<typeof createPageRegistry>} registry
619
- * @param {number} pageId
620
- * @returns {PageEntry | null}
621
- */
622
- export function registryGetPage(registry, pageId) {
786
+ export function registryGetPage(registry: PageRegistry, pageId: number): PageEntry | null {
623
787
  return registry.pages.find((p) => p.id === pageId) ?? null;
624
788
  }
625
789
 
626
- /**
627
- * List all pages (without the raw `page` reference).
628
- * @param {ReturnType<typeof createPageRegistry>} registry
629
- * @returns {Array<{ id: number, title: string, url: string, opener: number | null, isActive: boolean }>}
630
- */
631
- export function registryListPages(registry) {
790
+ export function registryListPages(registry: PageRegistry): PageListEntry[] {
632
791
  return registry.pages.map((entry) => ({
633
792
  id: entry.id,
634
793
  title: entry.title,
@@ -642,13 +801,8 @@ export function registryListPages(registry) {
642
801
  // FIFO Bounded Log Pusher
643
802
  // ---------------------------------------------------------------------------
644
803
 
645
- /**
646
- * Create a push function that enforces FIFO eviction at push-time.
647
- * @param {number} maxSize — maximum number of entries to retain
648
- * @returns {(array: Array, entry: any) => void}
649
- */
650
- export function createBoundedLogPusher(maxSize) {
651
- return function push(array, entry) {
804
+ export function createBoundedLogPusher(maxSize: number): (array: unknown[], entry: unknown) => void {
805
+ return function push(array: unknown[], entry: unknown): void {
652
806
  array.push(entry);
653
807
  if (array.length > maxSize) {
654
808
  array.splice(0, array.length - maxSize);
@@ -656,10 +810,14 @@ export function createBoundedLogPusher(maxSize) {
656
810
  };
657
811
  }
658
812
 
659
- export async function runBatchSteps({ steps, executeStep, stopOnFailure = true }) {
660
- const results = [];
813
+ export async function runBatchSteps({ steps, executeStep, stopOnFailure = true }: {
814
+ steps: unknown[];
815
+ executeStep: (step: unknown, index: number) => Promise<{ ok: boolean; [key: string]: unknown }>;
816
+ stopOnFailure?: boolean;
817
+ }): Promise<BatchStepResult> {
818
+ const results: unknown[] = [];
661
819
  for (let i = 0; i < steps.length; i += 1) {
662
- const step = steps[i];
820
+ const step = steps[i] as { action: string };
663
821
  const result = await executeStep(step, i);
664
822
  results.push(result);
665
823
  if (result.ok === false && stopOnFailure) {
@@ -685,15 +843,7 @@ export async function runBatchSteps({ steps, executeStep, stopOnFailure = true }
685
843
  // Snapshot Modes — semantic element filtering for browser_snapshot_refs
686
844
  // ---------------------------------------------------------------------------
687
845
 
688
- /**
689
- * Pre-defined snapshot modes that filter elements by semantic category.
690
- * Each mode config defines which elements should be captured.
691
- *
692
- * Shape: { tags: string[], roles: string[], selectors: string[],
693
- * ariaAttributes: string[], useInteractiveFilter: boolean,
694
- * visibleOnly?: boolean, containerExpand?: boolean }
695
- */
696
- export const SNAPSHOT_MODES = {
846
+ export const SNAPSHOT_MODES: Record<string, SnapshotModeConfig> = {
697
847
  interactive: {
698
848
  tags: [],
699
849
  roles: [],
@@ -748,12 +898,7 @@ export const SNAPSHOT_MODES = {
748
898
  },
749
899
  };
750
900
 
751
- /**
752
- * Get the snapshot mode config by name.
753
- * @param {string} mode — mode name (e.g. "form", "dialog", "interactive")
754
- * @returns {{ tags: string[], roles: string[], selectors: string[], ariaAttributes: string[], useInteractiveFilter: boolean, visibleOnly?: boolean, containerExpand?: boolean } | null}
755
- */
756
- export function getSnapshotModeConfig(mode) {
901
+ export function getSnapshotModeConfig(mode: string): SnapshotModeConfig | null {
757
902
  return SNAPSHOT_MODES[mode] ?? null;
758
903
  }
759
904
 
@@ -761,13 +906,7 @@ export function getSnapshotModeConfig(mode) {
761
906
  // Fingerprint functions — structural identity for ref resolution
762
907
  // ---------------------------------------------------------------------------
763
908
 
764
- /**
765
- * Compute a content hash from visible text using djb2.
766
- * Caller is expected to pre-truncate to ~200 chars and normalize whitespace.
767
- * @param {string} text — visible text content
768
- * @returns {string} — hex string hash, or "0" for empty input
769
- */
770
- export function computeContentHash(text) {
909
+ export function computeContentHash(text: string): string {
771
910
  if (!text) return "0";
772
911
  let h = 5381;
773
912
  for (let i = 0; i < text.length; i++) {
@@ -776,15 +915,7 @@ export function computeContentHash(text) {
776
915
  return (h >>> 0).toString(16);
777
916
  }
778
917
 
779
- /**
780
- * Compute a structural signature from tag, role, and immediate child tag names.
781
- * Uses djb2 hash on the concatenated string `tag|role|child1,child2,...`.
782
- * @param {string} tag — element tag name (lowercase)
783
- * @param {string} role — ARIA role or empty string
784
- * @param {string[]} childTags — array of immediate child tag names (lowercase)
785
- * @returns {string} — hex string hash
786
- */
787
- export function computeStructuralSignature(tag, role, childTags) {
918
+ export function computeStructuralSignature(tag: string, role: string, childTags: string[]): string {
788
919
  const input = `${tag}|${role}|${childTags.join(",")}`;
789
920
  let h = 5381;
790
921
  for (let i = 0; i < input.length; i++) {
@@ -793,14 +924,10 @@ export function computeStructuralSignature(tag, role, childTags) {
793
924
  return (h >>> 0).toString(16);
794
925
  }
795
926
 
796
- /**
797
- * Match two fingerprint objects by contentHash and structuralSignature.
798
- * Returns true only when both fields are present on both objects and both match.
799
- * @param {{ contentHash?: string, structuralSignature?: string }} stored
800
- * @param {{ contentHash?: string, structuralSignature?: string }} candidate
801
- * @returns {boolean}
802
- */
803
- export function matchFingerprint(stored, candidate) {
927
+ export function matchFingerprint(
928
+ stored: { contentHash?: string; structuralSignature?: string },
929
+ candidate: { contentHash?: string; structuralSignature?: string },
930
+ ): boolean {
804
931
  if (!stored || !candidate) return false;
805
932
  if (!stored.contentHash || !stored.structuralSignature) return false;
806
933
  if (!candidate.contentHash || !candidate.structuralSignature) return false;
@@ -808,30 +935,34 @@ export function matchFingerprint(stored, candidate) {
808
935
  stored.structuralSignature === candidate.structuralSignature;
809
936
  }
810
937
 
811
- function formatDurationMs(entry) {
938
+ // ---------------------------------------------------------------------------
939
+ // Timeline Formatting
940
+ // ---------------------------------------------------------------------------
941
+
942
+ function formatDurationMs(entry: { startedAt?: number; finishedAt?: number | null }): number | null {
812
943
  const startedAt = typeof entry?.startedAt === "number" ? entry.startedAt : null;
813
944
  const finishedAt = typeof entry?.finishedAt === "number" ? entry.finishedAt : null;
814
945
  if (startedAt == null || finishedAt == null || finishedAt < startedAt) return null;
815
946
  return finishedAt - startedAt;
816
947
  }
817
948
 
818
- function summarizeActionStatus(status) {
949
+ function summarizeActionStatus(status: string | undefined): string {
819
950
  if (status === "error") return "error";
820
951
  if (status === "running") return "running";
821
952
  return "success";
822
953
  }
823
954
 
824
- function looksBoundedWarning(value) {
955
+ function looksBoundedWarning(value: unknown): boolean {
825
956
  return /bounded .*history/i.test(String(value ?? ""));
826
957
  }
827
958
 
828
- function uniqueStrings(values) {
829
- return [...new Set(values.filter(Boolean))];
959
+ function uniqueStrings(values: (string | undefined)[]): string[] {
960
+ return [...new Set(values.filter(Boolean))] as string[];
830
961
  }
831
962
 
832
- export function formatTimelineEntries(entries = [], options = {}) {
833
- const retained = options.retained ?? entries.length;
834
- const totalRecorded = options.totalRecorded ?? retained;
963
+ export function formatTimelineEntries(entries: ActionEntry[] = [], options: Record<string, unknown> = {}): FormattedTimeline {
964
+ const retained = (options.retained as number) ?? entries.length;
965
+ const totalRecorded = (options.totalRecorded as number) ?? retained;
835
966
  const bounded = totalRecorded > retained;
836
967
 
837
968
  if (!entries.length) {
@@ -847,7 +978,7 @@ export function formatTimelineEntries(entries = [], options = {}) {
847
978
  const formattedEntries = entries.map((entry) => {
848
979
  const status = summarizeActionStatus(entry.status);
849
980
  const durationMs = formatDurationMs(entry);
850
- const parts = [
981
+ const parts: string[] = [
851
982
  `#${entry.id ?? "?"}`,
852
983
  entry.tool ?? "unknown_tool",
853
984
  status,
@@ -884,12 +1015,16 @@ export function formatTimelineEntries(entries = [], options = {}) {
884
1015
  };
885
1016
  }
886
1017
 
887
- export function buildFailureHypothesis(session = {}) {
1018
+ // ---------------------------------------------------------------------------
1019
+ // Failure Hypothesis
1020
+ // ---------------------------------------------------------------------------
1021
+
1022
+ export function buildFailureHypothesis(session: Record<string, any> = {}): FailureHypothesis {
888
1023
  const timelineEntries = session.actionTimeline?.entries ?? [];
889
1024
  const consoleEntries = session.consoleEntries ?? [];
890
1025
  const networkEntries = session.networkEntries ?? [];
891
1026
  const dialogEntries = session.dialogEntries ?? [];
892
- const signals = [];
1027
+ const signals: Array<{ category: string; source: string; detail: string }> = [];
893
1028
 
894
1029
  for (const entry of timelineEntries) {
895
1030
  if (entry?.status !== "error") continue;
@@ -920,7 +1055,7 @@ export function buildFailureHypothesis(session = {}) {
920
1055
  if (entry?.type !== "error" && entry?.type !== "pageerror") continue;
921
1056
  signals.push({
922
1057
  category: "console",
923
- source: entry.type,
1058
+ source: entry.type!,
924
1059
  detail: entry.text || "Console error recorded",
925
1060
  });
926
1061
  }
@@ -957,18 +1092,22 @@ export function buildFailureHypothesis(session = {}) {
957
1092
  };
958
1093
  }
959
1094
 
960
- export function summarizeBrowserSession(session = {}) {
961
- const actionTimeline = session.actionTimeline ?? { limit: 0, entries: [] };
962
- const actionEntries = actionTimeline.entries ?? [];
963
- const retainedActionCount = session.retainedActionCount ?? actionEntries.length;
964
- const totalActionCount = session.totalActionCount ?? retainedActionCount;
965
- const pages = session.pages ?? [];
966
- const consoleEntries = session.consoleEntries ?? [];
967
- const networkEntries = session.networkEntries ?? [];
968
- const dialogEntries = session.dialogEntries ?? [];
1095
+ // ---------------------------------------------------------------------------
1096
+ // Session Summary
1097
+ // ---------------------------------------------------------------------------
1098
+
1099
+ export function summarizeBrowserSession(session: Record<string, any> = {}): SessionSummary {
1100
+ const actionTimeline = session.actionTimeline ?? { limit: 0, entries: [] as ActionEntry[] };
1101
+ const actionEntries: ActionEntry[] = actionTimeline.entries ?? [];
1102
+ const retainedActionCount: number = session.retainedActionCount ?? actionEntries.length;
1103
+ const totalActionCount: number = session.totalActionCount ?? retainedActionCount;
1104
+ const pages: Array<Record<string, any>> = session.pages ?? [];
1105
+ const consoleEntries: Array<Record<string, any>> = session.consoleEntries ?? [];
1106
+ const networkEntries: Array<Record<string, any>> = session.networkEntries ?? [];
1107
+ const dialogEntries: Array<Record<string, any>> = session.dialogEntries ?? [];
969
1108
 
970
1109
  const actionStatusCounts = actionEntries.reduce(
971
- (acc, entry) => {
1110
+ (acc: Record<string, number>, entry: ActionEntry) => {
972
1111
  const status = summarizeActionStatus(entry.status);
973
1112
  acc[status] = (acc[status] ?? 0) + 1;
974
1113
  return acc;
@@ -976,13 +1115,13 @@ export function summarizeBrowserSession(session = {}) {
976
1115
  { success: 0, error: 0, running: 0 },
977
1116
  );
978
1117
 
979
- const waitEntries = actionEntries.filter((entry) => entry.tool === "browser_wait_for");
980
- const assertEntries = actionEntries.filter((entry) => entry.tool === "browser_assert");
981
- const consoleErrors = consoleEntries.filter((entry) => entry.type === "error" || entry.type === "pageerror");
982
- const failedRequests = networkEntries.filter((entry) => entry.failed || (typeof entry.status === "number" && entry.status >= 400));
983
- const activePage = pages.find((page) => page.isActive) ?? pages[0] ?? null;
1118
+ const waitEntries = actionEntries.filter((entry: ActionEntry) => entry.tool === "browser_wait_for");
1119
+ const assertEntries = actionEntries.filter((entry: ActionEntry) => entry.tool === "browser_assert");
1120
+ const consoleErrors = consoleEntries.filter((entry: Record<string, any>) => entry.type === "error" || entry.type === "pageerror");
1121
+ const failedRequests = networkEntries.filter((entry: Record<string, any>) => entry.failed || (typeof entry.status === "number" && entry.status >= 400));
1122
+ const activePage = pages.find((page: Record<string, any>) => page.isActive) ?? pages[0] ?? null;
984
1123
 
985
- const caveats = [];
1124
+ const caveats: string[] = [];
986
1125
  if (totalActionCount > retainedActionCount) {
987
1126
  caveats.push(`Showing ${retainedActionCount} of ${totalActionCount} recorded actions; older actions were discarded due to bounded history.`);
988
1127
  }