gsd-pi 2.63.0 → 2.64.0-dev.9c14bd0

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 (448) hide show
  1. package/README.md +46 -134
  2. package/dist/cli.js +48 -6
  3. package/dist/headless-query.js +11 -1
  4. package/dist/help-text.js +4 -1
  5. package/dist/onboarding.js +15 -8
  6. package/dist/resource-loader.js +18 -3
  7. package/dist/resources/extensions/cmux/index.js +21 -12
  8. package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
  9. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +4 -0
  11. package/dist/resources/extensions/gsd/auto/phases.js +157 -22
  12. package/dist/resources/extensions/gsd/auto/session.js +12 -0
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +14 -8
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +222 -11
  16. package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
  17. package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
  18. package/dist/resources/extensions/gsd/auto-start.js +10 -21
  19. package/dist/resources/extensions/gsd/auto-timers.js +2 -1
  20. package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
  21. package/dist/resources/extensions/gsd/auto-verification.js +138 -1
  22. package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
  23. package/dist/resources/extensions/gsd/auto.js +24 -2
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
  25. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
  26. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
  27. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
  28. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  29. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
  30. package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
  31. package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
  32. package/dist/resources/extensions/gsd/constants.js +42 -0
  33. package/dist/resources/extensions/gsd/db-writer.js +72 -4
  34. package/dist/resources/extensions/gsd/forensics.js +20 -4
  35. package/dist/resources/extensions/gsd/gsd-db.js +64 -17
  36. package/dist/resources/extensions/gsd/guided-flow.js +19 -0
  37. package/dist/resources/extensions/gsd/metrics.js +27 -1
  38. package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
  39. package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
  40. package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
  41. package/dist/resources/extensions/gsd/preferences-types.js +6 -0
  42. package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
  43. package/dist/resources/extensions/gsd/preferences.js +11 -2
  44. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  45. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  48. package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
  49. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  50. package/dist/resources/extensions/gsd/prompts/system.md +4 -7
  51. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  52. package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
  53. package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
  54. package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
  55. package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
  56. package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
  57. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
  58. package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
  59. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
  60. package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
  61. package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
  62. package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
  63. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
  64. package/dist/resources/extensions/gsd/state.js +74 -14
  65. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  66. package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
  67. package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
  68. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
  69. package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
  70. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
  71. package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
  72. package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
  73. package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
  74. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
  75. package/dist/resources/extensions/mcp-client/auth.js +101 -0
  76. package/dist/resources/extensions/mcp-client/index.js +10 -1
  77. package/dist/resources/extensions/ollama/index.js +28 -22
  78. package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
  79. package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
  80. package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
  81. package/dist/resources/extensions/ollama/ollama-client.js +23 -32
  82. package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
  83. package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
  84. package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
  85. package/dist/update-cmd.js +4 -2
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  88. package/dist/web/standalone/.next/build-manifest.json +3 -3
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/required-server-files.json +4 -4
  91. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  92. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  94. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  102. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  113. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  116. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  137. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  177. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  183. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  189. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  192. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  197. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  200. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  203. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  205. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  207. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  209. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  210. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  211. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  212. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  213. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  217. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  218. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  219. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  221. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  223. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/app/index.html +1 -1
  225. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  226. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  227. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  228. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  229. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  230. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  231. package/dist/web/standalone/.next/server/app/page.js +2 -2
  232. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  233. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  234. package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
  235. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  236. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  237. package/dist/web/standalone/.next/server/middleware.js +2 -2
  238. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  239. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  240. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  241. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  242. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  243. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  244. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  245. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  246. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  247. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  248. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  249. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  250. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  251. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  252. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  253. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  254. package/dist/web/standalone/server.js +1 -1
  255. package/dist/welcome-screen.js +1 -1
  256. package/package.json +1 -1
  257. package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
  258. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  259. package/packages/pi-agent-core/dist/agent-loop.js +50 -0
  260. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  261. package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
  262. package/packages/pi-agent-core/src/agent-loop.ts +53 -0
  263. package/packages/pi-ai/dist/types.d.ts +16 -1
  264. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  265. package/packages/pi-ai/dist/types.js.map +1 -1
  266. package/packages/pi-ai/src/types.ts +18 -1
  267. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
  268. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  269. package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
  272. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  273. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
  274. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  275. package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
  276. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  277. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
  278. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
  280. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
  281. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
  282. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
  283. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  284. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  286. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
  287. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  288. package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
  289. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
  291. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
  293. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  294. package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
  295. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  298. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  299. package/packages/pi-coding-agent/package.json +1 -1
  300. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
  301. package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
  302. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
  303. package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
  304. package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
  305. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  306. package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
  307. package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
  308. package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
  309. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  310. package/pkg/package.json +1 -1
  311. package/src/resources/extensions/cmux/index.ts +18 -12
  312. package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
  313. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
  314. package/src/resources/extensions/gsd/auto/loop.ts +5 -0
  315. package/src/resources/extensions/gsd/auto/phases.ts +194 -33
  316. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  317. package/src/resources/extensions/gsd/auto-dashboard.ts +16 -7
  318. package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
  319. package/src/resources/extensions/gsd/auto-post-unit.ts +263 -12
  320. package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
  321. package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
  322. package/src/resources/extensions/gsd/auto-start.ts +11 -20
  323. package/src/resources/extensions/gsd/auto-timers.ts +2 -1
  324. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  325. package/src/resources/extensions/gsd/auto-verification.ts +190 -2
  326. package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
  327. package/src/resources/extensions/gsd/auto.ts +26 -1
  328. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
  329. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
  330. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
  331. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
  332. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
  333. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
  334. package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
  335. package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
  336. package/src/resources/extensions/gsd/constants.ts +44 -0
  337. package/src/resources/extensions/gsd/db-writer.ts +78 -4
  338. package/src/resources/extensions/gsd/forensics.ts +21 -5
  339. package/src/resources/extensions/gsd/gsd-db.ts +64 -17
  340. package/src/resources/extensions/gsd/guided-flow.ts +22 -0
  341. package/src/resources/extensions/gsd/metrics.ts +28 -1
  342. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
  343. package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
  344. package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
  345. package/src/resources/extensions/gsd/preferences-types.ts +44 -0
  346. package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
  347. package/src/resources/extensions/gsd/preferences.ts +13 -2
  348. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  349. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  350. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  351. package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  352. package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
  353. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  354. package/src/resources/extensions/gsd/prompts/system.md +4 -7
  355. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  356. package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
  357. package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
  358. package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
  359. package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
  360. package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
  361. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
  362. package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
  363. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
  364. package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
  365. package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
  366. package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
  367. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
  368. package/src/resources/extensions/gsd/state.ts +67 -12
  369. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  370. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
  371. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
  372. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
  373. package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
  374. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
  375. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  376. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
  377. package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
  378. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
  379. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
  380. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
  381. package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
  382. package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
  383. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
  384. package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
  385. package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
  386. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
  387. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
  388. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
  389. package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
  390. package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
  391. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
  392. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
  393. package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
  394. package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
  395. package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
  396. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
  397. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
  398. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
  399. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
  400. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
  401. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
  402. package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
  403. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
  404. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
  405. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
  406. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
  407. package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
  408. package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
  409. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
  410. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
  411. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
  412. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
  413. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
  414. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
  415. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
  416. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
  417. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
  418. package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
  419. package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
  420. package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
  421. package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
  422. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
  423. package/src/resources/extensions/gsd/types.ts +44 -22
  424. package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
  425. package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
  426. package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
  427. package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
  428. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
  429. package/src/resources/extensions/mcp-client/auth.ts +149 -0
  430. package/src/resources/extensions/mcp-client/index.ts +16 -1
  431. package/src/resources/extensions/ollama/index.ts +26 -25
  432. package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
  433. package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
  434. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
  435. package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
  436. package/src/resources/extensions/ollama/ollama-client.ts +30 -30
  437. package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
  438. package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
  439. package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
  440. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
  441. package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
  442. package/src/resources/extensions/ollama/types.ts +23 -0
  443. package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
  444. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  445. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  446. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  447. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
  448. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
