@agiflowai/aicode-toolkit 1.0.3 → 1.0.5

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.
@@ -21,24 +21,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- let node_path = require("node:path");
25
- node_path = __toESM(node_path);
24
+ let __agiflowai_coding_agent_bridge = require("@agiflowai/coding-agent-bridge");
26
25
  let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
27
- __agiflowai_aicode_utils = __toESM(__agiflowai_aicode_utils);
28
- let fs_extra = require("fs-extra");
29
- fs_extra = __toESM(fs_extra);
30
26
  let chalk = require("chalk");
31
27
  chalk = __toESM(chalk);
32
28
  let gradient_string = require("gradient-string");
33
29
  gradient_string = __toESM(gradient_string);
30
+ let node_path = require("node:path");
31
+ node_path = __toESM(node_path);
34
32
  let execa = require("execa");
35
- execa = __toESM(execa);
36
- let __agiflowai_coding_agent_bridge = require("@agiflowai/coding-agent-bridge");
37
- __agiflowai_coding_agent_bridge = __toESM(__agiflowai_coding_agent_bridge);
38
33
  let node_fs_promises = require("node:fs/promises");
39
34
  node_fs_promises = __toESM(node_fs_promises);
40
35
  let liquidjs = require("liquidjs");
41
- liquidjs = __toESM(liquidjs);
42
36
  let node_os = require("node:os");
43
37
  node_os = __toESM(node_os);
44
38
 
@@ -103,6 +97,187 @@ const BANNER_GRADIENT = [
103
97
  THEME.colors.secondary.dark
104
98
  ];
105
99
 
