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