@draht/coding-agent 2026.3.11-1 → 2026.3.25-1

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 (380) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +45 -30
  3. package/bin/draht-tools.cjs +187 -32
  4. package/dist/bun/cli.d.ts +3 -0
  5. package/dist/bun/cli.d.ts.map +1 -0
  6. package/dist/bun/cli.js +7 -0
  7. package/dist/bun/cli.js.map +1 -0
  8. package/dist/bun/register-bedrock.d.ts +2 -0
  9. package/dist/bun/register-bedrock.d.ts.map +1 -0
  10. package/dist/bun/register-bedrock.js +4 -0
  11. package/dist/bun/register-bedrock.js.map +1 -0
  12. package/dist/cli/args.d.ts +1 -0
  13. package/dist/cli/args.d.ts.map +1 -1
  14. package/dist/cli/args.js +11 -6
  15. package/dist/cli/args.js.map +1 -1
  16. package/dist/cli/file-processor.d.ts.map +1 -1
  17. package/dist/cli/file-processor.js +4 -0
  18. package/dist/cli/file-processor.js.map +1 -1
  19. package/dist/cli/initial-message.d.ts +18 -0
  20. package/dist/cli/initial-message.d.ts.map +1 -0
  21. package/dist/cli/initial-message.js +22 -0
  22. package/dist/cli/initial-message.js.map +1 -0
  23. package/dist/cli/session-picker.d.ts.map +1 -1
  24. package/dist/cli/session-picker.js +2 -1
  25. package/dist/cli/session-picker.js.map +1 -1
  26. package/dist/cli.d.ts.map +1 -1
  27. package/dist/cli.js +1 -3
  28. package/dist/cli.js.map +1 -1
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +2 -2
  31. package/dist/config.js.map +1 -1
  32. package/dist/core/agent-session.d.ts +38 -5
  33. package/dist/core/agent-session.d.ts.map +1 -1
  34. package/dist/core/agent-session.js +201 -73
  35. package/dist/core/agent-session.js.map +1 -1
  36. package/dist/core/bash-executor.d.ts +6 -7
  37. package/dist/core/bash-executor.d.ts.map +1 -1
  38. package/dist/core/bash-executor.js +8 -107
  39. package/dist/core/bash-executor.js.map +1 -1
  40. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  41. package/dist/core/compaction/branch-summarization.js +1 -0
  42. package/dist/core/compaction/branch-summarization.js.map +1 -1
  43. package/dist/core/compaction/compaction.d.ts.map +1 -1
  44. package/dist/core/compaction/compaction.js +2 -0
  45. package/dist/core/compaction/compaction.js.map +1 -1
  46. package/dist/core/exec.d.ts.map +1 -1
  47. package/dist/core/exec.js +7 -3
  48. package/dist/core/exec.js.map +1 -1
  49. package/dist/core/export-html/index.d.ts +2 -2
  50. package/dist/core/export-html/index.d.ts.map +1 -1
  51. package/dist/core/export-html/index.js +7 -6
  52. package/dist/core/export-html/index.js.map +1 -1
  53. package/dist/core/export-html/template.css +43 -13
  54. package/dist/core/export-html/template.html +1 -0
  55. package/dist/core/export-html/template.js +107 -0
  56. package/dist/core/export-html/tool-renderer.d.ts +2 -2
  57. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  58. package/dist/core/export-html/tool-renderer.js +41 -16
  59. package/dist/core/export-html/tool-renderer.js.map +1 -1
  60. package/dist/core/extensions/index.d.ts +4 -3
  61. package/dist/core/extensions/index.d.ts.map +1 -1
  62. package/dist/core/extensions/index.js +1 -1
  63. package/dist/core/extensions/index.js.map +1 -1
  64. package/dist/core/extensions/loader.d.ts.map +1 -1
  65. package/dist/core/extensions/loader.js +16 -6
  66. package/dist/core/extensions/loader.js.map +1 -1
  67. package/dist/core/extensions/runner.d.ts +9 -9
  68. package/dist/core/extensions/runner.d.ts.map +1 -1
  69. package/dist/core/extensions/runner.js +89 -71
  70. package/dist/core/extensions/runner.js.map +1 -1
  71. package/dist/core/extensions/types.d.ts +49 -13
  72. package/dist/core/extensions/types.d.ts.map +1 -1
  73. package/dist/core/extensions/types.js.map +1 -1
  74. package/dist/core/extensions/wrapper.d.ts +4 -11
  75. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  76. package/dist/core/extensions/wrapper.js +6 -86
  77. package/dist/core/extensions/wrapper.js.map +1 -1
  78. package/dist/core/footer-data-provider.d.ts +13 -1
  79. package/dist/core/footer-data-provider.d.ts.map +1 -1
  80. package/dist/core/footer-data-provider.js +155 -37
  81. package/dist/core/footer-data-provider.js.map +1 -1
  82. package/dist/core/index.d.ts +2 -1
  83. package/dist/core/index.d.ts.map +1 -1
  84. package/dist/core/index.js +2 -1
  85. package/dist/core/index.js.map +1 -1
  86. package/dist/core/keybindings.d.ts +270 -50
  87. package/dist/core/keybindings.d.ts.map +1 -1
  88. package/dist/core/keybindings.js +222 -134
  89. package/dist/core/keybindings.js.map +1 -1
  90. package/dist/core/model-registry.d.ts +1 -0
  91. package/dist/core/model-registry.d.ts.map +1 -1
  92. package/dist/core/model-registry.js +49 -23
  93. package/dist/core/model-registry.js.map +1 -1
  94. package/dist/core/model-resolver.d.ts +6 -0
  95. package/dist/core/model-resolver.d.ts.map +1 -1
  96. package/dist/core/model-resolver.js +41 -17
  97. package/dist/core/model-resolver.js.map +1 -1
  98. package/dist/core/output-guard.d.ts +6 -0
  99. package/dist/core/output-guard.d.ts.map +1 -0
  100. package/dist/core/output-guard.js +59 -0
  101. package/dist/core/output-guard.js.map +1 -0
  102. package/dist/core/package-manager.d.ts +22 -1
  103. package/dist/core/package-manager.d.ts.map +1 -1
  104. package/dist/core/package-manager.js +374 -54
  105. package/dist/core/package-manager.js.map +1 -1
  106. package/dist/core/prompt-templates.d.ts +2 -1
  107. package/dist/core/prompt-templates.d.ts.map +1 -1
  108. package/dist/core/prompt-templates.js +39 -39
  109. package/dist/core/prompt-templates.js.map +1 -1
  110. package/dist/core/resolve-config-value.d.ts.map +1 -1
  111. package/dist/core/resolve-config-value.js +43 -8
  112. package/dist/core/resolve-config-value.js.map +1 -1
  113. package/dist/core/resource-loader.d.ts +6 -7
  114. package/dist/core/resource-loader.d.ts.map +1 -1
  115. package/dist/core/resource-loader.js +141 -118
  116. package/dist/core/resource-loader.js.map +1 -1
  117. package/dist/core/sdk.d.ts +3 -3
  118. package/dist/core/sdk.d.ts.map +1 -1
  119. package/dist/core/sdk.js +4 -4
  120. package/dist/core/sdk.js.map +1 -1
  121. package/dist/core/session-manager.d.ts +6 -0
  122. package/dist/core/session-manager.d.ts.map +1 -1
  123. package/dist/core/session-manager.js +9 -10
  124. package/dist/core/session-manager.js.map +1 -1
  125. package/dist/core/settings-manager.d.ts +3 -0
  126. package/dist/core/settings-manager.d.ts.map +1 -1
  127. package/dist/core/settings-manager.js +8 -0
  128. package/dist/core/settings-manager.js.map +1 -1
  129. package/dist/core/skills.d.ts +5 -3
  130. package/dist/core/skills.d.ts.map +1 -1
  131. package/dist/core/skills.js +54 -9
  132. package/dist/core/skills.js.map +1 -1
  133. package/dist/core/slash-commands.d.ts +2 -3
  134. package/dist/core/slash-commands.d.ts.map +1 -1
  135. package/dist/core/slash-commands.js +3 -2
  136. package/dist/core/slash-commands.js.map +1 -1
  137. package/dist/core/source-info.d.ts +18 -0
  138. package/dist/core/source-info.d.ts.map +1 -0
  139. package/dist/core/source-info.js +19 -0
  140. package/dist/core/source-info.js.map +1 -0
  141. package/dist/core/system-prompt.d.ts.map +1 -1
  142. package/dist/core/system-prompt.js +17 -60
  143. package/dist/core/system-prompt.js.map +1 -1
  144. package/dist/core/tools/bash.d.ts +24 -6
  145. package/dist/core/tools/bash.d.ts.map +1 -1
  146. package/dist/core/tools/bash.js +210 -110
  147. package/dist/core/tools/bash.js.map +1 -1
  148. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  149. package/dist/core/tools/edit-diff.js +1 -0
  150. package/dist/core/tools/edit-diff.js.map +1 -1
  151. package/dist/core/tools/edit.d.ts +14 -2
  152. package/dist/core/tools/edit.d.ts.map +1 -1
  153. package/dist/core/tools/edit.js +95 -23
  154. package/dist/core/tools/edit.js.map +1 -1
  155. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  156. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  157. package/dist/core/tools/file-mutation-queue.js +37 -0
  158. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  159. package/dist/core/tools/find.d.ts +11 -4
  160. package/dist/core/tools/find.d.ts.map +1 -1
  161. package/dist/core/tools/find.js +82 -30
  162. package/dist/core/tools/find.js.map +1 -1
  163. package/dist/core/tools/grep.d.ts +15 -4
  164. package/dist/core/tools/grep.d.ts.map +1 -1
  165. package/dist/core/tools/grep.js +83 -29
  166. package/dist/core/tools/grep.js.map +1 -1
  167. package/dist/core/tools/index.d.ts +58 -19
  168. package/dist/core/tools/index.d.ts.map +1 -1
  169. package/dist/core/tools/index.js +51 -26
  170. package/dist/core/tools/index.js.map +1 -1
  171. package/dist/core/tools/ls.d.ts +9 -3
  172. package/dist/core/tools/ls.d.ts.map +1 -1
  173. package/dist/core/tools/ls.js +67 -13
  174. package/dist/core/tools/ls.js.map +1 -1
  175. package/dist/core/tools/read.d.ts +10 -3
  176. package/dist/core/tools/read.d.ts.map +1 -1
  177. package/dist/core/tools/read.js +110 -51
  178. package/dist/core/tools/read.js.map +1 -1
  179. package/dist/core/tools/render-utils.d.ts +21 -0
  180. package/dist/core/tools/render-utils.d.ts.map +1 -0
  181. package/dist/core/tools/render-utils.js +49 -0
  182. package/dist/core/tools/render-utils.js.map +1 -0
  183. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  184. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  185. package/dist/core/tools/tool-definition-wrapper.js +30 -0
  186. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  187. package/dist/core/tools/write.d.ts +9 -3
  188. package/dist/core/tools/write.d.ts.map +1 -1
  189. package/dist/core/tools/write.js +168 -30
  190. package/dist/core/tools/write.js.map +1 -1
  191. package/dist/gsd/domain.d.ts +5 -1
  192. package/dist/gsd/domain.d.ts.map +1 -1
  193. package/dist/gsd/domain.js +71 -1
  194. package/dist/gsd/domain.js.map +1 -1
  195. package/dist/gsd/git.d.ts.map +1 -1
  196. package/dist/gsd/git.js +18 -0
  197. package/dist/gsd/git.js.map +1 -1
  198. package/dist/gsd/index.d.ts +1 -0
  199. package/dist/gsd/index.d.ts.map +1 -1
  200. package/dist/gsd/index.js.map +1 -1
  201. package/dist/index.d.ts +5 -4
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +4 -3
  204. package/dist/index.js.map +1 -1
  205. package/dist/main.d.ts.map +1 -1
  206. package/dist/main.js +105 -226
  207. package/dist/main.js.map +1 -1
  208. package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
  209. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  210. package/dist/modes/interactive/components/bash-execution.js +22 -9
  211. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  212. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  213. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  214. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  215. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  216. package/dist/modes/interactive/components/branch-summary-message.js +2 -2
  217. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  218. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  219. package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
  220. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  221. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  222. package/dist/modes/interactive/components/config-selector.js +8 -8
  223. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  224. package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
  225. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  226. package/dist/modes/interactive/components/custom-editor.js +6 -6
  227. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  228. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  229. package/dist/modes/interactive/components/extension-editor.js +9 -9
  230. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  231. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  232. package/dist/modes/interactive/components/extension-input.js +5 -5
  233. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  234. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  235. package/dist/modes/interactive/components/extension-selector.js +8 -8
  236. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  237. package/dist/modes/interactive/components/index.d.ts +1 -1
  238. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  239. package/dist/modes/interactive/components/index.js +1 -1
  240. package/dist/modes/interactive/components/index.js.map +1 -1
  241. package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
  242. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  243. package/dist/modes/interactive/components/keybinding-hints.js +5 -44
  244. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  245. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  246. package/dist/modes/interactive/components/login-dialog.js +6 -6
  247. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  248. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/model-selector.js +13 -9
  250. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  251. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/oauth-selector.js +6 -6
  253. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  254. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  255. package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
  256. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  257. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  258. package/dist/modes/interactive/components/session-selector.js +32 -35
  259. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  260. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  261. package/dist/modes/interactive/components/settings-selector.js +5 -1
  262. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  263. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  264. package/dist/modes/interactive/components/show-images-selector.js +5 -1
  265. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  266. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  267. package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
  268. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  269. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  270. package/dist/modes/interactive/components/theme-selector.js +5 -1
  271. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  272. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  273. package/dist/modes/interactive/components/thinking-selector.js +5 -1
  274. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  275. package/dist/modes/interactive/components/tool-execution.d.ts +16 -34
  276. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/tool-execution.js +128 -636
  278. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  279. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/tree-selector.js +27 -16
  281. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  282. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  283. package/dist/modes/interactive/components/user-message-selector.js +6 -6
  284. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  285. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  286. package/dist/modes/interactive/components/user-message.js +2 -1
  287. package/dist/modes/interactive/components/user-message.js.map +1 -1
  288. package/dist/modes/interactive/interactive-mode.d.ts +7 -11
  289. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  290. package/dist/modes/interactive/interactive-mode.js +353 -212
  291. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  292. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  293. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  294. package/dist/modes/interactive/theme/theme.js +63 -37
  295. package/dist/modes/interactive/theme/theme.js.map +1 -1
  296. package/dist/modes/print-mode.d.ts.map +1 -1
  297. package/dist/modes/print-mode.js +5 -11
  298. package/dist/modes/print-mode.js.map +1 -1
  299. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  300. package/dist/modes/rpc/rpc-mode.js +27 -17
  301. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  302. package/dist/modes/rpc/rpc-types.d.ts +3 -4
  303. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  304. package/dist/modes/rpc/rpc-types.js.map +1 -1
  305. package/dist/prompts/commands/execute-phase.md +2 -2
  306. package/dist/prompts/commands/fix.md +2 -2
  307. package/dist/prompts/commands/plan-phase.md +5 -1
  308. package/dist/prompts/commands/quick.md +5 -1
  309. package/dist/utils/changelog.d.ts +12 -0
  310. package/dist/utils/changelog.d.ts.map +1 -1
  311. package/dist/utils/changelog.js +25 -14
  312. package/dist/utils/changelog.js.map +1 -1
  313. package/dist/utils/child-process.d.ts +11 -0
  314. package/dist/utils/child-process.d.ts.map +1 -0
  315. package/dist/utils/child-process.js +78 -0
  316. package/dist/utils/child-process.js.map +1 -0
  317. package/dist/utils/clipboard-image.d.ts.map +1 -1
  318. package/dist/utils/clipboard-image.js +94 -11
  319. package/dist/utils/clipboard-image.js.map +1 -1
  320. package/dist/utils/clipboard-native.d.ts +1 -0
  321. package/dist/utils/clipboard-native.d.ts.map +1 -1
  322. package/dist/utils/clipboard-native.js.map +1 -1
  323. package/dist/utils/clipboard.d.ts +1 -1
  324. package/dist/utils/clipboard.d.ts.map +1 -1
  325. package/dist/utils/clipboard.js +27 -16
  326. package/dist/utils/clipboard.js.map +1 -1
  327. package/dist/utils/exif-orientation.d.ts +5 -0
  328. package/dist/utils/exif-orientation.d.ts.map +1 -0
  329. package/dist/utils/exif-orientation.js +158 -0
  330. package/dist/utils/exif-orientation.js.map +1 -0
  331. package/dist/utils/image-convert.d.ts.map +1 -1
  332. package/dist/utils/image-convert.js +5 -1
  333. package/dist/utils/image-convert.js.map +1 -1
  334. package/dist/utils/image-resize.d.ts +5 -5
  335. package/dist/utils/image-resize.d.ts.map +1 -1
  336. package/dist/utils/image-resize.js +51 -95
  337. package/dist/utils/image-resize.js.map +1 -1
  338. package/dist/utils/notify.d.ts +12 -0
  339. package/dist/utils/notify.d.ts.map +1 -0
  340. package/dist/utils/notify.js +41 -0
  341. package/dist/utils/notify.js.map +1 -0
  342. package/dist/utils/tools-manager.d.ts.map +1 -1
  343. package/dist/utils/tools-manager.js +5 -4
  344. package/dist/utils/tools-manager.js.map +1 -1
  345. package/docs/custom-provider.md +6 -2
  346. package/docs/extensions.md +108 -21
  347. package/docs/keybindings.md +103 -112
  348. package/docs/models.md +39 -1
  349. package/docs/packages.md +9 -0
  350. package/docs/providers.md +7 -0
  351. package/docs/rpc.md +15 -6
  352. package/docs/sdk.md +2 -2
  353. package/docs/settings.md +9 -0
  354. package/docs/terminal-setup.md +11 -0
  355. package/docs/tui.md +2 -2
  356. package/examples/extensions/README.md +2 -2
  357. package/examples/extensions/antigravity-image-gen.ts +9 -6
  358. package/examples/extensions/built-in-tool-renderer.ts +8 -8
  359. package/examples/extensions/commands.ts +3 -3
  360. package/examples/extensions/custom-provider-gitlab-duo/test.ts +2 -2
  361. package/examples/extensions/minimal-mode.ts +14 -14
  362. package/examples/extensions/notify.ts +9 -2
  363. package/examples/extensions/preset.ts +2 -3
  364. package/examples/extensions/question.ts +2 -2
  365. package/examples/extensions/questionnaire.ts +2 -2
  366. package/examples/extensions/sandbox/index.ts +2 -3
  367. package/examples/extensions/subagent/index.ts +30 -8
  368. package/examples/extensions/titlebar-spinner.ts +2 -2
  369. package/examples/extensions/todo.ts +2 -2
  370. package/examples/extensions/tool-override.ts +10 -9
  371. package/examples/extensions/truncated-tool.ts +8 -5
  372. package/examples/sdk/04-skills.ts +8 -2
  373. package/examples/sdk/08-prompt-templates.ts +8 -2
  374. package/examples/sdk/12-full-control.ts +0 -1
  375. package/examples/sdk/README.md +1 -1
  376. package/package.json +4 -4
  377. package/prompts/commands/execute-phase.md +2 -2
  378. package/prompts/commands/fix.md +2 -2
  379. package/prompts/commands/plan-phase.md +5 -1
  380. package/prompts/commands/quick.md +5 -1
