@draht/coding-agent 2026.3.2-2

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 (639) hide show
  1. package/CHANGELOG.md +2866 -0
  2. package/README.md +566 -0
  3. package/bin/draht-tools.cjs +912 -0
  4. package/dist/cli/args.d.ts +48 -0
  5. package/dist/cli/args.d.ts.map +1 -0
  6. package/dist/cli/args.js +298 -0
  7. package/dist/cli/args.js.map +1 -0
  8. package/dist/cli/config-selector.d.ts +14 -0
  9. package/dist/cli/config-selector.d.ts.map +1 -0
  10. package/dist/cli/config-selector.js +31 -0
  11. package/dist/cli/config-selector.js.map +1 -0
  12. package/dist/cli/file-processor.d.ts +15 -0
  13. package/dist/cli/file-processor.d.ts.map +1 -0
  14. package/dist/cli/file-processor.js +79 -0
  15. package/dist/cli/file-processor.js.map +1 -0
  16. package/dist/cli/list-models.d.ts +9 -0
  17. package/dist/cli/list-models.d.ts.map +1 -0
  18. package/dist/cli/list-models.js +92 -0
  19. package/dist/cli/list-models.js.map +1 -0
  20. package/dist/cli/session-picker.d.ts +9 -0
  21. package/dist/cli/session-picker.d.ts.map +1 -0
  22. package/dist/cli/session-picker.js +34 -0
  23. package/dist/cli/session-picker.js.map +1 -0
  24. package/dist/cli.d.ts +3 -0
  25. package/dist/cli.d.ts.map +1 -0
  26. package/dist/cli.js +11 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/config.d.ts +85 -0
  29. package/dist/config.d.ts.map +1 -0
  30. package/dist/config.js +235 -0
  31. package/dist/config.js.map +1 -0
  32. package/dist/core/agent-session.d.ts +571 -0
  33. package/dist/core/agent-session.d.ts.map +1 -0
  34. package/dist/core/agent-session.js +2343 -0
  35. package/dist/core/agent-session.js.map +1 -0
  36. package/dist/core/auth-storage.d.ts +129 -0
  37. package/dist/core/auth-storage.d.ts.map +1 -0
  38. package/dist/core/auth-storage.js +394 -0
  39. package/dist/core/auth-storage.js.map +1 -0
  40. package/dist/core/bash-executor.d.ts +47 -0
  41. package/dist/core/bash-executor.d.ts.map +1 -0
  42. package/dist/core/bash-executor.js +212 -0
  43. package/dist/core/bash-executor.js.map +1 -0
  44. package/dist/core/compaction/branch-summarization.d.ts +86 -0
  45. package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  46. package/dist/core/compaction/branch-summarization.js +242 -0
  47. package/dist/core/compaction/branch-summarization.js.map +1 -0
  48. package/dist/core/compaction/compaction.d.ts +121 -0
  49. package/dist/core/compaction/compaction.d.ts.map +1 -0
  50. package/dist/core/compaction/compaction.js +607 -0
  51. package/dist/core/compaction/compaction.js.map +1 -0
  52. package/dist/core/compaction/index.d.ts +7 -0
  53. package/dist/core/compaction/index.d.ts.map +1 -0
  54. package/dist/core/compaction/index.js +7 -0
  55. package/dist/core/compaction/index.js.map +1 -0
  56. package/dist/core/compaction/utils.d.ts +35 -0
  57. package/dist/core/compaction/utils.d.ts.map +1 -0
  58. package/dist/core/compaction/utils.js +138 -0
  59. package/dist/core/compaction/utils.js.map +1 -0
  60. package/dist/core/defaults.d.ts +3 -0
  61. package/dist/core/defaults.d.ts.map +1 -0
  62. package/dist/core/defaults.js +2 -0
  63. package/dist/core/defaults.js.map +1 -0
  64. package/dist/core/diagnostics.d.ts +15 -0
  65. package/dist/core/diagnostics.d.ts.map +1 -0
  66. package/dist/core/diagnostics.js +2 -0
  67. package/dist/core/diagnostics.js.map +1 -0
  68. package/dist/core/event-bus.d.ts +9 -0
  69. package/dist/core/event-bus.d.ts.map +1 -0
  70. package/dist/core/event-bus.js +25 -0
  71. package/dist/core/event-bus.js.map +1 -0
  72. package/dist/core/exec.d.ts +29 -0
  73. package/dist/core/exec.d.ts.map +1 -0
  74. package/dist/core/exec.js +71 -0
  75. package/dist/core/exec.js.map +1 -0
  76. package/dist/core/export-html/ansi-to-html.d.ts +22 -0
  77. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  78. package/dist/core/export-html/ansi-to-html.js +249 -0
  79. package/dist/core/export-html/ansi-to-html.js.map +1 -0
  80. package/dist/core/export-html/index.d.ts +34 -0
  81. package/dist/core/export-html/index.d.ts.map +1 -0
  82. package/dist/core/export-html/index.js +222 -0
  83. package/dist/core/export-html/index.js.map +1 -0
  84. package/dist/core/export-html/template.css +971 -0
  85. package/dist/core/export-html/template.html +54 -0
  86. package/dist/core/export-html/template.js +1586 -0
  87. package/dist/core/export-html/tool-renderer.d.ts +35 -0
  88. package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  89. package/dist/core/export-html/tool-renderer.js +57 -0
  90. package/dist/core/export-html/tool-renderer.js.map +1 -0
  91. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  92. package/dist/core/export-html/vendor/marked.min.js +6 -0
  93. package/dist/core/extensions/index.d.ts +11 -0
  94. package/dist/core/extensions/index.d.ts.map +1 -0
  95. package/dist/core/extensions/index.js +9 -0
  96. package/dist/core/extensions/index.js.map +1 -0
  97. package/dist/core/extensions/loader.d.ts +25 -0
  98. package/dist/core/extensions/loader.d.ts.map +1 -0
  99. package/dist/core/extensions/loader.js +415 -0
  100. package/dist/core/extensions/loader.js.map +1 -0
  101. package/dist/core/extensions/runner.d.ts +146 -0
  102. package/dist/core/extensions/runner.d.ts.map +1 -0
  103. package/dist/core/extensions/runner.js +645 -0
  104. package/dist/core/extensions/runner.js.map +1 -0
  105. package/dist/core/extensions/types.d.ts +1011 -0
  106. package/dist/core/extensions/types.d.ts.map +1 -0
  107. package/dist/core/extensions/types.js +35 -0
  108. package/dist/core/extensions/types.js.map +1 -0
  109. package/dist/core/extensions/wrapper.d.ts +27 -0
  110. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  111. package/dist/core/extensions/wrapper.js +102 -0
  112. package/dist/core/extensions/wrapper.js.map +1 -0
  113. package/dist/core/footer-data-provider.d.ts +32 -0
  114. package/dist/core/footer-data-provider.d.ts.map +1 -0
  115. package/dist/core/footer-data-provider.js +134 -0
  116. package/dist/core/footer-data-provider.js.map +1 -0
  117. package/dist/core/index.d.ts +9 -0
  118. package/dist/core/index.d.ts.map +1 -0
  119. package/dist/core/index.js +9 -0
  120. package/dist/core/index.js.map +1 -0
  121. package/dist/core/keybindings.d.ts +55 -0
  122. package/dist/core/keybindings.d.ts.map +1 -0
  123. package/dist/core/keybindings.js +153 -0
  124. package/dist/core/keybindings.js.map +1 -0
  125. package/dist/core/messages.d.ts +77 -0
  126. package/dist/core/messages.d.ts.map +1 -0
  127. package/dist/core/messages.js +123 -0
  128. package/dist/core/messages.js.map +1 -0
  129. package/dist/core/model-registry.d.ts +112 -0
  130. package/dist/core/model-registry.d.ts.map +1 -0
  131. package/dist/core/model-registry.js +534 -0
  132. package/dist/core/model-registry.js.map +1 -0
  133. package/dist/core/model-resolver.d.ts +104 -0
  134. package/dist/core/model-resolver.d.ts.map +1 -0
  135. package/dist/core/model-resolver.js +432 -0
  136. package/dist/core/model-resolver.js.map +1 -0
  137. package/dist/core/package-manager.d.ts +151 -0
  138. package/dist/core/package-manager.d.ts.map +1 -0
  139. package/dist/core/package-manager.js +1447 -0
  140. package/dist/core/package-manager.js.map +1 -0
  141. package/dist/core/prompt-templates.d.ts +50 -0
  142. package/dist/core/prompt-templates.d.ts.map +1 -0
  143. package/dist/core/prompt-templates.js +268 -0
  144. package/dist/core/prompt-templates.js.map +1 -0
  145. package/dist/core/resolve-config-value.d.ts +17 -0
  146. package/dist/core/resolve-config-value.d.ts.map +1 -0
  147. package/dist/core/resolve-config-value.js +59 -0
  148. package/dist/core/resolve-config-value.js.map +1 -0
  149. package/dist/core/resource-loader.d.ts +184 -0
  150. package/dist/core/resource-loader.d.ts.map +1 -0
  151. package/dist/core/resource-loader.js +670 -0
  152. package/dist/core/resource-loader.js.map +1 -0
  153. package/dist/core/sdk.d.ts +90 -0
  154. package/dist/core/sdk.d.ts.map +1 -0
  155. package/dist/core/sdk.js +235 -0
  156. package/dist/core/sdk.js.map +1 -0
  157. package/dist/core/session-manager.d.ts +323 -0
  158. package/dist/core/session-manager.d.ts.map +1 -0
  159. package/dist/core/session-manager.js +1098 -0
  160. package/dist/core/session-manager.js.map +1 -0
  161. package/dist/core/settings-manager.d.ts +225 -0
  162. package/dist/core/settings-manager.d.ts.map +1 -0
  163. package/dist/core/settings-manager.js +653 -0
  164. package/dist/core/settings-manager.js.map +1 -0
  165. package/dist/core/skills.d.ts +58 -0
  166. package/dist/core/skills.d.ts.map +1 -0
  167. package/dist/core/skills.js +364 -0
  168. package/dist/core/skills.js.map +1 -0
  169. package/dist/core/slash-commands.d.ts +15 -0
  170. package/dist/core/slash-commands.d.ts.map +1 -0
  171. package/dist/core/slash-commands.js +22 -0
  172. package/dist/core/slash-commands.js.map +1 -0
  173. package/dist/core/system-prompt.d.ts +24 -0
  174. package/dist/core/system-prompt.d.ts.map +1 -0
  175. package/dist/core/system-prompt.js +137 -0
  176. package/dist/core/system-prompt.js.map +1 -0
  177. package/dist/core/timings.d.ts +7 -0
  178. package/dist/core/timings.d.ts.map +1 -0
  179. package/dist/core/timings.js +25 -0
  180. package/dist/core/timings.js.map +1 -0
  181. package/dist/core/tools/bash.d.ts +55 -0
  182. package/dist/core/tools/bash.d.ts.map +1 -0
  183. package/dist/core/tools/bash.js +242 -0
  184. package/dist/core/tools/bash.js.map +1 -0
  185. package/dist/core/tools/edit-diff.d.ts +63 -0
  186. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  187. package/dist/core/tools/edit-diff.js +243 -0
  188. package/dist/core/tools/edit-diff.js.map +1 -0
  189. package/dist/core/tools/edit.d.ts +39 -0
  190. package/dist/core/tools/edit.d.ts.map +1 -0
  191. package/dist/core/tools/edit.js +146 -0
  192. package/dist/core/tools/edit.js.map +1 -0
  193. package/dist/core/tools/find.d.ts +39 -0
  194. package/dist/core/tools/find.d.ts.map +1 -0
  195. package/dist/core/tools/find.js +206 -0
  196. package/dist/core/tools/find.js.map +1 -0
  197. package/dist/core/tools/grep.d.ts +45 -0
  198. package/dist/core/tools/grep.d.ts.map +1 -0
  199. package/dist/core/tools/grep.js +239 -0
  200. package/dist/core/tools/grep.js.map +1 -0
  201. package/dist/core/tools/index.d.ts +73 -0
  202. package/dist/core/tools/index.d.ts.map +1 -0
  203. package/dist/core/tools/index.js +61 -0
  204. package/dist/core/tools/index.js.map +1 -0
  205. package/dist/core/tools/ls.d.ts +40 -0
  206. package/dist/core/tools/ls.d.ts.map +1 -0
  207. package/dist/core/tools/ls.js +118 -0
  208. package/dist/core/tools/ls.js.map +1 -0
  209. package/dist/core/tools/path-utils.d.ts +8 -0
  210. package/dist/core/tools/path-utils.d.ts.map +1 -0
  211. package/dist/core/tools/path-utils.js +81 -0
  212. package/dist/core/tools/path-utils.js.map +1 -0
  213. package/dist/core/tools/read.d.ts +39 -0
  214. package/dist/core/tools/read.d.ts.map +1 -0
  215. package/dist/core/tools/read.js +166 -0
  216. package/dist/core/tools/read.js.map +1 -0
  217. package/dist/core/tools/truncate.d.ts +70 -0
  218. package/dist/core/tools/truncate.d.ts.map +1 -0
  219. package/dist/core/tools/truncate.js +205 -0
  220. package/dist/core/tools/truncate.js.map +1 -0
  221. package/dist/core/tools/write.d.ts +29 -0
  222. package/dist/core/tools/write.d.ts.map +1 -0
  223. package/dist/core/tools/write.js +78 -0
  224. package/dist/core/tools/write.js.map +1 -0
  225. package/dist/extensions/gsd-commands.ts +338 -0
  226. package/dist/extensions/subagent.ts +312 -0
  227. package/dist/hooks/gsd/draht-post-phase.js +133 -0
  228. package/dist/hooks/gsd/draht-post-task.js +132 -0
  229. package/dist/hooks/gsd/draht-pre-execute.js +146 -0
  230. package/dist/hooks/gsd/draht-quality-gate.js +210 -0
  231. package/dist/index.d.ts +27 -0
  232. package/dist/index.d.ts.map +1 -0
  233. package/dist/index.js +42 -0
  234. package/dist/index.js.map +1 -0
  235. package/dist/main.d.ts +8 -0
  236. package/dist/main.d.ts.map +1 -0
  237. package/dist/main.js +812 -0
  238. package/dist/main.js.map +1 -0
  239. package/dist/migrations.d.ts +33 -0
  240. package/dist/migrations.d.ts.map +1 -0
  241. package/dist/migrations.js +261 -0
  242. package/dist/migrations.js.map +1 -0
  243. package/dist/modes/index.d.ts +9 -0
  244. package/dist/modes/index.d.ts.map +1 -0
  245. package/dist/modes/index.js +8 -0
  246. package/dist/modes/index.js.map +1 -0
  247. package/dist/modes/interactive/components/armin.d.ts +34 -0
  248. package/dist/modes/interactive/components/armin.d.ts.map +1 -0
  249. package/dist/modes/interactive/components/armin.js +333 -0
  250. package/dist/modes/interactive/components/armin.js.map +1 -0
  251. package/dist/modes/interactive/components/assistant-message.d.ts +16 -0
  252. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
  253. package/dist/modes/interactive/components/assistant-message.js +96 -0
  254. package/dist/modes/interactive/components/assistant-message.js.map +1 -0
  255. package/dist/modes/interactive/components/bash-execution.d.ts +35 -0
  256. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
  257. package/dist/modes/interactive/components/bash-execution.js +162 -0
  258. package/dist/modes/interactive/components/bash-execution.js.map +1 -0
  259. package/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  260. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  261. package/dist/modes/interactive/components/bordered-loader.js +51 -0
  262. package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  263. package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  264. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  265. package/dist/modes/interactive/components/branch-summary-message.js +44 -0
  266. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  267. package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  268. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  269. package/dist/modes/interactive/components/compaction-summary-message.js +45 -0
  270. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  271. package/dist/modes/interactive/components/config-selector.d.ts +71 -0
  272. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
  273. package/dist/modes/interactive/components/config-selector.js +479 -0
  274. package/dist/modes/interactive/components/config-selector.js.map +1 -0
  275. package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  276. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
  277. package/dist/modes/interactive/components/countdown-timer.js +33 -0
  278. package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
  279. package/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  280. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
  281. package/dist/modes/interactive/components/custom-editor.js +70 -0
  282. package/dist/modes/interactive/components/custom-editor.js.map +1 -0
  283. package/dist/modes/interactive/components/custom-message.d.ts +20 -0
  284. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  285. package/dist/modes/interactive/components/custom-message.js +79 -0
  286. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  287. package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  288. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
  289. package/dist/modes/interactive/components/daxnuts.js +140 -0
  290. package/dist/modes/interactive/components/daxnuts.js.map +1 -0
  291. package/dist/modes/interactive/components/diff.d.ts +12 -0
  292. package/dist/modes/interactive/components/diff.d.ts.map +1 -0
  293. package/dist/modes/interactive/components/diff.js +133 -0
  294. package/dist/modes/interactive/components/diff.js.map +1 -0
  295. package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  296. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
  297. package/dist/modes/interactive/components/dynamic-border.js +21 -0
  298. package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
  299. package/dist/modes/interactive/components/extension-editor.d.ts +17 -0
  300. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  301. package/dist/modes/interactive/components/extension-editor.js +102 -0
  302. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  303. package/dist/modes/interactive/components/extension-input.d.ts +23 -0
  304. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  305. package/dist/modes/interactive/components/extension-input.js +61 -0
  306. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  307. package/dist/modes/interactive/components/extension-selector.d.ts +24 -0
  308. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  309. package/dist/modes/interactive/components/extension-selector.js +78 -0
  310. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  311. package/dist/modes/interactive/components/footer.d.ts +26 -0
  312. package/dist/modes/interactive/components/footer.d.ts.map +1 -0
  313. package/dist/modes/interactive/components/footer.js +213 -0
  314. package/dist/modes/interactive/components/footer.js.map +1 -0
  315. package/dist/modes/interactive/components/index.d.ts +32 -0
  316. package/dist/modes/interactive/components/index.d.ts.map +1 -0
  317. package/dist/modes/interactive/components/index.js +33 -0
  318. package/dist/modes/interactive/components/index.js.map +1 -0
  319. package/dist/modes/interactive/components/keybinding-hints.d.ts +41 -0
  320. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
  321. package/dist/modes/interactive/components/keybinding-hints.js +61 -0
  322. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
  323. package/dist/modes/interactive/components/login-dialog.d.ts +42 -0
  324. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
  325. package/dist/modes/interactive/components/login-dialog.js +145 -0
  326. package/dist/modes/interactive/components/login-dialog.js.map +1 -0
  327. package/dist/modes/interactive/components/model-selector.d.ts +47 -0
  328. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
  329. package/dist/modes/interactive/components/model-selector.js +271 -0
  330. package/dist/modes/interactive/components/model-selector.js.map +1 -0
  331. package/dist/modes/interactive/components/oauth-selector.d.ts +19 -0
  332. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
  333. package/dist/modes/interactive/components/oauth-selector.js +97 -0
  334. package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
  335. package/dist/modes/interactive/components/scoped-models-selector.d.ts +49 -0
  336. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
  337. package/dist/modes/interactive/components/scoped-models-selector.js +275 -0
  338. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
  339. package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  340. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
  341. package/dist/modes/interactive/components/session-selector-search.js +155 -0
  342. package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
  343. package/dist/modes/interactive/components/session-selector.d.ts +95 -0
  344. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
  345. package/dist/modes/interactive/components/session-selector.js +851 -0
  346. package/dist/modes/interactive/components/session-selector.js.map +1 -0
  347. package/dist/modes/interactive/components/settings-selector.d.ts +56 -0
  348. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
  349. package/dist/modes/interactive/components/settings-selector.js +287 -0
  350. package/dist/modes/interactive/components/settings-selector.js.map +1 -0
  351. package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  352. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
  353. package/dist/modes/interactive/components/show-images-selector.js +35 -0
  354. package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
  355. package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  356. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
  357. package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
  358. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
  359. package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  360. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
  361. package/dist/modes/interactive/components/theme-selector.js +46 -0
  362. package/dist/modes/interactive/components/theme-selector.js.map +1 -0
  363. package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  364. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
  365. package/dist/modes/interactive/components/thinking-selector.js +47 -0
  366. package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
  367. package/dist/modes/interactive/components/tool-execution.d.ts +75 -0
  368. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
  369. package/dist/modes/interactive/components/tool-execution.js +752 -0
  370. package/dist/modes/interactive/components/tool-execution.js.map +1 -0
  371. package/dist/modes/interactive/components/tree-selector.d.ts +68 -0
  372. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  373. package/dist/modes/interactive/components/tree-selector.js +934 -0
  374. package/dist/modes/interactive/components/tree-selector.js.map +1 -0
  375. package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  376. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
  377. package/dist/modes/interactive/components/user-message-selector.js +113 -0
  378. package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
  379. package/dist/modes/interactive/components/user-message.d.ts +8 -0
  380. package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
  381. package/dist/modes/interactive/components/user-message.js +16 -0
  382. package/dist/modes/interactive/components/user-message.js.map +1 -0
  383. package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  384. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
  385. package/dist/modes/interactive/components/visual-truncate.js +33 -0
  386. package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
  387. package/dist/modes/interactive/interactive-mode.d.ts +315 -0
  388. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
  389. package/dist/modes/interactive/interactive-mode.js +3719 -0
  390. package/dist/modes/interactive/interactive-mode.js.map +1 -0
  391. package/dist/modes/interactive/theme/dark.json +85 -0
  392. package/dist/modes/interactive/theme/light.json +84 -0
  393. package/dist/modes/interactive/theme/theme-schema.json +335 -0
  394. package/dist/modes/interactive/theme/theme.d.ts +78 -0
  395. package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  396. package/dist/modes/interactive/theme/theme.js +944 -0
  397. package/dist/modes/interactive/theme/theme.js.map +1 -0
  398. package/dist/modes/print-mode.d.ts +28 -0
  399. package/dist/modes/print-mode.d.ts.map +1 -0
  400. package/dist/modes/print-mode.js +101 -0
  401. package/dist/modes/print-mode.js.map +1 -0
  402. package/dist/modes/rpc/rpc-client.d.ts +217 -0
  403. package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
  404. package/dist/modes/rpc/rpc-client.js +405 -0
  405. package/dist/modes/rpc/rpc-client.js.map +1 -0
  406. package/dist/modes/rpc/rpc-mode.d.ts +20 -0
  407. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  408. package/dist/modes/rpc/rpc-mode.js +511 -0
  409. package/dist/modes/rpc/rpc-mode.js.map +1 -0
  410. package/dist/modes/rpc/rpc-types.d.ts +409 -0
  411. package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
  412. package/dist/modes/rpc/rpc-types.js +8 -0
  413. package/dist/modes/rpc/rpc-types.js.map +1 -0
  414. package/dist/prompts/agents/build.md +44 -0
  415. package/dist/prompts/agents/plan.md +39 -0
  416. package/dist/prompts/agents/verify.md +33 -0
  417. package/dist/prompts/commands/atomic-commit.md +39 -0
  418. package/dist/prompts/commands/discuss-phase.md +25 -0
  419. package/dist/prompts/commands/execute-phase.md +59 -0
  420. package/dist/prompts/commands/map-codebase.md +32 -0
  421. package/dist/prompts/commands/new-project.md +40 -0
  422. package/dist/prompts/commands/pause-work.md +12 -0
  423. package/dist/prompts/commands/plan-phase.md +61 -0
  424. package/dist/prompts/commands/progress.md +12 -0
  425. package/dist/prompts/commands/quick.md +19 -0
  426. package/dist/prompts/commands/resume-work.md +13 -0
  427. package/dist/prompts/commands/verify-work.md +27 -0
  428. package/dist/utils/changelog.d.ts +21 -0
  429. package/dist/utils/changelog.d.ts.map +1 -0
  430. package/dist/utils/changelog.js +87 -0
  431. package/dist/utils/changelog.js.map +1 -0
  432. package/dist/utils/clipboard-image.d.ts +11 -0
  433. package/dist/utils/clipboard-image.d.ts.map +1 -0
  434. package/dist/utils/clipboard-image.js +162 -0
  435. package/dist/utils/clipboard-image.js.map +1 -0
  436. package/dist/utils/clipboard-native.d.ts +7 -0
  437. package/dist/utils/clipboard-native.d.ts.map +1 -0
  438. package/dist/utils/clipboard-native.js +14 -0
  439. package/dist/utils/clipboard-native.js.map +1 -0
  440. package/dist/utils/clipboard.d.ts +2 -0
  441. package/dist/utils/clipboard.d.ts.map +1 -0
  442. package/dist/utils/clipboard.js +67 -0
  443. package/dist/utils/clipboard.js.map +1 -0
  444. package/dist/utils/frontmatter.d.ts +8 -0
  445. package/dist/utils/frontmatter.d.ts.map +1 -0
  446. package/dist/utils/frontmatter.js +26 -0
  447. package/dist/utils/frontmatter.js.map +1 -0
  448. package/dist/utils/git.d.ts +26 -0
  449. package/dist/utils/git.d.ts.map +1 -0
  450. package/dist/utils/git.js +163 -0
  451. package/dist/utils/git.js.map +1 -0
  452. package/dist/utils/image-convert.d.ts +9 -0
  453. package/dist/utils/image-convert.d.ts.map +1 -0
  454. package/dist/utils/image-convert.js +35 -0
  455. package/dist/utils/image-convert.js.map +1 -0
  456. package/dist/utils/image-resize.d.ts +36 -0
  457. package/dist/utils/image-resize.d.ts.map +1 -0
  458. package/dist/utils/image-resize.js +181 -0
  459. package/dist/utils/image-resize.js.map +1 -0
  460. package/dist/utils/mime.d.ts +2 -0
  461. package/dist/utils/mime.d.ts.map +1 -0
  462. package/dist/utils/mime.js +26 -0
  463. package/dist/utils/mime.js.map +1 -0
  464. package/dist/utils/photon.d.ts +21 -0
  465. package/dist/utils/photon.d.ts.map +1 -0
  466. package/dist/utils/photon.js +121 -0
  467. package/dist/utils/photon.js.map +1 -0
  468. package/dist/utils/shell.d.ts +26 -0
  469. package/dist/utils/shell.d.ts.map +1 -0
  470. package/dist/utils/shell.js +186 -0
  471. package/dist/utils/shell.js.map +1 -0
  472. package/dist/utils/sleep.d.ts +5 -0
  473. package/dist/utils/sleep.d.ts.map +1 -0
  474. package/dist/utils/sleep.js +17 -0
  475. package/dist/utils/sleep.js.map +1 -0
  476. package/dist/utils/tools-manager.d.ts +3 -0
  477. package/dist/utils/tools-manager.d.ts.map +1 -0
  478. package/dist/utils/tools-manager.js +251 -0
  479. package/dist/utils/tools-manager.js.map +1 -0
  480. package/docs/compaction.md +390 -0
  481. package/docs/custom-provider.md +580 -0
  482. package/docs/development.md +69 -0
  483. package/docs/extensions.md +1952 -0
  484. package/docs/images/doom-extension.png +0 -0
  485. package/docs/images/exy.png +0 -0
  486. package/docs/images/interactive-mode.png +0 -0
  487. package/docs/images/tree-view.png +0 -0
  488. package/docs/json.md +79 -0
  489. package/docs/keybindings.md +174 -0
  490. package/docs/models.md +293 -0
  491. package/docs/packages.md +209 -0
  492. package/docs/prompt-templates.md +67 -0
  493. package/docs/providers.md +186 -0
  494. package/docs/rpc.md +1317 -0
  495. package/docs/sdk.md +968 -0
  496. package/docs/session.md +412 -0
  497. package/docs/settings.md +223 -0
  498. package/docs/shell-aliases.md +13 -0
  499. package/docs/skills.md +231 -0
  500. package/docs/terminal-setup.md +70 -0
  501. package/docs/termux.md +127 -0
  502. package/docs/themes.md +295 -0
  503. package/docs/tree.md +219 -0
  504. package/docs/tui.md +887 -0
  505. package/docs/windows.md +17 -0
  506. package/examples/README.md +25 -0
  507. package/examples/extensions/README.md +204 -0
  508. package/examples/extensions/antigravity-image-gen.ts +413 -0
  509. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  510. package/examples/extensions/bash-spawn-hook.ts +30 -0
  511. package/examples/extensions/bookmark.ts +50 -0
  512. package/examples/extensions/built-in-tool-renderer.ts +246 -0
  513. package/examples/extensions/claude-rules.ts +86 -0
  514. package/examples/extensions/commands.ts +72 -0
  515. package/examples/extensions/confirm-destructive.ts +59 -0
  516. package/examples/extensions/custom-compaction.ts +114 -0
  517. package/examples/extensions/custom-footer.ts +64 -0
  518. package/examples/extensions/custom-header.ts +73 -0
  519. package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
  520. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  521. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  522. package/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
  523. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  524. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  525. package/examples/extensions/custom-provider-qwen-cli/index.ts +345 -0
  526. package/examples/extensions/custom-provider-qwen-cli/package.json +16 -0
  527. package/examples/extensions/dirty-repo-guard.ts +56 -0
  528. package/examples/extensions/doom-overlay/README.md +46 -0
  529. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
  530. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  531. package/examples/extensions/doom-overlay/doom/build.sh +152 -0
  532. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  533. package/examples/extensions/doom-overlay/doom-component.ts +132 -0
  534. package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  535. package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  536. package/examples/extensions/doom-overlay/index.ts +74 -0
  537. package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
  538. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  539. package/examples/extensions/dynamic-resources/dynamic.json +79 -0
  540. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  541. package/examples/extensions/dynamic-resources/index.ts +15 -0
  542. package/examples/extensions/event-bus.ts +43 -0
  543. package/examples/extensions/file-trigger.ts +41 -0
  544. package/examples/extensions/git-checkpoint.ts +53 -0
  545. package/examples/extensions/handoff.ts +150 -0
  546. package/examples/extensions/hello.ts +25 -0
  547. package/examples/extensions/inline-bash.ts +94 -0
  548. package/examples/extensions/input-transform.ts +43 -0
  549. package/examples/extensions/interactive-shell.ts +196 -0
  550. package/examples/extensions/mac-system-theme.ts +47 -0
  551. package/examples/extensions/message-renderer.ts +59 -0
  552. package/examples/extensions/minimal-mode.ts +426 -0
  553. package/examples/extensions/modal-editor.ts +85 -0
  554. package/examples/extensions/model-status.ts +31 -0
  555. package/examples/extensions/notify.ts +55 -0
  556. package/examples/extensions/overlay-qa-tests.ts +881 -0
  557. package/examples/extensions/overlay-test.ts +150 -0
  558. package/examples/extensions/permission-gate.ts +34 -0
  559. package/examples/extensions/pirate.ts +47 -0
  560. package/examples/extensions/plan-mode/README.md +65 -0
  561. package/examples/extensions/plan-mode/index.ts +340 -0
  562. package/examples/extensions/plan-mode/utils.ts +168 -0
  563. package/examples/extensions/preset.ts +398 -0
  564. package/examples/extensions/protected-paths.ts +30 -0
  565. package/examples/extensions/qna.ts +119 -0
  566. package/examples/extensions/question.ts +264 -0
  567. package/examples/extensions/questionnaire.ts +427 -0
  568. package/examples/extensions/rainbow-editor.ts +88 -0
  569. package/examples/extensions/reload-runtime.ts +37 -0
  570. package/examples/extensions/rpc-demo.ts +124 -0
  571. package/examples/extensions/sandbox/index.ts +318 -0
  572. package/examples/extensions/sandbox/package-lock.json +92 -0
  573. package/examples/extensions/sandbox/package.json +19 -0
  574. package/examples/extensions/send-user-message.ts +97 -0
  575. package/examples/extensions/session-name.ts +27 -0
  576. package/examples/extensions/shutdown-command.ts +63 -0
  577. package/examples/extensions/snake.ts +343 -0
  578. package/examples/extensions/space-invaders.ts +560 -0
  579. package/examples/extensions/ssh.ts +220 -0
  580. package/examples/extensions/sst-resource-manager.ts +203 -0
  581. package/examples/extensions/status-line.ts +40 -0
  582. package/examples/extensions/subagent/README.md +172 -0
  583. package/examples/extensions/subagent/agents/planner.md +37 -0
  584. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  585. package/examples/extensions/subagent/agents/scout.md +50 -0
  586. package/examples/extensions/subagent/agents/worker.md +24 -0
  587. package/examples/extensions/subagent/agents.ts +126 -0
  588. package/examples/extensions/subagent/index.ts +964 -0
  589. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  590. package/examples/extensions/subagent/prompts/implement.md +10 -0
  591. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  592. package/examples/extensions/summarize.ts +195 -0
  593. package/examples/extensions/system-prompt-header.ts +17 -0
  594. package/examples/extensions/timed-confirm.ts +70 -0
  595. package/examples/extensions/titlebar-spinner.ts +58 -0
  596. package/examples/extensions/todo.ts +299 -0
  597. package/examples/extensions/tool-override.ts +143 -0
  598. package/examples/extensions/tools.ts +146 -0
  599. package/examples/extensions/trigger-compact.ts +40 -0
  600. package/examples/extensions/truncated-tool.ts +192 -0
  601. package/examples/extensions/widget-placement.ts +17 -0
  602. package/examples/extensions/with-deps/index.ts +36 -0
  603. package/examples/extensions/with-deps/package-lock.json +31 -0
  604. package/examples/extensions/with-deps/package.json +22 -0
  605. package/examples/rpc-extension-ui.ts +632 -0
  606. package/examples/sdk/01-minimal.ts +22 -0
  607. package/examples/sdk/02-custom-model.ts +49 -0
  608. package/examples/sdk/03-custom-prompt.ts +55 -0
  609. package/examples/sdk/04-skills.ts +46 -0
  610. package/examples/sdk/05-tools.ts +56 -0
  611. package/examples/sdk/06-extensions.ts +88 -0
  612. package/examples/sdk/07-context-files.ts +40 -0
  613. package/examples/sdk/08-prompt-templates.ts +42 -0
  614. package/examples/sdk/09-api-keys-and-oauth.ts +48 -0
  615. package/examples/sdk/10-settings.ts +51 -0
  616. package/examples/sdk/11-sessions.ts +48 -0
  617. package/examples/sdk/12-full-control.ts +82 -0
  618. package/examples/sdk/README.md +144 -0
  619. package/extensions/gsd-commands.ts +338 -0
  620. package/extensions/subagent.ts +312 -0
  621. package/hooks/gsd/draht-post-phase.js +133 -0
  622. package/hooks/gsd/draht-post-task.js +132 -0
  623. package/hooks/gsd/draht-pre-execute.js +146 -0
  624. package/hooks/gsd/draht-quality-gate.js +210 -0
  625. package/package.json +105 -0
  626. package/prompts/agents/build.md +44 -0
  627. package/prompts/agents/plan.md +39 -0
  628. package/prompts/agents/verify.md +33 -0
  629. package/prompts/commands/atomic-commit.md +39 -0
  630. package/prompts/commands/discuss-phase.md +25 -0
  631. package/prompts/commands/execute-phase.md +59 -0
  632. package/prompts/commands/map-codebase.md +32 -0
  633. package/prompts/commands/new-project.md +40 -0
  634. package/prompts/commands/pause-work.md +12 -0
  635. package/prompts/commands/plan-phase.md +61 -0
  636. package/prompts/commands/progress.md +12 -0
  637. package/prompts/commands/quick.md +19 -0
  638. package/prompts/commands/resume-work.md +13 -0
  639. package/prompts/commands/verify-work.md +27 -0
