gsd-pi 2.74.0-dev.b741afb → 2.74.0-dev.b7c5f96

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 (430) hide show
  1. package/dist/resources/extensions/gsd/activity-log.js +16 -0
  2. package/dist/resources/extensions/gsd/auto/loop.js +147 -10
  3. package/dist/resources/extensions/gsd/auto/phases.js +164 -9
  4. package/dist/resources/extensions/gsd/auto/session.js +10 -0
  5. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -1
  6. package/dist/resources/extensions/gsd/auto-model-selection.js +54 -8
  7. package/dist/resources/extensions/gsd/auto-post-unit.js +215 -8
  8. package/dist/resources/extensions/gsd/auto-recovery.js +24 -10
  9. package/dist/resources/extensions/gsd/auto-unit-closeout.js +18 -0
  10. package/dist/resources/extensions/gsd/auto-verification.js +100 -2
  11. package/dist/resources/extensions/gsd/auto-worktree.js +2 -0
  12. package/dist/resources/extensions/gsd/auto.js +28 -2
  13. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -3
  14. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +10 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +61 -9
  16. package/dist/resources/extensions/gsd/cache.js +16 -5
  17. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  18. package/dist/resources/extensions/gsd/commands/handlers/core.js +5 -1
  19. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  20. package/dist/resources/extensions/gsd/commands-extract-learnings.js +225 -0
  21. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +51 -4
  22. package/dist/resources/extensions/gsd/docs/preferences-reference.md +16 -1
  23. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +144 -0
  24. package/dist/resources/extensions/gsd/ecosystem/loader.js +145 -0
  25. package/dist/resources/extensions/gsd/git-service.js +49 -1
  26. package/dist/resources/extensions/gsd/graph-context.js +98 -7
  27. package/dist/resources/extensions/gsd/gsd-db.js +260 -2
  28. package/dist/resources/extensions/gsd/guided-flow.js +31 -6
  29. package/dist/resources/extensions/gsd/init-wizard.js +1 -0
  30. package/dist/resources/extensions/gsd/journal.js +27 -0
  31. package/dist/resources/extensions/gsd/metrics.js +19 -0
  32. package/dist/resources/extensions/gsd/parallel-orchestrator.js +33 -1
  33. package/dist/resources/extensions/gsd/preferences-models.js +20 -3
  34. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  35. package/dist/resources/extensions/gsd/preferences-validation.js +118 -2
  36. package/dist/resources/extensions/gsd/preferences.js +31 -0
  37. package/dist/resources/extensions/gsd/safety/evidence-collector.js +15 -30
  38. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +12 -2
  39. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +19 -0
  40. package/dist/resources/extensions/gsd/tools/complete-slice.js +5 -0
  41. package/dist/resources/extensions/gsd/tools/validate-milestone.js +39 -4
  42. package/dist/resources/extensions/gsd/unit-ownership.js +1 -1
  43. package/dist/resources/extensions/gsd/uok/audit-toggle.js +7 -0
  44. package/dist/resources/extensions/gsd/uok/audit.js +40 -0
  45. package/dist/resources/extensions/gsd/uok/contracts.js +1 -0
  46. package/dist/resources/extensions/gsd/uok/execution-graph.js +179 -0
  47. package/dist/resources/extensions/gsd/uok/flags.js +29 -0
  48. package/dist/resources/extensions/gsd/uok/gate-runner.js +109 -0
  49. package/dist/resources/extensions/gsd/uok/gitops.js +53 -0
  50. package/dist/resources/extensions/gsd/uok/kernel.js +80 -0
  51. package/dist/resources/extensions/gsd/uok/loop-adapter.js +133 -0
  52. package/dist/resources/extensions/gsd/uok/model-policy.js +66 -0
  53. package/dist/resources/extensions/gsd/uok/plan-v2.js +132 -0
  54. package/dist/resources/extensions/gsd/workflow-logger.js +22 -0
  55. package/dist/resources/extensions/ttsr/ttsr-manager.js +3 -1
  56. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  57. package/dist/web/standalone/.next/BUILD_ID +1 -1
  58. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  59. package/dist/web/standalone/.next/build-manifest.json +2 -2
  60. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  61. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/index.html +1 -1
  83. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  90. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  91. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  93. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  94. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  95. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  96. package/package.json +1 -1
  97. package/packages/mcp-server/dist/readers/graph.d.ts +1 -1
  98. package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -1
  99. package/packages/mcp-server/dist/readers/graph.js +107 -0
  100. package/packages/mcp-server/dist/readers/graph.js.map +1 -1
  101. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  102. package/packages/mcp-server/dist/workflow-tools.js +88 -6
  103. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  104. package/packages/mcp-server/src/readers/graph.test.ts +178 -0
  105. package/packages/mcp-server/src/readers/graph.ts +148 -1
  106. package/packages/mcp-server/src/workflow-tools.ts +95 -10
  107. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  108. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  109. package/packages/pi-ai/dist/index.d.ts +1 -9
  110. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  111. package/packages/pi-ai/dist/index.js +1 -9
  112. package/packages/pi-ai/dist/index.js.map +1 -1
  113. package/packages/pi-ai/dist/models/capability-patches.d.ts +19 -0
  114. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -0
  115. package/packages/pi-ai/dist/models/capability-patches.js +36 -0
  116. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -0
  117. package/packages/pi-ai/dist/{models.custom.d.ts → models/custom.d.ts} +1 -1
  118. package/packages/pi-ai/dist/models/custom.d.ts.map +1 -0
  119. package/packages/pi-ai/dist/{models.custom.js → models/custom.js} +4 -4
  120. package/packages/pi-ai/dist/models/custom.js.map +1 -0
  121. package/packages/pi-ai/dist/models/generated/amazon-bedrock.d.ts +1482 -0
  122. package/packages/pi-ai/dist/models/generated/amazon-bedrock.d.ts.map +1 -0
  123. package/packages/pi-ai/dist/models/generated/amazon-bedrock.js +1484 -0
  124. package/packages/pi-ai/dist/models/generated/amazon-bedrock.js.map +1 -0
  125. package/packages/pi-ai/dist/models/generated/anthropic.d.ts +377 -0
  126. package/packages/pi-ai/dist/models/generated/anthropic.d.ts.map +1 -0
  127. package/packages/pi-ai/dist/models/generated/anthropic.js +379 -0
  128. package/packages/pi-ai/dist/models/generated/anthropic.js.map +1 -0
  129. package/packages/pi-ai/dist/models/generated/azure-openai-responses.d.ts +700 -0
  130. package/packages/pi-ai/dist/models/generated/azure-openai-responses.d.ts.map +1 -0
  131. package/packages/pi-ai/dist/models/generated/azure-openai-responses.js +702 -0
  132. package/packages/pi-ai/dist/models/generated/azure-openai-responses.js.map +1 -0
  133. package/packages/pi-ai/dist/models/generated/cerebras.d.ts +71 -0
  134. package/packages/pi-ai/dist/models/generated/cerebras.d.ts.map +1 -0
  135. package/packages/pi-ai/dist/models/generated/cerebras.js +73 -0
  136. package/packages/pi-ai/dist/models/generated/cerebras.js.map +1 -0
  137. package/packages/pi-ai/dist/models/generated/github-copilot.d.ts +590 -0
  138. package/packages/pi-ai/dist/models/generated/github-copilot.d.ts.map +1 -0
  139. package/packages/pi-ai/dist/models/generated/github-copilot.js +444 -0
  140. package/packages/pi-ai/dist/models/generated/github-copilot.js.map +1 -0
  141. package/packages/pi-ai/dist/models/generated/google-antigravity.d.ts +156 -0
  142. package/packages/pi-ai/dist/models/generated/google-antigravity.d.ts.map +1 -0
  143. package/packages/pi-ai/dist/models/generated/google-antigravity.js +158 -0
  144. package/packages/pi-ai/dist/models/generated/google-antigravity.js.map +1 -0
  145. package/packages/pi-ai/dist/models/generated/google-gemini-cli.d.ts +105 -0
  146. package/packages/pi-ai/dist/models/generated/google-gemini-cli.d.ts.map +1 -0
  147. package/packages/pi-ai/dist/models/generated/google-gemini-cli.js +107 -0
  148. package/packages/pi-ai/dist/models/generated/google-gemini-cli.js.map +1 -0
  149. package/packages/pi-ai/dist/models/generated/google-vertex.d.ts +207 -0
  150. package/packages/pi-ai/dist/models/generated/google-vertex.d.ts.map +1 -0
  151. package/packages/pi-ai/dist/models/generated/google-vertex.js +209 -0
  152. package/packages/pi-ai/dist/models/generated/google-vertex.js.map +1 -0
  153. package/packages/pi-ai/dist/models/generated/google.d.ts +462 -0
  154. package/packages/pi-ai/dist/models/generated/google.d.ts.map +1 -0
  155. package/packages/pi-ai/dist/models/generated/google.js +464 -0
  156. package/packages/pi-ai/dist/models/generated/google.js.map +1 -0
  157. package/packages/pi-ai/dist/models/generated/groq.d.ts +309 -0
  158. package/packages/pi-ai/dist/models/generated/groq.d.ts.map +1 -0
  159. package/packages/pi-ai/dist/models/generated/groq.js +311 -0
  160. package/packages/pi-ai/dist/models/generated/groq.js.map +1 -0
  161. package/packages/pi-ai/dist/models/generated/huggingface.d.ts +383 -0
  162. package/packages/pi-ai/dist/models/generated/huggingface.d.ts.map +1 -0
  163. package/packages/pi-ai/dist/models/generated/huggingface.js +347 -0
  164. package/packages/pi-ai/dist/models/generated/huggingface.js.map +1 -0
  165. package/packages/pi-ai/dist/{models.generated.d.ts → models/generated/index.d.ts} +1 -1
  166. package/packages/pi-ai/dist/{models.generated.d.ts.map → models/generated/index.d.ts.map} +1 -1
  167. package/packages/pi-ai/dist/models/generated/index.js +51 -0
  168. package/packages/pi-ai/dist/models/generated/index.js.map +1 -0
  169. package/packages/pi-ai/dist/models/generated/kimi-coding.d.ts +37 -0
  170. package/packages/pi-ai/dist/models/generated/kimi-coding.d.ts.map +1 -0
  171. package/packages/pi-ai/dist/models/generated/kimi-coding.js +39 -0
  172. package/packages/pi-ai/dist/models/generated/kimi-coding.js.map +1 -0
  173. package/packages/pi-ai/dist/models/generated/minimax-cn.d.ts +105 -0
  174. package/packages/pi-ai/dist/models/generated/minimax-cn.d.ts.map +1 -0
  175. package/packages/pi-ai/dist/models/generated/minimax-cn.js +107 -0
  176. package/packages/pi-ai/dist/models/generated/minimax-cn.js.map +1 -0
  177. package/packages/pi-ai/dist/models/generated/minimax.d.ts +105 -0
  178. package/packages/pi-ai/dist/models/generated/minimax.d.ts.map +1 -0
  179. package/packages/pi-ai/dist/models/generated/minimax.js +107 -0
  180. package/packages/pi-ai/dist/models/generated/minimax.js.map +1 -0
  181. package/packages/pi-ai/dist/models/generated/mistral.d.ts +445 -0
  182. package/packages/pi-ai/dist/models/generated/mistral.d.ts.map +1 -0
  183. package/packages/pi-ai/dist/models/generated/mistral.js +447 -0
  184. package/packages/pi-ai/dist/models/generated/mistral.js.map +1 -0
  185. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +139 -0
  186. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -0
  187. package/packages/pi-ai/dist/models/generated/openai-codex.js +141 -0
  188. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -0
  189. package/packages/pi-ai/dist/models/generated/openai.d.ts +700 -0
  190. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -0
  191. package/packages/pi-ai/dist/models/generated/openai.js +702 -0
  192. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -0
  193. package/packages/pi-ai/dist/models/generated/opencode-go.d.ts +122 -0
  194. package/packages/pi-ai/dist/models/generated/opencode-go.d.ts.map +1 -0
  195. package/packages/pi-ai/dist/models/generated/opencode-go.js +124 -0
  196. package/packages/pi-ai/dist/models/generated/opencode-go.js.map +1 -0
  197. package/packages/pi-ai/dist/models/generated/opencode.d.ts +530 -0
  198. package/packages/pi-ai/dist/models/generated/opencode.d.ts.map +1 -0
  199. package/packages/pi-ai/dist/models/generated/opencode.js +532 -0
  200. package/packages/pi-ai/dist/models/generated/opencode.js.map +1 -0
  201. package/packages/pi-ai/dist/models/generated/openrouter.d.ts +4270 -0
  202. package/packages/pi-ai/dist/models/generated/openrouter.d.ts.map +1 -0
  203. package/packages/pi-ai/dist/models/generated/openrouter.js +4272 -0
  204. package/packages/pi-ai/dist/models/generated/openrouter.js.map +1 -0
  205. package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.d.ts +2604 -0
  206. package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.d.ts.map +1 -0
  207. package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.js +2606 -0
  208. package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.js.map +1 -0
  209. package/packages/pi-ai/dist/models/generated/xai.d.ts +411 -0
  210. package/packages/pi-ai/dist/models/generated/xai.d.ts.map +1 -0
  211. package/packages/pi-ai/dist/models/generated/xai.js +413 -0
  212. package/packages/pi-ai/dist/models/generated/xai.js.map +1 -0
  213. package/packages/pi-ai/dist/models/generated/zai.d.ts +276 -0
  214. package/packages/pi-ai/dist/models/generated/zai.d.ts.map +1 -0
  215. package/packages/pi-ai/dist/models/generated/zai.js +239 -0
  216. package/packages/pi-ai/dist/models/generated/zai.js.map +1 -0
  217. package/packages/pi-ai/dist/models/index.d.ts +27 -0
  218. package/packages/pi-ai/dist/models/index.d.ts.map +1 -0
  219. package/packages/pi-ai/dist/models/index.js +80 -0
  220. package/packages/pi-ai/dist/models/index.js.map +1 -0
  221. package/packages/pi-ai/dist/models.d.ts +1 -36
  222. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  223. package/packages/pi-ai/dist/models.generated.test.js +1 -2
  224. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  225. package/packages/pi-ai/dist/models.js +3 -112
  226. package/packages/pi-ai/dist/models.js.map +1 -1
  227. package/packages/pi-ai/dist/models.test.js +6 -5
  228. package/packages/pi-ai/dist/models.test.js.map +1 -1
  229. package/packages/pi-ai/scripts/generate-models.ts +74 -40
  230. package/packages/pi-ai/src/index.ts +1 -9
  231. package/packages/pi-ai/src/models/capability-patches.ts +40 -0
  232. package/packages/pi-ai/src/{models.custom.ts → models/custom.ts} +4 -4
  233. package/packages/pi-ai/src/models/generated/amazon-bedrock.ts +1486 -0
  234. package/packages/pi-ai/src/models/generated/anthropic.ts +381 -0
  235. package/packages/pi-ai/src/models/generated/azure-openai-responses.ts +704 -0
  236. package/packages/pi-ai/src/models/generated/cerebras.ts +75 -0
  237. package/packages/pi-ai/src/models/generated/github-copilot.ts +446 -0
  238. package/packages/pi-ai/src/models/generated/google-antigravity.ts +160 -0
  239. package/packages/pi-ai/src/models/generated/google-gemini-cli.ts +109 -0
  240. package/packages/pi-ai/src/models/generated/google-vertex.ts +211 -0
  241. package/packages/pi-ai/src/models/generated/google.ts +466 -0
  242. package/packages/pi-ai/src/models/generated/groq.ts +313 -0
  243. package/packages/pi-ai/src/models/generated/huggingface.ts +349 -0
  244. package/packages/pi-ai/src/models/generated/index.ts +52 -0
  245. package/packages/pi-ai/src/models/generated/kimi-coding.ts +41 -0
  246. package/packages/pi-ai/src/models/generated/minimax-cn.ts +109 -0
  247. package/packages/pi-ai/src/models/generated/minimax.ts +109 -0
  248. package/packages/pi-ai/src/models/generated/mistral.ts +449 -0
  249. package/packages/pi-ai/src/models/generated/openai-codex.ts +143 -0
  250. package/packages/pi-ai/src/models/generated/openai.ts +704 -0
  251. package/packages/pi-ai/src/models/generated/opencode-go.ts +126 -0
  252. package/packages/pi-ai/src/models/generated/opencode.ts +534 -0
  253. package/packages/pi-ai/src/models/generated/openrouter.ts +4274 -0
  254. package/packages/pi-ai/src/models/generated/vercel-ai-gateway.ts +2608 -0
  255. package/packages/pi-ai/src/models/generated/xai.ts +415 -0
  256. package/packages/pi-ai/src/models/generated/zai.ts +241 -0
  257. package/packages/pi-ai/src/models/index.ts +106 -0
  258. package/packages/pi-ai/src/models.generated.test.ts +1 -2
  259. package/packages/pi-ai/src/models.test.ts +6 -5
  260. package/packages/pi-ai/src/models.ts +3 -153
  261. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  262. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  263. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -2
  264. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  265. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +472 -0
  266. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  267. package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.d.ts +2 -0
  268. package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.d.ts.map +1 -0
  269. package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.js +52 -0
  270. package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.js.map +1 -0
  271. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  272. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  273. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  274. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.d.ts +2 -0
  275. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.d.ts.map +1 -0
  276. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +61 -0
  277. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -0
  278. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +11 -0
  279. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  280. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +1 -0
  281. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +23 -9
  283. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  284. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +11 -0
  285. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -0
  286. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +53 -0
  287. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -0
  288. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts +8 -5
  289. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  290. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +27 -13
  291. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +8 -0
  293. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  294. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +68 -8
  295. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +22 -22
  298. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  299. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  300. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +232 -18
  301. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.d.ts +2 -0
  303. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.d.ts.map +1 -0
  304. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js +38 -0
  305. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js.map +1 -0
  306. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +14 -0
  307. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  308. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +70 -6
  309. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  310. package/packages/pi-coding-agent/src/core/agent-session.ts +12 -6
  311. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +612 -0
  312. package/packages/pi-coding-agent/src/core/model-registry-env-fallback.test.ts +59 -0
  313. package/packages/pi-coding-agent/src/core/model-registry.ts +2 -1
  314. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +92 -0
  315. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +19 -0
  316. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +25 -10
  317. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +75 -0
  318. package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +36 -15
  319. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +83 -7
  320. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +23 -26
  321. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +298 -41
  322. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-ordering.test.ts +44 -0
  323. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +92 -6
  324. package/packages/pi-coding-agent/src/types/ambient-modules.d.ts +69 -0
  325. package/packages/pi-coding-agent/tsconfig.json +2 -2
  326. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  327. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  328. package/packages/pi-tui/dist/tui.js +9 -2
  329. package/packages/pi-tui/dist/tui.js.map +1 -1
  330. package/packages/pi-tui/src/tui.ts +9 -1
  331. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  332. package/src/resources/extensions/gsd/activity-log.ts +21 -0
  333. package/src/resources/extensions/gsd/auto/loop-deps.ts +4 -0
  334. package/src/resources/extensions/gsd/auto/loop.ts +159 -10
  335. package/src/resources/extensions/gsd/auto/phases.ts +193 -9
  336. package/src/resources/extensions/gsd/auto/session.ts +10 -0
  337. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -6
  338. package/src/resources/extensions/gsd/auto-model-selection.ts +69 -8
  339. package/src/resources/extensions/gsd/auto-post-unit.ts +226 -9
  340. package/src/resources/extensions/gsd/auto-recovery.ts +29 -9
  341. package/src/resources/extensions/gsd/auto-unit-closeout.ts +25 -1
  342. package/src/resources/extensions/gsd/auto-verification.ts +129 -2
  343. package/src/resources/extensions/gsd/auto-worktree.ts +1 -0
  344. package/src/resources/extensions/gsd/auto.ts +34 -2
  345. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +5 -3
  346. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -1
  347. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +72 -8
  348. package/src/resources/extensions/gsd/cache.ts +16 -5
  349. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  350. package/src/resources/extensions/gsd/commands/handlers/core.ts +5 -1
  351. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  352. package/src/resources/extensions/gsd/commands-extract-learnings.ts +304 -0
  353. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +58 -4
  354. package/src/resources/extensions/gsd/docs/preferences-reference.md +16 -1
  355. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +228 -0
  356. package/src/resources/extensions/gsd/ecosystem/loader.ts +201 -0
  357. package/src/resources/extensions/gsd/git-service.ts +68 -0
  358. package/src/resources/extensions/gsd/graph-context.ts +139 -12
  359. package/src/resources/extensions/gsd/gsd-db.ts +321 -3
  360. package/src/resources/extensions/gsd/guided-flow.ts +36 -2
  361. package/src/resources/extensions/gsd/init-wizard.ts +3 -2
  362. package/src/resources/extensions/gsd/journal.ts +30 -0
  363. package/src/resources/extensions/gsd/metrics.ts +26 -0
  364. package/src/resources/extensions/gsd/parallel-orchestrator.ts +40 -1
  365. package/src/resources/extensions/gsd/preferences-models.ts +20 -3
  366. package/src/resources/extensions/gsd/preferences-types.ts +38 -0
  367. package/src/resources/extensions/gsd/preferences-validation.ts +117 -2
  368. package/src/resources/extensions/gsd/preferences.ts +34 -0
  369. package/src/resources/extensions/gsd/safety/evidence-collector.ts +15 -31
  370. package/src/resources/extensions/gsd/session-lock.ts +14 -2
  371. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +20 -1
  372. package/src/resources/extensions/gsd/templates/PREFERENCES.md +19 -0
  373. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +177 -0
  374. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +7 -3
  375. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +20 -0
  376. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +7 -3
  377. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +272 -0
  378. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +117 -0
  379. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +6 -2
  380. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +340 -0
  381. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  382. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  383. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +10 -7
  384. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  385. package/src/resources/extensions/gsd/tests/health-widget.test.ts +1 -1
  386. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
  387. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -2
  388. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -3
  389. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  390. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +2 -1
  391. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +40 -1
  392. package/src/resources/extensions/gsd/tests/preferences.test.ts +145 -0
  393. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +57 -2
  394. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +1 -1
  395. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -5
  396. package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +101 -0
  397. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +85 -0
  398. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +69 -0
  399. package/src/resources/extensions/gsd/tests/uok-flags.test.ts +39 -0
  400. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +70 -0
  401. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +85 -0
  402. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +35 -0
  403. package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +89 -0
  404. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +167 -0
  405. package/src/resources/extensions/gsd/tests/uok-preferences.test.ts +42 -0
  406. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +39 -0
  407. package/src/resources/extensions/gsd/tools/complete-slice.ts +9 -2
  408. package/src/resources/extensions/gsd/tools/validate-milestone.ts +48 -3
  409. package/src/resources/extensions/gsd/types.ts +14 -1
  410. package/src/resources/extensions/gsd/unit-ownership.ts +2 -2
  411. package/src/resources/extensions/gsd/uok/audit-toggle.ts +9 -0
  412. package/src/resources/extensions/gsd/uok/audit.ts +51 -0
  413. package/src/resources/extensions/gsd/uok/contracts.ts +135 -0
  414. package/src/resources/extensions/gsd/uok/execution-graph.ts +241 -0
  415. package/src/resources/extensions/gsd/uok/flags.ts +45 -0
  416. package/src/resources/extensions/gsd/uok/gate-runner.ts +146 -0
  417. package/src/resources/extensions/gsd/uok/gitops.ts +75 -0
  418. package/src/resources/extensions/gsd/uok/kernel.ts +105 -0
  419. package/src/resources/extensions/gsd/uok/loop-adapter.ts +162 -0
  420. package/src/resources/extensions/gsd/uok/model-policy.ts +112 -0
  421. package/src/resources/extensions/gsd/uok/plan-v2.ts +156 -0
  422. package/src/resources/extensions/gsd/workflow-logger.ts +27 -1
  423. package/src/resources/extensions/ttsr/ttsr-manager.ts +10 -5
  424. package/packages/pi-ai/dist/models.custom.d.ts.map +0 -1
  425. package/packages/pi-ai/dist/models.custom.js.map +0 -1
  426. package/packages/pi-ai/dist/models.generated.js +0 -14343
  427. package/packages/pi-ai/dist/models.generated.js.map +0 -1
  428. package/packages/pi-ai/src/models.generated.ts +0 -14345
  429. /package/dist/web/standalone/.next/static/{XnHY5eXUsTCFmNodWHetD → k1VZ0h-SB6uWSZgPhgNwi}/_buildManifest.js +0 -0
  430. /package/dist/web/standalone/.next/static/{XnHY5eXUsTCFmNodWHetD → k1VZ0h-SB6uWSZgPhgNwi}/_ssgManifest.js +0 -0