@@ -1,7 +1,13 @@
1
+ import { Container, Text } from "@draht/tui";
1
2
  import { Type } from "@sinclair/typebox";
2
3
  import { mkdir as fsMkdir, writeFile as fsWriteFile } from "fs/promises";
3
4
  import { dirname } from "path";
5
+ import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
6
+ import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
7
+ import { withFileMutationQueue } from "./file-mutation-queue.js";
4
8
  import { resolveToCwd } from "./path-utils.js";
9
+ import { invalidArgText, normalizeDisplayText, replaceTabs, shortenPath, str } from "./render-utils.js";
10
+ import { wrapToolDefinition } from "./tool-definition-wrapper.js";
5
11
  const writeSchema = Type.Object({
6
12
  path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
7
13
  content: Type.String({ description: "Content to write to the file" }),
@@ -10,69 +16,201 @@ const defaultWriteOperations = {
10
16
  writeFile: (path, content) => fsWriteFile(path, content, "utf-8"),
11
17
  mkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => { }),
12
18
  };
13
- export function createWriteTool(cwd, options) {
19
+ class WriteCallRenderComponent extends Text {
20
+ cache;
21
+ constructor() {
22
+ super("", 0, 0);
23
+ }
24
+ }
25
+ const WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;
26
+ function highlightSingleLine(line, lang) {
27
+ const highlighted = highlightCode(line, lang);
28
+ return highlighted[0] ?? "";
29
+ }
30
+ function refreshWriteHighlightPrefix(cache) {
31
+ const prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);
32
+ if (prefixCount === 0)
33
+ return;
34
+ const prefixSource = cache.normalizedLines.slice(0, prefixCount).join("\n");
35
+ const prefixHighlighted = highlightCode(prefixSource, cache.lang);
36
+ for (let i = 0; i < prefixCount; i++) {
37
+ cache.highlightedLines[i] =
38
+ prefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? "", cache.lang);
39
+ }
40
+ }
41
+ function rebuildWriteHighlightCacheFull(rawPath, fileContent) {
42
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
43
+ if (!lang)
44
+ return undefined;
45
+ const displayContent = normalizeDisplayText(fileContent);
46
+ const normalized = replaceTabs(displayContent);
47
+ return {
48
+ rawPath,
49
+ lang,
50
+ rawContent: fileContent,
51
+ normalizedLines: normalized.split("\n"),
52
+ highlightedLines: highlightCode(normalized, lang),
53
+ };
54
+ }
55
+ function updateWriteHighlightCacheIncremental(cache, rawPath, fileContent) {
56
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
57
+ if (!lang)
58
+ return undefined;
59
+ if (!cache)
60
+ return rebuildWriteHighlightCacheFull(rawPath, fileContent);
61
+ if (cache.lang !== lang || cache.rawPath !== rawPath)
62
+ return rebuildWriteHighlightCacheFull(rawPath, fileContent);
63
+ if (!fileContent.startsWith(cache.rawContent))
64
+ return rebuildWriteHighlightCacheFull(rawPath, fileContent);
65
+ if (fileContent.length === cache.rawContent.length)
66
+ return cache;
67
+ const deltaRaw = fileContent.slice(cache.rawContent.length);
68
+ const deltaDisplay = normalizeDisplayText(deltaRaw);
69
+ const deltaNormalized = replaceTabs(deltaDisplay);
70
+ cache.rawContent = fileContent;
71
+ if (cache.normalizedLines.length === 0) {
72
+ cache.normalizedLines.push("");
73
+ cache.highlightedLines.push("");
74
+ }
75
+ const segments = deltaNormalized.split("\n");
76
+ const lastIndex = cache.normalizedLines.length - 1;
77
+ cache.normalizedLines[lastIndex] += segments[0];
78
+ cache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);
79
+ for (let i = 1; i < segments.length; i++) {
80
+ cache.normalizedLines.push(segments[i]);
81
+ cache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));
82
+ }
83
+ refreshWriteHighlightPrefix(cache);
84
+ return cache;
85
+ }
86
+ function trimTrailingEmptyLines(lines) {
87
+ let end = lines.length;
88
+ while (end > 0 && lines[end - 1] === "") {
89
+ end--;
90
+ }
91
+ return lines.slice(0, end);
92
+ }
93
+ function formatWriteCall(args, options, theme, cache) {
94
+ const rawPath = str(args?.file_path ?? args?.path);
95
+ const fileContent = str(args?.content);
96
+ const path = rawPath !== null ? shortenPath(rawPath) : null;
97
+ const invalidArg = invalidArgText(theme);
98
+ let text = `${theme.fg("toolTitle", theme.bold("write"))} ${path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...")}`;
99
+ if (fileContent === null) {
100
+ text += `\n\n${theme.fg("error", "[invalid content arg - expected string]")}`;
101
+ }
102
+ else if (fileContent) {
103
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
104
+ const renderedLines = lang
105
+ ? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))
106
+ : normalizeDisplayText(fileContent).split("\n");
107
+ const lines = trimTrailingEmptyLines(renderedLines);
108
+ const totalLines = lines.length;
109
+ const maxLines = options.expanded ? lines.length : 10;
110
+ const displayLines = lines.slice(0, maxLines);
111
+ const remaining = lines.length - maxLines;
112
+ text += `\n\n${displayLines.map((line) => (lang ? line : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
113
+ if (remaining > 0) {
114
+ text += `${theme.fg("muted", `\n... (${remaining} more lines, ${totalLines} total,`)} ${keyHint("app.tools.expand", "to expand")})`;
115
+ }
116
+ }
117
+ return text;
118
+ }
119
+ function formatWriteResult(result, theme) {
120
+ if (!result.isError) {
121
+ return undefined;
122
+ }
123
+ const output = result.content
124
+ .filter((c) => c.type === "text")
125
+ .map((c) => c.text || "")
126
+ .join("\n");
127
+ if (!output) {
128
+ return undefined;
129
+ }
130
+ return `\n${theme.fg("error", output)}`;
131
+ }
132
+ export function createWriteToolDefinition(cwd, options) {
14
133
  const ops = options?.operations ?? defaultWriteOperations;
15
134
  return {
16
135
  name: "write",
17
136
  label: "write",
18
137
  description: "Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.",
138
+ promptSnippet: "Create or overwrite files",
139
+ promptGuidelines: ["Use write only for new files or complete rewrites."],
19
140
  parameters: writeSchema,
20
- execute: async (_toolCallId, { path, content }, signal) => {
141
+ async execute(_toolCallId, { path, content }, signal, _onUpdate, _ctx) {
21
142
  const absolutePath = resolveToCwd(path, cwd);
22
143
  const dir = dirname(absolutePath);
23
- return new Promise((resolve, reject) => {
24
- // Check if already aborted
144
+ return withFileMutationQueue(absolutePath, () => new Promise((resolve, reject) => {
25
145
  if (signal?.aborted) {
26
146
  reject(new Error("Operation aborted"));
27
147
  return;
28
148
  }
29
149
  let aborted = false;
30
- // Set up abort handler
31
150
  const onAbort = () => {
32
151
  aborted = true;
33
152
  reject(new Error("Operation aborted"));
34
153
  };
35
- if (signal) {
36
- signal.addEventListener("abort", onAbort, { once: true });
37
- }
38
- // Perform the write operation
154
+ signal?.addEventListener("abort", onAbort, { once: true });
39
155
  (async () => {
40
156
  try {
41
- // Create parent directories if needed
157
+ // Create parent directories if needed.
42
158
  await ops.mkdir(dir);
43
- // Check if aborted before writing
44
- if (aborted) {
159
+ if (aborted)
45
160
  return;
46
- }
47
- // Write the file
161
+ // Write the file contents.
48
162
  await ops.writeFile(absolutePath, content);
49
- // Check if aborted after writing
50
- if (aborted) {
163
+ if (aborted)
51
164
  return;
52
- }
53
- // Clean up abort handler
54
- if (signal) {
55
- signal.removeEventListener("abort", onAbort);
56
- }
165
+ signal?.removeEventListener("abort", onAbort);
57
166
  resolve({
58
- content: [{ type: "text", text: `Successfully wrote ${content.length} bytes to ${path}` }],
167
+ content: [
168
+ { type: "text", text: `Successfully wrote ${content.length} bytes to ${path}` },
169
+ ],
59
170
  details: undefined,
60
171
  });
61
172
  }
62
173
  catch (error) {
63
- // Clean up abort handler
64
- if (signal) {
65
- signal.removeEventListener("abort", onAbort);
66
- }
67
- if (!aborted) {
174
+ signal?.removeEventListener("abort", onAbort);
175
+ if (!aborted)
68
176
  reject(error);
69
- }
70
177
  }
71
178
  })();
72
- });
179
+ }));
180
+ },
181
+ renderCall(args, theme, context) {
182
+ const renderArgs = args;
183
+ const rawPath = str(renderArgs?.file_path ?? renderArgs?.path);
184
+ const fileContent = str(renderArgs?.content);
185
+ const component = context.lastComponent ?? new WriteCallRenderComponent();
186
+ if (fileContent !== null) {
187
+ component.cache = context.argsComplete
188
+ ? rebuildWriteHighlightCacheFull(rawPath, fileContent)
189
+ : updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);
190
+ }
191
+ else {
192
+ component.cache = undefined;
193
+ }
194
+ component.setText(formatWriteCall(renderArgs, { expanded: context.expanded, isPartial: context.isPartial }, theme, component.cache));
195
+ return component;
196
+ },
197
+ renderResult(result, _options, theme, context) {
198
+ const output = formatWriteResult({ ...result, isError: context.isError }, theme);
199
+ if (!output) {
200
+ const component = context.lastComponent ?? new Container();
201
+ component.clear();
202
+ return component;
203
+ }
204
+ const text = context.lastComponent ?? new Text("", 0, 0);
205
+ text.setText(output);
206
+ return text;
73
207
  },
74
208
  };
75
209
  }
76
- /** Default write tool using process.cwd() - for backwards compatibility */
210
+ export function createWriteTool(cwd, options) {
211
+ return wrapToolDefinition(createWriteToolDefinition(cwd, options));
212
+ }
213
+ /** Default write tool using process.cwd() for backwards compatibility. */
214
+ export const writeToolDefinition = createWriteToolDefinition(process.cwd());
77
215
  export const writeTool = createWriteTool(process.cwd());
78
216
  //# sourceMappingURL=write.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@draht/agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { mkdir as fsMkdir, writeFile as fsWriteFile } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { resolveToCwd } from \"./path-utils.js\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (e.g., SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory (recursively) */\n\tmkdir: (dir: string) => Promise<void>;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tparameters: writeSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ path, content }: { path: string; content: string },\n\t\t\tsignal?: AbortSignal,\n\t\t) => {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\n\t\t\treturn new Promise<{ content: Array<{ type: \"text\"; text: string }>; details: undefined }>(\n\t\t\t\t(resolve, reject) => {\n\t\t\t\t\t// Check if already aborted\n\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet aborted = false;\n\n\t\t\t\t\t// Set up abort handler\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t};\n\n\t\t\t\t\tif (signal) {\n\t\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Perform the write operation\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Create parent directories if needed\n\t\t\t\t\t\t\tawait ops.mkdir(dir);\n\n\t\t\t\t\t\t\t// Check if aborted before writing\n\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Write the file\n\t\t\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\n\t\t\t\t\t\t\t// Check if aborted after writing\n\t\t\t\t\t\t\tif (aborted) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Clean up abort handler\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\t\t// Clean up abort handler\n\t\t\t\t\t\t\tif (signal) {\n\t\t\t\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!aborted) {\n\t\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t};\n}\n\n/** Default write tool using process.cwd() - for backwards compatibility */\nexport const writeTool = createWriteTool(process.cwd());\n"]}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@draht/agent-core\";\nimport { Container, Text } from \"@draht/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { mkdir as fsMkdir, writeFile as fsWriteFile } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { getLanguageFromPath, highlightCode } from \"../../modes/interactive/theme/theme.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { invalidArgText, normalizeDisplayText, replaceTabs, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\n}\n\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\n\nfunction formatWriteCall(\n\targs: { path?: string; file_path?: string; content?: string } | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\")}`;\n\n\tif (fileContent === null) {\n\t\ttext += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t} else if (fileContent) {\n\t\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\t\tconst renderedLines = lang\n\t\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t\t: normalizeDisplayText(fileContent).split(\"\\n\");\n\t\tconst lines = trimTrailingEmptyLines(renderedLines);\n\t\tconst totalLines = lines.length;\n\t\tconst maxLines = options.expanded ? lines.length : 10;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines, ${totalLines} total,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t\t}\n\t}\n\n\treturn text;\n}\n\nfunction formatWriteResult(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ToolDefinition<typeof writeSchema, undefined> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: \"Create or overwrite files\",\n\t\tpromptGuidelines: [\"Use write only for new files or complete rewrites.\"],\n\t\tparameters: writeSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, content }: { path: string; content: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(\n\t\t\t\tabsolutePath,\n\t\t\t\t() =>\n\t\t\t\t\tnew Promise<{ content: Array<{ type: \"text\"; text: string }>; details: undefined }>(\n\t\t\t\t\t\t(resolve, reject) => {\n\t\t\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlet aborted = false;\n\t\t\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t\t\t\t(async () => {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\t\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\t\t\t// Write the file contents.\n\t\t\t\t\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\t\t\tif (!aborted) reject(error);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})();\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as { path?: string; file_path?: string; content?: string } | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tcomponent.setText(\n\t\t\t\tformatWriteCall(\n\t\t\t\t\trenderArgs,\n\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\ttheme,\n\t\t\t\t\tcomponent.cache,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n\n/** Default write tool using process.cwd() for backwards compatibility. */\nexport const writeToolDefinition = createWriteToolDefinition(process.cwd());\nexport const writeTool = createWriteTool(process.cwd());\n"]}
