gsd-pi 2.73.0 → 2.73.1-dev.06e4302

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 (300) hide show
  1. package/dist/cli-web-branch.d.ts +4 -3
  2. package/dist/cli-web-branch.js +10 -7
  3. package/dist/cli.js +99 -253
  4. package/dist/help-text.js +1 -1
  5. package/dist/logo.d.ts +1 -1
  6. package/dist/logo.js +1 -1
  7. package/dist/onboarding.js +59 -53
  8. package/dist/resource-loader.js +2 -2
  9. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +68 -4
  10. package/dist/resources/extensions/gsd/auto/phases.js +15 -9
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -6
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +41 -1
  14. package/dist/resources/extensions/gsd/auto-prompts.js +9 -6
  15. package/dist/resources/extensions/gsd/auto-start.js +23 -6
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +13 -0
  17. package/dist/resources/extensions/gsd/auto-verification.js +88 -3
  18. package/dist/resources/extensions/gsd/auto.js +34 -9
  19. package/dist/resources/extensions/gsd/bootstrap/crash-log.js +31 -0
  20. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -7
  21. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -1
  22. package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
  23. package/dist/resources/extensions/gsd/crash-recovery.js +51 -0
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  25. package/dist/resources/extensions/gsd/gsd-db.js +36 -2
  26. package/dist/resources/extensions/gsd/milestone-actions.js +19 -1
  27. package/dist/resources/extensions/gsd/notification-widget.js +2 -2
  28. package/dist/resources/extensions/gsd/preferences-models.js +43 -0
  29. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  30. package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
  31. package/dist/resources/extensions/gsd/state.js +61 -14
  32. package/dist/startup-model-validation.js +8 -5
  33. package/dist/update-check.d.ts +1 -0
  34. package/dist/update-check.js +13 -5
  35. package/dist/update-cmd.js +4 -3
  36. package/dist/web/standalone/.next/BUILD_ID +1 -1
  37. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  38. package/dist/web/standalone/.next/build-manifest.json +3 -3
  39. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  40. package/dist/web/standalone/.next/required-server-files.json +3 -3
  41. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  42. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  52. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  56. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  58. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  68. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  80. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  100. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  110. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  116. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  130. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  134. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  136. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/index.html +1 -1
  146. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  147. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  148. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  149. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  151. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/page.js +2 -2
  153. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  155. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  156. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  157. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  159. package/dist/web/standalone/.next/server/middleware.js +2 -2
  160. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  162. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  163. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  164. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  165. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  166. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  167. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  168. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  169. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  170. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  171. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  172. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  173. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  174. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  175. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  176. package/dist/web/standalone/server.js +1 -1
  177. package/package.json +1 -2
  178. package/packages/pi-ai/dist/index.d.ts +1 -0
  179. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  180. package/packages/pi-ai/dist/index.js +1 -0
  181. package/packages/pi-ai/dist/index.js.map +1 -1
  182. package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  183. package/packages/pi-ai/dist/utils/overflow.js +12 -0
  184. package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
  185. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
  186. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
  187. package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
  188. package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
  189. package/packages/pi-ai/src/index.ts +4 -0
  190. package/packages/pi-ai/src/utils/overflow.ts +14 -1
  191. package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
  192. package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -1
  193. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -0
  195. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +313 -8
  197. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
  199. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
  201. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
  202. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
  203. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
  204. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/model-resolver.js +25 -68
  206. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +61 -28
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +9 -3
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +116 -25
  222. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts +2 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts.map +1 -0
  225. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +63 -0
  226. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +11 -3
  229. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  230. package/packages/pi-coding-agent/package.json +1 -1
  231. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +38 -0
  232. package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -1
  233. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +355 -8
  234. package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
  235. package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
  236. package/packages/pi-coding-agent/src/core/model-resolver.ts +26 -70
  237. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +74 -32
  238. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
  239. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
  240. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +71 -0
  241. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +136 -30
  242. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +11 -3
  243. package/packages/pi-tui/dist/__tests__/tui.test.js +60 -1
  244. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  245. package/packages/pi-tui/dist/tui.d.ts +8 -0
  246. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  247. package/packages/pi-tui/dist/tui.js +32 -3
  248. package/packages/pi-tui/dist/tui.js.map +1 -1
  249. package/packages/pi-tui/src/__tests__/tui.test.ts +76 -1
  250. package/packages/pi-tui/src/tui.ts +31 -3
  251. package/pkg/package.json +1 -1
  252. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +107 -5
  253. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +111 -2
  254. package/src/resources/extensions/gsd/auto/phases.ts +22 -9
  255. package/src/resources/extensions/gsd/auto-dispatch.ts +15 -4
  256. package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
  257. package/src/resources/extensions/gsd/auto-post-unit.ts +47 -1
  258. package/src/resources/extensions/gsd/auto-prompts.ts +9 -3
  259. package/src/resources/extensions/gsd/auto-start.ts +30 -6
  260. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +17 -0
  261. package/src/resources/extensions/gsd/auto-verification.ts +98 -3
  262. package/src/resources/extensions/gsd/auto.ts +36 -14
  263. package/src/resources/extensions/gsd/bootstrap/crash-log.ts +32 -0
  264. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -7
  265. package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -1
  266. package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
  267. package/src/resources/extensions/gsd/crash-recovery.ts +59 -0
  268. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  269. package/src/resources/extensions/gsd/gsd-db.ts +52 -2
  270. package/src/resources/extensions/gsd/milestone-actions.ts +19 -1
  271. package/src/resources/extensions/gsd/notification-widget.ts +2 -2
  272. package/src/resources/extensions/gsd/preferences-models.ts +41 -0
  273. package/src/resources/extensions/gsd/preferences-types.ts +12 -0
  274. package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
  275. package/src/resources/extensions/gsd/state.ts +71 -15
  276. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
  277. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +53 -0
  278. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
  279. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +142 -0
  280. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +42 -0
  281. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +235 -0
  282. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
  283. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
  284. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +68 -8
  285. package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
  286. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
  287. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +59 -1
  288. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  289. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
  290. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
  291. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
  292. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
  293. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +267 -0
  294. package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
  295. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +179 -0
  296. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  297. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  298. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  299. /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → RXD20AQgB9BHSQJ07MDdd}/_buildManifest.js +0 -0
  300. /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → RXD20AQgB9BHSQJ07MDdd}/_ssgManifest.js +0 -0