@@ -98,6 +98,49 @@ function makeProjectWithArtifacts(projectDir: string): void {
98
98
  ].join('\n'));
99
99
  }
100
100
 
101
+ // ---------------------------------------------------------------------------
102
+ // LEARNINGS.md fixture helpers
103
+ // ---------------------------------------------------------------------------
104
+
105
+ function writeLearningsFixture(projectDir: string, milestoneId: string, content: string): void {
106
+ writeFixture(projectDir, `.gsd/milestones/${milestoneId}/${milestoneId}-LEARNINGS.md`, content);
107
+ }
108
+
109
+ const SAMPLE_LEARNINGS = `---
110
+ phase: "M001"
111
+ phase_name: "User Auth"
112
+ project: "my-project"
113
+ generated: "2026-04-15T10:00:00Z"
114
+ counts:
115
+ decisions: 2
116
+ lessons: 1
117
+ patterns: 1
118
+ surprises: 1
119
+ missing_artifacts: []
120
+ ---
121
+
122
+ # Learnings: User Auth
123
+
124
+ ## Decisions
125
+ - Use JWT for stateless auth across services.
126
+ Source: M001-PLAN.md/Architecture
127
+
128
+ - Store refresh tokens in HTTP-only cookies only.
129
+ Source: M001-PLAN.md/Security
130
+
131
+ ## Lessons
132
+ - Integration tests need a real DB — mocks missed migration bugs.
133
+ Source: M001-SUMMARY.md/Testing
134
+
135
+ ## Patterns
136
+ - Repository pattern abstracts DB access and simplifies testing.
137
+ Source: M001-PLAN.md/Design
138
+
139
+ ## Surprises
140
+ - Token expiry edge case caused silent auth failures in prod.
141
+ Source: M001-SUMMARY.md/Issues
142
+ `;
143
+
101
144
  // ---------------------------------------------------------------------------
