@draht/coding-agent 2026.3.3 → 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 (139) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/README.md +1 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +1 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +5 -0
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config.d.ts +0 -7
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +0 -12
  12. package/dist/config.js.map +1 -1
  13. package/dist/core/agent-session.d.ts +14 -4
  14. package/dist/core/agent-session.d.ts.map +1 -1
  15. package/dist/core/agent-session.js +167 -49
  16. package/dist/core/agent-session.js.map +1 -1
  17. package/dist/core/auth-storage.d.ts +1 -1
  18. package/dist/core/auth-storage.d.ts.map +1 -1
  19. package/dist/core/auth-storage.js +2 -1
  20. package/dist/core/auth-storage.js.map +1 -1
  21. package/dist/core/builtins/subagent.d.ts +14 -0
  22. package/dist/core/builtins/subagent.d.ts.map +1 -0
  23. package/dist/core/builtins/subagent.js +492 -0
  24. package/dist/core/builtins/subagent.js.map +1 -0
  25. package/dist/core/compaction/compaction.d.ts.map +1 -1
  26. package/dist/core/compaction/compaction.js +4 -1
  27. package/dist/core/compaction/compaction.js.map +1 -1
  28. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  29. package/dist/core/export-html/tool-renderer.js +6 -0
  30. package/dist/core/export-html/tool-renderer.js.map +1 -1
  31. package/dist/core/extensions/loader.d.ts.map +1 -1
  32. package/dist/core/extensions/loader.js +19 -8
  33. package/dist/core/extensions/loader.js.map +1 -1
  34. package/dist/core/extensions/runner.d.ts.map +1 -1
  35. package/dist/core/extensions/runner.js +1 -0
  36. package/dist/core/extensions/runner.js.map +1 -1
  37. package/dist/core/extensions/types.d.ts +8 -2
  38. package/dist/core/extensions/types.d.ts.map +1 -1
  39. package/dist/core/extensions/types.js.map +1 -1
  40. package/dist/core/model-registry.d.ts +1 -0
  41. package/dist/core/model-registry.d.ts.map +1 -1
  42. package/dist/core/model-registry.js +9 -6
  43. package/dist/core/model-registry.js.map +1 -1
  44. package/dist/core/model-resolver.d.ts.map +1 -1
  45. package/dist/core/model-resolver.js +35 -5
  46. package/dist/core/model-resolver.js.map +1 -1
  47. package/dist/core/package-manager.d.ts.map +1 -1
  48. package/dist/core/package-manager.js +1 -10
  49. package/dist/core/package-manager.js.map +1 -1
  50. package/dist/core/prompt-templates.js.map +1 -1
  51. package/dist/core/resource-loader.d.ts +2 -0
  52. package/dist/core/resource-loader.d.ts.map +1 -1
  53. package/dist/core/resource-loader.js +5 -1
  54. package/dist/core/resource-loader.js.map +1 -1
  55. package/dist/core/sdk.d.ts +1 -1
  56. package/dist/core/sdk.d.ts.map +1 -1
  57. package/dist/core/sdk.js.map +1 -1
  58. package/dist/core/session-manager.js.map +1 -1
  59. package/dist/core/settings-manager.d.ts +3 -0
  60. package/dist/core/settings-manager.d.ts.map +1 -1
  61. package/dist/core/settings-manager.js +4 -0
  62. package/dist/core/settings-manager.js.map +1 -1
  63. package/dist/core/system-prompt.d.ts +4 -0
  64. package/dist/core/system-prompt.d.ts.map +1 -1
  65. package/dist/core/system-prompt.js +34 -12
  66. package/dist/core/system-prompt.js.map +1 -1
  67. package/dist/core/tools/edit-diff.js.map +1 -1
  68. package/dist/core/tools/path-utils.js.map +1 -1
  69. package/dist/main.d.ts.map +1 -1
  70. package/dist/main.js +5 -5
  71. package/dist/main.js.map +1 -1
  72. package/dist/migrations.js.map +1 -1
  73. package/dist/modes/interactive/components/armin.js.map +1 -1
  74. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  75. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  76. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  77. package/dist/modes/interactive/components/extension-editor.d.ts +5 -2
  78. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  79. package/dist/modes/interactive/components/extension-editor.js +9 -1
  80. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  81. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  82. package/dist/modes/interactive/components/footer.js.map +1 -1
  83. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  84. package/dist/modes/interactive/components/login-dialog.js +1 -1
  85. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  86. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  87. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  88. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  89. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  90. package/dist/modes/interactive/components/oauth-selector.js +1 -1
  91. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  92. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  93. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  94. package/dist/modes/interactive/components/session-selector.js +1 -1
  95. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  96. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  97. package/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  98. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  99. package/dist/modes/interactive/components/tool-execution.js +28 -3
  100. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  101. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  102. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  103. package/dist/modes/interactive/components/user-message.d.ts +1 -0
  104. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  105. package/dist/modes/interactive/components/user-message.js +11 -0
  106. package/dist/modes/interactive/components/user-message.js.map +1 -1
  107. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  108. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  109. package/dist/modes/interactive/interactive-mode.js +27 -26
  110. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  111. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  112. package/dist/modes/interactive/theme/theme.js +5 -0
  113. package/dist/modes/interactive/theme/theme.js.map +1 -1
  114. package/dist/prompts/commands/fix.md +29 -0
  115. package/dist/prompts/commands/init-project.md +49 -0
  116. package/dist/prompts/commands/next-milestone.md +44 -0
  117. package/dist/prompts/commands/review.md +26 -0
  118. package/docs/custom-provider.md +10 -2
  119. package/docs/extensions.md +20 -1
  120. package/docs/providers.md +3 -1
  121. package/docs/settings.md +1 -0
  122. package/examples/extensions/README.md +1 -0
  123. package/examples/extensions/antigravity-image-gen.ts +3 -1
  124. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  125. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  126. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  127. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  128. package/examples/extensions/dynamic-tools.ts +74 -0
  129. package/examples/extensions/with-deps/package-lock.json +2 -2
  130. package/examples/extensions/with-deps/package.json +1 -1
  131. package/package.json +8 -7
  132. package/prompts/commands/fix.md +29 -0
  133. package/prompts/commands/init-project.md +49 -0
  134. package/prompts/commands/next-milestone.md +44 -0
  135. package/prompts/commands/review.md +26 -0
  136. package/dist/extensions/gsd-commands.ts +0 -403
  137. package/dist/extensions/subagent.ts +0 -515
  138. package/extensions/gsd-commands.ts +0 -403
  139. package/extensions/subagent.ts +0 -515