@@ -1,3 +1,7 @@
1
+ export type MapCodebaseResult = string[] & {
2
+ entities: string[];
3
+ valueObjects: string[];
4
+ };
1
5
  /**
2
6
  * Generate a DOMAIN-MODEL.md scaffold from PROJECT.md.
3
7
  * Requires .planning/PROJECT.md to exist.
@@ -8,5 +12,5 @@ export declare function createDomainModel(cwd: string): string;
8
12
  * Scan the codebase and write .planning/codebase/ analysis files.
9
13
  * Returns array of created file paths.
10
14
  */
11
- export declare function mapCodebase(cwd: string): string[];
15
+ export declare function mapCodebase(cwd: string): MapCodebaseResult;
12
16
  //# sourceMappingURL=domain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/gsd/domain.ts"],"names":[],"mappings":"AAsBA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqCrD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAoFjD","sourcesContent":["// GSD Domain module — domain model and codebase mapping operations.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst PLANNING = \".planning\";\n\nfunction planningPath(cwd: string, ...segments: string[]): string {\n\treturn path.join(cwd, PLANNING, ...segments);\n}\n\nfunction ensureDir(dir: string): void {\n\tif (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction timestamp(): string {\n\treturn new Date().toISOString().replace(\"T\", \" \").slice(0, 19);\n}\n\n/**\n * Generate a DOMAIN-MODEL.md scaffold from PROJECT.md.\n * Requires .planning/PROJECT.md to exist.\n * Returns the path to the created file.\n */\nexport function createDomainModel(cwd: string): string {\n\tconst projectPath = planningPath(cwd, \"PROJECT.md\");\n\tif (!fs.existsSync(projectPath)) {\n\t\tthrow new Error(\"No PROJECT.md found — run create-project first\");\n\t}\n\n\tconst outPath = planningPath(cwd, \"DOMAIN-MODEL.md\");\n\tconst 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`;\n\tfs.writeFileSync(outPath, tmpl, \"utf-8\");\n\treturn outPath;\n}\n\n/**\n * Scan the codebase and write .planning/codebase/ analysis files.\n * Returns array of created file paths.\n */\nexport function mapCodebase(cwd: string): string[] {\n\tconst outDir = planningPath(cwd, \"codebase\");\n\tensureDir(outDir);\n\n\t// Gather file tree\n\tlet tree = \"\";\n\ttry {\n\t\ttree = execSync(\n\t\t\t`find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/.planning/*' | head -200`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t} catch {\n\t\ttree = \"(unable to list files)\";\n\t}\n\n\t// Gather package info\n\tlet pkgJson: {\n\t\tname?: string;\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t} | null = null;\n\ttry {\n\t\tpkgJson = JSON.parse(fs.readFileSync(path.join(cwd, \"package.json\"), \"utf-8\"));\n\t} catch {\n\t\t// not a Node.js project\n\t}\n\n\tconst stackPath = path.join(outDir, \"STACK.md\");\n\tfs.writeFileSync(\n\t\tstackPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\tconst archPath = path.join(outDir, \"ARCHITECTURE.md\");\n\tfs.writeFileSync(\n\t\tarchPath,\n\t\t`# Architecture\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document file/directory patterns\\n- [ ] Map module boundaries\\n- [ ] Describe data flow\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst convPath = path.join(outDir, \"CONVENTIONS.md\");\n\tfs.writeFileSync(\n\t\tconvPath,\n\t\t`# Conventions\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document code style patterns\\n- [ ] Document testing patterns\\n- [ ] Document error handling approach\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst concernsPath = path.join(outDir, \"CONCERNS.md\");\n\tfs.writeFileSync(\n\t\tconcernsPath,\n\t\t`# Concerns\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Identify technical debt\\n- [ ] Flag security concerns\\n- [ ] Note missing tests\\n`,\n\t\t\"utf-8\",\n\t);\n\n\t// Domain model extraction\n\tlet domainHints = \"\";\n\ttry {\n\t\tconst types = execSync(\n\t\t\t`grep -rn 'export\\\\s\\\\+\\\\(interface\\\\|type\\\\|class\\\\)' --include='*.ts' --include='*.go' . 2>/dev/null | grep -v node_modules | grep -v dist | head -50`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (types) domainHints += `## Types/Interfaces (potential entities)\\n\\`\\`\\`\\n${types}\\n\\`\\`\\`\\n\\n`;\n\t} catch {\n\t\t// no ts/go files\n\t}\n\ttry {\n\t\tconst dirs = execSync(\n\t\t\t`find . -type d -maxdepth 3 -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | sort`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (dirs) domainHints += `## Directory Structure (potential bounded contexts)\\n\\`\\`\\`\\n${dirs}\\n\\`\\`\\`\\n`;\n\t} catch {\n\t\t// ignore\n\t}\n\n\tconst hintsPath = path.join(outDir, \"DOMAIN-HINTS.md\");\n\tfs.writeFileSync(\n\t\thintsPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\treturn [stackPath, archPath, convPath, concernsPath, hintsPath];\n}\n"]}