102
145
  // buildGraph tests
103
146
  // ---------------------------------------------------------------------------
@@ -162,6 +205,141 @@ describe('buildGraph', () => {
162
205
  });
163
206
  });
164
207
 
208
+ // ---------------------------------------------------------------------------
209
+ // buildGraph — LEARNINGS.md parsing tests
210
+ // ---------------------------------------------------------------------------
211
+
212
+ describe('buildGraph — LEARNINGS.md parsing', () => {
213
+ let projectDir: string;
214
+
215
+ beforeEach(() => {
216
+ projectDir = tmpProject();
217
+ // Create minimal milestone directory so parseMilestoneFiles finds it
218
+ mkdirSync(join(projectDir, '.gsd', 'milestones', 'M001'), { recursive: true });
219
+ writeLearningsFixture(projectDir, 'M001', SAMPLE_LEARNINGS);
220
+ });
221
+
222
+ afterEach(() => rmSync(projectDir, { recursive: true, force: true }));
223
+
224
+ it('extracts decision nodes from ## Decisions section', async () => {
225
+ const graph = await buildGraph(projectDir);
226
+ const decisions = graph.nodes.filter((n) => n.type === 'decision' || (n.type === 'rule' && n.id.startsWith('decision:')));
227
+ // Decisions should be extracted with a 'decision' type (or similar existing type)
228
+ const decisionNodes = graph.nodes.filter((n) => n.id.includes('decision:M001'));
229
+ assert.ok(decisionNodes.length >= 2, `Expected >= 2 decision nodes, got ${decisionNodes.length}`);
230
+ });
231
+
232
+ it('extracts lesson nodes from ## Lessons section', async () => {
233
+ const graph = await buildGraph(projectDir);
234
+ const lessonNodes = graph.nodes.filter((n) => n.id.includes('lesson:M001'));
235
+ assert.ok(lessonNodes.length >= 1, `Expected >= 1 lesson node, got ${lessonNodes.length}`);
236
+ assert.ok(lessonNodes.every((n) => n.type === 'lesson'), 'All lesson nodes must have type "lesson"');
237
+ });
238
+
239
+ it('extracts pattern nodes from ## Patterns section', async () => {
240
+ const graph = await buildGraph(projectDir);
241
+ const patternNodes = graph.nodes.filter((n) => n.id.includes('pattern:M001'));
242
+ assert.ok(patternNodes.length >= 1, `Expected >= 1 pattern node, got ${patternNodes.length}`);
243
+ assert.ok(patternNodes.every((n) => n.type === 'pattern'), 'All pattern nodes must have type "pattern"');
244
+ });
245
+
246
+ it('maps surprises to lesson nodes', async () => {
247
+ const graph = await buildGraph(projectDir);
248
+ // Surprises should be mapped to lesson type since no "surprise" NodeType exists
249
+ const surpriseNodes = graph.nodes.filter((n) => n.id.includes('surprise:M001'));
250
+ assert.ok(surpriseNodes.length >= 1, `Expected >= 1 surprise node, got ${surpriseNodes.length}`);
251
+ assert.ok(surpriseNodes.every((n) => n.type === 'lesson'), 'Surprises must be mapped to type "lesson"');
252
+ });
253
+
254
+ it('node labels contain the learning text', async () => {
255
+ const graph = await buildGraph(projectDir);
256
+ const hasJwtDecision = graph.nodes.some((n) =>
257
+ n.label.toLowerCase().includes('jwt') || n.description?.toLowerCase().includes('jwt'),
258
+ );
259
+ assert.ok(hasJwtDecision, 'Expected a node describing the JWT decision');
260
+ });
261
+
262
+ it('node description includes source attribution', async () => {
263
+ const graph = await buildGraph(projectDir);
264
+ const learningNodes = graph.nodes.filter((n) =>
265
+ n.id.includes(':M001:') || n.id.match(/:(decision|lesson|pattern|surprise):M001/),
266
+ );
267
+ const withSource = learningNodes.filter((n) => n.description?.includes('Source:') || n.description?.includes('M001-PLAN'));
268
+ assert.ok(withSource.length > 0, 'Expected at least one node with source attribution in description');
269
+ });
270
+
271
+ it('adds relates_to edge from learning node to milestone node', async () => {
272
+ const graph = await buildGraph(projectDir);
273
+ const edgesToMilestone = graph.edges.filter(
274
+ (e) => e.to === 'milestone:M001' || e.from === 'milestone:M001',
275
+ );
276
+ // At least one learning node should relate to the milestone
277
+ const learningEdges = graph.edges.filter(
278
+ (e) => (e.from.includes('M001') && (e.type === 'relates_to' || e.type === 'contains')) ||
279
+ (e.to.includes('M001') && e.type === 'relates_to'),
280
+ );
281
+ assert.ok(learningEdges.length > 0 || edgesToMilestone.length > 0,
282
+ 'Expected edges connecting learning nodes to milestone');
283
+ });
284
+
285
+ it('skips LEARNINGS.md gracefully when file is malformed', async () => {
286
+ const badProject = tmpProject();
287
+ mkdirSync(join(badProject, '.gsd', 'milestones', 'M002'), { recursive: true });
288
+ writeLearningsFixture(badProject, 'M002', '\0\0\0 not valid yaml or markdown \0\0\0');
289
+ // Must not throw
290
+ const graph = await buildGraph(badProject);
291
+ assert.ok(graph.nodes.length >= 0);
292
+ assert.equal(typeof graph.builtAt, 'string');
293
+ rmSync(badProject, { recursive: true, force: true });
294
+ });
295
+
296
+ it('produces no learning nodes when all sections are empty', async () => {
297
+ const emptyProject = tmpProject();
298
+ mkdirSync(join(emptyProject, '.gsd', 'milestones', 'M003'), { recursive: true });
299
+ writeLearningsFixture(emptyProject, 'M003', `---
300
+ phase: "M003"
301
+ phase_name: "Empty"
302
+ project: "test"
303
+ generated: "2026-04-15T10:00:00Z"
304
+ counts:
305
+ decisions: 0
306
+ lessons: 0
307
+ patterns: 0
308
+ surprises: 0
309
+ missing_artifacts: []
310
+ ---
311
+
312
+ # Learnings: Empty
313
+
314
+ ## Decisions
315
+
316
+ ## Lessons
317
+
318
+ ## Patterns
319
+
320
+ ## Surprises
321
+ `);
322
+ const graph = await buildGraph(emptyProject);
323
+ const learningNodes = graph.nodes.filter((n) =>
324
+ n.id.includes('decision:M003') ||
325
+ n.id.includes('lesson:M003') ||
326
+ n.id.includes('pattern:M003') ||
327
+ n.id.includes('surprise:M003'),
328
+ );
329
+ assert.equal(learningNodes.length, 0, 'Empty sections should produce no nodes');
330
+ rmSync(emptyProject, { recursive: true, force: true });
331
+ });
332
+
333
+ it('does not crash when LEARNINGS.md is missing entirely', async () => {
334
+ const noLearningsProject = tmpProject();
335
+ mkdirSync(join(noLearningsProject, '.gsd', 'milestones', 'M004'), { recursive: true });
336
+ // No LEARNINGS.md file written
337
+ const graph = await buildGraph(noLearningsProject);
338
+ assert.ok(graph.nodes.length >= 0);
339
+ rmSync(noLearningsProject, { recursive: true, force: true });
340
+ });
341
+ });
342
+
165
343
  // ---------------------------------------------------------------------------
