gsd-pi 2.71.0-dev.977c553 → 2.71.0-dev.d4d916a

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 (278) hide show
  1. package/dist/cli.js +12 -3
  2. package/dist/headless-events.d.ts +2 -0
  3. package/dist/headless-events.js +7 -0
  4. package/dist/headless.js +16 -3
  5. package/dist/mcp-server.js +6 -6
  6. package/dist/provider-migrations.d.ts +10 -0
  7. package/dist/provider-migrations.js +12 -0
  8. package/dist/resource-loader.js +139 -13
  9. package/dist/resources/GSD-WORKFLOW.md +1 -1
  10. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -4
  11. package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
  12. package/dist/resources/extensions/gsd/auto/loop.js +32 -1
  13. package/dist/resources/extensions/gsd/auto/phases.js +1 -1
  14. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  15. package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
  17. package/dist/resources/extensions/gsd/auto-start.js +31 -7
  18. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  19. package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
  20. package/dist/resources/extensions/gsd/auto.js +52 -0
  21. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +2 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
  23. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
  24. package/dist/resources/extensions/gsd/commands/context.js +15 -6
  25. package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
  26. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
  27. package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
  28. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
  29. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
  30. package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
  31. package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
  32. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  33. package/dist/resources/extensions/gsd/forensics.js +19 -6
  34. package/dist/resources/extensions/gsd/guided-flow.js +5 -10
  35. package/dist/resources/extensions/gsd/metrics.js +1 -0
  36. package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
  37. package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
  38. package/dist/resources/extensions/gsd/notification-store.js +56 -5
  39. package/dist/resources/extensions/gsd/notification-widget.js +5 -13
  40. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
  42. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  43. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  44. package/dist/resources/extensions/gsd/prompts/execute-task.md +20 -19
  45. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  48. package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
  49. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  50. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  51. package/dist/resources/extensions/gsd/session-model-override.js +25 -0
  52. package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
  53. package/dist/resources/extensions/ollama/index.js +13 -5
  54. package/dist/resources/skills/create-skill/SKILL.md +2 -0
  55. package/dist/startup-model-validation.d.ts +0 -1
  56. package/dist/startup-model-validation.js +6 -2
  57. package/dist/web/standalone/.next/BUILD_ID +1 -1
  58. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  59. package/dist/web/standalone/.next/build-manifest.json +2 -2
  60. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  61. package/dist/web/standalone/.next/required-server-files.json +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.html +1 -1
  79. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  86. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  88. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  89. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  90. package/dist/web/standalone/server.js +1 -1
  91. package/package.json +1 -1
  92. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  93. package/packages/mcp-server/dist/workflow-tools.js +21 -11
  94. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  95. package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
  96. package/packages/mcp-server/src/workflow-tools.ts +31 -11
  97. package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  98. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
  100. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
  101. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
  102. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
  103. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
  104. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  105. package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
  106. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  107. package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
  108. package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
  109. package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
  110. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  111. package/packages/pi-ai/dist/providers/anthropic.js +7 -4
  112. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  113. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  114. package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
  115. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  116. package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
  117. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
  118. package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
  119. package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
  120. package/packages/pi-ai/src/providers/anthropic.ts +8 -4
  121. package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
  122. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
  123. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
  124. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
  125. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
  126. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
  128. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
  130. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
  132. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
  134. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts +2 -0
  136. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
  137. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
  138. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
  139. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
  141. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
  143. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
  144. package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
  145. package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
  147. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
  149. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
  151. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
  152. package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
  153. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
  154. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  155. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/index.js +1 -1
  157. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
  165. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  168. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  171. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +43 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  173. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +7 -2
  175. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -3
  181. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
  183. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  184. package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
  185. package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
  186. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
  187. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
  188. package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
  189. package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
  190. package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
  191. package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
  192. package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
  193. package/packages/pi-coding-agent/src/index.ts +1 -0
  194. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
  195. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
  196. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
  197. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
  198. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +7 -2
  199. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
  200. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +4 -3
  201. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
  202. package/src/resources/GSD-WORKFLOW.md +1 -1
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +13 -5
  204. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +56 -4
  205. package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
  206. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  207. package/src/resources/extensions/gsd/auto/loop.ts +45 -1
  208. package/src/resources/extensions/gsd/auto/phases.ts +2 -0
  209. package/src/resources/extensions/gsd/auto/session.ts +11 -0
  210. package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
  211. package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
  212. package/src/resources/extensions/gsd/auto-start.ts +38 -7
  213. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  214. package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
  215. package/src/resources/extensions/gsd/auto.ts +68 -0
  216. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +2 -0
  217. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
  218. package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
  219. package/src/resources/extensions/gsd/commands/context.ts +16 -5
  220. package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
  221. package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
  222. package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
  223. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
  224. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
  225. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
  226. package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
  227. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  228. package/src/resources/extensions/gsd/forensics.ts +23 -7
  229. package/src/resources/extensions/gsd/guided-flow.ts +5 -10
  230. package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
  231. package/src/resources/extensions/gsd/metrics.ts +12 -1
  232. package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
  233. package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
  234. package/src/resources/extensions/gsd/notification-store.ts +54 -5
  235. package/src/resources/extensions/gsd/notification-widget.ts +5 -14
  236. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
  237. package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
  238. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  239. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  240. package/src/resources/extensions/gsd/prompts/execute-task.md +20 -19
  241. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  242. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  243. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  244. package/src/resources/extensions/gsd/prompts/queue.md +3 -2
  245. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  246. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  247. package/src/resources/extensions/gsd/session-model-override.ts +36 -0
  248. package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
  249. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
  250. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
  251. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
  252. package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
  253. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  254. package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
  255. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
  256. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
  257. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
  258. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
  259. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
  260. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
  261. package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
  262. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
  263. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
  264. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
  265. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
  266. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
  267. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
  268. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  269. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
  270. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
  271. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
  272. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
  273. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
  274. package/src/resources/extensions/ollama/index.ts +13 -3
  275. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
  276. package/src/resources/skills/create-skill/SKILL.md +2 -0
  277. /package/dist/web/standalone/.next/static/{4xyaXTn7-shVHaGMcl75o → IRnpNeY-_eO7SxKBIkTbL}/_buildManifest.js +0 -0
  278. /package/dist/web/standalone/.next/static/{4xyaXTn7-shVHaGMcl75o → IRnpNeY-_eO7SxKBIkTbL}/_ssgManifest.js +0 -0
