gsd-pi 2.44.0 → 2.45.0-dev.6b9da3e

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 (463) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  3. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  4. package/dist/resources/extensions/gsd/auto/phases.js +37 -36
  5. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  6. package/dist/resources/extensions/gsd/auto-start.js +31 -2
  7. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  8. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  9. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  10. package/dist/resources/extensions/gsd/auto.js +30 -3
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  12. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  13. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  14. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  16. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  17. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  18. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  19. package/dist/resources/extensions/gsd/doctor.js +8 -0
  20. package/dist/resources/extensions/gsd/git-service.js +8 -3
  21. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  22. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  23. package/dist/resources/extensions/gsd/preferences.js +9 -1
  24. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  25. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  26. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  27. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  28. package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
  29. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  30. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  31. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  32. package/dist/resources/extensions/gsd/rethink.js +115 -0
  33. package/dist/resources/extensions/gsd/state.js +41 -3
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  36. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  37. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +32 -2
  39. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  40. package/dist/resources/extensions/mcp-client/index.js +14 -0
  41. package/dist/web/standalone/.next/BUILD_ID +1 -1
  42. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  43. package/dist/web/standalone/.next/build-manifest.json +4 -4
  44. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  45. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  46. package/dist/web/standalone/.next/required-server-files.json +3 -3
  47. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  48. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  50. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  58. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.rsc +5 -5
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  67. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  74. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  112. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  118. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  134. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  136. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  138. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +6 -6
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +6 -6
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  154. package/dist/web/standalone/.next/server/app/page.js +2 -2
  155. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  157. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  158. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  159. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/middleware.js +2 -2
  162. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  164. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  165. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  166. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  167. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  168. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  169. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  170. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  171. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  172. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  173. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  174. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  175. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  176. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  177. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  178. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  179. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  180. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  181. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  182. package/dist/web/standalone/server.js +1 -1
  183. package/package.json +1 -1
  184. package/packages/native/dist/stream-process/index.js +2 -2
  185. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  186. package/packages/native/src/stream-process/index.ts +2 -2
  187. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  188. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  190. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  192. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  194. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  196. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  198. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  199. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  200. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  201. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  202. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  203. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  204. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  206. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  208. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  210. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  212. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  214. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/main.js +17 -0
  217. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  236. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  238. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  240. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  243. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  246. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  248. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  249. package/packages/pi-coding-agent/package.json +1 -1
  250. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  251. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  252. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  253. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  254. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  255. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  256. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  257. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  258. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  259. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  260. package/packages/pi-coding-agent/src/main.ts +19 -0
  261. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  263. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  266. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  268. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  269. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  270. package/pkg/package.json +1 -1
  271. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  272. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  273. package/src/resources/extensions/gsd/auto/phases.ts +46 -48
  274. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  275. package/src/resources/extensions/gsd/auto-start.ts +39 -2
  276. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  277. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  278. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  279. package/src/resources/extensions/gsd/auto.ts +37 -3
  280. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  281. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  282. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  283. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  284. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  285. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  286. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  287. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  288. package/src/resources/extensions/gsd/doctor.ts +7 -1
  289. package/src/resources/extensions/gsd/git-service.ts +6 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  291. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  292. package/src/resources/extensions/gsd/preferences.ts +11 -1
  293. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  294. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  295. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  296. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  297. package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
  298. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  299. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  300. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  301. package/src/resources/extensions/gsd/rethink.ts +154 -0
  302. package/src/resources/extensions/gsd/state.ts +41 -1
  303. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  304. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  305. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  306. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  307. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  308. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  309. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  310. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  311. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  312. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  313. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  314. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  315. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  316. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  317. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  318. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  319. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  320. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  321. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  322. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  323. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  324. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  325. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  326. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  327. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  328. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  329. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  330. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  331. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  332. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  333. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  334. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  335. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  336. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  337. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  338. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  339. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  340. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  341. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  342. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  343. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  344. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  345. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  346. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  347. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  348. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  349. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  350. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  351. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  352. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  353. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  354. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  355. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  356. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  357. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  358. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  359. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  360. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  361. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  362. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  363. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  364. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  365. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  366. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  367. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  368. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  369. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  370. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  371. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  372. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  373. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  374. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  375. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  376. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  377. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  378. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  379. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  380. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  381. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  382. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  383. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  384. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  385. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  386. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  387. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  388. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  389. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  390. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  391. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  392. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  393. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  394. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  395. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  396. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  397. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  398. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  399. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  400. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  401. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  402. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  403. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  404. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  405. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  406. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  407. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  408. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  409. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  410. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  411. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  412. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  413. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  414. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  415. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  416. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  417. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  418. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  419. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  420. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  421. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  422. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  423. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  424. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  425. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  426. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  427. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  428. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  429. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  430. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  431. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  432. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  433. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  434. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  435. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  436. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  437. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  438. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  439. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  440. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  441. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  442. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  443. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  444. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  445. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  446. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  447. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  448. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  449. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  450. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  451. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  452. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  453. package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
  454. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  455. package/src/resources/extensions/mcp-client/index.ts +20 -0
  456. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  457. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +0 -1
  458. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  459. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  461. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  462. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_ssgManifest.js +0 -0
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
2
+ import { describe, it, afterEach } from "node:test";
3
3
  import { mkdtempSync, rmSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
@@ -22,44 +22,44 @@ function makeAssistantMessage(input: number, output: number, cacheRead = 0, cach
22
22
  }
23
23
 
24
24
  describe("SessionManager usage totals", () => {
25
- it("tracks assistant usage incrementally without rescanning entries", () => {
26
- const dir = mkdtempSync(join(tmpdir(), "gsd-session-manager-test-"));
27
- try {
28
- const manager = SessionManager.create(dir, dir);
29
-
30
- manager.appendMessage({ role: "user", content: [{ type: "text", text: "hello" }] } as any);
31
- manager.appendMessage(makeAssistantMessage(10, 5, 3, 2, 0.25));
32
- manager.appendMessage(makeAssistantMessage(7, 4, 1, 0, 0.1));
25
+ let dir: string;
33
26
 
34
- assert.deepEqual(manager.getUsageTotals(), {
35
- input: 17,
36
- output: 9,
37
- cacheRead: 4,
38
- cacheWrite: 2,
39
- cost: 0.35,
40
- });
41
- } finally {
27
+ afterEach(() => {
28
+ if (dir) {
42
29
  rmSync(dir, { recursive: true, force: true });
43
30
  }
44
31
  });
45
32
 
33
+ it("tracks assistant usage incrementally without rescanning entries", () => {
34
+ dir = mkdtempSync(join(tmpdir(), "gsd-session-manager-test-"));
35
+ const manager = SessionManager.create(dir, dir);
36
+
37
+ manager.appendMessage({ role: "user", content: [{ type: "text", text: "hello" }] } as any);
38
+ manager.appendMessage(makeAssistantMessage(10, 5, 3, 2, 0.25));
39
+ manager.appendMessage(makeAssistantMessage(7, 4, 1, 0, 0.1));
40
+
41
+ assert.deepEqual(manager.getUsageTotals(), {
42
+ input: 17,
43
+ output: 9,
44
+ cacheRead: 4,
45
+ cacheWrite: 2,
46
+ cost: 0.35,
47
+ });
48
+ });
49
+
46
50
  it("resets totals when starting a new session", () => {
47
- const dir = mkdtempSync(join(tmpdir(), "gsd-session-manager-test-"));
48
- try {
49
- const manager = SessionManager.create(dir, dir);
50
- manager.appendMessage(makeAssistantMessage(5, 5, 0, 0, 0.05));
51
- assert.equal(manager.getUsageTotals().input, 5);
51
+ dir = mkdtempSync(join(tmpdir(), "gsd-session-manager-test-"));
52
+ const manager = SessionManager.create(dir, dir);
53
+ manager.appendMessage(makeAssistantMessage(5, 5, 0, 0, 0.05));
54
+ assert.equal(manager.getUsageTotals().input, 5);
52
55
 
53
- manager.newSession();
54
- assert.deepEqual(manager.getUsageTotals(), {
55
- input: 0,
56
- output: 0,
57
- cacheRead: 0,
58
- cacheWrite: 0,
59
- cost: 0,
60
- });
61
- } finally {
62
- rmSync(dir, { recursive: true, force: true });
63
- }
56
+ manager.newSession();
57
+ assert.deepEqual(manager.getUsageTotals(), {
58
+ input: 0,
59
+ output: 0,
60
+ cacheRead: 0,
61
+ cacheWrite: 0,
62
+ cost: 0,
63
+ });
64
64
  });
65
65
  });
@@ -151,6 +151,7 @@ export interface Settings {
151
151
  fallback?: FallbackSettings;
152
152
  modelDiscovery?: ModelDiscoverySettings;
153
153
  editMode?: "standard" | "hashline"; // Edit tool mode: "standard" (text match) or "hashline" (LINE#ID anchors). Default: "standard"
154
+ timestampFormat?: "date-time-iso" | "date-time-us"; // Timestamp display format for messages. Default: "date-time-iso"
154
155
  }
155
156
 
156
157
  /** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
@@ -1087,4 +1088,12 @@ export class SettingsManager {
1087
1088
  setEditMode(mode: "standard" | "hashline"): void {
1088
1089
  this.setGlobalSetting("editMode", mode);
1089
1090
  }
1091
+
1092
+ getTimestampFormat(): "date-time-iso" | "date-time-us" {
1093
+ return this.settings.timestampFormat ?? "date-time-iso";
1094
+ }
1095
+
1096
+ setTimestampFormat(format: "date-time-iso" | "date-time-us"): void {
1097
+ this.setGlobalSetting("timestampFormat", format);
1098
+ }
1090
1099
  }
@@ -60,26 +60,26 @@ describe("edit-diff", () => {
60
60
  assert.match(result.diff, /CHANGED/);
61
61
  });
62
62
 
63
- it("computes diffs for preview without native helpers", async () => {
63
+ it("computes diffs for preview without native helpers", async (t) => {
64
64
  const dir = mkdtempSync(join(tmpdir(), "edit-diff-test-"));
65
- try {
66
- const file = join(dir, "sample.ts");
67
- writeFileSync(file, "const title = “Hello”;\n", "utf-8");
65
+ t.after(() => {
66
+ rmSync(dir, { recursive: true, force: true });
67
+ });
68
68
 
69
- const result = await computeEditDiff(
70
- file,
71
- "const title = \"Hello\";\n",
72
- "const title = \"Hi\";\n",
73
- dir,
74
- );
69
+ const file = join(dir, "sample.ts");
70
+ writeFileSync(file, "const title = “Hello”;\n", "utf-8");
75
71
 
76
- assert.ok(!("error" in result), "expected a diff result");
77
- if (!("error" in result)) {
78
- assert.equal(result.firstChangedLine, 1);
79
- assert.match(result.diff, /\+1 const title = "Hi";/);
80
- }
81
- } finally {
82
- rmSync(dir, { recursive: true, force: true });
72
+ const result = await computeEditDiff(
73
+ file,
74
+ "const title = \"Hello\";\n",
75
+ "const title = \"Hi\";\n",
76
+ dir,
77
+ );
78
+
79
+ assert.ok(!("error" in result), "expected a diff result");
80
+ if (!("error" in result)) {
81
+ assert.equal(result.firstChangedLine, 1);
82
+ assert.match(result.diff, /\+1 const title = "Hi";/);
83
83
  }
84
84
  });
85
85
  });
@@ -391,6 +391,25 @@ export async function main(args: string[]) {
391
391
  const authStorage = AuthStorage.create();
392
392
  const modelRegistry = new ModelRegistry(authStorage, getModelsPath());
393
393
 
394
+ // Offline mode validation / auto-detection
395
+ if (offlineMode) {
396
+ // --offline flag: validate all models are local
397
+ if (!modelRegistry.isAllLocalChain()) {
398
+ const remoteModel = modelRegistry.getAll().find((m) => !ModelRegistry.isLocalModel(m));
399
+ if (remoteModel) {
400
+ console.error(
401
+ `Error: --offline requires all configured models to be local. Found remote model: ${remoteModel.name} (${remoteModel.baseUrl || "cloud API"})`,
402
+ );
403
+ process.exit(1);
404
+ }
405
+ }
406
+ } else if (modelRegistry.isAllLocalChain() && modelRegistry.getAll().length > 0) {
407
+ // Auto-detect: all models are local, enable offline mode
408
+ process.env.PI_OFFLINE = "1";
409
+ process.env.PI_SKIP_VERSION_CHECK = "1";
410
+ console.log("[gsd] All configured models are local \u2014 enabling offline mode automatically.");
411
+ }
412
+
394
413
  const resourceLoader = new DefaultResourceLoader({
395
414
  cwd,
396
415
  agentDir,
@@ -0,0 +1,38 @@
1
+ import { test, describe } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { formatTimestamp } from "../timestamp.js";
4
+
5
+ describe("formatTimestamp", () => {
6
+ // Use a fixed local timestamp to avoid timezone issues
7
+ const d = new Date(2026, 2, 24, 10, 34, 0); // Mar 24, 2026 10:34:00 local time
8
+ const ts = d.getTime();
9
+
10
+ test("date-time-iso format (default)", () => {
11
+ assert.equal(formatTimestamp(ts, "date-time-iso"), "2026-03-24 10:34");
12
+ assert.equal(formatTimestamp(ts), "2026-03-24 10:34"); // default
13
+ });
14
+
15
+ test("date-time-us format", () => {
16
+ assert.equal(formatTimestamp(ts, "date-time-us"), "03-24-2026 10:34 AM");
17
+ });
18
+
19
+ test("US format handles PM correctly", () => {
20
+ const pm = new Date(2026, 2, 24, 14, 5, 0).getTime();
21
+ assert.equal(formatTimestamp(pm, "date-time-us"), "03-24-2026 2:05 PM");
22
+ });
23
+
24
+ test("US format handles noon as 12 PM", () => {
25
+ const noon = new Date(2026, 2, 24, 12, 0, 0).getTime();
26
+ assert.equal(formatTimestamp(noon, "date-time-us"), "03-24-2026 12:00 PM");
27
+ });
28
+
29
+ test("US format handles midnight as 12 AM", () => {
30
+ const midnight = new Date(2026, 2, 24, 0, 0, 0).getTime();
31
+ assert.equal(formatTimestamp(midnight, "date-time-us"), "03-24-2026 12:00 AM");
32
+ });
33
+
34
+ test("ISO format pads single digit months and days", () => {
35
+ const jan1 = new Date(2026, 0, 1, 9, 5, 0).getTime();
36
+ assert.equal(formatTimestamp(jan1, "date-time-iso"), "2026-01-01 09:05");
37
+ });
38
+ });
@@ -1,6 +1,7 @@
1
1
  import type { AssistantMessage } from "@gsd/pi-ai";
2
2
  import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
3
3
  import { getMarkdownTheme, theme } from "../theme/theme.js";
4
+ import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
4
5
 
5
6
  /**
6
7
  * Component that renders a complete assistant message
@@ -10,16 +11,19 @@ export class AssistantMessageComponent extends Container {
10
11
  private hideThinkingBlock: boolean;
11
12
  private markdownTheme: MarkdownTheme;
12
13
  private lastMessage?: AssistantMessage;
14
+ private timestampFormat: TimestampFormat;
13
15
 
14
16
  constructor(
15
17
  message?: AssistantMessage,
16
18
  hideThinkingBlock = false,
17
19
  markdownTheme: MarkdownTheme = getMarkdownTheme(),
20
+ timestampFormat: TimestampFormat = "date-time-iso",
18
21
  ) {
19
22
  super();
20
23
 
21
24
  this.hideThinkingBlock = hideThinkingBlock;
22
25
  this.markdownTheme = markdownTheme;
26
+ this.timestampFormat = timestampFormat;
23
27
 
24
28
  // Container for text/thinking content
25
29
  this.contentContainer = new Container();
@@ -111,5 +115,11 @@ export class AssistantMessageComponent extends Container {
111
115
  this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
112
116
  }
113
117
  }
118
+
119
+ // Show timestamp when the message is complete (has a stop reason)
120
+ if (message.stopReason && message.timestamp) {
121
+ const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
122
+ this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
123
+ }
114
124
  }
115
125
  }
@@ -45,6 +45,7 @@ export interface SettingsConfig {
45
45
  respectGitignoreInPicker: boolean;
46
46
  quietStartup: boolean;
47
47
  clearOnShrink: boolean;
48
+ timestampFormat: "date-time-iso" | "date-time-us";
48
49
  }
49
50
 
50
51
  export interface SettingsCallbacks {
@@ -69,6 +70,7 @@ export interface SettingsCallbacks {
69
70
  onRespectGitignoreInPickerChange: (enabled: boolean) => void;
70
71
  onQuietStartupChange: (enabled: boolean) => void;
71
72
  onClearOnShrinkChange: (enabled: boolean) => void;
73
+ onTimestampFormatChange: (format: "date-time-iso" | "date-time-us") => void;
72
74
  onCancel: () => void;
73
75
  }
74
76
 
@@ -355,6 +357,16 @@ export class SettingsSelectorComponent extends Container {
355
357
  values: ["true", "false"],
356
358
  });
357
359
 
360
+ // Timestamp format (insert after respect-gitignore-in-picker)
361
+ const gitignoreIndex = items.findIndex((item) => item.id === "respect-gitignore-in-picker");
362
+ items.splice(gitignoreIndex + 1, 0, {
363
+ id: "timestamp-format",
364
+ label: "Timestamp format",
365
+ description: "Date/time format for message timestamps",
366
+ currentValue: config.timestampFormat,
367
+ values: ["date-time-iso", "date-time-us"],
368
+ });
369
+
358
370
  // Add borders
359
371
  this.addChild(new DynamicBorder());
360
372
 
@@ -420,6 +432,9 @@ export class SettingsSelectorComponent extends Container {
420
432
  case "respect-gitignore-in-picker":
421
433
  callbacks.onRespectGitignoreInPickerChange(newValue === "true");
422
434
  break;
435
+ case "timestamp-format":
436
+ callbacks.onTimestampFormatChange(newValue as "date-time-iso" | "date-time-us");
437
+ break;
423
438
  }
424
439
  },
425
440
  callbacks.onCancel,
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Timestamp formatting for message display.
3
+ *
4
+ * Formats:
5
+ * - "time-date-iso": 10:34 2025-03-24 (default)
6
+ * - "date-time-iso": 2025-03-24 10:34
7
+ * - "time-date-us": 10:34 AM 03/24/2025
8
+ * - "date-time-us": 03/24/2025 10:34 AM
9
+ */
10
+
11
+ export type TimestampFormat = "date-time-iso" | "date-time-us";
12
+
13
+ function pad2(n: number): string {
14
+ return n.toString().padStart(2, "0");
15
+ }
16
+
17
+ function isoDate(d: Date): string {
18
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
19
+ }
20
+
21
+ function isoTime(d: Date): string {
22
+ return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
23
+ }
24
+
25
+ function usDate(d: Date): string {
26
+ return `${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}-${d.getFullYear()}`;
27
+ }
28
+
29
+ function usTime(d: Date): string {
30
+ const hours = d.getHours();
31
+ const period = hours >= 12 ? "PM" : "AM";
32
+ const h = hours % 12 || 12;
33
+ return `${h}:${pad2(d.getMinutes())} ${period}`;
34
+ }
35
+
36
+ /**
37
+ * Format a timestamp for message display using the specified format.
38
+ */
39
+ export function formatTimestamp(timestamp: number, format: TimestampFormat = "date-time-iso"): string {
40
+ const d = new Date(timestamp);
41
+
42
+ switch (format) {
43
+ case "date-time-iso":
44
+ return `${isoDate(d)} ${isoTime(d)}`;
45
+ case "date-time-us":
46
+ return `${usDate(d)} ${usTime(d)}`;
47
+ }
48
+ }
@@ -895,7 +895,9 @@ export class ToolExecutionComponent extends Container {
895
895
  // Server-side Anthropic web search
896
896
  text = theme.fg("toolTitle", theme.bold("web search"));
897
897
 
898
- if (this.result) {
898
+ if (process.env.PI_OFFLINE === "1") {
899
+ text += "\n\n" + theme.fg("muted", "\u{1F50C} Offline \u{2014} web search unavailable");
900
+ } else if (this.result) {
899
901
  const output = this.getTextOutput().trim();
900
902
  if (output) {
901
903
  const lines = output.split("\n");
@@ -1,15 +1,21 @@
1
- import { Container, Markdown, type MarkdownTheme, Spacer } from "@gsd/pi-tui";
1
+ import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
2
2
  import { getMarkdownTheme, theme } from "../theme/theme.js";
3
+ import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
3
4
 
4
5
  const OSC133_ZONE_START = "\x1b]133;A\x07";
5
6
  const OSC133_ZONE_END = "\x1b]133;B\x07";
6
7
 
7
8
  /**
8
- * Component that renders a user message
9
+ * Component that renders a user message with a right-aligned timestamp.
9
10
  */
10
11
  export class UserMessageComponent extends Container {
11
- constructor(text: string, markdownTheme: MarkdownTheme = getMarkdownTheme()) {
12
+ private timestamp: number | undefined;
13
+ private timestampFormat: TimestampFormat;
14
+
15
+ constructor(text: string, markdownTheme: MarkdownTheme = getMarkdownTheme(), timestamp?: number, timestampFormat: TimestampFormat = "date-time-iso") {
12
16
  super();
17
+ this.timestamp = timestamp;
18
+ this.timestampFormat = timestampFormat;
13
19
  this.addChild(new Spacer(1));
14
20
  this.addChild(
15
21
  new Markdown(text, 1, 1, markdownTheme, {
@@ -25,6 +31,15 @@ export class UserMessageComponent extends Container {
25
31
  return lines;
26
32
  }
27
33
 
34
+ // Insert right-aligned timestamp above the message content
35
+ if (this.timestamp) {
36
+ const timeStr = formatTimestamp(this.timestamp, this.timestampFormat);
37
+ const label = theme.fg("dim", timeStr);
38
+ const padding = Math.max(0, width - timeStr.length - 1);
39
+ const timestampLine = " ".repeat(padding) + label;
40
+ lines.splice(0, 0, timestampLine);
41
+ }
42
+
28
43
  lines[0] = OSC133_ZONE_START + lines[0];
29
44
  lines[lines.length - 1] = lines[lines.length - 1] + OSC133_ZONE_END;
30
45
  return lines;
@@ -100,6 +100,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
100
100
  undefined,
101
101
  host.hideThinkingBlock,
102
102
  host.getMarkdownThemeWithSettings(),
103
+ host.settingsManager.getTimestampFormat(),
103
104
  );
104
105
  host.streamingMessage = event.message;
105
106
  host.chatContainer.addChild(host.streamingComponent);
@@ -144,13 +145,21 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
144
145
  } else if (content.type === "webSearchResult") {
145
146
  const component = host.pendingTools.get(content.toolUseId);
146
147
  if (component) {
147
- const searchContent = content.content;
148
- const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
149
- component.updateResult({
150
- content: [{ type: "text", text: host.formatWebSearchResult(searchContent) }],
151
- isError: !!isError,
152
- });
153
- host.pendingTools.delete(content.toolUseId);
148
+ if (process.env.PI_OFFLINE === "1") {
149
+ component.updateResult({
150
+ content: [{ type: "text", text: "Web search disabled (offline mode)" }],
151
+ isError: false,
152
+ });
153
+ host.pendingTools.delete(content.toolUseId);
154
+ } else {
155
+ const searchContent = content.content;
156
+ const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
157
+ component.updateResult({
158
+ content: [{ type: "text", text: host.formatWebSearchResult(searchContent) }],
159
+ isError: !!isError,
160
+ });
161
+ host.pendingTools.delete(content.toolUseId);
162
+ }
154
163
  }
155
164
  }
156
165
  }
@@ -2099,11 +2099,13 @@ export class InteractiveMode {
2099
2099
  const userComponent = new UserMessageComponent(
2100
2100
  skillBlock.userMessage,
2101
2101
  this.getMarkdownThemeWithSettings(),
2102
+ message.timestamp,
2103
+ this.settingsManager.getTimestampFormat(),
2102
2104
  );
2103
2105
  this.chatContainer.addChild(userComponent);
2104
2106
  }
2105
2107
  } else {
2106
- const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings());
2108
+ const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), message.timestamp, this.settingsManager.getTimestampFormat());
2107
2109
  this.chatContainer.addChild(userComponent);
2108
2110
  }
2109
2111
  if (options?.populateHistory) {
@@ -2117,6 +2119,7 @@ export class InteractiveMode {
2117
2119
  message,
2118
2120
  this.hideThinkingBlock,
2119
2121
  this.getMarkdownThemeWithSettings(),
2122
+ this.settingsManager.getTimestampFormat(),
2120
2123
  );
2121
2124
  this.chatContainer.addChild(assistantComponent);
2122
2125
  break;
@@ -2795,6 +2798,7 @@ export class InteractiveMode {
2795
2798
  respectGitignoreInPicker: this.settingsManager.getRespectGitignoreInPicker(),
2796
2799
  quietStartup: this.settingsManager.getQuietStartup(),
2797
2800
  clearOnShrink: this.settingsManager.getClearOnShrink(),
2801
+ timestampFormat: this.settingsManager.getTimestampFormat(),
2798
2802
  },
2799
2803
  {
2800
2804
  onAutoCompactChange: (enabled) => {
@@ -2898,6 +2902,9 @@ export class InteractiveMode {
2898
2902
  this.settingsManager.setRespectGitignoreInPicker(enabled);
2899
2903
  this.autocompleteProvider?.setRespectGitignore(enabled);
2900
2904
  },
2905
+ onTimestampFormatChange: (format) => {
2906
+ this.settingsManager.setTimestampFormat(format);
2907
+ },
2901
2908
  onCancel: () => {
2902
2909
  done();
2903
2910
  this.ui.requestRender();
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { describe, it, mock } from "node:test";
2
+ import { describe, it, afterEach } from "node:test";
3
3
  import { mkdtempSync, rmSync, readFileSync, existsSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
@@ -15,84 +15,84 @@ function wait(ms: number): Promise<void> {
15
15
  }
16
16
 
17
17
  describe("MemoryStorage debounced persistence", () => {
18
- it("multiple rapid mutations only trigger one persist write", async () => {
19
- const dir = makeTmpDir();
20
- const dbPath = join(dir, "test.db");
21
- try {
22
- const storage = await MemoryStorage.create(dbPath);
23
-
24
- const initialStat = readFileSync(dbPath);
25
- const initialMtime = initialStat.length;
26
-
27
- storage.upsertThreads([
28
- { threadId: "t1", filePath: "/a.txt", fileSize: 100, fileMtime: 1000, cwd: "/proj" },
29
- ]);
30
- storage.upsertThreads([
31
- { threadId: "t2", filePath: "/b.txt", fileSize: 200, fileMtime: 2000, cwd: "/proj" },
32
- ]);
33
- storage.upsertThreads([
34
- { threadId: "t3", filePath: "/c.txt", fileSize: 300, fileMtime: 3000, cwd: "/proj" },
35
- ]);
36
-
37
- const afterMutationsBuf = readFileSync(dbPath);
38
- assert.deepEqual(
39
- afterMutationsBuf,
40
- initialStat,
41
- "File should not have been written yet (debounce window has not elapsed)",
42
- );
43
-
44
- await wait(700);
45
-
46
- const afterDebounceBuf = readFileSync(dbPath);
47
- assert.notDeepEqual(
48
- afterDebounceBuf,
49
- initialStat,
50
- "File should have been written after debounce window elapsed",
51
- );
52
-
53
- const stats = storage.getStats();
54
- assert.equal(stats.totalThreads, 3);
55
-
56
- storage.close();
57
- } finally {
18
+ let dir: string;
19
+
20
+ afterEach(() => {
21
+ if (dir) {
58
22
  rmSync(dir, { recursive: true, force: true });
59
23
  }
60
24
  });
61
25
 
26
+ it("multiple rapid mutations only trigger one persist write", async () => {
27
+ dir = makeTmpDir();
28
+ const dbPath = join(dir, "test.db");
29
+ const storage = await MemoryStorage.create(dbPath);
30
+
31
+ const initialStat = readFileSync(dbPath);
32
+ const initialMtime = initialStat.length;
33
+
34
+ storage.upsertThreads([
35
+ { threadId: "t1", filePath: "/a.txt", fileSize: 100, fileMtime: 1000, cwd: "/proj" },
36
+ ]);
37
+ storage.upsertThreads([
38
+ { threadId: "t2", filePath: "/b.txt", fileSize: 200, fileMtime: 2000, cwd: "/proj" },
39
+ ]);
40
+ storage.upsertThreads([
41
+ { threadId: "t3", filePath: "/c.txt", fileSize: 300, fileMtime: 3000, cwd: "/proj" },
42
+ ]);
43
+
44
+ const afterMutationsBuf = readFileSync(dbPath);
45
+ assert.deepEqual(
46
+ afterMutationsBuf,
47
+ initialStat,
48
+ "File should not have been written yet (debounce window has not elapsed)",
49
+ );
50
+
51
+ await wait(700);
52
+
53
+ const afterDebounceBuf = readFileSync(dbPath);
54
+ assert.notDeepEqual(
55
+ afterDebounceBuf,
56
+ initialStat,
57
+ "File should have been written after debounce window elapsed",
58
+ );
59
+
60
+ const stats = storage.getStats();
61
+ assert.equal(stats.totalThreads, 3);
62
+
63
+ storage.close();
64
+ });
65
+
62
66
  it("close() flushes pending changes immediately without waiting for debounce", async () => {
63
- const dir = makeTmpDir();
67
+ dir = makeTmpDir();
64
68
  const dbPath = join(dir, "test.db");
65
- try {
66
- const storage = await MemoryStorage.create(dbPath);
67
-
68
- const initialBuf = readFileSync(dbPath);
69
-
70
- storage.upsertThreads([
71
- { threadId: "t1", filePath: "/a.txt", fileSize: 100, fileMtime: 1000, cwd: "/proj" },
72
- ]);
73
-
74
- const beforeCloseBuf = readFileSync(dbPath);
75
- assert.deepEqual(
76
- beforeCloseBuf,
77
- initialBuf,
78
- "File should not have been written yet (debounce window has not elapsed)",
79
- );
80
-
81
- storage.close();
82
-
83
- const afterCloseBuf = readFileSync(dbPath);
84
- assert.notDeepEqual(
85
- afterCloseBuf,
86
- initialBuf,
87
- "File should have been written immediately on close()",
88
- );
89
-
90
- const reopened = await MemoryStorage.create(dbPath);
91
- const stats = reopened.getStats();
92
- assert.equal(stats.totalThreads, 1, "Data should be persisted and readable after close");
93
- reopened.close();
94
- } finally {
95
- rmSync(dir, { recursive: true, force: true });
96
- }
69
+ const storage = await MemoryStorage.create(dbPath);
70
+
71
+ const initialBuf = readFileSync(dbPath);
72
+
73
+ storage.upsertThreads([
74
+ { threadId: "t1", filePath: "/a.txt", fileSize: 100, fileMtime: 1000, cwd: "/proj" },
75
+ ]);
76
+
77
+ const beforeCloseBuf = readFileSync(dbPath);
78
+ assert.deepEqual(
79
+ beforeCloseBuf,
80
+ initialBuf,
81
+ "File should not have been written yet (debounce window has not elapsed)",
82
+ );
83
+
84
+ storage.close();
85
+
86
+ const afterCloseBuf = readFileSync(dbPath);
87
+ assert.notDeepEqual(
88
+ afterCloseBuf,
89
+ initialBuf,
90
+ "File should have been written immediately on close()",
91
+ );
92
+
93
+ const reopened = await MemoryStorage.create(dbPath);
94
+ const stats = reopened.getStats();
95
+ assert.equal(stats.totalThreads, 1, "Data should be persisted and readable after close");
96
+ reopened.close();
97
97
  });
98
98
  });
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "2.44.0",
3
+ "version": "2.45.0",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -153,6 +153,7 @@ export function pruneActivityLogs(activityDir: string, retentionDays: number): v
153
153
  const cutoff = Date.now() - retentionDays * 86_400_000;
154
154
  for (const entry of entries) {
155
155
  if (entry.seq === maxSeq) continue; // always preserve highest-seq
156
+ if (retentionDays === 0) { try { unlinkSync(entry.filePath); } catch { /* skip */ } continue; }
156
157
  try {
157
158
  const mtime = statSync(entry.filePath).mtimeMs;
158
159
  if (Math.floor(mtime) <= cutoff) unlinkSync(entry.filePath);