166
344
  // writeGraph tests
167
345
  // ---------------------------------------------------------------------------
@@ -27,7 +27,8 @@ export type NodeType =
27
27
  | 'rule'
28
28
  | 'pattern'
29
29
  | 'lesson'
30
- | 'concept';
30
+ | 'concept'
31
+ | 'decision';
31
32
 
32
33
  export type EdgeType =
33
34
  | 'contains'
@@ -386,6 +387,151 @@ function parseTasksFromPlan(
386
387
  }
387
388
  }
388
389
 
390
+ // ---------------------------------------------------------------------------
391
+ // LEARNINGS.md parser
392
+ // ---------------------------------------------------------------------------
393
+
394
+ /**
395
+ * Parse all *-LEARNINGS.md files found in milestone directories.
396
+ * Extracts Decisions, Lessons, Patterns, and Surprises as typed graph nodes.
397
+ * Surprises are mapped to the 'lesson' NodeType (no distinct type exists).
398
+ * Parse errors per file are caught — the file is skipped, never rethrows.
399
+ */
400
+ function parseLearningsFiles(gsdRoot: string, nodes: GraphNode[], edges: GraphEdge[]): void {
401
+ const milestoneIds = findMilestoneIds(gsdRoot);
402
+
403
+ for (const milestoneId of milestoneIds) {
404
+ try {
405
+ parseSingleLearningsFile(gsdRoot, milestoneId, nodes, edges);
406
+ } catch {
407
+ // Skip this milestone's LEARNINGS.md on any error
408
+ }
409
+ }
410
+ }
411
+
412
+ function parseSingleLearningsFile(
413
+ gsdRoot: string,
414
+ milestoneId: string,
415
+ nodes: GraphNode[],
416
+ edges: GraphEdge[],
417
+ ): void {
418
+ const mDir = resolveMilestoneDir(gsdRoot, milestoneId);
419
+ if (!mDir) return;
420
+
421
+ const learningsPath = join(mDir, `${milestoneId}-LEARNINGS.md`);
422
+ if (!existsSync(learningsPath)) return;
423
+
424
+ let content: string;
425
+ try {
426
+ content = readFileSync(learningsPath, 'utf-8');
427
+ } catch {
428
+ return;
429
+ }
430
+
431
+ // Strip YAML frontmatter if present
432
+ const withoutFrontmatter = content.replace(/^---[\s\S]*?---\n?/, '');
433
+
434
+ const milestoneNodeId = `milestone:${milestoneId}`;
435
+ const sourceFile = `milestones/${milestoneId}/${milestoneId}-LEARNINGS.md`;
436
+
437
+ // Parse each section: [sectionName, nodeType, idPrefix]
438
+ const sections: Array<[string, NodeType, string]> = [
439
+ ['Decisions', 'decision', 'decision'],
440
+ ['Lessons', 'lesson', 'lesson'],
441
+ ['Patterns', 'pattern', 'pattern'],
442
+ ['Surprises', 'lesson', 'surprise'],
443
+ ];
444
+
445
+ for (const [sectionName, nodeType, idPrefix] of sections) {
446
+ const sectionMatch = withoutFrontmatter.match(
447
+ new RegExp(`##\\s+${sectionName}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$)`, 'i'),
448
+ );
449
+ if (!sectionMatch) continue;
450
+
451
+ const sectionContent = sectionMatch[1];
452
+ parseLearningsSection(
453
+ sectionContent,
454
+ milestoneId,
455
+ idPrefix,
456
+ nodeType,
457
+ milestoneNodeId,
458
+ sourceFile,
459
+ nodes,
460
+ edges,
461
+ );
462
+ }
463
+ }
464
+
465
+ function parseLearningsSection(
466
+ sectionContent: string,
467
+ milestoneId: string,
468
+ idPrefix: string,
469
+ nodeType: NodeType,
470
+ milestoneNodeId: string,
471
+ sourceFile: string,
472
+ nodes: GraphNode[],
473
+ edges: GraphEdge[],
474
+ ): void {
475
+ // Each item is a bullet line starting with "- " followed by optional
476
+ // indented "Source: ..." line.
477
+ // We collect bullet items and their associated source attribution.
478
+ const lines = sectionContent.split('\n');
479
+ let itemIndex = 0;
480
+ let currentText: string | null = null;
481
+ let currentSource: string | null = null;
482
+
483
+ const flushItem = (): void => {
484
+ if (!currentText) return;
485
+ itemIndex += 1;
486
+ const nodeId = `${idPrefix}:${milestoneId}:${itemIndex}`;
487
+ const description = currentSource ? `${currentSource}` : undefined;
488
+
489
+ nodes.push({
490
+ id: nodeId,
491
+ label: currentText,
492
+ type: nodeType,
493
+ description,
494
+ confidence: 'EXTRACTED',
495
+ sourceFile,
496
+ });
497
+
498
+ // Edge: milestone relates_to this learning node
499
+ edges.push({
500
+ from: milestoneNodeId,
501
+ to: nodeId,
502
+ type: 'relates_to',
503
+ confidence: 'EXTRACTED',
504
+ });
505
+
506
+ currentText = null;
507
+ currentSource = null;
508
+ };
509
+
510
+ for (const line of lines) {
511
+ const bulletMatch = line.match(/^[-*]\s+(.+)/);
512
+ if (bulletMatch) {
513
+ flushItem();
514
+ currentText = bulletMatch[1].trim();
515
+ continue;
516
+ }
517
+
518
+ // Indented source attribution: " Source: ..."
519
+ const sourceMatch = line.match(/^\s+Source:\s+(.+)/i);
520
+ if (sourceMatch && currentText !== null) {
521
+ currentSource = `Source: ${sourceMatch[1].trim()}`;
522
+ continue;
523
+ }
524
+
525
+ // Continuation of current item text (indented non-source line)
526
+ const continuationMatch = line.match(/^\s{2,}(.+)/);
527
+ if (continuationMatch && currentText !== null && currentSource === null) {
528
+ currentText += ' ' + continuationMatch[1].trim();
529
+ }
530
+ }
531
+
532
+ flushItem();
533
+ }
534
+
389
535
  // ---------------------------------------------------------------------------
