gsd-pi 2.76.0-dev.82e249f7b → 2.76.0-dev.97f5583d9

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 (271) hide show
  1. package/dist/claude-cli-check.js +32 -3
  2. package/dist/mcp-server.d.ts +7 -0
  3. package/dist/mcp-server.js +35 -1
  4. package/dist/resource-loader.d.ts +1 -1
  5. package/dist/resource-loader.js +2 -8
  6. package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
  7. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
  8. package/dist/resources/extensions/gsd/auto/phases.js +42 -1
  9. package/dist/resources/extensions/gsd/auto/run-unit.js +27 -0
  10. package/dist/resources/extensions/gsd/auto/session.js +12 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +1 -1
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
  14. package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
  15. package/dist/resources/extensions/gsd/auto-recovery.js +13 -0
  16. package/dist/resources/extensions/gsd/auto-start.js +27 -18
  17. package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
  18. package/dist/resources/extensions/gsd/auto.js +55 -27
  19. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
  21. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +51 -5
  24. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
  25. package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
  26. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
  27. package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
  28. package/dist/resources/extensions/gsd/error-classifier.js +10 -3
  29. package/dist/resources/extensions/gsd/exec-history.js +120 -0
  30. package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
  31. package/dist/resources/extensions/gsd/gsd-db.js +115 -7
  32. package/dist/resources/extensions/gsd/guided-flow.js +189 -0
  33. package/dist/resources/extensions/gsd/health-widget.js +4 -1
  34. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  35. package/dist/resources/extensions/gsd/key-manager.js +6 -0
  36. package/dist/resources/extensions/gsd/model-router.js +36 -3
  37. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -9
  38. package/dist/resources/extensions/gsd/preferences-types.js +9 -0
  39. package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
  40. package/dist/resources/extensions/gsd/preferences.js +17 -17
  41. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  42. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
  43. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  44. package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
  45. package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
  46. package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
  47. package/dist/resources/extensions/gsd/token-counter.js +22 -5
  48. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
  49. package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
  50. package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
  51. package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
  52. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
  53. package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
  54. package/dist/resources/skills/write-docs/SKILL.md +2 -1
  55. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  56. package/dist/web/standalone/.next/BUILD_ID +1 -1
  57. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  58. package/dist/web/standalone/.next/build-manifest.json +2 -2
  59. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  60. package/dist/web/standalone/.next/required-server-files.json +1 -1
  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/index.html +1 -1
  78. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  85. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  87. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  88. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  89. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  90. package/dist/web/standalone/server.js +1 -1
  91. package/package.json +1 -1
  92. package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
  93. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
  94. package/packages/mcp-server/dist/remote-questions.js +732 -0
  95. package/packages/mcp-server/dist/remote-questions.js.map +1 -0
  96. package/packages/mcp-server/dist/server.d.ts +7 -0
  97. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  98. package/packages/mcp-server/dist/server.js +41 -4
  99. package/packages/mcp-server/dist/server.js.map +1 -1
  100. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  101. package/packages/mcp-server/dist/workflow-tools.js +64 -25
  102. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  103. package/packages/mcp-server/package.json +2 -1
  104. package/packages/mcp-server/src/mcp-server.test.ts +30 -0
  105. package/packages/mcp-server/src/remote-questions.test.ts +294 -0
  106. package/packages/mcp-server/src/remote-questions.ts +916 -0
  107. package/packages/mcp-server/src/server.ts +62 -10
  108. package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
  109. package/packages/mcp-server/src/workflow-tools.ts +84 -43
  110. package/packages/mcp-server/tsconfig.test.json +19 -0
  111. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  112. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
  113. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  114. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  115. package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
  116. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  117. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  118. package/packages/pi-ai/dist/providers/anthropic.js +8 -3
  119. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  120. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
  121. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
  122. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
  123. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
  124. package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
  125. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/providers/simple-options.js +16 -1
  127. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  128. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
  129. package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
  130. package/packages/pi-ai/src/providers/anthropic.ts +9 -3
  131. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
  132. package/packages/pi-ai/src/providers/simple-options.ts +17 -1
  133. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  134. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
  135. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
  136. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
  137. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
  138. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/model-registry.js +14 -0
  140. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
  142. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
  144. package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
  145. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
  146. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
  147. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
  148. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
  149. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/session-manager.js +9 -5
  151. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/session-manager.test.js +25 -1
  153. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  157. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  159. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  161. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  163. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  165. package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
  166. package/packages/pi-coding-agent/src/core/model-registry.ts +16 -0
  167. package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
  168. package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
  169. package/packages/pi-coding-agent/src/core/session-manager.test.ts +36 -1
  170. package/packages/pi-coding-agent/src/core/session-manager.ts +9 -5
  171. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  172. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  173. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
  174. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  175. package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
  176. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
  177. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
  178. package/src/resources/extensions/gsd/auto/loop-deps.ts +13 -0
  179. package/src/resources/extensions/gsd/auto/phases.ts +66 -1
  180. package/src/resources/extensions/gsd/auto/run-unit.ts +29 -0
  181. package/src/resources/extensions/gsd/auto/session.ts +22 -0
  182. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
  183. package/src/resources/extensions/gsd/auto-model-selection.ts +1 -1
  184. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  185. package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
  186. package/src/resources/extensions/gsd/auto-recovery.ts +15 -0
  187. package/src/resources/extensions/gsd/auto-start.ts +29 -19
  188. package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
  189. package/src/resources/extensions/gsd/auto.ts +58 -27
  190. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
  191. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
  192. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
  193. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  194. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +53 -5
  195. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
  196. package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
  197. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
  198. package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
  199. package/src/resources/extensions/gsd/error-classifier.ts +10 -3
  200. package/src/resources/extensions/gsd/exec-history.ts +153 -0
  201. package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
  202. package/src/resources/extensions/gsd/gsd-db.ts +122 -7
  203. package/src/resources/extensions/gsd/guided-flow.ts +221 -0
  204. package/src/resources/extensions/gsd/health-widget.ts +3 -1
  205. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  206. package/src/resources/extensions/gsd/journal.ts +2 -1
  207. package/src/resources/extensions/gsd/key-manager.ts +6 -0
  208. package/src/resources/extensions/gsd/model-router.ts +42 -1
  209. package/src/resources/extensions/gsd/pre-execution-checks.ts +36 -10
  210. package/src/resources/extensions/gsd/preferences-types.ts +46 -0
  211. package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
  212. package/src/resources/extensions/gsd/preferences.ts +17 -17
  213. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  214. package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
  215. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  216. package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
  217. package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
  218. package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
  219. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +119 -1
  220. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
  221. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
  222. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
  223. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
  224. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  225. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  226. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  227. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
  228. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
  229. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
  230. package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
  231. package/src/resources/extensions/gsd/tests/exec-history.test.ts +124 -0
  232. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
  233. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
  234. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +152 -1
  235. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  236. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
  237. package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
  238. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +2 -0
  239. package/src/resources/extensions/gsd/tests/key-manager.test.ts +7 -0
  240. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  241. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  242. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
  243. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
  244. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
  245. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +234 -0
  246. package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
  247. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
  248. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
  249. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
  250. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
  251. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
  252. package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
  253. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
  254. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
  255. package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
  256. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
  257. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
  258. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
  259. package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
  260. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
  261. package/src/resources/extensions/gsd/token-counter.ts +22 -5
  262. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
  263. package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
  264. package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
  265. package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
  266. package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
  267. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
  268. package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
  269. package/src/resources/skills/write-docs/SKILL.md +2 -1
  270. /package/dist/web/standalone/.next/static/{ecSsu49rxxcpbNmVP4mLD → lLdDRDspgYzfz0bJAmUSz}/_buildManifest.js +0 -0
  271. /package/dist/web/standalone/.next/static/{ecSsu49rxxcpbNmVP4mLD → lLdDRDspgYzfz0bJAmUSz}/_ssgManifest.js +0 -0
