gsd-remix 1.0.0

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 (554) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +939 -0
  3. package/README.zh-CN.md +876 -0
  4. package/agents/gsd-advisor-researcher.md +127 -0
  5. package/agents/gsd-ai-researcher.md +133 -0
  6. package/agents/gsd-assumptions-analyzer.md +105 -0
  7. package/agents/gsd-code-fixer.md +517 -0
  8. package/agents/gsd-code-reviewer.md +371 -0
  9. package/agents/gsd-codebase-mapper.md +781 -0
  10. package/agents/gsd-debug-session-manager.md +314 -0
  11. package/agents/gsd-debugger.md +1452 -0
  12. package/agents/gsd-doc-classifier.md +168 -0
  13. package/agents/gsd-doc-synthesizer.md +204 -0
  14. package/agents/gsd-doc-verifier.md +217 -0
  15. package/agents/gsd-doc-writer.md +615 -0
  16. package/agents/gsd-domain-researcher.md +153 -0
  17. package/agents/gsd-eval-auditor.md +191 -0
  18. package/agents/gsd-eval-planner.md +154 -0
  19. package/agents/gsd-executor.md +603 -0
  20. package/agents/gsd-framework-selector.md +160 -0
  21. package/agents/gsd-integration-checker.md +470 -0
  22. package/agents/gsd-intel-updater.md +334 -0
  23. package/agents/gsd-nyquist-auditor.md +203 -0
  24. package/agents/gsd-pattern-mapper.md +335 -0
  25. package/agents/gsd-phase-researcher.md +841 -0
  26. package/agents/gsd-plan-checker.md +978 -0
  27. package/agents/gsd-planner.md +1251 -0
  28. package/agents/gsd-project-researcher.md +677 -0
  29. package/agents/gsd-research-synthesizer.md +247 -0
  30. package/agents/gsd-roadmapper.md +688 -0
  31. package/agents/gsd-security-auditor.md +155 -0
  32. package/agents/gsd-ui-auditor.md +495 -0
  33. package/agents/gsd-ui-checker.md +309 -0
  34. package/agents/gsd-ui-researcher.md +380 -0
  35. package/agents/gsd-user-profiler.md +171 -0
  36. package/agents/gsd-verifier.md +830 -0
  37. package/bin/install.js +7062 -0
  38. package/commands/gsd/add-backlog.md +79 -0
  39. package/commands/gsd/add-phase.md +43 -0
  40. package/commands/gsd/add-tests.md +41 -0
  41. package/commands/gsd/add-todo.md +47 -0
  42. package/commands/gsd/ai-integration-phase.md +36 -0
  43. package/commands/gsd/analyze-dependencies.md +34 -0
  44. package/commands/gsd/audit-fix.md +33 -0
  45. package/commands/gsd/audit-milestone.md +36 -0
  46. package/commands/gsd/audit-uat.md +24 -0
  47. package/commands/gsd/autonomous.md +46 -0
  48. package/commands/gsd/check-todos.md +45 -0
  49. package/commands/gsd/cleanup.md +23 -0
  50. package/commands/gsd/code-review-fix.md +52 -0
  51. package/commands/gsd/code-review.md +55 -0
  52. package/commands/gsd/complete-milestone.md +136 -0
  53. package/commands/gsd/debug.md +263 -0
  54. package/commands/gsd/discuss-phase.md +69 -0
  55. package/commands/gsd/do.md +30 -0
  56. package/commands/gsd/docs-update.md +48 -0
  57. package/commands/gsd/eval-review.md +32 -0
  58. package/commands/gsd/execute-phase.md +63 -0
  59. package/commands/gsd/explore.md +27 -0
  60. package/commands/gsd/extract_learnings.md +22 -0
  61. package/commands/gsd/fast.md +30 -0
  62. package/commands/gsd/forensics.md +56 -0
  63. package/commands/gsd/from-gsd2.md +47 -0
  64. package/commands/gsd/graphify.md +201 -0
  65. package/commands/gsd/health.md +22 -0
  66. package/commands/gsd/help.md +24 -0
  67. package/commands/gsd/import.md +37 -0
  68. package/commands/gsd/inbox.md +38 -0
  69. package/commands/gsd/ingest-docs.md +42 -0
  70. package/commands/gsd/insert-phase.md +32 -0
  71. package/commands/gsd/intel.md +179 -0
  72. package/commands/gsd/join-discord.md +19 -0
  73. package/commands/gsd/list-phase-assumptions.md +46 -0
  74. package/commands/gsd/list-workspaces.md +19 -0
  75. package/commands/gsd/manager.md +40 -0
  76. package/commands/gsd/map-codebase.md +71 -0
  77. package/commands/gsd/milestone-summary.md +51 -0
  78. package/commands/gsd/new-milestone.md +44 -0
  79. package/commands/gsd/new-project.md +46 -0
  80. package/commands/gsd/new-workspace.md +44 -0
  81. package/commands/gsd/next.md +28 -0
  82. package/commands/gsd/note.md +34 -0
  83. package/commands/gsd/pause-work.md +38 -0
  84. package/commands/gsd/plan-milestone-gaps.md +34 -0
  85. package/commands/gsd/plan-phase.md +52 -0
  86. package/commands/gsd/plan-review-convergence.md +52 -0
  87. package/commands/gsd/plant-seed.md +28 -0
  88. package/commands/gsd/pr-branch.md +25 -0
  89. package/commands/gsd/profile-user.md +46 -0
  90. package/commands/gsd/progress.md +25 -0
  91. package/commands/gsd/quick.md +173 -0
  92. package/commands/gsd/reapply-patches.md +331 -0
  93. package/commands/gsd/remove-phase.md +31 -0
  94. package/commands/gsd/remove-workspace.md +26 -0
  95. package/commands/gsd/research-phase.md +195 -0
  96. package/commands/gsd/resume-work.md +40 -0
  97. package/commands/gsd/review-backlog.md +62 -0
  98. package/commands/gsd/review.md +40 -0
  99. package/commands/gsd/scan.md +26 -0
  100. package/commands/gsd/secure-phase.md +35 -0
  101. package/commands/gsd/session-report.md +19 -0
  102. package/commands/gsd/set-profile.md +12 -0
  103. package/commands/gsd/settings.md +36 -0
  104. package/commands/gsd/ship.md +23 -0
  105. package/commands/gsd/sketch-wrap-up.md +31 -0
  106. package/commands/gsd/sketch.md +49 -0
  107. package/commands/gsd/spec-phase.md +62 -0
  108. package/commands/gsd/spike-wrap-up.md +31 -0
  109. package/commands/gsd/spike.md +46 -0
  110. package/commands/gsd/stats.md +18 -0
  111. package/commands/gsd/sync-skills.md +19 -0
  112. package/commands/gsd/thread.md +227 -0
  113. package/commands/gsd/ui-phase.md +34 -0
  114. package/commands/gsd/ui-review.md +32 -0
  115. package/commands/gsd/ultraplan-phase.md +33 -0
  116. package/commands/gsd/undo.md +34 -0
  117. package/commands/gsd/update.md +37 -0
  118. package/commands/gsd/validate-phase.md +35 -0
  119. package/commands/gsd/verify-work.md +38 -0
  120. package/commands/gsd/workstreams.md +69 -0
  121. package/get-shit-done/bin/gsd-tools.cjs +1263 -0
  122. package/get-shit-done/bin/lib/artifacts.cjs +52 -0
  123. package/get-shit-done/bin/lib/audit.cjs +757 -0
  124. package/get-shit-done/bin/lib/commands.cjs +1023 -0
  125. package/get-shit-done/bin/lib/config-schema.cjs +79 -0
  126. package/get-shit-done/bin/lib/config.cjs +463 -0
  127. package/get-shit-done/bin/lib/core.cjs +1794 -0
  128. package/get-shit-done/bin/lib/docs.cjs +267 -0
  129. package/get-shit-done/bin/lib/frontmatter.cjs +379 -0
  130. package/get-shit-done/bin/lib/graphify.cjs +494 -0
  131. package/get-shit-done/bin/lib/gsd2-import.cjs +511 -0
  132. package/get-shit-done/bin/lib/init.cjs +1878 -0
  133. package/get-shit-done/bin/lib/intel.cjs +639 -0
  134. package/get-shit-done/bin/lib/learnings.cjs +378 -0
  135. package/get-shit-done/bin/lib/milestone.cjs +283 -0
  136. package/get-shit-done/bin/lib/model-profiles.cjs +71 -0
  137. package/get-shit-done/bin/lib/phase.cjs +1058 -0
  138. package/get-shit-done/bin/lib/profile-output.cjs +1080 -0
  139. package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
  140. package/get-shit-done/bin/lib/roadmap.cjs +523 -0
  141. package/get-shit-done/bin/lib/schema-detect.cjs +238 -0
  142. package/get-shit-done/bin/lib/security.cjs +504 -0
  143. package/get-shit-done/bin/lib/state.cjs +1649 -0
  144. package/get-shit-done/bin/lib/template.cjs +226 -0
  145. package/get-shit-done/bin/lib/uat.cjs +288 -0
  146. package/get-shit-done/bin/lib/verify.cjs +1184 -0
  147. package/get-shit-done/bin/lib/workstream.cjs +495 -0
  148. package/get-shit-done/bin/repair-sdk.cjs +177 -0
  149. package/get-shit-done/contexts/dev.md +21 -0
  150. package/get-shit-done/contexts/research.md +22 -0
  151. package/get-shit-done/contexts/review.md +22 -0
  152. package/get-shit-done/references/agent-contracts.md +79 -0
  153. package/get-shit-done/references/ai-evals.md +156 -0
  154. package/get-shit-done/references/ai-frameworks.md +186 -0
  155. package/get-shit-done/references/artifact-types.md +131 -0
  156. package/get-shit-done/references/autonomous-smart-discuss.md +277 -0
  157. package/get-shit-done/references/checkpoints.md +808 -0
  158. package/get-shit-done/references/common-bug-patterns.md +114 -0
  159. package/get-shit-done/references/context-budget.md +49 -0
  160. package/get-shit-done/references/continuation-format.md +253 -0
  161. package/get-shit-done/references/debugger-philosophy.md +76 -0
  162. package/get-shit-done/references/decimal-phase-calculation.md +64 -0
  163. package/get-shit-done/references/doc-conflict-engine.md +91 -0
  164. package/get-shit-done/references/domain-probes.md +125 -0
  165. package/get-shit-done/references/executor-examples.md +110 -0
  166. package/get-shit-done/references/few-shot-examples/plan-checker.md +73 -0
  167. package/get-shit-done/references/few-shot-examples/verifier.md +109 -0
  168. package/get-shit-done/references/gate-prompts.md +100 -0
  169. package/get-shit-done/references/gates.md +70 -0
  170. package/get-shit-done/references/git-integration.md +295 -0
  171. package/get-shit-done/references/git-planning-commit.md +40 -0
  172. package/get-shit-done/references/ios-scaffold.md +123 -0
  173. package/get-shit-done/references/mandatory-initial-read.md +2 -0
  174. package/get-shit-done/references/model-profile-resolution.md +38 -0
  175. package/get-shit-done/references/model-profiles.md +145 -0
  176. package/get-shit-done/references/phase-argument-parsing.md +61 -0
  177. package/get-shit-done/references/planner-antipatterns.md +89 -0
  178. package/get-shit-done/references/planner-gap-closure.md +62 -0
  179. package/get-shit-done/references/planner-reviews.md +39 -0
  180. package/get-shit-done/references/planner-revision.md +87 -0
  181. package/get-shit-done/references/planner-source-audit.md +73 -0
  182. package/get-shit-done/references/planning-config.md +460 -0
  183. package/get-shit-done/references/project-skills-discovery.md +19 -0
  184. package/get-shit-done/references/questioning.md +162 -0
  185. package/get-shit-done/references/revision-loop.md +97 -0
  186. package/get-shit-done/references/sketch-interactivity.md +41 -0
  187. package/get-shit-done/references/sketch-theme-system.md +94 -0
  188. package/get-shit-done/references/sketch-tooling.md +45 -0
  189. package/get-shit-done/references/sketch-variant-patterns.md +81 -0
  190. package/get-shit-done/references/tdd.md +330 -0
  191. package/get-shit-done/references/thinking-models-debug.md +44 -0
  192. package/get-shit-done/references/thinking-models-execution.md +50 -0
  193. package/get-shit-done/references/thinking-models-planning.md +62 -0
  194. package/get-shit-done/references/thinking-models-research.md +50 -0
  195. package/get-shit-done/references/thinking-models-verification.md +55 -0
  196. package/get-shit-done/references/thinking-partner.md +96 -0
  197. package/get-shit-done/references/ui-brand.md +160 -0
  198. package/get-shit-done/references/universal-anti-patterns.md +63 -0
  199. package/get-shit-done/references/user-profiling.md +681 -0
  200. package/get-shit-done/references/verification-overrides.md +227 -0
  201. package/get-shit-done/references/verification-patterns.md +612 -0
  202. package/get-shit-done/references/workstream-flag.md +111 -0
  203. package/get-shit-done/templates/AI-SPEC.md +246 -0
  204. package/get-shit-done/templates/DEBUG.md +169 -0
  205. package/get-shit-done/templates/README.md +76 -0
  206. package/get-shit-done/templates/SECURITY.md +61 -0
  207. package/get-shit-done/templates/UAT.md +265 -0
  208. package/get-shit-done/templates/UI-SPEC.md +100 -0
  209. package/get-shit-done/templates/VALIDATION.md +76 -0
  210. package/get-shit-done/templates/claude-md.md +145 -0
  211. package/get-shit-done/templates/codebase/architecture.md +255 -0
  212. package/get-shit-done/templates/codebase/concerns.md +310 -0
  213. package/get-shit-done/templates/codebase/conventions.md +307 -0
  214. package/get-shit-done/templates/codebase/integrations.md +280 -0
  215. package/get-shit-done/templates/codebase/stack.md +186 -0
  216. package/get-shit-done/templates/codebase/structure.md +285 -0
  217. package/get-shit-done/templates/codebase/testing.md +480 -0
  218. package/get-shit-done/templates/config.json +56 -0
  219. package/get-shit-done/templates/context.md +352 -0
  220. package/get-shit-done/templates/continue-here.md +78 -0
  221. package/get-shit-done/templates/copilot-instructions.md +7 -0
  222. package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
  223. package/get-shit-done/templates/dev-preferences.md +21 -0
  224. package/get-shit-done/templates/discovery.md +146 -0
  225. package/get-shit-done/templates/discussion-log.md +63 -0
  226. package/get-shit-done/templates/milestone-archive.md +123 -0
  227. package/get-shit-done/templates/milestone.md +115 -0
  228. package/get-shit-done/templates/phase-prompt.md +610 -0
  229. package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
  230. package/get-shit-done/templates/project.md +186 -0
  231. package/get-shit-done/templates/requirements.md +231 -0
  232. package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
  233. package/get-shit-done/templates/research-project/FEATURES.md +147 -0
  234. package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
  235. package/get-shit-done/templates/research-project/STACK.md +120 -0
  236. package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
  237. package/get-shit-done/templates/research.md +592 -0
  238. package/get-shit-done/templates/retrospective.md +54 -0
  239. package/get-shit-done/templates/roadmap.md +202 -0
  240. package/get-shit-done/templates/spec.md +307 -0
  241. package/get-shit-done/templates/state.md +184 -0
  242. package/get-shit-done/templates/summary-complex.md +59 -0
  243. package/get-shit-done/templates/summary-minimal.md +41 -0
  244. package/get-shit-done/templates/summary-standard.md +48 -0
  245. package/get-shit-done/templates/summary.md +248 -0
  246. package/get-shit-done/templates/user-profile.md +146 -0
  247. package/get-shit-done/templates/user-setup.md +311 -0
  248. package/get-shit-done/templates/verification-report.md +322 -0
  249. package/get-shit-done/workflows/add-phase.md +112 -0
  250. package/get-shit-done/workflows/add-tests.md +354 -0
  251. package/get-shit-done/workflows/add-todo.md +160 -0
  252. package/get-shit-done/workflows/ai-integration-phase.md +284 -0
  253. package/get-shit-done/workflows/analyze-dependencies.md +96 -0
  254. package/get-shit-done/workflows/audit-fix.md +175 -0
  255. package/get-shit-done/workflows/audit-milestone.md +340 -0
  256. package/get-shit-done/workflows/audit-uat.md +109 -0
  257. package/get-shit-done/workflows/autonomous.md +789 -0
  258. package/get-shit-done/workflows/check-todos.md +179 -0
  259. package/get-shit-done/workflows/cleanup.md +154 -0
  260. package/get-shit-done/workflows/code-review-fix.md +497 -0
  261. package/get-shit-done/workflows/code-review.md +515 -0
  262. package/get-shit-done/workflows/complete-milestone.md +847 -0
  263. package/get-shit-done/workflows/diagnose-issues.md +238 -0
  264. package/get-shit-done/workflows/discovery-phase.md +291 -0
  265. package/get-shit-done/workflows/discuss-phase-assumptions.md +670 -0
  266. package/get-shit-done/workflows/discuss-phase-power.md +308 -0
  267. package/get-shit-done/workflows/discuss-phase.md +1378 -0
  268. package/get-shit-done/workflows/do.md +110 -0
  269. package/get-shit-done/workflows/docs-update.md +1155 -0
  270. package/get-shit-done/workflows/eval-review.md +155 -0
  271. package/get-shit-done/workflows/execute-phase.md +1677 -0
  272. package/get-shit-done/workflows/execute-plan.md +533 -0
  273. package/get-shit-done/workflows/explore.md +141 -0
  274. package/get-shit-done/workflows/extract_learnings.md +242 -0
  275. package/get-shit-done/workflows/fast.md +105 -0
  276. package/get-shit-done/workflows/forensics.md +265 -0
  277. package/get-shit-done/workflows/graduation.md +195 -0
  278. package/get-shit-done/workflows/health.md +314 -0
  279. package/get-shit-done/workflows/help.md +667 -0
  280. package/get-shit-done/workflows/import.md +246 -0
  281. package/get-shit-done/workflows/inbox.md +387 -0
  282. package/get-shit-done/workflows/ingest-docs.md +328 -0
  283. package/get-shit-done/workflows/insert-phase.md +130 -0
  284. package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
  285. package/get-shit-done/workflows/list-workspaces.md +56 -0
  286. package/get-shit-done/workflows/manager.md +365 -0
  287. package/get-shit-done/workflows/map-codebase.md +393 -0
  288. package/get-shit-done/workflows/milestone-summary.md +223 -0
  289. package/get-shit-done/workflows/new-milestone.md +611 -0
  290. package/get-shit-done/workflows/new-project.md +1391 -0
  291. package/get-shit-done/workflows/new-workspace.md +239 -0
  292. package/get-shit-done/workflows/next.md +220 -0
  293. package/get-shit-done/workflows/node-repair.md +92 -0
  294. package/get-shit-done/workflows/note.md +158 -0
  295. package/get-shit-done/workflows/pause-work.md +243 -0
  296. package/get-shit-done/workflows/plan-milestone-gaps.md +273 -0
  297. package/get-shit-done/workflows/plan-phase.md +1349 -0
  298. package/get-shit-done/workflows/plan-review-convergence.md +254 -0
  299. package/get-shit-done/workflows/plant-seed.md +172 -0
  300. package/get-shit-done/workflows/pr-branch.md +157 -0
  301. package/get-shit-done/workflows/profile-user.md +452 -0
  302. package/get-shit-done/workflows/progress.md +619 -0
  303. package/get-shit-done/workflows/quick.md +970 -0
  304. package/get-shit-done/workflows/remove-phase.md +155 -0
  305. package/get-shit-done/workflows/remove-workspace.md +92 -0
  306. package/get-shit-done/workflows/research-phase.md +89 -0
  307. package/get-shit-done/workflows/resume-project.md +326 -0
  308. package/get-shit-done/workflows/review.md +344 -0
  309. package/get-shit-done/workflows/scan.md +102 -0
  310. package/get-shit-done/workflows/secure-phase.md +166 -0
  311. package/get-shit-done/workflows/session-report.md +146 -0
  312. package/get-shit-done/workflows/settings.md +319 -0
  313. package/get-shit-done/workflows/ship.md +302 -0
  314. package/get-shit-done/workflows/sketch-wrap-up.md +283 -0
  315. package/get-shit-done/workflows/sketch.md +286 -0
  316. package/get-shit-done/workflows/spec-phase.md +262 -0
  317. package/get-shit-done/workflows/spike-wrap-up.md +281 -0
  318. package/get-shit-done/workflows/spike.md +362 -0
  319. package/get-shit-done/workflows/stats.md +60 -0
  320. package/get-shit-done/workflows/sync-skills.md +182 -0
  321. package/get-shit-done/workflows/transition.md +693 -0
  322. package/get-shit-done/workflows/ui-phase.md +323 -0
  323. package/get-shit-done/workflows/ui-review.md +190 -0
  324. package/get-shit-done/workflows/ultraplan-phase.md +189 -0
  325. package/get-shit-done/workflows/undo.md +314 -0
  326. package/get-shit-done/workflows/update.md +587 -0
  327. package/get-shit-done/workflows/validate-phase.md +176 -0
  328. package/get-shit-done/workflows/verify-phase.md +465 -0
  329. package/get-shit-done/workflows/verify-work.md +740 -0
  330. package/hooks/dist/gsd-check-update-worker.js +108 -0
  331. package/hooks/dist/gsd-check-update.js +64 -0
  332. package/hooks/dist/gsd-context-monitor.js +192 -0
  333. package/hooks/dist/gsd-phase-boundary.sh +28 -0
  334. package/hooks/dist/gsd-prompt-guard.js +97 -0
  335. package/hooks/dist/gsd-read-guard.js +82 -0
  336. package/hooks/dist/gsd-read-injection-scanner.js +152 -0
  337. package/hooks/dist/gsd-session-state.sh +34 -0
  338. package/hooks/dist/gsd-statusline.js +293 -0
  339. package/hooks/dist/gsd-validate-commit.sh +48 -0
  340. package/hooks/dist/gsd-workflow-guard.js +94 -0
  341. package/hooks/gsd-check-update-worker.js +108 -0
  342. package/hooks/gsd-check-update.js +64 -0
  343. package/hooks/gsd-context-monitor.js +192 -0
  344. package/hooks/gsd-phase-boundary.sh +28 -0
  345. package/hooks/gsd-prompt-guard.js +97 -0
  346. package/hooks/gsd-read-guard.js +82 -0
  347. package/hooks/gsd-read-injection-scanner.js +152 -0
  348. package/hooks/gsd-session-state.sh +34 -0
  349. package/hooks/gsd-statusline.js +293 -0
  350. package/hooks/gsd-validate-commit.sh +48 -0
  351. package/hooks/gsd-workflow-guard.js +94 -0
  352. package/package.json +59 -0
  353. package/scripts/base64-scan.sh +262 -0
  354. package/scripts/build-hooks.js +95 -0
  355. package/scripts/gen-inventory-manifest.cjs +109 -0
  356. package/scripts/prompt-injection-scan.sh +201 -0
  357. package/scripts/run-tests.cjs +33 -0
  358. package/scripts/secret-scan.sh +227 -0
  359. package/sdk/package-lock.json +1998 -0
  360. package/sdk/package.json +52 -0
  361. package/sdk/prompts/agents/gsd-executor.md +110 -0
  362. package/sdk/prompts/agents/gsd-phase-researcher.md +158 -0
  363. package/sdk/prompts/agents/gsd-plan-checker.md +160 -0
  364. package/sdk/prompts/agents/gsd-planner.md +214 -0
  365. package/sdk/prompts/agents/gsd-project-researcher.md +323 -0
  366. package/sdk/prompts/agents/gsd-research-synthesizer.md +237 -0
  367. package/sdk/prompts/agents/gsd-roadmapper.md +670 -0
  368. package/sdk/prompts/agents/gsd-verifier.md +159 -0
  369. package/sdk/prompts/templates/project.md +186 -0
  370. package/sdk/prompts/templates/requirements.md +231 -0
  371. package/sdk/prompts/templates/research-project/ARCHITECTURE.md +204 -0
  372. package/sdk/prompts/templates/research-project/FEATURES.md +147 -0
  373. package/sdk/prompts/templates/research-project/PITFALLS.md +200 -0
  374. package/sdk/prompts/templates/research-project/STACK.md +120 -0
  375. package/sdk/prompts/templates/research-project/SUMMARY.md +170 -0
  376. package/sdk/prompts/templates/roadmap.md +202 -0
  377. package/sdk/prompts/templates/state.md +175 -0
  378. package/sdk/prompts/workflows/discuss-phase.md +126 -0
  379. package/sdk/prompts/workflows/execute-plan.md +106 -0
  380. package/sdk/prompts/workflows/plan-phase.md +84 -0
  381. package/sdk/prompts/workflows/research-phase.md +45 -0
  382. package/sdk/prompts/workflows/verify-phase.md +142 -0
  383. package/sdk/src/assembled-prompts.test.ts +349 -0
  384. package/sdk/src/cli-transport.test.ts +388 -0
  385. package/sdk/src/cli-transport.ts +130 -0
  386. package/sdk/src/cli.test.ts +383 -0
  387. package/sdk/src/cli.ts +670 -0
  388. package/sdk/src/config.test.ts +168 -0
  389. package/sdk/src/config.ts +177 -0
  390. package/sdk/src/context-engine.test.ts +295 -0
  391. package/sdk/src/context-engine.ts +170 -0
  392. package/sdk/src/context-truncation.test.ts +163 -0
  393. package/sdk/src/context-truncation.ts +233 -0
  394. package/sdk/src/e2e.integration.test.ts +178 -0
  395. package/sdk/src/errors.ts +72 -0
  396. package/sdk/src/event-stream.test.ts +661 -0
  397. package/sdk/src/event-stream.ts +441 -0
  398. package/sdk/src/failure-memory.test.ts +457 -0
  399. package/sdk/src/failure-memory.ts +1324 -0
  400. package/sdk/src/golden/capture.ts +95 -0
  401. package/sdk/src/golden/fixtures/generate-slug.golden.json +1 -0
  402. package/sdk/src/golden/fixtures/profile-sample-sessions/demo-project/sample.jsonl +3 -0
  403. package/sdk/src/golden/fixtures/summary-extract-sample.md +26 -0
  404. package/sdk/src/golden/fixtures/uat-render-checkpoint-sample.md +15 -0
  405. package/sdk/src/golden/golden-integration-covered.ts +30 -0
  406. package/sdk/src/golden/golden-mutation-covered.ts +7 -0
  407. package/sdk/src/golden/golden-policy.test.ts +8 -0
  408. package/sdk/src/golden/golden-policy.ts +112 -0
  409. package/sdk/src/golden/golden.integration.test.ts +373 -0
  410. package/sdk/src/golden/init-golden-normalize.ts +15 -0
  411. package/sdk/src/golden/read-only-golden-rows.ts +77 -0
  412. package/sdk/src/golden/read-only-parity.integration.test.ts +125 -0
  413. package/sdk/src/golden/registry-canonical-commands.ts +31 -0
  414. package/sdk/src/gsd-tools.test.ts +409 -0
  415. package/sdk/src/gsd-tools.ts +595 -0
  416. package/sdk/src/headless-prompts.test.ts +159 -0
  417. package/sdk/src/index.ts +333 -0
  418. package/sdk/src/init-e2e.integration.test.ts +136 -0
  419. package/sdk/src/init-runner.test.ts +783 -0
  420. package/sdk/src/init-runner.ts +735 -0
  421. package/sdk/src/lifecycle-e2e.integration.test.ts +258 -0
  422. package/sdk/src/logger.test.ts +149 -0
  423. package/sdk/src/logger.ts +113 -0
  424. package/sdk/src/milestone-runner.test.ts +421 -0
  425. package/sdk/src/phase-prompt.test.ts +538 -0
  426. package/sdk/src/phase-prompt.ts +264 -0
  427. package/sdk/src/phase-runner-types.test.ts +421 -0
  428. package/sdk/src/phase-runner.integration.test.ts +377 -0
  429. package/sdk/src/phase-runner.test.ts +2333 -0
  430. package/sdk/src/phase-runner.ts +1203 -0
  431. package/sdk/src/plan-parser.test.ts +528 -0
  432. package/sdk/src/plan-parser.ts +427 -0
  433. package/sdk/src/prompt-builder.test.ts +306 -0
  434. package/sdk/src/prompt-builder.ts +193 -0
  435. package/sdk/src/prompt-sanitizer.test.ts +260 -0
  436. package/sdk/src/prompt-sanitizer.ts +71 -0
  437. package/sdk/src/query/QUERY-HANDLERS.md +317 -0
  438. package/sdk/src/query/audit-open.ts +722 -0
  439. package/sdk/src/query/check-auto-mode.test.ts +77 -0
  440. package/sdk/src/query/check-auto-mode.ts +50 -0
  441. package/sdk/src/query/check-completion.test.ts +113 -0
  442. package/sdk/src/query/check-completion.ts +182 -0
  443. package/sdk/src/query/check-gates.test.ts +103 -0
  444. package/sdk/src/query/check-gates.ts +112 -0
  445. package/sdk/src/query/check-ship-ready.test.ts +77 -0
  446. package/sdk/src/query/check-ship-ready.ts +103 -0
  447. package/sdk/src/query/check-verification-status.test.ts +143 -0
  448. package/sdk/src/query/check-verification-status.ts +160 -0
  449. package/sdk/src/query/commit.test.ts +202 -0
  450. package/sdk/src/query/commit.ts +301 -0
  451. package/sdk/src/query/config-gates.test.ts +89 -0
  452. package/sdk/src/query/config-gates.ts +69 -0
  453. package/sdk/src/query/config-mutation.test.ts +365 -0
  454. package/sdk/src/query/config-mutation.ts +497 -0
  455. package/sdk/src/query/config-query.test.ts +161 -0
  456. package/sdk/src/query/config-query.ts +190 -0
  457. package/sdk/src/query/context-history.test.ts +165 -0
  458. package/sdk/src/query/context-history.ts +467 -0
  459. package/sdk/src/query/decomposed-handlers.test.ts +365 -0
  460. package/sdk/src/query/detect-custom-files.ts +97 -0
  461. package/sdk/src/query/detect-phase-type.test.ts +105 -0
  462. package/sdk/src/query/detect-phase-type.ts +141 -0
  463. package/sdk/src/query/docs-init.ts +257 -0
  464. package/sdk/src/query/failure-capture.ts +58 -0
  465. package/sdk/src/query/frontmatter-array.test.ts +14 -0
  466. package/sdk/src/query/frontmatter-mutation.test.ts +259 -0
  467. package/sdk/src/query/frontmatter-mutation.ts +343 -0
  468. package/sdk/src/query/frontmatter.test.ts +281 -0
  469. package/sdk/src/query/frontmatter.ts +397 -0
  470. package/sdk/src/query/helpers.test.ts +426 -0
  471. package/sdk/src/query/helpers.ts +482 -0
  472. package/sdk/src/query/index.ts +586 -0
  473. package/sdk/src/query/init-complex.test.ts +232 -0
  474. package/sdk/src/query/init-complex.ts +578 -0
  475. package/sdk/src/query/init.test.ts +522 -0
  476. package/sdk/src/query/init.ts +1046 -0
  477. package/sdk/src/query/intel.test.ts +90 -0
  478. package/sdk/src/query/intel.ts +404 -0
  479. package/sdk/src/query/normalize-query-command.test.ts +50 -0
  480. package/sdk/src/query/normalize-query-command.ts +56 -0
  481. package/sdk/src/query/phase-lifecycle.test.ts +1126 -0
  482. package/sdk/src/query/phase-lifecycle.ts +1799 -0
  483. package/sdk/src/query/phase-list-queries.test.ts +88 -0
  484. package/sdk/src/query/phase-list-queries.ts +152 -0
  485. package/sdk/src/query/phase-ready.test.ts +65 -0
  486. package/sdk/src/query/phase-ready.ts +158 -0
  487. package/sdk/src/query/phase.test.ts +307 -0
  488. package/sdk/src/query/phase.ts +340 -0
  489. package/sdk/src/query/pipeline.test.ts +169 -0
  490. package/sdk/src/query/pipeline.ts +243 -0
  491. package/sdk/src/query/plan-execution-route.test.ts +166 -0
  492. package/sdk/src/query/plan-execution-route.ts +209 -0
  493. package/sdk/src/query/plan-task-structure.test.ts +65 -0
  494. package/sdk/src/query/plan-task-structure.ts +63 -0
  495. package/sdk/src/query/profile-extract-messages.ts +247 -0
  496. package/sdk/src/query/profile-output.ts +908 -0
  497. package/sdk/src/query/profile-questionnaire-data.ts +181 -0
  498. package/sdk/src/query/profile-sample.ts +184 -0
  499. package/sdk/src/query/profile-scan-sessions.ts +174 -0
  500. package/sdk/src/query/profile.test.ts +74 -0
  501. package/sdk/src/query/profile.ts +337 -0
  502. package/sdk/src/query/progress.test.ts +156 -0
  503. package/sdk/src/query/progress.ts +566 -0
  504. package/sdk/src/query/registry.test.ts +216 -0
  505. package/sdk/src/query/registry.ts +174 -0
  506. package/sdk/src/query/requirements-extract-from-plans.test.ts +58 -0
  507. package/sdk/src/query/requirements-extract-from-plans.ts +86 -0
  508. package/sdk/src/query/roadmap-update-plan-progress.ts +132 -0
  509. package/sdk/src/query/roadmap.test.ts +359 -0
  510. package/sdk/src/query/roadmap.ts +591 -0
  511. package/sdk/src/query/route-next-action.test.ts +61 -0
  512. package/sdk/src/query/route-next-action.ts +345 -0
  513. package/sdk/src/query/runtime-health.ts +7 -0
  514. package/sdk/src/query/schema-detect.ts +189 -0
  515. package/sdk/src/query/skill-manifest.ts +214 -0
  516. package/sdk/src/query/skills.test.ts +80 -0
  517. package/sdk/src/query/skills.ts +62 -0
  518. package/sdk/src/query/state-mutation.test.ts +450 -0
  519. package/sdk/src/query/state-mutation.ts +1444 -0
  520. package/sdk/src/query/state-project-load.ts +109 -0
  521. package/sdk/src/query/state.test.ts +347 -0
  522. package/sdk/src/query/state.ts +397 -0
  523. package/sdk/src/query/summary.test.ts +95 -0
  524. package/sdk/src/query/summary.ts +296 -0
  525. package/sdk/src/query/template.test.ts +180 -0
  526. package/sdk/src/query/template.ts +242 -0
  527. package/sdk/src/query/uat.test.ts +77 -0
  528. package/sdk/src/query/uat.ts +314 -0
  529. package/sdk/src/query/utils.test.ts +82 -0
  530. package/sdk/src/query/utils.ts +92 -0
  531. package/sdk/src/query/validate.test.ts +656 -0
  532. package/sdk/src/query/validate.ts +807 -0
  533. package/sdk/src/query/verify.test.ts +414 -0
  534. package/sdk/src/query/verify.ts +645 -0
  535. package/sdk/src/query/websearch.test.ts +31 -0
  536. package/sdk/src/query/websearch.ts +82 -0
  537. package/sdk/src/query/workspace.test.ts +119 -0
  538. package/sdk/src/query/workspace.ts +131 -0
  539. package/sdk/src/query/workstream.test.ts +51 -0
  540. package/sdk/src/query/workstream.ts +434 -0
  541. package/sdk/src/research-gate.test.ts +190 -0
  542. package/sdk/src/research-gate.ts +94 -0
  543. package/sdk/src/runtime-health.test.ts +176 -0
  544. package/sdk/src/runtime-health.ts +387 -0
  545. package/sdk/src/session-runner.test.ts +98 -0
  546. package/sdk/src/session-runner.ts +299 -0
  547. package/sdk/src/tool-scoping.test.ts +160 -0
  548. package/sdk/src/tool-scoping.ts +61 -0
  549. package/sdk/src/types.ts +917 -0
  550. package/sdk/src/workstream-utils.ts +33 -0
  551. package/sdk/src/ws-flag.test.ts +285 -0
  552. package/sdk/src/ws-transport.test.ts +161 -0
  553. package/sdk/src/ws-transport.ts +93 -0
  554. package/sdk/tsconfig.json +20 -0
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Unit tests for frontmatter mutation handlers.
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { mkdtemp, writeFile, readFile, rm } from 'node:fs/promises';
7
+ import { join } from 'node:path';
8
+ import { tmpdir } from 'node:os';
9
+ import {
10
+ reconstructFrontmatter,
11
+ spliceFrontmatter,
12
+ frontmatterSet,
13
+ frontmatterMerge,
14
+ frontmatterValidate,
15
+ FRONTMATTER_SCHEMAS,
16
+ } from './frontmatter-mutation.js';
17
+ import { extractFrontmatter } from './frontmatter.js';
18
+
19
+ // ─── reconstructFrontmatter ─────────────────────────────────────────────────
20
+
21
+ describe('reconstructFrontmatter', () => {
22
+ it('serializes flat key-value pairs', () => {
23
+ const result = reconstructFrontmatter({ phase: '10', plan: '01' });
24
+ expect(result).toContain('phase: 10');
25
+ expect(result).toContain('plan: 01');
26
+ });
27
+
28
+ it('serializes short arrays inline', () => {
29
+ const result = reconstructFrontmatter({ tags: ['a', 'b', 'c'] });
30
+ expect(result).toBe('tags: [a, b, c]');
31
+ });
32
+
33
+ it('serializes long arrays as dash items', () => {
34
+ const result = reconstructFrontmatter({
35
+ items: ['alpha', 'bravo', 'charlie', 'delta'],
36
+ });
37
+ expect(result).toContain('items:');
38
+ expect(result).toContain(' - alpha');
39
+ expect(result).toContain(' - delta');
40
+ });
41
+
42
+ it('serializes empty arrays as []', () => {
43
+ const result = reconstructFrontmatter({ depends_on: [] });
44
+ expect(result).toBe('depends_on: []');
45
+ });
46
+
47
+ it('serializes nested objects with 2-space indent', () => {
48
+ const result = reconstructFrontmatter({ progress: { total: 5, done: 3 } });
49
+ expect(result).toContain('progress:');
50
+ expect(result).toContain(' total: 5');
51
+ expect(result).toContain(' done: 3');
52
+ });
53
+
54
+ it('skips null and undefined values', () => {
55
+ const result = reconstructFrontmatter({ a: 'yes', b: null, c: undefined });
56
+ expect(result).toBe('a: yes');
57
+ });
58
+
59
+ it('quotes strings containing colons', () => {
60
+ const result = reconstructFrontmatter({ label: 'key: value' });
61
+ expect(result).toContain('"key: value"');
62
+ });
63
+
64
+ it('quotes strings containing hash', () => {
65
+ const result = reconstructFrontmatter({ label: 'color #red' });
66
+ expect(result).toContain('"color #red"');
67
+ });
68
+
69
+ it('quotes strings starting with [ or {', () => {
70
+ const result = reconstructFrontmatter({ data: '[1,2,3]' });
71
+ expect(result).toContain('"[1,2,3]"');
72
+ });
73
+ });
74
+
75
+ // ─── spliceFrontmatter ──────────────────────────────────────────────────────
76
+
77
+ describe('spliceFrontmatter', () => {
78
+ it('replaces existing frontmatter block', () => {
79
+ const content = '---\nphase: 10\n---\n\n# Body';
80
+ const result = spliceFrontmatter(content, { phase: '11', plan: '01' });
81
+ expect(result).toMatch(/^---\nphase: 11\nplan: 01\n---/);
82
+ expect(result).toContain('# Body');
83
+ });
84
+
85
+ it('prepends frontmatter when none exists', () => {
86
+ const content = '# Just a body';
87
+ const result = spliceFrontmatter(content, { phase: '10' });
88
+ expect(result).toMatch(/^---\nphase: 10\n---\n\n# Just a body/);
89
+ });
90
+ });
91
+
92
+ // ─── frontmatterSet ─────────────────────────────────────────────────────────
93
+
94
+ describe('frontmatterSet', () => {
95
+ let tmpDir: string;
96
+
97
+ beforeEach(async () => {
98
+ tmpDir = await mkdtemp(join(tmpdir(), 'gsd-fm-set-'));
99
+ });
100
+
101
+ afterEach(async () => {
102
+ await rm(tmpDir, { recursive: true, force: true });
103
+ });
104
+
105
+ it('writes a single field and round-trips through extractFrontmatter', async () => {
106
+ const filePath = join(tmpDir, 'test.md');
107
+ await writeFile(filePath, '---\nphase: 10\nplan: 01\n---\n\n# Body\n');
108
+
109
+ await frontmatterSet([filePath, 'status', 'executing'], tmpDir);
110
+
111
+ const content = await readFile(filePath, 'utf-8');
112
+ const fm = extractFrontmatter(content);
113
+ expect(fm.status).toBe('executing');
114
+ expect(fm.phase).toBe('10');
115
+ });
116
+
117
+ it('converts boolean string values', async () => {
118
+ const filePath = join(tmpDir, 'test.md');
119
+ await writeFile(filePath, '---\nphase: 10\n---\n\n# Body\n');
120
+
121
+ await frontmatterSet([filePath, 'autonomous', 'true'], tmpDir);
122
+
123
+ const content = await readFile(filePath, 'utf-8');
124
+ const fm = extractFrontmatter(content);
125
+ expect(fm.autonomous).toBe('true');
126
+ });
127
+
128
+ it('handles numeric string values', async () => {
129
+ const filePath = join(tmpDir, 'test.md');
130
+ await writeFile(filePath, '---\nphase: 10\n---\n\n# Body\n');
131
+
132
+ await frontmatterSet([filePath, 'wave', '3'], tmpDir);
133
+
134
+ const content = await readFile(filePath, 'utf-8');
135
+ const fm = extractFrontmatter(content);
136
+ // reconstructFrontmatter outputs the number, extractFrontmatter reads it back as string
137
+ expect(String(fm.wave)).toBe('3');
138
+ });
139
+
140
+ it('rejects null bytes in file path', async () => {
141
+ await expect(
142
+ frontmatterSet(['/path/with\0null', 'key', 'val'], tmpDir)
143
+ ).rejects.toThrow(/null bytes/);
144
+ });
145
+ });
146
+
147
+ // ─── frontmatterMerge ───────────────────────────────────────────────────────
148
+
149
+ describe('frontmatterMerge', () => {
150
+ let tmpDir: string;
151
+
152
+ beforeEach(async () => {
153
+ tmpDir = await mkdtemp(join(tmpdir(), 'gsd-fm-merge-'));
154
+ });
155
+
156
+ afterEach(async () => {
157
+ await rm(tmpDir, { recursive: true, force: true });
158
+ });
159
+
160
+ it('deep merges JSON into existing frontmatter', async () => {
161
+ const filePath = join(tmpDir, 'test.md');
162
+ await writeFile(filePath, '---\nphase: 10\nplan: 01\n---\n\n# Body\n');
163
+
164
+ const result = await frontmatterMerge(
165
+ [filePath, JSON.stringify({ status: 'done', wave: 2 })],
166
+ tmpDir
167
+ );
168
+
169
+ const content = await readFile(filePath, 'utf-8');
170
+ const fm = extractFrontmatter(content);
171
+ expect(fm.phase).toBe('10');
172
+ expect(fm.status).toBe('done');
173
+ expect((result.data as Record<string, unknown>).merged).toBe(true);
174
+ });
175
+
176
+ it('rejects invalid JSON', async () => {
177
+ const filePath = join(tmpDir, 'test.md');
178
+ await writeFile(filePath, '---\nphase: 10\n---\n\n# Body\n');
179
+
180
+ await expect(
181
+ frontmatterMerge([filePath, 'not-json'], tmpDir)
182
+ ).rejects.toThrow();
183
+ });
184
+ });
185
+
186
+ // ─── frontmatterValidate ────────────────────────────────────────────────────
187
+
188
+ describe('frontmatterValidate', () => {
189
+ let tmpDir: string;
190
+
191
+ beforeEach(async () => {
192
+ tmpDir = await mkdtemp(join(tmpdir(), 'gsd-fm-validate-'));
193
+ });
194
+
195
+ afterEach(async () => {
196
+ await rm(tmpDir, { recursive: true, force: true });
197
+ });
198
+
199
+ it('validates a valid plan file', async () => {
200
+ const filePath = join(tmpDir, 'plan.md');
201
+ const fm = '---\nphase: 10\nplan: 01\ntype: execute\nwave: 1\ndepends_on: []\nfiles_modified: []\nautonomous: true\nmust_haves:\n truths:\n - foo\n---\n\n# Plan\n';
202
+ await writeFile(filePath, fm);
203
+
204
+ const result = await frontmatterValidate([filePath, '--schema', 'plan'], tmpDir);
205
+ const data = result.data as Record<string, unknown>;
206
+ expect(data.valid).toBe(true);
207
+ expect((data.missing as string[]).length).toBe(0);
208
+ });
209
+
210
+ it('detects missing fields', async () => {
211
+ const filePath = join(tmpDir, 'plan.md');
212
+ await writeFile(filePath, '---\nphase: 10\n---\n\n# Plan\n');
213
+
214
+ const result = await frontmatterValidate([filePath, '--schema', 'plan'], tmpDir);
215
+ const data = result.data as Record<string, unknown>;
216
+ expect(data.valid).toBe(false);
217
+ expect((data.missing as string[]).length).toBeGreaterThan(0);
218
+ });
219
+
220
+ it('rejects unknown schema', async () => {
221
+ const filePath = join(tmpDir, 'test.md');
222
+ await writeFile(filePath, '---\nphase: 10\n---\n\n# Body\n');
223
+
224
+ await expect(
225
+ frontmatterValidate([filePath, '--schema', 'unknown'], tmpDir)
226
+ ).rejects.toThrow(/Unknown schema/);
227
+ });
228
+
229
+ it('has plan, summary, and verification schemas', () => {
230
+ expect(FRONTMATTER_SCHEMAS).toHaveProperty('plan');
231
+ expect(FRONTMATTER_SCHEMAS).toHaveProperty('summary');
232
+ expect(FRONTMATTER_SCHEMAS).toHaveProperty('verification');
233
+ });
234
+ });
235
+
236
+ // ─── Round-trip (extract → reconstruct → splice) ───────────────────────────
237
+
238
+ describe('frontmatter round-trip', () => {
239
+ it('preserves scalar and list fields through extract + splice', () => {
240
+ const original = `---
241
+ phase: "01"
242
+ plan: "02"
243
+ type: execute
244
+ wave: 1
245
+ depends_on: []
246
+ tags: [a, b]
247
+ ---
248
+ # Title
249
+ `;
250
+ const fm = extractFrontmatter(original) as Record<string, unknown>;
251
+ const spliced = spliceFrontmatter('# Title\n', fm);
252
+ expect(spliced.startsWith('---\n')).toBe(true);
253
+ const round = extractFrontmatter(spliced) as Record<string, unknown>;
254
+ expect(String(round.phase)).toBe('01');
255
+ // YAML may round-trip wave as number or string depending on parser output
256
+ expect(Number(round.wave)).toBe(1);
257
+ expect(Array.isArray(round.tags)).toBe(true);
258
+ });
259
+ });
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Frontmatter mutation handlers — write operations for YAML frontmatter.
3
+ *
4
+ * Ported from get-shit-done/bin/lib/frontmatter.cjs.
5
+ * Provides reconstructFrontmatter (serialization), spliceFrontmatter (replacement),
6
+ * and query handlers for frontmatter.set, frontmatter.merge, frontmatter.validate.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { reconstructFrontmatter, spliceFrontmatter } from './frontmatter-mutation.js';
11
+ *
12
+ * const yaml = reconstructFrontmatter({ phase: '10', tags: ['a', 'b'] });
13
+ * // 'phase: 10\ntags: [a, b]'
14
+ *
15
+ * const updated = spliceFrontmatter('---\nold: val\n---\nbody', { new: 'val' });
16
+ * // '---\nnew: val\n---\nbody'
17
+ * ```
18
+ */
19
+
20
+ import { readFile, writeFile } from 'node:fs/promises';
21
+ import { GSDError, ErrorClassification } from '../errors.js';
22
+ import { extractFrontmatter } from './frontmatter.js';
23
+ import { normalizeMd, resolvePathUnderProject } from './helpers.js';
24
+ import type { QueryHandler } from './utils.js';
25
+
26
+ // ─── FRONTMATTER_SCHEMAS ──────────────────────────────────────────────────
27
+
28
+ /** Schema definitions for frontmatter validation. */
29
+ export const FRONTMATTER_SCHEMAS: Record<string, { required: string[] }> = {
30
+ plan: { required: ['phase', 'plan', 'type', 'wave', 'depends_on', 'files_modified', 'autonomous', 'must_haves'] },
31
+ summary: { required: ['phase', 'plan', 'subsystem', 'tags', 'duration', 'completed'] },
32
+ verification: { required: ['phase', 'verified', 'status', 'score'] },
33
+ };
34
+
35
+ // ─── reconstructFrontmatter ────────────────────────────────────────────────
36
+
37
+ /**
38
+ * Serialize a flat/nested object into YAML frontmatter lines.
39
+ *
40
+ * Port of `reconstructFrontmatter` from frontmatter.cjs lines 122-183.
41
+ * Handles arrays (inline/dash), nested objects (2 levels), and quoting.
42
+ *
43
+ * @param obj - Object to serialize
44
+ * @returns YAML string (without --- delimiters)
45
+ */
46
+ export function reconstructFrontmatter(obj: Record<string, unknown>): string {
47
+ const lines: string[] = [];
48
+
49
+ for (const [key, value] of Object.entries(obj)) {
50
+ if (value === null || value === undefined) continue;
51
+
52
+ if (Array.isArray(value)) {
53
+ serializeArray(lines, key, value, '');
54
+ } else if (typeof value === 'object') {
55
+ lines.push(`${key}:`);
56
+ for (const [subkey, subval] of Object.entries(value as Record<string, unknown>)) {
57
+ if (subval === null || subval === undefined) continue;
58
+ if (Array.isArray(subval)) {
59
+ serializeArray(lines, subkey, subval, ' ');
60
+ } else if (typeof subval === 'object') {
61
+ lines.push(` ${subkey}:`);
62
+ for (const [subsubkey, subsubval] of Object.entries(subval as Record<string, unknown>)) {
63
+ if (subsubval === null || subsubval === undefined) continue;
64
+ if (Array.isArray(subsubval)) {
65
+ if (subsubval.length === 0) {
66
+ lines.push(` ${subsubkey}: []`);
67
+ } else {
68
+ lines.push(` ${subsubkey}:`);
69
+ for (const item of subsubval) {
70
+ lines.push(` - ${item}`);
71
+ }
72
+ }
73
+ } else {
74
+ lines.push(` ${subsubkey}: ${subsubval}`);
75
+ }
76
+ }
77
+ } else {
78
+ const sv = String(subval);
79
+ lines.push(` ${subkey}: ${needsQuoting(sv) ? `"${sv}"` : sv}`);
80
+ }
81
+ }
82
+ } else {
83
+ const sv = String(value);
84
+ if (sv.includes(':') || sv.includes('#') || sv.startsWith('[') || sv.startsWith('{')) {
85
+ lines.push(`${key}: "${sv}"`);
86
+ } else {
87
+ lines.push(`${key}: ${sv}`);
88
+ }
89
+ }
90
+ }
91
+
92
+ return lines.join('\n');
93
+ }
94
+
95
+ /** Serialize an array at the given indent level. */
96
+ function serializeArray(lines: string[], key: string, arr: unknown[], indent: string): void {
97
+ if (arr.length === 0) {
98
+ lines.push(`${indent}${key}: []`);
99
+ } else if (
100
+ arr.every(v => typeof v === 'string') &&
101
+ arr.length <= 3 &&
102
+ (arr as string[]).join(', ').length < 60
103
+ ) {
104
+ lines.push(`${indent}${key}: [${(arr as string[]).join(', ')}]`);
105
+ } else {
106
+ lines.push(`${indent}${key}:`);
107
+ for (const item of arr) {
108
+ const s = String(item);
109
+ lines.push(`${indent} - ${typeof item === 'string' && needsQuoting(s) ? `"${s}"` : s}`);
110
+ }
111
+ }
112
+ }
113
+
114
+ /** Check if a string value needs quoting in YAML. */
115
+ function needsQuoting(s: string): boolean {
116
+ return s.includes(':') || s.includes('#');
117
+ }
118
+
119
+ // ─── spliceFrontmatter ─────────────────────────────────────────────────────
120
+
121
+ /**
122
+ * Replace or prepend frontmatter in content.
123
+ *
124
+ * Port of `spliceFrontmatter` from frontmatter.cjs lines 186-193.
125
+ *
126
+ * @param content - File content with potential existing frontmatter
127
+ * @param newObj - New frontmatter object to serialize
128
+ * @returns Content with updated frontmatter
129
+ */
130
+ export function spliceFrontmatter(content: string, newObj: Record<string, unknown>): string {
131
+ const yamlStr = reconstructFrontmatter(newObj);
132
+ const match = content.match(/^---\r?\n[\s\S]+?\r?\n---/);
133
+ if (match) {
134
+ return `---\n${yamlStr}\n---` + content.slice(match[0].length);
135
+ }
136
+ return `---\n${yamlStr}\n---\n\n` + content;
137
+ }
138
+
139
+ // ─── parseSimpleValue ──────────────────────────────────────────────────────
140
+
141
+ /**
142
+ * Parse a simple CLI value string into a typed value.
143
+ * Tries JSON.parse first (handles booleans, numbers, arrays, objects).
144
+ * Falls back to raw string.
145
+ */
146
+ function parseSimpleValue(value: string): unknown {
147
+ try {
148
+ return JSON.parse(value);
149
+ } catch {
150
+ return value;
151
+ }
152
+ }
153
+
154
+ // ─── frontmatterSet ────────────────────────────────────────────────────────
155
+
156
+ /**
157
+ * Query handler for frontmatter.set command.
158
+ *
159
+ * Reads a file, sets a single frontmatter field, writes back with normalization.
160
+ * Port of `cmdFrontmatterSet` from frontmatter.cjs lines 328-342.
161
+ *
162
+ * @param args - args[0]: file path, args[1]: field name, args[2]: value
163
+ * @param projectDir - Project root directory
164
+ * @returns QueryResult with { updated: true, field, value }
165
+ */
166
+ export const frontmatterSet: QueryHandler = async (args, projectDir) => {
167
+ let filePath: string;
168
+ let field: string;
169
+ let value: string;
170
+
171
+ const fi = args.indexOf('--field');
172
+ const vi = args.indexOf('--value');
173
+ const hasNamedArgs = fi !== -1 || vi !== -1;
174
+ if (hasNamedArgs) {
175
+ if (fi === -1 || vi === -1 || !args[fi + 1] || args[vi + 1] === undefined) {
176
+ throw new GSDError('file, --field, and --value required together', ErrorClassification.Validation);
177
+ }
178
+ filePath = args[0];
179
+ field = args[fi + 1];
180
+ value = args[vi + 1];
181
+ } else {
182
+ filePath = args[0];
183
+ field = args[1];
184
+ value = args[2];
185
+ }
186
+
187
+ if (!filePath || !field || value === undefined) {
188
+ throw new GSDError('file, field, and value required', ErrorClassification.Validation);
189
+ }
190
+
191
+ // Path traversal guard: reject null bytes
192
+ if (filePath.includes('\0')) {
193
+ throw new GSDError('file path contains null bytes', ErrorClassification.Validation);
194
+ }
195
+
196
+ let fullPath: string;
197
+ try {
198
+ fullPath = await resolvePathUnderProject(projectDir, filePath);
199
+ } catch (err) {
200
+ if (err instanceof GSDError) {
201
+ return { data: { error: err.message, path: filePath } };
202
+ }
203
+ throw err;
204
+ }
205
+
206
+ let content: string;
207
+ try {
208
+ content = await readFile(fullPath, 'utf-8');
209
+ } catch {
210
+ return { data: { error: 'File not found', path: filePath } };
211
+ }
212
+
213
+ const fm = extractFrontmatter(content);
214
+ const parsedValue = parseSimpleValue(value);
215
+ fm[field] = parsedValue;
216
+ const newContent = spliceFrontmatter(content, fm);
217
+ await writeFile(fullPath, normalizeMd(newContent), 'utf-8');
218
+
219
+ return { data: { updated: true, field, value: parsedValue } };
220
+ };
221
+
222
+ // ─── frontmatterMerge ──────────────────────────────────────────────────────
223
+
224
+ /**
225
+ * Query handler for frontmatter.merge command.
226
+ *
227
+ * Reads a file, merges JSON object into existing frontmatter, writes back.
228
+ * Port of `cmdFrontmatterMerge` from frontmatter.cjs lines 344-356.
229
+ *
230
+ * @param args - `file --data <json>` (gsd-tools) or `[file, jsonString]` (SDK)
231
+ * @param projectDir - Project root directory
232
+ * @returns QueryResult with { merged: true, fields: [...] }
233
+ */
234
+ export const frontmatterMerge: QueryHandler = async (args, projectDir) => {
235
+ const filePath = args[0];
236
+ const dataIdx = args.indexOf('--data');
237
+ const jsonString = dataIdx !== -1 ? args[dataIdx + 1] : args[1];
238
+
239
+ if (!filePath || !jsonString) {
240
+ throw new GSDError('file and data required', ErrorClassification.Validation);
241
+ }
242
+
243
+ // Path traversal guard: reject null bytes (consistent with frontmatterSet)
244
+ if (filePath.includes('\0')) {
245
+ throw new GSDError('file path contains null bytes', ErrorClassification.Validation);
246
+ }
247
+
248
+ let fullPath: string;
249
+ try {
250
+ fullPath = await resolvePathUnderProject(projectDir, filePath);
251
+ } catch (err) {
252
+ if (err instanceof GSDError) {
253
+ return { data: { error: err.message, path: filePath } };
254
+ }
255
+ throw err;
256
+ }
257
+
258
+ let content: string;
259
+ try {
260
+ content = await readFile(fullPath, 'utf-8');
261
+ } catch {
262
+ return { data: { error: 'File not found', path: filePath } };
263
+ }
264
+
265
+ let mergeData: Record<string, unknown>;
266
+ try {
267
+ mergeData = JSON.parse(jsonString) as Record<string, unknown>;
268
+ } catch {
269
+ throw new GSDError('Invalid JSON for merge data', ErrorClassification.Validation);
270
+ }
271
+
272
+ const fm = extractFrontmatter(content);
273
+ Object.assign(fm, mergeData);
274
+ const newContent = spliceFrontmatter(content, fm);
275
+ await writeFile(fullPath, normalizeMd(newContent), 'utf-8');
276
+
277
+ return { data: { merged: true, fields: Object.keys(mergeData) } };
278
+ };
279
+
280
+ // ─── frontmatterValidate ───────────────────────────────────────────────────
281
+
282
+ /**
283
+ * Query handler for frontmatter.validate command.
284
+ *
285
+ * Reads a file and checks its frontmatter against a known schema.
286
+ * Port of `cmdFrontmatterValidate` from frontmatter.cjs lines 358-369.
287
+ *
288
+ * @param args - args[0]: file path, args[1]: '--schema', args[2]: schema name
289
+ * @param projectDir - Project root directory
290
+ * @returns QueryResult with { valid, missing, present, schema }
291
+ */
292
+ export const frontmatterValidate: QueryHandler = async (args, projectDir) => {
293
+ const filePath = args[0];
294
+
295
+ // Parse --schema flag from args
296
+ let schemaName: string | undefined;
297
+ for (let i = 1; i < args.length; i++) {
298
+ if (args[i] === '--schema' && args[i + 1]) {
299
+ schemaName = args[i + 1];
300
+ break;
301
+ }
302
+ }
303
+
304
+ if (!filePath || !schemaName) {
305
+ throw new GSDError('file and schema required', ErrorClassification.Validation);
306
+ }
307
+
308
+ // Path traversal guard: reject null bytes (consistent with frontmatterSet)
309
+ if (filePath.includes('\0')) {
310
+ throw new GSDError('file path contains null bytes', ErrorClassification.Validation);
311
+ }
312
+
313
+ const schema = FRONTMATTER_SCHEMAS[schemaName];
314
+ if (!schema) {
315
+ throw new GSDError(
316
+ `Unknown schema: ${schemaName}. Available: ${Object.keys(FRONTMATTER_SCHEMAS).join(', ')}`,
317
+ ErrorClassification.Validation
318
+ );
319
+ }
320
+
321
+ let fullPath: string;
322
+ try {
323
+ fullPath = await resolvePathUnderProject(projectDir, filePath);
324
+ } catch (err) {
325
+ if (err instanceof GSDError) {
326
+ return { data: { error: err.message, path: filePath } };
327
+ }
328
+ throw err;
329
+ }
330
+
331
+ let content: string;
332
+ try {
333
+ content = await readFile(fullPath, 'utf-8');
334
+ } catch {
335
+ return { data: { error: 'File not found', path: filePath } };
336
+ }
337
+
338
+ const fm = extractFrontmatter(content);
339
+ const missing = schema.required.filter(f => fm[f] === undefined);
340
+ const present = schema.required.filter(f => fm[f] !== undefined);
341
+
342
+ return { data: { valid: missing.length === 0, missing, present, schema: schemaName } };
343
+ };