@@ -0,0 +1,464 @@
1
+ /**
2
+ * Pre-Execution Checks — Validate task plans before execution begins.
3
+ *
4
+ * Runs these checks against a slice's task plan:
5
+ * 1. Package existence — npm view calls in parallel with timeout
6
+ * 2. File path consistency — verify files exist or are in prior expected_output
7
+ * 3. Task ordering — detect impossible ordering (task reads file created later)
8
+ * 4. Interface contracts — detect contradictory function signatures (warn only)
9
+ *
10
+ * Design principles:
11
+ * - Pure functions taking (tasks: TaskRow[], basePath: string) for testability
12
+ * - Network failures warn, don't fail (R012 conservative design)
13
+ * - Total execution <2s target (R013)
14
+ * - No AST parsers — interface parsing is heuristic (regex on code blocks)
15
+ */
16
+ import { existsSync } from "node:fs";
17
+ import { spawn } from "node:child_process";
18
+ import { resolve } from "node:path";
19
+ // ─── Package Existence Check ─────────────────────────────────────────────────
20
+ /**
21
+ * Extract npm package names from task descriptions.
22
+ * Looks for:
23
+ * - `npm install <pkg>` patterns
24
+ * - Code blocks with `require('<pkg>')` or `import ... from '<pkg>'`
25
+ * - Explicit mentions like "uses lodash" or "package: axios"
26
+ */
27
+ export function extractPackageReferences(description) {
28
+ const packages = new Set();
29
+ // Common words that aren't package names but might appear after install
30
+ const stopwords = new Set([
31
+ "then", "and", "the", "to", "a", "an", "in", "for", "with", "from", "or",
32
+ "npm", "yarn", "pnpm", "i", // Don't capture the command itself
33
+ ]);
34
+ // npm install <pkg> patterns (handles npm i, npm add, yarn add, pnpm add)
35
+ // Use a global pattern to find all install commands, then parse following tokens
36
+ const installCmdPattern = /(?:npm\s+(?:install|i|add)|yarn\s+add|pnpm\s+add)\s+/g;
37
+ let cmdMatch;
38
+ while ((cmdMatch = installCmdPattern.exec(description)) !== null) {
39
+ // Start after the install command
40
+ const afterCmd = description.slice(cmdMatch.index + cmdMatch[0].length);
41
+ // Match package-like tokens (alphanumeric, @, /, -, _) until we hit
42
+ // something that's not a package (non-token char after whitespace)
43
+ const tokenPattern = /^([@a-zA-Z][a-zA-Z0-9@/_-]*)(?:\s+|$)/;
44
+ let remaining = afterCmd;
45
+ while (remaining.length > 0) {
46
+ // Skip any flags like -D, --save-dev
47
+ const flagMatch = remaining.match(/^(-[a-zA-Z-]+)\s*/);
48
+ if (flagMatch) {
49
+ remaining = remaining.slice(flagMatch[0].length);
50
+ continue;
51
+ }
52
+ // Try to match a package name
53
+ const pkgMatch = remaining.match(tokenPattern);
54
+ if (pkgMatch) {
55
+ const token = pkgMatch[1];
56
+ // Skip stopwords - they indicate end of package list
57
+ if (stopwords.has(token.toLowerCase())) {
58
+ break;
59
+ }
60
+ packages.add(normalizePackageName(token));
61
+ remaining = remaining.slice(pkgMatch[0].length);
62
+ }
63
+ else {
64
+ // Not a package name, stop parsing this install command
65
+ break;
66
+ }
67
+ }
68
+ }
69
+ // require('pkg') or import from 'pkg' in code blocks
70
+ const importPattern = /(?:require\s*\(\s*['"]|from\s+['"])([a-zA-Z0-9@/_-]+)['"\)]/g;
71
+ let importMatch;
72
+ while ((importMatch = importPattern.exec(description)) !== null) {
73
+ // Skip relative imports and node builtins
74
+ const pkg = importMatch[1];
75
+ if (!pkg.startsWith(".") && !pkg.startsWith("node:")) {
76
+ packages.add(normalizePackageName(pkg));
77
+ }
78
+ }
79
+ return Array.from(packages);
80
+ }
81
+ /**
82
+ * Normalize package name to registry-checkable form.
83
+ * Handles scoped packages (@org/pkg) and subpaths (pkg/subpath → pkg).
84
+ */
85
+ function normalizePackageName(raw) {
86
+ // Scoped package: @org/pkg or @org/pkg/subpath
87
+ if (raw.startsWith("@")) {
88
+ const parts = raw.split("/");
89
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : raw;
90
+ }
91
+ // Regular package: pkg or pkg/subpath
92
+ return raw.split("/")[0];
93
+ }
94
+ /**
95
+ * Check if a package exists on npm registry.
96
+ * Returns null on success, error message on failure.
97
+ * Times out after timeoutMs (default 5000ms).
98
+ */
99
+ async function checkPackageOnNpm(packageName, timeoutMs = 5000) {
100
+ return new Promise((resolve) => {
101
+ const child = spawn("npm", ["view", packageName, "name"], {
102
+ stdio: ["ignore", "pipe", "pipe"],
103
+ timeout: timeoutMs,
104
+ });
105
+ let stdout = "";
106
+ let stderr = "";
107
+ child.stdout.on("data", (data) => {
108
+ stdout += data.toString();
109
+ });
110
+ child.stderr.on("data", (data) => {
111
+ stderr += data.toString();
112
+ });
113
+ const timer = setTimeout(() => {
114
+ child.kill("SIGTERM");
115
+ resolve({ exists: false, error: `Timeout after ${timeoutMs}ms` });
116
+ }, timeoutMs);
117
+ child.on("close", (code) => {
118
+ clearTimeout(timer);
119
+ if (code === 0 && stdout.trim()) {
120
+ resolve({ exists: true });
121
+ }
122
+ else if (stderr.includes("404") || stderr.includes("not found")) {
123
+ resolve({ exists: false, error: `Package not found: ${packageName}` });
124
+ }
125
+ else if (code !== 0) {
126
+ // Network error or other issue — warn, don't fail
127
+ resolve({ exists: true, error: `npm view failed (code ${code}): ${stderr.slice(0, 100)}` });
128
+ }
129
+ else {
130
+ resolve({ exists: true });
131
+ }
132
+ });
133
+ child.on("error", (err) => {
134
+ clearTimeout(timer);
135
+ resolve({ exists: true, error: `npm spawn error: ${err.message}` });
136
+ });
137
+ });
138
+ }
139
+ /**
140
+ * Check all package references in tasks for existence on npm.
141
+ * Runs checks in parallel with a 5s timeout per package.
142
+ * Network failures warn but don't fail (R012 conservative design).
143
+ */
144
+ export async function checkPackageExistence(tasks, _basePath) {
145
+ const results = [];
146
+ const packagesToCheck = new Set();
147
+ // Collect all package references from task descriptions
148
+ for (const task of tasks) {
149
+ const packages = extractPackageReferences(task.description);
150
+ for (const pkg of packages) {
151
+ packagesToCheck.add(pkg);
152
+ }
153
+ }
154
+ if (packagesToCheck.size === 0) {
155
+ return results;
156
+ }
157
+ // Check packages in parallel
158
+ const checkPromises = Array.from(packagesToCheck).map(async (pkg) => {
159
+ const result = await checkPackageOnNpm(pkg);
160
+ return { pkg, result };
161
+ });
162
+ const checkResults = await Promise.all(checkPromises);
163
+ for (const { pkg, result } of checkResults) {
164
+ if (!result.exists && !result.error?.includes("Timeout") && !result.error?.includes("spawn error")) {
165
+ // Package genuinely doesn't exist — blocking failure
166
+ results.push({
167
+ category: "package",
168
+ target: pkg,
169
+ passed: false,
170
+ message: result.error || `Package '${pkg}' not found on npm`,
171
+ blocking: true,
172
+ });
173
+ }
174
+ else if (result.error) {
175
+ // Network issue or timeout — warn but don't block
176
+ results.push({
177
+ category: "package",
178
+ target: pkg,
179
+ passed: true,
180
+ message: `Warning: ${result.error}`,
181
+ blocking: false,
182
+ });
183
+ }
184
+ // Silent success for existing packages — no need to report
185
+ }
186
+ return results;
187
+ }
188
+ // ─── File Path Consistency Check ─────────────────────────────────────────────
189
+ /**
190
+ * Normalize a file path for consistent comparison.
191
+ * - Strips leading ./
192
+ * - Normalizes path separators to forward slashes
193
+ * - Resolves redundant segments (e.g., foo/../bar → bar)
194
+ *
195
+ * This ensures that "./src/a.ts", "src/a.ts", and "src//a.ts" all compare equal.
196
+ */
197
+ export function normalizeFilePath(filePath) {
198
+ if (!filePath)
199
+ return filePath;
200
+ // Normalize path separators to forward slashes
201
+ let normalized = filePath.replace(/\\/g, "/");
202
+ // Remove leading ./
203
+ while (normalized.startsWith("./")) {
204
+ normalized = normalized.slice(2);
205
+ }
206
+ // Remove duplicate slashes
207
+ normalized = normalized.replace(/\/+/g, "/");
208
+ // Remove trailing slash unless it's the root
209
+ if (normalized.length > 1 && normalized.endsWith("/")) {
210
+ normalized = normalized.slice(0, -1);
211
+ }
212
+ return normalized;
213
+ }
214
+ /**
215
+ * Build a set of files that will be created by tasks up to (but not including) taskIndex.
216
+ * All paths are normalized for consistent comparison.
217
+ */
218
+ function getExpectedOutputsUpTo(tasks, taskIndex) {
219
+ const outputs = new Set();
220
+ for (let i = 0; i < taskIndex; i++) {
221
+ for (const file of tasks[i].expected_output) {
222
+ outputs.add(normalizeFilePath(file));
223
+ }
224
+ }
225
+ return outputs;
226
+ }
227
+ /**
228
+ * Check that all files referenced in task.files and task.inputs either:
229
+ * 1. Exist on disk, OR
230
+ * 2. Are in a prior task's expected_output
231
+ *
232
+ * All paths are normalized before comparison to ensure ./src/a.ts matches src/a.ts.
233
+ */
234
+ export function checkFilePathConsistency(tasks, basePath) {
235
+ const results = [];
236
+ for (let i = 0; i < tasks.length; i++) {
237
+ const task = tasks[i];
238
+ const priorOutputs = getExpectedOutputsUpTo(tasks, i);
239
+ const filesToCheck = [...task.files, ...task.inputs];
240
+ for (const file of filesToCheck) {
241
+ // Skip empty strings
242
+ if (!file.trim())
243
+ continue;
244
+ // Normalize path for consistent comparison
245
+ const normalizedFile = normalizeFilePath(file);
246
+ // Check if file exists on disk
247
+ const absolutePath = resolve(basePath, normalizedFile);
248
+ const existsOnDisk = existsSync(absolutePath);
249
+ // Check if file is in prior expected outputs (priorOutputs already normalized)
250
+ const inPriorOutputs = priorOutputs.has(normalizedFile);
251
+ if (!existsOnDisk && !inPriorOutputs) {
252
+ results.push({
253
+ category: "file",
254
+ target: file,
255
+ passed: false,
256
+ message: `Task ${task.id} references '${file}' which doesn't exist and isn't created by prior tasks`,
257
+ blocking: true,
258
+ });
259
+ }
260
+ }
261
+ }
262
+ return results;
263
+ }
264
+ // ─── Task Ordering Check ─────────────────────────────────────────────────────
265
+ /**
266
+ * Detect impossible task ordering: task N reads a file that task N+M creates.
267
+ * This is a fatal error — the plan has an impossible dependency.
268
+ *
269
+ * All paths are normalized before comparison to ensure ./src/a.ts matches src/a.ts.
270
+ */
271
+ export function checkTaskOrdering(tasks, _basePath) {
272
+ const results = [];
273
+ // Build map: normalized file → task index that creates it
274
+ const fileCreators = new Map();
275
+ for (let i = 0; i < tasks.length; i++) {
276
+ const task = tasks[i];
277
+ for (const file of task.expected_output) {
278
+ const normalizedFile = normalizeFilePath(file);
279
+ if (!fileCreators.has(normalizedFile)) {
280
+ fileCreators.set(normalizedFile, { taskId: task.id, index: i, originalPath: file });
281
+ }
282
+ }
283
+ }
284
+ // Check each task's inputs against file creators
285
+ for (let i = 0; i < tasks.length; i++) {
286
+ const task = tasks[i];
287
+ const filesToCheck = [...task.files, ...task.inputs];
288
+ for (const file of filesToCheck) {
289
+ const normalizedFile = normalizeFilePath(file);
290
+ const creator = fileCreators.get(normalizedFile);
291
+ if (creator && creator.index > i) {
292
+ // Task reads file that is created later — impossible ordering
293
+ results.push({
294
+ category: "file",
295
+ target: file,
296
+ passed: false,
297
+ message: `Task ${task.id} reads '${file}' but it's created by task ${creator.taskId} (sequence violation)`,
298
+ blocking: true,
299
+ });
300
+ }
301
+ }
302
+ }
303
+ return results;
304
+ }
305
+ /**
306
+ * Extract function signatures from code blocks in task description.
307
+ * Uses heuristic regex — not an AST parser.
308
+ */
309
+ function extractFunctionSignatures(description, taskId) {
310
+ const signatures = [];
311
+ // Match code blocks (```...```)
312
+ const codeBlockPattern = /```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/g;
313
+ let blockMatch;
314
+ while ((blockMatch = codeBlockPattern.exec(description)) !== null) {
315
+ const codeBlock = blockMatch[1];
316
+ // Match function declarations and exports
317
+ // Patterns:
318
+ // function name(params): ReturnType
319
+ // export function name(params): ReturnType
320
+ // export async function name(params): Promise<ReturnType>
321
+ // const name = (params): ReturnType =>
322
+ // export const name = (params): ReturnType =>
323
+ const funcPattern = /(?:export\s+)?(?:async\s+)?(?:function\s+|const\s+)(\w+)(?:\s*=\s*)?\s*\(([^)]*)\)(?:\s*:\s*([^{=>\n]+))?/g;
324
+ let funcMatch;
325
+ while ((funcMatch = funcPattern.exec(codeBlock)) !== null) {
326
+ const [raw, name, params, returnType] = funcMatch;
327
+ signatures.push({
328
+ name,
329
+ params: normalizeParams(params),
330
+ returnType: normalizeType(returnType || "void"),
331
+ taskId,
332
+ raw: raw.trim(),
333
+ });
334
+ }
335
+ // Match interface method signatures
336
+ // Pattern: methodName(params): ReturnType;
337
+ const methodPattern = /^\s*(\w+)\s*\(([^)]*)\)\s*:\s*([^;]+);/gm;
338
+ let methodMatch;
339
+ while ((methodMatch = methodPattern.exec(codeBlock)) !== null) {
340
+ const [raw, name, params, returnType] = methodMatch;
341
+ signatures.push({
342
+ name,
343
+ params: normalizeParams(params),
344
+ returnType: normalizeType(returnType),
345
+ taskId,
346
+ raw: raw.trim(),
347
+ });
348
+ }
349
+ }
350
+ return signatures;
351
+ }
352
+ /**
353
+ * Normalize parameter list for comparison.
354
+ * Removes whitespace, comments, and default values.
355
+ */
356
+ function normalizeParams(params) {
357
+ return params
358
+ .replace(/\/\*[\s\S]*?\*\//g, "") // Remove block comments
359
+ .replace(/\/\/[^\n]*/g, "") // Remove line comments
360
+ .replace(/\s*=\s*[^,)]+/g, "") // Remove default values
361
+ .replace(/\s+/g, " ") // Normalize whitespace
362
+ .trim();
363
+ }
364
+ /**
365
+ * Normalize type for comparison.
366
+ */
367
+ function normalizeType(type) {
368
+ return type
369
+ .replace(/\s+/g, " ")
370
+ .trim();
371
+ }
372
+ /**
373
+ * Check for contradictory function signatures across tasks.
374
+ * Same function name with different signatures is a warning (not blocking).
375
+ */
376
+ export function checkInterfaceContracts(tasks, _basePath) {
377
+ const results = [];
378
+ // Collect all signatures
379
+ const allSignatures = [];
380
+ for (const task of tasks) {
381
+ const sigs = extractFunctionSignatures(task.description, task.id);
382
+ allSignatures.push(...sigs);
383
+ }
384
+ // Group by function name
385
+ const byName = new Map();
386
+ for (const sig of allSignatures) {
387
+ const existing = byName.get(sig.name) || [];
388
+ existing.push(sig);
389
+ byName.set(sig.name, existing);
390
+ }
391
+ // Check for contradictions
392
+ for (const [name, sigs] of byName) {
393
+ if (sigs.length < 2)
394
+ continue;
395
+ // Compare signatures
396
+ const first = sigs[0];
397
+ for (let i = 1; i < sigs.length; i++) {
398
+ const current = sigs[i];
399
+ // Check parameter mismatch
400
+ if (first.params !== current.params) {
401
+ results.push({
402
+ category: "schema",
403
+ target: name,
404
+ passed: true, // Warning only, not blocking
405
+ message: `Function '${name}' has different parameters: '${first.params}' (${first.taskId}) vs '${current.params}' (${current.taskId})`,
406
+ blocking: false,
407
+ });
408
+ }
409
+ // Check return type mismatch
410
+ if (first.returnType !== current.returnType) {
411
+ results.push({
412
+ category: "schema",
413
+ target: name,
414
+ passed: true, // Warning only, not blocking
415
+ message: `Function '${name}' has different return types: '${first.returnType}' (${first.taskId}) vs '${current.returnType}' (${current.taskId})`,
416
+ blocking: false,
417
+ });
418
+ }
419
+ }
420
+ }
421
+ return results;
422
+ }
423
+ // ─── Main Entry Point ────────────────────────────────────────────────────────
424
+ /**
425
+ * Run all pre-execution checks against a slice's task plan.
426
+ *
427
+ * @param tasks - Array of TaskRow from the slice
428
+ * @param basePath - Base path for resolving file references
429
+ * @returns PreExecutionResult with status, checks, and duration
430
+ */
431
+ export async function runPreExecutionChecks(tasks, basePath) {
432
+ const startTime = Date.now();
433
+ const allChecks = [];
434
+ // Run sync checks first
435
+ const fileChecks = checkFilePathConsistency(tasks, basePath);
436
+ const orderingChecks = checkTaskOrdering(tasks, basePath);
437
+ const contractChecks = checkInterfaceContracts(tasks, basePath);
438
+ allChecks.push(...fileChecks, ...orderingChecks, ...contractChecks);
439
+ // Run async package checks
440
+ const packageChecks = await checkPackageExistence(tasks, basePath);
441
+ allChecks.push(...packageChecks);
442
+ const durationMs = Date.now() - startTime;
443
+ // Determine overall status
444
+ const hasBlockingFailure = allChecks.some((c) => !c.passed && c.blocking);
445
+ const hasNonBlockingFailure = allChecks.some((c) => !c.passed && !c.blocking);
446
+ // Interface contract checks pass but still report warnings via message
447
+ const hasInterfaceWarning = allChecks.some((c) => c.category === "schema" && c.message && !c.message.startsWith("Warning:"));
448
+ const hasNetworkWarning = allChecks.some((c) => c.passed && c.message?.startsWith("Warning:"));
449
+ let status;
450
+ if (hasBlockingFailure) {
451
+ status = "fail";
452
+ }
453
+ else if (hasNonBlockingFailure || hasInterfaceWarning || hasNetworkWarning) {
454
+ status = "warn";
455
+ }
456
+ else {
457
+ status = "pass";
458
+ }
459
+ return {
460
+ status,
461
+ checks: allChecks,
462
+ durationMs,
463
+ };
464
+ }
@@ -74,6 +74,12 @@ export const KNOWN_PREFERENCE_KEYS = new Set([
74
74
  "context_management",
75
75
  "experimental",
76
76
  "codebase",
77
+ "slice_parallel",
78
+ "safety_harness",
79
+ "enhanced_verification",
80
+ "enhanced_verification_pre",
81
+ "enhanced_verification_post",
82
+ "enhanced_verification_strict",
77
83
  ]);
78
84
  /** Canonical list of all dispatch unit types. */
79
85
  export const KNOWN_UNIT_TYPES = [
@@ -939,5 +939,38 @@ export function validatePreferences(preferences) {
939
939
  errors.push("codebase must be an object");
940
940
  }
941
941
  }
942
+ // ─── Enhanced Verification ──────────────────────────────────────────────────
943
+ if (preferences.enhanced_verification !== undefined) {
944
+ if (typeof preferences.enhanced_verification === "boolean") {
945
+ validated.enhanced_verification = preferences.enhanced_verification;
946
+ }
947
+ else {
948
+ errors.push("enhanced_verification must be a boolean");
949
+ }
950
+ }
951
+ if (preferences.enhanced_verification_pre !== undefined) {
952
+ if (typeof preferences.enhanced_verification_pre === "boolean") {
953
+ validated.enhanced_verification_pre = preferences.enhanced_verification_pre;
954
+ }
955
+ else {
956
+ errors.push("enhanced_verification_pre must be a boolean");
957
+ }
958
+ }
959
+ if (preferences.enhanced_verification_post !== undefined) {
960
+ if (typeof preferences.enhanced_verification_post === "boolean") {
961
+ validated.enhanced_verification_post = preferences.enhanced_verification_post;
962
+ }
963
+ else {
964
+ errors.push("enhanced_verification_post must be a boolean");
965
+ }
966
+ }
967
+ if (preferences.enhanced_verification_strict !== undefined) {
968
+ if (typeof preferences.enhanced_verification_strict === "boolean") {
969
+ validated.enhanced_verification_strict = preferences.enhanced_verification_strict;
970
+ }
971
+ else {
972
+ errors.push("enhanced_verification_strict must be a boolean");
973
+ }
974
+ }
942
975
  return { preferences: validated, errors, warnings };
943
976
  }
@@ -148,9 +148,11 @@ export function parsePreferencesMarkdown(content) {
148
148
  if (/^##\s+\w/m.test(content)) {
149
149
  return parseHeadingListFormat(content);
150
150
  }
151
- if (!_warnedUnrecognizedFormat) {
151
+ // Warn when a non-empty file exists but lacks frontmatter delimiters (#2036).
152
+ if (content.trim().length > 0 && !_warnedUnrecognizedFormat) {
152
153
  _warnedUnrecognizedFormat = true;
153
- console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping.");
154
+ console.warn("[GSD] Warning: preferences file has unrecognized format — content does not use YAML frontmatter delimiters (---). " +
155
+ "Wrap your preferences in --- fences. See https://github.com/gsd-build/gsd-2/issues/2036");
154
156
  }
155
157
  return null;
156
158
  }
@@ -280,6 +282,10 @@ function mergePreferences(base, override) {
280
282
  verification_commands: mergeStringLists(base.verification_commands, override.verification_commands),
281
283
  verification_auto_fix: override.verification_auto_fix ?? base.verification_auto_fix,
282
284
  verification_max_retries: override.verification_max_retries ?? base.verification_max_retries,
285
+ enhanced_verification: override.enhanced_verification ?? base.enhanced_verification,
286
+ enhanced_verification_pre: override.enhanced_verification_pre ?? base.enhanced_verification_pre,
287
+ enhanced_verification_post: override.enhanced_verification_post ?? base.enhanced_verification_post,
288
+ enhanced_verification_strict: override.enhanced_verification_strict ?? base.enhanced_verification_strict,
283
289
  search_provider: override.search_provider ?? base.search_provider,
284
290
  context_selection: override.context_selection ?? base.context_selection,
285
291
  auto_visualize: override.auto_visualize ?? base.auto_visualize,
@@ -301,6 +307,9 @@ function mergePreferences(base, override) {
301
307
  ].filter(Boolean),
302
308
  }
303
309
  : undefined,
310
+ slice_parallel: (base.slice_parallel || override.slice_parallel)
311
+ ? { ...(base.slice_parallel ?? {}), ...(override.slice_parallel ?? {}) }
312
+ : undefined,
304
313
  };
305
314
  }
306
315
  function mergeStringLists(base, override) {
@@ -47,6 +47,13 @@ function resolveExtensionDir() {
47
47
  const __extensionDir = resolveExtensionDir();
48
48
  const promptsDir = join(__extensionDir, "prompts");
49
49
  const templatesDir = join(__extensionDir, "templates");
50
+ /**
51
+ * Return the resolved templates directory path for use in prompts.
52
+ * Avoids hardcoding `~/.gsd/agent/extensions/gsd/templates/` in templates. (#3575)
53
+ */
54
+ export function getTemplatesDir() {
55
+ return templatesDir;
56
+ }
50
57
  // Cache all templates eagerly at module load — a running session uses the
51
58
  // template versions that were on disk at startup, immune to later overwrites.
52
59
  const templateCache = new Map();
@@ -24,6 +24,8 @@ Then:
24
24
  7. Fill the **Decision Re-evaluation** table in the milestone summary. For each key decision from `.gsd/DECISIONS.md` made during this milestone, evaluate whether it is still valid given what was actually built. Flag decisions that should be revisited next milestone.
25
25
  8. Validate **requirement status transitions**. For each requirement that changed status during this milestone, confirm the transition is supported by evidence. Requirements can move between Active, Validated, Deferred, Blocked, or Out of Scope — but only with proof.
26
26
 
27
+ **DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')` — the engine owns the WAL connection. Use `gsd_milestone_status` to read milestone and slice state. All data you need is already inlined in the context above or accessible via the `gsd_*` tools — never via direct SQL.
28
+
27
29
  ### Verification Gate — STOP if verification failed
28
30
 
29
31
  **If ANY verification failure was recorded in steps 3, 4, or 5, you MUST follow the failure path below. Do NOT proceed to step 9.**
@@ -35,6 +35,8 @@ Then:
35
35
 
36
36
  **Autonomous execution:** Do not call `ask_user_questions` or `secure_env_collect`. You are running in auto-mode — there is no human available to answer questions. Make reasonable assumptions and document them in the slice summary. If a decision genuinely requires human input, note it in the summary and proceed with the best available option.
37
37
 
38
+ **File system safety:** Task summaries are preloaded in the inlined context above. If you need to re-read any of them, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` to list file paths first — never pass `{{slicePath}}` or any other directory path directly to the `read` tool. The `read` tool only accepts file paths, not directories.
39
+
38
40
  **You MUST call `gsd_complete_slice` with the slice summary and UAT content before finishing. The tool persists to both DB and disk and renders `{{sliceSummaryPath}}` and `{{sliceUatPath}}` automatically.**
39
41
 
40
42
  When done, say: "Slice {{sliceId}} complete."
@@ -9,6 +9,7 @@ Rules:
9
9
  4. For missing summaries or UAT files, generate the real artifact from existing slice/task context when possible — do not leave placeholders if you can reconstruct the real content.
10
10
  5. After each repair cluster, verify the relevant invariant directly from disk.
11
11
  6. When done, rerun `/gsd doctor {{doctorCommandSuffix}}` mentally by ensuring the remaining issue set for this scope is reduced or cleared.
12
+ 7. Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')` — use `gsd_milestone_status` to inspect DB state. Direct access bypasses the WAL connection owned by the engine and can corrupt in-flight writes.
12
13
 
13
14
  ## Doctor Summary
14
15
 
@@ -116,6 +116,8 @@ A unit dispatched more than once (`type/id` appears multiple times) indicates a
116
116
 
117
117
  5. **Read the actual GSD source code** at `{{gsdSourceDir}}` to confirm or deny each hypothesis. Do not guess what code does — read it.
118
118
 
119
+ **DB inspection:** If you need to check DB state as part of investigation, use `gsd_milestone_status` — never run `sqlite3 .gsd/gsd.db` or `node -e require('better-sqlite3')` directly. The engine holds a WAL write lock; direct access will either fail or return stale data.
120
+
119
121
  6. **Trace the code path** from the entry point (usually `auto-loop.ts` dispatch or `auto-dispatch.ts`) through to the failure point. Follow function calls across files.
120
122
 
121
123
  7. **Identify the specific file and line** where the bug lives. Determine what kind of defect it is:
@@ -63,4 +63,6 @@ If `.gsd/REQUIREMENTS.md` exists and requirement ownership or status changed, up
63
63
 
64
64
  {{commitInstruction}}
65
65
 
66
+ **DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')`. Use `gsd_milestone_status` to read current milestone and slice state. All roadmap mutations go through `gsd_reassess_roadmap` — the tool writes to the DB and re-renders ROADMAP.md atomically.
67
+
66
68
  When done, say: "Roadmap reassessed."
@@ -24,13 +24,9 @@ Leave the project in a state where the next agent can immediately understand wha
24
24
 
25
25
  ## Skills
26
26
 
27
- GSD ships with bundled skills. Load the relevant skill file with the `read` tool before starting work when the task matches.
27
+ GSD ships with bundled skills. Load the relevant skill file with the `read` tool before starting work when the task matches. Use bare skill names — GSD resolves them to the correct path automatically.
28
28
 
29
- | Trigger | Skill to load |
30
- |---|---|
31
- | Frontend UI - web components, pages, landing pages, dashboards, React/HTML/CSS, styling | `~/.gsd/agent/skills/frontend-design/SKILL.md` |
32
- | macOS or iOS apps - SwiftUI, Xcode, App Store | `~/.gsd/agent/skills/swiftui/SKILL.md` |
33
- | Debugging - complex bugs, failing tests, root-cause investigation after standard approaches fail | `~/.gsd/agent/skills/debug-like-expert/SKILL.md` |
29
+ {{bundledSkillsTable}}
34
30
 
35
31
  ## Hard Rules
36
32
 
@@ -119,7 +115,7 @@ In all modes, slices commit sequentially on the active branch; there are no per-
119
115
  ### Artifact Templates
120
116
 
121
117
  Templates showing the expected format for each artifact type are in:
122
- `~/.gsd/agent/extensions/gsd/templates/`
118
+ `{{templatesDir}}`
123
119
 
124
120
  **Always read the relevant template before writing an artifact** to match the expected structure exactly. The parsers that read these files depend on specific formatting:
125
121
 
@@ -175,6 +171,7 @@ Templates showing the expected format for each artifact type are in:
175
171
  - Never guess at library APIs from training data — use `get_library_docs`.
176
172
  - Never ask the user to run a command, set a variable, or check something you can check yourself.
177
173
  - Never await stale async jobs after editing source — `cancel_job` them first, then re-run.
174
+ - Never query `.gsd/gsd.db` directly via `sqlite3`, `better-sqlite3`, or `node -e require('better-sqlite3')` — the database uses a single-writer WAL connection managed by the engine. Direct access causes reader/writer conflicts and bypasses validation logic. Use `gsd_milestone_status`, `gsd_journal_query`, or other `gsd_*` tools exclusively for all DB reads and writes.
178
175
 
179
176
  ### Ask vs infer
180
177
 
@@ -38,6 +38,8 @@ All relevant context has been preloaded below — the roadmap, all slice summari
38
38
 
39
39
  **Persist validation results through `gsd_validate_milestone`.** Call it with: `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verificationClasses` (when non-empty), `verdictRationale`, and `remediationPlan` (if verdict is `needs-remediation`). The tool writes the validation to the DB and renders VALIDATION.md to disk.
40
40
 
41
+ **DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')` — the engine owns the WAL connection. Use `gsd_milestone_status` to read milestone and slice state. All data you need is already inlined in the context above or accessible via the `gsd_*` tools. Direct DB access corrupts the WAL and bypasses tool-level validation.
42
+
41
43
  If verdict is `needs-remediation`:
42
44
  - After calling `gsd_validate_milestone`, use `gsd_reassess_roadmap` to add remediation slices. Pass `milestoneId`, a synthetic `completedSliceId` (e.g. "VALIDATION"), `verdict: "roadmap-adjusted"`, `assessment` text, and `sliceChanges` with the new slices in the `added` array. The tool persists the changes to the DB and re-renders ROADMAP.md.
43
45
  - These remediation slices will be planned and executed before validation re-runs.
@@ -31,7 +31,7 @@ export function markSliceDoneInRoadmap(basePath, mid, sid) {
31
31
  if (updated === content) {
32
32
  updated = content.replace(new RegExp(`^(#{1,4}\\s+(?:\\*{0,2})(?:Slice\\s+)?${sid}\\*{0,2}[:\\s.\\u2014\\u2013-]+\\s*)(.+)`, "m"), (match, prefix, title) => {
33
33
  // Already marked done — no-op
34
- if (/^\u2713/.test(title) || /\(Complete\)\s*$/i.test(title))
34
+ if (/^[\u2713\u2705]/.test(title) || /[\u2705]\s*$/.test(title) || /\(Complete\)\s*$/i.test(title))
35
35
  return match;
36
36
  return `${prefix}\u2713 ${title}`;
37
37
  });