@@ -1,403 +0,0 @@
1
- /**
2
- * GSD — Get Shit Done
3
- *
4
- * Batteries-included phase commands for structured AI-assisted development.
5
- *
6
- * Full workflow:
7
- * /discuss <feature> — clarify requirements before planning
8
- * /plan <feature> — architect produces implementation plan
9
- * /execute <task1, task2...> — parallel implement → review → commit
10
- * /verify — parallel lint/typecheck/tests + security audit
11
- *
12
- * Utilities:
13
- * /review <scope> — ad-hoc code review + security audit
14
- * /fix <issue> — targeted fix plan for a failing task
15
- * /quick <task> — one-shot implement + commit (skip full GSD)
16
- * /continue — pick up interrupted work from CONTINUE-HERE.md
17
- * /status — show .planning/STATE.md overview
18
- * /next-milestone — plan next milestone after current one is complete
19
- * /new-project <name> [path] — create project dir, git init, scaffold .draht/
20
- * /init-project — scaffold .draht/ in existing project
21
- */
22
-
23
- import * as fs from "node:fs";
24
- import * as path from "node:path";
25
- import type { ExtensionAPI } from "@draht/coding-agent";
26
-
27
- function isBusy(ctx: { isIdle: () => boolean }, ui: { notify: (msg: string, level: string) => void }): boolean {
28
- if (!ctx.isIdle()) {
29
- ui.notify("Agent is busy", "warning");
30
- return true;
31
- }
32
- return false;
33
- }
34
-
35
- export default function (pi: ExtensionAPI) {
36
- // ── /discuss ─────────────────────────────────────────────────────────────
37
- pi.registerCommand("discuss", {
38
- description: "Clarify requirements before planning. Architect asks questions and defines scope. Usage: /discuss <feature>",
39
- handler: async (args, ctx) => {
40
- if (!args.trim()) {
41
- ctx.ui.notify("Usage: /discuss <feature description>", "warning");
42
- return;
43
- }
44
- if (isBusy(ctx, ctx.ui)) return;
45
-
46
- pi.sendUserMessage(
47
- `Use the subagent tool to delegate to the architect agent with this task:
48
-
49
- "We are in the DISCUSS phase for: ${args.trim()}
50
-
51
- Your job is NOT to plan yet. First:
52
- 1. Read relevant existing code to understand the current state
53
- 2. Identify ambiguities, unknowns, and risks
54
- 3. List clarifying questions that need answers before planning can begin
55
- 4. Define a clear, bounded scope for what will and won't be built
56
- 5. Output a DISCUSS summary with: scope, assumptions, open questions, risks
57
-
58
- Do not produce file lists or implementation details yet."
59
-
60
- Set agentScope to "project".`,
61
- );
62
- },
63
- });
64
-
65
- // ── /plan ────────────────────────────────────────────────────────────────
66
- pi.registerCommand("plan", {
67
- description: "Plan a feature — architect reads codebase and produces structured implementation plan. Usage: /plan <feature>",
68
- handler: async (args, ctx) => {
69
- if (!args.trim()) {
70
- ctx.ui.notify("Usage: /plan <feature description>", "warning");
71
- return;
72
- }
73
- if (isBusy(ctx, ctx.ui)) return;
74
-
75
- pi.sendUserMessage(
76
- `Use the subagent tool to delegate to the architect agent with this task: "${args.trim()}"\n\nSet agentScope to "project".`,
77
- );
78
- },
79
- });
80
-
81
- // ── /execute ─────────────────────────────────────────────────────────────
82
- pi.registerCommand("execute", {
83
- description: "Execute tasks in parallel, then chain reviewer + git-committer. Usage: /execute task1, task2, task3",
84
- handler: async (args, ctx) => {
85
- if (!args.trim()) {
86
- ctx.ui.notify("Usage: /execute task1, task2, task3", "warning");
87
- return;
88
- }
89
- if (isBusy(ctx, ctx.ui)) return;
90
-
91
- const tasks = args.split(",").map((t) => t.trim()).filter(Boolean);
92
-
93
- if (tasks.length === 1) {
94
- pi.sendUserMessage(
95
- `Use the subagent tool in chain mode with agentScope "project":
96
- 1. agent: implementer — task: "${tasks[0]}"
97
- 2. agent: reviewer — task: "Review the changes just made: {previous}"
98
- 3. agent: git-committer — task: "Commit all changes. Review context: {previous}"`,
99
- );
100
- } else {
101
- const parallelList = tasks.map((t, i) => `${i + 1}. agent: implementer — task: "${t}"`).join("\n");
102
- pi.sendUserMessage(
103
- `Use the subagent tool with agentScope "project":
104
-
105
- Step 1 — PARALLEL mode (run all simultaneously):
106
- ${parallelList}
107
-
108
- Step 2 — CHAIN mode (after all parallel tasks complete):
109
- 1. agent: reviewer — task: "Review all changes just implemented"
110
- 2. agent: git-committer — task: "Commit all changes. Review findings: {previous}"`,
111
- );
112
- }
113
- },
114
- });
115
-
116
- // ── /verify ──────────────────────────────────────────────────────────────
117
- pi.registerCommand("verify", {
118
- description: "Parallel verification: lint, typecheck, tests, and security audit",
119
- handler: async (_args, ctx) => {
120
- if (isBusy(ctx, ctx.ui)) return;
121
-
122
- pi.sendUserMessage(
123
- `Use the subagent tool in PARALLEL mode with agentScope "project":
124
- 1. agent: verifier — task: "Run all lint, typecheck, and test checks"
125
- 2. agent: security-auditor — task: "Audit all recent changes for security vulnerabilities"
126
-
127
- After both complete, merge findings into a single prioritized report. List what must be fixed before this is production-ready.`,
128
- );
129
- },
130
- });
131
-
132
- // ── /review ──────────────────────────────────────────────────────────────
133
- pi.registerCommand("review", {
134
- description: "Ad-hoc code review + security audit. Usage: /review <scope or files>",
135
- handler: async (args, ctx) => {
136
- if (isBusy(ctx, ctx.ui)) return;
137
-
138
- const scope = args.trim() || "all recent changes";
139
- pi.sendUserMessage(
140
- `Use the subagent tool in PARALLEL mode with agentScope "project":
141
- 1. agent: reviewer — task: "Review ${scope} for correctness, type safety, and conventions"
142
- 2. agent: security-auditor — task: "Audit ${scope} for security vulnerabilities"
143
-
144
- After both complete, merge into a single prioritized findings report.`,
145
- );
146
- },
147
- });
148
-
149
- // ── /fix ─────────────────────────────────────────────────────────────────
150
- pi.registerCommand("fix", {
151
- description: "Create a targeted fix plan for a failing task or bug. Usage: /fix <description of what's broken>",
152
- handler: async (args, ctx) => {
153
- if (!args.trim()) {
154
- ctx.ui.notify("Usage: /fix <description of what's broken>", "warning");
155
- return;
156
- }
157
- if (isBusy(ctx, ctx.ui)) return;
158
-
159
- pi.sendUserMessage(
160
- `Use the subagent tool in chain mode with agentScope "project":
161
- 1. agent: architect — task: "Diagnose this issue and produce a minimal fix plan: ${args.trim()}. Read the relevant code first. Output: root cause, exact files to change, fix steps."
162
- 2. agent: implementer — task: "Apply this fix plan exactly: {previous}"
163
- 3. agent: reviewer — task: "Verify the fix is correct and doesn't introduce regressions: {previous}"
164
- 4. agent: git-committer — task: "Commit the fix. Fix summary: {previous}"`,
165
- );
166
- },
167
- });
168
-
169
- // ── /quick ───────────────────────────────────────────────────────────────
170
- pi.registerCommand("quick", {
171
- description: "One-shot task: implement + commit. Skips full GSD workflow. Usage: /quick <task>",
172
- handler: async (args, ctx) => {
173
- if (!args.trim()) {
174
- ctx.ui.notify("Usage: /quick <task description>", "warning");
175
- return;
176
- }
177
- if (isBusy(ctx, ctx.ui)) return;
178
-
179
- pi.sendUserMessage(
180
- `Use the subagent tool in chain mode with agentScope "project":
181
- 1. agent: implementer — task: "${args.trim()}"
182
- 2. agent: git-committer — task: "Commit the changes just made: {previous}"`,
183
- );
184
- },
185
- });
186
-
187
- // ── /continue ────────────────────────────────────────────────────────────
188
- pi.registerCommand("continue", {
189
- description: "Continue interrupted work — reads CONTINUE-HERE.md and picks up where we left off",
190
- handler: async (_args, ctx) => {
191
- if (isBusy(ctx, ctx.ui)) return;
192
-
193
- const continueFile = path.join(ctx.cwd, ".planning", "CONTINUE-HERE.md");
194
- const stateFile = path.join(ctx.cwd, ".planning", "STATE.md");
195
-
196
- if (!fs.existsSync(continueFile) && !fs.existsSync(stateFile)) {
197
- ctx.ui.notify("No .planning/CONTINUE-HERE.md or STATE.md found. Nothing to resume.", "warning");
198
- return;
199
- }
200
-
201
- let context = "";
202
- if (fs.existsSync(continueFile)) {
203
- context += `\nCONTINUE-HERE.md:\n${fs.readFileSync(continueFile, "utf-8")}`;
204
- }
205
- if (fs.existsSync(stateFile)) {
206
- context += `\nSTATE.md:\n${fs.readFileSync(stateFile, "utf-8")}`;
207
- }
208
-
209
- pi.sendUserMessage(
210
- `Read the following project state and resume work from where it was interrupted. Identify the next incomplete task and proceed with it using the appropriate subagent.${context}`,
211
- );
212
- },
213
- });
214
-
215
- // ── /status ──────────────────────────────────────────────────────────────
216
- pi.registerCommand("status", {
217
- description: "Show current GSD project state from .planning/STATE.md",
218
- handler: async (_args, ctx) => {
219
- const stateFile = path.join(ctx.cwd, ".planning", "STATE.md");
220
- const logFile = path.join(ctx.cwd, ".planning", "execution-log.jsonl");
221
-
222
- if (!fs.existsSync(stateFile)) {
223
- ctx.ui.notify("No .planning/STATE.md found. Run /init-project or /new-project first.", "warning");
224
- return;
225
- }
226
-
227
- let output = fs.readFileSync(stateFile, "utf-8");
228
-
229
- if (fs.existsSync(logFile)) {
230
- const entries = fs.readFileSync(logFile, "utf-8")
231
- .split("\n").filter(Boolean)
232
- .map((l) => { try { return JSON.parse(l); } catch { return null; } })
233
- .filter(Boolean);
234
-
235
- const passed = entries.filter((e) => e.status === "pass").length;
236
- const failed = entries.filter((e) => e.status === "fail").length;
237
- const skipped = entries.filter((e) => e.status === "skip").length;
238
- output += `\n\n---\n**Execution log:** ${passed} passed, ${failed} failed, ${skipped} skipped`;
239
- }
240
-
241
- pi.sendUserMessage(`Here is the current project state:\n\n${output}`);
242
- },
243
- });
244
-
245
- // ── /next-milestone ──────────────────────────────────────────────────────
246
- pi.registerCommand("next-milestone", {
247
- description: "Plan the next milestone after the current one is complete — review progress, update requirements, create new phases",
248
- handler: async (_args, ctx) => {
249
- if (isBusy(ctx, ctx.ui)) return;
250
-
251
- const planningDir = path.join(ctx.cwd, ".planning");
252
- const roadmapFile = path.join(planningDir, "ROADMAP.md");
253
- const stateFile = path.join(planningDir, "STATE.md");
254
- const requirementsFile = path.join(planningDir, "REQUIREMENTS.md");
255
-
256
- if (!fs.existsSync(roadmapFile)) {
257
- ctx.ui.notify("No .planning/ROADMAP.md found. Run /new-project first.", "warning");
258
- return;
259
- }
260
-
261
- let context = `\nROADMAP.md:\n${fs.readFileSync(roadmapFile, "utf-8")}`;
262
- if (fs.existsSync(stateFile)) {
263
- context += `\nSTATE.md:\n${fs.readFileSync(stateFile, "utf-8")}`;
264
- }
265
- if (fs.existsSync(requirementsFile)) {
266
- context += `\nREQUIREMENTS.md:\n${fs.readFileSync(requirementsFile, "utf-8")}`;
267
- }
268
-
269
- // Collect UAT and summary files for completed phases
270
- const phasesDir = path.join(planningDir, "phases");
271
- if (fs.existsSync(phasesDir)) {
272
- for (const dir of fs.readdirSync(phasesDir)) {
273
- const phaseDir = path.join(phasesDir, dir);
274
- if (!fs.statSync(phaseDir).isDirectory()) continue;
275
- for (const file of fs.readdirSync(phaseDir)) {
276
- if (file.endsWith("-UAT.md") || file.endsWith("-SUMMARY.md")) {
277
- context += `\n${dir}/${file}:\n${fs.readFileSync(path.join(phaseDir, file), "utf-8")}`;
278
- }
279
- }
280
- }
281
- }
282
-
283
- pi.sendUserMessage(
284
- `Use the subagent tool to delegate to the architect agent with this task:
285
-
286
- "We have completed a milestone. Your job is to plan the NEXT milestone.
287
-
288
- 1. Read the current ROADMAP.md and identify the completed milestone and its phases
289
- 2. Review all UAT reports and summaries to understand what was built
290
- 3. Review REQUIREMENTS.md — identify which v1 requirements are now satisfied and which remain
291
- 4. Assess if any v2 requirements should be promoted to the next milestone based on what we learned
292
- 5. Propose the next milestone with new phases:
293
- - Each phase has a clear goal (outcome, not activity)
294
- - Phases are ordered by dependency
295
- - Map phases to remaining/new requirements
296
- 6. Update ROADMAP.md with the new milestone and phases
297
- 7. Update STATE.md to reflect milestone transition
298
- 8. Update REQUIREMENTS.md if requirements changed based on learnings
299
-
300
- Present the proposed milestone for user approval before writing files.
301
-
302
- Project context:${context}"
303
-
304
- Set agentScope to "project".`,
305
- );
306
- },
307
- });
308
-
309
- // ── /new-project ─────────────────────────────────────────────────────────
310
- pi.registerCommand("new-project", {
311
- description: "Create a new project: mkdir, git init, scaffold .draht/. Usage: /new-project <name> [/base/path]",
312
- handler: async (args, ctx) => {
313
- const parts = args.trim().split(/\s+/);
314
- const name = parts[0];
315
- if (!name) {
316
- ctx.ui.notify("Usage: /new-project <name> [/optional/base/path]", "warning");
317
- return;
318
- }
319
-
320
- const basePath = parts[1] ?? ctx.cwd;
321
- const projectDir = path.join(basePath, name);
322
-
323
- if (fs.existsSync(projectDir)) {
324
- ctx.ui.notify(`Directory already exists: ${projectDir}`, "warning");
325
- return;
326
- }
327
-
328
- fs.mkdirSync(projectDir, { recursive: true });
329
-
330
- const { execSync } = await import("node:child_process");
331
- execSync("git init", { cwd: projectDir });
332
-
333
- // Scaffold .draht/ from global agents
334
- const agentSrc = path.join(process.env.HOME ?? "~", ".draht", "agent", "agents");
335
- const agentDest = path.join(projectDir, ".draht", "agents");
336
- const extDest = path.join(projectDir, ".draht", "extensions");
337
- fs.mkdirSync(agentDest, { recursive: true });
338
- fs.mkdirSync(extDest, { recursive: true });
339
-
340
- let agentsCopied = 0;
341
- if (fs.existsSync(agentSrc)) {
342
- for (const file of fs.readdirSync(agentSrc)) {
343
- if (!file.endsWith(".md")) continue;
344
- fs.copyFileSync(path.join(agentSrc, file), path.join(agentDest, file));
345
- agentsCopied++;
346
- }
347
- }
348
-
349
- // Copy shipped extensions into the new project
350
- const extSrc = path.dirname(new URL(import.meta.url).pathname);
351
- for (const file of ["subagent.ts", "gsd-commands.ts"]) {
352
- const src = path.join(extSrc, file);
353
- if (fs.existsSync(src)) {
354
- fs.copyFileSync(src, path.join(extDest, file));
355
- }
356
- }
357
-
358
- fs.writeFileSync(path.join(projectDir, ".gitignore"), "node_modules/\n.env\n.env.local\n");
359
-
360
- ctx.ui.notify(
361
- `✓ ${projectDir} created — ${agentsCopied} agents, git initialized. Customize .draht/agents/*.md for your stack.`,
362
- "info",
363
- );
364
-
365
- pi.sendUserMessage(
366
- `New project "${name}" created at ${projectDir} with ${agentsCopied} GSD agents scaffolded. What should we build first?`,
367
- );
368
- },
369
- });
370
-
371
- // ── /init-project ────────────────────────────────────────────────────────
372
- pi.registerCommand("init-project", {
373
- description: "Scaffold .draht/ config into an existing project from global agent defaults",
374
- handler: async (_args, ctx) => {
375
- const targetDir = path.join(ctx.cwd, ".draht");
376
-
377
- if (fs.existsSync(targetDir)) {
378
- ctx.ui.notify(".draht/ already exists in this project", "warning");
379
- return;
380
- }
381
-
382
- const agentSrc = path.join(process.env.HOME ?? "~", ".draht", "agent", "agents");
383
- const agentDest = path.join(targetDir, "agents");
384
- const extDest = path.join(targetDir, "extensions");
385
- fs.mkdirSync(agentDest, { recursive: true });
386
- fs.mkdirSync(extDest, { recursive: true });
387
-
388
- let copied = 0;
389
- if (fs.existsSync(agentSrc)) {
390
- for (const file of fs.readdirSync(agentSrc)) {
391
- if (!file.endsWith(".md")) continue;
392
- fs.copyFileSync(path.join(agentSrc, file), path.join(agentDest, file));
393
- copied++;
394
- }
395
- }
396
-
397
- ctx.ui.notify(
398
- `.draht/ scaffolded with ${copied} agents. Customize .draht/agents/*.md for this project's stack.`,
399
- "info",
400
- );
401
- },
402
- });
403
- }