@@ -0,0 +1,912 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ /**
5
+ * Draht Tools — CLI for the Get Shit Done methodology.
6
+ * Manages .planning/ directory structure, state tracking, and git integration.
7
+ */
8
+
9
+ const fs = require("node:fs");
10
+ const path = require("node:path");
11
+ const { execSync } = require("node:child_process");
12
+
13
+ const PLANNING_DIR = ".planning";
14
+ const BANNER_WIDTH = 55;
15
+
16
+ // ============================================================================
17
+ // Helpers
18
+ // ============================================================================
19
+
20
+ function banner(stage) {
21
+ const line = "━".repeat(BANNER_WIDTH);
22
+ return `${line}\n DRAHT ► ${stage}\n${line}`;
23
+ }
24
+
25
+ function planningPath(...segments) {
26
+ return path.join(process.cwd(), PLANNING_DIR, ...segments);
27
+ }
28
+
29
+ function ensureDir(dir) {
30
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
31
+ }
32
+
33
+ function readJson(filePath) {
34
+ try {
35
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ function readMd(filePath) {
42
+ try {
43
+ return fs.readFileSync(filePath, "utf-8");
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ function writeMd(filePath, content) {
50
+ ensureDir(path.dirname(filePath));
51
+ fs.writeFileSync(filePath, content, "utf-8");
52
+ }
53
+
54
+ function writeJson(filePath, data) {
55
+ ensureDir(path.dirname(filePath));
56
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
57
+ }
58
+
59
+ function timestamp() {
60
+ return new Date().toISOString().replace("T", " ").slice(0, 19);
61
+ }
62
+
63
+ function dateStamp() {
64
+ return new Date().toISOString().slice(0, 10);
65
+ }
66
+
67
+ function slugify(text, maxLen = 40) {
68
+ return text
69
+ .toLowerCase()
70
+ .replace(/[^a-z0-9]+/g, "-")
71
+ .replace(/^-|-$/g, "")
72
+ .slice(0, maxLen);
73
+ }
74
+
75
+ function padNum(n, digits = 2) {
76
+ return String(n).padStart(digits, "0");
77
+ }
78
+
79
+ function hasGit() {
80
+ try {
81
+ execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
82
+ return true;
83
+ } catch {
84
+ return false;
85
+ }
86
+ }
87
+
88
+ function gitCommit(message) {
89
+ if (!hasGit()) return null;
90
+ try {
91
+ execSync(`git add ${PLANNING_DIR}/`, { stdio: "ignore" });
92
+ execSync(`git commit -m ${JSON.stringify(message)}`, { stdio: "ignore" });
93
+ const hash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
94
+ return hash;
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+
100
+ function gitCommitAll(message) {
101
+ if (!hasGit()) return null;
102
+ try {
103
+ execSync("git add -A", { stdio: "ignore" });
104
+ execSync(`git commit -m ${JSON.stringify(message)}`, { stdio: "ignore" });
105
+ const hash = execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
106
+ return hash;
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ function getPhaseDir(phaseNum) {
113
+ const dir = planningPath("phases");
114
+ if (!fs.existsSync(dir)) return null;
115
+ const entries = fs.readdirSync(dir);
116
+ const match = entries.find((e) => e.startsWith(padNum(phaseNum) + "-"));
117
+ return match ? path.join(dir, match) : null;
118
+ }
119
+
120
+ function getPhaseSlug(phaseNum) {
121
+ const dir = getPhaseDir(phaseNum);
122
+ if (!dir) return null;
123
+ return path.basename(dir).replace(/^\d+-/, "");
124
+ }
125
+
126
+ function parsePhaseFromRoadmap(roadmapContent, phaseNum) {
127
+ const regex = new RegExp(
128
+ `## Phase ${phaseNum}:\\s*(.+?)\\s*—\\s*\`(\\w+)\``,
129
+ "m"
130
+ );
131
+ const match = roadmapContent.match(regex);
132
+ if (match) return { name: match[1].trim(), status: match[2] };
133
+ // Try without status
134
+ const regex2 = new RegExp(`## Phase ${phaseNum}:\\s*(.+?)\\n`, "m");
135
+ const match2 = roadmapContent.match(regex2);
136
+ if (match2) return { name: match2[1].trim(), status: "unknown" };
137
+ return null;
138
+ }
139
+
140
+ function getState() {
141
+ return readMd(planningPath("STATE.md"));
142
+ }
143
+
144
+ function getRoadmap() {
145
+ return readMd(planningPath("ROADMAP.md"));
146
+ }
147
+
148
+ function getConfig() {
149
+ return readJson(planningPath("config.json")) || {
150
+ mode: "yolo",
151
+ depth: "standard",
152
+ workflow: { research: true, plan_check: true, verifier: true },
153
+ git: { commit_docs: true },
154
+ };
155
+ }
156
+
157
+ // ============================================================================
158
+ // Commands
159
+ // ============================================================================
160
+
161
+ const commands = {};
162
+
163
+ // --- init ---
164
+ commands.init = function () {
165
+ const exists = fs.existsSync(planningPath("PROJECT.md"));
166
+ if (exists) {
167
+ console.log("Project already initialized. Use `draht progress` to see status.");
168
+ process.exit(1);
169
+ }
170
+ ensureDir(planningPath());
171
+ const hasCode =
172
+ fs.existsSync("package.json") ||
173
+ fs.existsSync("src") ||
174
+ fs.existsSync("Cargo.toml") ||
175
+ fs.existsSync("go.mod");
176
+ console.log(banner("INIT"));
177
+ console.log(`\nPlanning directory: ${PLANNING_DIR}/`);
178
+ if (hasCode) {
179
+ console.log("\n⚠️ Existing code detected. Consider running: draht map-codebase");
180
+ }
181
+ if (!hasGit()) {
182
+ console.log("\n⚠️ No git repository. Consider running: git init");
183
+ }
184
+ console.log("\nReady for project initialization.");
185
+ };
186
+
187
+ // --- map-codebase ---
188
+ commands["map-codebase"] = function (dir) {
189
+ const cwd = dir || process.cwd();
190
+ const outDir = planningPath("codebase");
191
+ ensureDir(outDir);
192
+
193
+ console.log(banner("MAPPING CODEBASE"));
194
+
195
+ // Gather file tree
196
+ let tree = "";
197
+ try {
198
+ tree = execSync(
199
+ `find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/.planning/*' | head -200`,
200
+ { cwd, encoding: "utf-8" }
201
+ );
202
+ } catch { /* empty */ }
203
+
204
+ // Gather package info
205
+ let pkgJson = null;
206
+ try {
207
+ pkgJson = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
208
+ } catch { /* empty */ }
209
+
210
+ // Write STACK.md
211
+ writeMd(path.join(outDir, "STACK.md"), `# Technology Stack\n\nGenerated: ${timestamp()}\n\n## File Tree (first 200 files)\n\`\`\`\n${tree}\`\`\`\n\n## Package Info\n\`\`\`json\n${pkgJson ? JSON.stringify({ name: pkgJson.name, dependencies: pkgJson.dependencies, devDependencies: pkgJson.devDependencies }, null, 2) : "No package.json found"}\n\`\`\`\n\n## TODO\n- [ ] Fill in languages, versions, frameworks\n- [ ] Document build tools and runtime\n`);
212
+
213
+ // Write placeholder files
214
+ writeMd(path.join(outDir, "ARCHITECTURE.md"), `# Architecture\n\nGenerated: ${timestamp()}\n\n## TODO\n- [ ] Document file/directory patterns\n- [ ] Map module boundaries\n- [ ] Describe data flow\n`);
215
+
216
+ writeMd(path.join(outDir, "CONVENTIONS.md"), `# Conventions\n\nGenerated: ${timestamp()}\n\n## TODO\n- [ ] Document code style patterns\n- [ ] Document testing patterns\n- [ ] Document error handling approach\n`);
217
+
218
+ writeMd(path.join(outDir, "CONCERNS.md"), `# Concerns\n\nGenerated: ${timestamp()}\n\n## TODO\n- [ ] Identify technical debt\n- [ ] Flag security concerns\n- [ ] Note missing tests\n`);
219
+
220
+ // Domain model extraction
221
+ let domainHints = "";
222
+ try {
223
+ // Extract types/interfaces as potential entities
224
+ const types = execSync(
225
+ `grep -rn 'export\\s\\+\\(interface\\|type\\|class\\)' --include='*.ts' --include='*.go' . 2>/dev/null | grep -v node_modules | grep -v dist | head -50`,
226
+ { cwd, encoding: "utf-8" }
227
+ ).trim();
228
+ if (types) domainHints += `## Types/Interfaces (potential entities)\n\`\`\`\n${types}\n\`\`\`\n\n`;
229
+ } catch { /* empty */ }
230
+ try {
231
+ // Extract directory structure as potential bounded contexts
232
+ const dirs = execSync(
233
+ `find . -type d -maxdepth 3 -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | sort`,
234
+ { cwd, encoding: "utf-8" }
235
+ ).trim();
236
+ if (dirs) domainHints += `## Directory Structure (potential bounded contexts)\n\`\`\`\n${dirs}\n\`\`\`\n`;
237
+ } catch { /* empty */ }
238
+
239
+ writeMd(path.join(outDir, "DOMAIN-HINTS.md"), `# Domain Model Hints\n\nGenerated: ${timestamp()}\n\nExtracted from codebase to help identify domain model.\n\n${domainHints}\n## TODO\n- [ ] Identify entities vs value objects\n- [ ] Map bounded contexts from directory structure\n- [ ] Define ubiquitous language glossary\n`);
240
+
241
+ console.log(`\nCreated:\n ${outDir}/STACK.md\n ${outDir}/ARCHITECTURE.md\n ${outDir}/CONVENTIONS.md\n ${outDir}/CONCERNS.md\n ${outDir}/DOMAIN-HINTS.md`);
242
+ console.log("\n→ Review and fill in the TODOs, then run: draht commit-docs \"map existing codebase\"");
243
+ };
244
+
245
+ // --- create-project ---
246
+ commands["create-project"] = function (...args) {
247
+ const name = args.join(" ") || "Untitled Project";
248
+ const tmpl = `# Project: ${name}\n\n## Vision\n[Fill in]\n\n## Problem\n[What problem does this solve?]\n\n## Target User\n[Who is this for?]\n\n## Tech Stack\n[Languages, frameworks, infrastructure]\n\n## Domain Model\n\n### Bounded Contexts\n- **[Context Name]** — [responsibility]\n\n### Entities\n- **[Entity]** — [description, key attributes]\n\n### Value Objects\n- **[ValueObject]** — [description]\n\n### Aggregates\n- **[Aggregate Root]** — [entities it owns]\n\n### Ubiquitous Language\n| Term | Definition |\n|------|------------|\n| [Term] | [What it means in this domain] |\n\n## Constraints\n[Budget, timeline, platform requirements]\n\n## Success Criteria\n[How do we know this works?]\n\n---\nCreated: ${timestamp()}\n`;
249
+ writeMd(planningPath("PROJECT.md"), tmpl);
250
+ console.log(`Created: ${PLANNING_DIR}/PROJECT.md`);
251
+ };
252
+
253
+ // --- create-requirements ---
254
+ commands["create-requirements"] = function () {
255
+ const tmpl = `# Requirements\n\n## v1 — Must Have\n- [ ] [Requirement 1] *(Context: [bounded context])*\n\n## v2 — Nice to Have\n- [ ] [Requirement 1] *(Context: [bounded context])*\n\n## Bounded Context Mapping\n| Requirement | Bounded Context | Aggregate |\n|-------------|----------------|----------|\n| R1 | [context] | [aggregate] |\n\n## Out of Scope\n- [Explicitly excluded]\n\n---\nCreated: ${timestamp()}\n`;
256
+ writeMd(planningPath("REQUIREMENTS.md"), tmpl);
257
+ console.log(`Created: ${PLANNING_DIR}/REQUIREMENTS.md`);
258
+ };
259
+
260
+ // --- create-roadmap ---
261
+ commands["create-roadmap"] = function () {
262
+ const tmpl = `# Roadmap\n\n## Phase 1: [Name] — \`pending\`\n**Goal:** [Outcome, not activity]\n**Requirements:** [Which requirements this covers]\n**Acceptance:** [How we know it's done]\n\n---\nCreated: ${timestamp()}\n`;
263
+ writeMd(planningPath("ROADMAP.md"), tmpl);
264
+ console.log(`Created: ${PLANNING_DIR}/ROADMAP.md`);
265
+ };
266
+
267
+ // --- create-domain-model ---
268
+ commands["create-domain-model"] = function () {
269
+ const project = readMd(planningPath("PROJECT.md"));
270
+ if (!project) { console.error("No PROJECT.md found — run create-project first"); process.exit(1); }
271
+
272
+ const tmpl = `# Domain Model\n\n## Bounded Contexts\n[Extract from PROJECT.md — identify distinct areas of responsibility]\n\n## Context Map\n[How bounded contexts interact — upstream/downstream, shared kernel, etc.]\n\n## Entities\n[Core domain objects with identity]\n\n## Value Objects\n[Immutable objects defined by attributes]\n\n## Aggregates\n[Cluster of entities with a root — transactional boundary]\n\n## Domain Events\n[Things that happen in the domain]\n\n## Ubiquitous Language Glossary\n| Term | Context | Definition |\n|------|---------|------------|\n| [term] | [context] | [definition] |\n\n---\nGenerated from PROJECT.md: ${timestamp()}\n`;
273
+ writeMd(planningPath("DOMAIN-MODEL.md"), tmpl);
274
+ console.log(`Created: ${PLANNING_DIR}/DOMAIN-MODEL.md`);
275
+ console.log("Fill in the domain model based on PROJECT.md context.");
276
+ };
277
+
278
+ // --- init-state ---
279
+ commands["init-state"] = function () {
280
+ const tmpl = `# State\n\n## Current Phase: 1\n## Status: initialized\n\n## Decisions\n(none yet)\n\n## Blockers\nNone.\n\n## Quick Tasks Completed\n(none)\n\n## Last Activity: ${timestamp()}\n`;
281
+ writeMd(planningPath("STATE.md"), tmpl);
282
+ writeJson(planningPath("config.json"), getConfig());
283
+ console.log(`Created: ${PLANNING_DIR}/STATE.md`);
284
+ console.log(`Created: ${PLANNING_DIR}/config.json`);
285
+ };
286
+
287
+ // --- phase-info ---
288
+ commands["phase-info"] = function (n) {
289
+ const num = parseInt(n, 10);
290
+ if (!num) { console.error("Usage: draht phase-info N"); process.exit(1); }
291
+
292
+ const roadmap = getRoadmap();
293
+ if (!roadmap) { console.error("No ROADMAP.md found"); process.exit(1); }
294
+
295
+ const info = parsePhaseFromRoadmap(roadmap, num);
296
+ const phaseDir = getPhaseDir(num);
297
+ const contextFile = phaseDir ? path.join(phaseDir, `${padNum(num)}-CONTEXT.md`) : null;
298
+ const context = contextFile ? readMd(contextFile) : null;
299
+
300
+ console.log(banner(`PHASE ${num} INFO`));
301
+ if (info) console.log(`\nName: ${info.name}\nStatus: ${info.status}`);
302
+ if (phaseDir) console.log(`Directory: ${phaseDir}`);
303
+ if (context) console.log(`\n--- Context ---\n${context}`);
304
+ else console.log("\nNo context captured yet. Run: gsd-discuss-phase " + num);
305
+ };
306
+
307
+ // --- save-context ---
308
+ commands["save-context"] = function (n, ...rest) {
309
+ const num = parseInt(n, 10);
310
+ if (!num) { console.error("Usage: draht save-context N"); process.exit(1); }
311
+
312
+ const slug = getPhaseSlug(num) || `phase-${num}`;
313
+ const dir = planningPath("phases", `${padNum(num)}-${slug}`);
314
+ ensureDir(dir);
315
+
316
+ const contextPath = path.join(dir, `${padNum(num)}-CONTEXT.md`);
317
+ if (fs.existsSync(contextPath)) {
318
+ console.log(`Context already exists at ${contextPath}`);
319
+ console.log("Edit it directly or pass content via stdin.");
320
+ } else {
321
+ const tmpl = `# Phase ${num} Context\n\n## Domain Boundary\n[What this phase covers]\n\n## Decisions\n[Captured during discussion]\n\n## Claude's Discretion\n[Areas where Claude can decide]\n\n## Deferred Ideas\n[Saved for later]\n\n---\nCreated: ${timestamp()}\n`;
322
+ writeMd(contextPath, tmpl);
323
+ console.log(`Created: ${contextPath}`);
324
+ }
325
+ };
326
+
327
+ // --- load-phase-context ---
328
+ commands["load-phase-context"] = function (n) {
329
+ const num = parseInt(n, 10);
330
+ if (!num) { console.error("Usage: draht load-phase-context N"); process.exit(1); }
331
+
332
+ const files = [];
333
+ const project = readMd(planningPath("PROJECT.md"));
334
+ if (project) files.push({ name: "PROJECT.md", content: project });
335
+
336
+ const reqs = readMd(planningPath("REQUIREMENTS.md"));
337
+ if (reqs) files.push({ name: "REQUIREMENTS.md", content: reqs });
338
+
339
+ const roadmap = getRoadmap();
340
+ if (roadmap) files.push({ name: "ROADMAP.md", content: roadmap });
341
+
342
+ const phaseDir = getPhaseDir(num);
343
+ if (phaseDir) {
344
+ const context = readMd(path.join(phaseDir, `${padNum(num)}-CONTEXT.md`));
345
+ if (context) files.push({ name: `${padNum(num)}-CONTEXT.md`, content: context });
346
+ }
347
+
348
+ // Codebase docs
349
+ const cbDir = planningPath("codebase");
350
+ if (fs.existsSync(cbDir)) {
351
+ for (const f of fs.readdirSync(cbDir)) {
352
+ if (f.endsWith(".md")) {
353
+ files.push({ name: `codebase/${f}`, content: readMd(path.join(cbDir, f)) });
354
+ }
355
+ }
356
+ }
357
+
358
+ // Research docs
359
+ const resDir = planningPath("research");
360
+ if (fs.existsSync(resDir)) {
361
+ for (const f of fs.readdirSync(resDir)) {
362
+ if (f.endsWith(".md")) {
363
+ files.push({ name: `research/${f}`, content: readMd(path.join(resDir, f)) });
364
+ }
365
+ }
366
+ }
367
+
368
+ console.log(banner(`PHASE ${num} CONTEXT`));
369
+ for (const f of files) {
370
+ console.log(`\n=== ${f.name} ===\n${f.content}`);
371
+ }
372
+ };
373
+
374
+ // --- create-plan ---
375
+ commands["create-plan"] = function (n, p, ...titleWords) {
376
+ const phaseNum = parseInt(n, 10);
377
+ const planNum = parseInt(p, 10);
378
+ if (!phaseNum || !planNum) { console.error("Usage: draht create-plan N P [title]"); process.exit(1); }
379
+
380
+ const slug = getPhaseSlug(phaseNum) || `phase-${phaseNum}`;
381
+ const dir = planningPath("phases", `${padNum(phaseNum)}-${slug}`);
382
+ ensureDir(dir);
383
+
384
+ const title = titleWords.join(" ") || `Plan ${planNum}`;
385
+ const planPath = path.join(dir, `${padNum(phaseNum)}-${padNum(planNum)}-PLAN.md`);
386
+
387
+ const tmpl = `---
388
+ phase: ${phaseNum}
389
+ plan: ${planNum}
390
+ depends_on: []
391
+ must_haves:
392
+ - "[Observable truth this plan delivers]"
393
+ ---
394
+
395
+ # Phase ${phaseNum}, Plan ${planNum}: ${title}
396
+
397
+ ## Goal
398
+ [What this plan achieves from user perspective]
399
+
400
+ ## Context
401
+ [Key decisions that affect this plan]
402
+
403
+ ## Tasks
404
+
405
+ <task type="auto">
406
+ <n>[Task name]</n>
407
+ <files>[affected files]</files>
408
+ <test>[Write tests first — what should pass when done]</test>
409
+ <action>
410
+ [Implementation to make tests pass]
411
+ </action>
412
+ <refactor>[Optional cleanup after green]</refactor>
413
+ <verify>[How to verify]</verify>
414
+ <done>[What "done" looks like]</done>
415
+ </task>
416
+
417
+ ---
418
+ Created: ${timestamp()}
419
+ `;
420
+ writeMd(planPath, tmpl);
421
+ console.log(`Created: ${planPath}`);
422
+ };
423
+
424
+ // --- discover-plans ---
425
+ commands["discover-plans"] = function (n) {
426
+ const num = parseInt(n, 10);
427
+ if (!num) { console.error("Usage: draht discover-plans N"); process.exit(1); }
428
+
429
+ const phaseDir = getPhaseDir(num);
430
+ if (!phaseDir) { console.error(`Phase ${num} directory not found`); process.exit(1); }
431
+
432
+ const files = fs.readdirSync(phaseDir).sort();
433
+ const plans = files.filter((f) => f.endsWith("-PLAN.md") && !f.includes("FIX"));
434
+ const summaries = files.filter((f) => f.endsWith("-SUMMARY.md"));
435
+ const fixPlans = files.filter((f) => f.includes("FIX-PLAN.md"));
436
+
437
+ const completedPlanNums = new Set(
438
+ summaries.map((s) => {
439
+ const match = s.match(/\d+-(\d+)-SUMMARY/);
440
+ return match ? match[1] : null;
441
+ }).filter(Boolean)
442
+ );
443
+
444
+ const incomplete = plans.filter((p) => {
445
+ const match = p.match(/\d+-(\d+)-PLAN/);
446
+ return match ? !completedPlanNums.has(match[1]) : true;
447
+ });
448
+
449
+ console.log(banner(`PHASE ${num} PLANS`));
450
+ console.log(`\nTotal plans: ${plans.length}`);
451
+ console.log(`Completed: ${summaries.length}`);
452
+ console.log(`Remaining: ${incomplete.length}`);
453
+ console.log(`Fix plans: ${fixPlans.length}`);
454
+
455
+ if (incomplete.length > 0) {
456
+ console.log(`\nIncomplete plans:`);
457
+ for (const p of incomplete) console.log(` - ${p}`);
458
+ }
459
+
460
+ // Parse dependencies for ordering
461
+ const planData = plans.map((p) => {
462
+ const content = readMd(path.join(phaseDir, p));
463
+ const depsMatch = content?.match(/depends_on:\s*\[(.*?)\]/);
464
+ const deps = depsMatch ? depsMatch[1].split(",").map((d) => d.trim()).filter(Boolean) : [];
465
+ return { file: p, deps };
466
+ });
467
+
468
+ // Output as JSON for programmatic use
469
+ console.log(`\n--- JSON ---`);
470
+ console.log(JSON.stringify({ plans: planData, incomplete, fixPlans }, null, 2));
471
+ };
472
+
473
+ // --- read-plan ---
474
+ commands["read-plan"] = function (n, p) {
475
+ const phaseNum = parseInt(n, 10);
476
+ const planNum = parseInt(p, 10);
477
+ if (!phaseNum || !planNum) { console.error("Usage: draht read-plan N P"); process.exit(1); }
478
+
479
+ const phaseDir = getPhaseDir(phaseNum);
480
+ if (!phaseDir) { console.error(`Phase ${phaseNum} not found`); process.exit(1); }
481
+
482
+ const planFile = `${padNum(phaseNum)}-${padNum(planNum)}-PLAN.md`;
483
+ const content = readMd(path.join(phaseDir, planFile));
484
+ if (!content) { console.error(`Plan file not found: ${planFile}`); process.exit(1); }
485
+
486
+ console.log(content);
487
+ };
488
+
489
+ // --- validate-plans ---
490
+ commands["validate-plans"] = function (n) {
491
+ const num = parseInt(n, 10);
492
+ if (!num) { console.error("Usage: draht validate-plans N"); process.exit(1); }
493
+
494
+ const phaseDir = getPhaseDir(num);
495
+ if (!phaseDir) { console.error(`Phase ${num} not found`); process.exit(1); }
496
+
497
+ const files = fs.readdirSync(phaseDir).filter((f) => f.endsWith("-PLAN.md"));
498
+ const issues = [];
499
+
500
+ for (const file of files) {
501
+ const content = readMd(path.join(phaseDir, file));
502
+ if (!content) continue;
503
+
504
+ // Check for required elements
505
+ if (!content.includes("<task")) issues.push(`${file}: No <task> elements found`);
506
+ if (!content.includes("<verify>")) issues.push(`${file}: Missing <verify> in tasks`);
507
+ if (!content.includes("<done>")) issues.push(`${file}: Missing <done> in tasks`);
508
+ if (!content.includes("must_haves")) issues.push(`${file}: Missing must_haves in frontmatter`);
509
+
510
+ // Count tasks
511
+ const taskCount = (content.match(/<task/g) || []).length;
512
+ if (taskCount > 5) issues.push(`${file}: ${taskCount} tasks (max recommended: 5)`);
513
+ if (taskCount === 0) issues.push(`${file}: No tasks defined`);
514
+ }
515
+
516
+ console.log(banner(`VALIDATE PHASE ${num}`));
517
+ console.log(`\nPlans checked: ${files.length}`);
518
+ if (issues.length === 0) {
519
+ console.log("✅ All plans valid");
520
+ } else {
521
+ console.log(`\n⚠️ ${issues.length} issue(s):`);
522
+ for (const issue of issues) console.log(` - ${issue}`);
523
+ }
524
+ };
525
+
526
+ // --- commit-task ---
527
+ commands["commit-task"] = function (n, p, t, ...desc) {
528
+ const phaseNum = padNum(parseInt(n, 10));
529
+ const planNum = padNum(parseInt(p, 10));
530
+ const taskNum = t || "1";
531
+ const description = desc.join(" ") || "implement task";
532
+
533
+ const hash = gitCommitAll(`feat(${phaseNum}-${planNum}): ${description}`);
534
+ if (hash) {
535
+ console.log(`Committed: ${hash} — feat(${phaseNum}-${planNum}): ${description}`);
536
+ // TDD check: warn if no test files in commit
537
+ try {
538
+ const files = execSync(`git diff-tree --no-commit-id --name-only -r ${hash}`, { encoding: "utf-8" }).trim();
539
+ const hasTests = files.split("\n").some((f) => /\.(test|spec)\.(ts|tsx|js|jsx)$|_test\.(go|ts)$/.test(f));
540
+ if (!hasTests) {
541
+ console.log("⚠️ No test files in this commit — TDD requires tests first");
542
+ }
543
+ } catch { /* ignore */ }
544
+ } else {
545
+ console.log("Nothing to commit (or no git)");
546
+ }
547
+ };
548
+
549
+ // --- write-summary ---
550
+ commands["write-summary"] = function (n, p) {
551
+ const phaseNum = parseInt(n, 10);
552
+ const planNum = parseInt(p, 10);
553
+ if (!phaseNum || !planNum) { console.error("Usage: draht write-summary N P"); process.exit(1); }
554
+
555
+ const phaseDir = getPhaseDir(phaseNum);
556
+ if (!phaseDir) { console.error(`Phase ${phaseNum} not found`); process.exit(1); }
557
+
558
+ const summaryPath = path.join(phaseDir, `${padNum(phaseNum)}-${padNum(planNum)}-SUMMARY.md`);
559
+ const tmpl = `# Phase ${phaseNum}, Plan ${planNum} Summary\n\n## Completed Tasks\n| # | Task | Status | Commit |\n|---|------|--------|--------|\n| 1 | [task] | ✅ Done | [hash] |\n\n## Files Changed\n- [files]\n\n## Verification Results\n- [results]\n\n## Notes\n[deviations, decisions]\n\n---\nCompleted: ${timestamp()}\n`;
560
+ writeMd(summaryPath, tmpl);
561
+ console.log(`Created: ${summaryPath}`);
562
+ };
563
+
564
+ // --- verify-phase ---
565
+ commands["verify-phase"] = function (n) {
566
+ const num = parseInt(n, 10);
567
+ if (!num) { console.error("Usage: draht verify-phase N"); process.exit(1); }
568
+
569
+ const phaseDir = getPhaseDir(num);
570
+ if (!phaseDir) { console.error(`Phase ${num} not found`); process.exit(1); }
571
+
572
+ const plans = fs.readdirSync(phaseDir).filter((f) => f.endsWith("-PLAN.md") && !f.includes("FIX"));
573
+ const summaries = fs.readdirSync(phaseDir).filter((f) => f.endsWith("-SUMMARY.md"));
574
+
575
+ console.log(banner(`VERIFY PHASE ${num}`));
576
+ console.log(`\nPlans: ${plans.length}`);
577
+ console.log(`Summaries: ${summaries.length}`);
578
+
579
+ if (summaries.length >= plans.length) {
580
+ console.log("\n✅ All plans have summaries — phase execution complete");
581
+ // Write verification file
582
+ const verPath = path.join(phaseDir, `${padNum(num)}-VERIFICATION.md`);
583
+ writeMd(verPath, `# Phase ${num} Verification\n\nAll ${plans.length} plans executed.\nVerified: ${timestamp()}\n`);
584
+ } else {
585
+ console.log(`\n⚠️ ${plans.length - summaries.length} plan(s) still incomplete`);
586
+ }
587
+ };
588
+
589
+ // --- extract-deliverables ---
590
+ commands["extract-deliverables"] = function (n) {
591
+ const num = parseInt(n, 10);
592
+ if (!num) { console.error("Usage: draht extract-deliverables N"); process.exit(1); }
593
+
594
+ const phaseDir = getPhaseDir(num);
595
+ if (!phaseDir) { console.error(`Phase ${num} not found`); process.exit(1); }
596
+
597
+ const plans = fs.readdirSync(phaseDir).filter((f) => f.endsWith("-PLAN.md"));
598
+ const deliverables = [];
599
+
600
+ for (const planFile of plans) {
601
+ const content = readMd(path.join(phaseDir, planFile));
602
+ if (!content) continue;
603
+
604
+ // Extract must_haves
605
+ const mustHaveMatch = content.match(/must_haves:\s*\n((?:\s+-\s+.+\n?)*)/);
606
+ if (mustHaveMatch) {
607
+ const items = mustHaveMatch[1].match(/- ["']?(.+?)["']?\s*$/gm) || [];
608
+ for (const item of items) {
609
+ deliverables.push({ source: planFile, type: "must_have", text: item.replace(/^\s*-\s*["']?|["']?\s*$/g, "") });
610
+ }
611
+ }
612
+
613
+ // Extract <done> tags
614
+ const doneMatches = content.matchAll(/<done>([\s\S]*?)<\/done>/g);
615
+ for (const match of doneMatches) {
616
+ deliverables.push({ source: planFile, type: "done", text: match[1].trim() });
617
+ }
618
+ }
619
+
620
+ console.log(banner(`PHASE ${num} DELIVERABLES`));
621
+ console.log(`\nFound ${deliverables.length} testable items:\n`);
622
+ deliverables.forEach((d, i) => {
623
+ console.log(` ${i + 1}. [${d.type}] ${d.text} (from ${d.source})`);
624
+ });
625
+
626
+ console.log(`\n--- JSON ---`);
627
+ console.log(JSON.stringify(deliverables, null, 2));
628
+ };
629
+
630
+ // --- create-fix-plan ---
631
+ commands["create-fix-plan"] = function (n, p, ...issueWords) {
632
+ const phaseNum = parseInt(n, 10);
633
+ const planNum = parseInt(p, 10);
634
+ if (!phaseNum || !planNum) { console.error("Usage: draht create-fix-plan N P [issue]"); process.exit(1); }
635
+
636
+ const slug = getPhaseSlug(phaseNum) || `phase-${phaseNum}`;
637
+ const dir = planningPath("phases", `${padNum(phaseNum)}-${slug}`);
638
+ const issue = issueWords.join(" ") || "Fix identified issues";
639
+
640
+ const fixPath = path.join(dir, `${padNum(phaseNum)}-${padNum(planNum)}-FIX-PLAN.md`);
641
+ const tmpl = `---
642
+ gap_closure: true
643
+ fixes_plan: ${planNum}
644
+ issue: "${issue}"
645
+ ---
646
+
647
+ # Fix Plan for Phase ${phaseNum}, Plan ${planNum}
648
+
649
+ ## Issue
650
+ ${issue}
651
+
652
+ ## Tasks
653
+
654
+ <task type="auto">
655
+ <n>[Fix description]</n>
656
+ <files>[affected files]</files>
657
+ <action>[Fix instructions]</action>
658
+ <verify>[How to verify fix]</verify>
659
+ <done>[What "fixed" looks like]</done>
660
+ </task>
661
+
662
+ ---
663
+ Created: ${timestamp()}
664
+ `;
665
+ writeMd(fixPath, tmpl);
666
+ console.log(`Created: ${fixPath}`);
667
+ };
668
+
669
+ // --- write-uat ---
670
+ commands["write-uat"] = function (n) {
671
+ const num = parseInt(n, 10);
672
+ if (!num) { console.error("Usage: draht write-uat N"); process.exit(1); }
673
+
674
+ const phaseDir = getPhaseDir(num);
675
+ if (!phaseDir) { console.error(`Phase ${num} not found`); process.exit(1); }
676
+
677
+ const uatPath = path.join(phaseDir, `${padNum(num)}-UAT.md`);
678
+ const tmpl = `# Phase ${num} User Acceptance Testing\n\n## Test Date: ${dateStamp()}\n\n## Results\n| # | Deliverable | Status | Notes |\n|---|-------------|--------|-------|\n| 1 | [description] | ✅ Pass | |\n\n## Summary\n- Passed: X/Y\n- Failed: 0/Y\n- Skipped: 0/Y\n\n## Fix Plans Created\n(none)\n`;
679
+ writeMd(uatPath, tmpl);
680
+ console.log(`Created: ${uatPath}`);
681
+ };
682
+
683
+ // --- next-quick-number ---
684
+ commands["next-quick-number"] = function () {
685
+ const dir = planningPath("quick");
686
+ if (!fs.existsSync(dir)) { console.log("001"); return; }
687
+ const entries = fs.readdirSync(dir).sort();
688
+ const last = entries[entries.length - 1];
689
+ const num = last ? parseInt(last.match(/^(\d+)/)?.[1] || "0", 10) + 1 : 1;
690
+ console.log(padNum(num, 3));
691
+ };
692
+
693
+ // --- create-quick-plan ---
694
+ commands["create-quick-plan"] = function (n, ...descWords) {
695
+ const num = padNum(parseInt(n, 10), 3);
696
+ const desc = descWords.join(" ") || "Quick task";
697
+ const slug = slugify(desc);
698
+ const dir = planningPath("quick", `${num}-${slug}`);
699
+ ensureDir(dir);
700
+
701
+ const planPath = path.join(dir, `${num}-PLAN.md`);
702
+ const tmpl = `# Quick Task ${num}: ${desc}\n\n## Tasks\n\n<task type="auto">\n <n>[Task]</n>\n <files>[files]</files>\n <action>[instructions]</action>\n <verify>[verify]</verify>\n <done>[done]</done>\n</task>\n\n---\nCreated: ${timestamp()}\n`;
703
+ writeMd(planPath, tmpl);
704
+ console.log(`Created: ${planPath}`);
705
+ };
706
+
707
+ // --- write-quick-summary ---
708
+ commands["write-quick-summary"] = function (n) {
709
+ const num = padNum(parseInt(n, 10), 3);
710
+ const dir = planningPath("quick");
711
+ if (!fs.existsSync(dir)) { console.error("No quick tasks directory"); process.exit(1); }
712
+ const match = fs.readdirSync(dir).find((e) => e.startsWith(num));
713
+ if (!match) { console.error(`Quick task ${num} not found`); process.exit(1); }
714
+
715
+ const summaryPath = path.join(dir, match, `${num}-SUMMARY.md`);
716
+ const tmpl = `# Quick Task ${num} Summary\n\n## Tasks Completed\n| # | Task | Status | Commit |\n|---|------|--------|--------|\n| 1 | [task] | ✅ Done | [hash] |\n\n## Files Changed\n- [files]\n\n---\nCompleted: ${timestamp()}\n`;
717
+ writeMd(summaryPath, tmpl);
718
+ console.log(`Created: ${summaryPath}`);
719
+ };
720
+
721
+ // --- update-state ---
722
+ commands["update-state"] = function () {
723
+ const statePath = planningPath("STATE.md");
724
+ let state = readMd(statePath);
725
+ if (!state) { console.error("No STATE.md found"); process.exit(1); }
726
+
727
+ // Update last activity
728
+ state = state.replace(/## Last Activity:.*/, `## Last Activity: ${timestamp()}`);
729
+ writeMd(statePath, state);
730
+ console.log(`Updated: ${statePath}`);
731
+ };
732
+
733
+ // --- progress ---
734
+ commands.progress = function () {
735
+ const state = getState();
736
+ const roadmap = getRoadmap();
737
+ if (!state || !roadmap) {
738
+ console.log("No Draht project found. Run: draht init");
739
+ process.exit(1);
740
+ }
741
+
742
+ console.log(banner("PROJECT STATUS"));
743
+
744
+ // Parse phases from roadmap
745
+ const phaseRegex = /## Phase (\d+):\s*(.+?)\s*—\s*`(\w+)`/g;
746
+ let match;
747
+ const phases = [];
748
+ while ((match = phaseRegex.exec(roadmap))) {
749
+ phases.push({ num: parseInt(match[1], 10), name: match[2], status: match[3] });
750
+ }
751
+
752
+ if (phases.length === 0) {
753
+ console.log("\nNo phases found in ROADMAP.md");
754
+ } else {
755
+ console.log("\nPhases:");
756
+ for (const phase of phases) {
757
+ const icon = phase.status === "complete" ? "✅" : phase.status === "in-progress" ? "🔄" : "⬜";
758
+ const phaseDir = getPhaseDir(phase.num);
759
+ let planInfo = "";
760
+ if (phaseDir) {
761
+ const plans = fs.readdirSync(phaseDir).filter((f) => f.endsWith("-PLAN.md") && !f.includes("FIX"));
762
+ const summaries = fs.readdirSync(phaseDir).filter((f) => f.endsWith("-SUMMARY.md"));
763
+ planInfo = ` (${summaries.length}/${plans.length} plans)`;
764
+ }
765
+ console.log(` ${icon} Phase ${phase.num}: ${phase.name} — ${phase.status}${planInfo}`);
766
+ }
767
+ }
768
+
769
+ // Quick tasks
770
+ const quickDir = planningPath("quick");
771
+ if (fs.existsSync(quickDir)) {
772
+ const quickCount = fs.readdirSync(quickDir).length;
773
+ console.log(`\nQuick Tasks: ${quickCount}`);
774
+ }
775
+
776
+ // Blockers from state
777
+ const blockerMatch = state.match(/## Blockers\n([\s\S]*?)(?=\n##|\n---|\Z)/);
778
+ if (blockerMatch) {
779
+ const blockers = blockerMatch[1].trim();
780
+ if (blockers && blockers !== "None." && blockers !== "(none)") {
781
+ console.log(`\n⚠️ Blockers:\n${blockers}`);
782
+ }
783
+ }
784
+
785
+ // Last activity
786
+ const lastMatch = state.match(/## Last Activity:\s*(.+)/);
787
+ if (lastMatch) console.log(`\nLast activity: ${lastMatch[1]}`);
788
+ };
789
+
790
+ // --- pause ---
791
+ commands.pause = function () {
792
+ const state = getState();
793
+ if (!state) { console.error("No STATE.md found"); process.exit(1); }
794
+
795
+ let gitStatus = "";
796
+ try { gitStatus = execSync("git status --porcelain", { encoding: "utf-8" }).trim(); } catch { /* empty */ }
797
+
798
+ const tmpl = `# Continue Here\n\n## Session Paused: ${timestamp()}\n\n## Current Position\n[Fill from STATE.md]\n\n## What Was Happening\n[Brief description]\n\n## Uncommitted Changes\n${gitStatus || "None"}\n\n## Next Steps\n1. [What to do next]\n\n## Open Questions\n- [Any unresolved decisions]\n`;
799
+ writeMd(planningPath("CONTINUE-HERE.md"), tmpl);
800
+ console.log(`Created: ${PLANNING_DIR}/CONTINUE-HERE.md`);
801
+ };
802
+
803
+ // --- resume ---
804
+ commands.resume = function () {
805
+ const continueFile = readMd(planningPath("CONTINUE-HERE.md"));
806
+ if (continueFile) {
807
+ console.log(banner("RESUMING WORK"));
808
+ console.log(`\n${continueFile}`);
809
+ } else {
810
+ const state = getState();
811
+ if (state) {
812
+ console.log(banner("RESUMING WORK (from STATE.md)"));
813
+ console.log(`\n${state}`);
814
+ } else {
815
+ console.log("No Draht project found.");
816
+ }
817
+ }
818
+ };
819
+
820
+ // --- commit-docs ---
821
+ commands["commit-docs"] = function (...msg) {
822
+ const message = msg.join(" ") || "update planning docs";
823
+ const hash = gitCommit(`docs: ${message}`);
824
+ if (hash) console.log(`Committed: ${hash} — docs: ${message}`);
825
+ else console.log("Nothing to commit (or no git)");
826
+ };
827
+
828
+ // --- research-phase ---
829
+ commands["research-phase"] = function (n) {
830
+ const num = parseInt(n, 10);
831
+ if (!num) { console.error("Usage: draht research-phase N"); process.exit(1); }
832
+
833
+ const slug = getPhaseSlug(num) || `phase-${num}`;
834
+ const dir = planningPath("phases", `${padNum(num)}-${slug}`);
835
+ ensureDir(dir);
836
+
837
+ const resPath = path.join(dir, `${padNum(num)}-RESEARCH.md`);
838
+ const tmpl = `# Phase ${num} Research\n\nGenerated: ${timestamp()}\n\n## Best Practices\n[Fill in]\n\n## Patterns & Anti-Patterns\n[Fill in]\n\n## Library Recommendations\n[Fill in]\n\n## Edge Cases & Gotchas\n[Fill in]\n`;
839
+ writeMd(resPath, tmpl);
840
+ console.log(`Created: ${resPath}`);
841
+ console.log("→ Fill in research findings, then plan the phase.");
842
+ };
843
+
844
+ // ============================================================================
845
+ // Help & Dispatch
846
+ // ============================================================================
847
+
848
+ commands.help = function () {
849
+ console.log(`
850
+ Draht Tools — Get Shit Done CLI
851
+
852
+ Usage: draht <command> [args]
853
+
854
+ Project Setup:
855
+ init Check preconditions, create .planning/
856
+ map-codebase [dir] Analyze existing codebase
857
+ create-project [name] Create PROJECT.md
858
+ create-requirements Create REQUIREMENTS.md
859
+ create-domain-model Generate DOMAIN-MODEL.md from PROJECT.md
860
+ create-roadmap Create ROADMAP.md
861
+ init-state Create STATE.md + config.json
862
+
863
+ Phase Management:
864
+ phase-info N Show phase details
865
+ save-context N Create/show CONTEXT.md for phase
866
+ load-phase-context N Load all context for planning
867
+ research-phase N Create research template for phase
868
+ create-plan N P [title] Create PLAN.md template
869
+ discover-plans N List and order plans in a phase
870
+ read-plan N P Output plan content
871
+ validate-plans N Check plans for required elements
872
+
873
+ Execution:
874
+ commit-task N P T [desc] Git commit for a task
875
+ write-summary N P Create SUMMARY.md for completed plan
876
+ verify-phase N Check all plans have summaries
877
+
878
+ Verification:
879
+ extract-deliverables N List testable items from plans
880
+ create-fix-plan N P [issue] Create FIX-PLAN.md for failed tests
881
+ write-uat N Create UAT report
882
+
883
+ Quick Tasks:
884
+ next-quick-number Get next quick task number
885
+ create-quick-plan NNN [desc] Create quick task plan
886
+ write-quick-summary NNN Create quick task summary
887
+
888
+ Session:
889
+ pause Create CONTINUE-HERE.md
890
+ resume Load last session state
891
+ progress Show project status
892
+ update-state Update STATE.md timestamp
893
+
894
+ Git:
895
+ commit-docs [message] Commit .planning/ changes
896
+ commit-task N P T [desc] Commit all changes as task
897
+
898
+ Version: 1.0.0
899
+ `);
900
+ };
901
+
902
+ // Dispatch
903
+ const [cmd, ...args] = process.argv.slice(2);
904
+
905
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
906
+ commands.help();
907
+ } else if (commands[cmd]) {
908
+ commands[cmd](...args);
909
+ } else {
910
+ console.error(`Unknown command: ${cmd}\nRun: draht help`);
911
+ process.exit(1);
912
+ }