@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,59 @@
1
+ # /execute-phase
2
+
3
+ Execute all plans in a phase with atomic commits.
4
+
5
+ ## Usage
6
+ ```
7
+ /execute-phase [N] [--gaps-only]
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht discover-plans N` to find and order plans
12
+ 2. For each plan in dependency order:
13
+ a. Load plan: `draht read-plan N P`
14
+ b. Execute each task in strict TDD cycle:
15
+
16
+ **🔴 RED — Write failing tests first**
17
+ - Write the test cases from `<test>`
18
+ - Run tests — confirm they FAIL (if they pass, the test is wrong)
19
+ - Commit failing tests: `draht commit-task N P T "red: test description"`
20
+
21
+ **🟢 GREEN — Minimal implementation**
22
+ - Write the minimum code from `<action>` to make tests pass
23
+ - Use domain language from `<context>` and `<domain>` for all names
24
+ - Run tests — confirm they PASS
25
+ - Commit: `draht commit-task N P T "green: task name"`
26
+
27
+ **🔵 REFACTOR — Clean up with safety net**
28
+ - Apply improvements from `<refactor>` (if any)
29
+ - Run tests after each change — must stay green
30
+ - Verify domain language compliance (names match DOMAIN.md)
31
+ - Commit: `draht commit-task N P T "refactor: description"`
32
+
33
+ **✅ VERIFY**
34
+ - Run the `<verify>` step
35
+ - Confirm `<done>` criteria met
36
+
37
+ c. Write summary: `draht write-summary N P`
38
+ 3. Phase verification: `draht verify-phase N`
39
+ 4. Update state: `draht update-state`
40
+ 5. Final commit: `draht commit-docs "complete phase N execution"`
41
+
42
+ ## TDD Rules
43
+ - Never write implementation before a failing test exists
44
+ - If a test passes immediately after being written, it is not testing the right thing — fix it
45
+ - Red → Green → Refactor is not optional; skipping steps invalidates the safety net
46
+ - Each TDD phase gets its own commit so the history is auditable
47
+
48
+ ## Domain Rules
49
+ - All identifiers (class names, method names, variables) must use the ubiquitous language from `.planning/DOMAIN.md`
50
+ - Do not import across bounded context boundaries directly — use domain events or ACL adapters
51
+ - If implementation reveals a missing domain term, stop and update DOMAIN.md before continuing
52
+
53
+ ## Checkpoint Handling
54
+ - `type="auto"` → execute without pausing
55
+ - `type="checkpoint:human-verify"` → pause, show user, wait for confirmation
56
+ - `type="checkpoint:decision"` → pause, present options, wait for choice
57
+
58
+ ## Flags
59
+ - `--gaps-only` → only execute FIX-PLAN.md files from failed verification
@@ -0,0 +1,32 @@
1
+ # /map-codebase
2
+
3
+ Analyze existing codebase before planning.
4
+
5
+ ## Usage
6
+ ```
7
+ /map-codebase [directory]
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht map-codebase [dir]`
12
+ 2. Tool generates: STACK.md, ARCHITECTURE.md, CONVENTIONS.md, CONCERNS.md
13
+ 3. Review output, supplement with your own analysis if needed
14
+ 4. Identify implicit bounded contexts from directory structure:
15
+ - Look for top-level `src/` subdirectories, packages, or modules that encapsulate a coherent domain concept
16
+ - Note any cross-directory coupling that suggests context boundaries are blurred
17
+ 5. Extract domain language from existing code:
18
+ - Collect PascalCase class/interface/type names, key function names, and database table/collection names
19
+ - Look for repeated nouns that represent core domain concepts (e.g., `Order`, `Invoice`, `Subscription`)
20
+ 6. If `.planning/DOMAIN.md` does not already exist, create it with:
21
+ - `## Bounded Contexts` — one entry per discovered context with a brief description
22
+ - `## Ubiquitous Language` — glossary of extracted domain terms
23
+ - `## Context Map` — rough diagram or list showing how contexts relate
24
+ - `## Aggregates` — list aggregates and their root entities per context
25
+ - `## Domain Events` — any existing event names or patterns discovered
26
+ 7. Discover test infrastructure and document findings in `.planning/TEST-STRATEGY.md`:
27
+ - Test framework(s) in use (Jest, Vitest, Bun test, Mocha, etc.)
28
+ - Test directory conventions (co-located, `__tests__/`, `test/`, etc.)
29
+ - Existing coverage configuration and goals (if any)
30
+ - Which layers have tests today (unit, integration, e2e)
31
+ - Gaps and recommendations
32
+ 8. Commit: `draht commit-docs "map existing codebase"`
@@ -0,0 +1,40 @@
1
+ # /new-project
2
+
3
+ Initialize a new GSD project: questioning → research → requirements → roadmap.
4
+
5
+ ## Usage
6
+ ```
7
+ /new-project [description]
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht init` to check preconditions
12
+ 2. If existing code detected, run `draht map-codebase` first
13
+ 3. Deep questioning phase (3-7 rounds, 1-2 questions at a time)
14
+ 4. Run `draht create-project` with gathered info
15
+ 5. Run `draht create-domain-model` to define bounded contexts, entities, and ubiquitous language
16
+ 6. Create `.planning/DOMAIN.md` with:
17
+ - `## Bounded Contexts` — each context with name, responsibility, and brief description
18
+ - `## Ubiquitous Language` — glossary of domain terms agreed with the user (term → definition)
19
+ - `## Context Map` — how bounded contexts relate to each other (upstream/downstream, shared kernel, ACL)
20
+ - `## Aggregates` — aggregates and their root entities per context
21
+ - `## Domain Events` — named events that cross context boundaries
22
+ 7. Create `.planning/TEST-STRATEGY.md` with:
23
+ - `## Test Framework` — chosen framework and rationale
24
+ - `## Directory Conventions` — where test files live relative to source
25
+ - `## Coverage Goals` — target coverage percentage and which paths are critical
26
+ - `## Testing Levels` — what is tested at unit level vs integration vs e2e, with examples
27
+ - `## Excluded` — what is explicitly not tested and why (config files, generated code, etc.)
28
+ 8. Optional research phase via `draht research`
29
+ 9. Run `draht create-requirements` with v1/v2/out-of-scope (map requirements to bounded contexts)
30
+ 10. Run `draht create-roadmap` with phases
31
+ 11. Run `draht init-state`
32
+ 12. Git commit via `draht commit-docs "initialize GSD project"`
33
+
34
+ ## Rules
35
+ - Ask 1-2 questions at a time, never dump 10 at once
36
+ - Follow threads based on answers
37
+ - Use examples ("Like Stripe Checkout, or custom?")
38
+ - Confirm, don't assume
39
+ - 3-7 follow-up rounds typical
40
+ - Stop when you have: problem, audience, MVP scope, constraints, success criteria
@@ -0,0 +1,12 @@
1
+ # /pause-work
2
+
3
+ Create a handoff document for session continuity.
4
+
5
+ ## Usage
6
+ ```
7
+ /pause-work
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht pause` — creates CONTINUE-HERE.md
12
+ 2. Commit: `draht commit-docs "pause work"`
@@ -0,0 +1,61 @@
1
+ # /plan-phase
2
+
3
+ Create atomic execution plans for a roadmap phase.
4
+
5
+ ## Usage
6
+ ```
7
+ /plan-phase [N]
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht load-phase-context N` to gather all context
12
+ 2. Optional: `draht research-phase N` for domain research
13
+ 3. Goal-backward planning:
14
+ a. State the goal (outcome, not activity)
15
+ b. Derive observable truths (3-7 from user perspective)
16
+ c. From each observable truth, derive the test scenarios that would prove it (specific inputs → expected outputs or state changes)
17
+ d. Map to required artifacts (files, endpoints, schemas)
18
+ e. Break into atomic tasks (2-5 per plan)
19
+ 4. Write plans: `draht create-plan N P`
20
+ 5. Validate: `draht validate-plans N`
21
+ 6. Commit: `draht commit-docs "create phase N plans"`
22
+
23
+ ## Plan Format
24
+ Plans use XML task format:
25
+ ```xml
26
+ <task type="auto">
27
+ <n>Task name</n>
28
+ <context>Bounded context this task belongs to</context>
29
+ <domain>Aggregates, entities, value objects, or events touched</domain>
30
+ <files>affected files</files>
31
+ <test>
32
+ RED phase: Write failing tests FIRST.
33
+ - List specific test cases with expected behavior
34
+ - Test domain invariants and business rules
35
+ - Test at the right level: unit for domain logic, integration for context boundaries
36
+ - Tests MUST fail before implementation
37
+ </test>
38
+ <action>
39
+ GREEN phase: Minimal implementation to make tests pass.
40
+ - No gold-plating — just make the red tests green
41
+ - Respect aggregate boundaries
42
+ - Use domain language in code (class names, method names, variable names)
43
+ </action>
44
+ <refactor>
45
+ REFACTOR phase: Improve structure while keeping tests green.
46
+ - Extract value objects, push logic into domain layer
47
+ - Ensure naming matches ubiquitous language
48
+ - Remove duplication across bounded contexts (or make shared kernel explicit)
49
+ </refactor>
50
+ <verify>How to verify (tests pass + manual check if needed)</verify>
51
+ <done>What "done" looks like — expressed as passing test assertions</done>
52
+ </task>
53
+ ```
54
+
55
+ Task types: `auto`, `checkpoint:human-verify`, `checkpoint:decision`
56
+
57
+ ## Domain Rules for Plans
58
+ - File/module structure should mirror bounded contexts (e.g., `src/billing/`, `src/catalog/`)
59
+ - Never scatter one aggregate's logic across multiple contexts without an explicit ACL
60
+ - If a plan introduces a new domain term, update `.planning/DOMAIN.md` first
61
+ - Cross-context communication should use domain events, not direct imports
@@ -0,0 +1,12 @@
1
+ # /progress
2
+
3
+ Show current project status.
4
+
5
+ ## Usage
6
+ ```
7
+ /progress
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht progress` — outputs formatted status
12
+ 2. Display the result as-is
@@ -0,0 +1,19 @@
1
+ # /quick
2
+
3
+ Execute a small ad-hoc task with GSD tracking.
4
+
5
+ ## Usage
6
+ ```
7
+ /quick [description]
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht next-quick-number` to get task number
12
+ 2. Create quick plan: `draht create-quick-plan NNN "description"`
13
+ 3. Execute tasks following the TDD cycle:
14
+ - **🔴 RED** — Write a failing test that describes the desired behaviour
15
+ - **🟢 GREEN** — Write the minimum implementation to make it pass
16
+ - **🔵 REFACTOR** — Clean up while keeping the test green
17
+ - *Exception: skip the TDD cycle only for pure config or documentation-only tasks that have no testable behaviour*
18
+ 4. Write summary: `draht write-quick-summary NNN`
19
+ 5. Update state: `draht update-state`
@@ -0,0 +1,13 @@
1
+ # /resume-work
2
+
3
+ Resume from last session state.
4
+
5
+ ## Usage
6
+ ```
7
+ /resume-work
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run `draht resume` — loads CONTINUE-HERE.md or STATE.md
12
+ 2. Display context and ask to continue
13
+ 3. Delete CONTINUE-HERE.md after confirmation
@@ -0,0 +1,27 @@
1
+ # /verify-work
2
+
3
+ Walk through acceptance testing of completed phase work.
4
+
5
+ ## Usage
6
+ ```
7
+ /verify-work [N]
8
+ ```
9
+
10
+ ## Steps
11
+ 1. Run full test suite and capture results:
12
+ - Execute all tests (`bun test` or project-specific runner)
13
+ - Record pass/fail counts — verification cannot proceed if tests are failing
14
+ 2. Check domain language violations:
15
+ - Load `.planning/DOMAIN.md` and extract all defined terms
16
+ - Scan source files for PascalCase identifiers not present in the glossary
17
+ - Flag any bounded context boundary violations (cross-context direct imports)
18
+ 3. Run quality gate: `draht quality-gate --strict`
19
+ 4. Run `draht extract-deliverables N` to get testable items
20
+ 5. Walk user through each deliverable one at a time
21
+ 6. Record results (pass/fail/partially/skip)
22
+ 7. For failures: diagnose and create fix plans via `draht create-fix-plan N P`
23
+ - Fix plans MUST include a reproducing test that demonstrates the failure before any implementation
24
+ 8. Write UAT report: `draht write-uat N`
25
+ - Report must include: test health summary (pass/fail/coverage), domain model status (any glossary violations), deliverable results
26
+ 9. If all passed: mark phase complete
27
+ 10. If failures: route to `execute-phase N --gaps-only`
@@ -0,0 +1,21 @@
1
+ export interface ChangelogEntry {
2
+ major: number;
3
+ minor: number;
4
+ patch: number;
5
+ content: string;
6
+ }
7
+ /**
8
+ * Parse changelog entries from CHANGELOG.md
9
+ * Scans for ## lines and collects content until next ## or EOF
10
+ */
11
+ export declare function parseChangelog(changelogPath: string): ChangelogEntry[];
12
+ /**
13
+ * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
14
+ */
15
+ export declare function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number;
16
+ /**
17
+ * Get entries newer than lastVersion
18
+ */
19
+ export declare function getNewEntries(entries: ChangelogEntry[], lastVersion: string): ChangelogEntry[];
20
+ export { getChangelogPath } from "../config.js";
21
+ //# sourceMappingURL=changelog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/utils/changelog.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,EAAE,CAyDtE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,MAAM,CAI9E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAW9F;AAGD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import { existsSync, readFileSync } from \"fs\";\n\nexport interface ChangelogEntry {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\tcontent: string;\n}\n\n/**\n * Parse changelog entries from CHANGELOG.md\n * Scans for ## lines and collects content until next ## or EOF\n */\nexport function parseChangelog(changelogPath: string): ChangelogEntry[] {\n\tif (!existsSync(changelogPath)) {\n\t\treturn [];\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(changelogPath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tconst entries: ChangelogEntry[] = [];\n\n\t\tlet currentLines: string[] = [];\n\t\tlet currentVersion: { major: number; minor: number; patch: number } | null = null;\n\n\t\tfor (const line of lines) {\n\t\t\t// Check if this is a version header (## [x.y.z] ...)\n\t\t\tif (line.startsWith(\"## \")) {\n\t\t\t\t// Save previous entry if exists\n\t\t\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\t\t\tentries.push({\n\t\t\t\t\t\t...currentVersion,\n\t\t\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Try to parse version from this line\n\t\t\t\tconst versionMatch = line.match(/##\\s+\\[?(\\d+)\\.(\\d+)\\.(\\d+)\\]?/);\n\t\t\t\tif (versionMatch) {\n\t\t\t\t\tcurrentVersion = {\n\t\t\t\t\t\tmajor: Number.parseInt(versionMatch[1], 10),\n\t\t\t\t\t\tminor: Number.parseInt(versionMatch[2], 10),\n\t\t\t\t\t\tpatch: Number.parseInt(versionMatch[3], 10),\n\t\t\t\t\t};\n\t\t\t\t\tcurrentLines = [line];\n\t\t\t\t} else {\n\t\t\t\t\t// Reset if we can't parse version\n\t\t\t\t\tcurrentVersion = null;\n\t\t\t\t\tcurrentLines = [];\n\t\t\t\t}\n\t\t\t} else if (currentVersion) {\n\t\t\t\t// Collect lines for current version\n\t\t\t\tcurrentLines.push(line);\n\t\t\t}\n\t\t}\n\n\t\t// Save last entry\n\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\tentries.push({\n\t\t\t\t...currentVersion,\n\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t});\n\t\t}\n\n\t\treturn entries;\n\t} catch (error) {\n\t\tconsole.error(`Warning: Could not parse changelog: ${error}`);\n\t\treturn [];\n\t}\n}\n\n/**\n * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2\n */\nexport function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number {\n\tif (v1.major !== v2.major) return v1.major - v2.major;\n\tif (v1.minor !== v2.minor) return v1.minor - v2.minor;\n\treturn v1.patch - v2.patch;\n}\n\n/**\n * Get entries newer than lastVersion\n */\nexport function getNewEntries(entries: ChangelogEntry[], lastVersion: string): ChangelogEntry[] {\n\t// Parse lastVersion\n\tconst parts = lastVersion.split(\".\").map(Number);\n\tconst last: ChangelogEntry = {\n\t\tmajor: parts[0] || 0,\n\t\tminor: parts[1] || 0,\n\t\tpatch: parts[2] || 0,\n\t\tcontent: \"\",\n\t};\n\n\treturn entries.filter((entry) => compareVersions(entry, last) > 0);\n}\n\n// Re-export getChangelogPath from paths.ts for convenience\nexport { getChangelogPath } from \"../config.js\";\n"]}
@@ -0,0 +1,87 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ /**
3
+ * Parse changelog entries from CHANGELOG.md
4
+ * Scans for ## lines and collects content until next ## or EOF
5
+ */
6
+ export function parseChangelog(changelogPath) {
7
+ if (!existsSync(changelogPath)) {
8
+ return [];
9
+ }
10
+ try {
11
+ const content = readFileSync(changelogPath, "utf-8");
12
+ const lines = content.split("\n");
13
+ const entries = [];
14
+ let currentLines = [];
15
+ let currentVersion = null;
16
+ for (const line of lines) {
17
+ // Check if this is a version header (## [x.y.z] ...)
18
+ if (line.startsWith("## ")) {
19
+ // Save previous entry if exists
20
+ if (currentVersion && currentLines.length > 0) {
21
+ entries.push({
22
+ ...currentVersion,
23
+ content: currentLines.join("\n").trim(),
24
+ });
25
+ }
26
+ // Try to parse version from this line
27
+ const versionMatch = line.match(/##\s+\[?(\d+)\.(\d+)\.(\d+)\]?/);
28
+ if (versionMatch) {
29
+ currentVersion = {
30
+ major: Number.parseInt(versionMatch[1], 10),
31
+ minor: Number.parseInt(versionMatch[2], 10),
32
+ patch: Number.parseInt(versionMatch[3], 10),
33
+ };
34
+ currentLines = [line];
35
+ }
36
+ else {
37
+ // Reset if we can't parse version
38
+ currentVersion = null;
39
+ currentLines = [];
40
+ }
41
+ }
42
+ else if (currentVersion) {
43
+ // Collect lines for current version
44
+ currentLines.push(line);
45
+ }
46
+ }
47
+ // Save last entry
48
+ if (currentVersion && currentLines.length > 0) {
49
+ entries.push({
50
+ ...currentVersion,
51
+ content: currentLines.join("\n").trim(),
52
+ });
53
+ }
54
+ return entries;
55
+ }
56
+ catch (error) {
57
+ console.error(`Warning: Could not parse changelog: ${error}`);
58
+ return [];
59
+ }
60
+ }
61
+ /**
62
+ * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
63
+ */
64
+ export function compareVersions(v1, v2) {
65
+ if (v1.major !== v2.major)
66
+ return v1.major - v2.major;
67
+ if (v1.minor !== v2.minor)
68
+ return v1.minor - v2.minor;
69
+ return v1.patch - v2.patch;
70
+ }
71
+ /**
72
+ * Get entries newer than lastVersion
73
+ */
74
+ export function getNewEntries(entries, lastVersion) {
75
+ // Parse lastVersion
76
+ const parts = lastVersion.split(".").map(Number);
77
+ const last = {
78
+ major: parts[0] || 0,
79
+ minor: parts[1] || 0,
80
+ patch: parts[2] || 0,
81
+ content: "",
82
+ };
83
+ return entries.filter((entry) => compareVersions(entry, last) > 0);
84
+ }
85
+ // Re-export getChangelogPath from paths.ts for convenience
86
+ export { getChangelogPath } from "../config.js";
87
+ //# sourceMappingURL=changelog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/utils/changelog.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"fs\";\n\nexport interface ChangelogEntry {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\tcontent: string;\n}\n\n/**\n * Parse changelog entries from CHANGELOG.md\n * Scans for ## lines and collects content until next ## or EOF\n */\nexport function parseChangelog(changelogPath: string): ChangelogEntry[] {\n\tif (!existsSync(changelogPath)) {\n\t\treturn [];\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(changelogPath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tconst entries: ChangelogEntry[] = [];\n\n\t\tlet currentLines: string[] = [];\n\t\tlet currentVersion: { major: number; minor: number; patch: number } | null = null;\n\n\t\tfor (const line of lines) {\n\t\t\t// Check if this is a version header (## [x.y.z] ...)\n\t\t\tif (line.startsWith(\"## \")) {\n\t\t\t\t// Save previous entry if exists\n\t\t\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\t\t\tentries.push({\n\t\t\t\t\t\t...currentVersion,\n\t\t\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Try to parse version from this line\n\t\t\t\tconst versionMatch = line.match(/##\\s+\\[?(\\d+)\\.(\\d+)\\.(\\d+)\\]?/);\n\t\t\t\tif (versionMatch) {\n\t\t\t\t\tcurrentVersion = {\n\t\t\t\t\t\tmajor: Number.parseInt(versionMatch[1], 10),\n\t\t\t\t\t\tminor: Number.parseInt(versionMatch[2], 10),\n\t\t\t\t\t\tpatch: Number.parseInt(versionMatch[3], 10),\n\t\t\t\t\t};\n\t\t\t\t\tcurrentLines = [line];\n\t\t\t\t} else {\n\t\t\t\t\t// Reset if we can't parse version\n\t\t\t\t\tcurrentVersion = null;\n\t\t\t\t\tcurrentLines = [];\n\t\t\t\t}\n\t\t\t} else if (currentVersion) {\n\t\t\t\t// Collect lines for current version\n\t\t\t\tcurrentLines.push(line);\n\t\t\t}\n\t\t}\n\n\t\t// Save last entry\n\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\tentries.push({\n\t\t\t\t...currentVersion,\n\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t});\n\t\t}\n\n\t\treturn entries;\n\t} catch (error) {\n\t\tconsole.error(`Warning: Could not parse changelog: ${error}`);\n\t\treturn [];\n\t}\n}\n\n/**\n * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2\n */\nexport function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number {\n\tif (v1.major !== v2.major) return v1.major - v2.major;\n\tif (v1.minor !== v2.minor) return v1.minor - v2.minor;\n\treturn v1.patch - v2.patch;\n}\n\n/**\n * Get entries newer than lastVersion\n */\nexport function getNewEntries(entries: ChangelogEntry[], lastVersion: string): ChangelogEntry[] {\n\t// Parse lastVersion\n\tconst parts = lastVersion.split(\".\").map(Number);\n\tconst last: ChangelogEntry = {\n\t\tmajor: parts[0] || 0,\n\t\tminor: parts[1] || 0,\n\t\tpatch: parts[2] || 0,\n\t\tcontent: \"\",\n\t};\n\n\treturn entries.filter((entry) => compareVersions(entry, last) > 0);\n}\n\n// Re-export getChangelogPath from paths.ts for convenience\nexport { getChangelogPath } from \"../config.js\";\n"]}
@@ -0,0 +1,11 @@
1
+ export type ClipboardImage = {
2
+ bytes: Uint8Array;
3
+ mimeType: string;
4
+ };
5
+ export declare function isWaylandSession(env?: NodeJS.ProcessEnv): boolean;
6
+ export declare function extensionForImageMimeType(mimeType: string): string | null;
7
+ export declare function readClipboardImage(options?: {
8
+ env?: NodeJS.ProcessEnv;
9
+ platform?: NodeJS.Platform;
10
+ }): Promise<ClipboardImage | null>;
11
+ //# sourceMappingURL=clipboard-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-image.d.ts","sourceRoot":"","sources":["../../src/utils/clipboard-image.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAQF,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE9E;AAMD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAazE;AA8HD,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAwCjC","sourcesContent":["import { spawnSync } from \"child_process\";\n\nimport { clipboard } from \"./clipboard-native.js\";\nimport { loadPhoton } from \"./photon.js\";\n\nexport type ClipboardImage = {\n\tbytes: Uint8Array;\n\tmimeType: string;\n};\n\nconst SUPPORTED_IMAGE_MIME_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"] as const;\n\nconst DEFAULT_LIST_TIMEOUT_MS = 1000;\nconst DEFAULT_READ_TIMEOUT_MS = 3000;\nconst DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;\n\nexport function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === \"wayland\";\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nexport function extensionForImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"png\";\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction selectPreferredImageMimeType(mimeTypes: string[]): string | null {\n\tconst normalized = mimeTypes\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean)\n\t\t.map((t) => ({ raw: t, base: baseMimeType(t) }));\n\n\tfor (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {\n\t\tconst match = normalized.find((t) => t.base === preferred);\n\t\tif (match) {\n\t\t\treturn match.raw;\n\t\t}\n\t}\n\n\tconst anyImage = normalized.find((t) => t.base.startsWith(\"image/\"));\n\treturn anyImage?.raw ?? null;\n}\n\nfunction isSupportedImageMimeType(mimeType: string): boolean {\n\tconst base = baseMimeType(mimeType);\n\treturn SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);\n}\n\n/**\n * Convert unsupported image formats to PNG using Photon.\n * Returns null if conversion is unavailable or fails.\n */\nasync function convertToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst image = photon.PhotonImage.new_from_byteslice(bytes);\n\t\ttry {\n\t\t\treturn image.get_bytes();\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction runCommand(\n\tcommand: string,\n\targs: string[],\n\toptions?: { timeoutMs?: number; maxBufferBytes?: number },\n): { stdout: Buffer; ok: boolean } {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;\n\tconst maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;\n\n\tconst result = spawnSync(command, args, {\n\t\ttimeout: timeoutMs,\n\t\tmaxBuffer: maxBufferBytes,\n\t});\n\n\tif (result.error) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (result.status !== 0) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tconst stdout = Buffer.isBuffer(result.stdout)\n\t\t? result.stdout\n\t\t: Buffer.from(result.stdout ?? \"\", typeof result.stdout === \"string\" ? \"utf-8\" : undefined);\n\n\treturn { ok: true, stdout };\n}\n\nfunction readClipboardImageViaWlPaste(): ClipboardImage | null {\n\tconst list = runCommand(\"wl-paste\", [\"--list-types\"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\tif (!list.ok) {\n\t\treturn null;\n\t}\n\n\tconst types = list.stdout\n\t\t.toString(\"utf-8\")\n\t\t.split(/\\r?\\n/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\n\tconst selectedType = selectPreferredImageMimeType(types);\n\tif (!selectedType) {\n\t\treturn null;\n\t}\n\n\tconst data = runCommand(\"wl-paste\", [\"--type\", selectedType, \"--no-newline\"]);\n\tif (!data.ok || data.stdout.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn { bytes: data.stdout, mimeType: baseMimeType(selectedType) };\n}\n\nfunction readClipboardImageViaXclip(): ClipboardImage | null {\n\tconst targets = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"], {\n\t\ttimeoutMs: DEFAULT_LIST_TIMEOUT_MS,\n\t});\n\n\tlet candidateTypes: string[] = [];\n\tif (targets.ok) {\n\t\tcandidateTypes = targets.stdout\n\t\t\t.toString(\"utf-8\")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tconst preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;\n\tconst tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];\n\n\tfor (const mimeType of tryTypes) {\n\t\tconst data = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", mimeType, \"-o\"]);\n\t\tif (data.ok && data.stdout.length > 0) {\n\t\t\treturn { bytes: data.stdout, mimeType: baseMimeType(mimeType) };\n\t\t}\n\t}\n\n\treturn null;\n}\n\nexport async function readClipboardImage(options?: {\n\tenv?: NodeJS.ProcessEnv;\n\tplatform?: NodeJS.Platform;\n}): Promise<ClipboardImage | null> {\n\tconst env = options?.env ?? process.env;\n\tconst platform = options?.platform ?? process.platform;\n\n\tif (env.TERMUX_VERSION) {\n\t\treturn null;\n\t}\n\n\tlet image: ClipboardImage | null = null;\n\n\tif (platform === \"linux\" && isWaylandSession(env)) {\n\t\timage = readClipboardImageViaWlPaste() ?? readClipboardImageViaXclip();\n\t} else {\n\t\tif (!clipboard || !clipboard.hasImage()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst imageData = await clipboard.getImageBinary();\n\t\tif (!imageData || imageData.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);\n\t\timage = { bytes, mimeType: \"image/png\" };\n\t}\n\n\tif (!image) {\n\t\treturn null;\n\t}\n\n\t// Convert unsupported formats (e.g., BMP from WSLg) to PNG\n\tif (!isSupportedImageMimeType(image.mimeType)) {\n\t\tconst pngBytes = await convertToPng(image.bytes);\n\t\tif (!pngBytes) {\n\t\t\treturn null;\n\t\t}\n\t\treturn { bytes: pngBytes, mimeType: \"image/png\" };\n\t}\n\n\treturn image;\n}\n"]}
@@ -0,0 +1,162 @@
1
+ import { spawnSync } from "child_process";
2
+ import { clipboard } from "./clipboard-native.js";
3
+ import { loadPhoton } from "./photon.js";
4
+ const SUPPORTED_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
5
+ const DEFAULT_LIST_TIMEOUT_MS = 1000;
6
+ const DEFAULT_READ_TIMEOUT_MS = 3000;
7
+ const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
8
+ export function isWaylandSession(env = process.env) {
9
+ return Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === "wayland";
10
+ }
11
+ function baseMimeType(mimeType) {
12
+ return mimeType.split(";")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();
13
+ }
14
+ export function extensionForImageMimeType(mimeType) {
15
+ switch (baseMimeType(mimeType)) {
16
+ case "image/png":
17
+ return "png";
18
+ case "image/jpeg":
19
+ return "jpg";
20
+ case "image/webp":
21
+ return "webp";
22
+ case "image/gif":
23
+ return "gif";
24
+ default:
25
+ return null;
26
+ }
27
+ }
28
+ function selectPreferredImageMimeType(mimeTypes) {
29
+ const normalized = mimeTypes
30
+ .map((t) => t.trim())
31
+ .filter(Boolean)
32
+ .map((t) => ({ raw: t, base: baseMimeType(t) }));
33
+ for (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {
34
+ const match = normalized.find((t) => t.base === preferred);
35
+ if (match) {
36
+ return match.raw;
37
+ }
38
+ }
39
+ const anyImage = normalized.find((t) => t.base.startsWith("image/"));
40
+ return anyImage?.raw ?? null;
41
+ }
42
+ function isSupportedImageMimeType(mimeType) {
43
+ const base = baseMimeType(mimeType);
44
+ return SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);
45
+ }
46
+ /**
47
+ * Convert unsupported image formats to PNG using Photon.
48
+ * Returns null if conversion is unavailable or fails.
49
+ */
50
+ async function convertToPng(bytes) {
51
+ const photon = await loadPhoton();
52
+ if (!photon) {
53
+ return null;
54
+ }
55
+ try {
56
+ const image = photon.PhotonImage.new_from_byteslice(bytes);
57
+ try {
58
+ return image.get_bytes();
59
+ }
60
+ finally {
61
+ image.free();
62
+ }
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ function runCommand(command, args, options) {
69
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;
70
+ const maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;
71
+ const result = spawnSync(command, args, {
72
+ timeout: timeoutMs,
73
+ maxBuffer: maxBufferBytes,
74
+ });
75
+ if (result.error) {
76
+ return { ok: false, stdout: Buffer.alloc(0) };
77
+ }
78
+ if (result.status !== 0) {
79
+ return { ok: false, stdout: Buffer.alloc(0) };
80
+ }
81
+ const stdout = Buffer.isBuffer(result.stdout)
82
+ ? result.stdout
83
+ : Buffer.from(result.stdout ?? "", typeof result.stdout === "string" ? "utf-8" : undefined);
84
+ return { ok: true, stdout };
85
+ }
86
+ function readClipboardImageViaWlPaste() {
87
+ const list = runCommand("wl-paste", ["--list-types"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
88
+ if (!list.ok) {
89
+ return null;
90
+ }
91
+ const types = list.stdout
92
+ .toString("utf-8")
93
+ .split(/\r?\n/)
94
+ .map((t) => t.trim())
95
+ .filter(Boolean);
96
+ const selectedType = selectPreferredImageMimeType(types);
97
+ if (!selectedType) {
98
+ return null;
99
+ }
100
+ const data = runCommand("wl-paste", ["--type", selectedType, "--no-newline"]);
101
+ if (!data.ok || data.stdout.length === 0) {
102
+ return null;
103
+ }
104
+ return { bytes: data.stdout, mimeType: baseMimeType(selectedType) };
105
+ }
106
+ function readClipboardImageViaXclip() {
107
+ const targets = runCommand("xclip", ["-selection", "clipboard", "-t", "TARGETS", "-o"], {
108
+ timeoutMs: DEFAULT_LIST_TIMEOUT_MS,
109
+ });
110
+ let candidateTypes = [];
111
+ if (targets.ok) {
112
+ candidateTypes = targets.stdout
113
+ .toString("utf-8")
114
+ .split(/\r?\n/)
115
+ .map((t) => t.trim())
116
+ .filter(Boolean);
117
+ }
118
+ const preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;
119
+ const tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];
120
+ for (const mimeType of tryTypes) {
121
+ const data = runCommand("xclip", ["-selection", "clipboard", "-t", mimeType, "-o"]);
122
+ if (data.ok && data.stdout.length > 0) {
123
+ return { bytes: data.stdout, mimeType: baseMimeType(mimeType) };
124
+ }
125
+ }
126
+ return null;
127
+ }
128
+ export async function readClipboardImage(options) {
129
+ const env = options?.env ?? process.env;
130
+ const platform = options?.platform ?? process.platform;
131
+ if (env.TERMUX_VERSION) {
132
+ return null;
133
+ }
134
+ let image = null;
135
+ if (platform === "linux" && isWaylandSession(env)) {
136
+ image = readClipboardImageViaWlPaste() ?? readClipboardImageViaXclip();
137
+ }
138
+ else {
139
+ if (!clipboard || !clipboard.hasImage()) {
140
+ return null;
141
+ }
142
+ const imageData = await clipboard.getImageBinary();
143
+ if (!imageData || imageData.length === 0) {
144
+ return null;
145
+ }
146
+ const bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);
147
+ image = { bytes, mimeType: "image/png" };
148
+ }
149
+ if (!image) {
150
+ return null;
151
+ }
152
+ // Convert unsupported formats (e.g., BMP from WSLg) to PNG
153
+ if (!isSupportedImageMimeType(image.mimeType)) {
154
+ const pngBytes = await convertToPng(image.bytes);
155
+ if (!pngBytes) {
156
+ return null;
157
+ }
158
+ return { bytes: pngBytes, mimeType: "image/png" };
159
+ }
160
+ return image;
161
+ }
162
+ //# sourceMappingURL=clipboard-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-image.js","sourceRoot":"","sources":["../../src/utils/clipboard-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"child_process\";\n\nimport { clipboard } from \"./clipboard-native.js\";\nimport { loadPhoton } from \"./photon.js\";\n\nexport type ClipboardImage = {\n\tbytes: Uint8Array;\n\tmimeType: string;\n};\n\nconst SUPPORTED_IMAGE_MIME_TYPES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"] as const;\n\nconst DEFAULT_LIST_TIMEOUT_MS = 1000;\nconst DEFAULT_READ_TIMEOUT_MS = 3000;\nconst DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;\n\nexport function isWaylandSession(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn Boolean(env.WAYLAND_DISPLAY) || env.XDG_SESSION_TYPE === \"wayland\";\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nexport function extensionForImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"png\";\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction selectPreferredImageMimeType(mimeTypes: string[]): string | null {\n\tconst normalized = mimeTypes\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean)\n\t\t.map((t) => ({ raw: t, base: baseMimeType(t) }));\n\n\tfor (const preferred of SUPPORTED_IMAGE_MIME_TYPES) {\n\t\tconst match = normalized.find((t) => t.base === preferred);\n\t\tif (match) {\n\t\t\treturn match.raw;\n\t\t}\n\t}\n\n\tconst anyImage = normalized.find((t) => t.base.startsWith(\"image/\"));\n\treturn anyImage?.raw ?? null;\n}\n\nfunction isSupportedImageMimeType(mimeType: string): boolean {\n\tconst base = baseMimeType(mimeType);\n\treturn SUPPORTED_IMAGE_MIME_TYPES.some((t) => t === base);\n}\n\n/**\n * Convert unsupported image formats to PNG using Photon.\n * Returns null if conversion is unavailable or fails.\n */\nasync function convertToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst image = photon.PhotonImage.new_from_byteslice(bytes);\n\t\ttry {\n\t\t\treturn image.get_bytes();\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction runCommand(\n\tcommand: string,\n\targs: string[],\n\toptions?: { timeoutMs?: number; maxBufferBytes?: number },\n): { stdout: Buffer; ok: boolean } {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS;\n\tconst maxBufferBytes = options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;\n\n\tconst result = spawnSync(command, args, {\n\t\ttimeout: timeoutMs,\n\t\tmaxBuffer: maxBufferBytes,\n\t});\n\n\tif (result.error) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tif (result.status !== 0) {\n\t\treturn { ok: false, stdout: Buffer.alloc(0) };\n\t}\n\n\tconst stdout = Buffer.isBuffer(result.stdout)\n\t\t? result.stdout\n\t\t: Buffer.from(result.stdout ?? \"\", typeof result.stdout === \"string\" ? \"utf-8\" : undefined);\n\n\treturn { ok: true, stdout };\n}\n\nfunction readClipboardImageViaWlPaste(): ClipboardImage | null {\n\tconst list = runCommand(\"wl-paste\", [\"--list-types\"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });\n\tif (!list.ok) {\n\t\treturn null;\n\t}\n\n\tconst types = list.stdout\n\t\t.toString(\"utf-8\")\n\t\t.split(/\\r?\\n/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\n\tconst selectedType = selectPreferredImageMimeType(types);\n\tif (!selectedType) {\n\t\treturn null;\n\t}\n\n\tconst data = runCommand(\"wl-paste\", [\"--type\", selectedType, \"--no-newline\"]);\n\tif (!data.ok || data.stdout.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn { bytes: data.stdout, mimeType: baseMimeType(selectedType) };\n}\n\nfunction readClipboardImageViaXclip(): ClipboardImage | null {\n\tconst targets = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", \"TARGETS\", \"-o\"], {\n\t\ttimeoutMs: DEFAULT_LIST_TIMEOUT_MS,\n\t});\n\n\tlet candidateTypes: string[] = [];\n\tif (targets.ok) {\n\t\tcandidateTypes = targets.stdout\n\t\t\t.toString(\"utf-8\")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean);\n\t}\n\n\tconst preferred = candidateTypes.length > 0 ? selectPreferredImageMimeType(candidateTypes) : null;\n\tconst tryTypes = preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];\n\n\tfor (const mimeType of tryTypes) {\n\t\tconst data = runCommand(\"xclip\", [\"-selection\", \"clipboard\", \"-t\", mimeType, \"-o\"]);\n\t\tif (data.ok && data.stdout.length > 0) {\n\t\t\treturn { bytes: data.stdout, mimeType: baseMimeType(mimeType) };\n\t\t}\n\t}\n\n\treturn null;\n}\n\nexport async function readClipboardImage(options?: {\n\tenv?: NodeJS.ProcessEnv;\n\tplatform?: NodeJS.Platform;\n}): Promise<ClipboardImage | null> {\n\tconst env = options?.env ?? process.env;\n\tconst platform = options?.platform ?? process.platform;\n\n\tif (env.TERMUX_VERSION) {\n\t\treturn null;\n\t}\n\n\tlet image: ClipboardImage | null = null;\n\n\tif (platform === \"linux\" && isWaylandSession(env)) {\n\t\timage = readClipboardImageViaWlPaste() ?? readClipboardImageViaXclip();\n\t} else {\n\t\tif (!clipboard || !clipboard.hasImage()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst imageData = await clipboard.getImageBinary();\n\t\tif (!imageData || imageData.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst bytes = imageData instanceof Uint8Array ? imageData : Uint8Array.from(imageData);\n\t\timage = { bytes, mimeType: \"image/png\" };\n\t}\n\n\tif (!image) {\n\t\treturn null;\n\t}\n\n\t// Convert unsupported formats (e.g., BMP from WSLg) to PNG\n\tif (!isSupportedImageMimeType(image.mimeType)) {\n\t\tconst pngBytes = await convertToPng(image.bytes);\n\t\tif (!pngBytes) {\n\t\t\treturn null;\n\t\t}\n\t\treturn { bytes: pngBytes, mimeType: \"image/png\" };\n\t}\n\n\treturn image;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export type ClipboardModule = {
2
+ hasImage: () => boolean;
3
+ getImageBinary: () => Promise<Array<number>>;
4
+ };
5
+ declare let clipboard: ClipboardModule | null;
6
+ export { clipboard };
7
+ //# sourceMappingURL=clipboard-native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-native.d.ts","sourceRoot":"","sources":["../../src/utils/clipboard-native.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GAAG;IAC7B,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,cAAc,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;CAC7C,CAAC;AAGF,QAAA,IAAI,SAAS,EAAE,eAAe,GAAG,IAAW,CAAC;AAY7C,OAAO,EAAE,SAAS,EAAE,CAAC","sourcesContent":["import { createRequire } from \"module\";\n\nexport type ClipboardModule = {\n\thasImage: () => boolean;\n\tgetImageBinary: () => Promise<Array<number>>;\n};\n\nconst require = createRequire(import.meta.url);\nlet clipboard: ClipboardModule | null = null;\n\nconst hasDisplay = process.platform !== \"linux\" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);\n\nif (!process.env.TERMUX_VERSION && hasDisplay) {\n\ttry {\n\t\tclipboard = require(\"@mariozechner/clipboard\") as ClipboardModule;\n\t} catch {\n\t\tclipboard = null;\n\t}\n}\n\nexport { clipboard };\n"]}