@@ -11,6 +11,10 @@
11
11
 
12
12
  import test from 'node:test';
13
13
  import assert from 'node:assert/strict';
14
+ import { mkdirSync, writeFileSync, unlinkSync, existsSync, rmSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+ import { tmpdir } from 'node:os';
17
+ import { randomUUID } from 'node:crypto';
14
18
  import {
15
19
  isDepthConfirmationAnswer,
16
20
  shouldBlockContextWrite,
@@ -20,8 +24,10 @@ import {
20
24
  markDepthVerified,
21
25
  isMilestoneDepthVerified,
22
26
  shouldBlockContextArtifactSave,
27
+ shouldBlockContextArtifactSaveInSnapshot,
23
28
  clearDiscussionFlowState,
24
29
  resetWriteGateState,
30
+ loadWriteGateSnapshot,
25
31
  } from '../bootstrap/write-gate.ts';
26
32
 
27
33
  // ─── Scenario 1: Blocks CONTEXT.md write during discussion without depth verification (absolute path) ──
@@ -488,3 +494,61 @@ test('write-gate: isDepthConfirmationAnswer falls back to (Recommended) match wi
488
494
  'should reject non-Recommended via fallback',
489
495
  );
490
496
  });
497
+
498
+ // ─── Scenario 29: loadWriteGateSnapshot returns clean state when persist file deleted (#4343) ──
499
+
500
+ test('write-gate: loadWriteGateSnapshot returns empty default when persist file is deleted (#4343)', () => {
501
+ const base = join(tmpdir(), `gsd-write-gate-4343-${randomUUID()}`);
502
+ mkdirSync(join(base, '.gsd', 'runtime'), { recursive: true });
503
+ const stateFilePath = join(base, '.gsd', 'runtime', 'write-gate-state.json');
504
+ const originalEnv = process.env.GSD_PERSIST_WRITE_GATE_STATE;
505
+
506
+ try {
507
+ process.env.GSD_PERSIST_WRITE_GATE_STATE = '1';
508
+
509
+ // Write a state file with a pending gate and verified milestone
510
+ writeFileSync(stateFilePath, JSON.stringify({
511
+ verifiedDepthMilestones: ['M001'],
512
+ activeQueuePhase: false,
513
+ pendingGateId: 'depth_verification_M001',
514
+ }));
515
+ assert.ok(existsSync(stateFilePath), 'precondition: state file exists');
516
+
517
+ // While file exists, snapshot reflects its contents
518
+ const beforeDeletion = loadWriteGateSnapshot(base);
519
+ assert.strictEqual(beforeDeletion.pendingGateId, 'depth_verification_M001', 'pending gate from file');
520
+ assert.deepEqual(beforeDeletion.verifiedDepthMilestones, ['M001'], 'verified milestones from file');
521
+
522
+ // User deletes the state file to clear the HARD BLOCK
523
+ unlinkSync(stateFilePath);
524
+ assert.ok(!existsSync(stateFilePath), 'state file deleted');
525
+
526
+ // After deletion in persist mode, snapshot should be clean (not stale in-memory)
527
+ const afterDeletion = loadWriteGateSnapshot(base);
528
+ assert.strictEqual(afterDeletion.pendingGateId, null, 'pendingGateId cleared after file deletion');
529
+ assert.deepEqual(afterDeletion.verifiedDepthMilestones, [], 'verifiedDepthMilestones cleared after file deletion');
530
+ assert.strictEqual(afterDeletion.activeQueuePhase, false, 'activeQueuePhase cleared after file deletion');
531
+
532
+ // The CONTEXT artifact block check must also resolve to unblocked after deletion+verification
533
+ // (simulate the re-verify flow users would do: delete → depth verify → save)
534
+ const stillBlocked = shouldBlockContextArtifactSaveInSnapshot(afterDeletion, 'CONTEXT', 'M001', null);
535
+ assert.strictEqual(stillBlocked.block, true, 'still blocked without new depth verification');
536
+
537
+ const verifiedSnapshot = {
538
+ ...afterDeletion,
539
+ verifiedDepthMilestones: ['M001'],
540
+ };
541
+ const unblocked = shouldBlockContextArtifactSaveInSnapshot(verifiedSnapshot, 'CONTEXT', 'M001', null);
542
+ assert.strictEqual(unblocked.block, false, 'unblocked after fresh depth verification');
543
+ } finally {
544
+ if (originalEnv === undefined) {
545
+ delete process.env.GSD_PERSIST_WRITE_GATE_STATE;
546
+ } else {
547
+ process.env.GSD_PERSIST_WRITE_GATE_STATE = originalEnv;
548
+ }
549
+ clearDiscussionFlowState();
550
+ try {
551
+ rmSync(base, { recursive: true, force: true });
552
+ } catch { /* swallow */ }
553
+ }
554
+ });
@@ -72,7 +72,9 @@ const autoStartSrc = readFileSync(
72
72
  const symlinkIdx = autoStartSrc.indexOf("ensureGsdSymlink(base)");
73
73
  assertTrue(symlinkIdx >= 0, "auto-start.ts calls ensureGsdSymlink(base)");
74
74
 
75
- const afterSymlink = symlinkIdx >= 0 ? autoStartSrc.slice(symlinkIdx, symlinkIdx + 800) : "";
75
+ const afterSymlink = symlinkIdx >= 0
76
+ ? autoStartSrc.slice(symlinkIdx, autoStartSrc.indexOf("Initialize GitServiceImpl", symlinkIdx))
77
+ : "";
76
78
 
77
79
  // The milestones bootstrap must check milestones path, not gsdDir
78
80
  // Old (dead) code: if (!existsSync(gsdDir)) { mkdirSync(join(gsdDir, "milestones"), ...) }
@@ -22,7 +22,11 @@ async function getEncoder(): Promise<TokenEncoder | null> {
22
22
  try {
23
23
  // @ts-ignore — tiktoken may not have type declarations in extensions tsconfig
24
24
  const tiktoken = await import("tiktoken");
25
- encoder = tiktoken.encoding_for_model("gpt-4o") as TokenEncoder;
25
+ // Use cl100k_base the most conservative and broadly compatible BPE encoding.
26
+ // It is shared by GPT-3.5/GPT-4 and gives a safer (larger) estimate than
27
+ // gpt-4o's o200k_base encoding, which produces fewer tokens for the same text
28
+ // and would cause context windows for non-OpenAI providers to be under-counted.
29
+ encoder = tiktoken.get_encoding("cl100k_base") as TokenEncoder;
26
30
  return encoder;
27
31
  } catch {
28
32
  encoderFailed = true;
@@ -30,20 +34,33 @@ async function getEncoder(): Promise<TokenEncoder | null> {
30
34
  }
31
35
  }
32
36
 
33
- export async function countTokens(text: string): Promise<number> {
37
+ /**
38
+ * Count tokens in `text` using tiktoken (cl100k_base) when available.
39
+ *
40
+ * When tiktoken is not loaded, falls back to a provider-aware character-ratio
41
+ * estimate via `estimateTokensForProvider`. Passing `provider` is recommended
42
+ * so the heuristic fallback is as accurate as possible.
43
+ */
44
+ export async function countTokens(text: string, provider?: TokenProvider): Promise<number> {
34
45
  const enc = await getEncoder();
35
46
  if (enc) {
36
47
  const tokens = enc.encode(text);
37
48
  return tokens.length;
38
49
  }
39
- return Math.ceil(text.length / 4);
50
+ return estimateTokensForProvider(text, provider ?? "unknown");
40
51
  }
41
52
 
42
- export function countTokensSync(text: string): number {
53
+ /**
54
+ * Synchronous token count — only accurate after `initTokenCounter()` resolves.
55
+ *
56
+ * Before init, or when tiktoken is unavailable, falls back to a provider-aware
57
+ * character-ratio estimate. Passing `provider` is recommended.
58
+ */
59
+ export function countTokensSync(text: string, provider?: TokenProvider): number {
43
60
  if (encoder) {
44
61
  return encoder.encode(text).length;
45
62
  }
46
- return Math.ceil(text.length / 4);
63
+ return estimateTokensForProvider(text, provider ?? "unknown");
47
64
  }
48
65
 
49
66
  export async function initTokenCounter(): Promise<boolean> {
@@ -0,0 +1,81 @@
1
+ // GSD Exec Search Tool — lists and filters prior gsd_exec runs.
2
+ //
3
+ // Scans .gsd/exec/*.meta.json and returns a ranked summary so agents can
4
+ // re-discover past runs without re-executing. Read-only; no DB writes.
5
+
6
+ import { searchExecHistory, type ExecSearchOptions } from "../exec-history.js";
7
+
8
+ export interface ExecSearchToolParams {
9
+ query?: string;
10
+ runtime?: "bash" | "node" | "python";
11
+ failing_only?: boolean;
12
+ limit?: number;
13
+ }
14
+
15
+ export interface ToolExecutionResult {
16
+ content: Array<{ type: "text"; text: string }>;
17
+ details: Record<string, unknown>;
18
+ isError?: boolean;
19
+ }
20
+
21
+ export function executeExecSearch(
22
+ params: ExecSearchToolParams,
23
+ opts: { baseDir: string },
24
+ ): ToolExecutionResult {
25
+ const searchOpts: ExecSearchOptions = {
26
+ query: typeof params.query === "string" ? params.query : undefined,
27
+ runtime: params.runtime,
28
+ failing_only: params.failing_only === true,
29
+ limit: typeof params.limit === "number" ? params.limit : undefined,
30
+ };
31
+ const hits = searchExecHistory(opts.baseDir, searchOpts);
32
+
33
+ if (hits.length === 0) {
34
+ return {
35
+ content: [{ type: "text", text: "No prior gsd_exec runs match those filters." }],
36
+ details: { operation: "gsd_exec_search", matches: 0 },
37
+ };
38
+ }
39
+
40
+ const lines: string[] = [`Found ${hits.length} exec run(s), most recent first:`];
41
+ for (const hit of hits) {
42
+ const e = hit.entry;
43
+ const status = formatStatus(e);
44
+ const purpose = e.purpose ? ` — ${e.purpose}` : "";
45
+ const truncated = e.stdout_truncated ? " (stdout truncated)" : "";
46
+ lines.push(
47
+ `- [${e.id}] ${e.runtime} ${status} ${e.duration_ms}ms${truncated}${purpose}`,
48
+ ` stdout: ${e.stdout_path}`,
49
+ );
50
+ if (hit.digest_preview) {
51
+ const preview = hit.digest_preview.replace(/\n/g, "\n ");
52
+ lines.push(` preview:\n ${preview}`);
53
+ }
54
+ }
55
+
56
+ return {
57
+ content: [{ type: "text", text: lines.join("\n") }],
58
+ details: {
59
+ operation: "gsd_exec_search",
60
+ matches: hits.length,
61
+ results: hits.map((hit) => ({
62
+ id: hit.entry.id,
63
+ runtime: hit.entry.runtime,
64
+ exit_code: hit.entry.exit_code,
65
+ timed_out: hit.entry.timed_out,
66
+ duration_ms: hit.entry.duration_ms,
67
+ purpose: hit.entry.purpose,
68
+ stdout_path: hit.entry.stdout_path,
69
+ stderr_path: hit.entry.stderr_path,
70
+ meta_path: hit.entry.meta_path,
71
+ })),
72
+ },
73
+ };
74
+ }
75
+
76
+ function formatStatus(entry: { exit_code: number | null; timed_out: boolean; signal: string | null }): string {
77
+ if (entry.timed_out) return "timeout";
78
+ if (entry.signal) return `signal:${entry.signal}`;
79
+ if (entry.exit_code === null) return "exit:null";
80
+ return `exit:${entry.exit_code}`;
81
+ }
@@ -0,0 +1,183 @@
1
+ // GSD Exec Tool — executor for the gsd_exec MCP tool.
2
+ //
3
+ // Thin wrapper around exec-sandbox.ts that reads effective options from
4
+ // the project preferences (context_mode block) and formats the result
5
+ // for MCP return.
6
+
7
+ import {
8
+ EXEC_DEFAULTS,
9
+ runExecSandbox,
10
+ type ExecSandboxOptions,
11
+ type ExecSandboxRequest,
12
+ type ExecSandboxResult,
13
+ } from "../exec-sandbox.js";
14
+ import { isContextModeEnabled, type ContextModeConfig } from "../preferences-types.js";
15
+
16
+ export interface ExecToolParams {
17
+ runtime: ExecSandboxRequest["runtime"];
18
+ script: string;
19
+ purpose?: string;
20
+ timeout_ms?: number;
21
+ }
22
+
23
+ export interface ToolExecutionResult {
24
+ content: Array<{ type: "text"; text: string }>;
25
+ details: Record<string, unknown>;
26
+ isError?: boolean;
27
+ }
28
+
29
+ export interface ExecToolDeps {
30
+ baseDir: string;
31
+ preferences: { context_mode?: ContextModeConfig } | null;
32
+ /** Optional override for testing. */
33
+ run?: (req: ExecSandboxRequest, opts: ExecSandboxOptions) => Promise<ExecSandboxResult>;
34
+ now?: () => Date;
35
+ generateId?: () => string;
36
+ }
37
+
38
+ export function buildExecOptions(
39
+ baseDir: string,
40
+ cfg: ContextModeConfig | undefined,
41
+ extras?: Pick<ExecSandboxOptions, "env" | "now" | "generateId">,
42
+ ): ExecSandboxOptions {
43
+ const allowlist = Array.isArray(cfg?.exec_env_allowlist) ? cfg!.exec_env_allowlist! : EXEC_DEFAULTS.envAllowlist;
44
+ const stdoutCap = clampNumber(
45
+ cfg?.exec_stdout_cap_bytes,
46
+ EXEC_DEFAULTS.stdoutCapBytes,
47
+ 4_096,
48
+ 16_777_216,
49
+ );
50
+ const defaultTimeout = clampNumber(
51
+ cfg?.exec_timeout_ms,
52
+ EXEC_DEFAULTS.defaultTimeoutMs,
53
+ 1_000,
54
+ EXEC_DEFAULTS.clampTimeoutMs,
55
+ );
56
+ const digestChars = clampNumber(cfg?.exec_digest_chars, EXEC_DEFAULTS.digestChars, 0, 4_000);
57
+ return {
58
+ baseDir,
59
+ clamp_timeout_ms: EXEC_DEFAULTS.clampTimeoutMs,
60
+ default_timeout_ms: defaultTimeout,
61
+ stdout_cap_bytes: stdoutCap,
62
+ stderr_cap_bytes: EXEC_DEFAULTS.stderrCapBytes,
63
+ digest_chars: digestChars,
64
+ env_allowlist: allowlist,
65
+ ...extras,
66
+ };
67
+ }
68
+
69
+ function clampNumber(value: unknown, fallback: number, min: number, max: number): number {
70
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
71
+ if (value < min) return min;
72
+ if (value > max) return max;
73
+ return Math.floor(value);
74
+ }
75
+
76
+ function isEnabled(prefs: ExecToolDeps["preferences"]): boolean {
77
+ return isContextModeEnabled(prefs);
78
+ }
79
+
80
+ function disabledResult(): ToolExecutionResult {
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text:
86
+ "gsd_exec is disabled by `context_mode.enabled: false` in preferences. Remove that " +
87
+ "override (or set it to true) to re-enable sandboxed tool-output execution.",
88
+ },
89
+ ],
90
+ details: { operation: "gsd_exec", error: "context_mode_disabled" },
91
+ isError: true,
92
+ };
93
+ }
94
+
95
+ function paramError(message: string): ToolExecutionResult {
96
+ return {
97
+ content: [{ type: "text", text: `Error: ${message}` }],
98
+ details: { operation: "gsd_exec", error: "invalid_params", detail: message },
99
+ isError: true,
100
+ };
101
+ }
102
+
103
+ export async function executeGsdExec(
104
+ params: ExecToolParams,
105
+ deps: ExecToolDeps,
106
+ ): Promise<ToolExecutionResult> {
107
+ if (!isEnabled(deps.preferences)) return disabledResult();
108
+
109
+ const runtime = params.runtime;
110
+ if (runtime !== "bash" && runtime !== "node" && runtime !== "python") {
111
+ return paramError(`invalid runtime "${String(runtime)}" — must be bash | node | python`);
112
+ }
113
+ const script = typeof params.script === "string" ? params.script : "";
114
+ if (script.trim().length === 0) {
115
+ return paramError("script is required and must be a non-empty string");
116
+ }
117
+ if (Buffer.byteLength(script, "utf8") > 200_000) {
118
+ return paramError("script exceeds the 200 KB length limit");
119
+ }
120
+
121
+ const opts = buildExecOptions(
122
+ deps.baseDir,
123
+ deps.preferences?.context_mode,
124
+ { now: deps.now, generateId: deps.generateId },
125
+ );
126
+ const run = deps.run ?? runExecSandbox;
127
+
128
+ try {
129
+ const result = await run(
130
+ {
131
+ runtime,
132
+ script,
133
+ ...(typeof params.purpose === "string" ? { purpose: params.purpose } : {}),
134
+ ...(typeof params.timeout_ms === "number" ? { timeout_ms: params.timeout_ms } : {}),
135
+ },
136
+ opts,
137
+ );
138
+ return formatResult(result);
139
+ } catch (err) {
140
+ const message = err instanceof Error ? err.message : String(err);
141
+ return {
142
+ content: [{ type: "text", text: `Error: gsd_exec failed — ${message}` }],
143
+ details: { operation: "gsd_exec", error: message },
144
+ isError: true,
145
+ };
146
+ }
147
+ }
148
+
149
+ function formatResult(result: ExecSandboxResult): ToolExecutionResult {
150
+ const headerLines = [
151
+ `gsd_exec[${result.id}] runtime=${result.runtime} exit=${formatExit(result)} duration=${result.duration_ms}ms`,
152
+ ` stdout: ${result.stdout_bytes}B${result.stdout_truncated ? " (truncated)" : ""} → ${result.stdout_path}`,
153
+ ` stderr: ${result.stderr_bytes}B${result.stderr_truncated ? " (truncated)" : ""} → ${result.stderr_path}`,
154
+ ];
155
+ const summary = `${headerLines.join("\n")}\n--- digest ---\n${result.digest}`.trimEnd();
156
+ return {
157
+ content: [{ type: "text", text: summary }],
158
+ details: {
159
+ operation: "gsd_exec",
160
+ id: result.id,
161
+ runtime: result.runtime,
162
+ exit_code: result.exit_code,
163
+ signal: result.signal,
164
+ timed_out: result.timed_out,
165
+ duration_ms: result.duration_ms,
166
+ stdout_bytes: result.stdout_bytes,
167
+ stderr_bytes: result.stderr_bytes,
168
+ stdout_truncated: result.stdout_truncated,
169
+ stderr_truncated: result.stderr_truncated,
170
+ stdout_path: result.stdout_path,
171
+ stderr_path: result.stderr_path,
172
+ meta_path: result.meta_path,
173
+ },
174
+ isError: result.timed_out || result.signal !== null || result.exit_code !== 0,
175
+ };
176
+ }
177
+
178
+ function formatExit(result: ExecSandboxResult): string {
179
+ if (result.timed_out) return "timeout";
180
+ if (result.signal) return `signal:${result.signal}`;
181
+ if (result.exit_code === null) return "null";
182
+ return String(result.exit_code);
183
+ }
@@ -0,0 +1,40 @@
1
+ // GSD Resume Tool — returns the contents of .gsd/last-snapshot.md so
2
+ // agents can re-orient after compaction or session resume without
3
+ // re-deriving project memory state.
4
+
5
+ import { readCompactionSnapshot } from "../compaction-snapshot.js";
6
+
7
+ export interface ResumeToolParams {
8
+ /** Ignored — reserved for future variant (e.g. dated snapshots). */
9
+ _variant?: string;
10
+ }
11
+
12
+ export interface ToolExecutionResult {
13
+ content: Array<{ type: "text"; text: string }>;
14
+ details: Record<string, unknown>;
15
+ isError?: boolean;
16
+ }
17
+
18
+ export function executeResume(
19
+ _params: ResumeToolParams,
20
+ opts: { baseDir: string },
21
+ ): ToolExecutionResult {
22
+ const snapshot = readCompactionSnapshot(opts.baseDir);
23
+ if (snapshot == null) {
24
+ return {
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text:
29
+ "No snapshot found at .gsd/last-snapshot.md. The snapshot is written automatically " +
30
+ "on session_before_compact (enabled by default; set context_mode.enabled=false to opt out).",
31
+ },
32
+ ],
33
+ details: { operation: "gsd_resume", found: false },
34
+ };
35
+ }
36
+ return {
37
+ content: [{ type: "text", text: snapshot }],
38
+ details: { operation: "gsd_resume", found: true, bytes: Buffer.byteLength(snapshot, "utf-8") },
39
+ };
40
+ }
@@ -38,6 +38,29 @@ function hasFileContent(path: string | null): boolean {
38
38
  }