@@ -1,9 +1,10 @@
1
1
  import { launchWebMode, stopWebMode, type WebModeLaunchStatus, type WebModeStopOptions, type WebModeStopResult } from './web-mode.js';
2
2
  export interface CliFlags {
3
- mode?: 'text' | 'json' | 'rpc';
3
+ mode?: 'text' | 'json' | 'rpc' | 'mcp';
4
4
  print?: boolean;
5
5
  continue?: boolean;
6
6
  noSession?: boolean;
7
+ worktree?: boolean | string;
7
8
  model?: string;
8
9
  listModels?: string | true;
9
10
  extensions: string[];
@@ -19,8 +20,8 @@ export interface CliFlags {
19
20
  webPort?: number;
20
21
  /** Additional allowed origins for CORS: `--allowed-origins http://192.168.1.10:8080` */
21
22
  webAllowedOrigins?: string[];
22
- help?: boolean;
23
- version?: boolean;
23
+ /** Set by `gsd sessions` when the user picks a specific session to resume */
24
+ _selectedSessionPath?: string;
24
25
  }
25
26
  type WritableLike = Pick<typeof process.stderr, 'write'>;
26
27
  export interface RunWebCliBranchDeps {
@@ -10,7 +10,7 @@ export function parseCliArgs(argv) {
10
10
  const arg = args[i];
11
11
  if (arg === '--mode' && i + 1 < args.length) {
12
12
  const mode = args[++i];
13
- if (mode === 'text' || mode === 'json' || mode === 'rpc')
13
+ if (mode === 'text' || mode === 'json' || mode === 'rpc' || mode === 'mcp')
14
14
  flags.mode = mode;
15
15
  }
16
16
  else if (arg === '--print' || arg === '-p') {
@@ -22,6 +22,15 @@ export function parseCliArgs(argv) {
22
22
  else if (arg === '--no-session') {
23
23
  flags.noSession = true;
24
24
  }
25
+ else if (arg === '--worktree' || arg === '-w') {
26
+ // -w with no value → auto-generate name; -w <name> → use that name
27
+ if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
28
+ flags.worktree = args[++i];
29
+ }
30
+ else {
31
+ flags.worktree = true;
32
+ }
33
+ }
25
34
  else if (arg === '--web') {
26
35
  flags.web = true;
27
36
  // Peek at next arg — if it looks like a path (not another flag), capture it
@@ -58,12 +67,6 @@ export function parseCliArgs(argv) {
58
67
  else if (arg === '--list-models') {
59
68
  flags.listModels = (i + 1 < args.length && !args[i + 1].startsWith('-')) ? args[++i] : true;
60
69
  }
61
- else if (arg === '--version' || arg === '-v') {
62
- flags.version = true;
63
- }
64
- else if (arg === '--help' || arg === '-h') {
65
- flags.help = true;
66
- }
67
70
  else if (!arg.startsWith('--') && !arg.startsWith('-')) {
68
71
  flags.messages.push(arg);
69
72
  }
package/dist/cli.js CHANGED
@@ -5,14 +5,14 @@ import { agentDir, sessionsDir, authFilePath } from './app-paths.js';
5
5
  import { initResources, buildResourceLoader, getNewerManagedResourceVersion } from './resource-loader.js';
6
6
  import { ensureManagedTools } from './tool-bootstrap.js';
7
7
  import { loadStoredEnvKeys } from './wizard.js';
8
- import { migratePiCredentials, getPiDefaultModelAndProvider } from './pi-migration.js';
9
- import { shouldMigrateAnthropicToClaudeCode } from './provider-migrations.js';
8
+ import { migratePiCredentials } from './pi-migration.js';
10
9
  import { shouldRunOnboarding, runOnboarding } from './onboarding.js';
11
10
  import chalk from 'chalk';
12
11
  import { checkForUpdates } from './update-check.js';
13
12
  import { printHelp, printSubcommandHelp } from './help-text.js';
14
13
  import { applySecurityOverrides } from './security-overrides.js';
15
- import { parseCliArgs as parseWebCliArgs, runWebCliBranch, migrateLegacyFlatSessions, } from './cli-web-branch.js';
14
+ import { validateConfiguredModel } from './startup-model-validation.js';
15
+ import { parseCliArgs, runWebCliBranch, migrateLegacyFlatSessions, } from './cli-web-branch.js';
16
16
  import { stopWebMode } from './web-mode.js';
17
17
  import { getProjectSessionsDir } from './project-sessions.js';
18
18
  import { markStartup, printStartupTimings } from './startup-timings.js';
@@ -37,112 +37,85 @@ function exitIfManagedResourcesAreNewer(currentAgentDir) {
37
37
  `[gsd] Run ${chalk.bold('npm install -g gsd-pi@latest')} or ${chalk.bold('gsd update')}, then try again.\n`);
38
38
  process.exit(1);
39
39
  }
40
- function parseCliArgs(argv) {
41
- const flags = { extensions: [], messages: [] };
42
- const args = argv.slice(2); // skip node + script
43
- for (let i = 0; i < args.length; i++) {
44
- const arg = args[i];
45
- if (arg === '--mode' && i + 1 < args.length) {
46
- const m = args[++i];
47
- if (m === 'text' || m === 'json' || m === 'rpc' || m === 'mcp')
48
- flags.mode = m;
49
- }
50
- else if (arg === '--print' || arg === '-p') {
51
- flags.print = true;
52
- }
53
- else if (arg === '--continue' || arg === '-c') {
54
- flags.continue = true;
55
- }
56
- else if (arg === '--no-session') {
57
- flags.noSession = true;
58
- }
59
- else if (arg === '--model' && i + 1 < args.length) {
60
- flags.model = args[++i];
61
- }
62
- else if (arg === '--extension' && i + 1 < args.length) {
63
- flags.extensions.push(args[++i]);
64
- }
65
- else if (arg === '--append-system-prompt' && i + 1 < args.length) {
66
- flags.appendSystemPrompt = args[++i];
67
- }
68
- else if (arg === '--tools' && i + 1 < args.length) {
69
- flags.tools = args[++i].split(',');
70
- }
71
- else if (arg === '--list-models') {
72
- flags.listModels = (i + 1 < args.length && !args[i + 1].startsWith('-')) ? args[++i] : true;
73
- }
74
- else if (arg === '--version' || arg === '-v') {
75
- process.stdout.write((process.env.GSD_VERSION || '0.0.0') + '\n');
76
- process.exit(0);
77
- }
78
- else if (arg === '--worktree' || arg === '-w') {
79
- // -w with no value → auto-generate name; -w <name> → use that name
80
- if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
81
- flags.worktree = args[++i];
82
- }
83
- else {
84
- flags.worktree = true;
85
- }
86
- }
87
- else if (arg === '--help' || arg === '-h') {
88
- printHelp(process.env.GSD_VERSION || '0.0.0');
89
- process.exit(0);
90
- }
91
- else if (arg === '--web') {
92
- flags.web = true;
93
- // Capture optional project path after --web (not a flag)
94
- if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
95
- flags.webPath = args[++i];
96
- }
97
- }
98
- else if (!arg.startsWith('--') && !arg.startsWith('-')) {
99
- flags.messages.push(arg);
100
- }
40
+ // ---------------------------------------------------------------------------
41
+ // Shared helpers used by both the print and interactive code paths
42
+ // ---------------------------------------------------------------------------
43
+ /**
44
+ * Print the non-interactive-mode error and exit. Called both from the early
45
+ * TTY gate (before heavy init) and from the interactive-mode TTY gate right
46
+ * before `InteractiveMode.run()`. The `includeWebHint` variant also lists
47
+ * `--web` and `headless` as alternatives.
48
+ */
49
+ function printNonTtyErrorAndExit(missing, includeWebHint) {
50
+ const suffix = missing ? ` but ${missing} not a TTY` : '';
51
+ process.stderr.write(`[gsd] Error: Interactive mode requires a terminal (TTY)${suffix}.\n`);
52
+ process.stderr.write('[gsd] Non-interactive alternatives:\n');
53
+ process.stderr.write('[gsd] gsd auto Auto-mode (pipeable, no TUI)\n');
54
+ process.stderr.write('[gsd] gsd --print "your message" Single-shot prompt\n');
55
+ if (includeWebHint) {
56
+ process.stderr.write('[gsd] gsd --web [path] Browser-only web mode\n');
57
+ }
58
+ process.stderr.write('[gsd] gsd --mode rpc JSON-RPC over stdin/stdout\n');
59
+ process.stderr.write('[gsd] gsd --mode mcp MCP server over stdin/stdout\n');
60
+ process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n');
61
+ if (includeWebHint) {
62
+ process.stderr.write('[gsd] gsd headless Auto-mode without TUI\n');
101
63
  }
102
- return flags;
64
+ process.exit(1);
103
65
  }
104
66
  /**
105
- * Validate the configured default model against the registry and reset it if
106
- * it no longer exists. Must run AFTER extensions have registered their
107
- * providers so that extension models (e.g. pi-claude-cli) are visible.
67
+ * Print extension load/conflict errors from an extensions result. Downgrades
68
+ * conflicts with built-in tools to warnings (#1347).
108
69
  */
109
- function validateConfiguredModel(modelRegistry, settingsManager) {
110
- const configuredProvider = settingsManager.getDefaultProvider();
111
- const configuredModel = settingsManager.getDefaultModel();
112
- const allModels = modelRegistry.getAll();
113
- const availableModels = modelRegistry.getAvailable();
114
- const configuredExists = configuredProvider && configuredModel &&
115
- allModels.some((m) => m.provider === configuredProvider && m.id === configuredModel);
116
- const configuredAvailable = configuredProvider && configuredModel &&
117
- availableModels.some((m) => m.provider === configuredProvider && m.id === configuredModel);
118
- if (!configuredModel || !configuredExists) {
119
- // Model not configured at all, or removed from registry — pick a fallback.
120
- // Only fires when the model is genuinely unknown (not just temporarily unavailable).
121
- const piDefault = getPiDefaultModelAndProvider();
122
- const preferred = (piDefault
123
- ? availableModels.find((m) => m.provider === piDefault.provider && m.id === piDefault.model)
124
- : undefined) ||
125
- availableModels.find((m) => m.provider === 'openai' && m.id === 'gpt-5.4') ||
126
- availableModels.find((m) => m.provider === 'openai') ||
127
- availableModels.find((m) => m.provider === 'anthropic' && m.id === 'claude-opus-4-6') ||
128
- availableModels.find((m) => m.provider === 'anthropic' && m.id.includes('opus')) ||
129
- availableModels.find((m) => m.provider === 'anthropic') ||
130
- availableModels[0];
131
- if (preferred) {
132
- settingsManager.setDefaultModelAndProvider(preferred.provider, preferred.id);
133
- }
70
+ function printExtensionErrors(errors) {
71
+ for (const err of errors) {
72
+ const isConflict = err.error.includes('supersedes') || err.error.includes('conflicts with');
73
+ const prefix = isConflict ? 'Extension conflict' : 'Extension load error';
74
+ process.stderr.write(`[gsd] ${prefix}: ${err.error}\n`);
134
75
  }
135
- if (settingsManager.getDefaultThinkingLevel() !== 'off' && !configuredExists) {
136
- settingsManager.setDefaultThinkingLevel('off');
76
+ }
77
+ /**
78
+ * Re-apply the validated model to the session when `createAgentSession()`
79
+ * reports that it had to use a fallback. Prevents silently overriding the
80
+ * persisted model of resumed conversations (#3534).
81
+ */
82
+ async function reapplyValidatedModelOnFallback(session, modelRegistry, settingsManager, fallbackMessage) {
83
+ if (!fallbackMessage)
84
+ return;
85
+ const validatedProvider = settingsManager.getDefaultProvider();
86
+ const validatedModelId = settingsManager.getDefaultModel();
87
+ if (!validatedProvider || !validatedModelId)
88
+ return;
89
+ const correctModel = modelRegistry.getAvailable()
90
+ .find((m) => m.provider === validatedProvider && m.id === validatedModelId);
91
+ if (!correctModel)
92
+ return;
93
+ try {
94
+ await session.setModel(correctModel);
95
+ }
96
+ catch {
97
+ // Provider not ready — leave session on its current model
137
98
  }
138
99
  }
139
100
  const cliFlags = parseCliArgs(process.argv);
140
101
  const isPrintMode = cliFlags.print || cliFlags.mode !== undefined;
141
- // Early resource-skew checkmust run before TTY gate so version mismatch
142
- // errors surface even in non-TTY environments.
143
- async function ensureRtkBootstrap() {
144
- if (ensureRtkBootstrap._done)
145
- return;
102
+ // `gsd [subcommand] --help` / `-h` print help before any subcommand runs.
103
+ // loader.ts only catches --help/-h as the *first* arg; here we handle the
104
+ // case where it appears later (e.g. `gsd update --help`, `gsd --foo --help`).
105
+ // Prefer subcommand-specific help when the first positional is a known
106
+ // subcommand, otherwise fall back to general help.
107
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
108
+ const helpSubcommand = cliFlags.messages[0];
109
+ const version = process.env.GSD_VERSION || '0.0.0';
110
+ if (!helpSubcommand || !printSubcommandHelp(helpSubcommand, version)) {
111
+ printHelp(version);
112
+ }
113
+ process.exit(0);
114
+ }
115
+ // RTK bootstrap — runs once per process, memoized via a module-level promise
116
+ // so concurrent callers await the same initialization.
117
+ let rtkBootstrapPromise;
118
+ async function doRtkBootstrap() {
146
119
  // RTK is opt-in via experimental.rtk preference. Default: disabled.
147
120
  // Honor GSD_RTK_DISABLED if already explicitly set in the environment
148
121
  // (env var takes precedence over preferences for manual override).
@@ -150,16 +123,18 @@ async function ensureRtkBootstrap() {
150
123
  const prefs = loadEffectiveGSDPreferences();
151
124
  const rtkEnabled = prefs?.preferences.experimental?.rtk === true;
152
125
  if (!rtkEnabled) {
153
- process.env[GSD_RTK_DISABLED_ENV] = "1";
126
+ process.env[GSD_RTK_DISABLED_ENV] = '1';
154
127
  }
155
128
  }
156
129
  const rtkStatus = await bootstrapRtk();
157
- ensureRtkBootstrap._done = true;
158
130
  markStartup('bootstrapRtk');
159
131
  if (!rtkStatus.available && rtkStatus.supported && rtkStatus.enabled && rtkStatus.reason) {
160
132
  process.stderr.write(`[gsd] Warning: RTK unavailable — continuing without shell-command compression (${rtkStatus.reason}).\n`);
161
133
  }
162
134
  }
135
+ function ensureRtkBootstrap() {
136
+ return (rtkBootstrapPromise ??= doRtkBootstrap());
137
+ }
163
138
  // `gsd update` — update to the latest version via npm
164
139
  if (cliFlags.messages[0] === 'update') {
165
140
  const { runUpdate } = await import('./update-cmd.js');
@@ -171,21 +146,7 @@ exitIfManagedResourcesAreNewer(agentDir);
171
146
  // handles that prevent process.exit() from completing promptly.
172
147
  const hasSubcommand = cliFlags.messages.length > 0;
173
148
  if (!process.stdin.isTTY && !isPrintMode && !hasSubcommand && !cliFlags.listModels && !cliFlags.web) {
174
- process.stderr.write('[gsd] Error: Interactive mode requires a terminal (TTY).\n');
175
- process.stderr.write('[gsd] Non-interactive alternatives:\n');
176
- process.stderr.write('[gsd] gsd auto Auto-mode (pipeable, no TUI)\n');
177
- process.stderr.write('[gsd] gsd --print "your message" Single-shot prompt\n');
178
- process.stderr.write('[gsd] gsd --mode rpc JSON-RPC over stdin/stdout\n');
179
- process.stderr.write('[gsd] gsd --mode mcp MCP server over stdin/stdout\n');
180
- process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n');
181
- process.exit(1);
182
- }
183
- // `gsd <subcommand> --help` — show subcommand-specific help
184
- const subcommand = cliFlags.messages[0];
185
- if (subcommand && process.argv.includes('--help')) {
186
- if (printSubcommandHelp(subcommand, process.env.GSD_VERSION || '0.0.0')) {
187
- process.exit(0);
188
- }
149
+ printNonTtyErrorAndExit(undefined, false);
189
150
  }
190
151
  const packageCommand = await runPackageCommand({
191
152
  appName: 'gsd',
@@ -208,8 +169,7 @@ if (cliFlags.messages[0] === 'config') {
208
169
  }
209
170
  // `gsd web stop [path|all]` — stop web server before anything else
210
171
  if (cliFlags.messages[0] === 'web' && cliFlags.messages[1] === 'stop') {
211
- const webFlags = parseWebCliArgs(process.argv);
212
- const webBranch = await runWebCliBranch(webFlags, {
172
+ const webBranch = await runWebCliBranch(cliFlags, {
213
173
  stopWebMode,
214
174
  stderr: process.stderr,
215
175
  baseSessionsDir: sessionsDir,
@@ -222,8 +182,7 @@ if (cliFlags.messages[0] === 'web' && cliFlags.messages[1] === 'stop') {
222
182
  // `gsd --web [path]` or `gsd web [start] [path]` — launch browser-only web mode
223
183
  if (cliFlags.web || (cliFlags.messages[0] === 'web' && cliFlags.messages[1] !== 'stop')) {
224
184
  await ensureRtkBootstrap();
225
- const webFlags = parseWebCliArgs(process.argv);
226
- const webBranch = await runWebCliBranch(webFlags, {
185
+ const webBranch = await runWebCliBranch(cliFlags, {
227
186
  stderr: process.stderr,
228
187
  baseSessionsDir: sessionsDir,
229
188
  agentDir,
@@ -298,21 +257,23 @@ if (cliFlags.messages[0] === 'headless') {
298
257
  await runHeadless(parseHeadlessArgs(process.argv));
299
258
  process.exit(0);
300
259
  }
260
+ /**
261
+ * Run a headless command by invoking the headless entrypoint with a synthetic
262
+ * argv. Shared by the `auto` shorthand (#2732) and the auto-piped-stdout
263
+ * redirect so they use the same bootstrap + dynamic-import dance.
264
+ */
265
+ async function runHeadlessFromAuto(headlessArgs) {
266
+ await ensureRtkBootstrap();
267
+ const { runHeadless, parseHeadlessArgs } = await import('./headless.js');
268
+ const argv = [process.argv[0], process.argv[1], 'headless', ...headlessArgs];
269
+ await runHeadless(parseHeadlessArgs(argv));
270
+ process.exit(0);
271
+ }
301
272
  // `gsd auto [args...]` — shorthand for `gsd headless auto [args...]` (#2732)
302
273
  // Without this, `gsd auto` falls through to the interactive TUI which hangs
303
274
  // when stdin/stdout are piped (non-TTY environments).
304
275
  if (cliFlags.messages[0] === 'auto') {
305
- await ensureRtkBootstrap();
306
- const { runHeadless, parseHeadlessArgs } = await import('./headless.js');
307
- // Rewrite argv so parseHeadlessArgs sees: [node, gsd, headless, auto, ...rest]
308
- const rewrittenArgv = [
309
- process.argv[0],
310
- process.argv[1],
311
- 'headless',
312
- ...cliFlags.messages, // ['auto', ...extra args]
313
- ];
314
- await runHeadless(parseHeadlessArgs(rewrittenArgv));
315
- process.exit(0);
276
+ await runHeadlessFromAuto(cliFlags.messages);
316
277
  }
317
278
  // Pi's tool bootstrap can mis-detect already-installed fd/rg on some systems
318
279
  // because spawnSync(..., ["--version"]) returns EPERM despite a zero exit code.
@@ -455,64 +416,12 @@ if (isPrintMode) {
455
416
  isClaudeCodeReady: () => modelRegistry.isProviderRequestReady('claude-code'),
456
417
  });
457
418
  markStartup('createAgentSession');
458
- // Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
459
- // Anthropic blocks third-party apps from using subscription quotas — routing through
460
- // the local claude CLI binary is TOS-compliant.
461
- if (shouldMigrateAnthropicToClaudeCode({
462
- authStorage,
463
- isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
464
- defaultProvider: settingsManager.getDefaultProvider(),
465
- })) {
466
- const currentModelId = settingsManager.getDefaultModel();
467
- if (currentModelId) {
468
- const ccModel = modelRegistry.find('claude-code', currentModelId);
469
- if (ccModel) {
470
- try {
471
- await session.setModel(ccModel);
472
- // Only persist after successful session switch to avoid desync
473
- settingsManager.setDefaultModelAndProvider('claude-code', currentModelId);
474
- }
475
- catch {
476
- // claude-code provider not ready — leave both session and settings unchanged
477
- }
478
- }
479
- }
480
- }
481
419
  // Validate configured model AFTER extensions have registered their models (#2626).
482
420
  // Before this, extension-provided models (e.g. claude-code/*) were not yet in the
483
421
  // registry, causing the user's valid choice to be silently overwritten.
484
422
  validateConfiguredModel(modelRegistry, settingsManager);
485
- // Re-apply the validated model to the session only when findInitialModel() used a
486
- // fallback (not when restoring an existing session's model). This prevents silently
487
- // overriding the persisted model of resumed conversations (#3534).
488
- if (modelFallbackMessage) {
489
- const validatedProvider = settingsManager.getDefaultProvider();
490
- const validatedModelId = settingsManager.getDefaultModel();
491
- if (validatedProvider && validatedModelId) {
492
- const correctModel = modelRegistry.getAvailable()
493
- .find((m) => m.provider === validatedProvider && m.id === validatedModelId);
494
- if (correctModel) {
495
- try {
496
- await session.setModel(correctModel);
497
- }
498
- catch {
499
- // Provider not ready — leave session on its current model
500
- }
501
- }
502
- }
503
- }
504
- if (extensionsResult.errors.length > 0) {
505
- for (const err of extensionsResult.errors) {
506
- // Downgrade conflicts with built-in tools to warnings (#1347)
507
- const isConflict = err.error.includes("supersedes") || err.error.includes("conflicts with");
508
- const prefix = isConflict ? "Extension conflict" : "Extension load error";
509
- process.stderr.write(`[gsd] ${prefix}: ${err.error}\n`);
510
- }
511
- }
512
- // Validate configured model now that extension providers are registered.
513
- // Must run after createAgentSession() which flushes pendingProviderRegistrations
514
- // so extension models (e.g. pi-claude-cli) are visible in the registry.
515
- validateConfiguredModel(modelRegistry, settingsManager);
423
+ await reapplyValidatedModelOnFallback(session, modelRegistry, settingsManager, modelFallbackMessage);
424
+ printExtensionErrors(extensionsResult.errors);
516
425
  // Apply --model override if specified
517
426
  if (cliFlags.model) {
518
427
  const available = modelRegistry.getAvailable();
@@ -603,11 +512,8 @@ if (!cliFlags.worktree && !isPrintMode) {
603
512
  // which handles non-interactive output gracefully.
604
513
  // ---------------------------------------------------------------------------
605
514
  if (cliFlags.messages[0] === 'auto' && !process.stdout.isTTY) {
606
- await ensureRtkBootstrap();
607
- const { runHeadless, parseHeadlessArgs } = await import('./headless.js');
608
515
  process.stderr.write('[gsd] stdout is not a terminal — running auto-mode in headless mode.\n');
609
- await runHeadless(parseHeadlessArgs(['node', 'gsd', 'headless', ...cliFlags.messages.slice(1)]));
610
- process.exit(0);
516
+ await runHeadlessFromAuto(cliFlags.messages.slice(1));
611
517
  }
612
518
  // ---------------------------------------------------------------------------
613
519
  // Interactive mode — normal TTY session
@@ -647,63 +553,12 @@ const { session, extensionsResult, modelFallbackMessage: interactiveFallbackMsg
647
553
  isClaudeCodeReady: () => modelRegistry.isProviderRequestReady('claude-code'),
648
554
  });
649
555
  markStartup('createAgentSession');
650
- // Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
651
- // Anthropic blocks third-party apps from using subscription quotas — routing through
652
- // the local claude CLI binary is TOS-compliant.
653
- if (shouldMigrateAnthropicToClaudeCode({
654
- authStorage,
655
- isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
656
- defaultProvider: settingsManager.getDefaultProvider(),
657
- })) {
658
- const currentModelId = settingsManager.getDefaultModel();
659
- if (currentModelId) {
660
- const ccModel = modelRegistry.find('claude-code', currentModelId);
661
- if (ccModel) {
662
- try {
663
- await session.setModel(ccModel);
664
- // Only persist after successful session switch to avoid desync
665
- settingsManager.setDefaultModelAndProvider('claude-code', currentModelId);
666
- }
667
- catch {
668
- // claude-code provider not ready — leave both session and settings unchanged
669
- }
670
- }
671
- }
672
- }
673
556
  // Validate configured model AFTER extensions have registered their models (#2626).
674
557
  // Before this, extension-provided models (e.g. claude-code/*) were not yet in the
675
558
  // registry, causing the user's valid choice to be silently overwritten.
676
559
  validateConfiguredModel(modelRegistry, settingsManager);
677
- // Re-apply the validated model to the session only when findInitialModel() used a
678
- // fallback (not when restoring an existing session's model). This prevents silently
679
- // overriding the persisted model of resumed conversations (#3534).
680
- if (interactiveFallbackMsg) {
681
- const validatedProvider = settingsManager.getDefaultProvider();
682
- const validatedModelId = settingsManager.getDefaultModel();
683
- if (validatedProvider && validatedModelId) {
684
- const correctModel = modelRegistry.getAvailable()
685
- .find((m) => m.provider === validatedProvider && m.id === validatedModelId);
686
- if (correctModel) {
687
- try {
688
- await session.setModel(correctModel);
689
- }
690
- catch {
691
- // Provider not ready — leave session on its current model
692
- }
693
- }
694
- }
695
- }
696
- if (extensionsResult.errors.length > 0) {
697
- for (const err of extensionsResult.errors) {
698
- const isConflict = err.error.includes("supersedes") || err.error.includes("conflicts with");
699
- const prefix = isConflict ? "Extension conflict" : "Extension load error";
700
- process.stderr.write(`[gsd] ${prefix}: ${err.error}\n`);
701
- }
702
- }
703
- // Validate configured model now that extension providers are registered.
704
- // Must run after createAgentSession() which flushes pendingProviderRegistrations
705
- // so extension models (e.g. pi-claude-cli) are visible in the registry.
706
- validateConfiguredModel(modelRegistry, settingsManager);
560
+ await reapplyValidatedModelOnFallback(session, modelRegistry, settingsManager, interactiveFallbackMsg);
561
+ printExtensionErrors(extensionsResult.errors);
707
562
  // Restore scoped models from settings on startup.
708
563
  // The upstream InteractiveMode reads enabledModels from settings when /scoped-models is opened,
709
564
  // but doesn't apply them to the session at startup — so Ctrl+P cycles all models instead of
@@ -751,16 +606,7 @@ if (!process.stdin.isTTY || !process.stdout.isTTY) {
751
606
  : !process.stdin.isTTY
752
607
  ? 'stdin is'
753
608
  : 'stdout is';
754
- process.stderr.write(`[gsd] Error: Interactive mode requires a terminal (TTY) but ${missing} not a TTY.\n`);
755
- process.stderr.write('[gsd] Non-interactive alternatives:\n');
756
- process.stderr.write('[gsd] gsd auto Auto-mode (pipeable, no TUI)\n');
757
- process.stderr.write('[gsd] gsd --print "your message" Single-shot prompt\n');
758
- process.stderr.write('[gsd] gsd --web [path] Browser-only web mode\n');
759
- process.stderr.write('[gsd] gsd --mode rpc JSON-RPC over stdin/stdout\n');
760
- process.stderr.write('[gsd] gsd --mode mcp MCP server over stdin/stdout\n');
761
- process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n');
762
- process.stderr.write('[gsd] gsd headless Auto-mode without TUI\n');
763
- process.exit(1);
609
+ printNonTtyErrorAndExit(missing, true);
764
610
  }
765
611
  // Welcome screen — shown on every fresh interactive session before TUI takes over.
766
612
  // Skip when the first-run banner was already printed in loader.ts (prevents double banner).
package/dist/help-text.js CHANGED
@@ -148,7 +148,7 @@ export function printHelp(version) {
148
148
  process.stdout.write(' --print, -p Single-shot print mode\n');
149
149
  process.stdout.write(' --continue, -c Resume the most recent session\n');
150
150
  process.stdout.write(' --worktree, -w [name] Start in an isolated worktree (auto-named if omitted)\n');
151
- process.stdout.write(' --model <id> Override model (e.g. claude-opus-4-6)\n');
151
+ process.stdout.write(' --model <id> Override model (e.g. provider/model-id)\n');
152
152
  process.stdout.write(' --no-session Disable session persistence\n');
153
153
  process.stdout.write(' --extension <path> Load additional extension\n');
154
154
  process.stdout.write(' --tools <a,b,c> Restrict available tools\n');
package/dist/logo.d.ts CHANGED
@@ -10,7 +10,7 @@ export declare const GSD_LOGO: readonly string[];
10
10
  /**
11
11
  * Render the logo block with a color function applied to each line.
12
12
  *
13
- * @param color — e.g. `(s) => `\x1b[36m${s}\x1b[0m`` or picocolors.cyan
13
+ * @param color — e.g. `(s) => `\x1b[36m${s}\x1b[0m`` or chalk.cyan
14
14
  * @returns Ready-to-write string with leading/trailing newlines.
15
15
  */
16
16
  export declare function renderLogo(color: (s: string) => string): string;
package/dist/logo.js CHANGED
@@ -17,7 +17,7 @@ export const GSD_LOGO = [
17
17
  /**
18
18
  * Render the logo block with a color function applied to each line.
19
19
  *
20
- * @param color — e.g. `(s) => `\x1b[36m${s}\x1b[0m`` or picocolors.cyan
20
+ * @param color — e.g. `(s) => `\x1b[36m${s}\x1b[0m`` or chalk.cyan
21
21
  * @returns Ready-to-write string with leading/trailing newlines.
22
22
  */
23
23
  export function renderLogo(color) {