@draht/coding-agent 2026.3.6 → 2026.3.14

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 (177) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +6 -2
  3. package/bin/draht-tools.cjs +187 -32
  4. package/dist/cli/args.d.ts +6 -0
  5. package/dist/cli/args.d.ts.map +1 -1
  6. package/dist/cli/args.js +24 -0
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/attach-mode.d.ts +13 -0
  9. package/dist/cli/attach-mode.d.ts.map +1 -0
  10. package/dist/cli/attach-mode.js +97 -0
  11. package/dist/cli/attach-mode.js.map +1 -0
  12. package/dist/cli/list-sessions.d.ts +8 -0
  13. package/dist/cli/list-sessions.d.ts.map +1 -0
  14. package/dist/cli/list-sessions.js +52 -0
  15. package/dist/cli/list-sessions.js.map +1 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +2 -2
  18. package/dist/config.js.map +1 -1
  19. package/dist/core/agent-session.d.ts +1 -0
  20. package/dist/core/agent-session.d.ts.map +1 -1
  21. package/dist/core/agent-session.js +50 -17
  22. package/dist/core/agent-session.js.map +1 -1
  23. package/dist/core/auth-storage.d.ts +2 -1
  24. package/dist/core/auth-storage.d.ts.map +1 -1
  25. package/dist/core/auth-storage.js +25 -1
  26. package/dist/core/auth-storage.js.map +1 -1
  27. package/dist/core/compaction/utils.d.ts +3 -0
  28. package/dist/core/compaction/utils.d.ts.map +1 -1
  29. package/dist/core/compaction/utils.js +16 -1
  30. package/dist/core/compaction/utils.js.map +1 -1
  31. package/dist/core/export-html/index.d.ts +5 -2
  32. package/dist/core/export-html/index.d.ts.map +1 -1
  33. package/dist/core/export-html/index.js +4 -3
  34. package/dist/core/export-html/index.js.map +1 -1
  35. package/dist/core/export-html/template.js +11 -14
  36. package/dist/core/export-html/tool-renderer.d.ts +5 -2
  37. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  38. package/dist/core/export-html/tool-renderer.js +12 -5
  39. package/dist/core/export-html/tool-renderer.js.map +1 -1
  40. package/dist/core/extensions/index.d.ts +1 -1
  41. package/dist/core/extensions/index.d.ts.map +1 -1
  42. package/dist/core/extensions/index.js.map +1 -1
  43. package/dist/core/extensions/loader.d.ts.map +1 -1
  44. package/dist/core/extensions/loader.js +6 -6
  45. package/dist/core/extensions/loader.js.map +1 -1
  46. package/dist/core/extensions/runner.d.ts +3 -2
  47. package/dist/core/extensions/runner.d.ts.map +1 -1
  48. package/dist/core/extensions/runner.js +32 -0
  49. package/dist/core/extensions/runner.js.map +1 -1
  50. package/dist/core/extensions/types.d.ts +21 -2
  51. package/dist/core/extensions/types.d.ts.map +1 -1
  52. package/dist/core/extensions/types.js.map +1 -1
  53. package/dist/core/model-resolver.d.ts.map +1 -1
  54. package/dist/core/model-resolver.js +2 -2
  55. package/dist/core/model-resolver.js.map +1 -1
  56. package/dist/core/package-manager.d.ts.map +1 -1
  57. package/dist/core/package-manager.js +2 -2
  58. package/dist/core/package-manager.js.map +1 -1
  59. package/dist/core/resource-loader.d.ts.map +1 -1
  60. package/dist/core/resource-loader.js +1 -1
  61. package/dist/core/resource-loader.js.map +1 -1
  62. package/dist/core/sdk.d.ts.map +1 -1
  63. package/dist/core/sdk.js +7 -0
  64. package/dist/core/sdk.js.map +1 -1
  65. package/dist/core/settings-manager.d.ts +4 -0
  66. package/dist/core/settings-manager.d.ts.map +1 -1
  67. package/dist/core/settings-manager.js +36 -2
  68. package/dist/core/settings-manager.js.map +1 -1
  69. package/dist/core/socket-server/discovery.d.ts +19 -0
  70. package/dist/core/socket-server/discovery.d.ts.map +1 -0
  71. package/dist/core/socket-server/discovery.js +91 -0
  72. package/dist/core/socket-server/discovery.js.map +1 -0
  73. package/dist/core/socket-server/index.d.ts +13 -0
  74. package/dist/core/socket-server/index.d.ts.map +1 -0
  75. package/dist/core/socket-server/index.js +11 -0
  76. package/dist/core/socket-server/index.js.map +1 -0
  77. package/dist/core/socket-server/session-integration.d.ts +17 -0
  78. package/dist/core/socket-server/session-integration.d.ts.map +1 -0
  79. package/dist/core/socket-server/session-integration.js +77 -0
  80. package/dist/core/socket-server/session-integration.js.map +1 -0
  81. package/dist/core/socket-server/socket-client.d.ts +65 -0
  82. package/dist/core/socket-server/socket-client.d.ts.map +1 -0
  83. package/dist/core/socket-server/socket-client.js +197 -0
  84. package/dist/core/socket-server/socket-client.js.map +1 -0
  85. package/dist/core/socket-server/socket-server.d.ts +60 -0
  86. package/dist/core/socket-server/socket-server.d.ts.map +1 -0
  87. package/dist/core/socket-server/socket-server.js +273 -0
  88. package/dist/core/socket-server/socket-server.js.map +1 -0
  89. package/dist/core/socket-server/types.d.ts +81 -0
  90. package/dist/core/socket-server/types.d.ts.map +1 -0
  91. package/dist/core/socket-server/types.js +8 -0
  92. package/dist/core/socket-server/types.js.map +1 -0
  93. package/dist/gsd/domain.d.ts +5 -1
  94. package/dist/gsd/domain.d.ts.map +1 -1
  95. package/dist/gsd/domain.js +71 -1
  96. package/dist/gsd/domain.js.map +1 -1
  97. package/dist/gsd/git.d.ts.map +1 -1
  98. package/dist/gsd/git.js +18 -0
  99. package/dist/gsd/git.js.map +1 -1
  100. package/dist/gsd/index.d.ts +1 -0
  101. package/dist/gsd/index.d.ts.map +1 -1
  102. package/dist/gsd/index.js.map +1 -1
  103. package/dist/index.d.ts +1 -1
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js.map +1 -1
  106. package/dist/main.d.ts.map +1 -1
  107. package/dist/main.js +76 -11
  108. package/dist/main.js.map +1 -1
  109. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  110. package/dist/modes/interactive/components/extension-editor.js +1 -0
  111. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  112. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  113. package/dist/modes/interactive/components/footer.js +8 -23
  114. package/dist/modes/interactive/components/footer.js.map +1 -1
  115. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  116. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  117. package/dist/modes/interactive/components/settings-selector.js +10 -0
  118. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  119. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  120. package/dist/modes/interactive/components/tool-execution.js +14 -4
  121. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  122. package/dist/modes/interactive/components/tree-selector.d.ts +21 -2
  123. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  124. package/dist/modes/interactive/components/tree-selector.js +115 -9
  125. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  126. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  127. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  128. package/dist/modes/interactive/interactive-mode.js +66 -5
  129. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  130. package/dist/modes/rpc/jsonl.d.ts +17 -0
  131. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  132. package/dist/modes/rpc/jsonl.js +49 -0
  133. package/dist/modes/rpc/jsonl.js.map +1 -0
  134. package/dist/modes/rpc/rpc-client.d.ts +1 -1
  135. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  136. package/dist/modes/rpc/rpc-client.js +7 -11
  137. package/dist/modes/rpc/rpc-client.js.map +1 -1
  138. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  139. package/dist/modes/rpc/rpc-mode.js +9 -11
  140. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  141. package/dist/prompts/commands/execute-phase.md +2 -2
  142. package/dist/prompts/commands/fix.md +2 -2
  143. package/dist/prompts/commands/plan-phase.md +5 -1
  144. package/dist/prompts/commands/quick.md +5 -1
  145. package/dist/utils/changelog.d.ts +12 -0
  146. package/dist/utils/changelog.d.ts.map +1 -1
  147. package/dist/utils/changelog.js +25 -14
  148. package/dist/utils/changelog.js.map +1 -1
  149. package/dist/utils/notify.d.ts +12 -0
  150. package/dist/utils/notify.d.ts.map +1 -0
  151. package/dist/utils/notify.js +41 -0
  152. package/dist/utils/notify.js.map +1 -0
  153. package/docs/compaction.md +2 -0
  154. package/docs/custom-provider.md +11 -7
  155. package/docs/extensions.md +55 -3
  156. package/docs/keybindings.md +9 -1
  157. package/docs/models.md +5 -1
  158. package/docs/rpc.md +40 -3
  159. package/docs/session.md +2 -2
  160. package/docs/settings.md +1 -0
  161. package/docs/terminal-setup.md +28 -3
  162. package/docs/tmux.md +61 -0
  163. package/docs/tree.md +9 -0
  164. package/examples/extensions/antigravity-image-gen.ts +5 -4
  165. package/examples/extensions/custom-provider-gitlab-duo/test.ts +2 -2
  166. package/examples/extensions/notify.ts +9 -2
  167. package/examples/extensions/overlay-qa-tests.ts +468 -1
  168. package/examples/extensions/preset.ts +2 -3
  169. package/examples/extensions/provider-payload.ts +14 -0
  170. package/examples/extensions/sandbox/index.ts +2 -3
  171. package/examples/extensions/tool-override.ts +2 -3
  172. package/examples/extensions/with-deps/index.ts +1 -5
  173. package/package.json +7 -5
  174. package/prompts/commands/execute-phase.md +2 -2
  175. package/prompts/commands/fix.md +2 -2
  176. package/prompts/commands/plan-phase.md +5 -1
  177. package/prompts/commands/quick.md +5 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,93 @@
