@draht/coding-agent 2026.3.2 → 2026.3.4

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 (194) hide show
  1. package/CHANGELOG.md +194 -13
  2. package/README.md +90 -106
  3. package/agents/architect.md +45 -0
  4. package/agents/debugger.md +57 -0
  5. package/agents/git-committer.md +46 -0
  6. package/agents/implementer.md +25 -0
  7. package/agents/reviewer.md +52 -0
  8. package/agents/security-auditor.md +61 -0
  9. package/agents/verifier.md +44 -0
  10. package/bin/draht-tools.cjs +20 -20
  11. package/dist/agents/architect.md +45 -0
  12. package/dist/agents/debugger.md +57 -0
  13. package/dist/agents/git-committer.md +46 -0
  14. package/dist/agents/implementer.md +25 -0
  15. package/dist/agents/reviewer.md +52 -0
  16. package/dist/agents/security-auditor.md +61 -0
  17. package/dist/agents/verifier.md +44 -0
  18. package/dist/cli/args.d.ts.map +1 -1
  19. package/dist/cli/args.js +1 -0
  20. package/dist/cli/args.js.map +1 -1
  21. package/dist/cli.d.ts.map +1 -1
  22. package/dist/cli.js +5 -0
  23. package/dist/cli.js.map +1 -1
  24. package/dist/config.d.ts +0 -7
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +2 -14
  27. package/dist/config.js.map +1 -1
  28. package/dist/core/agent-session.d.ts +14 -4
  29. package/dist/core/agent-session.d.ts.map +1 -1
  30. package/dist/core/agent-session.js +167 -49
  31. package/dist/core/agent-session.js.map +1 -1
  32. package/dist/core/auth-storage.d.ts +1 -1
  33. package/dist/core/auth-storage.d.ts.map +1 -1
  34. package/dist/core/auth-storage.js +2 -1
  35. package/dist/core/auth-storage.js.map +1 -1
  36. package/dist/core/builtins/subagent.d.ts +14 -0
  37. package/dist/core/builtins/subagent.d.ts.map +1 -0
  38. package/dist/core/builtins/subagent.js +492 -0
  39. package/dist/core/builtins/subagent.js.map +1 -0
  40. package/dist/core/compaction/compaction.d.ts.map +1 -1
  41. package/dist/core/compaction/compaction.js +4 -1
  42. package/dist/core/compaction/compaction.js.map +1 -1
  43. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  44. package/dist/core/export-html/tool-renderer.js +6 -0
  45. package/dist/core/export-html/tool-renderer.js.map +1 -1
  46. package/dist/core/extensions/loader.d.ts.map +1 -1
  47. package/dist/core/extensions/loader.js +19 -8
  48. package/dist/core/extensions/loader.js.map +1 -1
  49. package/dist/core/extensions/runner.d.ts.map +1 -1
  50. package/dist/core/extensions/runner.js +1 -0
  51. package/dist/core/extensions/runner.js.map +1 -1
  52. package/dist/core/extensions/types.d.ts +8 -2
  53. package/dist/core/extensions/types.d.ts.map +1 -1
  54. package/dist/core/extensions/types.js.map +1 -1
  55. package/dist/core/model-registry.d.ts +1 -0
  56. package/dist/core/model-registry.d.ts.map +1 -1
  57. package/dist/core/model-registry.js +9 -6
  58. package/dist/core/model-registry.js.map +1 -1
  59. package/dist/core/model-resolver.d.ts.map +1 -1
  60. package/dist/core/model-resolver.js +35 -5
  61. package/dist/core/model-resolver.js.map +1 -1
  62. package/dist/core/prompt-templates.js.map +1 -1
  63. package/dist/core/resource-loader.d.ts +2 -0
  64. package/dist/core/resource-loader.d.ts.map +1 -1
  65. package/dist/core/resource-loader.js +5 -1
  66. package/dist/core/resource-loader.js.map +1 -1
  67. package/dist/core/sdk.d.ts +1 -1
  68. package/dist/core/sdk.d.ts.map +1 -1
  69. package/dist/core/sdk.js.map +1 -1
  70. package/dist/core/session-manager.js.map +1 -1
  71. package/dist/core/settings-manager.d.ts +3 -0
  72. package/dist/core/settings-manager.d.ts.map +1 -1
  73. package/dist/core/settings-manager.js +4 -0
  74. package/dist/core/settings-manager.js.map +1 -1
  75. package/dist/core/system-prompt.d.ts +4 -0
  76. package/dist/core/system-prompt.d.ts.map +1 -1
  77. package/dist/core/system-prompt.js +34 -12
  78. package/dist/core/system-prompt.js.map +1 -1
  79. package/dist/core/tools/edit-diff.js.map +1 -1
  80. package/dist/core/tools/path-utils.js.map +1 -1
  81. package/dist/index.d.ts +1 -1
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +1 -1
  84. package/dist/index.js.map +1 -1
  85. package/dist/main.d.ts.map +1 -1
  86. package/dist/main.js +5 -5
  87. package/dist/main.js.map +1 -1
  88. package/dist/migrations.d.ts +1 -1
  89. package/dist/migrations.d.ts.map +1 -1
  90. package/dist/migrations.js +3 -3
  91. package/dist/migrations.js.map +1 -1
  92. package/dist/modes/interactive/components/armin.js.map +1 -1
  93. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  94. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  95. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  96. package/dist/modes/interactive/components/extension-editor.d.ts +5 -2
  97. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  98. package/dist/modes/interactive/components/extension-editor.js +9 -1
  99. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  100. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  101. package/dist/modes/interactive/components/footer.js.map +1 -1
  102. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/login-dialog.js +1 -1
  104. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  105. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  106. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  108. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  109. package/dist/modes/interactive/components/oauth-selector.js +1 -1
  110. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  111. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  112. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  113. package/dist/modes/interactive/components/session-selector.js +1 -1
  114. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  115. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  116. package/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  117. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  118. package/dist/modes/interactive/components/tool-execution.js +28 -3
  119. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  120. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  121. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  122. package/dist/modes/interactive/components/user-message.d.ts +1 -0
  123. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  124. package/dist/modes/interactive/components/user-message.js +11 -0
  125. package/dist/modes/interactive/components/user-message.js.map +1 -1
  126. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  127. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  128. package/dist/modes/interactive/interactive-mode.js +28 -27
  129. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  130. package/dist/modes/interactive/theme/dark.json +1 -1
  131. package/dist/modes/interactive/theme/light.json +1 -1
  132. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  133. package/dist/modes/interactive/theme/theme.js +5 -0
  134. package/dist/modes/interactive/theme/theme.js.map +1 -1
  135. package/dist/prompts/commands/discuss-phase.md +3 -3
  136. package/dist/prompts/commands/execute-phase.md +9 -9
  137. package/dist/prompts/commands/fix.md +29 -0
  138. package/dist/prompts/commands/init-project.md +49 -0
  139. package/dist/prompts/commands/map-codebase.md +2 -2
  140. package/dist/prompts/commands/new-project.md +9 -9
  141. package/dist/prompts/commands/next-milestone.md +44 -0
  142. package/dist/prompts/commands/pause-work.md +2 -2
  143. package/dist/prompts/commands/plan-phase.md +5 -5
  144. package/dist/prompts/commands/progress.md +1 -1
  145. package/dist/prompts/commands/quick.md +4 -4
  146. package/dist/prompts/commands/resume-work.md +1 -1
  147. package/dist/prompts/commands/review.md +26 -0
  148. package/dist/prompts/commands/verify-work.md +4 -4
  149. package/docs/compaction.md +14 -14
  150. package/docs/custom-provider.md +19 -11
  151. package/docs/development.md +1 -1
  152. package/docs/extensions.md +52 -33
  153. package/docs/json.md +4 -4
  154. package/docs/packages.md +1 -1
  155. package/docs/providers.md +4 -2
  156. package/docs/rpc.md +1 -1
  157. package/docs/sdk.md +24 -24
  158. package/docs/session.md +6 -6
  159. package/docs/settings.md +1 -0
  160. package/docs/termux.md +1 -1
  161. package/docs/themes.md +2 -2
  162. package/docs/tui.md +20 -20
  163. package/examples/extensions/README.md +5 -4
  164. package/examples/extensions/antigravity-image-gen.ts +3 -1
  165. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  166. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  167. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  168. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  169. package/examples/extensions/doom-overlay/README.md +1 -1
  170. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  171. package/examples/extensions/dynamic-tools.ts +74 -0
  172. package/examples/extensions/subagent/README.md +11 -11
  173. package/examples/extensions/with-deps/package-lock.json +2 -2
  174. package/examples/extensions/with-deps/package.json +1 -1
  175. package/examples/sdk/README.md +3 -3
  176. package/package.json +11 -8
  177. package/prompts/commands/discuss-phase.md +3 -3
  178. package/prompts/commands/execute-phase.md +9 -9
  179. package/prompts/commands/fix.md +29 -0
  180. package/prompts/commands/init-project.md +49 -0
  181. package/prompts/commands/map-codebase.md +2 -2
  182. package/prompts/commands/new-project.md +9 -9
  183. package/prompts/commands/next-milestone.md +44 -0
  184. package/prompts/commands/pause-work.md +2 -2
  185. package/prompts/commands/plan-phase.md +5 -5
  186. package/prompts/commands/progress.md +1 -1
  187. package/prompts/commands/quick.md +4 -4
  188. package/prompts/commands/resume-work.md +1 -1
  189. package/prompts/commands/review.md +26 -0
  190. package/prompts/commands/verify-work.md +4 -4
  191. package/dist/extensions/gsd-commands.ts +0 -338
  192. package/dist/extensions/subagent.ts +0 -312
  193. package/extensions/gsd-commands.ts +0 -338
  194. package/extensions/subagent.ts +0 -312