@@ -4,10 +4,43 @@ import { ensurePreferencesFile, handlePrefs, handlePrefsMode, handlePrefsWizard
4
4
  import { runEnvironmentChecks } from "../../doctor-environment.js";
5
5
  import { deriveState } from "../../state.js";
6
6
  import { handleCmux } from "../../commands-cmux.js";
7
+ import { setSessionModelOverride } from "../../session-model-override.js";
7
8
  import { projectRoot } from "../context.js";
8
- import { formatShortcut } from "../../files.js";
9
- export function showHelp(ctx) {
10
- const lines = [
9
+ import { formattedShortcutPair } from "../../shortcut-defs.js";
10
+ export function showHelp(ctx, args = "") {
11
+ const summaryLines = [
12
+ "GSD — Get Shit Done\n",
13
+ "QUICK START",
14
+ " /gsd start <tpl> Start a workflow template",
15
+ " /gsd Run next unit (same as /gsd next)",
16
+ " /gsd auto Run all queued units continuously",
17
+ " /gsd pause Pause auto-mode",
18
+ " /gsd stop Stop auto-mode gracefully",
19
+ "",
20
+ "VISIBILITY",
21
+ ` /gsd status Dashboard (${formattedShortcutPair("dashboard")})`,
22
+ ` /gsd parallel watch Parallel monitor (${formattedShortcutPair("parallel")})`,
23
+ ` /gsd notifications Notification history (${formattedShortcutPair("notifications")})`,
24
+ " /gsd visualize Interactive 10-tab TUI",
25
+ " /gsd queue Show queued/dispatched units",
26
+ "",
27
+ "COURSE CORRECTION",
28
+ " /gsd steer <desc> Apply user override to active work",
29
+ " /gsd capture <text> Quick-capture a thought to CAPTURES.md",
30
+ " /gsd triage Classify and route pending captures",
31
+ " /gsd undo Revert last completed unit [--force]",
32
+ " /gsd rethink Conversational project reorganization",
33
+ "",
34
+ "SETUP",
35
+ " /gsd init Project init wizard",
36
+ " /gsd setup Global setup status [llm|search|remote|keys|prefs]",
37
+ " /gsd model Switch active session model",
38
+ " /gsd prefs Manage preferences",
39
+ " /gsd doctor Diagnose and repair .gsd/ state",
40
+ "",
41
+ "Use /gsd help full for the complete command reference.",
42
+ ];
43
+ const fullLines = [
11
44
  "GSD — Get Shit Done\n",
12
45
  "WORKFLOW",
13
46
  " /gsd start <tpl> Start a workflow template (bugfix, spike, feature, hotfix, etc.)",
@@ -21,12 +54,13 @@ export function showHelp(ctx) {
21
54
  " /gsd new-milestone Create milestone from headless context (used by gsd headless)",
22
55
  "",
23
56
  "VISIBILITY",
24
- ` /gsd status Show progress dashboard (${formatShortcut("Ctrl+Alt+G")})`,
57
+ ` /gsd status Show progress dashboard (${formattedShortcutPair("dashboard")})`,
58
+ ` /gsd parallel watch Open parallel worker monitor (${formattedShortcutPair("parallel")})`,
25
59
  " /gsd visualize Interactive 10-tab TUI (progress, timeline, deps, metrics, health, agent, changes, knowledge, captures, export)",
26
60
  " /gsd queue Show queued/dispatched units and execution order",
27
61
  " /gsd history View execution history [--cost] [--phase] [--model] [N]",
28
62
  " /gsd changelog Show categorized release notes [version]",
29
- ` /gsd notifications View persistent notification history [clear|tail|filter] (${formatShortcut("Ctrl+Alt+N")})`,
63
+ ` /gsd notifications View persistent notification history [clear|tail|filter] (${formattedShortcutPair("notifications")})`,
30
64
  "",
31
65
  "COURSE CORRECTION",
32
66
  " /gsd steer <desc> Apply user override to active work",
@@ -66,7 +100,8 @@ export function showHelp(ctx) {
66
100
  " /gsd inspect Show SQLite DB diagnostics (schema, row counts, recent entries)",
67
101
  " /gsd update Update GSD to the latest version via npm",
68
102
  ];
69
- ctx.ui.notify(lines.join("\n"), "info");
103
+ const full = ["full", "--full", "all"].includes(args.trim().toLowerCase());
104
+ ctx.ui.notify((full ? fullLines : summaryLines).join("\n"), "info");
70
105
  }
71
106
  export async function handleStatus(ctx) {
72
107
  const basePath = projectRoot();
@@ -82,9 +117,9 @@ export async function handleStatus(ctx) {
82
117
  const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done(true)), {
83
118
  overlay: true,
84
119
  overlayOptions: {
85
- width: "70%",
86
- minWidth: 60,
87
- maxHeight: "90%",
120
+ width: "90%",
121
+ minWidth: 80,
122
+ maxHeight: "92%",
88
123
  anchor: "center",
89
124
  },
90
125
  });
@@ -251,11 +286,21 @@ async function handleModel(trimmedArgs, ctx, pi) {
251
286
  ctx.ui.notify(`No API key for ${targetModel.provider}/${targetModel.id}`, "warning");
252
287
  return;
253
288
  }
289
+ // /gsd model is an explicit per-session pin for GSD dispatches.
290
+ // This is captured at auto bootstrap so it survives internal session
291
+ // switches during /gsd auto and /gsd next runs.
292
+ const sessionId = ctx.sessionManager?.getSessionId?.();
293
+ if (sessionId) {
294
+ setSessionModelOverride(sessionId, {
295
+ provider: targetModel.provider,
296
+ id: targetModel.id,
297
+ });
298
+ }
254
299
  ctx.ui.notify(`Model: ${targetModel.provider}/${targetModel.id}`, "info");
255
300
  }
256
301
  export async function handleCoreCommand(trimmed, ctx, pi) {
257
- if (trimmed === "help" || trimmed === "h" || trimmed === "?") {
258
- showHelp(ctx);
302
+ if (trimmed === "help" || trimmed === "h" || trimmed === "?" || trimmed.startsWith("help ")) {
303
+ showHelp(ctx, trimmed.startsWith("help ") ? trimmed.slice(5).trim() : "");
259
304
  return true;
260
305
  }
261
306
  if (trimmed === "status") {
@@ -2,6 +2,7 @@
2
2
  // View, filter, and clear the persistent notification history.
3
3
  import { readNotifications, clearNotifications, getUnreadCount, suppressPersistence, unsuppressPersistence, } from "../../notification-store.js";
4
4
  import { GSDNotificationOverlay } from "../../notification-overlay.js";
5
+ const MAX_INLINE_ENTRIES = 40;
5
6
  function severityIcon(severity) {
6
7
  switch (severity) {
7
8
  case "error": return "✗";
@@ -38,14 +39,18 @@ export async function handleNotificationsCommand(args, ctx, pi) {
38
39
  if (args === "tail" || args.startsWith("tail ")) {
39
40
  const countStr = args.replace(/^tail\s*/, "").trim();
40
41
  const count = countStr ? parseInt(countStr, 10) : 20;
41
- const n = isNaN(count) || count < 1 ? 20 : Math.min(count, 100);
42
- const entries = readNotifications().slice(0, n);
42
+ const all = readNotifications();
43
+ const n = isNaN(count) || count < 1 ? 20 : Math.min(count, MAX_INLINE_ENTRIES);
44
+ const entries = all.slice(0, n);
43
45
  if (entries.length === 0) {
44
46
  ctx.ui.notify("No notifications.", "info");
45
47
  return true;
46
48
  }
47
49
  const lines = entries.map((e) => `${severityIcon(e.severity)} [${formatTimestamp(e.ts)}] ${e.message}`);
48
- ctx.ui.notify(`Last ${entries.length} notification(s):\n${lines.join("\n")}`, "info");
50
+ const suffix = all.length > entries.length
51
+ ? `\n... and ${all.length - entries.length} more (open /gsd notifications to browse all)`
52
+ : "";
53
+ ctx.ui.notify(`Last ${entries.length} notification(s):\n${lines.join("\n")}${suffix}`, "info");
49
54
  return true;
50
55
  }
51
56
  // /gsd notifications filter <severity>
@@ -61,7 +66,9 @@ export async function handleNotificationsCommand(args, ctx, pi) {
61
66
  return true;
62
67
  }
63
68
  const lines = entries.slice(0, 20).map((e) => `${severityIcon(e.severity)} [${formatTimestamp(e.ts)}] ${e.message}`);
64
- const suffix = entries.length > 20 ? `\n... and ${entries.length - 20} more` : "";
69
+ const suffix = entries.length > 20
70
+ ? `\n... and ${entries.length - 20} more (open /gsd notifications to browse all)`
71
+ : "";
65
72
  ctx.ui.notify(`${severity} notifications (${entries.length}):\n${lines.join("\n")}${suffix}`, "info");
66
73
  return true;
67
74
  }
@@ -70,7 +77,7 @@ export async function handleNotificationsCommand(args, ctx, pi) {
70
77
  // Try overlay first (TUI mode)
71
78
  if (ctx.hasUI) {
72
79
  try {
73
- await ctx.ui.custom((tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done()), {
80
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done(true)), {
74
81
  overlay: true,
75
82
  overlayOptions: {
76
83
  width: "80%",
@@ -80,7 +87,9 @@ export async function handleNotificationsCommand(args, ctx, pi) {
80
87
  backdrop: true,
81
88
  },
82
89
  });
83
- return true;
90
+ if (result !== undefined) {
91
+ return true;
92
+ }
84
93
  }
85
94
  catch {
86
95
  // Fall through to text output if overlay fails
@@ -12,7 +12,7 @@ import { nextMilestoneId } from "../../milestone-ids.js";
12
12
  import { findMilestoneIds } from "../../guided-flow.js";
13
13
  import { projectRoot } from "../context.js";
14
14
  import { createRun, listRuns } from "../../run-manager.js";
15
- import { setActiveEngineId, setActiveRunDir, startAuto, pauseAuto, isAutoActive, getActiveEngineId, } from "../../auto.js";
15
+ import { setActiveEngineId, setActiveRunDir, startAutoDetached, pauseAuto, isAutoActive, getActiveEngineId, } from "../../auto.js";
16
16
  import { validateDefinition } from "../../definition-loader.js";
17
17
  // ─── Custom Workflow Subcommands ─────────────────────────────────────────
18
18
  const WORKFLOW_USAGE = [
@@ -58,7 +58,7 @@ async function handleCustomWorkflow(sub, ctx, pi) {
58
58
  setActiveEngineId("custom");
59
59
  setActiveRunDir(runDir);
60
60
  ctx.ui.notify(`Created workflow run: ${defName}\nRun dir: ${runDir}`, "info");
61
- await startAuto(ctx, pi, base, false);
61
+ startAutoDetached(ctx, pi, base, false);
62
62
  }
63
63
  catch (err) {
64
64
  // Clean up engine state so a failed workflow run doesn't pollute the next /gsd auto
@@ -137,14 +137,8 @@ async function handleCustomWorkflow(sub, ctx, pi) {
137
137
  ctx.ui.notify("No custom workflow to resume. Use /gsd auto for dev workflow.", "warning");
138
138
  return true;
139
139
  }
140
- try {
141
- await startAuto(ctx, pi, projectRoot(), false);
142
- ctx.ui.notify("Custom workflow resumed.", "info");
143
- }
144
- catch (err) {
145
- const msg = err instanceof Error ? err.message : String(err);
146
- ctx.ui.notify(`Failed to resume workflow: ${msg}`, "error");
147
- }
140
+ startAutoDetached(ctx, pi, projectRoot(), false);
141
+ ctx.ui.notify("Custom workflow resumed.", "info");
148
142
  return true;
149
143
  }
150
144
  // Unknown subcommand — show usage
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Full-screen overlay showing auto-mode progress: milestone/slice/task
5
5
  * breakdown, current unit, completed units, timing, and activity log.
6
- * Toggled with Ctrl+Alt+G (⌃⌥G on macOS) or opened from /gsd status.
6
+ * Toggled with Ctrl+Alt+G (⌃⌥G on macOS), Ctrl+Shift+G fallback,
7
+ * or opened from /gsd status.
7
8
  */
8
9
  import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
9
10
  import { deriveState } from "./state.js";
@@ -19,6 +20,7 @@ import { formatDuration, padRight, joinColumns, centerLine, fitColumns, STATUS_G
19
20
  import { estimateTimeRemaining } from "./auto-dashboard.js";
20
21
  import { computeProgressScore } from "./progress-score.js";
21
22
  import { runEnvironmentChecks } from "./doctor-environment.js";
23
+ import { formattedShortcutPair } from "./shortcut-defs.js";
22
24
  function unitLabel(type) {
23
25
  switch (type) {
24
26
  case "discuss-milestone":
@@ -174,7 +176,10 @@ export class GSDDashboardOverlay {
174
176
  }
175
177
  }
176
178
  handleInput(data) {
177
- if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.ctrlAlt("g"))) {
179
+ if (matchesKey(data, Key.escape) ||
180
+ matchesKey(data, Key.ctrl("c")) ||
181
+ matchesKey(data, Key.ctrlAlt("g")) ||
182
+ matchesKey(data, Key.ctrlShift("g"))) {
178
183
  this.dispose();
179
184
  this.onClose();
180
185
  return;
@@ -507,7 +512,7 @@ export class GSDDashboardOverlay {
507
512
  }
508
513
  lines.push(blank());
509
514
  lines.push(hr());
510
- lines.push(centered(th.fg("dim", "↑↓ scroll · g/G top/end · esc close")));
515
+ lines.push(centered(th.fg("dim", `↑↓ scroll · g/G top/end · Esc/${formattedShortcutPair("dashboard")} close`)));
511
516
  return lines;
512
517
  }
513
518
  renderProgressRow(label, done, total, color, width) {
@@ -102,10 +102,27 @@ export function getPriorSliceCompletionBlocker(base, _mainBranch, unitType, unit
102
102
  }
103
103
  }
104
104
  else {
105
+ // Positional fallback is only a heuristic for legacy slices with no
106
+ // declared dependencies. Skip any earlier slice that depends on the
107
+ // target, directly or transitively, or we can deadlock a valid zero-dep
108
+ // slice behind its own downstream dependents (#3720).
109
+ const reverseDependents = new Set();
110
+ let changed = true;
111
+ while (changed) {
112
+ changed = false;
113
+ for (const slice of slices) {
114
+ if (reverseDependents.has(slice.id))
115
+ continue;
116
+ if (slice.depends.some((depId) => depId === targetSid || reverseDependents.has(depId))) {
117
+ reverseDependents.add(slice.id);
118
+ changed = true;
119
+ }
120
+ }
121
+ }
105
122
  const targetIndex = slices.findIndex((slice) => slice.id === targetSid);
106
123
  const incomplete = slices
107
124
  .slice(0, targetIndex)
108
- .find((slice) => !slice.done);
125
+ .find((slice) => !slice.done && !reverseDependents.has(slice.id));
109
126
  if (incomplete) {
110
127
  return `Cannot dispatch ${unitType} ${unitId}: earlier slice ${targetMid}/${incomplete.id} is not complete.`;
111
128
  }
@@ -23,7 +23,7 @@ const RATE_LIMIT_RE = /rate.?limit|too many requests|429/i;
23
23
  const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i;
24
24
  const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i;
25
25
  // ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
26
- const CONNECTION_RE = /terminated|connection.?refused|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
26
+ const CONNECTION_RE = /terminated|connection.?(?:refused|error)|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
27
27
  // Catch-all for V8 JSON.parse errors: all modern variants end with "in JSON at position \d+".
28
28
  // This eliminates the need to enumerate every error message variant individually.
29
29
  const STREAM_RE = /in JSON at position \d+|Unexpected end of JSON|SyntaxError.*JSON/i;
@@ -508,19 +508,30 @@ function getDbCompletionCounts() {
508
508
  * Exported for testability.
509
509
  */
510
510
  export function detectStuckLoops(units, anomalies) {
511
- // First, collect unique startedAt values per type/id key
511
+ // First, collect unique startedAt values per type/id key, bucketed by
512
+ // autoSessionKey when available so cross-session recovery does not look
513
+ // like a within-session stuck loop.
512
514
  const dispatchMap = new Map();
513
515
  for (const u of units) {
514
516
  const key = `${u.type}/${u.id}`;
515
- let starts = dispatchMap.get(key);
517
+ let sessionBuckets = dispatchMap.get(key);
518
+ if (!sessionBuckets) {
519
+ sessionBuckets = new Map();
520
+ dispatchMap.set(key, sessionBuckets);
521
+ }
522
+ const sessionKey = u.autoSessionKey ?? "__legacy__";
523
+ let starts = sessionBuckets.get(sessionKey);
516
524
  if (!starts) {
517
525
  starts = new Set();
518
- dispatchMap.set(key, starts);
526
+ sessionBuckets.set(sessionKey, starts);
519
527
  }
520
528
  starts.add(u.startedAt);
521
529
  }
522
- for (const [key, starts] of dispatchMap) {
523
- const count = starts.size;
530
+ for (const [key, sessionBuckets] of dispatchMap) {
531
+ const hasSessionAwareData = Array.from(sessionBuckets.keys()).some((sessionKey) => sessionKey !== "__legacy__");
532
+ const count = hasSessionAwareData
533
+ ? Math.max(...Array.from(sessionBuckets.values(), (starts) => starts.size))
534
+ : (sessionBuckets.get("__legacy__")?.size ?? 0);
524
535
  if (count > 1) {
525
536
  const [unitType, ...idParts] = key.split("/");
526
537
  anomalies.push({
@@ -529,7 +540,9 @@ export function detectStuckLoops(units, anomalies) {
529
540
  unitType,
530
541
  unitId: idParts.join("/"),
531
542
  summary: `Unit ${key} was dispatched ${count} times`,
532
- details: `Repeated dispatch suggests the unit completed but its artifacts weren't verified, or the state machine kept returning it.`,
543
+ details: hasSessionAwareData
544
+ ? `Repeated dispatch within the same auto session suggests the unit completed but its artifacts were not verified, or the state machine kept returning it. Cross-session recovery runs are ignored.`
545
+ : `Repeated dispatch suggests the unit completed but its artifacts weren't verified, or the state machine kept returning it.`,
533
546
  });
534
547
  }
535
548
  }
@@ -13,7 +13,7 @@ import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
13
13
  import { buildSkillActivationBlock } from "./auto-prompts.js";
14
14
  import { deriveState } from "./state.js";
15
15
  import { invalidateAllCaches } from "./cache.js";
16
- import { startAuto } from "./auto.js";
16
+ import { startAutoDetached } from "./auto.js";
17
17
  import { clearLock } from "./crash-recovery.js";
18
18
  import { assessInterruptedSession, formatInterruptedSessionRunningMessage, formatInterruptedSessionSummary, } from "./interrupted-session.js";
19
19
  import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
@@ -40,7 +40,6 @@ import { runPreparation, formatCodebaseBrief, formatPriorContextBrief, } from ".
40
40
  // ─── Re-exports (preserve public API for existing importers) ────────────────
41
41
  export { MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId, extractMilestoneSeq, parseMilestoneId, milestoneIdSort, maxMilestoneNum, findMilestoneIds, reserveMilestoneId, claimReservedId, getReservedMilestoneIds, clearReservedMilestoneIds, } from "./milestone-ids.js";
42
42
  export { showQueue, handleQueueReorder, showQueueAdd, buildExistingMilestonesContext, } from "./guided-flow-queue.js";
43
- import { getErrorMessage } from "./error-utils.js";
44
43
  import { logWarning } from "./workflow-logger.js";
45
44
  // ─── ID Generation with Reservation ─────────────────────────────────────────
46
45
  /**
@@ -200,11 +199,7 @@ export function checkAutoStartAfterDiscuss() {
200
199
  }
201
200
  pendingAutoStartMap.delete(basePath);
202
201
  ctx.ui.notify(`Milestone ${milestoneId} ready.`, "info");
203
- startAuto(ctx, pi, basePath, false, { step }).catch((err) => {
204
- ctx.ui.notify(`Auto-start failed: ${getErrorMessage(err)}`, "error");
205
- logWarning("guided", `auto start error: ${getErrorMessage(err)}`);
206
- debugLog("auto-start-failed", { error: getErrorMessage(err) });
207
- });
202
+ startAutoDetached(ctx, pi, basePath, false, { step });
208
203
  return true;
209
204
  }
210
205
  /**
@@ -1097,7 +1092,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1097
1092
  ],
1098
1093
  });
1099
1094
  if (resume === "resume") {
1100
- await startAuto(ctx, pi, basePath, false, {
1095
+ startAutoDetached(ctx, pi, basePath, false, {
1101
1096
  interrupted,
1102
1097
  step: interrupted.pausedSession?.stepMode ?? false,
1103
1098
  });
@@ -1409,7 +1404,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1409
1404
  notYetMessage: "Run /gsd status for details.",
1410
1405
  });
1411
1406
  if (choice === "auto") {
1412
- await startAuto(ctx, pi, basePath, false);
1407
+ startAutoDetached(ctx, pi, basePath, false);
1413
1408
  }
1414
1409
  else if (choice === "status") {
1415
1410
  const { fireStatusViaCommand } = await import("./commands.js");
@@ -1622,7 +1617,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1622
1617
  notYetMessage: "Run /gsd when ready.",
1623
1618
  });
1624
1619
  if (choice === "auto") {
1625
- await startAuto(ctx, pi, basePath, false);
1620
+ startAutoDetached(ctx, pi, basePath, false);
1626
1621
  return;
1627
1622
  }
1628
1623
  if (choice === "execute") {
@@ -113,6 +113,7 @@ export function snapshotUnitMetrics(ctx, unitType, unitId, startedAt, model, opt
113
113
  model,
114
114
  startedAt,
115
115
  finishedAt: Date.now(),
116
+ ...(opts?.autoSessionKey ? { autoSessionKey: opts.autoSessionKey } : {}),
116
117
  tokens,
117
118
  cost,
118
119
  toolCalls,
@@ -15,7 +15,7 @@ import { join } from "node:path";
15
15
  import { resolveMilestonePath, resolveMilestoneFile, buildMilestoneFileName, } from "./paths.js";
16
16
  import { invalidateAllCaches } from "./cache.js";
17
17
  import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
18
- import { isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
18
+ import { getMilestone, isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
19
19
  import { logWarning } from "./workflow-logger.js";
20
20
  // ─── Park ──────────────────────────────────────────────────────────────────
21
21
  /**
@@ -68,9 +68,15 @@ export function unparkMilestone(basePath, milestoneId) {
68
68
  if (!mDir || !existsSync(mDir))
69
69
  return false;
70
70
  const parkedPath = join(mDir, buildMilestoneFileName(milestoneId, "PARKED"));
71
- if (!existsSync(parkedPath))
72
- return false; // not parked
73
- unlinkSync(parkedPath);
71
+ const hadParkedFile = existsSync(parkedPath);
72
+ const dbThinksParked = isDbAvailable() && getMilestone(milestoneId)?.status === "parked";
73
+ // Recover the reverse desync too: DB can still say "parked" even when the
74
+ // PARKED marker was lost on disk, and /gsd unpark should repair that state.
75
+ if (!hadParkedFile && !dbThinksParked)
76
+ return false;
77
+ if (hadParkedFile) {
78
+ unlinkSync(parkedPath);
79
+ }
74
80
  // Sync DB status so deriveStateFromDb picks up the unparked milestone (#2694)
75
81
  if (isDbAvailable()) {
76
82
  try {
@@ -1,8 +1,9 @@
1
1
  // GSD Extension — Notification History Overlay
2
2
  // Scrollable panel showing all persisted notifications with severity filtering.
3
- // Toggled with Ctrl+Alt+N (⌃⌥N on macOS) or opened from /gsd notifications.
3
+ // Toggled with Ctrl+Alt+N (⌃⌥N on macOS), Ctrl+Shift+N fallback, or /gsd notifications.
4
4
  import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
5
- import { readNotifications, markAllRead, clearNotifications, } from "./notification-store.js";
5
+ import { readNotifications, markAllRead, clearNotifications, onNotificationStoreChange, } from "./notification-store.js";
6
+ import { formattedShortcutPair } from "./shortcut-defs.js";
6
7
  import { padRight, joinColumns } from "../shared/mod.js";
7
8
  const FILTER_CYCLE = ["all", "error", "warning", "info"];
8
9
  function severityIcon(severity) {
@@ -55,6 +56,11 @@ function formatTimestamp(ts) {
55
56
  return ts.slice(11, 19); // fallback: HH:MM:SS
56
57
  }
57
58
  }
59
+ function notificationSignature(entries) {
60
+ return entries
61
+ .map((entry) => `${entry.ts}|${entry.severity}|${entry.read ? 1 : 0}|${entry.message}`)
62
+ .join("\n");
63
+ }
58
64
  export class GSDNotificationOverlay {
59
65
  tui;
60
66
  theme;
@@ -64,9 +70,11 @@ export class GSDNotificationOverlay {
64
70
  scrollOffset = 0;
65
71
  filterIndex = 0;
66
72
  entries = [];
73
+ entriesSignature = "";
67
74
  refreshTimer;
68
75
  disposed = false;
69
76
  resizeHandler = null;
77
+ unsubscribeStore = null;
70
78
  constructor(tui, theme, onClose) {
71
79
  this.tui = tui;
72
80
  this.theme = theme;
@@ -74,6 +82,7 @@ export class GSDNotificationOverlay {
74
82
  // Mark all as read on open
75
83
  markAllRead();
76
84
  this.entries = readNotifications();
85
+ this.entriesSignature = notificationSignature(this.entries);
77
86
  // Resize handler
78
87
  this.resizeHandler = () => {
79
88
  if (this.disposed)
@@ -82,18 +91,18 @@ export class GSDNotificationOverlay {
82
91
  this.tui.requestRender();
83
92
  };
84
93
  process.stdout.on("resize", this.resizeHandler);
85
- // Refresh every 3s for new notifications
94
+ // Subscribe to store mutations for immediate updates
95
+ this.unsubscribeStore = onNotificationStoreChange(() => {
96
+ if (this.disposed)
97
+ return;
98
+ this._refreshFromDisk();
99
+ });
100
+ // 30s safety-net for cross-process edits (web subprocess, parallel workers)
86
101
  this.refreshTimer = setInterval(() => {
87
102
  if (this.disposed)
88
103
  return;
89
- const fresh = readNotifications();
90
- if (fresh.length !== this.entries.length) {
91
- this.entries = fresh;
92
- markAllRead();
93
- this.invalidate();
94
- this.tui.requestRender();
95
- }
96
- }, 3000);
104
+ this._refreshFromDisk();
105
+ }, 30_000);
97
106
  }
98
107
  get filter() {
99
108
  return FILTER_CYCLE[this.filterIndex];
@@ -104,7 +113,10 @@ export class GSDNotificationOverlay {
104
113
  return this.entries.filter((e) => e.severity === this.filter);
105
114
  }
106
115
  handleInput(data) {
107
- if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.ctrlAlt("n"))) {
116
+ if (matchesKey(data, Key.escape) ||
117
+ matchesKey(data, Key.ctrl("c")) ||
118
+ matchesKey(data, Key.ctrlAlt("n")) ||
119
+ matchesKey(data, Key.ctrlShift("n"))) {
108
120
  this.dispose();
109
121
  this.onClose();
110
122
  return;
@@ -146,6 +158,7 @@ export class GSDNotificationOverlay {
146
158
  if (data === "c") {
147
159
  clearNotifications();
148
160
  this.entries = [];
161
+ this.entriesSignature = notificationSignature(this.entries);
149
162
  this.scrollOffset = 0;
150
163
  this.invalidate();
151
164
  this.tui.requestRender();
@@ -174,11 +187,26 @@ export class GSDNotificationOverlay {
174
187
  dispose() {
175
188
  this.disposed = true;
176
189
  clearInterval(this.refreshTimer);
190
+ if (this.unsubscribeStore) {
191
+ this.unsubscribeStore();
192
+ this.unsubscribeStore = null;
193
+ }
177
194
  if (this.resizeHandler) {
178
195
  process.stdout.removeListener("resize", this.resizeHandler);
179
196
  this.resizeHandler = null;
180
197
  }
181
198
  }
199
+ _refreshFromDisk() {
200
+ const fresh = readNotifications();
201
+ const signature = notificationSignature(fresh);
202
+ if (signature !== this.entriesSignature) {
203
+ markAllRead();
204
+ this.entries = readNotifications();
205
+ this.entriesSignature = notificationSignature(this.entries);
206
+ this.invalidate();
207
+ this.tui.requestRender();
208
+ }
209
+ }
182
210
  wrapInBox(inner, width) {
183
211
  const th = this.theme;
184
212
  const border = (s) => th.fg("borderAccent", s);
@@ -215,7 +243,8 @@ export class GSDNotificationOverlay {
215
243
  lines.push(row(joinColumns(`${title} ${th.fg("dim", "filter:")} ${filterLabel}`, th.fg("dim", count), contentWidth)));
216
244
  lines.push(hr());
217
245
  // Controls
218
- lines.push(row(th.fg("dim", "↑/↓ scroll f filter c clear Esc close")));
246
+ const closeShortcut = formattedShortcutPair("notifications");
247
+ lines.push(row(th.fg("dim", `↑/↓ scroll f filter c clear Esc close (${closeShortcut})`)));
219
248
  lines.push(blank());
220
249
  // Entries
221
250
  const filtered = this.filteredEntries;