gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.cf9433f56

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 (443) hide show
  1. package/README.md +4 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +2 -2
  4. package/dist/resources/extensions/github-sync/templates.js +39 -8
  5. package/dist/resources/extensions/gsd/auto/loop.js +48 -10
  6. package/dist/resources/extensions/gsd/auto/phases.js +66 -45
  7. package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
  8. package/dist/resources/extensions/gsd/auto/run-unit.js +32 -16
  9. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  12. package/dist/resources/extensions/gsd/auto-prompts.js +124 -2
  13. package/dist/resources/extensions/gsd/auto-recovery.js +197 -9
  14. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  15. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  17. package/dist/resources/extensions/gsd/auto.js +77 -5
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +36 -3
  19. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +27 -20
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  21. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  22. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  23. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  24. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  25. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  26. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  27. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  28. package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
  29. package/dist/resources/extensions/gsd/db-base-schema.js +18 -2
  30. package/dist/resources/extensions/gsd/db-migration-steps.js +22 -0
  31. package/dist/resources/extensions/gsd/detection.js +106 -0
  32. package/dist/resources/extensions/gsd/git-service.js +36 -4
  33. package/dist/resources/extensions/gsd/graph.js +9 -3
  34. package/dist/resources/extensions/gsd/gsd-db.js +146 -13
  35. package/dist/resources/extensions/gsd/guided-flow.js +82 -16
  36. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  37. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  38. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  39. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  40. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +7 -0
  42. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  44. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  47. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  48. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  49. package/dist/resources/extensions/gsd/quick.js +34 -2
  50. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  51. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  52. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  53. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  54. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  55. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  56. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  57. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  58. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  59. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  60. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  61. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  62. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  63. package/dist/resources/extensions/gsd/worktree-resolver.js +33 -17
  64. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  65. package/dist/web/standalone/.next/BUILD_ID +1 -1
  66. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  67. package/dist/web/standalone/.next/build-manifest.json +3 -3
  68. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  69. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/index.html +1 -1
  88. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  95. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  96. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  99. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  100. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  101. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  102. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  103. package/package.json +12 -8
  104. package/packages/contracts/package.json +1 -1
  105. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  106. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  107. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  108. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  109. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  110. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  111. package/packages/native/tsconfig.tsbuildinfo +1 -1
  112. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  113. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  114. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  115. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  116. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  117. package/packages/pi-ai/dist/models/index.js +8 -0
  118. package/packages/pi-ai/dist/models/index.js.map +1 -1
  119. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  120. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  121. package/packages/pi-ai/dist/providers/fake.js +319 -0
  122. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  123. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  124. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  125. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  126. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  127. package/packages/pi-ai/src/models/index.ts +9 -0
  128. package/packages/pi-ai/src/providers/fake.ts +376 -0
  129. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  130. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  131. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  132. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -0
  135. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
  137. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  139. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  141. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  143. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  144. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  145. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  147. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  148. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  149. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  150. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  151. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  152. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  153. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  154. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
  155. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/extensions/runner.js +17 -1
  157. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +99 -0
  159. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  161. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  163. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  165. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +24 -0
  167. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/settings-manager.js +33 -0
  169. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  171. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  172. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  182. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  183. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  185. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  188. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  189. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  191. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  193. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  195. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +17 -0
  197. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +109 -17
  199. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  201. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +69 -2
  202. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +93 -1
  204. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  206. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  208. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  211. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +26 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  214. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  216. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  227. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  231. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  235. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  237. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  239. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  240. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  241. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  242. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  243. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  244. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  246. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  247. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  248. package/packages/pi-coding-agent/src/core/agent-session.ts +8 -0
  249. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
  250. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  251. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  252. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  253. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  254. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +110 -0
  255. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -1
  256. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  257. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  258. package/packages/pi-coding-agent/src/core/settings-manager.ts +51 -1
  259. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  260. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  261. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  262. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  263. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  266. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  267. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  268. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +122 -17
  269. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +99 -1
  270. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -3
  271. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  272. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  273. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -0
  274. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  275. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  276. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  277. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  278. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  279. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  280. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  281. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  282. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  283. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  284. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  285. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  286. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  287. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  288. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  289. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  290. package/packages/pi-tui/dist/index.d.ts +1 -0
  291. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  292. package/packages/pi-tui/dist/index.js +2 -0
  293. package/packages/pi-tui/dist/index.js.map +1 -1
  294. package/packages/pi-tui/dist/style.d.ts +41 -0
  295. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  296. package/packages/pi-tui/dist/style.js +158 -0
  297. package/packages/pi-tui/dist/style.js.map +1 -0
  298. package/packages/pi-tui/dist/tui.d.ts +0 -1
  299. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  300. package/packages/pi-tui/dist/tui.js +21 -16
  301. package/packages/pi-tui/dist/tui.js.map +1 -1
  302. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  303. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  304. package/packages/pi-tui/src/index.ts +9 -0
  305. package/packages/pi-tui/src/style.ts +225 -0
  306. package/packages/pi-tui/src/tui.ts +23 -16
  307. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  308. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  309. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  310. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  311. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  312. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  313. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  314. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  315. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  316. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  317. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  318. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  319. package/src/resources/GSD-WORKFLOW.md +2 -2
  320. package/src/resources/extensions/github-sync/templates.ts +38 -8
  321. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  322. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  323. package/src/resources/extensions/gsd/auto/loop.ts +67 -18
  324. package/src/resources/extensions/gsd/auto/phases.ts +77 -48
  325. package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
  326. package/src/resources/extensions/gsd/auto/run-unit.ts +42 -15
  327. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  328. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  329. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  330. package/src/resources/extensions/gsd/auto-prompts.ts +133 -2
  331. package/src/resources/extensions/gsd/auto-recovery.ts +207 -7
  332. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  333. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  334. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  335. package/src/resources/extensions/gsd/auto.ts +92 -4
  336. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +37 -2
  337. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +27 -19
  338. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -1
  339. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  340. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  341. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  342. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  343. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  344. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  345. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  346. package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
  347. package/src/resources/extensions/gsd/db-base-schema.ts +19 -2
  348. package/src/resources/extensions/gsd/db-migration-steps.ts +25 -0
  349. package/src/resources/extensions/gsd/detection.ts +128 -0
  350. package/src/resources/extensions/gsd/git-service.ts +46 -8
  351. package/src/resources/extensions/gsd/graph.ts +12 -5
  352. package/src/resources/extensions/gsd/gsd-db.ts +168 -13
  353. package/src/resources/extensions/gsd/guided-flow.ts +98 -16
  354. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  355. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  356. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  357. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  358. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  359. package/src/resources/extensions/gsd/pre-execution-checks.ts +7 -0
  360. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  361. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  362. package/src/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  363. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  364. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  365. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  366. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  367. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  368. package/src/resources/extensions/gsd/quick.ts +37 -2
  369. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  370. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  371. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  372. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +155 -5
  373. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  374. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +184 -2
  375. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  376. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  377. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  378. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  379. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  380. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  381. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  382. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  383. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  384. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  385. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  386. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  387. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  388. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  389. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  390. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  391. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  392. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  393. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  394. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  395. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  396. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
  397. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +234 -0
  398. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  399. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  400. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  401. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  402. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  403. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  404. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  405. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +38 -0
  406. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  407. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  408. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  409. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  410. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  411. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  412. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  413. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  414. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  415. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +101 -2
  416. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
  417. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  418. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
  419. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  420. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  421. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  422. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  423. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  424. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  425. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  426. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +63 -1
  427. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  428. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  429. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  430. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  431. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  432. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  433. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  434. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  435. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  436. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  437. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  438. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  439. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  440. package/src/resources/extensions/gsd/worktree-resolver.ts +36 -15
  441. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  442. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → -5nHJWzSdG-WkPMul_khA}/_buildManifest.js +0 -0
  443. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → -5nHJWzSdG-WkPMul_khA}/_ssgManifest.js +0 -0