39
39
  }
40
40
 
41
+ function getArtifactLookupBases(basePath: string): string[] {
42
+ const bases = [basePath];
43
+ const projectRoot = process.env.GSD_PROJECT_ROOT;
44
+ if (projectRoot && projectRoot.trim().length > 0 && projectRoot !== basePath) {
45
+ bases.push(projectRoot);
46
+ }
47
+ return bases;
48
+ }
49
+
50
+ function hasMilestoneFileContent(
51
+ basePath: string,
52
+ milestoneId: string,
53
+ suffix: string,
54
+ ): boolean {
55
+ const bases = getArtifactLookupBases(basePath);
56
+ for (const candidateBase of bases) {
57
+ if (hasFileContent(resolveMilestoneFile(candidateBase, milestoneId, suffix))) {
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+
41
64
  function countSliceResearchArtifacts(basePath: string, milestoneId: string, slices: SliceRow[]): number {
42
65
  let count = 0;
43
66
  for (const slice of slices) {
@@ -60,9 +83,9 @@ export function compileUnitGraphFromState(basePath: string, state: GSDState): Pl
60
83
  const slices = getMilestoneSlices(mid).sort((a, b) => Number(a.sequence ?? 0) - Number(b.sequence ?? 0));
61
84
  const nodes: UokGraphNode[] = [];
62
85
  const clarifyRoundLimit = PLAN_V2_CLARIFY_ROUND_LIMIT;
63
- const draftContextIncluded = hasFileContent(resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT"));
64
- const finalizedContextIncluded = hasFileContent(resolveMilestoneFile(basePath, mid, "CONTEXT"));
65
- const researchSynthesized = hasFileContent(resolveMilestoneFile(basePath, mid, "RESEARCH"))
86
+ const draftContextIncluded = hasMilestoneFileContent(basePath, mid, "CONTEXT-DRAFT");
87
+ const finalizedContextIncluded = hasMilestoneFileContent(basePath, mid, "CONTEXT");
88
+ const researchSynthesized = hasMilestoneFileContent(basePath, mid, "RESEARCH")
66
89
  || countSliceResearchArtifacts(basePath, mid, slices) > 0;
67
90
 
68
91
  if (isExecutionEntryPhase(state.phase) && !finalizedContextIncluded) {
@@ -57,7 +57,9 @@ export type LogComponent =
57
57
  | "ecosystem" // GSD ecosystem extension loader and dispatch
58
58
  | "memory-embeddings" // Memory layer embedding generation
59
59
  | "memory-ingest" // Memory layer ingestion pipeline
60
- | "memory-backfill"; // ADR-013: decisions->memories backfill
60
+ | "memory-backfill" // ADR-013: decisions->memories backfill
61
+ | "context-mode" // Context-mode exec sandbox and compaction snapshot
62
+ | "preflight"; // Clean-root preflight gate at milestone completion
61
63
 
62
64
  export interface LogEntry {
63
65
  ts: string;
@@ -23,6 +23,9 @@ export interface WorkflowCapabilityOptions {
23
23
  const MCP_WORKFLOW_TOOL_SURFACE = new Set([
24
24
  "ask_user_questions",
25
25
  "gsd_decision_save",
26
+ "gsd_exec",
27
+ "gsd_exec_search",
28
+ "gsd_resume",
26
29
  "gsd_complete_milestone",
27
30
  "gsd_complete_task",
28
31
  "gsd_complete_slice",
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: verify-before-complete
3
- description: Block completion claims until verification evidence has been produced in the current message. Use before marking a task/slice/milestone complete, before creating a commit or PR, before saying "it works" or "tests pass", and any time you are about to claim work is done. The rule is: evidence before claims, always — running the verification must happen now, not "earlier in the session". Fresh output or no claim.
3
+ description: >-
4
+ Block completion claims until verification evidence has been produced in the current message. Use before marking a task/slice/milestone complete, before creating a commit or PR, before saying "it works" or "tests pass", and any time you are about to claim work is done. The rule is: evidence before claims, always — running the verification must happen now, not "earlier in the session". Fresh output or no claim.
4
5
  ---
5
6
 
6
7
  <objective>
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: write-docs
3
- description: Collaborative document authoring workflow for proposals, technical specs, decision docs, README sections, ADRs, and long-form prose that must work for fresh readers. Use when asked to "write the docs", "draft a proposal", "write a spec", "write an RFC", "write the README", or when a document needs to be understandable by someone without this session's context. Three stages: gather context, iterate on structure, reader-test for a stranger.
3
+ description: >-
4
+ Collaborative document authoring workflow for proposals, technical specs, decision docs, README sections, ADRs, and long-form prose that must work for fresh readers. Use when asked to "write the docs", "draft a proposal", "write a spec", "write an RFC", "write the README", or when a document needs to be understandable by someone without this session's context. Three stages: gather context, iterate on structure, reader-test for a stranger.
4
5
  ---
5
6
 
6
7
  <objective>