390
536
  // buildGraph
391
537
  // ---------------------------------------------------------------------------
@@ -407,6 +553,7 @@ export async function buildGraph(projectDir: string): Promise<KnowledgeGraph> {
407
553
  parseStateFile,
408
554
  parseKnowledgeFile,
409
555
  parseMilestoneFiles,
556
+ parseLearningsFiles,
410
557
  ];
411
558
 
412
559
  for (const parser of parsers) {
@@ -2,7 +2,8 @@
2
2
  * Workflow MCP tools — exposes the core GSD mutation/read handlers over MCP.
3
3
  */
4
4
 
5
- import { isAbsolute, relative, resolve } from "node:path";
5
+ import { existsSync, realpathSync } from "node:fs";
6
+ import { isAbsolute, join, relative, resolve } from "node:path";
6
7
  import { pathToFileURL } from "node:url";
7
8
  import { z } from "zod";
8
9
 
@@ -262,6 +263,22 @@ function isWithinRoot(candidatePath: string, rootPath: string): boolean {
262
263
  return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
263
264
  }
264
265
 
266
+ /**
267
+ * Resolve the symlink target of `<allowedRoot>/.gsd` when it points into the
268
+ * external state layout (`~/.gsd/projects/<hash>/`). Returns the realpath of
269
+ * that target so callers can accept worktree paths that live under
270
+ * `<external-state>/worktrees/<MID>/`. Returns null when `.gsd` is absent or
271
+ * resolution fails — the caller should fall back to the direct containment
272
+ * check in that case.
273
+ */
274
+ function resolveExternalStateRoot(allowedRoot: string): string | null {
275
+ try {
276
+ return realpathSync(join(allowedRoot, ".gsd"));
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
281
+
265
282
  function validateProjectDir(projectDir: string, env: NodeJS.ProcessEnv = process.env): string {
266
283
  if (!isAbsolute(projectDir)) {
267
284
  throw new Error(`projectDir must be an absolute path. Received: ${projectDir}`);
@@ -269,27 +286,88 @@ function validateProjectDir(projectDir: string, env: NodeJS.ProcessEnv = process
269
286
 
270
287
  const resolvedProjectDir = resolve(projectDir);
271
288
  const allowedRoot = getAllowedProjectRoot(env);
272
- if (allowedRoot && !isWithinRoot(resolvedProjectDir, allowedRoot)) {
273
- throw new Error(
274
- `projectDir must stay within the configured workflow project root. Received: ${resolvedProjectDir}; allowed root: ${allowedRoot}`,
275
- );
289
+ if (!allowedRoot) return resolvedProjectDir;
290
+
291
+ if (isWithinRoot(resolvedProjectDir, allowedRoot)) return resolvedProjectDir;
292
+
293
+ // External state layout: `<allowedRoot>/.gsd` may be a symlink into
294
+ // `~/.gsd/projects/<hash>/`, and auto-worktrees live under
295
+ // `~/.gsd/projects/<hash>/worktrees/<MID>/`. Accept candidates that are
296
+ // under the realpath of `<allowedRoot>/.gsd` — they belong to this project
297
+ // even though their absolute path is outside allowedRoot (#issue-a44).
298
+ const externalRoot = resolveExternalStateRoot(allowedRoot);
299
+ if (externalRoot && isWithinRoot(resolvedProjectDir, externalRoot)) {
300
+ return resolvedProjectDir;
276
301
  }
277
302
 
278
- return resolvedProjectDir;
303
+ throw new Error(
304
+ `projectDir must stay within the configured workflow project root. Received: ${resolvedProjectDir}; allowed root: ${allowedRoot}`,
305
+ );
279
306
  }
280
307
 
281
308
  function parseToolArgs<T>(schema: z.ZodType<T>, args: Record<string, unknown>): T {
282
309
  return schema.parse(args);
283
310
  }
284
311
 
285
- function parseWorkflowArgs<T extends { projectDir: string }>(
312
+ /**
313
+ * Extract a milestone ID from parsed tool args, trying common field names.
314
+ * Returns null when no field is present or the value is not a string.
315
+ */
316
+ function extractMilestoneId(parsed: Record<string, unknown>): string | null {
317
+ const candidates = [parsed.milestoneId, parsed.milestone_id, parsed.mid];
318
+ for (const c of candidates) {
319
+ if (typeof c === "string" && c.trim() !== "") return c.trim();
320
+ }
321
+ return null;
322
+ }
323
+
324
+ /**
325
+ * If an auto-worktree exists for the given milestone under
326
+ * `<projectRoot>/.gsd/worktrees/<milestoneId>/`, return that path as the
327
+ * basePath the tool should write against. Returns null when no worktree
328
+ * exists for this milestone, leaving the caller to use the project root.
329
+ *
330
+ * This unbreaks the external-state layout where the MCP server's process.cwd()
331
+ * is the project root (set at Claude Code launch) but auto-mode is actually
332
+ * working inside a per-milestone worktree. Without this, tool writes go to
333
+ * the shared project `.gsd/` and auto-mode's verifyExpectedArtifact (which
334
+ * uses the worktree `.gsd/`) fails, triggering a guaranteed retry per unit.
335
+ */
336
+ function resolveActiveWorktreeBasePath(
337
+ projectRoot: string,
338
+ milestoneId: string | null,
339
+ ): string | null {
340
+ if (!milestoneId) return null;
341
+ const wtPath = join(projectRoot, ".gsd", "worktrees", milestoneId);
342
+ if (!existsSync(wtPath)) return null;
343
+ // Sanity check: a real git worktree has a `.git` file with a gitdir pointer.
344
+ // Bare directories without it shouldn't hijack the write path.
345
+ if (!existsSync(join(wtPath, ".git"))) return null;
346
+ return wtPath;
347
+ }
348
+
349
+ function parseWorkflowArgs<T extends { projectDir?: string }>(
286
350
  schema: z.ZodType<T>,
287
351
  args: Record<string, unknown>,
288
- ): T {
352
+ ): T & { projectDir: string } {
289
353
  const parsed = parseToolArgs(schema, args);
354
+ // Step 1: figure out the project root. The agent shouldn't need to pass
355
+ // projectDir — default to process.cwd() which the MCP server inherited from
356
+ // Claude Code (launched at the project root).
357
+ const projectRootCandidate = parsed.projectDir ?? process.cwd();
358
+ const projectRoot = validateProjectDir(projectRootCandidate);
359
+
360
+ // Step 2: if this tool call is scoped to a milestone that has an active
361
+ // auto-worktree, re-route writes to the worktree's .gsd rather than the
362
+ // project's shared .gsd. auto-mode's verifyExpectedArtifact runs against
363
+ // the worktree, and a mismatch here causes every unit to retry once.
364
+ const milestoneId = extractMilestoneId(parsed as Record<string, unknown>);
365
+ const worktreeBasePath = resolveActiveWorktreeBasePath(projectRoot, milestoneId);
366
+ const effectiveBasePath = worktreeBasePath ?? projectRoot;
367
+
290
368
  return {
291
369
  ...parsed,
292
- projectDir: validateProjectDir(parsed.projectDir),
370
+ projectDir: effectiveBasePath,
293
371
  };
294
372
  }
295
373
 
@@ -664,7 +742,14 @@ async function ensureMilestoneDbRow(milestoneId: string): Promise<void> {
664
742
  }
665
743
  }
666
744
 
667
- const projectDirParam = z.string().describe("Absolute path to the project directory within the configured workflow root");
745
+ // projectDir is optional. When omitted, the server uses process.cwd(). This
746
+ // prevents the agent from burning tokens reasoning about which absolute path
747
+ // to pass (git root vs worktree vs symlink-resolved external state layout) —
748
+ // the server already knows where it is running.
749
+ const projectDirParam = z
750
+ .string()
751
+ .optional()
752
+ .describe("Optional. Omit this field — the server defaults to its current working directory, which is already the correct project or worktree root.");
668
753
 
669
754
  const planMilestoneParams = {
670
755
  projectDir: projectDirParam,