@@ -1,312 +0,0 @@
1
- /**
2
- * Subagent Tool for Draht — adapted from the pi subagent example
3
- *
4
- * Spawns isolated draht processes for delegated tasks.
5
- * Agents are defined in .draht/agents/*.md (project) or ~/.draht/agent/agents/*.md (global).
6
- *
7
- * Modes:
8
- * single — { agent, task }
9
- * parallel — { tasks: [{ agent, task }] } (max 8, concurrency 4)
10
- * chain — { chain: [{ agent, task }] } (sequential, {previous} placeholder)
11
- */
12
-
13
- import { spawn } from "node:child_process";
14
- import * as fs from "node:fs";
15
- import * as os from "node:os";
16
- import * as path from "node:path";
17
- import type { Message } from "@draht/ai";
18
- import { StringEnum } from "@draht/ai";
19
- import { type ExtensionAPI, getAgentDir, parseFrontmatter } from "@draht/coding-agent";
20
- import { Type } from "@sinclair/typebox";
21
-
22
- const MAX_PARALLEL = 8;
23
- const MAX_CONCURRENCY = 4;
24
- // Use the same binary that's currently running
25
- const DRAHT_BIN = process.execPath;
26
-
27
- // ─── Agent discovery ────────────────────────────────────────────────────────
28
-
29
- interface AgentConfig {
30
- name: string;
31
- description: string;
32
- tools?: string[];
33
- model?: string;
34
- systemPrompt: string;
35
- source: "user" | "project";
36
- }
37
-
38
- function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig[] {
39
- if (!fs.existsSync(dir)) return [];
40
- let entries: fs.Dirent[];
41
- try {
42
- entries = fs.readdirSync(dir, { withFileTypes: true });
43
- } catch {
44
- return [];
45
- }
46
- const agents: AgentConfig[] = [];
47
- for (const entry of entries) {
48
- if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
49
- try {
50
- const content = fs.readFileSync(path.join(dir, entry.name), "utf-8");
51
- const { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);
52
- if (!frontmatter.name || !frontmatter.description) continue;
53
- const tools = frontmatter.tools?.split(",").map((t: string) => t.trim()).filter(Boolean);
54
- agents.push({
55
- name: frontmatter.name,
56
- description: frontmatter.description,
57
- tools: tools?.length ? tools : undefined,
58
- model: frontmatter.model,
59
- systemPrompt: body,
60
- source,
61
- });
62
- } catch {
63
- continue;
64
- }
65
- }
66
- return agents;
67
- }
68
-
69
- function findProjectAgentsDir(cwd: string): string | null {
70
- let dir = cwd;
71
- while (true) {
72
- const candidate = path.join(dir, ".draht", "agents");
73
- try {
74
- if (fs.statSync(candidate).isDirectory()) return candidate;
75
- } catch {}
76
- const parent = path.dirname(dir);
77
- if (parent === dir) return null;
78
- dir = parent;
79
- }
80
- }
81
-
82
- type AgentScope = "user" | "project" | "both";
83
-
84
- function discoverAgents(cwd: string, scope: AgentScope): AgentConfig[] {
85
- const userDir = path.join(getAgentDir(), "agents");
86
- const projectDir = findProjectAgentsDir(cwd);
87
- const userAgents = scope !== "project" ? loadAgentsFromDir(userDir, "user") : [];
88
- const projectAgents = scope !== "user" && projectDir ? loadAgentsFromDir(projectDir, "project") : [];
89
- const map = new Map<string, AgentConfig>();
90
- for (const a of userAgents) map.set(a.name, a);
91
- for (const a of projectAgents) map.set(a.name, a); // project overrides global
92
- return Array.from(map.values());
93
- }
94
-
95
- // ─── Runner ─────────────────────────────────────────────────────────────────
96
-
97
- interface RunResult {
98
- agent: string;
99
- task: string;
100
- exitCode: number;
101
- output: string;
102
- stderr: string;
103
- step?: number;
104
- }
105
-
106
- function writeTemp(name: string, content: string): { file: string; dir: string } {
107
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "draht-subagent-"));
108
- const file = path.join(dir, `${name.replace(/[^\w.-]/g, "_")}.md`);
109
- fs.writeFileSync(file, content, { encoding: "utf-8", mode: 0o600 });
110
- return { file, dir };
111
- }
112
-
113
- function cleanTemp(file: string, dir: string) {
114
- try { fs.unlinkSync(file); } catch {}
115
- try { fs.rmdirSync(dir); } catch {}
116
- }
117
-
118
- function getFinalText(messages: Message[]): string {
119
- for (let i = messages.length - 1; i >= 0; i--) {
120
- const msg = messages[i];
121
- if (msg.role === "assistant") {
122
- for (const part of msg.content) {
123
- if (part.type === "text") return part.text;
124
- }
125
- }
126
- }
127
- return "";
128
- }
129
-
130
- async function runAgent(
131
- cwd: string,
132
- agent: AgentConfig,
133
- task: string,
134
- signal?: AbortSignal,
135
- step?: number,
136
- ): Promise<RunResult> {
137
- const args: string[] = ["--mode", "json", "-p", "--no-session"];
138
- if (agent.model) args.push("--model", agent.model);
139
- if (agent.tools?.length) args.push("--tools", agent.tools.join(","));
140
-
141
- let tmpFile: string | null = null;
142
- let tmpDir: string | null = null;
143
-
144
- if (agent.systemPrompt.trim()) {
145
- const tmp = writeTemp(agent.name, agent.systemPrompt);
146
- tmpFile = tmp.file;
147
- tmpDir = tmp.dir;
148
- args.push("--append-system-prompt", tmpFile);
149
- }
150
-
151
- args.push(`Task: ${task}`);
152
-
153
- const messages: Message[] = [];
154
- let stderr = "";
155
-
156
- try {
157
- const exitCode = await new Promise<number>((resolve) => {
158
- const proc = spawn(DRAHT_BIN, args, { cwd, shell: false, stdio: ["ignore", "pipe", "pipe"] });
159
- let buf = "";
160
-
161
- const processLine = (line: string) => {
162
- if (!line.trim()) return;
163
- try {
164
- const event = JSON.parse(line);
165
- if ((event.type === "message_end" || event.type === "tool_result_end") && event.message) {
166
- messages.push(event.message as Message);
167
- }
168
- } catch {}
169
- };
170
-
171
- proc.stdout.on("data", (d) => {
172
- buf += d.toString();
173
- const lines = buf.split("\n");
174
- buf = lines.pop() || "";
175
- for (const l of lines) processLine(l);
176
- });
177
- proc.stderr.on("data", (d) => { stderr += d.toString(); });
178
- proc.on("close", (code) => {
179
- if (buf.trim()) processLine(buf);
180
- resolve(code ?? 0);
181
- });
182
- proc.on("error", () => resolve(1));
183
-
184
- if (signal) {
185
- const kill = () => { proc.kill("SIGTERM"); setTimeout(() => { if (!proc.killed) proc.kill("SIGKILL"); }, 5000); };
186
- if (signal.aborted) kill();
187
- else signal.addEventListener("abort", kill, { once: true });
188
- }
189
- });
190
-
191
- return { agent: agent.name, task, exitCode, output: getFinalText(messages), stderr, step };
192
- } finally {
193
- if (tmpFile && tmpDir) cleanTemp(tmpFile, tmpDir);
194
- }
195
- }
196
-
197
- async function runParallel<T>(
198
- items: T[],
199
- concurrency: number,
200
- fn: (item: T, i: number) => Promise<T extends unknown ? RunResult : never>,
201
- ): Promise<RunResult[]> {
202
- const results: RunResult[] = new Array(items.length);
203
- let next = 0;
204
- await Promise.all(
205
- Array.from({ length: Math.min(concurrency, items.length) }, async () => {
206
- while (true) {
207
- const i = next++;
208
- if (i >= items.length) return;
209
- results[i] = await (fn as (item: T, i: number) => Promise<RunResult>)(items[i], i);
210
- }
211
- }),
212
- );
213
- return results;
214
- }
215
-
216
- // ─── Extension ──────────────────────────────────────────────────────────────
217
-
218
- const TaskItem = Type.Object({
219
- agent: Type.String({ description: "Agent name" }),
220
- task: Type.String({ description: "Task description" }),
221
- });
222
-
223
- const ChainItem = Type.Object({
224
- agent: Type.String({ description: "Agent name" }),
225
- task: Type.String({ description: "Task, optionally using {previous} placeholder" }),
226
- });
227
-
228
- const Params = Type.Object({
229
- agent: Type.Optional(Type.String()),
230
- task: Type.Optional(Type.String()),
231
- tasks: Type.Optional(Type.Array(TaskItem, { description: "Parallel tasks" })),
232
- chain: Type.Optional(Type.Array(ChainItem, { description: "Chained tasks" })),
233
- agentScope: Type.Optional(
234
- StringEnum(["user", "project", "both"] as const, { default: "both" }),
235
- ),
236
- });
237
-
238
- export default function (pi: ExtensionAPI) {
239
- pi.registerTool({
240
- name: "subagent",
241
- label: "Subagent",
242
- description:
243
- "Delegate to specialized agents. single: {agent,task} | parallel: {tasks:[]} | chain: {chain:[]} with {previous} placeholder. agentScope: 'both' (default) uses project .draht/agents/ + global.",
244
- parameters: Params,
245
-
246
- async execute(_id, params, signal, _onUpdate, ctx) {
247
- const scope: AgentScope = (params.agentScope as AgentScope) ?? "both";
248
- const agents = discoverAgents(ctx.cwd, scope);
249
- const available = agents.map((a) => a.name).join(", ") || "none";
250
-
251
- const find = (name: string) => agents.find((a) => a.name === name);
252
- const notFound = (name: string) => ({
253
- content: [{ type: "text" as const, text: `Unknown agent "${name}". Available: ${available}` }],
254
- isError: true,
255
- });
256
-
257
- // ── Chain mode ──
258
- if (params.chain?.length) {
259
- let previous = "";
260
- const results: RunResult[] = [];
261
- for (let i = 0; i < params.chain.length; i++) {
262
- const step = params.chain[i];
263
- const agent = find(step.agent);
264
- if (!agent) return notFound(step.agent);
265
- const task = step.task.replace(/\{previous\}/g, previous);
266
- const result = await runAgent(ctx.cwd, agent, task, signal, i + 1);
267
- results.push(result);
268
- if (result.exitCode !== 0) {
269
- return {
270
- content: [{ type: "text" as const, text: `Chain failed at step ${i + 1} (${step.agent}):\n${result.output || result.stderr}` }],
271
- isError: true,
272
- };
273
- }
274
- previous = result.output;
275
- }
276
- return { content: [{ type: "text" as const, text: results[results.length - 1].output || "(no output)" }] };
277
- }
278
-
279
- // ── Parallel mode ──
280
- if (params.tasks?.length) {
281
- if (params.tasks.length > MAX_PARALLEL) {
282
- return { content: [{ type: "text" as const, text: `Too many tasks (max ${MAX_PARALLEL})` }], isError: true };
283
- }
284
- for (const t of params.tasks) { if (!find(t.agent)) return notFound(t.agent); }
285
-
286
- const results = await runParallel(params.tasks, MAX_CONCURRENCY, async (t, i) => {
287
- return runAgent(ctx.cwd, find(t.agent)!, t.task, signal);
288
- });
289
-
290
- const ok = results.filter((r) => r.exitCode === 0).length;
291
- const summary = results
292
- .map((r) => `[${r.agent}] ${r.exitCode === 0 ? "✓" : "✗"} ${r.output.slice(0, 200)}`)
293
- .join("\n\n");
294
- return { content: [{ type: "text" as const, text: `Parallel: ${ok}/${results.length} succeeded\n\n${summary}` }] };
295
- }
296
-
297
- // ── Single mode ──
298
- if (params.agent && params.task) {
299
- const agent = find(params.agent);
300
- if (!agent) return notFound(params.agent);
301
- const result = await runAgent(ctx.cwd, agent, params.task, signal);
302
- const isError = result.exitCode !== 0;
303
- return {
304
- content: [{ type: "text" as const, text: result.output || result.stderr || "(no output)" }],
305
- ...(isError ? { isError: true } : {}),
306
- };
307
- }
308
-
309
- return { content: [{ type: "text" as const, text: `Provide exactly one mode. Available agents: ${available}` }], isError: true };
310
- },
311
- });
312
- }