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
@@ -0,0 +1,596 @@
1
+ /**
2
+ * GSD Forensics — Post-mortem investigation of auto-mode failures
3
+ *
4
+ * Programmatically scans activity logs, metrics, crash locks, and doctor
5
+ * diagnostics for anomalies, then hands a structured report to the LLM
6
+ * for interactive investigation.
7
+ *
8
+ * Entry point: handleForensics() called from commands.ts
9
+ */
10
+
11
+ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
12
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
13
+ import { join, dirname, relative } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ import { extractTrace, type ExecutionTrace } from "./session-forensics.js";
17
+ import { nativeParseJsonlTail } from "./native-parser-bridge.js";
18
+ import {
19
+ loadLedgerFromDisk, getAverageCostPerUnitType, getProjectTotals,
20
+ formatCost, formatTokenCount, type UnitMetrics, type MetricsLedger,
21
+ } from "./metrics.js";
22
+ import { readCrashLock, isLockProcessAlive, formatCrashInfo, type LockData } from "./crash-recovery.js";
23
+ import { runGSDDoctor, formatDoctorIssuesForPrompt, type DoctorIssue } from "./doctor.js";
24
+ import { verifyExpectedArtifact } from "./auto-recovery.js";
25
+ import { deriveState } from "./state.js";
26
+ import { isAutoActive } from "./auto.js";
27
+ import { loadPrompt } from "./prompt-loader.js";
28
+ import { gsdRoot } from "./paths.js";
29
+ import { formatDuration } from "./history.js";
30
+
31
+ // ─── Types ────────────────────────────────────────────────────────────────────
32
+
33
+ interface ForensicAnomaly {
34
+ type: "stuck-loop" | "cost-spike" | "timeout" | "missing-artifact" | "crash" | "doctor-issue" | "error-trace";
35
+ severity: "info" | "warning" | "error";
36
+ unitType?: string;
37
+ unitId?: string;
38
+ summary: string;
39
+ details: string;
40
+ }
41
+
42
+ interface UnitTrace {
43
+ file: string;
44
+ unitType: string;
45
+ unitId: string;
46
+ seq: number;
47
+ trace: ExecutionTrace;
48
+ mtime: number;
49
+ }
50
+
51
+ interface ForensicReport {
52
+ gsdVersion: string;
53
+ timestamp: string;
54
+ basePath: string;
55
+ activeMilestone: string | null;
56
+ activeSlice: string | null;
57
+ unitTraces: UnitTrace[];
58
+ metrics: MetricsLedger | null;
59
+ completedKeys: string[];
60
+ crashLock: LockData | null;
61
+ doctorIssues: DoctorIssue[];
62
+ anomalies: ForensicAnomaly[];
63
+ recentUnits: { type: string; id: string; cost: number; duration: number; model: string; finishedAt: number }[];
64
+ }
65
+
66
+ // ─── JSONL Parser (inline — session-forensics.ts version is module-private) ──
67
+
68
+ const MAX_JSONL_BYTES = 5 * 1024 * 1024;
69
+
70
+ function parseJSONL(raw: string): unknown[] {
71
+ const source = raw.length > MAX_JSONL_BYTES ? raw.slice(-MAX_JSONL_BYTES) : raw;
72
+ return source.trim().split("\n").map(line => {
73
+ try { return JSON.parse(line); } catch { return null; }
74
+ }).filter(Boolean) as unknown[];
75
+ }
76
+
77
+ // ─── Entry Point ──────────────────────────────────────────────────────────────
78
+
79
+ export async function handleForensics(
80
+ args: string,
81
+ ctx: ExtensionCommandContext,
82
+ pi: ExtensionAPI,
83
+ ): Promise<void> {
84
+ if (isAutoActive()) {
85
+ ctx.ui.notify("Cannot run forensics while auto-mode is active. Stop auto-mode first.", "error");
86
+ return;
87
+ }
88
+
89
+ const basePath = process.cwd();
90
+ const root = gsdRoot(basePath);
91
+ if (!existsSync(root)) {
92
+ ctx.ui.notify("No GSD state found. Run /gsd auto first.", "warning");
93
+ return;
94
+ }
95
+
96
+ let problemDescription = args.trim();
97
+ if (!problemDescription) {
98
+ problemDescription = await ctx.ui.input(
99
+ "Describe what went wrong:",
100
+ "e.g. auto-mode got stuck on task T03",
101
+ ) ?? "";
102
+ }
103
+ if (!problemDescription?.trim()) {
104
+ ctx.ui.notify("Problem description required for forensic analysis.", "warning");
105
+ return;
106
+ }
107
+
108
+ ctx.ui.notify("Building forensic report...", "info");
109
+
110
+ const report = await buildForensicReport(basePath);
111
+ const savedPath = saveForensicReport(basePath, report, problemDescription);
112
+
113
+ // Derive GSD source dir for prompt
114
+ const __extensionDir = dirname(fileURLToPath(import.meta.url));
115
+ const gsdSourceDir = __extensionDir;
116
+
117
+ const forensicData = formatReportForPrompt(report);
118
+ const content = loadPrompt("forensics", {
119
+ problemDescription,
120
+ forensicData,
121
+ gsdSourceDir,
122
+ });
123
+
124
+ ctx.ui.notify(`Forensic report saved: ${relative(basePath, savedPath)}`, "info");
125
+
126
+ pi.sendMessage(
127
+ { customType: "gsd-forensics", content, display: false },
128
+ { triggerTurn: true },
129
+ );
130
+ }
131
+
132
+ // ─── Report Builder ───────────────────────────────────────────────────────────
133
+
134
+ async function buildForensicReport(basePath: string): Promise<ForensicReport> {
135
+ const anomalies: ForensicAnomaly[] = [];
136
+
137
+ // 1. Derive current state
138
+ let activeMilestone: string | null = null;
139
+ let activeSlice: string | null = null;
140
+ try {
141
+ const state = await deriveState(basePath);
142
+ activeMilestone = state.activeMilestone?.id ?? null;
143
+ activeSlice = state.activeSlice?.id ?? null;
144
+ } catch { /* state derivation failure is non-fatal */ }
145
+
146
+ // 2. Scan activity logs (last 5)
147
+ const unitTraces = scanActivityLogs(basePath);
148
+
149
+ // 3. Load metrics
150
+ const metrics = loadLedgerFromDisk(basePath);
151
+
152
+ // 4. Load completed keys
153
+ const completedKeys = loadCompletedKeys(basePath);
154
+
155
+ // 5. Check crash lock
156
+ const crashLock = readCrashLock(basePath);
157
+
158
+ // 6. Run doctor
159
+ let doctorIssues: DoctorIssue[] = [];
160
+ try {
161
+ const report = await runGSDDoctor(basePath, { scope: undefined });
162
+ doctorIssues = report.issues;
163
+ } catch { /* doctor failure is non-fatal */ }
164
+
165
+ // 7. Build recent units from metrics
166
+ const recentUnits: ForensicReport["recentUnits"] = [];
167
+ if (metrics?.units) {
168
+ const sorted = [...metrics.units].sort((a, b) => b.finishedAt - a.finishedAt).slice(0, 10);
169
+ for (const u of sorted) {
170
+ recentUnits.push({
171
+ type: u.type,
172
+ id: u.id,
173
+ cost: u.cost,
174
+ duration: u.finishedAt - u.startedAt,
175
+ model: u.model,
176
+ finishedAt: u.finishedAt,
177
+ });
178
+ }
179
+ }
180
+
181
+ // 8. GSD version
182
+ let gsdVersion = "unknown";
183
+ try {
184
+ const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "../../../../package.json");
185
+ if (existsSync(pkgPath)) {
186
+ gsdVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version ?? "unknown";
187
+ }
188
+ } catch { /* non-fatal */ }
189
+
190
+ // 9. Run anomaly detectors
191
+ if (metrics?.units) detectStuckLoops(metrics.units, anomalies);
192
+ if (metrics?.units) detectCostSpikes(metrics.units, anomalies);
193
+ detectTimeouts(unitTraces, anomalies);
194
+ detectMissingArtifacts(completedKeys, basePath, anomalies);
195
+ detectCrash(crashLock, anomalies);
196
+ detectDoctorIssues(doctorIssues, anomalies);
197
+ detectErrorTraces(unitTraces, anomalies);
198
+
199
+ return {
200
+ gsdVersion,
201
+ timestamp: new Date().toISOString(),
202
+ basePath,
203
+ activeMilestone,
204
+ activeSlice,
205
+ unitTraces,
206
+ metrics,
207
+ completedKeys,
208
+ crashLock,
209
+ doctorIssues,
210
+ anomalies,
211
+ recentUnits,
212
+ };
213
+ }
214
+
215
+ // ─── Activity Log Scanner ─────────────────────────────────────────────────────
216
+
217
+ const ACTIVITY_FILENAME_RE = /^(\d+)-(.+?)-(.+)\.jsonl$/;
218
+
219
+ function scanActivityLogs(basePath: string): UnitTrace[] {
220
+ const activityDir = join(gsdRoot(basePath), "activity");
221
+ if (!existsSync(activityDir)) return [];
222
+
223
+ const files = readdirSync(activityDir).filter(f => f.endsWith(".jsonl")).sort();
224
+ const lastFiles = files.slice(-5);
225
+ const traces: UnitTrace[] = [];
226
+
227
+ for (const file of lastFiles) {
228
+ const match = ACTIVITY_FILENAME_RE.exec(file);
229
+ if (!match) continue;
230
+
231
+ const seq = parseInt(match[1]!, 10);
232
+ const unitType = match[2]!;
233
+ const unitId = match[3]!;
234
+ const filePath = join(activityDir, file);
235
+
236
+ let entries: unknown[] = [];
237
+ const nativeResult = nativeParseJsonlTail(filePath, MAX_JSONL_BYTES);
238
+ if (nativeResult) {
239
+ entries = nativeResult.entries;
240
+ } else {
241
+ try {
242
+ const raw = readFileSync(filePath, "utf-8");
243
+ entries = parseJSONL(raw);
244
+ } catch { continue; }
245
+ }
246
+
247
+ const trace = extractTrace(entries);
248
+ const stat = statSync(filePath, { throwIfNoEntry: false });
249
+
250
+ traces.push({
251
+ file,
252
+ unitType,
253
+ unitId,
254
+ seq,
255
+ trace,
256
+ mtime: stat?.mtimeMs ?? 0,
257
+ });
258
+ }
259
+
260
+ return traces.sort((a, b) => b.seq - a.seq);
261
+ }
262
+
263
+ // ─── Completed Keys Loader ────────────────────────────────────────────────────
264
+
265
+ function loadCompletedKeys(basePath: string): string[] {
266
+ const file = join(basePath, ".gsd", "completed-units.json");
267
+ try {
268
+ if (existsSync(file)) {
269
+ return JSON.parse(readFileSync(file, "utf-8"));
270
+ }
271
+ } catch { /* non-fatal */ }
272
+ return [];
273
+ }
274
+
275
+ // ─── Anomaly Detectors ───────────────────────────────────────────────────────
276
+
277
+ function detectStuckLoops(units: UnitMetrics[], anomalies: ForensicAnomaly[]): void {
278
+ const counts = new Map<string, number>();
279
+ for (const u of units) {
280
+ const key = `${u.type}/${u.id}`;
281
+ counts.set(key, (counts.get(key) ?? 0) + 1);
282
+ }
283
+ for (const [key, count] of counts) {
284
+ if (count > 1) {
285
+ const [unitType, ...idParts] = key.split("/");
286
+ anomalies.push({
287
+ type: "stuck-loop",
288
+ severity: count >= 3 ? "error" : "warning",
289
+ unitType,
290
+ unitId: idParts.join("/"),
291
+ summary: `Unit ${key} was dispatched ${count} times`,
292
+ details: `Repeated dispatch suggests the unit completed but its artifacts weren't verified, or the state machine kept returning it.`,
293
+ });
294
+ }
295
+ }
296
+ }
297
+
298
+ function detectCostSpikes(units: UnitMetrics[], anomalies: ForensicAnomaly[]): void {
299
+ const avgMap = getAverageCostPerUnitType(units);
300
+ for (const u of units) {
301
+ const avg = avgMap.get(u.type);
302
+ if (avg && avg > 0 && u.cost > avg * 3) {
303
+ anomalies.push({
304
+ type: "cost-spike",
305
+ severity: "warning",
306
+ unitType: u.type,
307
+ unitId: u.id,
308
+ summary: `${formatCost(u.cost)} vs ${formatCost(avg)} average for ${u.type}`,
309
+ details: `Unit ${u.type}/${u.id} cost ${(u.cost / avg).toFixed(1)}x the average. May indicate excessive retries or large context.`,
310
+ });
311
+ }
312
+ }
313
+ }
314
+
315
+ function detectTimeouts(traces: UnitTrace[], anomalies: ForensicAnomaly[]): void {
316
+ for (const ut of traces) {
317
+ // Check for timeout-recovery custom messages in tool calls
318
+ const hasTimeout = ut.trace.toolCalls.some(tc =>
319
+ tc.name === "sendmessage" &&
320
+ JSON.stringify(tc.input).includes("gsd-auto-timeout-recovery"),
321
+ );
322
+ // Check for timeout keywords in last reasoning
323
+ const reasoningTimeout = ut.trace.lastReasoning &&
324
+ /(?:idle.?timeout|hard.?timeout|timeout.?recovery)/i.test(ut.trace.lastReasoning);
325
+
326
+ if (hasTimeout || reasoningTimeout) {
327
+ anomalies.push({
328
+ type: "timeout",
329
+ severity: "warning",
330
+ unitType: ut.unitType,
331
+ unitId: ut.unitId,
332
+ summary: `Timeout detected in ${ut.unitType}/${ut.unitId}`,
333
+ details: `Activity log ${ut.file} contains timeout recovery patterns. The unit may have stalled.`,
334
+ });
335
+ }
336
+ }
337
+ }
338
+
339
+ function detectMissingArtifacts(completedKeys: string[], basePath: string, anomalies: ForensicAnomaly[]): void {
340
+ for (const key of completedKeys) {
341
+ const slashIdx = key.indexOf("/");
342
+ if (slashIdx === -1) continue;
343
+ const unitType = key.slice(0, slashIdx);
344
+ const unitId = key.slice(slashIdx + 1);
345
+
346
+ if (!verifyExpectedArtifact(unitType, unitId, basePath)) {
347
+ anomalies.push({
348
+ type: "missing-artifact",
349
+ severity: "error",
350
+ unitType,
351
+ unitId,
352
+ summary: `Completed key ${key} but artifact missing or invalid`,
353
+ details: `The unit is recorded as completed but verifyExpectedArtifact() returns false. The completion state is stale.`,
354
+ });
355
+ }
356
+ }
357
+ }
358
+
359
+ function detectCrash(crashLock: LockData | null, anomalies: ForensicAnomaly[]): void {
360
+ if (!crashLock) return;
361
+ if (isLockProcessAlive(crashLock)) return; // Process still running, not a crash
362
+
363
+ anomalies.push({
364
+ type: "crash",
365
+ severity: "error",
366
+ unitType: crashLock.unitType,
367
+ unitId: crashLock.unitId,
368
+ summary: `Stale crash lock: PID ${crashLock.pid} is dead`,
369
+ details: formatCrashInfo(crashLock),
370
+ });
371
+ }
372
+
373
+ function detectDoctorIssues(issues: DoctorIssue[], anomalies: ForensicAnomaly[]): void {
374
+ for (const issue of issues) {
375
+ if (issue.severity === "error") {
376
+ anomalies.push({
377
+ type: "doctor-issue",
378
+ severity: "error",
379
+ summary: `Doctor: ${issue.message}`,
380
+ details: `Code: ${issue.code}, Scope: ${issue.scope}, Unit: ${issue.unitId}${issue.file ? `, File: ${issue.file}` : ""}`,
381
+ });
382
+ }
383
+ }
384
+ }
385
+
386
+ function detectErrorTraces(traces: UnitTrace[], anomalies: ForensicAnomaly[]): void {
387
+ for (const ut of traces) {
388
+ if (ut.trace.errors.length > 0) {
389
+ anomalies.push({
390
+ type: "error-trace",
391
+ severity: "warning",
392
+ unitType: ut.unitType,
393
+ unitId: ut.unitId,
394
+ summary: `${ut.trace.errors.length} error(s) in ${ut.unitType}/${ut.unitId}`,
395
+ details: ut.trace.errors.slice(0, 3).join("\n"),
396
+ });
397
+ }
398
+ }
399
+ }
400
+
401
+ // ─── Report Persistence ───────────────────────────────────────────────────────
402
+
403
+ function saveForensicReport(basePath: string, report: ForensicReport, problemDescription: string): string {
404
+ const dir = join(gsdRoot(basePath), "forensics");
405
+ mkdirSync(dir, { recursive: true });
406
+
407
+ const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
408
+ const filePath = join(dir, `report-${ts}.md`);
409
+
410
+ const redact = (s: string) => redactForGitHub(s, basePath);
411
+
412
+ const sections: string[] = [
413
+ `# GSD Forensic Report`,
414
+ ``,
415
+ `**Generated:** ${report.timestamp}`,
416
+ `**GSD Version:** ${report.gsdVersion}`,
417
+ `**Active Milestone:** ${report.activeMilestone ?? "none"}`,
418
+ `**Active Slice:** ${report.activeSlice ?? "none"}`,
419
+ ``,
420
+ `## Problem Description`,
421
+ ``,
422
+ problemDescription,
423
+ ``,
424
+ ];
425
+
426
+ // Anomalies
427
+ if (report.anomalies.length > 0) {
428
+ sections.push(`## Anomalies Detected (${report.anomalies.length})`, ``);
429
+ for (const a of report.anomalies) {
430
+ sections.push(`### [${a.severity.toUpperCase()}] ${a.type}: ${a.summary}`);
431
+ if (a.unitType) sections.push(`- Unit: ${a.unitType}/${a.unitId ?? ""}`);
432
+ sections.push(`- ${redact(a.details)}`, ``);
433
+ }
434
+ } else {
435
+ sections.push(`## Anomalies`, ``, `No anomalies detected.`, ``);
436
+ }
437
+
438
+ // Recent units
439
+ if (report.recentUnits.length > 0) {
440
+ sections.push(`## Recent Units`, ``);
441
+ sections.push(`| Type | ID | Cost | Duration | Model |`);
442
+ sections.push(`|------|-----|------|----------|-------|`);
443
+ for (const u of report.recentUnits) {
444
+ sections.push(`| ${u.type} | ${u.id} | ${formatCost(u.cost)} | ${formatDuration(u.duration)} | ${u.model} |`);
445
+ }
446
+ sections.push(``);
447
+ }
448
+
449
+ // Unit traces
450
+ if (report.unitTraces.length > 0) {
451
+ sections.push(`## Activity Log Traces (last ${report.unitTraces.length})`, ``);
452
+ for (const ut of report.unitTraces) {
453
+ sections.push(`### ${ut.unitType}/${ut.unitId} (seq ${ut.seq})`);
454
+ sections.push(`- Tool calls: ${ut.trace.toolCallCount}`);
455
+ sections.push(`- Files written: ${ut.trace.filesWritten.length}`);
456
+ sections.push(`- Errors: ${ut.trace.errors.length}`);
457
+ if (ut.trace.lastReasoning) {
458
+ sections.push(`- Last reasoning: ${redact(ut.trace.lastReasoning.slice(0, 200))}`);
459
+ }
460
+ sections.push(``);
461
+ }
462
+ }
463
+
464
+ // Doctor issues
465
+ if (report.doctorIssues.length > 0) {
466
+ sections.push(`## Doctor Issues`, ``);
467
+ sections.push(formatDoctorIssuesForPrompt(report.doctorIssues), ``);
468
+ }
469
+
470
+ // Crash lock
471
+ if (report.crashLock) {
472
+ sections.push(`## Crash Lock`, ``);
473
+ sections.push(redact(formatCrashInfo(report.crashLock)), ``);
474
+ }
475
+
476
+ writeFileSync(filePath, sections.join("\n"), "utf-8");
477
+ return filePath;
478
+ }
479
+
480
+ // ─── Prompt Formatter ─────────────────────────────────────────────────────────
481
+
482
+ function formatReportForPrompt(report: ForensicReport): string {
483
+ const MAX_BYTES = 30 * 1024;
484
+ const sections: string[] = [];
485
+
486
+ // Anomalies (most important, first)
487
+ sections.push(`### Anomalies (${report.anomalies.length})`);
488
+ if (report.anomalies.length === 0) {
489
+ sections.push("No anomalies detected.");
490
+ } else {
491
+ for (const a of report.anomalies) {
492
+ sections.push(`- **[${a.severity.toUpperCase()}] ${a.type}**: ${a.summary}`);
493
+ if (a.details) sections.push(` ${a.details.slice(0, 300)}`);
494
+ }
495
+ }
496
+ sections.push("");
497
+
498
+ // Recent unit history
499
+ if (report.recentUnits.length > 0) {
500
+ sections.push(`### Recent Units (last ${report.recentUnits.length})`);
501
+ sections.push("| Type | ID | Cost | Duration | Model |");
502
+ sections.push("|------|-----|------|----------|-------|");
503
+ for (const u of report.recentUnits) {
504
+ sections.push(`| ${u.type} | ${u.id} | ${formatCost(u.cost)} | ${formatDuration(u.duration)} | ${u.model} |`);
505
+ }
506
+ sections.push("");
507
+ }
508
+
509
+ // Trace summaries (last 3)
510
+ const recentTraces = report.unitTraces.slice(0, 3);
511
+ if (recentTraces.length > 0) {
512
+ sections.push(`### Activity Log Traces (last ${recentTraces.length})`);
513
+ for (const ut of recentTraces) {
514
+ sections.push(`**${ut.unitType}/${ut.unitId}** (seq ${ut.seq})`);
515
+ sections.push(`- Tool calls: ${ut.trace.toolCallCount}, Errors: ${ut.trace.errors.length}`);
516
+ if (ut.trace.filesWritten.length > 0) {
517
+ sections.push(`- Files written: ${ut.trace.filesWritten.slice(0, 5).join(", ")}`);
518
+ }
519
+ if (ut.trace.errors.length > 0) {
520
+ sections.push(`- Errors: ${ut.trace.errors.slice(0, 2).map(e => e.slice(0, 200)).join("; ")}`);
521
+ }
522
+ if (ut.trace.lastReasoning) {
523
+ sections.push(`- Last reasoning: "${ut.trace.lastReasoning.slice(0, 300)}"`);
524
+ }
525
+ sections.push("");
526
+ }
527
+ }
528
+
529
+ // Doctor issues (error severity only)
530
+ const errorIssues = report.doctorIssues.filter(i => i.severity === "error");
531
+ if (errorIssues.length > 0) {
532
+ sections.push(`### Doctor Issues (${errorIssues.length} errors)`);
533
+ sections.push(formatDoctorIssuesForPrompt(errorIssues));
534
+ sections.push("");
535
+ }
536
+
537
+ // Crash lock
538
+ if (report.crashLock) {
539
+ sections.push("### Crash Lock");
540
+ sections.push(formatCrashInfo(report.crashLock));
541
+ const alive = isLockProcessAlive(report.crashLock);
542
+ sections.push(`Process alive: ${alive}`);
543
+ sections.push("");
544
+ }
545
+
546
+ // Metrics summary
547
+ if (report.metrics?.units) {
548
+ const totals = getProjectTotals(report.metrics.units);
549
+ sections.push("### Metrics Summary");
550
+ sections.push(`- Total units: ${totals.units}`);
551
+ sections.push(`- Total cost: ${formatCost(totals.cost)}`);
552
+ sections.push(`- Total tokens: ${formatTokenCount(totals.tokens.total)}`);
553
+ sections.push(`- Total duration: ${formatDuration(totals.duration)}`);
554
+ sections.push("");
555
+ }
556
+
557
+ // Completed keys count
558
+ sections.push(`### Completed Keys: ${report.completedKeys.length}`);
559
+ sections.push(`### GSD Version: ${report.gsdVersion}`);
560
+ sections.push(`### Active Milestone: ${report.activeMilestone ?? "none"}`);
561
+ sections.push(`### Active Slice: ${report.activeSlice ?? "none"}`);
562
+
563
+ let result = sections.join("\n");
564
+ if (result.length > MAX_BYTES) {
565
+ result = result.slice(0, MAX_BYTES) + "\n\n[... truncated at 30KB ...]";
566
+ }
567
+ return result;
568
+ }
569
+
570
+ // ─── Redaction ────────────────────────────────────────────────────────────────
571
+
572
+ function redactForGitHub(text: string, basePath: string): string {
573
+ let result = text;
574
+
575
+ // Replace absolute paths
576
+ result = result.replaceAll(basePath, ".");
577
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
578
+ if (home) result = result.replaceAll(home, "~");
579
+
580
+ // Strip API key patterns
581
+ result = result.replace(/sk-[a-zA-Z0-9]{20,}/g, "sk-***");
582
+ result = result.replace(/Bearer\s+\S+/g, "Bearer ***");
583
+
584
+ // Strip env var assignments
585
+ result = result.replace(/[A-Z_]{2,}=\S+/g, (match) => {
586
+ const eq = match.indexOf("=");
587
+ return match.slice(0, eq + 1) + "***";
588
+ });
589
+
590
+ // Truncate long lines
591
+ result = result.split("\n").map(line =>
592
+ line.length > 500 ? line.slice(0, 497) + "..." : line,
593
+ ).join("\n");
594
+
595
+ return result;
596
+ }
@@ -45,8 +45,9 @@ export interface GitPreferences {
45
45
  /** Controls auto-mode git isolation strategy.
46
46
  * - "worktree": (default) creates a milestone worktree for isolated work
47
47
  * - "branch": works directly in the project root (for submodule-heavy repos)
48
+ * - "none": no git isolation — commits land on the user's current branch directly
48
49
  */
49
- isolation?: "worktree" | "branch";
50
+ isolation?: "worktree" | "branch" | "none";
50
51
  /** When false, prevents GSD from committing .gsd/ planning artifacts to git.
51
52
  * The .gsd/ folder is added to .gitignore and kept local-only.
52
53
  * Default: true (planning docs are tracked in git).