1
1
  # Changelog
2
2
 
3
+ ## [2026.3.14] - 2026-03-14
4
+
5
+ ### Added
6
+
7
+ - built-in terminal notification on agent turn end
8
+
9
+ ### Changed
10
+
11
+ - fix(coding-agent): use getAgentDir() in example extensions (#2009)
12
+ - Export the structured mapping contract through the public GSD entrypoint
13
+ - Add typed extraction results to GSD domain mapping
14
+ - consolidate quality gate fixture helpers
15
+ - Cover strict-mode TypeScript error detection
16
+ - add TypeScript-error quality gate scenario
17
+ - Cover passing and failing test-suite outcomes
18
+ - add failing test-suite quality gate scenario
19
+ - Create a real-hook quality gate test harness
20
+ - add real quality gate hook harness test
21
+ - verify phase 21 lifecycle execution logs
22
+ - add failing phase 21 execution log verification test
23
+ - exercise phase 21 lifecycle task commit flow
24
+ - add failing phase 21 lifecycle commit flow test
25
+ - define mapCodebase structured extraction contract
26
+ - build phase 21 lifecycle workspace fixture
27
+ - add failing phase 21 lifecycle fixture test
28
+ - reduce duplicated domain fixture assertions
29
+ - Create a stable domain fixture codebase
30
+ - replace gsd structure checks with runtime loading coverage
31
+ - specify stable GSD domain fixture exports
32
+ - add gsd extension runtime loading spec
33
+ - extract reusable temp git repo setup steps
34
+ - Implement the shared git repo helper
35
+ - specify isolated temp git repo helper behavior
36
+
37
+ ### Fixed
38
+
39
+ - add stdin support to all draht-tools write commands
40
+ - add cmux support to notify example extension
41
+ - add stdin support to create-quick-plan and fix plan prompts to pipe actual content
42
+ - use conventional commit prefixes instead of TDD annotations in prompt templates
43
+ - restore phase 21 verification baseline
44
+ - The draht-tools create-plan command now reads stdin content when piped
45
+ - handle date-based version suffixes in changelog parsing
46
+ - change default update instruction to bun add -g
47
+
48
+ ## [2026.3.11] - 2026-03-11
49
+
50
+ ### Added
51
+
52
+ - add fold/unfold to tree branch navigation
53
+ - refine session_directory hook closes #1729
54
+ - add session_directory extension event
55
+ - add provider payload hook
56
+ - preserve custom editor onEscape/onCtrlD handlers
57
+ - add treeFilterMode setting for /tree default filter (#1852)
58
+ - add experimental attachable sessions
59
+
60
+ ### Changed
61
+
62
+ - fix upstream branding in cherry-picked files
63
+ - explain Windows Terminal Alt+Enter remap (#1967)
64
+ - add codex tool-loop cache probe
65
+ - document tool result truncation in compaction serialization
66
+ - clarify that tool errors must be thrown, not returned
67
+ - clarify models.json name behavior (fixes #1840)
68
+ - update session path from ~/.pi/ to ~/.draht/
69
+
70
+ ### Fixed
71
+
72
+ - add missing @sinclair/typebox and ajv dependencies
73
+ - update Anthropic compaction model and HTTPS package test (#1960)
74
+ - use shell for external editor on windows closes #1925
75
+ - prefer explicit -e extensions closes #1896
76
+ - handle tmux xterm extended keys and warn on tmux setup fixes #1872
77
+ - custom tool collapsed/expanded rendering in HTML export (#1934)
78
+ - use strict JSONL framing fixes #1911
79
+ - keep ~/.agents skills user-scoped\n\ncloses #1915
80
+ - guard against stale kept pre-compaction usage in error-path threshold check
81
+ - truncate tool results in compaction summarization to prevent overflow, fixes #1796
82
+ - allow threshold compaction for error messages using last successful usage, fixes #1834
83
+ - retry sync lockfile acquisition to prevent false auth errors during parallel startup
84
+ - preserve thinking defaults across model switches closes #1864
85
+ - clear header on /new closes #1880
86
+ - prefer workspace dist files for extension aliases
87
+ - normalize CRLF in write preview rendering (fixes #1854)
88
+ - make footer truncation width-aware
89
+ - use ESM resolution for extension alias fallback (#1821)
90
+
3
91
  ## [2026.3.6] - 2026-03-06
4
92
 
5
93
  ### Added
package/README.md CHANGED
@@ -59,7 +59,7 @@ draht
59
59
 
60
60
  Then just talk to draht. By default, draht gives the model four tools: `read`, `write`, `edit`, and `bash`. The model uses these to fulfill your requests. Add capabilities via [skills](#skills), [prompt templates](#prompt-templates), [extensions](#extensions), or [draht packages](#draht-packages).
61
61
 
62
- **Platform notes:** [Windows](docs/windows.md) | [Termux (Android)](docs/termux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md)
62
+ **Platform notes:** [Windows](docs/windows.md) | [Termux (Android)](docs/termux.md) | [tmux](docs/tmux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md)
63
63
 
64
64
  ---
65
65
 
@@ -177,6 +177,8 @@ Submit messages while the agent is working:
177
177
  - **Escape** aborts and restores queued messages to editor
178
178
  - **Alt+Up** retrieves queued messages back to editor
179
179
 
180
+ On Windows Terminal, `Alt+Enter` is fullscreen by default. Remap it in [docs/terminal-setup.md](docs/terminal-setup.md) so pi can receive the follow-up shortcut.
181
+
180
182
  Configure delivery in [settings](docs/settings.md): `steeringMode` and `followUpMode` can be `"one-at-a-time"` (default, waits for response) or `"all"` (delivers all queued at once). `transport` selects provider transport preference (`"sse"`, `"websocket"`, or `"auto"`) for providers that support multiple transports.
181
183
 
182
184
  ---
@@ -202,7 +204,7 @@ draht --session <path> # Use specific session file or ID
202
204
 
203
205
  <p align="center"><img src="docs/images/tree-view.png" alt="Tree View" width="600"></p>
204
206
 
205
- - Search by typing, page with ←/→
207
+ - Search by typing, fold/unfold and jump between branches with Ctrl+←/Ctrl+→ or Alt+←/Alt+→, page with ←/→
206
208
  - Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all
207
209
  - Press `l` to label entries as bookmarks
208
210
 
@@ -386,6 +388,8 @@ For non-Node.js integrations, use RPC mode over stdin/stdout:
386
388
  draht --mode rpc
387
389
  ```
388
390
 
391
+ RPC mode uses strict LF-delimited JSONL framing. Clients must split records on `\n` only. Do not use generic line readers like Node `readline`, which also split on Unicode separators inside JSON payloads.
392
+
389
393
  See [docs/rpc.md](docs/rpc.md) for the protocol.
390
394
 
391
395
  ---
@@ -305,7 +305,8 @@ commands["phase-info"] = function (n) {
305
305
  };
306
306
 
307
307
  // --- save-context ---
308
- commands["save-context"] = function (n, ...rest) {
308
+ // Supports stdin: echo "content" | draht-tools save-context N
309
+ commands["save-context"] = async function (n, ...rest) {
309
310
  const num = parseInt(n, 10);
310
311
  if (!num) { console.error("Usage: draht-tools save-context N"); process.exit(1); }
311
312
 
@@ -314,7 +315,22 @@ commands["save-context"] = function (n, ...rest) {
314
315
  ensureDir(dir);
315
316
 
316
317
  const contextPath = path.join(dir, `${padNum(num)}-CONTEXT.md`);
317
- if (fs.existsSync(contextPath)) {
318
+
319
+ // Check for stdin content (piped input)
320
+ let stdinContent = "";
321
+ if (!process.stdin.isTTY) {
322
+ stdinContent = await new Promise((resolve) => {
323
+ let data = "";
324
+ process.stdin.setEncoding("utf-8");
325
+ process.stdin.on("data", (chunk) => { data += chunk; });
326
+ process.stdin.on("end", () => { resolve(data.trim()); });
327
+ });
328
+ }
329
+
330
+ if (stdinContent) {
331
+ writeMd(contextPath, stdinContent);
332
+ console.log(`Created: ${contextPath}`);
333
+ } else if (fs.existsSync(contextPath)) {
318
334
  console.log(`Context already exists at ${contextPath}`);
319
335
  console.log("Edit it directly or pass content via stdin.");
320
336
  } else {
@@ -372,7 +388,8 @@ commands["load-phase-context"] = function (n) {
372
388
  };
373
389
 
374
390
  // --- create-plan ---
375
- commands["create-plan"] = function (n, p, ...titleWords) {
391
+ // Supports stdin: echo "content" | draht-tools create-plan N P [title]
392
+ commands["create-plan"] = async function (n, p, ...titleWords) {
376
393
  const phaseNum = parseInt(n, 10);
377
394
  const planNum = parseInt(p, 10);
378
395
  if (!phaseNum || !planNum) { console.error("Usage: draht-tools create-plan N P [title]"); process.exit(1); }
@@ -384,7 +401,24 @@ commands["create-plan"] = function (n, p, ...titleWords) {
384
401
  const title = titleWords.join(" ") || `Plan ${planNum}`;
385
402
  const planPath = path.join(dir, `${padNum(phaseNum)}-${padNum(planNum)}-PLAN.md`);
386
403
 
387
- const tmpl = `---
404
+ // Check for stdin content (piped input)
405
+ let stdinContent = "";
406
+ if (!process.stdin.isTTY) {
407
+ stdinContent = await new Promise((resolve) => {
408
+ let data = "";
409
+ process.stdin.setEncoding("utf-8");
410
+ process.stdin.on("data", (chunk) => { data += chunk; });
411
+ process.stdin.on("end", () => { resolve(data.trim()); });
412
+ });
413
+ }
414
+
415
+ let content;
416
+ if (stdinContent) {
417
+ // Use stdin content directly
418
+ content = stdinContent;
419
+ } else {
420
+ // Generate template
421
+ content = `---
388
422
  phase: ${phaseNum}
389
423
  plan: ${planNum}
390
424
  depends_on: []
@@ -417,7 +451,9 @@ must_haves:
417
451
  ---
418
452
  Created: ${timestamp()}
419
453
  `;
420
- writeMd(planPath, tmpl);
454
+ }
455
+
456
+ writeMd(planPath, content);
421
457
  console.log(`Created: ${planPath}`);
422
458
  };
423
459
 
@@ -547,7 +583,8 @@ commands["commit-task"] = function (n, p, t, ...desc) {
547
583
  };
548
584
 
549
585
  // --- write-summary ---
550
- commands["write-summary"] = function (n, p) {
586
+ // Supports stdin: echo "content" | draht-tools write-summary N P
587
+ commands["write-summary"] = async function (n, p) {
551
588
  const phaseNum = parseInt(n, 10);
552
589
  const planNum = parseInt(p, 10);
553
590
  if (!phaseNum || !planNum) { console.error("Usage: draht-tools write-summary N P"); process.exit(1); }
@@ -556,8 +593,26 @@ commands["write-summary"] = function (n, p) {
556
593
  if (!phaseDir) { console.error(`Phase ${phaseNum} not found`); process.exit(1); }
557
594
 
558
595
  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);
596
+
597
+ // Check for stdin content (piped input)
598
+ let stdinContent = "";
599
+ if (!process.stdin.isTTY) {
600
+ stdinContent = await new Promise((resolve) => {
601
+ let data = "";
602
+ process.stdin.setEncoding("utf-8");
603
+ process.stdin.on("data", (chunk) => { data += chunk; });
604
+ process.stdin.on("end", () => { resolve(data.trim()); });
605
+ });
606
+ }
607
+
608
+ let content;
609
+ if (stdinContent) {
610
+ content = stdinContent;
611
+ } else {
612
+ content = `# 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`;
613
+ }
614
+
615
+ writeMd(summaryPath, content);
561
616
  console.log(`Created: ${summaryPath}`);
562
617
  };
563
618
 
@@ -628,17 +683,35 @@ commands["extract-deliverables"] = function (n) {
628
683
  };
629
684
 
630
685
  // --- create-fix-plan ---
631
- commands["create-fix-plan"] = function (n, p, ...issueWords) {
686
+ // Supports stdin: echo "content" | draht-tools create-fix-plan N P [issue]
687
+ commands["create-fix-plan"] = async function (n, p, ...issueWords) {
632
688
  const phaseNum = parseInt(n, 10);
633
689
  const planNum = parseInt(p, 10);
634
690
  if (!phaseNum || !planNum) { console.error("Usage: draht-tools create-fix-plan N P [issue]"); process.exit(1); }
635
691
 
636
692
  const slug = getPhaseSlug(phaseNum) || `phase-${phaseNum}`;
637
693
  const dir = planningPath("phases", `${padNum(phaseNum)}-${slug}`);
694
+ ensureDir(dir);
638
695
  const issue = issueWords.join(" ") || "Fix identified issues";
639
696
 
640
697
  const fixPath = path.join(dir, `${padNum(phaseNum)}-${padNum(planNum)}-FIX-PLAN.md`);
641
- const tmpl = `---
698
+
699
+ // Check for stdin content (piped input)
700
+ let stdinContent = "";
701
+ if (!process.stdin.isTTY) {
702
+ stdinContent = await new Promise((resolve) => {
703
+ let data = "";
704
+ process.stdin.setEncoding("utf-8");
705
+ process.stdin.on("data", (chunk) => { data += chunk; });
706
+ process.stdin.on("end", () => { resolve(data.trim()); });
707
+ });
708
+ }
709
+
710
+ let content;
711
+ if (stdinContent) {
712
+ content = stdinContent;
713
+ } else {
714
+ content = `---
642
715
  gap_closure: true
643
716
  fixes_plan: ${planNum}
644
717
  issue: "${issue}"
@@ -662,12 +735,15 @@ ${issue}
662
735
  ---
663
736
  Created: ${timestamp()}
664
737
  `;
665
- writeMd(fixPath, tmpl);
738
+ }
739
+
740
+ writeMd(fixPath, content);
666
741
  console.log(`Created: ${fixPath}`);
667
742
  };
668
743
 
669
744
  // --- write-uat ---
670
- commands["write-uat"] = function (n) {
745
+ // Supports stdin: echo "content" | draht-tools write-uat N
746
+ commands["write-uat"] = async function (n) {
671
747
  const num = parseInt(n, 10);
672
748
  if (!num) { console.error("Usage: draht-tools write-uat N"); process.exit(1); }
673
749
 
@@ -675,8 +751,26 @@ commands["write-uat"] = function (n) {
675
751
  if (!phaseDir) { console.error(`Phase ${num} not found`); process.exit(1); }
676
752
 
677
753
  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);
754
+
755
+ // Check for stdin content (piped input)
756
+ let stdinContent = "";
757
+ if (!process.stdin.isTTY) {
758
+ stdinContent = await new Promise((resolve) => {
759
+ let data = "";
760
+ process.stdin.setEncoding("utf-8");
761
+ process.stdin.on("data", (chunk) => { data += chunk; });
762
+ process.stdin.on("end", () => { resolve(data.trim()); });
763
+ });
764
+ }
765
+
766
+ let content;
767
+ if (stdinContent) {
768
+ content = stdinContent;
769
+ } else {
770
+ content = `# 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`;
771
+ }
772
+
773
+ writeMd(uatPath, content);
680
774
  console.log(`Created: ${uatPath}`);
681
775
  };
682
776
 
@@ -691,7 +785,8 @@ commands["next-quick-number"] = function () {
691
785
  };
692
786
 
693
787
  // --- create-quick-plan ---
694
- commands["create-quick-plan"] = function (n, ...descWords) {
788
+ // Supports stdin: echo "content" | draht-tools create-quick-plan NNN [desc]
789
+ commands["create-quick-plan"] = async function (n, ...descWords) {
695
790
  const num = padNum(parseInt(n, 10), 3);
696
791
  const desc = descWords.join(" ") || "Quick task";
697
792
  const slug = slugify(desc);
@@ -699,13 +794,34 @@ commands["create-quick-plan"] = function (n, ...descWords) {
699
794
  ensureDir(dir);
700
795
 
701
796
  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);
797
+
798
+ // Check for stdin content (piped input)
799
+ let stdinContent = "";
800
+ if (!process.stdin.isTTY) {
801
+ stdinContent = await new Promise((resolve) => {
802
+ let data = "";
803
+ process.stdin.setEncoding("utf-8");
804
+ process.stdin.on("data", (chunk) => { data += chunk; });
805
+ process.stdin.on("end", () => { resolve(data.trim()); });
806
+ });
807
+ }
808
+
809
+ let content;
810
+ if (stdinContent) {
811
+ // Use stdin content directly
812
+ content = stdinContent;
813
+ } else {
814
+ // Generate template
815
+ content = `# 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`;
816
+ }
817
+
818
+ writeMd(planPath, content);
704
819
  console.log(`Created: ${planPath}`);
705
820
  };
706
821
 
707
822
  // --- write-quick-summary ---
708
- commands["write-quick-summary"] = function (n) {
823
+ // Supports stdin: echo "content" | draht-tools write-quick-summary NNN
824
+ commands["write-quick-summary"] = async function (n) {
709
825
  const num = padNum(parseInt(n, 10), 3);
710
826
  const dir = planningPath("quick");
711
827
  if (!fs.existsSync(dir)) { console.error("No quick tasks directory"); process.exit(1); }
@@ -713,8 +829,26 @@ commands["write-quick-summary"] = function (n) {
713
829
  if (!match) { console.error(`Quick task ${num} not found`); process.exit(1); }
714
830
 
715
831
  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);
832
+
833
+ // Check for stdin content (piped input)
834
+ let stdinContent = "";
835
+ if (!process.stdin.isTTY) {
836
+ stdinContent = await new Promise((resolve) => {
837
+ let data = "";
838
+ process.stdin.setEncoding("utf-8");
839
+ process.stdin.on("data", (chunk) => { data += chunk; });
840
+ process.stdin.on("end", () => { resolve(data.trim()); });
841
+ });
842
+ }
843
+
844
+ let content;
845
+ if (stdinContent) {
846
+ content = stdinContent;
847
+ } else {
848
+ content = `# 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`;
849
+ }
850
+
851
+ writeMd(summaryPath, content);
718
852
  console.log(`Created: ${summaryPath}`);
719
853
  };
720
854
 
@@ -826,7 +960,8 @@ commands["commit-docs"] = function (...msg) {
826
960
  };
827
961
 
828
962
  // --- research-phase ---
829
- commands["research-phase"] = function (n) {
963
+ // Supports stdin: echo "content" | draht-tools research-phase N
964
+ commands["research-phase"] = async function (n) {
830
965
  const num = parseInt(n, 10);
831
966
  if (!num) { console.error("Usage: draht-tools research-phase N"); process.exit(1); }
832
967
 
@@ -835,10 +970,28 @@ commands["research-phase"] = function (n) {
835
970
  ensureDir(dir);
836
971
 
837
972
  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);
973
+
974
+ // Check for stdin content (piped input)
975
+ let stdinContent = "";
976
+ if (!process.stdin.isTTY) {
977
+ stdinContent = await new Promise((resolve) => {
978
+ let data = "";
979
+ process.stdin.setEncoding("utf-8");
980
+ process.stdin.on("data", (chunk) => { data += chunk; });
981
+ process.stdin.on("end", () => { resolve(data.trim()); });
982
+ });
983
+ }
984
+
985
+ let content;
986
+ if (stdinContent) {
987
+ content = stdinContent;
988
+ } else {
989
+ content = `# 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`;
990
+ }
991
+
992
+ writeMd(resPath, content);
840
993
  console.log(`Created: ${resPath}`);
841
- console.log("→ Fill in research findings, then plan the phase.");
994
+ if (!stdinContent) console.log("→ Fill in research findings, then plan the phase.");
842
995
  };
843
996
 
844
997
  // ============================================================================
@@ -902,11 +1055,13 @@ Version: 1.0.0
902
1055
  // Dispatch
903
1056
  const [cmd, ...args] = process.argv.slice(2);
904
1057
 
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-tools help`);
911
- process.exit(1);
912
- }
1058
+ (async () => {
1059
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
1060
+ commands.help();
1061
+ } else if (commands[cmd]) {
1062
+ await commands[cmd](...args);
1063
+ } else {
1064
+ console.error(`Unknown command: ${cmd}\nRun: draht-tools help`);
1065
+ process.exit(1);
1066
+ }
1067
+ })();
@@ -35,6 +35,12 @@ export interface Args {
35
35
  listModels?: string | true;
36
36
  offline?: boolean;
37
37
  verbose?: boolean;
38
+ /** Experimental: Start session with socket server for multi-attach */
39
+ attachable?: boolean;
40
+ /** Experimental: Attach to an existing socket session */
41
+ attach?: string;
42
+ /** Experimental: List all attachable socket sessions */
43
+ listSessions?: boolean;
38
44
  messages: string[];
39
45
  fileArgs: string[];
40
46
  /** Unknown flags (potentially extension flags) - map of flag name to value */
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;CAC5C;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC,GAAG,IAAI,CA0H5G;AAED,wBAAgB,SAAS,IAAI,IAAI,CAyIhC","sourcesContent":["/**\n * CLI argument parsing and help display\n */\n\nimport type { ThinkingLevel } from \"@draht/agent-core\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR } from \"../config.js\";\nimport { allTools, type ToolName } from \"../core/tools/index.js\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string;\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: ToolName[];\n\tnoTools?: boolean;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[], extensionFlags?: Map<string, { type: \"boolean\" | \"string\" }>): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = args[++i];\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--tools\" && i + 1 < args.length) {\n\t\t\tconst toolNames = args[++i].split(\",\").map((s) => s.trim());\n\t\t\tconst validTools: ToolName[] = [];\n\t\t\tfor (const name of toolNames) {\n\t\t\t\tif (name in allTools) {\n\t\t\t\t\tvalidTools.push(name as ToolName);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tchalk.yellow(`Warning: Unknown tool \"${name}\". Valid tools: ${Object.keys(allTools).join(\", \")}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult.tools = validTools;\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.yellow(\n\t\t\t\t\t\t`Warning: Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\") && extensionFlags) {\n\t\t\t// Check if it's an extension-registered flag\n\t\t\tconst flagName = arg.slice(2);\n\t\t\tconst extFlag = extensionFlags.get(flagName);\n\t\t\tif (extFlag) {\n\t\t\t\tif (extFlag.type === \"boolean\") {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t} else if (extFlag.type === \"string\" && i + 1 < args.length) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, args[++i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Unknown flags without extensionFlags are silently ignored (first pass)\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(): void {\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] Remove extension source from settings\n ${APP_NAME} update [source] Update installed extensions (skips pinned sources)\n ${APP_NAME} list List installed extensions from settings\n ${APP_NAME} config Open TUI to enable/disable package resources\n ${APP_NAME} <command> --help Show help for install/remove/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path> Use specific session file\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools Disable all built-in tools\n --tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)\n Available: read, bash, edit, write, grep, find, ls\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --offline Disable startup network operations (same as DRAHT_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI base URL (https://{resource}.openai.azure.com/openai/v1)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n AWS_PROFILE - AWS profile for Amazon Bedrock\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)\n DRAHT_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n DRAHT_OFFLINE - Disable startup network operations when set to 1/true/yes\n DRAHT_SHARE_VIEWER_URL - Base URL for /share command (default: https://draht.dev/session/)\n DRAHT_AI_ANTIGRAVITY_VERSION - Override Antigravity User-Agent version (e.g., 1.23.0)\n\n${chalk.bold(\"Available Tools (default: read, bash, edit, write):\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;CAC5C;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC,GAAG,IAAI,CAgI5G;AAED,wBAAgB,SAAS,IAAI,IAAI,CAwJhC","sourcesContent":["/**\n * CLI argument parsing and help display\n */\n\nimport type { ThinkingLevel } from \"@draht/agent-core\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR } from \"../config.js\";\nimport { allTools, type ToolName } from \"../core/tools/index.js\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string;\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: ToolName[];\n\tnoTools?: boolean;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\t/** Experimental: Start session with socket server for multi-attach */\n\tattachable?: boolean;\n\t/** Experimental: Attach to an existing socket session */\n\tattach?: string;\n\t/** Experimental: List all attachable socket sessions */\n\tlistSessions?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[], extensionFlags?: Map<string, { type: \"boolean\" | \"string\" }>): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = args[++i];\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--tools\" && i + 1 < args.length) {\n\t\t\tconst toolNames = args[++i].split(\",\").map((s) => s.trim());\n\t\t\tconst validTools: ToolName[] = [];\n\t\t\tfor (const name of toolNames) {\n\t\t\t\tif (name in allTools) {\n\t\t\t\t\tvalidTools.push(name as ToolName);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tchalk.yellow(`Warning: Unknown tool \"${name}\". Valid tools: ${Object.keys(allTools).join(\", \")}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult.tools = validTools;\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.yellow(\n\t\t\t\t\t\t`Warning: Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg === \"--attachable\") {\n\t\t\tresult.attachable = true;\n\t\t} else if (arg === \"--attach\" && i + 1 < args.length) {\n\t\t\tresult.attach = args[++i];\n\t\t} else if (arg === \"--list-sessions\") {\n\t\t\tresult.listSessions = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\") && extensionFlags) {\n\t\t\t// Check if it's an extension-registered flag\n\t\t\tconst flagName = arg.slice(2);\n\t\t\tconst extFlag = extensionFlags.get(flagName);\n\t\t\tif (extFlag) {\n\t\t\t\tif (extFlag.type === \"boolean\") {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t} else if (extFlag.type === \"string\" && i + 1 < args.length) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, args[++i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Unknown flags without extensionFlags are silently ignored (first pass)\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(): void {\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] Remove extension source from settings\n ${APP_NAME} update [source] Update installed extensions (skips pinned sources)\n ${APP_NAME} list List installed extensions from settings\n ${APP_NAME} config Open TUI to enable/disable package resources\n ${APP_NAME} <command> --help Show help for install/remove/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path> Use specific session file\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools Disable all built-in tools\n --tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)\n Available: read, bash, edit, write, grep, find, ls\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --offline Disable startup network operations (same as DRAHT_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\n${chalk.bold(\"Experimental - Attachable Sessions (tmux-style multi-attach):\")}\n --attachable Start session with socket server for multi-client attachment\n --attach <session-id> Attach to an existing socket-based session\n --list-sessions List all running attachable sessions\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n # ${chalk.bold(\"Experimental - Attachable Sessions\")}\n # Start an attachable session (others can attach via --attach)\n ${APP_NAME} --attachable \"Build a new feature\"\n\n # List all running attachable sessions\n ${APP_NAME} --list-sessions\n\n # Attach to a running session (tmux-style surveillance)\n ${APP_NAME} --attach abc-123-session-id\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI base URL (https://{resource}.openai.azure.com/openai/v1)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n AWS_PROFILE - AWS profile for Amazon Bedrock\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)\n DRAHT_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n DRAHT_OFFLINE - Disable startup network operations when set to 1/true/yes\n DRAHT_SHARE_VIEWER_URL - Base URL for /share command (default: https://draht.dev/session/)\n DRAHT_AI_ANTIGRAVITY_VERSION - Override Antigravity User-Agent version (e.g., 1.23.0)\n\n${chalk.bold(\"Available Tools (default: read, bash, edit, write):\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
package/dist/cli/args.js CHANGED
@@ -135,6 +135,15 @@ export function parseArgs(args, extensionFlags) {
135
135
  else if (arg === "--offline") {
136
136
  result.offline = true;
137
137
  }
138
+ else if (arg === "--attachable") {
139
+ result.attachable = true;
140
+ }
141
+ else if (arg === "--attach" && i + 1 < args.length) {
142
+ result.attach = args[++i];
143
+ }
144
+ else if (arg === "--list-sessions") {
145
+ result.listSessions = true;
146
+ }
138
147
  else if (arg.startsWith("@")) {
139
148
  result.fileArgs.push(arg.slice(1)); // Remove @ prefix
140
149
  }
@@ -206,6 +215,11 @@ ${chalk.bold("Options:")}
206
215
  --help, -h Show this help
207
216
  --version, -v Show version number
208
217
 
218
+ ${chalk.bold("Experimental - Attachable Sessions (tmux-style multi-attach):")}
219
+ --attachable Start session with socket server for multi-client attachment
220
+ --attach <session-id> Attach to an existing socket-based session
221
+ --list-sessions List all running attachable sessions
222
+
209
223
  Extensions can register additional flags (e.g., --plan from plan-mode extension).
210
224
 
211
225
  ${chalk.bold("Examples:")}
@@ -255,6 +269,16 @@ ${chalk.bold("Examples:")}
255
269
  ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl
256
270
  ${APP_NAME} --export session.jsonl output.html
257
271
 
272
+ # ${chalk.bold("Experimental - Attachable Sessions")}
273
+ # Start an attachable session (others can attach via --attach)
274
+ ${APP_NAME} --attachable "Build a new feature"
275
+
276
+ # List all running attachable sessions
277
+ ${APP_NAME} --list-sessions
278
+
279
+ # Attach to a running session (tmux-style surveillance)
280
+ ${APP_NAME} --attach abc-123-session-id
281
+
258
282
  ${chalk.bold("Environment Variables:")}
259
283
  ANTHROPIC_API_KEY - Anthropic Claude API key
260
284
  ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)