@@ -23,6 +23,7 @@
23
23
  // excluded from this invariant.
24
24
 
25
25
  import { createRequire } from "node:module";
26
+ import { createHash } from "node:crypto";
26
27
  import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
27
28
  import { dirname } from "node:path";
28
29
  import type { Decision, Requirement, GateRow, GateId, GateScope, GateStatus, GateVerdict } from "./types.js";
@@ -77,6 +78,9 @@ import {
77
78
  applyMigrationV21StructuredMemories,
78
79
  applyMigrationV22QualityGateRepair,
79
80
  applyMigrationV23MilestoneQueue,
81
+ applyMigrationV26MilestoneCommitAttributions,
82
+ applyMigrationV27ArtifactHash,
83
+ applyMigrationV28MemoryLastHitAt,
80
84
  } from "./db-migration-steps.js";
81
85
  import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
82
86
  import { createDbOpenState, type DbOpenPhase } from "./db-open-state.js";
@@ -105,7 +109,7 @@ const providerLoader = createSqliteProviderLoader({
105
109
  writeStderr: (message: string) => process.stderr.write(message),
106
110
  });
107
111
 
108
- export const SCHEMA_VERSION = 25;
112
+ export const SCHEMA_VERSION = 28;
109
113
 
110
114
  function initSchema(db: DbAdapter, fileBacked: boolean): void {
111
115
  if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
@@ -329,6 +333,21 @@ function migrateSchema(db: DbAdapter): void {
329
333
  recordSchemaVersion(db, 25);
330
334
  }
331
335
 
336
+ if (currentVersion < 26) {
337
+ applyMigrationV26MilestoneCommitAttributions(db);
338
+ recordSchemaVersion(db, 26);
339
+ }
340
+
341
+ if (currentVersion < 27) {
342
+ applyMigrationV27ArtifactHash(db);
343
+ recordSchemaVersion(db, 27);
344
+ }
345
+
346
+ if (currentVersion < 28) {
347
+ applyMigrationV28MemoryLastHitAt(db);
348
+ recordSchemaVersion(db, 28);
349
+ }
350
+
332
351
  db.exec("COMMIT");
333
352
  } catch (err) {
334
353
  db.exec("ROLLBACK");
@@ -907,9 +926,10 @@ export function insertArtifact(a: {
907
926
  full_content: string;
908
927
  }): void {
909
928
  if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
929
+ const contentHash = createHash("sha256").update(a.full_content).digest("hex");
910
930
  currentDb.prepare(
911
- `INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
912
- VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at)`,
931
+ `INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash)
932
+ VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at, :content_hash)`,
913
933
  ).run({
914
934
  ":path": a.path,
915
935
  ":artifact_type": a.artifact_type,
@@ -918,6 +938,7 @@ export function insertArtifact(a: {
918
938
  ":task_id": a.task_id,
919
939
  ":full_content": a.full_content,
920
940
  ":imported_at": new Date().toISOString(),
941
+ ":content_hash": contentHash,
921
942
  });
922
943
  }
923
944
 
@@ -1349,6 +1370,45 @@ export function getSliceTasks(milestoneId: string, sliceId: string): TaskRow[] {
1349
1370
  return rows.map(rowToTask);
1350
1371
  }
1351
1372
 
1373
+ export function getCompletedMilestoneTaskFileHints(milestoneId: string): string[] {
1374
+ if (!currentDb) return [];
1375
+ const rows = currentDb.prepare(
1376
+ `SELECT files, key_files
1377
+ FROM tasks
1378
+ WHERE milestone_id = :mid AND status IN ('complete', 'done')`,
1379
+ ).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
1380
+
1381
+ const hints = new Set<string>();
1382
+ for (const row of rows) {
1383
+ for (const raw of [row["files"], row["key_files"]]) {
1384
+ for (const file of parseStringArrayColumn(raw)) {
1385
+ const normalized = normalizeRepoPath(file);
1386
+ if (normalized) hints.add(normalized);
1387
+ }
1388
+ }
1389
+ }
1390
+ return [...hints];
1391
+ }
1392
+
1393
+ function parseStringArrayColumn(raw: unknown): string[] {
1394
+ if (Array.isArray(raw)) return raw.filter((entry): entry is string => typeof entry === "string");
1395
+ if (typeof raw !== "string") return [];
1396
+ const trimmed = raw.trim();
1397
+ if (!trimmed) return [];
1398
+ try {
1399
+ const parsed = JSON.parse(trimmed);
1400
+ if (Array.isArray(parsed)) return parsed.filter((entry): entry is string => typeof entry === "string");
1401
+ if (typeof parsed === "string") return [parsed];
1402
+ } catch {
1403
+ return trimmed.split(",");
1404
+ }
1405
+ return [];
1406
+ }
1407
+
1408
+ function normalizeRepoPath(file: string): string {
1409
+ return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
1410
+ }
1411
+
1352
1412
  // ─── ADR-011 Phase 2 escalation helpers ──────────────────────────────────
1353
1413
 
1354
1414
  /** Set pause-on-escalation state on a completed task. Mutually exclusive with awaiting_review. */
@@ -1750,6 +1810,13 @@ export function reconcileWorktreeDb(
1750
1810
  const hasEscalationAwaiting = wtTaskInfo.some((col) => col["name"] === "escalation_awaiting_review");
1751
1811
  const hasEscalationArtifact = wtTaskInfo.some((col) => col["name"] === "escalation_artifact_path");
1752
1812
  const hasEscalationOverride = wtTaskInfo.some((col) => col["name"] === "escalation_override_applied_at");
1813
+ const wtArtifactInfo = adapter.prepare("PRAGMA wt.table_info('artifacts')").all();
1814
+ const hasArtifactContentHash = wtArtifactInfo.some((col) => col["name"] === "content_hash");
1815
+ const wtMemoryInfo = adapter.prepare("PRAGMA wt.table_info('memories')").all();
1816
+ const hasMemoryScope = wtMemoryInfo.some((col) => col["name"] === "scope");
1817
+ const hasMemoryTags = wtMemoryInfo.some((col) => col["name"] === "tags");
1818
+ const hasMemoryStructuredFields = wtMemoryInfo.some((col) => col["name"] === "structured_fields");
1819
+ const hasMemoryLastHitAt = wtMemoryInfo.some((col) => col["name"] === "last_hit_at");
1753
1820
 
1754
1821
  const decConf = adapter.prepare(
1755
1822
  `SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${
@@ -1797,12 +1864,17 @@ export function reconcileWorktreeDb(
1797
1864
  FROM wt.requirements
1798
1865
  `).run());
1799
1866
 
1867
+ // V27: preserve content_hash. If the worktree predates V27 (no column),
1868
+ // fall back to the main DB's existing hash so reconcile doesn't null
1869
+ // out integrity fingerprints on artifacts that were unchanged in wt.
1800
1870
  merged.artifacts = countChanges(adapter.prepare(`
1801
1871
  INSERT OR REPLACE INTO artifacts (
1802
- path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
1872
+ path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
1803
1873
  )
1804
- SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
1805
- FROM wt.artifacts
1874
+ SELECT w.path, w.artifact_type, w.milestone_id, w.slice_id, w.task_id, w.full_content, w.imported_at,
1875
+ ${hasArtifactContentHash ? "w.content_hash" : "m.content_hash"}
1876
+ FROM wt.artifacts w
1877
+ LEFT JOIN artifacts m ON m.path = w.path
1806
1878
  `).run());
1807
1879
 
1808
1880
  // Merge milestones — worktree may have updated status/planning fields.
@@ -1904,15 +1976,25 @@ export function reconcileWorktreeDb(
1904
1976
  LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
1905
1977
  `).run());
1906
1978
 
1907
- // Merge memories — keep worktree-learned insights
1979
+ // Merge memories — keep worktree-learned insights.
1980
+ // V18 (scope, tags), V21 (structured_fields), V28 (last_hit_at): for each
1981
+ // column the wt may not yet have (older worktree DB), fall back to the
1982
+ // main DB's existing value via LEFT JOIN so reconcile never silently
1983
+ // resets these fields to defaults on rows that already had them.
1908
1984
  merged.memories = countChanges(adapter.prepare(`
1909
1985
  INSERT OR REPLACE INTO memories (
1910
1986
  seq, id, category, content, confidence, source_unit_type, source_unit_id,
1911
- created_at, updated_at, superseded_by, hit_count
1987
+ created_at, updated_at, superseded_by, hit_count,
1988
+ scope, tags, structured_fields, last_hit_at
1912
1989
  )
1913
- SELECT seq, id, category, content, confidence, source_unit_type, source_unit_id,
1914
- created_at, updated_at, superseded_by, hit_count
1915
- FROM wt.memories
1990
+ SELECT w.seq, w.id, w.category, w.content, w.confidence, w.source_unit_type, w.source_unit_id,
1991
+ w.created_at, w.updated_at, w.superseded_by, w.hit_count,
1992
+ ${hasMemoryScope ? "w.scope" : "COALESCE(m.scope, 'project')"},
1993
+ ${hasMemoryTags ? "w.tags" : "COALESCE(m.tags, '[]')"},
1994
+ ${hasMemoryStructuredFields ? "w.structured_fields" : "m.structured_fields"},
1995
+ ${hasMemoryLastHitAt ? "w.last_hit_at" : "m.last_hit_at"}
1996
+ FROM wt.memories w
1997
+ LEFT JOIN memories m ON m.id = w.id
1916
1998
  `).run());
1917
1999
 
1918
2000
  // Merge verification evidence — append-only, use INSERT OR IGNORE to avoid duplicates
@@ -2073,6 +2155,9 @@ export function deleteMilestone(milestoneId: string): void {
2073
2155
  currentDb!.prepare(
2074
2156
  `DELETE FROM artifacts WHERE milestone_id = :mid`,
2075
2157
  ).run({ ":mid": milestoneId });
2158
+ currentDb!.prepare(
2159
+ `DELETE FROM milestone_commit_attributions WHERE milestone_id = :mid`,
2160
+ ).run({ ":mid": milestoneId });
2076
2161
  currentDb!.prepare(
2077
2162
  `DELETE FROM milestone_leases WHERE milestone_id = :mid`,
2078
2163
  ).run({ ":mid": milestoneId });
@@ -2391,6 +2476,75 @@ export function upsertTurnGitTransaction(entry: {
2391
2476
  });
2392
2477
  }
2393
2478
 
2479
+ export function getMilestoneCommitAttributionShas(milestoneId: string): string[] {
2480
+ if (!currentDb) return [];
2481
+ const rows = currentDb.prepare(
2482
+ `SELECT commit_sha
2483
+ FROM milestone_commit_attributions
2484
+ WHERE milestone_id = :mid
2485
+ ORDER BY created_at, commit_sha`,
2486
+ ).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
2487
+ return rows
2488
+ .map((row) => typeof row["commit_sha"] === "string" ? row["commit_sha"] : "")
2489
+ .filter(Boolean);
2490
+ }
2491
+
2492
+ export function recordMilestoneCommitAttribution(entry: {
2493
+ commitSha: string;
2494
+ milestoneId: string;
2495
+ sliceId?: string;
2496
+ taskId?: string;
2497
+ source: "recorded" | "backfill";
2498
+ confidence: number;
2499
+ files: string[];
2500
+ createdAt: string;
2501
+ }): void {
2502
+ if (!currentDb) return;
2503
+ transaction(() => {
2504
+ currentDb!.prepare(
2505
+ `INSERT OR REPLACE INTO milestone_commit_attributions (
2506
+ commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
2507
+ ) VALUES (
2508
+ :commit_sha, :milestone_id, :slice_id, :task_id, :source, :confidence, :files_json, :created_at
2509
+ )`,
2510
+ ).run({
2511
+ ":commit_sha": entry.commitSha,
2512
+ ":milestone_id": entry.milestoneId,
2513
+ ":slice_id": entry.sliceId ?? null,
2514
+ ":task_id": entry.taskId ?? null,
2515
+ ":source": entry.source,
2516
+ ":confidence": entry.confidence,
2517
+ ":files_json": JSON.stringify(entry.files),
2518
+ ":created_at": entry.createdAt,
2519
+ });
2520
+
2521
+ currentDb!.prepare(
2522
+ `INSERT OR IGNORE INTO audit_events (
2523
+ event_id, trace_id, turn_id, caused_by, category, type, ts, payload_json
2524
+ ) VALUES (
2525
+ :event_id, :trace_id, :turn_id, :caused_by, :category, :type, :ts, :payload_json
2526
+ )`,
2527
+ ).run({
2528
+ ":event_id": `milestone-commit-attribution:${entry.milestoneId}:${entry.commitSha}`,
2529
+ ":trace_id": "milestone-commit-attribution",
2530
+ ":turn_id": null,
2531
+ ":caused_by": null,
2532
+ ":category": "git",
2533
+ ":type": "milestone-commit-attribution-recorded",
2534
+ ":ts": entry.createdAt,
2535
+ ":payload_json": JSON.stringify({
2536
+ commitSha: entry.commitSha,
2537
+ milestoneId: entry.milestoneId,
2538
+ sliceId: entry.sliceId ?? null,
2539
+ taskId: entry.taskId ?? null,
2540
+ source: entry.source,
2541
+ confidence: entry.confidence,
2542
+ files: entry.files,
2543
+ }),
2544
+ });
2545
+ });
2546
+ }
2547
+
2394
2548
  export function insertAuditEvent(entry: {
2395
2549
  eventId: string;
2396
2550
  traceId: string;
@@ -2494,6 +2648,7 @@ export function clearEngineHierarchy(): void {
2494
2648
  currentDb!.exec("DELETE FROM slice_dependencies");
2495
2649
  currentDb!.exec("DELETE FROM assessments");
2496
2650
  currentDb!.exec("DELETE FROM replan_history");
2651
+ currentDb!.exec("DELETE FROM milestone_commit_attributions");
2497
2652
  currentDb!.exec("DELETE FROM tasks");
2498
2653
  currentDb!.exec("DELETE FROM slices");
2499
2654
  currentDb!.exec("DELETE FROM milestone_leases");
@@ -2944,8 +3099,8 @@ export function updateMemoryContentRow(
2944
3099
  export function incrementMemoryHitCount(id: string, updatedAt: string): void {
2945
3100
  if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
2946
3101
  currentDb.prepare(
2947
- "UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at WHERE id = :id",
2948
- ).run({ ":updated_at": updatedAt, ":id": id });
3102
+ "UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at, last_hit_at = :last_hit_at WHERE id = :id",
3103
+ ).run({ ":updated_at": updatedAt, ":last_hit_at": updatedAt, ":id": id });
2949
3104
  }
2950
3105
 
2951
3106
  export function supersedeMemoryRow(oldId: string, newId: string, updatedAt: string): void {
@@ -30,7 +30,7 @@ import {
30
30
  formatInterruptedSessionRunningMessage,
31
31
  formatInterruptedSessionSummary,
32
32
  } from "./interrupted-session.js";
33
- import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
33
+ import { listUnitRuntimeRecords, clearUnitRuntimeRecord, isInFlightRuntimePhase } from "./unit-runtime.js";
34
34
  import { resolveExpectedArtifactPath } from "./auto.js";
35
35
  import { gsdHome } from "./gsd-home.js";
36
36
  import {
@@ -87,6 +87,34 @@ import { logWarning } from "./workflow-logger.js";
87
87
  import { deleteRuntimeKv } from "./db/runtime-kv.js";
88
88
  import { PAUSED_SESSION_KV_KEY } from "./interrupted-session.js";
89
89
 
90
+ type AutoStartOptions = Parameters<typeof startAutoDetached>[4];
91
+ type AutoStartLauncher = typeof startAutoDetached;
92
+
93
+ function scheduleAutoStartAfterIdle(
94
+ ctx: ExtensionCommandContext,
95
+ pi: ExtensionAPI,
96
+ basePath: string,
97
+ verboseMode: boolean,
98
+ options?: AutoStartOptions,
99
+ launch: AutoStartLauncher = startAutoDetached,
100
+ ): void {
101
+ const waitForIdle =
102
+ typeof (ctx as { waitForIdle?: unknown }).waitForIdle === "function"
103
+ ? ctx.waitForIdle.bind(ctx)
104
+ : async () => {};
105
+ void waitForIdle()
106
+ .then(() => {
107
+ setTimeout(() => launch(ctx, pi, basePath, verboseMode, options), 0);
108
+ })
109
+ .catch((err) => {
110
+ const message = err instanceof Error ? err.message : String(err);
111
+ ctx.ui.notify(`Auto-start failed while waiting for the prior turn to settle: ${message}`, "error");
112
+ logWarning("guided", `auto-start idle wait failed: ${message}`);
113
+ });
114
+ }
115
+
116
+ export const _scheduleAutoStartAfterIdleForTest = scheduleAutoStartAfterIdle;
117
+
90
118
  // ─── Scope-based validator wrappers ──────────────────────────────────────────
91
119
  // These thin wrappers accept a MilestoneScope so callers that already hold a
92
120
  // pinned scope never have to re-derive (basePath, milestoneId) separately.
@@ -118,6 +146,22 @@ export function resolveExpectedArtifactPathForScope(
118
146
  return resolveExpectedArtifactPath(unitType, unitId, scope.workspace.projectRoot);
119
147
  }
120
148
 
149
+ async function runQuickTaskChoice(ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
150
+ if (!ctx.hasUI) {
151
+ ctx.ui.notify("Run /gsd quick <task> for small bounded work, or /gsd do <task> for natural-language routing.", "info");
152
+ return;
153
+ }
154
+
155
+ const task = (await ctx.ui.input("Quick task", "Describe the small task to run with /gsd quick"))?.trim();
156
+ if (!task) {
157
+ ctx.ui.notify("Quick task cancelled.", "info");
158
+ return;
159
+ }
160
+
161
+ const { handleQuick } = await import("./quick.js");
162
+ await handleQuick(task, ctx, pi);
163
+ }
164
+
121
165
  /**
122
166
  * Scope-based overload of isGhostMilestone.
123
167
  * Binds basePath and milestoneId from the scope, ensuring path resolution
@@ -430,7 +474,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
430
474
 
431
475
  if (!hasPendingDeepStage(prefs, entry.basePath)) {
432
476
  pendingDeepProjectSetupMap.delete(entry.basePath);
433
- startAutoDetached(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
477
+ scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
434
478
  return true;
435
479
  }
436
480
 
@@ -463,7 +507,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
463
507
  entry.ctx.ui.notify(result.reason, result.level);
464
508
  } else if (hasPendingDeepStage(prefs, entry.basePath)) {
465
509
  pendingDeepProjectSetupMap.delete(entry.basePath);
466
- startAutoDetached(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
510
+ scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
467
511
  return true;
468
512
  }
469
513
  return false;
@@ -471,7 +515,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
471
515
 
472
516
  if (!USER_DRIVEN_DEEP_SETUP_UNITS.has(result.unitType)) {
473
517
  pendingDeepProjectSetupMap.delete(entry.basePath);
474
- startAutoDetached(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
518
+ scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
475
519
  return true;
476
520
  }
477
521
 
@@ -677,7 +721,7 @@ export function checkAutoStartAfterDiscuss(): boolean {
677
721
 
678
722
  pendingAutoStartMap.delete(basePath);
679
723
  ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
680
- startAutoDetached(ctx, pi, basePath, false, { step });
724
+ scheduleAutoStartAfterIdle(ctx, pi, basePath, false, { step });
681
725
  return true;
682
726
  }
683
727
 
@@ -1786,8 +1830,8 @@ function selfHealRuntimeRecords(basePath: string, ctx: ExtensionContext): { clea
1786
1830
  cleared++;
1787
1831
  continue;
1788
1832
  }
1789
- // Clear records stuck in dispatched or timeout phase (process died mid-unit)
1790
- if (phase === "dispatched" || phase === "timeout") {
1833
+ // Clear records stuck in an in-flight phase (process died mid-unit).
1834
+ if (isInFlightRuntimePhase(phase)) {
1791
1835
  clearUnitRuntimeRecord(basePath, unitType, unitId);
1792
1836
  cleared++;
1793
1837
  }
@@ -2042,6 +2086,23 @@ export async function showSmartEntry(
2042
2086
  }
2043
2087
  }
2044
2088
 
2089
+ if (interrupted.classification !== "recoverable") {
2090
+ try {
2091
+ const { autoImportMarkdownHierarchyIfDbMismatch } = await import("./migration-auto-check.js");
2092
+ const result = await autoImportMarkdownHierarchyIfDbMismatch(basePath);
2093
+ if (result.action === "imported") {
2094
+ ctx.ui.notify(
2095
+ `Recovered migrated planning state into gsd.db (${result.reason}): ${result.afterDb.milestones} milestone(s), ${result.afterDb.slices} slice(s), ${result.afterDb.tasks} task(s).`,
2096
+ "info",
2097
+ );
2098
+ }
2099
+ } catch (err) {
2100
+ const message = err instanceof Error ? err.message : String(err);
2101
+ ctx.ui.notify(`GSD could not auto-import existing planning state into gsd.db: ${message}`, "warning");
2102
+ logWarning("guided", `planning state auto-import failed: ${message}`, { file: "guided-flow.ts" });
2103
+ }
2104
+ }
2105
+
2045
2106
  // Always derive from the project root — the assessment may have derived
2046
2107
  // state from a worktree path that was cleaned up in the stale branch above.
2047
2108
  const state = await deriveState(basePath);
@@ -2064,8 +2125,8 @@ export async function showSmartEntry(
2064
2125
  // standard wizard below.
2065
2126
  {
2066
2127
  const prefs = loadEffectiveGSDPreferences(basePath)?.preferences;
2067
- const { hasPendingDeepStage } = await import("./auto-dispatch.js");
2068
- if (hasPendingDeepStage(prefs, basePath)) {
2128
+ const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
2129
+ if (shouldRunDeepProjectSetup(state, prefs, basePath)) {
2069
2130
  await startDeepProjectSetupForeground(ctx, pi, basePath, stepMode);
2070
2131
  return;
2071
2132
  }
@@ -2134,17 +2195,24 @@ export async function showSmartEntry(
2134
2195
  title: "GSD — Get Shit Done",
2135
2196
  summary: ["No active milestone."],
2136
2197
  actions: [
2198
+ {
2199
+ id: "quick_task",
2200
+ label: "Quick task",
2201
+ description: "For small bounded work, run /gsd quick <task> or /gsd do <task>.",
2202
+ recommended: true,
2203
+ },
2137
2204
  {
2138
2205
  id: "new_milestone",
2139
2206
  label: "Create next milestone",
2140
- description: "Define what to build next.",
2141
- recommended: true,
2207
+ description: "Define a larger body of work with planning artifacts.",
2142
2208
  },
2143
2209
  ],
2144
2210
  notYetMessage: "Run /gsd when ready.",
2145
2211
  });
2146
2212
 
2147
- if (choice === "new_milestone") {
2213
+ if (choice === "quick_task") {
2214
+ await runQuickTaskChoice(ctx, pi);
2215
+ } else if (choice === "new_milestone") {
2148
2216
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
2149
2217
  await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
2150
2218
  `New milestone ${nextId}.`,
@@ -2181,11 +2249,16 @@ export async function showSmartEntry(
2181
2249
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
2182
2250
  summary: ["All milestones complete."],
2183
2251
  actions: [
2252
+ {
2253
+ id: "quick_task",
2254
+ label: "Quick task",
2255
+ description: "Do a small bounded task without opening a milestone.",
2256
+ recommended: true,
2257
+ },
2184
2258
  {
2185
2259
  id: "new_milestone",
2186
2260
  label: "Start new milestone",
2187
2261
  description: "Define and plan the next milestone.",
2188
- recommended: true,
2189
2262
  },
2190
2263
  {
2191
2264
  id: "status",
@@ -2196,7 +2269,9 @@ export async function showSmartEntry(
2196
2269
  notYetMessage: "Run /gsd when ready.",
2197
2270
  });
2198
2271
 
2199
- if (choice === "new_milestone") {
2272
+ if (choice === "quick_task") {
2273
+ await runQuickTaskChoice(ctx, pi);
2274
+ } else if (choice === "new_milestone") {
2200
2275
  const milestoneIds = findMilestoneIds(basePath);
2201
2276
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
2202
2277
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
@@ -2300,13 +2375,18 @@ export async function showSmartEntry(
2300
2375
  const hasContext = !!(contextFile && await loadFile(contextFile));
2301
2376
 
2302
2377
  const actions = [
2378
+ {
2379
+ id: "quick_task",
2380
+ label: "Quick task instead",
2381
+ description: "Use this when the work is small and should not become a milestone.",
2382
+ recommended: true,
2383
+ },
2303
2384
  {
2304
2385
  id: "plan",
2305
2386
  label: "Create roadmap",
2306
2387
  description: hasContext
2307
2388
  ? "Context captured. Decompose into slices with a boundary map."
2308
2389
  : "Decompose the milestone into slices with a boundary map.",
2309
- recommended: true,
2310
2390
  },
2311
2391
  ...(!hasContext ? [{
2312
2392
  id: "discuss",
@@ -2332,7 +2412,9 @@ export async function showSmartEntry(
2332
2412
  notYetMessage: "Run /gsd when ready.",
2333
2413
  });
2334
2414
 
2335
- if (choice === "plan") {
2415
+ if (choice === "quick_task") {
2416
+ await runQuickTaskChoice(ctx, pi);
2417
+ } else if (choice === "plan") {
2336
2418
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
2337
2419
  await dispatchWorkflow(
2338
2420
  pi,
@@ -44,6 +44,8 @@ export interface Memory {
44
44
  * decisions table (Step 5) with the original scope/decision/choice/etc.
45
45
  */
46
46
  structured_fields: Record<string, unknown> | null;
47
+ /** ISO timestamp of the most recent memory_query hit. NULL until first hit. */
48
+ last_hit_at: string | null;
47
49
  }
48
50
 
49
51
  export type MemoryActionCreate = {
@@ -100,6 +102,27 @@ const CATEGORY_PRIORITY: Record<string, number> = {
100
102
  preference: 5,
101
103
  };
102
104
 
105
+ // ─── Scoring Helpers ─────────────────────────────────────────────────────────
106
+
107
+ /**
108
+ * Time-decay factor for memory relevance scoring.
109
+ * Returns 1.0 for never-hit or recently-hit memories, decaying linearly to
110
+ * 0.7 for memories not accessed in 90+ days. Floor at 0.7 keeps old-but-valid
111
+ * knowledge from being fully suppressed.
112
+ *
113
+ * Defensive parsing: invalid timestamp strings (NaN from Date.parse) are
114
+ * treated as "no decay" rather than propagating NaN into score arithmetic.
115
+ * Future timestamps (clock skew, manual DB edits) clamp to daysAgo=0 so the
116
+ * factor stays in the documented [0.7, 1.0] contract.
117
+ */
118
+ export function memoryDecayFactor(lastHitAt: string | null): number {
119
+ if (!lastHitAt) return 1.0;
120
+ const ts = Date.parse(lastHitAt);
121
+ if (!Number.isFinite(ts)) return 1.0;
122
+ const daysAgo = Math.max(0, (Date.now() - ts) / 86_400_000);
123
+ return Math.max(0.7, 1.0 - 0.3 * Math.min(1.0, daysAgo / 90));
124
+ }
125
+
103
126
  // ─── Row Mapping ────────────────────────────────────────────────────────────
104
127
 
105
128
  function rowToMemory(row: Record<string, unknown>): Memory {
@@ -118,6 +141,7 @@ function rowToMemory(row: Record<string, unknown>): Memory {
118
141
  scope: (row['scope'] as string) ?? 'project',
119
142
  tags: parseTags(row['tags']),
120
143
  structured_fields: parseStructuredFields(row['structured_fields']),
144
+ last_hit_at: (row['last_hit_at'] as string | null) ?? null,
121
145
  };
122
146
  }
123
147
 
@@ -233,15 +257,39 @@ export function queryMemoriesRanked(opts: QueryMemoriesOptions): RankedMemory[]
233
257
  : [];
234
258
 
235
259
  if (keywordHits.length === 0 && semanticHits.length === 0 && !trimmedQuery) {
236
- // No query at all — fall back to the existing ranked-by-score listing.
237
- return getActiveMemoriesRanked(k).map((memory) => ({
238
- memory,
239
- score: memory.confidence * (1 + memory.hit_count * 0.1),
240
- keywordRank: null,
241
- semanticRank: null,
242
- confidenceBoost: memory.confidence * (1 + memory.hit_count * 0.1),
243
- reason: 'ranked' as const,
244
- })).filter((hit) => passesFilters(hit.memory, opts));
260
+ // No query at all — return top-k by decay-aware ranked score.
261
+ //
262
+ // Build the candidate pool from a direct SQL query that honors the
263
+ // request's activeClause (i.e. include_superseded). Using
264
+ // getActiveMemoriesRanked here would silently drop superseded rows even
265
+ // when the caller explicitly opted in, and would slice by raw score
266
+ // before decay/filters had a chance to reorder.
267
+ const candidatePool = Math.min(Math.max(k * 5, 50), 500);
268
+ const rows = adapter
269
+ .prepare(
270
+ `SELECT * FROM memories ${activeClause}
271
+ ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
272
+ LIMIT :limit`,
273
+ )
274
+ .all({ ':limit': candidatePool });
275
+
276
+ const ranked: RankedMemory[] = [];
277
+ for (const row of rows) {
278
+ const memory = rowToMemory(row);
279
+ if (!passesFilters(memory, opts)) continue;
280
+ const decay = memoryDecayFactor(memory.last_hit_at);
281
+ const score = memory.confidence * (1 + memory.hit_count * 0.1) * decay;
282
+ ranked.push({
283
+ memory,
284
+ score,
285
+ keywordRank: null,
286
+ semanticRank: null,
287
+ confidenceBoost: score,
288
+ reason: 'ranked' as const,
289
+ });
290
+ }
291
+ ranked.sort((a, b) => b.score - a.score);
292
+ return ranked.slice(0, k);
245
293
  }
246
294
 
247
295
  // 3) Reciprocal rank fusion — each hit contributes 1/(rrfK + rank).
@@ -275,7 +323,7 @@ export function queryMemoriesRanked(opts: QueryMemoriesOptions): RankedMemory[]
275
323
  const ranked: RankedMemory[] = [];
276
324
  for (const entry of fused.values()) {
277
325
  if (!passesFilters(entry.memory, opts)) continue;
278
- const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1);
326
+ const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1) * memoryDecayFactor(entry.memory.last_hit_at);
279
327
  const reason: RankedMemory['reason'] =
280
328
  entry.kwRank != null && entry.semRank != null
281
329
  ? 'both'
@@ -313,6 +361,8 @@ function passesFilters(memory: Memory, filters: QueryMemoriesFilters): boolean {
313
361
  return true;
314
362
  }
315
363
 
364
+ let ftsWarningEmitted = false;
365
+
316
366
  function keywordSearch(
317
367
  adapter: NonNullable<ReturnType<typeof _getAdapter>>,
318
368
  rawQuery: string,
@@ -340,14 +390,29 @@ function keywordSearch(
340
390
  }
341
391
  }
342
392
 
343
- // LIKE fallback — scans the candidate pool.
393
+ // LIKE fallback — scans a capped candidate pool.
394
+ if (!ftsWarningEmitted) {
395
+ ftsWarningEmitted = true;
396
+ logWarning('memory-store', 'FTS5 unavailable — using LIKE fallback scan (consider enabling FTS5)');
397
+ }
398
+
344
399
  const terms = rawQuery
345
400
  .toLowerCase()
346
401
  .split(/[^a-z0-9_]+/)
347
402
  .filter((t) => t.length >= 2);
348
403
  if (terms.length === 0) return [];
349
404
 
350
- const rows = adapter.prepare(`SELECT * FROM memories ${activeClause}`).all();
405
+ const preScanCap = Math.min(limit * 20, 2000);
406
+ // ORDER BY confidence-weighted hit_count DESC so the cap keeps the most
407
+ // valuable candidates instead of the oldest-by-rowid (which would silently
408
+ // exclude recently-stored memories on tables larger than preScanCap).
409
+ const rows = adapter
410
+ .prepare(
411
+ `SELECT * FROM memories ${activeClause}
412
+ ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
413
+ LIMIT :preScanCap`,
414
+ )
415
+ .all({ ':preScanCap': preScanCap });
351
416
  const scored: Array<{ memory: Memory; score: number }> = [];
352
417
  for (const row of rows) {
353
418
  const memory = rowToMemory(row);