1
+ {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/gsd/domain.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,iBAAiB,GAAG,MAAM,EAAE,GAAG;IAC1C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAsGF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqCrD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAuF1D","sourcesContent":["// GSD Domain module — domain model and codebase mapping operations.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst PLANNING = \".planning\";\nconst VALUE_OBJECT_NAME = /(Address|Amount|Code|Email|Id|Identifier|Key|Money|Price|Value)$/;\n\nexport type MapCodebaseResult = string[] & {\n\tentities: string[];\n\tvalueObjects: string[];\n};\n\nfunction planningPath(cwd: string, ...segments: string[]): string {\n\treturn path.join(cwd, PLANNING, ...segments);\n}\n\nfunction ensureDir(dir: string): void {\n\tif (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction ensureCodebaseExists(cwd: string): void {\n\tif (!fs.existsSync(cwd)) {\n\t\tthrow new Error(`Codebase path does not exist: ${cwd}`);\n\t}\n}\n\nfunction timestamp(): string {\n\treturn new Date().toISOString().replace(\"T\", \" \").slice(0, 19);\n}\n\nfunction toRelativeCodePath(cwd: string, filePath: string): string {\n\treturn path.relative(cwd, filePath).split(path.sep).join(\"/\");\n}\n\nfunction listCodeFiles(cwd: string): string[] {\n\tconst codeFiles: string[] = [];\n\tconst ignoredDirs = new Set([\".git\", \".planning\", \"dist\", \"node_modules\"]);\n\tconst pendingDirs = [cwd];\n\n\twhile (pendingDirs.length > 0) {\n\t\tconst currentDir = pendingDirs.pop();\n\t\tif (!currentDir) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entries = fs.readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst entryPath = path.join(currentDir, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (!ignoredDirs.has(entry.name)) {\n\t\t\t\t\tpendingDirs.push(entryPath);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (entry.isFile() && /\\.(go|ts)$/.test(entry.name)) {\n\t\t\t\tcodeFiles.push(entryPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn codeFiles.sort((left, right) => toRelativeCodePath(cwd, left).localeCompare(toRelativeCodePath(cwd, right)));\n}\n\nfunction extractExportedTerms(fileContent: string): string[] {\n\tconst terms = new Set<string>();\n\tconst exportPattern = /export\\s+(?:interface|type|class)\\s+([A-Z][A-Za-z0-9_]*)/g;\n\n\tfor (const match of fileContent.matchAll(exportPattern)) {\n\t\tterms.add(match[1]);\n\t}\n\n\treturn [...terms].sort((left, right) => left.localeCompare(right));\n}\n\nfunction classifyTerms(terms: string[]): { entities: string[]; valueObjects: string[] } {\n\tconst entities: string[] = [];\n\tconst valueObjects: string[] = [];\n\n\tfor (const term of terms) {\n\t\tif (VALUE_OBJECT_NAME.test(term)) {\n\t\t\tvalueObjects.push(term);\n\t\t\tcontinue;\n\t\t}\n\n\t\tentities.push(term);\n\t}\n\n\treturn { entities, valueObjects };\n}\n\nfunction scanStructuredDomainTerms(cwd: string): { entities: string[]; valueObjects: string[] } {\n\tconst uniqueTerms = new Set<string>();\n\n\tfor (const filePath of listCodeFiles(cwd)) {\n\t\tconst fileContent = fs.readFileSync(filePath, \"utf-8\");\n\t\tfor (const term of extractExportedTerms(fileContent)) {\n\t\t\tuniqueTerms.add(term);\n\t\t}\n\t}\n\n\tconst sortedTerms = [...uniqueTerms].sort((left, right) => left.localeCompare(right));\n\treturn classifyTerms(sortedTerms);\n}\n\nfunction attachStructuredDomainTerms(\n\tfiles: string[],\n\tstructuredTerms: { entities: string[]; valueObjects: string[] },\n): MapCodebaseResult {\n\treturn Object.assign(files, structuredTerms);\n}\n\n/**\n * Generate a DOMAIN-MODEL.md scaffold from PROJECT.md.\n * Requires .planning/PROJECT.md to exist.\n * Returns the path to the created file.\n */\nexport function createDomainModel(cwd: string): string {\n\tconst projectPath = planningPath(cwd, \"PROJECT.md\");\n\tif (!fs.existsSync(projectPath)) {\n\t\tthrow new Error(\"No PROJECT.md found — run create-project first\");\n\t}\n\n\tconst outPath = planningPath(cwd, \"DOMAIN-MODEL.md\");\n\tconst 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`;\n\tfs.writeFileSync(outPath, tmpl, \"utf-8\");\n\treturn outPath;\n}\n\n/**\n * Scan the codebase and write .planning/codebase/ analysis files.\n * Returns array of created file paths.\n */\nexport function mapCodebase(cwd: string): MapCodebaseResult {\n\tensureCodebaseExists(cwd);\n\n\tconst outDir = planningPath(cwd, \"codebase\");\n\tensureDir(outDir);\n\tconst structuredTerms = scanStructuredDomainTerms(cwd);\n\n\t// Gather file tree\n\tlet tree = \"\";\n\ttry {\n\t\ttree = execSync(\n\t\t\t`find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/.planning/*' | head -200`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t} catch {\n\t\ttree = \"(unable to list files)\";\n\t}\n\n\t// Gather package info\n\tlet pkgJson: {\n\t\tname?: string;\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t} | null = null;\n\ttry {\n\t\tpkgJson = JSON.parse(fs.readFileSync(path.join(cwd, \"package.json\"), \"utf-8\"));\n\t} catch {\n\t\t// not a Node.js project\n\t}\n\n\tconst stackPath = path.join(outDir, \"STACK.md\");\n\tfs.writeFileSync(\n\t\tstackPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\tconst archPath = path.join(outDir, \"ARCHITECTURE.md\");\n\tfs.writeFileSync(\n\t\tarchPath,\n\t\t`# Architecture\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document file/directory patterns\\n- [ ] Map module boundaries\\n- [ ] Describe data flow\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst convPath = path.join(outDir, \"CONVENTIONS.md\");\n\tfs.writeFileSync(\n\t\tconvPath,\n\t\t`# Conventions\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document code style patterns\\n- [ ] Document testing patterns\\n- [ ] Document error handling approach\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst concernsPath = path.join(outDir, \"CONCERNS.md\");\n\tfs.writeFileSync(\n\t\tconcernsPath,\n\t\t`# Concerns\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Identify technical debt\\n- [ ] Flag security concerns\\n- [ ] Note missing tests\\n`,\n\t\t\"utf-8\",\n\t);\n\n\t// Domain model extraction\n\tlet domainHints = \"\";\n\ttry {\n\t\tconst types = execSync(\n\t\t\t`grep -rn 'export\\\\s\\\\+\\\\(interface\\\\|type\\\\|class\\\\)' --include='*.ts' --include='*.go' . 2>/dev/null | grep -v node_modules | grep -v dist | head -50`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (types) domainHints += `## Types/Interfaces (potential entities)\\n\\`\\`\\`\\n${types}\\n\\`\\`\\`\\n\\n`;\n\t} catch {\n\t\t// no ts/go files\n\t}\n\ttry {\n\t\tconst dirs = execSync(\n\t\t\t`find . -type d -maxdepth 3 -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | sort`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (dirs) domainHints += `## Directory Structure (potential bounded contexts)\\n\\`\\`\\`\\n${dirs}\\n\\`\\`\\`\\n`;\n\t} catch {\n\t\t// ignore\n\t}\n\n\tconst hintsPath = path.join(outDir, \"DOMAIN-HINTS.md\");\n\tfs.writeFileSync(\n\t\thintsPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\treturn attachStructuredDomainTerms([stackPath, archPath, convPath, concernsPath, hintsPath], structuredTerms);\n}\n"]}
@@ -5,6 +5,7 @@ import { execSync } from "node:child_process";
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
7
  const PLANNING = ".planning";
8
+ const VALUE_OBJECT_NAME = /(Address|Amount|Code|Email|Id|Identifier|Key|Money|Price|Value)$/;
8
9
  function planningPath(cwd, ...segments) {
9
10
  return path.join(cwd, PLANNING, ...segments);
10
11
  }
@@ -12,9 +13,76 @@ function ensureDir(dir) {
12
13
  if (!fs.existsSync(dir))
13
14
  fs.mkdirSync(dir, { recursive: true });
14
15
  }
16
+ function ensureCodebaseExists(cwd) {
17
+ if (!fs.existsSync(cwd)) {
18
+ throw new Error(`Codebase path does not exist: ${cwd}`);
19
+ }
20
+ }
15
21
  function timestamp() {
16
22
  return new Date().toISOString().replace("T", " ").slice(0, 19);
17
23
  }
24
+ function toRelativeCodePath(cwd, filePath) {
25
+ return path.relative(cwd, filePath).split(path.sep).join("/");
26
+ }
27
+ function listCodeFiles(cwd) {
28
+ const codeFiles = [];
29
+ const ignoredDirs = new Set([".git", ".planning", "dist", "node_modules"]);
30
+ const pendingDirs = [cwd];
31
+ while (pendingDirs.length > 0) {
32
+ const currentDir = pendingDirs.pop();
33
+ if (!currentDir) {
34
+ continue;
35
+ }
36
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
37
+ for (const entry of entries) {
38
+ const entryPath = path.join(currentDir, entry.name);
39
+ if (entry.isDirectory()) {
40
+ if (!ignoredDirs.has(entry.name)) {
41
+ pendingDirs.push(entryPath);
42
+ }
43
+ continue;
44
+ }
45
+ if (entry.isFile() && /\.(go|ts)$/.test(entry.name)) {
46
+ codeFiles.push(entryPath);
47
+ }
48
+ }
49
+ }
50
+ return codeFiles.sort((left, right) => toRelativeCodePath(cwd, left).localeCompare(toRelativeCodePath(cwd, right)));
51
+ }
52
+ function extractExportedTerms(fileContent) {
53
+ const terms = new Set();
54
+ const exportPattern = /export\s+(?:interface|type|class)\s+([A-Z][A-Za-z0-9_]*)/g;
55
+ for (const match of fileContent.matchAll(exportPattern)) {
56
+ terms.add(match[1]);
57
+ }
58
+ return [...terms].sort((left, right) => left.localeCompare(right));
59
+ }
60
+ function classifyTerms(terms) {
61
+ const entities = [];
62
+ const valueObjects = [];
63
+ for (const term of terms) {
64
+ if (VALUE_OBJECT_NAME.test(term)) {
65
+ valueObjects.push(term);
66
+ continue;
67
+ }
68
+ entities.push(term);
69
+ }
70
+ return { entities, valueObjects };
71
+ }
72
+ function scanStructuredDomainTerms(cwd) {
73
+ const uniqueTerms = new Set();
74
+ for (const filePath of listCodeFiles(cwd)) {
75
+ const fileContent = fs.readFileSync(filePath, "utf-8");
76
+ for (const term of extractExportedTerms(fileContent)) {
77
+ uniqueTerms.add(term);
78
+ }
79
+ }
80
+ const sortedTerms = [...uniqueTerms].sort((left, right) => left.localeCompare(right));
81
+ return classifyTerms(sortedTerms);
82
+ }
83
+ function attachStructuredDomainTerms(files, structuredTerms) {
84
+ return Object.assign(files, structuredTerms);
85
+ }
18
86
  /**
19
87
  * Generate a DOMAIN-MODEL.md scaffold from PROJECT.md.
20
88
  * Requires .planning/PROJECT.md to exist.
@@ -62,8 +130,10 @@ Generated from PROJECT.md: ${timestamp()}
62
130
  * Returns array of created file paths.
63
131
  */
64
132
  export function mapCodebase(cwd) {
133
+ ensureCodebaseExists(cwd);
65
134
  const outDir = planningPath(cwd, "codebase");
66
135
  ensureDir(outDir);
136
+ const structuredTerms = scanStructuredDomainTerms(cwd);
67
137
  // Gather file tree
68
138
  let tree = "";
69
139
  try {
@@ -108,6 +178,6 @@ export function mapCodebase(cwd) {
108
178
  }
109
179
  const hintsPath = path.join(outDir, "DOMAIN-HINTS.md");
110
180
  fs.writeFileSync(hintsPath, `# 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`, "utf-8");
111
- return [stackPath, archPath, convPath, concernsPath, hintsPath];
181
+ return attachStructuredDomainTerms([stackPath, archPath, convPath, concernsPath, hintsPath], structuredTerms);
112
182
  }
113
183
  //# sourceMappingURL=domain.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"domain.js","sourceRoot":"","sources":["../../src/gsd/domain.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD Domain module — domain model and codebase mapping operations.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst PLANNING = \".planning\";\n\nfunction planningPath(cwd: string, ...segments: string[]): string {\n\treturn path.join(cwd, PLANNING, ...segments);\n}\n\nfunction ensureDir(dir: string): void {\n\tif (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction timestamp(): string {\n\treturn new Date().toISOString().replace(\"T\", \" \").slice(0, 19);\n}\n\n/**\n * Generate a DOMAIN-MODEL.md scaffold from PROJECT.md.\n * Requires .planning/PROJECT.md to exist.\n * Returns the path to the created file.\n */\nexport function createDomainModel(cwd: string): string {\n\tconst projectPath = planningPath(cwd, \"PROJECT.md\");\n\tif (!fs.existsSync(projectPath)) {\n\t\tthrow new Error(\"No PROJECT.md found — run create-project first\");\n\t}\n\n\tconst outPath = planningPath(cwd, \"DOMAIN-MODEL.md\");\n\tconst 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`;\n\tfs.writeFileSync(outPath, tmpl, \"utf-8\");\n\treturn outPath;\n}\n\n/**\n * Scan the codebase and write .planning/codebase/ analysis files.\n * Returns array of created file paths.\n */\nexport function mapCodebase(cwd: string): string[] {\n\tconst outDir = planningPath(cwd, \"codebase\");\n\tensureDir(outDir);\n\n\t// Gather file tree\n\tlet tree = \"\";\n\ttry {\n\t\ttree = execSync(\n\t\t\t`find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/.planning/*' | head -200`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t} catch {\n\t\ttree = \"(unable to list files)\";\n\t}\n\n\t// Gather package info\n\tlet pkgJson: {\n\t\tname?: string;\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t} | null = null;\n\ttry {\n\t\tpkgJson = JSON.parse(fs.readFileSync(path.join(cwd, \"package.json\"), \"utf-8\"));\n\t} catch {\n\t\t// not a Node.js project\n\t}\n\n\tconst stackPath = path.join(outDir, \"STACK.md\");\n\tfs.writeFileSync(\n\t\tstackPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\tconst archPath = path.join(outDir, \"ARCHITECTURE.md\");\n\tfs.writeFileSync(\n\t\tarchPath,\n\t\t`# Architecture\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document file/directory patterns\\n- [ ] Map module boundaries\\n- [ ] Describe data flow\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst convPath = path.join(outDir, \"CONVENTIONS.md\");\n\tfs.writeFileSync(\n\t\tconvPath,\n\t\t`# Conventions\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document code style patterns\\n- [ ] Document testing patterns\\n- [ ] Document error handling approach\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst concernsPath = path.join(outDir, \"CONCERNS.md\");\n\tfs.writeFileSync(\n\t\tconcernsPath,\n\t\t`# Concerns\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Identify technical debt\\n- [ ] Flag security concerns\\n- [ ] Note missing tests\\n`,\n\t\t\"utf-8\",\n\t);\n\n\t// Domain model extraction\n\tlet domainHints = \"\";\n\ttry {\n\t\tconst types = execSync(\n\t\t\t`grep -rn 'export\\\\s\\\\+\\\\(interface\\\\|type\\\\|class\\\\)' --include='*.ts' --include='*.go' . 2>/dev/null | grep -v node_modules | grep -v dist | head -50`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (types) domainHints += `## Types/Interfaces (potential entities)\\n\\`\\`\\`\\n${types}\\n\\`\\`\\`\\n\\n`;\n\t} catch {\n\t\t// no ts/go files\n\t}\n\ttry {\n\t\tconst dirs = execSync(\n\t\t\t`find . -type d -maxdepth 3 -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | sort`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (dirs) domainHints += `## Directory Structure (potential bounded contexts)\\n\\`\\`\\`\\n${dirs}\\n\\`\\`\\`\\n`;\n\t} catch {\n\t\t// ignore\n\t}\n\n\tconst hintsPath = path.join(outDir, \"DOMAIN-HINTS.md\");\n\tfs.writeFileSync(\n\t\thintsPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\treturn [stackPath, archPath, convPath, concernsPath, hintsPath];\n}\n"]}
1
+ {"version":3,"file":"domain.js","sourceRoot":"","sources":["../../src/gsd/domain.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD Domain module — domain model and codebase mapping operations.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst PLANNING = \".planning\";\nconst VALUE_OBJECT_NAME = /(Address|Amount|Code|Email|Id|Identifier|Key|Money|Price|Value)$/;\n\nexport type MapCodebaseResult = string[] & {\n\tentities: string[];\n\tvalueObjects: string[];\n};\n\nfunction planningPath(cwd: string, ...segments: string[]): string {\n\treturn path.join(cwd, PLANNING, ...segments);\n}\n\nfunction ensureDir(dir: string): void {\n\tif (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction ensureCodebaseExists(cwd: string): void {\n\tif (!fs.existsSync(cwd)) {\n\t\tthrow new Error(`Codebase path does not exist: ${cwd}`);\n\t}\n}\n\nfunction timestamp(): string {\n\treturn new Date().toISOString().replace(\"T\", \" \").slice(0, 19);\n}\n\nfunction toRelativeCodePath(cwd: string, filePath: string): string {\n\treturn path.relative(cwd, filePath).split(path.sep).join(\"/\");\n}\n\nfunction listCodeFiles(cwd: string): string[] {\n\tconst codeFiles: string[] = [];\n\tconst ignoredDirs = new Set([\".git\", \".planning\", \"dist\", \"node_modules\"]);\n\tconst pendingDirs = [cwd];\n\n\twhile (pendingDirs.length > 0) {\n\t\tconst currentDir = pendingDirs.pop();\n\t\tif (!currentDir) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entries = fs.readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst entryPath = path.join(currentDir, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (!ignoredDirs.has(entry.name)) {\n\t\t\t\t\tpendingDirs.push(entryPath);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (entry.isFile() && /\\.(go|ts)$/.test(entry.name)) {\n\t\t\t\tcodeFiles.push(entryPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn codeFiles.sort((left, right) => toRelativeCodePath(cwd, left).localeCompare(toRelativeCodePath(cwd, right)));\n}\n\nfunction extractExportedTerms(fileContent: string): string[] {\n\tconst terms = new Set<string>();\n\tconst exportPattern = /export\\s+(?:interface|type|class)\\s+([A-Z][A-Za-z0-9_]*)/g;\n\n\tfor (const match of fileContent.matchAll(exportPattern)) {\n\t\tterms.add(match[1]);\n\t}\n\n\treturn [...terms].sort((left, right) => left.localeCompare(right));\n}\n\nfunction classifyTerms(terms: string[]): { entities: string[]; valueObjects: string[] } {\n\tconst entities: string[] = [];\n\tconst valueObjects: string[] = [];\n\n\tfor (const term of terms) {\n\t\tif (VALUE_OBJECT_NAME.test(term)) {\n\t\t\tvalueObjects.push(term);\n\t\t\tcontinue;\n\t\t}\n\n\t\tentities.push(term);\n\t}\n\n\treturn { entities, valueObjects };\n}\n\nfunction scanStructuredDomainTerms(cwd: string): { entities: string[]; valueObjects: string[] } {\n\tconst uniqueTerms = new Set<string>();\n\n\tfor (const filePath of listCodeFiles(cwd)) {\n\t\tconst fileContent = fs.readFileSync(filePath, \"utf-8\");\n\t\tfor (const term of extractExportedTerms(fileContent)) {\n\t\t\tuniqueTerms.add(term);\n\t\t}\n\t}\n\n\tconst sortedTerms = [...uniqueTerms].sort((left, right) => left.localeCompare(right));\n\treturn classifyTerms(sortedTerms);\n}\n\nfunction attachStructuredDomainTerms(\n\tfiles: string[],\n\tstructuredTerms: { entities: string[]; valueObjects: string[] },\n): MapCodebaseResult {\n\treturn Object.assign(files, structuredTerms);\n}\n\n/**\n * Generate a DOMAIN-MODEL.md scaffold from PROJECT.md.\n * Requires .planning/PROJECT.md to exist.\n * Returns the path to the created file.\n */\nexport function createDomainModel(cwd: string): string {\n\tconst projectPath = planningPath(cwd, \"PROJECT.md\");\n\tif (!fs.existsSync(projectPath)) {\n\t\tthrow new Error(\"No PROJECT.md found — run create-project first\");\n\t}\n\n\tconst outPath = planningPath(cwd, \"DOMAIN-MODEL.md\");\n\tconst 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`;\n\tfs.writeFileSync(outPath, tmpl, \"utf-8\");\n\treturn outPath;\n}\n\n/**\n * Scan the codebase and write .planning/codebase/ analysis files.\n * Returns array of created file paths.\n */\nexport function mapCodebase(cwd: string): MapCodebaseResult {\n\tensureCodebaseExists(cwd);\n\n\tconst outDir = planningPath(cwd, \"codebase\");\n\tensureDir(outDir);\n\tconst structuredTerms = scanStructuredDomainTerms(cwd);\n\n\t// Gather file tree\n\tlet tree = \"\";\n\ttry {\n\t\ttree = execSync(\n\t\t\t`find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/.planning/*' | head -200`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t} catch {\n\t\ttree = \"(unable to list files)\";\n\t}\n\n\t// Gather package info\n\tlet pkgJson: {\n\t\tname?: string;\n\t\tdependencies?: Record<string, string>;\n\t\tdevDependencies?: Record<string, string>;\n\t} | null = null;\n\ttry {\n\t\tpkgJson = JSON.parse(fs.readFileSync(path.join(cwd, \"package.json\"), \"utf-8\"));\n\t} catch {\n\t\t// not a Node.js project\n\t}\n\n\tconst stackPath = path.join(outDir, \"STACK.md\");\n\tfs.writeFileSync(\n\t\tstackPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\tconst archPath = path.join(outDir, \"ARCHITECTURE.md\");\n\tfs.writeFileSync(\n\t\tarchPath,\n\t\t`# Architecture\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document file/directory patterns\\n- [ ] Map module boundaries\\n- [ ] Describe data flow\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst convPath = path.join(outDir, \"CONVENTIONS.md\");\n\tfs.writeFileSync(\n\t\tconvPath,\n\t\t`# Conventions\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Document code style patterns\\n- [ ] Document testing patterns\\n- [ ] Document error handling approach\\n`,\n\t\t\"utf-8\",\n\t);\n\n\tconst concernsPath = path.join(outDir, \"CONCERNS.md\");\n\tfs.writeFileSync(\n\t\tconcernsPath,\n\t\t`# Concerns\\n\\nGenerated: ${timestamp()}\\n\\n## TODO\\n- [ ] Identify technical debt\\n- [ ] Flag security concerns\\n- [ ] Note missing tests\\n`,\n\t\t\"utf-8\",\n\t);\n\n\t// Domain model extraction\n\tlet domainHints = \"\";\n\ttry {\n\t\tconst types = execSync(\n\t\t\t`grep -rn 'export\\\\s\\\\+\\\\(interface\\\\|type\\\\|class\\\\)' --include='*.ts' --include='*.go' . 2>/dev/null | grep -v node_modules | grep -v dist | head -50`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (types) domainHints += `## Types/Interfaces (potential entities)\\n\\`\\`\\`\\n${types}\\n\\`\\`\\`\\n\\n`;\n\t} catch {\n\t\t// no ts/go files\n\t}\n\ttry {\n\t\tconst dirs = execSync(\n\t\t\t`find . -type d -maxdepth 3 -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | sort`,\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t).trim();\n\t\tif (dirs) domainHints += `## Directory Structure (potential bounded contexts)\\n\\`\\`\\`\\n${dirs}\\n\\`\\`\\`\\n`;\n\t} catch {\n\t\t// ignore\n\t}\n\n\tconst hintsPath = path.join(outDir, \"DOMAIN-HINTS.md\");\n\tfs.writeFileSync(\n\t\thintsPath,\n\t\t`# 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`,\n\t\t\"utf-8\",\n\t);\n\n\treturn attachStructuredDomainTerms([stackPath, archPath, convPath, concernsPath, hintsPath], structuredTerms);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/gsd/git.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAErD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAwB5G;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAUrE","sourcesContent":["// GSD Git module — git commit operations for the GSD lifecycle.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\n\nexport interface CommitResult {\n\thash: string | null;\n\ttddWarning: boolean;\n}\n\n/**\n * Returns true if any file in the list matches known test file patterns.\n */\nexport function hasTestFiles(files: string[]): boolean {\n\treturn files.some((f) => /\\.(test|spec)\\.(ts|tsx|js|jsx)$|_test\\.(go|ts)$/.test(f));\n}\n\n/**\n * Stage all changes and commit as a task in the GSD methodology.\n * Message format: feat(NN-NN): description\n * Sets tddWarning=true when no test files are in the commit.\n */\nexport function commitTask(cwd: string, phaseNum: number, planNum: number, description: string): CommitResult {\n\tconst scope = `${String(phaseNum).padStart(2, \"0\")}-${String(planNum).padStart(2, \"0\")}`;\n\tconst message = `feat(${scope}): ${description}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(message)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\tlet tddWarning = false;\n\t\ttry {\n\t\t\tconst files = execSync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t})\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter(Boolean);\n\t\t\ttddWarning = !hasTestFiles(files);\n\t\t} catch {\n\t\t\t// not a git repo or commit not found\n\t\t}\n\t\treturn { hash, tddWarning };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n\n/**\n * Stage all changes and commit as a docs update.\n * Message format: docs: message\n */\nexport function commitDocs(cwd: string, message: string): CommitResult {\n\tconst msg = `docs: ${message}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(msg)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\treturn { hash, tddWarning: false };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n"]}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/gsd/git.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAoBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAErD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAgC5G;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAUrE","sourcesContent":["// GSD Git module — git commit operations for the GSD lifecycle.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport interface CommitResult {\n\thash: string | null;\n\ttddWarning: boolean;\n}\n\ninterface ExecutionLogEntry {\n\tcommit: string;\n\tphase: number;\n\tplan: number;\n\tstatus: \"pass\" | \"tdd-violation\";\n\ttask: number;\n\ttimestamp: string;\n}\n\nfunction appendExecutionLog(cwd: string, entry: ExecutionLogEntry): void {\n\tconst planningDir = path.join(cwd, \".planning\");\n\tif (!fs.existsSync(planningDir)) {\n\t\treturn;\n\t}\n\tconst logPath = path.join(planningDir, \"execution-log.jsonl\");\n\tfs.appendFileSync(logPath, `${JSON.stringify(entry)}\\n`, \"utf-8\");\n}\n\n/**\n * Returns true if any file in the list matches known test file patterns.\n */\nexport function hasTestFiles(files: string[]): boolean {\n\treturn files.some((f) => /\\.(test|spec)\\.(ts|tsx|js|jsx)$|_test\\.(go|ts)$/.test(f));\n}\n\n/**\n * Stage all changes and commit as a task in the GSD methodology.\n * Message format: feat(NN-NN): description\n * Sets tddWarning=true when no test files are in the commit.\n */\nexport function commitTask(cwd: string, phaseNum: number, planNum: number, description: string): CommitResult {\n\tconst scope = `${String(phaseNum).padStart(2, \"0\")}-${String(planNum).padStart(2, \"0\")}`;\n\tconst message = `feat(${scope}): ${description}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(message)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\tlet tddWarning = false;\n\t\ttry {\n\t\t\tconst files = execSync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t})\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter(Boolean);\n\t\t\ttddWarning = !hasTestFiles(files);\n\t\t} catch {\n\t\t\t// not a git repo or commit not found\n\t\t}\n\t\tappendExecutionLog(cwd, {\n\t\t\tcommit: hash,\n\t\t\tphase: phaseNum,\n\t\t\tplan: planNum,\n\t\t\tstatus: tddWarning ? \"tdd-violation\" : \"pass\",\n\t\t\ttask: 1,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t});\n\t\treturn { hash, tddWarning };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n\n/**\n * Stage all changes and commit as a docs update.\n * Message format: docs: message\n */\nexport function commitDocs(cwd: string, message: string): CommitResult {\n\tconst msg = `docs: ${message}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(msg)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\treturn { hash, tddWarning: false };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n"]}
package/dist/gsd/git.js CHANGED
@@ -2,6 +2,16 @@
2
2
  // Part of the draht GSD (Get Shit Done) methodology.
3
3
  // Exported via src/gsd/index.ts and @draht/coding-agent.
4
4
  import { execSync } from "node:child_process";
5
+ import * as fs from "node:fs";
6
+ import * as path from "node:path";
7
+ function appendExecutionLog(cwd, entry) {
8
+ const planningDir = path.join(cwd, ".planning");
9
+ if (!fs.existsSync(planningDir)) {
10
+ return;
11
+ }
12
+ const logPath = path.join(planningDir, "execution-log.jsonl");
13
+ fs.appendFileSync(logPath, `${JSON.stringify(entry)}\n`, "utf-8");
14
+ }
5
15
  /**
6
16
  * Returns true if any file in the list matches known test file patterns.
7
17
  */
@@ -34,6 +44,14 @@ export function commitTask(cwd, phaseNum, planNum, description) {
34
44
  catch {
35
45
  // not a git repo or commit not found
36
46
  }
47
+ appendExecutionLog(cwd, {
48
+ commit: hash,
49
+ phase: phaseNum,
50
+ plan: planNum,
51
+ status: tddWarning ? "tdd-violation" : "pass",
52
+ task: 1,
53
+ timestamp: new Date().toISOString(),
54
+ });
37
55
  return { hash, tddWarning };
38
56
  }
39
57
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/gsd/git.ts"],"names":[],"mappings":"AAAA,kEAAgE;AAChE,qDAAqD;AACrD,yDAAyD;AAEzD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAO9C;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAe,EAAW;IACtD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iDAAiD,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAAA,CACpF;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,QAAgB,EAAE,OAAe,EAAE,WAAmB,EAAgB;IAC7G,MAAM,KAAK,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACzF,MAAM,OAAO,GAAG,QAAQ,KAAK,MAAM,WAAW,EAAE,CAAC;IACjD,IAAI,CAAC;QACJ,QAAQ,CAAC,YAAY,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/C,QAAQ,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,QAAQ,CAAC,oBAAoB,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/E,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,QAAQ,CAAC,+CAA+C,IAAI,EAAE,EAAE;gBAC7E,GAAG;gBACH,QAAQ,EAAE,OAAO;aACjB,CAAC;iBACA,IAAI,EAAE;iBACN,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,OAAO,CAAC,CAAC;YAClB,UAAU,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACR,qCAAqC;QACtC,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC1C,CAAC;AAAA,CACD;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,OAAe,EAAgB;IACtE,MAAM,GAAG,GAAG,SAAS,OAAO,EAAE,CAAC;IAC/B,IAAI,CAAC;QACJ,QAAQ,CAAC,YAAY,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/C,QAAQ,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,QAAQ,CAAC,oBAAoB,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/E,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC1C,CAAC;AAAA,CACD","sourcesContent":["// GSD Git module — git commit operations for the GSD lifecycle.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\n\nexport interface CommitResult {\n\thash: string | null;\n\ttddWarning: boolean;\n}\n\n/**\n * Returns true if any file in the list matches known test file patterns.\n */\nexport function hasTestFiles(files: string[]): boolean {\n\treturn files.some((f) => /\\.(test|spec)\\.(ts|tsx|js|jsx)$|_test\\.(go|ts)$/.test(f));\n}\n\n/**\n * Stage all changes and commit as a task in the GSD methodology.\n * Message format: feat(NN-NN): description\n * Sets tddWarning=true when no test files are in the commit.\n */\nexport function commitTask(cwd: string, phaseNum: number, planNum: number, description: string): CommitResult {\n\tconst scope = `${String(phaseNum).padStart(2, \"0\")}-${String(planNum).padStart(2, \"0\")}`;\n\tconst message = `feat(${scope}): ${description}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(message)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\tlet tddWarning = false;\n\t\ttry {\n\t\t\tconst files = execSync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t})\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter(Boolean);\n\t\t\ttddWarning = !hasTestFiles(files);\n\t\t} catch {\n\t\t\t// not a git repo or commit not found\n\t\t}\n\t\treturn { hash, tddWarning };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n\n/**\n * Stage all changes and commit as a docs update.\n * Message format: docs: message\n */\nexport function commitDocs(cwd: string, message: string): CommitResult {\n\tconst msg = `docs: ${message}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(msg)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\treturn { hash, tddWarning: false };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n"]}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/gsd/git.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD Git module — git commit operations for the GSD lifecycle.\n// Part of the draht GSD (Get Shit Done) methodology.\n// Exported via src/gsd/index.ts and @draht/coding-agent.\n\nimport { execSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport interface CommitResult {\n\thash: string | null;\n\ttddWarning: boolean;\n}\n\ninterface ExecutionLogEntry {\n\tcommit: string;\n\tphase: number;\n\tplan: number;\n\tstatus: \"pass\" | \"tdd-violation\";\n\ttask: number;\n\ttimestamp: string;\n}\n\nfunction appendExecutionLog(cwd: string, entry: ExecutionLogEntry): void {\n\tconst planningDir = path.join(cwd, \".planning\");\n\tif (!fs.existsSync(planningDir)) {\n\t\treturn;\n\t}\n\tconst logPath = path.join(planningDir, \"execution-log.jsonl\");\n\tfs.appendFileSync(logPath, `${JSON.stringify(entry)}\\n`, \"utf-8\");\n}\n\n/**\n * Returns true if any file in the list matches known test file patterns.\n */\nexport function hasTestFiles(files: string[]): boolean {\n\treturn files.some((f) => /\\.(test|spec)\\.(ts|tsx|js|jsx)$|_test\\.(go|ts)$/.test(f));\n}\n\n/**\n * Stage all changes and commit as a task in the GSD methodology.\n * Message format: feat(NN-NN): description\n * Sets tddWarning=true when no test files are in the commit.\n */\nexport function commitTask(cwd: string, phaseNum: number, planNum: number, description: string): CommitResult {\n\tconst scope = `${String(phaseNum).padStart(2, \"0\")}-${String(planNum).padStart(2, \"0\")}`;\n\tconst message = `feat(${scope}): ${description}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(message)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\tlet tddWarning = false;\n\t\ttry {\n\t\t\tconst files = execSync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t})\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter(Boolean);\n\t\t\ttddWarning = !hasTestFiles(files);\n\t\t} catch {\n\t\t\t// not a git repo or commit not found\n\t\t}\n\t\tappendExecutionLog(cwd, {\n\t\t\tcommit: hash,\n\t\t\tphase: phaseNum,\n\t\t\tplan: planNum,\n\t\t\tstatus: tddWarning ? \"tdd-violation\" : \"pass\",\n\t\t\ttask: 1,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t});\n\t\treturn { hash, tddWarning };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n\n/**\n * Stage all changes and commit as a docs update.\n * Message format: docs: message\n */\nexport function commitDocs(cwd: string, message: string): CommitResult {\n\tconst msg = `docs: ${message}`;\n\ttry {\n\t\texecSync(\"git add -A\", { cwd, stdio: \"pipe\" });\n\t\texecSync(`git commit -m ${JSON.stringify(msg)}`, { cwd, stdio: \"pipe\" });\n\t\tconst hash = execSync(\"git rev-parse HEAD\", { cwd, encoding: \"utf-8\" }).trim();\n\t\treturn { hash, tddWarning: false };\n\t} catch {\n\t\treturn { hash: null, tddWarning: false };\n\t}\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export type { MapCodebaseResult } from "./domain.js";
1
2
  export { createDomainModel, mapCodebase } from "./domain.js";
2
3
  export { extractGlossaryTerms, loadDomainContent, validateDomainGlossary, } from "./domain-validator.js";
3
4
  export type { CommitResult } from "./git.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gsd/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EACN,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClE,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EACN,UAAU,EACV,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,YAAY,GACZ,MAAM,eAAe,CAAC","sourcesContent":["// GSD index — re-exports all GSD module functions.\n// Import from @draht/coding-agent for use in extensions.\n\nexport { createDomainModel, mapCodebase } from \"./domain.js\";\nexport {\n\textractGlossaryTerms,\n\tloadDomainContent,\n\tvalidateDomainGlossary,\n} from \"./domain-validator.js\";\nexport type { CommitResult } from \"./git.js\";\nexport { commitDocs, commitTask, hasTestFiles } from \"./git.js\";\nexport type { HookConfig, ToolchainInfo } from \"./hook-utils.js\";\nexport { detectToolchain, readHookConfig } from \"./hook-utils.js\";\nexport type { PhaseVerification, PlanDiscovery } from \"./planning.js\";\nexport {\n\tcreatePlan,\n\tdiscoverPlans,\n\treadPlan,\n\tupdateState,\n\tverifyPhase,\n\twriteSummary,\n} from \"./planning.js\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gsd/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EACN,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClE,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EACN,UAAU,EACV,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,YAAY,GACZ,MAAM,eAAe,CAAC","sourcesContent":["// GSD index — re-exports all GSD module functions.\n// Import from @draht/coding-agent for use in extensions.\n\nexport type { MapCodebaseResult } from \"./domain.js\";\nexport { createDomainModel, mapCodebase } from \"./domain.js\";\nexport {\n\textractGlossaryTerms,\n\tloadDomainContent,\n\tvalidateDomainGlossary,\n} from \"./domain-validator.js\";\nexport type { CommitResult } from \"./git.js\";\nexport { commitDocs, commitTask, hasTestFiles } from \"./git.js\";\nexport type { HookConfig, ToolchainInfo } from \"./hook-utils.js\";\nexport { detectToolchain, readHookConfig } from \"./hook-utils.js\";\nexport type { PhaseVerification, PlanDiscovery } from \"./planning.js\";\nexport {\n\tcreatePlan,\n\tdiscoverPlans,\n\treadPlan,\n\tupdateState,\n\tverifyPhase,\n\twriteSummary,\n} from \"./planning.js\";\n"]}