@agiflowai/aicode-toolkit 0.6.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_services = require('./services-s1vmufE4.cjs');
1
+ const require_services = require('./services-C6lqyioO.cjs');
2
2
  require('./mcp-Dwt8nYQV.cjs');
3
3
 
4
4
  exports.BANNER_GRADIENT = require_services.BANNER_GRADIENT;
package/dist/index.d.cts CHANGED
@@ -67,7 +67,7 @@ declare class CodingAgentService {
67
67
  constructor(workspaceRoot: string);
68
68
  /**
69
69
  * Detect which coding agent is enabled in the workspace
70
- * Checks for Claude Code, Codex, and Gemini CLI installations
70
+ * Checks for Claude Code, Codex, Gemini CLI, GitHub Copilot, and Cursor installations
71
71
  * @param workspaceRoot - The workspace root directory
72
72
  * @returns Promise resolving to detected agent ID or null
73
73
  */
@@ -80,6 +80,20 @@ declare class CodingAgentService {
80
80
  name: string;
81
81
  description: string;
82
82
  }>;
83
+ /**
84
+ * Get the coding agent service instance
85
+ * @param agent - The coding agent to get service for
86
+ * @returns The service instance or null if not supported
87
+ */
88
+ private getCodingAgentService;
89
+ /**
90
+ * Update custom instructions/prompts for the coding agent
91
+ * Appends custom instruction prompt to the agent's configuration
92
+ * @param agent - The coding agent to update
93
+ * @param instructionPrompt - The instruction prompt to append
94
+ * @param customInstructionFile - Optional custom file path to write instructions to (e.g., '.claude/aicode-instructions.md')
95
+ */
96
+ updateCustomInstructions(agent: CodingAgent, instructionPrompt: string, customInstructionFile?: string): Promise<void>;
83
97
  /**
84
98
  * Setup MCP configuration for the selected coding agent
85
99
  * @param agent - The coding agent to configure
package/dist/index.d.ts CHANGED
@@ -67,7 +67,7 @@ declare class CodingAgentService {
67
67
  constructor(workspaceRoot: string);
68
68
  /**
69
69
  * Detect which coding agent is enabled in the workspace
70
- * Checks for Claude Code, Codex, and Gemini CLI installations
70
+ * Checks for Claude Code, Codex, Gemini CLI, GitHub Copilot, and Cursor installations
71
71
  * @param workspaceRoot - The workspace root directory
72
72
  * @returns Promise resolving to detected agent ID or null
73
73
  */
@@ -80,6 +80,20 @@ declare class CodingAgentService {
80
80
  name: string;
81
81
  description: string;
82
82
  }>;
83
+ /**
84
+ * Get the coding agent service instance
85
+ * @param agent - The coding agent to get service for
86
+ * @returns The service instance or null if not supported
87
+ */
88
+ private getCodingAgentService;
89
+ /**
90
+ * Update custom instructions/prompts for the coding agent
91
+ * Appends custom instruction prompt to the agent's configuration
92
+ * @param agent - The coding agent to update
93
+ * @param instructionPrompt - The instruction prompt to append
94
+ * @param customInstructionFile - Optional custom file path to write instructions to (e.g., '.claude/aicode-instructions.md')
95
+ */
96
+ updateCustomInstructions(agent: CodingAgent, instructionPrompt: string, customInstructionFile?: string): Promise<void>;
83
97
  /**
84
98
  * Setup MCP configuration for the selected coding agent
85
99
  * @param agent - The coding agent to configure
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./mcp-BmhiAfeF.js";
2
- import { BANNER_GRADIENT, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, cloneRepository, cloneSubdirectory, displayBanner, displayCompactBanner, fetchGitHubDirectoryContents, findWorkspaceRoot, gitInit, parseGitHubUrl } from "./services-DNldrNnu.js";
2
+ import { BANNER_GRADIENT, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, cloneRepository, cloneSubdirectory, displayBanner, displayCompactBanner, fetchGitHubDirectoryContents, findWorkspaceRoot, gitInit, parseGitHubUrl } from "./services-zrdafWTg.js";
3
3
 
4
4
  export { BANNER_GRADIENT, CodingAgentService, NewProjectService, THEME, TemplateSelectionService, TemplatesService, cloneRepository, cloneSubdirectory, displayBanner, displayCompactBanner, fetchGitHubDirectoryContents, findWorkspaceRoot, gitInit, parseGitHubUrl };
@@ -35,6 +35,10 @@ let execa = require("execa");
35
35
  execa = __toESM(execa);
36
36
  let __agiflowai_coding_agent_bridge = require("@agiflowai/coding-agent-bridge");
37
37
  __agiflowai_coding_agent_bridge = __toESM(__agiflowai_coding_agent_bridge);
38
+ let node_fs_promises = require("node:fs/promises");
39
+ node_fs_promises = __toESM(node_fs_promises);
40
+ let liquidjs = require("liquidjs");
41
+ liquidjs = __toESM(liquidjs);
38
42
  let node_os = require("node:os");
39
43
  node_os = __toESM(node_os);
40
44
 
@@ -194,11 +198,11 @@ function parseGitHubUrl(url) {
194
198
  if (treeMatch || blobMatch) {
195
199
  const match = treeMatch || blobMatch;
196
200
  return {
197
- owner: match[1],
198
- repo: match[2],
199
- repoUrl: `https://github.com/${match[1]}/${match[2]}.git`,
200
- branch: match[3],
201
- subdirectory: match[4],
201
+ owner: match?.[1],
202
+ repo: match?.[2],
203
+ repoUrl: `https://github.com/${match?.[1]}/${match?.[2]}.git`,
204
+ branch: match?.[3],
205
+ subdirectory: match?.[4],
202
206
  isSubdirectory: true
203
207
  };
204
208
  }
@@ -264,8 +268,8 @@ async function cloneRepository(repoUrl, targetFolder) {
264
268
  /**
265
269
  * Fetch directory listing from GitHub API
266
270
  */
267
- async function fetchGitHubDirectoryContents(owner, repo, path$3, branch = "main") {
268
- const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$3}?ref=${branch}`;
271
+ async function fetchGitHubDirectoryContents(owner, repo, path$4, branch = "main") {
272
+ const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path$4}?ref=${branch}`;
269
273
  const response = await fetch(url, { headers: {
270
274
  Accept: "application/vnd.github.v3+json",
271
275
  "User-Agent": "scaffold-mcp"
@@ -289,12 +293,14 @@ var CodingAgentService = class {
289
293
  }
290
294
  /**
291
295
  * Detect which coding agent is enabled in the workspace
292
- * Checks for Claude Code, Codex, and Gemini CLI installations
296
+ * Checks for Claude Code, Codex, Gemini CLI, GitHub Copilot, and Cursor installations
293
297
  * @param workspaceRoot - The workspace root directory
294
298
  * @returns Promise resolving to detected agent ID or null
295
299
  */
296
300
  static async detectCodingAgent(workspaceRoot) {
297
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;
298
304
  if (await new __agiflowai_coding_agent_bridge.CodexService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.CODEX;
299
305
  if (await new __agiflowai_coding_agent_bridge.GeminiCliService({ workspaceRoot }).isEnabled()) return __agiflowai_coding_agent_bridge.GEMINI_CLI;
300
306
  return null;
@@ -309,6 +315,16 @@ var CodingAgentService = class {
309
315
  name: "Claude Code",
310
316
  description: "Anthropic Claude Code CLI agent"
311
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
+ },
312
328
  {
313
329
  value: __agiflowai_coding_agent_bridge.CODEX,
314
330
  name: "Codex",
@@ -327,6 +343,48 @@ var CodingAgentService = class {
327
343
  ];
328
344
  }
329
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
+ /**
330
388
  * Setup MCP configuration for the selected coding agent
331
389
  * @param agent - The coding agent to configure
332
390
  */
@@ -336,19 +394,22 @@ var CodingAgentService = class {
336
394
  return;
337
395
  }
338
396
  __agiflowai_aicode_utils.print.info(`\nSetting up MCP for ${agent}...`);
339
- let service = null;
397
+ const service = this.getCodingAgentService(agent);
340
398
  let configLocation = "";
341
399
  let restartInstructions = "";
342
400
  if (agent === __agiflowai_coding_agent_bridge.CLAUDE_CODE) {
343
- service = new __agiflowai_coding_agent_bridge.ClaudeCodeService({ workspaceRoot: this.workspaceRoot });
344
401
  configLocation = ".mcp.json";
345
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";
346
409
  } else if (agent === __agiflowai_coding_agent_bridge.CODEX) {
347
- service = new __agiflowai_coding_agent_bridge.CodexService({ workspaceRoot: this.workspaceRoot });
348
410
  configLocation = "~/.codex/config.toml";
349
411
  restartInstructions = "Restart Codex CLI to load the new MCP servers";
350
412
  } else if (agent === __agiflowai_coding_agent_bridge.GEMINI_CLI) {
351
- service = new __agiflowai_coding_agent_bridge.GeminiCliService({ workspaceRoot: this.workspaceRoot });
352
413
  configLocation = "~/.gemini/settings.json";
353
414
  restartInstructions = "Restart Gemini CLI to load the new MCP servers";
354
415
  }
@@ -463,7 +524,6 @@ var NewProjectService = class {
463
524
  async createProjectDirectory(projectPath, projectName) {
464
525
  try {
465
526
  await fs_extra.mkdir(projectPath, { recursive: false });
466
- __agiflowai_aicode_utils.print.success(`Created project directory: ${projectPath}`);
467
527
  } catch (error) {
468
528
  if (error.code === "EEXIST") throw new Error(`Directory '${projectName}' already exists. Please choose a different name.`);
469
529
  throw error;
@@ -475,12 +535,10 @@ var NewProjectService = class {
475
535
  * @param projectPath - Destination path for the cloned repository
476
536
  */
477
537
  async cloneExistingRepository(repoUrl, projectPath) {
478
- __agiflowai_aicode_utils.print.info("Cloning repository...");
479
538
  try {
480
539
  const parsed = parseGitHubUrl(repoUrl.trim());
481
540
  if (parsed.isSubdirectory && parsed.branch && parsed.subdirectory) await cloneSubdirectory(parsed.repoUrl, parsed.branch, parsed.subdirectory, projectPath);
482
541
  else await cloneRepository(parsed.repoUrl, projectPath);
483
- __agiflowai_aicode_utils.print.success("Repository cloned successfully");
484
542
  } catch (error) {
485
543
  await fs_extra.remove(projectPath);
486
544
  throw new Error(`Failed to clone repository: ${error.message}`);
@@ -491,10 +549,8 @@ var NewProjectService = class {
491
549
  * @param projectPath - Path where git repository should be initialized
492
550
  */
493
551
  async initializeGitRepository(projectPath) {
494
- __agiflowai_aicode_utils.print.info("Initializing Git repository...");
495
552
  try {
496
553
  await gitInit(projectPath);
497
- __agiflowai_aicode_utils.print.success("Git repository initialized");
498
554
  } catch (error) {
499
555
  __agiflowai_aicode_utils.messages.warning(`Failed to initialize Git: ${error.message}`);
500
556
  }
@@ -511,6 +567,153 @@ var NewProjectService = class {
511
567
  }
512
568
  };
513
569
 
570
+ //#endregion
571
+ //#region src/instructions/specs/openspec.md?raw
572
+ var openspec_default = "When working on this project, follow the OpenSpec spec-driven development workflow{% if scaffoldMcp or architectMcp %} integrated with MCP tools{% endif %}.\n\n{% if scaffoldMcp %}\n\n## {% if scaffoldMcp %}1{% else %}1{% endif %}. Create Proposals with scaffold-mcp\n\nWhen implementing new features or changes, use scaffold-mcp MCP tools:\n\n**For new projects/features:**\n1. Use `list-boilerplates` MCP tool to see available templates\n2. Use `use-boilerplate` MCP tool to scaffold new projects{% if projectType == 'monolith' %} (set `monolith: true`){% elsif projectType == 'monorepo' %} (omit `monolith` parameter){% endif %}\n3. Use `list-scaffolding-methods` MCP tool to understand which methods can be used to add features\n4. Create OpenSpec proposal with available scaffolding methods in mind: \"Create an OpenSpec proposal for [feature description]\"\n\n**For adding features to existing code:**\n1. Use `list-scaffolding-methods` MCP tool with projectPath to see available features{% if projectType == 'monolith' %} (`projectPath` = workspace root){% elsif projectType == 'monorepo' %} (`projectPath` = project directory with project.json){% endif %}\n2. Review available methods and plan which ones to use for the feature\n3. Use `use-scaffold-method` MCP tool to generate boilerplate code\n4. Create OpenSpec proposal to capture the specs\n{% if projectType %}\n\n**Path Mapping ({% if projectType == 'monolith' %}Monolith{% else %}Monorepo{% endif %} Project):**\n{% if projectType == 'monolith' %}\n*This is a monolith project:*\n- Single project at workspace root\n- Config file: `toolkit.yaml` at root\n- All code in workspace root (src/, lib/, etc.)\n- When scaffolding: Use `monolith: true` parameter\n- When using scaffold methods: `projectPath` = workspace root\n\nExample: For workspace at `/path/to/project`:\n- Project config: `/path/to/project/toolkit.yaml`\n- Source code: `/path/to/project/src/`\n- OpenSpec: `/path/to/project/openspec/`\n{% else %}\n*This is a monorepo project:*\n- Multiple projects in subdirectories\n- Config file: `project.json` in each project\n- Projects in apps/, packages/, libs/, etc.\n- When scaffolding: Omit `monolith` parameter (defaults to false)\n- When using scaffold methods: `projectPath` = path to specific project\n\nExample: For workspace at `/path/to/workspace`:\n- App config: `/path/to/workspace/apps/my-app/project.json`\n- App source: `/path/to/workspace/apps/my-app/src/`\n- OpenSpec: `/path/to/workspace/openspec/` (workspace-level)\n{% endif %}\n{% endif %}\n\nAI will scaffold: openspec/changes/[feature-name]/ with proposal.md, tasks.md, and spec deltas\n{% endif %}\n{% if architectMcp %}\n\n## {% if scaffoldMcp %}2{% else %}1{% endif %}. Review & Validate with architect-mcp\n\nBefore and after editing files, use architect-mcp MCP tools:\n\n**Before editing:**\n- Use `get-file-design-pattern` MCP tool to understand:\n - Applicable design patterns from architect.yaml\n - Coding rules from RULES.yaml (must_do, should_do, must_not_do)\n - Code examples showing the patterns\n\n**After editing:**\n- Use `review-code-change` MCP tool to check for:\n - Must not do violations (critical issues)\n - Must do missing (required patterns not followed)\n - Should do suggestions (best practices)\n\n**Validate OpenSpec specs:**\n- Use `openspec validate [feature-name]` to check spec formatting\n- Iterate with AI until specs are agreed upon\n{% endif %}\n\n## {% if scaffoldMcp and architectMcp %}3{% elsif scaffoldMcp or architectMcp %}2{% else %}1{% endif %}. Implement{% if architectMcp %} with MCP-Guided Development{% endif %}\n\nDuring implementation:\n1. Ask AI to implement: \"Apply the OpenSpec change [feature-name]\"\n{% if architectMcp %}\n2. **Before each file edit**: Use `get-file-design-pattern` to understand patterns\n3. AI implements tasks from tasks.md following design patterns\n4. **After each file edit**: Use `review-code-change` to verify compliance\n5. Fix any violations before proceeding\n{% else %}\n2. AI implements tasks from tasks.md following the agreed specs\n{% endif %}\n\n## {% if scaffoldMcp and architectMcp %}4{% elsif scaffoldMcp or architectMcp %}3{% else %}2{% endif %}. Archive Completed Changes\n{% if scaffoldMcp or architectMcp %}\n\n## MCP Tools Reference\n{% endif %}\n{% if scaffoldMcp %}\n\n### scaffold-mcp\n- `list-boilerplates` - List available project templates\n- `use-boilerplate` - Create new project from template\n- `list-scaffolding-methods` - List features for existing project\n- `use-scaffold-method` - Add feature to existing project\n{% endif %}\n{% if architectMcp %}\n\n### architect-mcp\n- `get-file-design-pattern` - Get design patterns for file\n- `review-code-change` - Review code for violations\n{% endif %}\n\n## Workflow Summary\n\n{% if scaffoldMcp %}\n1. **Plan**: Use scaffold-mcp to generate boilerplate + OpenSpec proposal for specs\n{% else %}\n1. **Plan**: Create OpenSpec proposal with specs\n{% endif %}\n{% if architectMcp %}\n{% if scaffoldMcp %}2{% else %}2{% endif %}. **Design**: Use architect-mcp to understand patterns before editing\n{% endif %}\n{% if scaffoldMcp and architectMcp %}3{% elsif scaffoldMcp or architectMcp %}{% if architectMcp %}3{% else %}2{% endif %}{% else %}2{% endif %}. **Implement**: Follow specs{% if architectMcp %} and patterns{% endif %}\n{% if architectMcp %}\n{% if scaffoldMcp %}4{% else %}4{% endif %}. **Review**: Use architect-mcp to validate code quality\n{% endif %}\n{% if scaffoldMcp and architectMcp %}5{% elsif scaffoldMcp or architectMcp %}{% if architectMcp %}5{% else %}3{% endif %}{% else %}3{% endif %}. **Archive**: Merge specs into source of truth\n";
573
+
574
+ //#endregion
575
+ //#region src/specs/openspec.ts
576
+ /**
577
+ * OpenSpec configuration
578
+ */
579
+ const OPENSPEC_CONFIG = {
580
+ folderName: "openspec",
581
+ packageName: "@fission-ai/openspec",
582
+ name: "OpenSpec",
583
+ description: "Spec-driven development for AI coding assistants"
584
+ };
585
+ /**
586
+ * Bridge implementation for OpenSpec
587
+ *
588
+ * Provides integration with OpenSpec spec tool through a standardized interface.
589
+ */
590
+ var OpenSpecBridge = class {
591
+ /**
592
+ * Check if OpenSpec is enabled/installed in the workspace
593
+ *
594
+ * @param workspaceRoot - Absolute path to the workspace root directory
595
+ * @returns Promise resolving to true if OpenSpec folder exists, false otherwise
596
+ */
597
+ async isEnabled(workspaceRoot) {
598
+ try {
599
+ const openspecPath = node_path.default.join(workspaceRoot, OPENSPEC_CONFIG.folderName);
600
+ await node_fs_promises.default.access(openspecPath);
601
+ return true;
602
+ } catch {
603
+ return false;
604
+ }
605
+ }
606
+ /**
607
+ * Initialize OpenSpec in the workspace
608
+ *
609
+ * Runs `npx @fission-ai/openspec init` interactively, allowing the user
610
+ * to configure OpenSpec during setup.
611
+ *
612
+ * @param workspaceRoot - Absolute path to the workspace root directory
613
+ * @throws Error if initialization fails
614
+ */
615
+ async initialize(workspaceRoot) {
616
+ try {
617
+ await (0, execa.execa)("npx", [OPENSPEC_CONFIG.packageName, "init"], {
618
+ cwd: workspaceRoot,
619
+ stdio: "inherit"
620
+ });
621
+ } catch (error) {
622
+ throw new Error(`Failed to initialize ${OPENSPEC_CONFIG.name}: ${error.message}`);
623
+ }
624
+ }
625
+ /**
626
+ * Generate agent instruction prompt for OpenSpec with MCP integration
627
+ *
628
+ * Creates a comprehensive instruction prompt that guides AI agents to use
629
+ * spec-driven development workflow with OpenSpec, leveraging enabled MCP servers.
630
+ *
631
+ * @param enabledMcps - Configuration of which MCP servers are enabled
632
+ * @returns Promise resolving to the instruction prompt string
633
+ */
634
+ async updateInstruction(enabledMcps) {
635
+ return (await new liquidjs.Liquid().parseAndRender(openspec_default, enabledMcps)).trim();
636
+ }
637
+ };
638
+
639
+ //#endregion
640
+ //#region src/services/SpecToolService.ts
641
+ /**
642
+ * Available spec tools
643
+ */
644
+ let SpecTool = /* @__PURE__ */ function(SpecTool$1) {
645
+ SpecTool$1["OPENSPEC"] = "openspec";
646
+ return SpecTool$1;
647
+ }({});
648
+ /**
649
+ * Spec tool information
650
+ */
651
+ const SPEC_TOOL_INFO = { [SpecTool.OPENSPEC]: {
652
+ name: "OpenSpec",
653
+ description: "Spec-driven development for AI coding assistants",
654
+ docUrl: "https://github.com/Fission-AI/OpenSpec"
655
+ } };
656
+ /**
657
+ * Service for managing spec tools (e.g., OpenSpec)
658
+ *
659
+ * This service acts as a facade for spec tool operations, using bridge
660
+ * implementations to interact with specific spec tools.
661
+ */
662
+ var SpecToolService = class {
663
+ bridge;
664
+ constructor(workspaceRoot, specTool = SpecTool.OPENSPEC, codingAgentService) {
665
+ this.workspaceRoot = workspaceRoot;
666
+ this.codingAgentService = codingAgentService;
667
+ this.bridge = this.createBridge(specTool);
668
+ }
669
+ /**
670
+ * Create a bridge instance for the specified spec tool
671
+ */
672
+ createBridge(specTool) {
673
+ switch (specTool) {
674
+ case SpecTool.OPENSPEC: return new OpenSpecBridge();
675
+ default: throw new Error(`Unsupported spec tool: ${specTool}`);
676
+ }
677
+ }
678
+ /**
679
+ * Detect if a spec tool is installed in the workspace
680
+ */
681
+ async detectSpecTool() {
682
+ try {
683
+ return await this.bridge.isEnabled(this.workspaceRoot) ? SpecTool.OPENSPEC : null;
684
+ } catch (error) {
685
+ __agiflowai_aicode_utils.print.error(`Error detecting spec tool: ${error.message}`);
686
+ return null;
687
+ }
688
+ }
689
+ /**
690
+ * Initialize the spec tool in the workspace
691
+ */
692
+ async initializeSpec() {
693
+ await this.bridge.initialize(this.workspaceRoot);
694
+ }
695
+ /**
696
+ * Update spec tool agent instructions
697
+ *
698
+ * Generates instruction prompt and updates the coding agent's custom instructions
699
+ *
700
+ * @param enabledMcps - Configuration of which MCP servers are enabled
701
+ * @param codingAgent - The coding agent to update instructions for
702
+ * @param customInstructionFile - Optional custom file to write instructions to (e.g., '.claude/aicode-instructions.md')
703
+ * @returns Promise resolving to the instruction prompt string
704
+ */
705
+ async updateInstructions(enabledMcps, codingAgent, customInstructionFile) {
706
+ const prompt = await this.bridge.updateInstruction(enabledMcps);
707
+ if (this.codingAgentService && codingAgent) await this.codingAgentService.updateCustomInstructions(codingAgent, prompt, customInstructionFile ?? ".claude/aicode-instructions.md");
708
+ else {
709
+ __agiflowai_aicode_utils.print.info("\nGenerated OpenSpec instruction prompt:");
710
+ __agiflowai_aicode_utils.print.info("\nYou can append this to your CLAUDE.md or agent config file:");
711
+ __agiflowai_aicode_utils.print.info(`\n${prompt}`);
712
+ }
713
+ return prompt;
714
+ }
715
+ };
716
+
514
717
  //#endregion
515
718
  //#region src/services/TemplateSelectionService.ts
516
719
  var TemplateSelectionService = class {
@@ -524,7 +727,6 @@ var TemplateSelectionService = class {
524
727
  * @returns Path to the tmp directory containing templates
525
728
  */
526
729
  async downloadTemplatesToTmp(repoConfig) {
527
- __agiflowai_aicode_utils.print.info(`Downloading templates from ${repoConfig.owner}/${repoConfig.repo}...`);
528
730
  try {
529
731
  await fs_extra.ensureDir(this.tmpDir);
530
732
  const contents = await fetchGitHubDirectoryContents(repoConfig.owner, repoConfig.repo, repoConfig.path, repoConfig.branch);
@@ -677,10 +879,7 @@ var TemplateSelectionService = class {
677
879
  */
678
880
  async cleanup() {
679
881
  try {
680
- if (await fs_extra.pathExists(this.tmpDir)) {
681
- await fs_extra.remove(this.tmpDir);
682
- __agiflowai_aicode_utils.print.info("Cleaned up temporary files");
683
- }
882
+ if (await fs_extra.pathExists(this.tmpDir)) await fs_extra.remove(this.tmpDir);
684
883
  } catch (error) {
685
884
  __agiflowai_aicode_utils.print.warning(`Warning: Failed to clean up tmp directory: ${error.message}`);
686
885
  }
@@ -704,19 +903,19 @@ var TemplatesService = class {
704
903
  return;
705
904
  }
706
905
  __agiflowai_aicode_utils.print.info(`Found ${templateDirs.length} template(s)`);
707
- let downloaded = 0;
708
- let skipped = 0;
906
+ let _downloaded = 0;
907
+ let _skipped = 0;
709
908
  for (const template of templateDirs) {
710
909
  const targetFolder = node_path.default.join(templatesPath, template.name);
711
910
  if (await fs_extra.pathExists(targetFolder)) {
712
911
  __agiflowai_aicode_utils.print.info(`Skipping ${template.name} (already exists)`);
713
- skipped++;
912
+ _skipped++;
714
913
  continue;
715
914
  }
716
915
  __agiflowai_aicode_utils.print.info(`Downloading ${template.name}...`);
717
916
  await cloneSubdirectory(`https://github.com/${repoConfig.owner}/${repoConfig.repo}.git`, repoConfig.branch, template.path, targetFolder);
718
917
  __agiflowai_aicode_utils.print.success(`Downloaded ${template.name}`);
719
- downloaded++;
918
+ _downloaded++;
720
919
  }
721
920
  __agiflowai_aicode_utils.print.success("\nAll templates downloaded successfully!");
722
921
  } catch (error) {
@@ -785,6 +984,24 @@ Object.defineProperty(exports, 'NewProjectService', {
785
984
  return NewProjectService;
786
985
  }
787
986
  });
987
+ Object.defineProperty(exports, 'SPEC_TOOL_INFO', {
988
+ enumerable: true,
989
+ get: function () {
990
+ return SPEC_TOOL_INFO;
991
+ }
992
+ });
993
+ Object.defineProperty(exports, 'SpecTool', {
994
+ enumerable: true,
995
+ get: function () {
996
+ return SpecTool;
997
+ }
998
+ });
999
+ Object.defineProperty(exports, 'SpecToolService', {
1000
+ enumerable: true,
1001
+ get: function () {
1002
+ return SpecToolService;
1003
+ }
1004
+ });
788
1005
  Object.defineProperty(exports, 'THEME', {
789
1006
  enumerable: true,
790
1007
  get: function () {