100
+ //#endregion
101
+ //#region src/services/CodingAgentService.ts
102
+ /**
103
+ * CodingAgentService
104
+ *
105
+ * DESIGN PATTERNS:
106
+ * - Service pattern for business logic encapsulation
107
+ * - Strategy pattern for different agent configurations
108
+ * - Single responsibility: Handle MCP setup for coding agents
109
+ *
110
+ * CODING STANDARDS:
111
+ * - Use async/await for asynchronous operations
112
+ * - Throw descriptive errors for error cases
113
+ * - Document methods with JSDoc comments
114
+ *
115
+ * AVOID:
116
+ * - Direct UI interaction (no prompts in services)
117
+ * - Hard-coding agent configurations (use strategies)
118
+ */
119
+ var CodingAgentService = class {
120
+ workspaceRoot;
121
+ constructor(workspaceRoot) {
122
+ this.workspaceRoot = workspaceRoot;
123
+ }
124
+ /**
125
+ * Detect which coding agent is enabled in the workspace
126
+ * Checks for Claude Code, Codex, Gemini CLI, GitHub Copilot, and Cursor installations
127
+ * @param workspaceRoot - The workspace root directory
128
+ * @returns Promise resolving to detected agent ID or null
129
+ */
130
+ static async detectCodingAgent(workspaceRoot) {
131
+ if (await new __agiflowai_coding_agent_bridge.ClaudeCodeService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.CLAUDE_CODE;
132
+ if (await new __agiflowai_coding_agent_bridge.CursorService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.CURSOR;
133
+ if (await new __agiflowai_coding_agent_bridge.GitHubCopilotService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.GITHUB_COPILOT;
134
+ if (await new __agiflowai_coding_agent_bridge.CodexService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.CODEX;
135
+ if (await new __agiflowai_coding_agent_bridge.GeminiCliService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.GEMINI_CLI;
136
+ return null;
137
+ }
138
+ /**
139
+ * Get available coding agents with their descriptions
140
+ */
141
+ static getAvailableAgents() {
142
+ return [
143
+ {
144
+ value: __agiflowai_coding_agent_bridge.CLAUDE_CODE,
145
+ name: "Claude Code",
146
+ description: "Anthropic Claude Code CLI agent"
147
+ },
148
+ {
149
+ value: __agiflowai_coding_agent_bridge.CURSOR,
150
+ name: "Cursor",
151
+ description: "Cursor AI-first code editor"
152
+ },
153
+ {
154
+ value: __agiflowai_coding_agent_bridge.GITHUB_COPILOT,
155
+ name: "GitHub Copilot",
156
+ description: "GitHub Copilot coding agent and CLI"
157
+ },
158
+ {
159
+ value: __agiflowai_coding_agent_bridge.CODEX,
160
+ name: "Codex",
161
+ description: "OpenAI Codex CLI agent"
162
+ },
163
+ {
164
+ value: __agiflowai_coding_agent_bridge.GEMINI_CLI,
165
+ name: "Gemini CLI",
166
+ description: "Google Gemini CLI agent"
167
+ },
168
+ {
169
+ value: __agiflowai_coding_agent_bridge.NONE,
170
+ name: "Other",
171
+ description: "Other coding agent or skip MCP configuration"
172
+ }
173
+ ];
174
+ }
175
+ /**
176
+ * Get the coding agent service instance
177
+ * @param agent - The coding agent to get service for
178
+ * @returns The service instance or null if not supported
179
+ */
180
+ getCodingAgentService(agent) {
181
+ if (agent === __agiflowai_coding_agent_bridge.CLAUDE_CODE) return new __agiflowai_coding_agent_bridge.ClaudeCodeService({ workspaceRoot: this.workspaceRoot });
182
+ if (agent === __agiflowai_coding_agent_bridge.CURSOR) return new __agiflowai_coding_agent_bridge.CursorService({ workspaceRoot: this.workspaceRoot });
183
+ if (agent === __agiflowai_coding_agent_bridge.GITHUB_COPILOT) return new __agiflowai_coding_agent_bridge.GitHubCopilotService({ workspaceRoot: this.workspaceRoot });
184
+ if (agent === __agiflowai_coding_agent_bridge.CODEX) return new __agiflowai_coding_agent_bridge.CodexService({ workspaceRoot: this.workspaceRoot });
185
+ if (agent === __agiflowai_coding_agent_bridge.GEMINI_CLI) return new __agiflowai_coding_agent_bridge.GeminiCliService({ workspaceRoot: this.workspaceRoot });
186
+ return null;
187
+ }
188
+ /**
189
+ * Update custom instructions/prompts for the coding agent
190
+ * Appends custom instruction prompt to the agent's configuration
191
+ * @param agent - The coding agent to update
192
+ * @param instructionPrompt - The instruction prompt to append
193
+ * @param customInstructionFile - Optional custom file path to write instructions to (e.g., '.claude/aicode-instructions.md')
194
+ */
195
+ async updateCustomInstructions(agent, instructionPrompt, customInstructionFile) {
196
+ if (agent === __agiflowai_coding_agent_bridge.NONE) {
197
+ __agiflowai_aicode_utils.print.info("Skipping custom instruction update");
198
+ return;
199
+ }
200
+ __agiflowai_aicode_utils.print.info(`\nUpdating custom instructions for ${agent}...`);
201
+ const service = this.getCodingAgentService(agent);
202
+ if (!service) {
203
+ __agiflowai_aicode_utils.print.info(`Custom instruction update for ${agent} is not yet supported.`);
204
+ __agiflowai_aicode_utils.print.info("Please manually add the instructions to your agent configuration.");
205
+ __agiflowai_aicode_utils.print.info("\nInstruction prompt to add:");
206
+ __agiflowai_aicode_utils.print.info(instructionPrompt);
207
+ return;
208
+ }
209
+ await service.updatePrompt({
210
+ systemPrompt: instructionPrompt,
211
+ customInstructionFile,
212
+ marker: true
213
+ });
214
+ if (customInstructionFile) __agiflowai_aicode_utils.print.success(`Custom instructions written to ${customInstructionFile} and referenced in CLAUDE.md and AGENTS.md`);
215
+ else __agiflowai_aicode_utils.print.success(`Custom instructions appended to CLAUDE.md and AGENTS.md`);
216
+ }
217
+ /**
218
+ * Setup MCP configuration for the selected coding agent
219
+ * @param agent - The coding agent to configure
220
+ */
221
+ async setupMCP(agent) {
222
+ if (agent === __agiflowai_coding_agent_bridge.NONE) {
223
+ __agiflowai_aicode_utils.print.info("Skipping MCP configuration");
224
+ return;
225
+ }
226
+ __agiflowai_aicode_utils.print.info(`\nSetting up MCP for ${agent}...`);
227
+ const service = this.getCodingAgentService(agent);
228
+ let configLocation = "";
229
+ let restartInstructions = "";
230
+ if (agent === __agiflowai_coding_agent_bridge.CLAUDE_CODE) {
231
+ configLocation = ".mcp.json";
232
+ restartInstructions = "Restart Claude Code to load the new MCP servers";
233
+ } else if (agent === __agiflowai_coding_agent_bridge.CURSOR) {
234
+ configLocation = "~/.cursor/mcp.json (or .cursor/mcp.json for workspace)";
235
+ restartInstructions = "Restart Cursor to load the new MCP servers";
236
+ } else if (agent === __agiflowai_coding_agent_bridge.GITHUB_COPILOT) {
237
+ configLocation = "~/.copilot/config.json (CLI) or GitHub UI (Coding Agent)";
238
+ restartInstructions = "Restart GitHub Copilot CLI or configure via GitHub repository settings";
239
+ } else if (agent === __agiflowai_coding_agent_bridge.CODEX) {
240
+ configLocation = "~/.codex/config.toml";
241
+ restartInstructions = "Restart Codex CLI to load the new MCP servers";
242
+ } else if (agent === __agiflowai_coding_agent_bridge.GEMINI_CLI) {
243
+ configLocation = "~/.gemini/settings.json";
244
+ restartInstructions = "Restart Gemini CLI to load the new MCP servers";
245
+ }
246
+ if (!service) {
247
+ __agiflowai_aicode_utils.print.info(`MCP configuration for ${agent} is not yet supported.`);
248
+ __agiflowai_aicode_utils.print.info("Please configure MCP servers manually for this coding agent.");
249
+ return;
250
+ }
251
+ await service.updateMcpSettings({ servers: {
252
+ "scaffold-mcp": {
253
+ type: "stdio",
254
+ command: "npx",
255
+ args: [
256
+ "-y",
257
+ "@agiflowai/scaffold-mcp",
258
+ "mcp-serve"
259
+ ],
260
+ disabled: false
261
+ },
262
+ "architect-mcp": {
263
+ type: "stdio",
264
+ command: "npx",
265
+ args: [
266
+ "-y",
267
+ "@agiflowai/architect-mcp",
268
+ "mcp-serve"
269
+ ],
270
+ disabled: false
271
+ }
272
+ } });
273
+ __agiflowai_aicode_utils.print.success(`Added scaffold-mcp and architect-mcp to ${configLocation}`);
274
+ __agiflowai_aicode_utils.print.info("\nNext steps:");
275
+ __agiflowai_aicode_utils.print.indent(`1. ${restartInstructions}`);
276
+ __agiflowai_aicode_utils.print.indent("2. The scaffold-mcp and architect-mcp servers will be available");
277
+ __agiflowai_aicode_utils.print.success("\nMCP configuration completed!");
278
+ }
279
+ };
280
+
106
281
  //#endregion
107
282
  //#region src/utils/banner.ts
108
283
  /**
@@ -178,8 +353,7 @@ async function findWorkspaceRoot(startPath = process.cwd()) {
178
353
  let currentPath = node_path.default.resolve(startPath);
179
354
  const rootPath = node_path.default.parse(currentPath).root;
180
355
  while (true) {
181
- const gitPath = node_path.default.join(currentPath, ".git");
182
- if (await fs_extra.pathExists(gitPath)) return currentPath;
356
+ if (await (0, __agiflowai_aicode_utils.pathExists)(node_path.default.join(currentPath, ".git"))) return currentPath;
183
357
  if (currentPath === rootPath) return null;
184
358
  currentPath = node_path.default.dirname(currentPath);
185
359
  }
@@ -235,8 +409,7 @@ async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
235
409
  "core.sparseCheckout",
236
410
  "true"
237
411
  ], tempFolder);
238
- const sparseCheckoutFile = node_path.default.join(tempFolder, ".git", "info", "sparse-checkout");
239
- await fs_extra.writeFile(sparseCheckoutFile, `${subdirectory}\n`);
412
+ await (0, __agiflowai_aicode_utils.writeFile)(node_path.default.join(tempFolder, ".git", "info", "sparse-checkout"), `${subdirectory}\n`);
240
413
  await execGit([
241
414
  "pull",
242
415
  "--depth=1",
@@ -244,12 +417,12 @@ async function cloneSubdirectory(repoUrl, branch, subdirectory, targetFolder) {
244
417
  branch
245
418
  ], tempFolder);
246
419
  const sourceDir = node_path.default.join(tempFolder, subdirectory);
247
- if (!await fs_extra.pathExists(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
248
- if (await fs_extra.pathExists(targetFolder)) throw new Error(`Target folder already exists: ${targetFolder}`);
249
- await fs_extra.move(sourceDir, targetFolder);
250
- await fs_extra.remove(tempFolder);
420
+ if (!await (0, __agiflowai_aicode_utils.pathExists)(sourceDir)) throw new Error(`Subdirectory '${subdirectory}' not found in repository at branch '${branch}'`);
421
+ if (await (0, __agiflowai_aicode_utils.pathExists)(targetFolder)) throw new Error(`Target folder already exists: ${targetFolder}`);
422
+ await (0, __agiflowai_aicode_utils.move)(sourceDir, targetFolder);
423
+ await (0, __agiflowai_aicode_utils.remove)(tempFolder);
251
424
  } catch (error) {
252
- if (await fs_extra.pathExists(tempFolder)) await fs_extra.remove(tempFolder);
425
+ if (await (0, __agiflowai_aicode_utils.pathExists)(tempFolder)) await (0, __agiflowai_aicode_utils.remove)(tempFolder);
253
426
  throw error;
254
427
  }
255
428
  }
@@ -263,7 +436,7 @@ async function cloneRepository(repoUrl, targetFolder) {
263
436
  targetFolder
264
437
  ]);
265
438
  const gitFolder = node_path.default.join(targetFolder, ".git");
266
- if (await fs_extra.pathExists(gitFolder)) await fs_extra.remove(gitFolder);
439
+ if (await (0, __agiflowai_aicode_utils.pathExists)(gitFolder)) await (0, __agiflowai_aicode_utils.remove)(gitFolder);
267
440
  }
268
441
  /**
269
442
  * Fetch directory listing from GitHub API
@@ -284,172 +457,27 @@ async function fetchGitHubDirectoryContents(owner, repo, path$4, branch = "main"
284
457
  }));
285
458
  }
286
459
 
287
- //#endregion
288
- //#region src/services/CodingAgentService.ts
289
- var CodingAgentService = class {
290
- workspaceRoot;
291
- constructor(workspaceRoot) {
292
- this.workspaceRoot = workspaceRoot;
293
- }
294
- /**
295
- * Detect which coding agent is enabled in the workspace
296
- * Checks for Claude Code, Codex, Gemini CLI, GitHub Copilot, and Cursor installations
297
- * @param workspaceRoot - The workspace root directory
298
- * @returns Promise resolving to detected agent ID or null
299
- */
300
- static async detectCodingAgent(workspaceRoot) {
301
- if (await new __agiflowai_coding_agent_bridge.ClaudeCodeService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.CLAUDE_CODE;
302
- if (await new __agiflowai_coding_agent_bridge.CursorService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.CURSOR;
303
- if (await new __agiflowai_coding_agent_bridge.GitHubCopilotService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.GITHUB_COPILOT;
304
- if (await new __agiflowai_coding_agent_bridge.CodexService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.CODEX;
305
- if (await new __agiflowai_coding_agent_bridge.GeminiCliService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.GEMINI_CLI;
306
- return null;
307
- }
308
- /**
309
- * Get available coding agents with their descriptions
310
- */
311
- static getAvailableAgents() {
312
- return [
313
- {
314
- value: __agiflowai_coding_agent_bridge.CLAUDE_CODE,
315
- name: "Claude Code",
316
- description: "Anthropic Claude Code CLI agent"
317
- },
318
- {
319
- value: __agiflowai_coding_agent_bridge.CURSOR,
320
- name: "Cursor",
321
- description: "Cursor AI-first code editor"
322
- },
323
- {
324
- value: __agiflowai_coding_agent_bridge.GITHUB_COPILOT,
325
- name: "GitHub Copilot",
326
- description: "GitHub Copilot coding agent and CLI"
327
- },
328
- {
329
- value: __agiflowai_coding_agent_bridge.CODEX,
330
- name: "Codex",
331
- description: "OpenAI Codex CLI agent"
332
- },
333
- {
334
- value: __agiflowai_coding_agent_bridge.GEMINI_CLI,
335
- name: "Gemini CLI",
336
- description: "Google Gemini CLI agent"
337
- },
338
- {
339
- value: __agiflowai_coding_agent_bridge.NONE,
340
- name: "Other",
341
- description: "Other coding agent or skip MCP configuration"
342
- }
343
- ];
344
- }
345
- /**
346
- * Get the coding agent service instance
347
- * @param agent - The coding agent to get service for
348
- * @returns The service instance or null if not supported
349
- */
350
- getCodingAgentService(agent) {
351
- if (agent === __agiflowai_coding_agent_bridge.CLAUDE_CODE) return new __agiflowai_coding_agent_bridge.ClaudeCodeService({ workspaceRoot: this.workspaceRoot });
352
- if (agent === __agiflowai_coding_agent_bridge.CURSOR) return new __agiflowai_coding_agent_bridge.CursorService({ workspaceRoot: this.workspaceRoot });
353
- if (agent === __agiflowai_coding_agent_bridge.GITHUB_COPILOT) return new __agiflowai_coding_agent_bridge.GitHubCopilotService({ workspaceRoot: this.workspaceRoot });
354
- if (agent === __agiflowai_coding_agent_bridge.CODEX) return new __agiflowai_coding_agent_bridge.CodexService({ workspaceRoot: this.workspaceRoot });
355
- if (agent === __agiflowai_coding_agent_bridge.GEMINI_CLI) return new __agiflowai_coding_agent_bridge.GeminiCliService({ workspaceRoot: this.workspaceRoot });
356
- return null;
357
- }
358
- /**
359
- * Update custom instructions/prompts for the coding agent
360
- * Appends custom instruction prompt to the agent's configuration
361
- * @param agent - The coding agent to update
362
- * @param instructionPrompt - The instruction prompt to append
363
- * @param customInstructionFile - Optional custom file path to write instructions to (e.g., '.claude/aicode-instructions.md')
364
- */
365
- async updateCustomInstructions(agent, instructionPrompt, customInstructionFile) {
366
- if (agent === __agiflowai_coding_agent_bridge.NONE) {
367
- __agiflowai_aicode_utils.print.info("Skipping custom instruction update");
368
- return;
369
- }
370
- __agiflowai_aicode_utils.print.info(`\nUpdating custom instructions for ${agent}...`);
371
- const service = this.getCodingAgentService(agent);
372
- if (!service) {
373
- __agiflowai_aicode_utils.print.info(`Custom instruction update for ${agent} is not yet supported.`);
374
- __agiflowai_aicode_utils.print.info("Please manually add the instructions to your agent configuration.");
375
- __agiflowai_aicode_utils.print.info("\nInstruction prompt to add:");
376
- __agiflowai_aicode_utils.print.info(instructionPrompt);
377
- return;
378
- }
379
- await service.updatePrompt({
380
- systemPrompt: instructionPrompt,
381
- customInstructionFile,
382
- marker: true
383
- });
384
- if (customInstructionFile) __agiflowai_aicode_utils.print.success(`Custom instructions written to ${customInstructionFile} and referenced in CLAUDE.md and AGENTS.md`);
385
- else __agiflowai_aicode_utils.print.success(`Custom instructions appended to CLAUDE.md and AGENTS.md`);
386
- }
387
- /**
388
- * Setup MCP configuration for the selected coding agent
389
- * @param agent - The coding agent to configure
390
- */
391
- async setupMCP(agent) {
392
- if (agent === __agiflowai_coding_agent_bridge.NONE) {
393
- __agiflowai_aicode_utils.print.info("Skipping MCP configuration");
394
- return;
395
- }
396
- __agiflowai_aicode_utils.print.info(`\nSetting up MCP for ${agent}...`);
397
- const service = this.getCodingAgentService(agent);
398
- let configLocation = "";
399
- let restartInstructions = "";
400
- if (agent === __agiflowai_coding_agent_bridge.CLAUDE_CODE) {
401
- configLocation = ".mcp.json";
402
- restartInstructions = "Restart Claude Code to load the new MCP servers";
403
- } else if (agent === __agiflowai_coding_agent_bridge.CURSOR) {
404
- configLocation = "~/.cursor/mcp.json (or .cursor/mcp.json for workspace)";
405
- restartInstructions = "Restart Cursor to load the new MCP servers";
406
- } else if (agent === __agiflowai_coding_agent_bridge.GITHUB_COPILOT) {
407
- configLocation = "~/.copilot/config.json (CLI) or GitHub UI (Coding Agent)";
408
- restartInstructions = "Restart GitHub Copilot CLI or configure via GitHub repository settings";
409
- } else if (agent === __agiflowai_coding_agent_bridge.CODEX) {
410
- configLocation = "~/.codex/config.toml";
411
- restartInstructions = "Restart Codex CLI to load the new MCP servers";
412
- } else if (agent === __agiflowai_coding_agent_bridge.GEMINI_CLI) {
413
- configLocation = "~/.gemini/settings.json";
414
- restartInstructions = "Restart Gemini CLI to load the new MCP servers";
415
- }
416
- if (!service) {
417
- __agiflowai_aicode_utils.print.info(`MCP configuration for ${agent} is not yet supported.`);
418
- __agiflowai_aicode_utils.print.info("Please configure MCP servers manually for this coding agent.");
419
- return;
420
- }
421
- await service.updateMcpSettings({ servers: {
422
- "scaffold-mcp": {
423
- type: "stdio",
424
- command: "npx",
425
- args: [
426
- "-y",
427
- "@agiflowai/scaffold-mcp",
428
- "mcp-serve"
429
- ],
430
- disabled: false
431
- },
432
- "architect-mcp": {
433
- type: "stdio",
434
- command: "npx",
435
- args: [
436
- "-y",
437
- "@agiflowai/architect-mcp",
438
- "mcp-serve"
439
- ],
440
- disabled: false
441
- }
442
- } });
443
- __agiflowai_aicode_utils.print.success(`Added scaffold-mcp and architect-mcp to ${configLocation}`);
444
- __agiflowai_aicode_utils.print.info("\nNext steps:");
445
- __agiflowai_aicode_utils.print.indent(`1. ${restartInstructions}`);
446
- __agiflowai_aicode_utils.print.indent("2. The scaffold-mcp and architect-mcp servers will be available");
447
- __agiflowai_aicode_utils.print.success("\nMCP configuration completed!");
448
- }
449
- };
450
-
451
460
  //#endregion
452
461
  //#region src/services/NewProjectService.ts
462
+ /**
463
+ * NewProjectService
464
+ *
465
+ * DESIGN PATTERNS:
466
+ * - Service pattern for business logic encapsulation
467
+ * - Single responsibility principle
468
+ * - No UI interaction (prompts handled by CLI layer)
469
+ *
470
+ * CODING STANDARDS:
471
+ * - Use async/await for asynchronous operations
472
+ * - Throw descriptive errors for error cases
473
+ * - Keep methods focused and well-named
474
+ * - Document complex logic with comments
475
+ *
476
+ * AVOID:
477
+ * - Mixing concerns (keep focused on single domain)
478
+ * - Direct UI interaction (no @inquirer/prompts in services)
479
+ * - Direct tool implementation (services should be tool-agnostic)
480
+ */
453
481
  const RESERVED_PROJECT_NAMES = [
454
482
  ".",
455
483
  "..",
@@ -523,7 +551,7 @@ var NewProjectService = class {
523
551
  */
524
552
  async createProjectDirectory(projectPath, projectName) {
525
553
  try {
526
- await fs_extra.mkdir(projectPath, { recursive: false });
554
+ await (0, __agiflowai_aicode_utils.mkdir)(projectPath, { recursive: false });
527
555
  } catch (error) {
528
556
  if (error.code === "EEXIST") throw new Error(`Directory '${projectName}' already exists. Please choose a different name.`);
529
557
  throw error;
@@ -540,7 +568,7 @@ var NewProjectService = class {
540
568
  if (parsed.isSubdirectory && parsed.branch && parsed.subdirectory) await cloneSubdirectory(parsed.repoUrl, parsed.branch, parsed.subdirectory, projectPath);
541
569
  else await cloneRepository(parsed.repoUrl, projectPath);
542
570
  } catch (error) {
543
- await fs_extra.remove(projectPath);
571
+ await (0, __agiflowai_aicode_utils.remove)(projectPath);
544
572
  throw new Error(`Failed to clone repository: ${error.message}`);
545
573
  }
546
574
  }
@@ -574,6 +602,18 @@ var openspec_default = "When working on this project, follow the OpenSpec spec-d
574
602
  //#endregion
575
603
  //#region src/specs/openspec.ts
576
604
  /**
605
+ * OpenSpec Bridge Implementation
606
+ *
607
+ * DESIGN PATTERNS:
608
+ * - Bridge pattern implementation for OpenSpec
609
+ * - Singleton pattern for OpenSpec configuration
610
+ *
611
+ * CODING STANDARDS:
612
+ * - Implement ISpecBridge interface
613
+ * - Use async/await for I/O operations
614
+ * - Handle errors with descriptive messages
615
+ */
616
+ /**
577
617
  * OpenSpec configuration
578
618
  */
579
619
  const OPENSPEC_CONFIG = {
@@ -639,6 +679,18 @@ var OpenSpecBridge = class {
639
679
  //#endregion
640
680
  //#region src/services/SpecToolService.ts
641
681
  /**
682
+ * Spec Tool Service
683
+ *
684
+ * DESIGN PATTERNS:
685
+ * - Service pattern for spec tool detection and installation
686
+ * - Bridge pattern to abstract spec tool implementations
687
+ *
688
+ * CODING STANDARDS:
689
+ * - Use async/await for asynchronous operations
690
+ * - Handle errors with try/catch blocks
691
+ * - Use descriptive method names
692
+ */
693
+ /**
642
694
  * Available spec tools
643
695
  */
644
696
  let SpecTool = /* @__PURE__ */ function(SpecTool$1) {
@@ -716,6 +768,22 @@ var SpecToolService = class {
716
768
 
717
769
  //#endregion
718
770
  //#region src/services/TemplateSelectionService.ts
771
+ /**
772
+ * TemplateSelectionService
773
+ *
774
+ * DESIGN PATTERNS:
775
+ * - Service pattern for business logic encapsulation
776
+ * - Single responsibility: Handle template download, listing, and selection
777
+ *
778
+ * CODING STANDARDS:
779
+ * - Use async/await for asynchronous operations
780
+ * - Throw descriptive errors for error cases
781
+ * - Document methods with JSDoc comments
782
+ *
783
+ * AVOID:
784
+ * - Direct UI interaction (no prompts in services)
785
+ * - Mixing concerns beyond template management
786
+ */
719
787
  var TemplateSelectionService = class {
720
788
  tmpDir;
721
789
  constructor(existingTmpDir) {
@@ -728,7 +796,7 @@ var TemplateSelectionService = class {
728
796
  */
729
797
  async downloadTemplatesToTmp(repoConfig) {
730
798
  try {
731
- await fs_extra.ensureDir(this.tmpDir);
799
+ await (0, __agiflowai_aicode_utils.ensureDir)(this.tmpDir);
732
800
  const contents = await fetchGitHubDirectoryContents(repoConfig.owner, repoConfig.repo, repoConfig.path, repoConfig.branch);
733
801
  const templateDirs = contents.filter((item) => item.type === "dir");
734
802
  const globalFiles = contents.filter((item) => item.type === "file" && item.name === "RULES.yaml");
@@ -746,8 +814,7 @@ var TemplateSelectionService = class {
746
814
  const targetFile = node_path.default.join(this.tmpDir, "RULES.yaml");
747
815
  const response = await fetch(rulesUrl);
748
816
  if (response.ok) {
749
- const content = await response.text();
750
- await fs_extra.writeFile(targetFile, content, "utf-8");
817
+ await (0, __agiflowai_aicode_utils.writeFile)(targetFile, await response.text(), "utf-8");
751
818
  __agiflowai_aicode_utils.print.success("Downloaded global RULES.yaml");
752
819
  }
753
820
  }
@@ -764,7 +831,7 @@ var TemplateSelectionService = class {
764
831
  */
765
832
  async listTemplates() {
766
833
  try {
767
- const entries = await fs_extra.readdir(this.tmpDir, { withFileTypes: true });
834
+ const entries = await (0, __agiflowai_aicode_utils.readdir)(this.tmpDir, { withFileTypes: true });
768
835
  const templates = [];
769
836
  for (const entry of entries) if (entry.isDirectory()) {
770
837
  const templatePath = node_path.default.join(this.tmpDir, entry.name);
@@ -790,27 +857,27 @@ var TemplateSelectionService = class {
790
857
  async copyTemplates(templateNames, destinationPath, projectType, selectedMcpServers) {
791
858
  try {
792
859
  if (projectType === __agiflowai_aicode_utils.ProjectType.MONOLITH && templateNames.length > 1) throw new Error("Monolith projects can only use a single template");
793
- await fs_extra.ensureDir(destinationPath);
860
+ await (0, __agiflowai_aicode_utils.ensureDir)(destinationPath);
794
861
  __agiflowai_aicode_utils.print.info(`\nCopying templates to ${destinationPath}...`);
795
862
  for (const templateName of templateNames) {
796
863
  const sourcePath = node_path.default.join(this.tmpDir, templateName);
797
864
  const targetPath = node_path.default.join(destinationPath, templateName);
798
- if (!await fs_extra.pathExists(sourcePath)) throw new Error(`Template '${templateName}' not found in downloaded templates`);
799
- if (await fs_extra.pathExists(targetPath)) {
865
+ if (!await (0, __agiflowai_aicode_utils.pathExists)(sourcePath)) throw new Error(`Template '${templateName}' not found in downloaded templates`);
866
+ if (await (0, __agiflowai_aicode_utils.pathExists)(targetPath)) {
800
867
  __agiflowai_aicode_utils.print.info(`Skipping ${templateName} (already exists)`);
801
868
  continue;
802
869
  }
803
870
  __agiflowai_aicode_utils.print.info(`Copying ${templateName}...`);
804
871
  if (selectedMcpServers && selectedMcpServers.length > 0) await this.copyTemplateWithMcpFilter(sourcePath, targetPath, selectedMcpServers);
805
- else await fs_extra.copy(sourcePath, targetPath);
872
+ else await (0, __agiflowai_aicode_utils.copy)(sourcePath, targetPath);
806
873
  __agiflowai_aicode_utils.print.success(`Copied ${templateName}`);
807
874
  }
808
875
  const globalRulesSource = node_path.default.join(this.tmpDir, "RULES.yaml");
809
876
  const globalRulesTarget = node_path.default.join(destinationPath, "RULES.yaml");
810
- if (await fs_extra.pathExists(globalRulesSource)) {
811
- if (!await fs_extra.pathExists(globalRulesTarget)) {
877
+ if (await (0, __agiflowai_aicode_utils.pathExists)(globalRulesSource)) {
878
+ if (!await (0, __agiflowai_aicode_utils.pathExists)(globalRulesTarget)) {
812
879
  __agiflowai_aicode_utils.print.info("Copying global RULES.yaml...");
813
- await fs_extra.copy(globalRulesSource, globalRulesTarget);
880
+ await (0, __agiflowai_aicode_utils.copy)(globalRulesSource, globalRulesTarget);
814
881
  __agiflowai_aicode_utils.print.success("Copied global RULES.yaml");
815
882
  }
816
883
  }
@@ -830,19 +897,19 @@ var TemplateSelectionService = class {
830
897
  const architectFiles = MCP_CONFIG_FILES$1[MCPServer$1.ARCHITECT];
831
898
  const hasArchitect = selectedMcpServers.includes(MCPServer$1.ARCHITECT);
832
899
  const hasScaffold = selectedMcpServers.includes(MCPServer$1.SCAFFOLD);
833
- await fs_extra.ensureDir(targetPath);
834
- const entries = await fs_extra.readdir(sourcePath, { withFileTypes: true });
900
+ await (0, __agiflowai_aicode_utils.ensureDir)(targetPath);
901
+ const entries = await (0, __agiflowai_aicode_utils.readdir)(sourcePath, { withFileTypes: true });
835
902
  for (const entry of entries) {
836
903
  const entrySourcePath = node_path.default.join(sourcePath, entry.name);
837
904
  const entryTargetPath = node_path.default.join(targetPath, entry.name);
838
905
  const isArchitectFile = architectFiles.includes(entry.name);
839
- if (hasArchitect && hasScaffold) if (entry.isDirectory()) await fs_extra.copy(entrySourcePath, entryTargetPath);
840
- else await fs_extra.copy(entrySourcePath, entryTargetPath);
906
+ if (hasArchitect && hasScaffold) if (entry.isDirectory()) await (0, __agiflowai_aicode_utils.copy)(entrySourcePath, entryTargetPath);
907
+ else await (0, __agiflowai_aicode_utils.copy)(entrySourcePath, entryTargetPath);
841
908
  else if (hasArchitect && !hasScaffold) {
842
- if (isArchitectFile) await fs_extra.copy(entrySourcePath, entryTargetPath);
909
+ if (isArchitectFile) await (0, __agiflowai_aicode_utils.copy)(entrySourcePath, entryTargetPath);
843
910
  } else if (!hasArchitect && hasScaffold) {
844
- if (!isArchitectFile) if (entry.isDirectory()) await fs_extra.copy(entrySourcePath, entryTargetPath);
845
- else await fs_extra.copy(entrySourcePath, entryTargetPath);
911
+ if (!isArchitectFile) if (entry.isDirectory()) await (0, __agiflowai_aicode_utils.copy)(entrySourcePath, entryTargetPath);
912
+ else await (0, __agiflowai_aicode_utils.copy)(entrySourcePath, entryTargetPath);
846
913
  }
847
914
  }
848
915
  }
@@ -854,15 +921,15 @@ var TemplateSelectionService = class {
854
921
  async readTemplateDescription(templatePath) {
855
922
  try {
856
923
  const scaffoldYamlPath = node_path.default.join(templatePath, "scaffold.yaml");
857
- if (await fs_extra.pathExists(scaffoldYamlPath)) {
924
+ if (await (0, __agiflowai_aicode_utils.pathExists)(scaffoldYamlPath)) {
858
925
  const yaml = await import("js-yaml");
859
- const content = await fs_extra.readFile(scaffoldYamlPath, "utf-8");
926
+ const content = await (0, __agiflowai_aicode_utils.readFile)(scaffoldYamlPath, "utf-8");
860
927
  const scaffoldConfig = yaml.load(content);
861
928
  if (scaffoldConfig?.description) return scaffoldConfig.description;
862
929
  if (scaffoldConfig?.boilerplate?.[0]?.description) return scaffoldConfig.boilerplate[0].description;
863
930
  }
864
931
  const readmePath = node_path.default.join(templatePath, "README.md");
865
- if (await fs_extra.pathExists(readmePath)) return (await fs_extra.readFile(readmePath, "utf-8")).split("\n\n")[0].substring(0, 200).trim();
932
+ if (await (0, __agiflowai_aicode_utils.pathExists)(readmePath)) return (await (0, __agiflowai_aicode_utils.readFile)(readmePath, "utf-8")).split("\n\n")[0].substring(0, 200).trim();
866
933
  return;
867
934
  } catch {
868
935
  return;
@@ -879,7 +946,7 @@ var TemplateSelectionService = class {
879
946
  */
880
947
  async cleanup() {
881
948
  try {
882
- if (await fs_extra.pathExists(this.tmpDir)) await fs_extra.remove(this.tmpDir);
949
+ if (await (0, __agiflowai_aicode_utils.pathExists)(this.tmpDir)) await (0, __agiflowai_aicode_utils.remove)(this.tmpDir);
883
950
  } catch (error) {
884
951
  __agiflowai_aicode_utils.print.warning(`Warning: Failed to clean up tmp directory: ${error.message}`);
885
952
  }
@@ -888,6 +955,23 @@ var TemplateSelectionService = class {
888
955
 
889
956
  //#endregion
890
957
  //#region src/services/TemplatesService.ts
958
+ /**
959
+ * TemplatesService
960
+ *
961
+ * DESIGN PATTERNS:
962
+ * - Service pattern for business logic encapsulation
963
+ * - Single responsibility principle
964
+ *
965
+ * CODING STANDARDS:
966
+ * - Use async/await for asynchronous operations
967
+ * - Throw descriptive errors for error cases
968
+ * - Keep methods focused and well-named
969
+ * - Document complex logic with comments
970
+ *
971
+ * AVOID:
972
+ * - Mixing concerns (keep focused on single domain)
973
+ * - Direct tool implementation (services should be tool-agnostic)
974
+ */
891
975
  var TemplatesService = class {
892
976
  /**
893
977
  * Download templates from a GitHub repository with UI feedback
@@ -907,7 +991,7 @@ var TemplatesService = class {
907
991
  let _skipped = 0;
908
992
  for (const template of templateDirs) {
909
993
  const targetFolder = node_path.default.join(templatesPath, template.name);
910
- if (await fs_extra.pathExists(targetFolder)) {
994
+ if (await (0, __agiflowai_aicode_utils.pathExists)(targetFolder)) {
911
995
  __agiflowai_aicode_utils.print.info(`Skipping ${template.name} (already exists)`);
912
996
  _skipped++;
913
997
  continue;
@@ -927,8 +1011,8 @@ var TemplatesService = class {
927
1011
  * @param templatesPath - Path where templates folder should be created
928
1012
  */
929
1013
  async initializeTemplatesFolder(templatesPath) {
930
- await fs_extra.ensureDir(templatesPath);
931
- await fs_extra.writeFile(node_path.default.join(templatesPath, "README.md"), `# Templates
1014
+ await (0, __agiflowai_aicode_utils.ensureDir)(templatesPath);
1015
+ await (0, __agiflowai_aicode_utils.writeFile)(node_path.default.join(templatesPath, "README.md"), `# Templates
932
1016
 
933
1017
  This folder contains boilerplate templates and scaffolding methods for your projects.
934
1018