@bugzy-ai/bugzy 1.2.1 → 1.3.0

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/cli/index.js CHANGED
@@ -560,7 +560,9 @@ Document all findings including:
560
560
  \`\`\`
561
561
 
562
562
  #### 3.2 Launch Test Runner Agent
563
- Invoke the test-runner agent with special exploration instructions:
563
+ {{INVOKE_TEST_RUNNER}}
564
+
565
+ Execute the exploration test case with special exploration instructions:
564
566
 
565
567
  \`\`\`
566
568
  Execute the exploration test case at ./test-cases/EXPLORATION-TEMP.md with focus on discovery and documentation.
@@ -1031,10 +1033,10 @@ Before invoking the agent, identify the test cases for the current area:
1031
1033
 
1032
1034
  #### Step 2.2: Invoke test-code-generator Agent
1033
1035
 
1034
- Use the test-code-generator agent for the current area with the following context:
1036
+ {{INVOKE_TEST_CODE_GENERATOR}} for the current area with the following context:
1035
1037
 
1036
1038
  **Agent Invocation:**
1037
- "Use the test-code-generator agent to automate test cases for the [AREA_NAME] area.
1039
+ "Automate test cases for the [AREA_NAME] area.
1038
1040
 
1039
1041
  **Context:**
1040
1042
  - Area: [AREA_NAME]
@@ -1174,10 +1176,10 @@ Provide a comprehensive summary showing:
1174
1176
  role: "documentation-researcher",
1175
1177
  contentBlock: `#### 1.4 Gather Product Documentation
1176
1178
 
1177
- Use the documentation-researcher agent to gather comprehensive product documentation:
1179
+ {{INVOKE_DOCUMENTATION_RESEARCHER}} to gather comprehensive product documentation:
1178
1180
 
1179
1181
  \`\`\`
1180
- Use the documentation-researcher agent to explore all available product documentation, specifically focusing on:
1182
+ Explore all available product documentation, specifically focusing on:
1181
1183
  - UI elements and workflows
1182
1184
  - User interactions and navigation paths
1183
1185
  - Form fields and validation rules
@@ -1191,10 +1193,9 @@ Use the documentation-researcher agent to explore all available product document
1191
1193
  role: "team-communicator",
1192
1194
  contentBlock: `### Step 4.5: Team Communication
1193
1195
 
1194
- Use the team-communicator agent to notify the product team about the new test cases and automated tests:
1196
+ {{INVOKE_TEAM_COMMUNICATOR}} to notify the product team about the new test cases and automated tests:
1195
1197
 
1196
1198
  \`\`\`
1197
- Use the team-communicator agent to:
1198
1199
  1. Post an update about test case and automation creation
1199
1200
  2. Provide summary of coverage:
1200
1201
  - Number of manual test cases created
@@ -1463,10 +1464,10 @@ Provide a summary of:
1463
1464
  role: "documentation-researcher",
1464
1465
  contentBlock: `### Step 2: Gather comprehensive project documentation
1465
1466
 
1466
- Use the documentation-researcher agent to explore and gather all available project information and other documentation sources. This ensures the test plan is based on complete and current information.
1467
+ {{INVOKE_DOCUMENTATION_RESEARCHER}} to explore and gather all available project information and other documentation sources. This ensures the test plan is based on complete and current information.
1467
1468
 
1468
1469
  \`\`\`
1469
- Use the documentation-researcher agent to explore all available project documentation related to: $ARGUMENTS
1470
+ Explore all available project documentation related to: $ARGUMENTS
1470
1471
 
1471
1472
  Specifically gather:
1472
1473
  - Product specifications and requirements
@@ -1491,10 +1492,9 @@ The agent will:
1491
1492
  role: "team-communicator",
1492
1493
  contentBlock: `### Step 7.5: Team Communication
1493
1494
 
1494
- Use the team-communicator agent to notify the product team about the new test plan:
1495
+ {{INVOKE_TEAM_COMMUNICATOR}} to notify the product team about the new test plan:
1495
1496
 
1496
1497
  \`\`\`
1497
- Use the team-communicator agent to:
1498
1498
  1. Post an update about the test plan creation
1499
1499
  2. Provide a brief summary of coverage areas and key features
1500
1500
  3. Mention any areas that need exploration or clarification
@@ -1747,7 +1747,7 @@ Classify the ambiguity level to determine appropriate response:
1747
1747
  #### Clarification Approach by Severity
1748
1748
 
1749
1749
  **For CRITICAL/HIGH ambiguity:**
1750
- 1. **Use team-communicator to ask specific questions**
1750
+ 1. **{{INVOKE_TEAM_COMMUNICATOR}} to ask specific questions**
1751
1751
  2. **WAIT for response before proceeding**
1752
1752
  3. **Document the clarification request in event history**
1753
1753
 
@@ -2020,7 +2020,7 @@ ${KNOWLEDGE_BASE_UPDATE_INSTRUCTIONS}`,
2020
2020
  contentBlock: `#### 3.3 Use Documentation Researcher if Needed
2021
2021
  For events mentioning unknown features or components:
2022
2022
  \`\`\`
2023
- Use documentation-researcher agent to find information about: [component/feature]
2023
+ {{INVOKE_DOCUMENTATION_RESEARCHER}} to find information about: [component/feature]
2024
2024
  \`\`\``
2025
2025
  },
2026
2026
  {
@@ -2029,7 +2029,7 @@ Use documentation-researcher agent to find information about: [component/feature
2029
2029
 
2030
2030
  When an issue needs to be tracked (task type: report_bug or update_story):
2031
2031
  \`\`\`
2032
- Use issue-tracker agent to:
2032
+ {{INVOKE_ISSUE_TRACKER}}
2033
2033
  1. Check for duplicate issues in the tracking system
2034
2034
  2. For bugs: Create detailed bug report with:
2035
2035
  - Clear, descriptive title
@@ -2307,7 +2307,7 @@ For each failed test:
2307
2307
  For each test classified as **[TEST ISSUE]**, use the test-debugger-fixer agent to automatically fix the test:
2308
2308
 
2309
2309
  \`\`\`
2310
- Use the test-debugger-fixer agent to fix test issues:
2310
+ {{INVOKE_TEST_DEBUGGER_FIXER}}
2311
2311
 
2312
2312
  For each failed test classified as a test issue (not a product bug), provide:
2313
2313
  - Test run timestamp: [from manifest.timestamp]
@@ -2402,7 +2402,7 @@ After triage in Step 5.1, for tests classified as **[PRODUCT BUG]**, use the iss
2402
2402
  For each bug to report, use the issue-tracker agent:
2403
2403
 
2404
2404
  \`\`\`
2405
- Use issue-tracker agent to:
2405
+ {{INVOKE_ISSUE_TRACKER}}
2406
2406
  1. Check for duplicate bugs in the tracking system
2407
2407
  - The agent will automatically search for similar existing issues
2408
2408
  - It maintains memory of recently reported issues
@@ -2473,10 +2473,11 @@ After issue tracker agent completes, create a summary:
2473
2473
  role: "team-communicator",
2474
2474
  contentBlock: `### Step 6: Team Communication
2475
2475
 
2476
- Use the team-communicator agent to notify the product team about test execution:
2476
+ {{INVOKE_TEAM_COMMUNICATOR}}
2477
+
2478
+ Notify the product team about test execution:
2477
2479
 
2478
2480
  \`\`\`
2479
- Use the team-communicator agent to:
2480
2481
  1. Post test execution summary with key statistics
2481
2482
  2. Highlight critical failures that need immediate attention
2482
2483
  3. Share important learnings about product behavior
@@ -2820,7 +2821,7 @@ Read and analyze the JSON report:
2820
2821
  For each test classified as **[TEST ISSUE]**, use the test-debugger-fixer agent to automatically fix the test:
2821
2822
 
2822
2823
  \`\`\`
2823
- Use the test-debugger-fixer agent to fix test issues:
2824
+ {{INVOKE_TEST_DEBUGGER_FIXER}}
2824
2825
 
2825
2826
  For each failed test classified as a test issue (not a product bug), provide:
2826
2827
  - Test file path: [from JSON report]
@@ -2861,7 +2862,7 @@ Classification guidelines:
2861
2862
  #### 4A.4 Fix Test Issues Automatically
2862
2863
 
2863
2864
  For tests classified as [TEST ISSUE]:
2864
- - Use test-debugger-fixer agent to analyze and fix
2865
+ - {{INVOKE_TEST_DEBUGGER_FIXER}} to analyze and fix
2865
2866
  - Agent debugs with browser if needed
2866
2867
  - Applies fix (selector update, wait condition, assertion correction)
2867
2868
  - Reruns test to verify fix (10x for flaky tests)
@@ -2877,7 +2878,7 @@ Track fixed tests with:
2877
2878
  {{ISSUE_TRACKER_INSTRUCTIONS}}
2878
2879
 
2879
2880
  For tests classified as [PRODUCT BUG]:
2880
- - Use issue-tracker agent to create bug reports
2881
+ - {{INVOKE_ISSUE_TRACKER}} to create bug reports
2881
2882
  - Agent checks for duplicates automatically
2882
2883
  - Creates detailed report with:
2883
2884
  - Title, description, reproduction steps
@@ -3193,10 +3194,10 @@ Format as comprehensive markdown report for terminal display:
3193
3194
 
3194
3195
  {{TEAM_COMMUNICATOR_INSTRUCTIONS}}
3195
3196
 
3196
- Use team-communicator agent to post concise results to Slack thread:
3197
+ {{INVOKE_TEAM_COMMUNICATOR}} to post concise results to Slack thread:
3197
3198
 
3198
3199
  \`\`\`
3199
- Use the team-communicator agent to post verification results.
3200
+ Post verification results.
3200
3201
 
3201
3202
  **Channel**: [from CHANGE_CONTEXT.slackChannel]
3202
3203
  **Thread**: [from CHANGE_CONTEXT.slackThread]
@@ -3377,10 +3378,10 @@ A successful verification includes:
3377
3378
  role: "documentation-researcher",
3378
3379
  contentBlock: `#### Research Project Documentation
3379
3380
 
3380
- Use the documentation-researcher agent to gather comprehensive context about the changed features:
3381
+ {{INVOKE_DOCUMENTATION_RESEARCHER}} to gather comprehensive context about the changed features:
3381
3382
 
3382
3383
  \`\`\`
3383
- Use the documentation-researcher agent to explore project documentation related to the changes.
3384
+ Explore project documentation related to the changes.
3384
3385
 
3385
3386
  Specifically gather:
3386
3387
  - Product specifications for affected features
@@ -3412,10 +3413,9 @@ Use this information to:
3412
3413
  role: "issue-tracker",
3413
3414
  contentBlock: `#### Log Product Bugs
3414
3415
 
3415
- For tests classified as **[PRODUCT BUG]**, use the issue-tracker agent to log bugs:
3416
+ For tests classified as **[PRODUCT BUG]**, {{INVOKE_ISSUE_TRACKER}} to log bugs:
3416
3417
 
3417
3418
  \`\`\`
3418
- Use issue-tracker agent to:
3419
3419
  1. Check for duplicate bugs in the tracking system
3420
3420
  - The agent will automatically search for similar existing issues
3421
3421
  - It maintains memory of recently reported issues
@@ -3464,10 +3464,9 @@ Use issue-tracker agent to:
3464
3464
  role: "team-communicator",
3465
3465
  contentBlock: `#### Team Communication
3466
3466
 
3467
- Use the team-communicator agent to share verification results (primarily for Slack trigger, but can be used for other triggers):
3467
+ {{INVOKE_TEAM_COMMUNICATOR}} to share verification results (primarily for Slack trigger, but can be used for other triggers):
3468
3468
 
3469
3469
  \`\`\`
3470
- Use the team-communicator agent to:
3471
3470
  1. Post verification results summary
3472
3471
  2. Highlight critical failures that need immediate attention
3473
3472
  3. Share bugs logged with issue tracker links
@@ -3590,13 +3589,14 @@ var init_tasks = __esm({
3590
3589
  init_esm_shims();
3591
3590
  import { Command } from "commander";
3592
3591
  import { readFileSync as readFileSync5 } from "fs";
3593
- import { join as join10, dirname as dirname3 } from "path";
3592
+ import { join as join12, dirname as dirname7 } from "path";
3594
3593
  import { fileURLToPath as fileURLToPath2 } from "url";
3595
3594
  import chalk3 from "chalk";
3596
3595
 
3597
3596
  // src/cli/commands/start.ts
3598
3597
  init_esm_shims();
3599
3598
  import { spawn } from "child_process";
3599
+ import * as path5 from "path";
3600
3600
  import chalk from "chalk";
3601
3601
  import ora from "ora";
3602
3602
 
@@ -3604,6 +3604,97 @@ import ora from "ora";
3604
3604
  init_esm_shims();
3605
3605
  import * as fs from "fs";
3606
3606
  import * as path2 from "path";
3607
+
3608
+ // src/core/tool-profile.ts
3609
+ init_esm_shims();
3610
+ var DEFAULT_TOOL = "claude-code";
3611
+ var CLAUDE_CODE_PROFILE = {
3612
+ id: "claude-code",
3613
+ name: "Claude Code",
3614
+ cliCommand: "claude",
3615
+ commandsDir: ".claude/commands",
3616
+ agentsDir: ".claude/agents",
3617
+ mcpConfigPath: ".mcp.json",
3618
+ mcpFormat: "json",
3619
+ memoryFile: "CLAUDE.md",
3620
+ commandFrontmatter: true,
3621
+ agentFrontmatter: true,
3622
+ commandInvocationPrefix: "/",
3623
+ commandExtension: ".md",
3624
+ agentExtension: ".md"
3625
+ };
3626
+ var CURSOR_PROFILE = {
3627
+ id: "cursor",
3628
+ name: "Cursor",
3629
+ cliCommand: "cursor-agent",
3630
+ commandsDir: ".cursor/commands",
3631
+ agentsDir: ".cursor/agents",
3632
+ mcpConfigPath: ".cursor/mcp.json",
3633
+ mcpFormat: "json",
3634
+ memoryFile: "AGENTS.md",
3635
+ // Cursor now uses AGENTS.md (.cursorrules deprecated as of v0.45+)
3636
+ commandFrontmatter: false,
3637
+ // Cursor uses plain markdown
3638
+ agentFrontmatter: false,
3639
+ // Agent files are plain markdown for CLI invocation
3640
+ commandInvocationPrefix: "/",
3641
+ commandExtension: ".md",
3642
+ agentExtension: ".md"
3643
+ };
3644
+ var CODEX_PROFILE = {
3645
+ id: "codex",
3646
+ name: "Codex CLI",
3647
+ cliCommand: "codex",
3648
+ commandsDir: ".codex/prompts",
3649
+ // Codex uses prompts directory
3650
+ agentsDir: ".codex/agents",
3651
+ mcpConfigPath: ".codex/config.toml",
3652
+ // Project-local via CODEX_HOME
3653
+ mcpFormat: "toml",
3654
+ homeEnvVar: "CODEX_HOME",
3655
+ // Set to project root for project-local config
3656
+ memoryFile: "AGENTS.md",
3657
+ commandFrontmatter: true,
3658
+ // Codex prompts support frontmatter
3659
+ agentFrontmatter: false,
3660
+ // Agent files are plain markdown for CLI invocation
3661
+ commandInvocationPrefix: "/prompts:",
3662
+ commandExtension: ".md",
3663
+ agentExtension: ".md"
3664
+ };
3665
+ var TOOL_PROFILES = {
3666
+ "claude-code": CLAUDE_CODE_PROFILE,
3667
+ "cursor": CURSOR_PROFILE,
3668
+ "codex": CODEX_PROFILE
3669
+ };
3670
+ function getToolProfile(toolId) {
3671
+ const profile = TOOL_PROFILES[toolId];
3672
+ if (!profile) {
3673
+ throw new Error(`Unknown tool: ${toolId}`);
3674
+ }
3675
+ return profile;
3676
+ }
3677
+ function getToolOptions() {
3678
+ return [
3679
+ {
3680
+ value: "claude-code",
3681
+ label: "Claude Code (CLI or Cloud)",
3682
+ hint: "Anthropic's official CLI - recommended"
3683
+ },
3684
+ {
3685
+ value: "cursor",
3686
+ label: "Cursor (Experimental)",
3687
+ hint: "VS Code-based AI editor - experimental support"
3688
+ },
3689
+ {
3690
+ value: "codex",
3691
+ label: "Codex CLI (Experimental)",
3692
+ hint: "OpenAI's terminal-based agent - experimental support"
3693
+ }
3694
+ ];
3695
+ }
3696
+
3697
+ // src/cli/utils/config.ts
3607
3698
  async function loadConfig(configPath = ".bugzy/config.json") {
3608
3699
  const fullPath = path2.join(process.cwd(), configPath);
3609
3700
  if (!fs.existsSync(fullPath)) {
@@ -3612,6 +3703,9 @@ async function loadConfig(configPath = ".bugzy/config.json") {
3612
3703
  try {
3613
3704
  const content = fs.readFileSync(fullPath, "utf-8");
3614
3705
  const config = JSON.parse(content);
3706
+ if (!config.tool) {
3707
+ config.tool = DEFAULT_TOOL;
3708
+ }
3615
3709
  return config;
3616
3710
  } catch (error) {
3617
3711
  console.error(`Error loading config from ${fullPath}:`, error);
@@ -3636,15 +3730,19 @@ function configExists(configPath = ".bugzy/config.json") {
3636
3730
  const fullPath = path2.join(process.cwd(), configPath);
3637
3731
  return fs.existsSync(fullPath);
3638
3732
  }
3639
- function createDefaultConfig(projectName) {
3733
+ function createDefaultConfig(projectName, tool = DEFAULT_TOOL) {
3640
3734
  return {
3641
3735
  version: "1.0.0",
3736
+ tool,
3642
3737
  project: {
3643
3738
  name: projectName
3644
3739
  },
3645
3740
  subagents: {}
3646
3741
  };
3647
3742
  }
3743
+ function getToolFromConfig(config) {
3744
+ return config.tool || DEFAULT_TOOL;
3745
+ }
3648
3746
 
3649
3747
  // src/cli/utils/env.ts
3650
3748
  init_esm_shims();
@@ -3707,13 +3805,19 @@ function validateEnvVars(mcpServers, envVars) {
3707
3805
  init_esm_shims();
3708
3806
  import * as fs3 from "fs";
3709
3807
  import * as path4 from "path";
3710
- function validateProjectStructure() {
3808
+ async function validateProjectStructure() {
3809
+ const config = await loadConfig();
3810
+ const tool = config ? getToolFromConfig(config) : DEFAULT_TOOL;
3811
+ const toolProfile = getToolProfile(tool);
3711
3812
  const requiredDirs = [
3712
3813
  ".bugzy",
3713
3814
  ".bugzy/runtime",
3714
- ".claude",
3715
- ".claude/commands",
3716
- ".claude/agents"
3815
+ path4.dirname(toolProfile.commandsDir),
3816
+ // .claude, .cursor, or .codex
3817
+ toolProfile.commandsDir,
3818
+ // .claude/commands, .cursor/commands, .codex/prompts
3819
+ toolProfile.agentsDir
3820
+ // .claude/agents, .cursor/agents, .codex/agents
3717
3821
  ];
3718
3822
  const requiredFiles = [
3719
3823
  ".bugzy/config.json",
@@ -3739,10 +3843,10 @@ function validateProjectStructure() {
3739
3843
  }
3740
3844
  return true;
3741
3845
  }
3742
- async function checkClaudeAvailable() {
3846
+ async function checkToolAvailable(command) {
3743
3847
  const { spawn: spawn2 } = await import("child_process");
3744
3848
  return new Promise((resolve) => {
3745
- const proc = spawn2("which", ["claude"]);
3849
+ const proc = spawn2("which", [command]);
3746
3850
  proc.on("close", (code) => {
3747
3851
  resolve(code === 0);
3748
3852
  });
@@ -3787,9 +3891,11 @@ async function startSession(prompt) {
3787
3891
  process.exit(1);
3788
3892
  }
3789
3893
  spinner.succeed(chalk.green("Configuration loaded"));
3894
+ const tool = getToolFromConfig(config);
3895
+ const toolProfile = getToolProfile(tool);
3790
3896
  spinner = ora("Validating project structure").start();
3791
3897
  try {
3792
- validateProjectStructure();
3898
+ await validateProjectStructure();
3793
3899
  spinner.succeed(chalk.green("Project structure validated"));
3794
3900
  } catch (error) {
3795
3901
  spinner.fail(chalk.red("Invalid project structure"));
@@ -3797,15 +3903,22 @@ async function startSession(prompt) {
3797
3903
  console.log(chalk.yellow("\nRun"), chalk.cyan("bugzy setup"), chalk.yellow("to fix the project structure"));
3798
3904
  process.exit(1);
3799
3905
  }
3800
- spinner = ora("Checking Claude Code availability").start();
3801
- const claudeAvailable = await checkClaudeAvailable();
3802
- if (!claudeAvailable) {
3803
- spinner.fail(chalk.red("Claude Code CLI not found"));
3804
- console.log(chalk.yellow("\nPlease install Claude Code:"));
3805
- console.log(chalk.cyan(" https://claude.com/claude-code"));
3906
+ spinner = ora(`Checking ${toolProfile.name} availability`).start();
3907
+ const toolAvailable = await checkToolAvailable(toolProfile.cliCommand);
3908
+ if (!toolAvailable) {
3909
+ spinner.fail(chalk.red(`${toolProfile.name} CLI not found`));
3910
+ console.log(chalk.yellow(`
3911
+ Please install ${toolProfile.name}:`));
3912
+ if (tool === "claude-code") {
3913
+ console.log(chalk.cyan(" https://claude.com/claude-code"));
3914
+ } else if (tool === "cursor") {
3915
+ console.log(chalk.cyan(" https://www.cursor.com/"));
3916
+ } else if (tool === "codex") {
3917
+ console.log(chalk.cyan(" npm install -g @openai/codex"));
3918
+ }
3806
3919
  process.exit(1);
3807
3920
  }
3808
- spinner.succeed(chalk.green("Claude Code CLI found"));
3921
+ spinner.succeed(chalk.green(`${toolProfile.name} CLI found`));
3809
3922
  spinner = ora("Loading environment variables").start();
3810
3923
  const envVars = loadEnvFiles();
3811
3924
  const envCount = Object.keys(envVars).length;
@@ -3823,14 +3936,20 @@ async function startSession(prompt) {
3823
3936
  process.exit(1);
3824
3937
  }
3825
3938
  spinner.succeed(chalk.green("All required MCP secrets present"));
3826
- console.log(chalk.green.bold("\n\u{1F680} Launching Claude Code...\n"));
3939
+ console.log(chalk.green.bold(`
3940
+ \u{1F680} Launching ${toolProfile.name}...
3941
+ `));
3827
3942
  const args = prompt ? [prompt] : [];
3828
- const claude = spawn("claude", args, {
3943
+ const spawnEnv = { ...process.env, ...envVars };
3944
+ if (toolProfile.homeEnvVar) {
3945
+ spawnEnv[toolProfile.homeEnvVar] = path5.join(process.cwd(), ".codex");
3946
+ }
3947
+ const child = spawn(toolProfile.cliCommand, args, {
3829
3948
  cwd: process.cwd(),
3830
- env: { ...process.env, ...envVars },
3949
+ env: spawnEnv,
3831
3950
  stdio: "inherit"
3832
3951
  });
3833
- claude.on("close", (code) => {
3952
+ child.on("close", (code) => {
3834
3953
  if (code === 0) {
3835
3954
  console.log(chalk.green("\n\u2713 Session ended successfully"));
3836
3955
  } else {
@@ -3838,15 +3957,16 @@ async function startSession(prompt) {
3838
3957
  \u2713 Session ended (exit code: ${code})`));
3839
3958
  }
3840
3959
  });
3841
- claude.on("error", (error) => {
3842
- console.error(chalk.red("\n\u2717 Error launching Claude Code:"), error);
3960
+ child.on("error", (error) => {
3961
+ console.error(chalk.red(`
3962
+ \u2717 Error launching ${toolProfile.name}:`), error);
3843
3963
  process.exit(1);
3844
3964
  });
3845
3965
  }
3846
3966
 
3847
3967
  // src/cli/commands/setup.ts
3848
3968
  init_esm_shims();
3849
- import * as path11 from "path";
3969
+ import * as path12 from "path";
3850
3970
  import chalk2 from "chalk";
3851
3971
  import inquirer from "inquirer";
3852
3972
  import ora2 from "ora";
@@ -6481,27 +6601,31 @@ function buildSubagentConfig(role, integration) {
6481
6601
  // src/cli/generators/structure.ts
6482
6602
  init_esm_shims();
6483
6603
  import * as fs4 from "fs";
6484
- import * as path5 from "path";
6485
- async function createProjectStructure() {
6604
+ import * as path6 from "path";
6605
+ async function createProjectStructure(tool = DEFAULT_TOOL) {
6486
6606
  const cwd = process.cwd();
6607
+ const toolProfile = getToolProfile(tool);
6487
6608
  const bugzyDirs = [
6488
6609
  ".bugzy",
6489
6610
  ".bugzy/runtime",
6490
6611
  ".bugzy/runtime/templates"
6491
6612
  ];
6492
6613
  for (const dir of bugzyDirs) {
6493
- const dirPath = path5.join(cwd, dir);
6614
+ const dirPath = path6.join(cwd, dir);
6494
6615
  if (!fs4.existsSync(dirPath)) {
6495
6616
  fs4.mkdirSync(dirPath, { recursive: true });
6496
6617
  }
6497
6618
  }
6498
- const claudeDirs = [
6499
- ".claude",
6500
- ".claude/commands",
6501
- ".claude/agents"
6619
+ const toolDirs = [
6620
+ path6.dirname(toolProfile.commandsDir),
6621
+ // .claude, .cursor, or .codex
6622
+ toolProfile.commandsDir,
6623
+ // .claude/commands, .cursor/commands, .codex/prompts
6624
+ toolProfile.agentsDir
6625
+ // .claude/agents, .cursor/agents, .codex/agents
6502
6626
  ];
6503
- for (const dir of claudeDirs) {
6504
- const dirPath = path5.join(cwd, dir);
6627
+ for (const dir of toolDirs) {
6628
+ const dirPath = path6.join(cwd, dir);
6505
6629
  if (!fs4.existsSync(dirPath)) {
6506
6630
  fs4.mkdirSync(dirPath, { recursive: true });
6507
6631
  }
@@ -6509,69 +6633,69 @@ async function createProjectStructure() {
6509
6633
  await createRuntimeFiles();
6510
6634
  }
6511
6635
  function getTemplatesDir() {
6512
- return path5.join(__dirname, "../../templates/init");
6636
+ return path6.join(__dirname, "../../templates/init");
6513
6637
  }
6514
6638
  async function createRuntimeFiles() {
6515
6639
  const cwd = process.cwd();
6516
6640
  const templatesDir = getTemplatesDir();
6517
- const projectContextPath = path5.join(cwd, ".bugzy/runtime/project-context.md");
6641
+ const projectContextPath = path6.join(cwd, ".bugzy/runtime/project-context.md");
6518
6642
  if (!fs4.existsSync(projectContextPath)) {
6519
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/project-context.md");
6643
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/project-context.md");
6520
6644
  let content = fs4.readFileSync(templatePath, "utf-8");
6521
- const projectName = path5.basename(cwd);
6645
+ const projectName = path6.basename(cwd);
6522
6646
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
6523
6647
  content = content.replace(/\{\{CUSTOMER_NAME\}\}/g, "[To be filled]");
6524
6648
  content = content.replace(/\{\{BUG_TRACKING_SYSTEM\}\}/g, "[To be filled]");
6525
6649
  content = content.replace(/\{\{DOCUMENTATION_SYSTEM\}\}/g, "[To be filled]");
6526
6650
  fs4.writeFileSync(projectContextPath, content, "utf-8");
6527
6651
  }
6528
- const testPlanTemplatePath = path5.join(cwd, ".bugzy/runtime/templates/test-plan-template.md");
6652
+ const testPlanTemplatePath = path6.join(cwd, ".bugzy/runtime/templates/test-plan-template.md");
6529
6653
  if (!fs4.existsSync(testPlanTemplatePath)) {
6530
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/templates/test-plan-template.md");
6654
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/templates/test-plan-template.md");
6531
6655
  const content = fs4.readFileSync(templatePath, "utf-8");
6532
6656
  fs4.writeFileSync(testPlanTemplatePath, content, "utf-8");
6533
6657
  }
6534
- const bestPracticesPath = path5.join(cwd, ".bugzy/runtime/testing-best-practices.md");
6658
+ const bestPracticesPath = path6.join(cwd, ".bugzy/runtime/testing-best-practices.md");
6535
6659
  if (!fs4.existsSync(bestPracticesPath)) {
6536
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/testing-best-practices.md");
6660
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/testing-best-practices.md");
6537
6661
  const content = fs4.readFileSync(templatePath, "utf-8");
6538
6662
  fs4.writeFileSync(bestPracticesPath, content, "utf-8");
6539
6663
  }
6540
- const testResultSchemaPath = path5.join(cwd, ".bugzy/runtime/templates/test-result-schema.md");
6664
+ const testResultSchemaPath = path6.join(cwd, ".bugzy/runtime/templates/test-result-schema.md");
6541
6665
  if (!fs4.existsSync(testResultSchemaPath)) {
6542
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/templates/test-result-schema.md");
6666
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/templates/test-result-schema.md");
6543
6667
  if (fs4.existsSync(templatePath)) {
6544
6668
  const content = fs4.readFileSync(templatePath, "utf-8");
6545
6669
  fs4.writeFileSync(testResultSchemaPath, content, "utf-8");
6546
6670
  }
6547
6671
  }
6548
- const knowledgeBasePath = path5.join(cwd, ".bugzy/runtime/knowledge-base.md");
6672
+ const knowledgeBasePath = path6.join(cwd, ".bugzy/runtime/knowledge-base.md");
6549
6673
  if (!fs4.existsSync(knowledgeBasePath)) {
6550
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/knowledge-base.md");
6674
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/knowledge-base.md");
6551
6675
  if (fs4.existsSync(templatePath)) {
6552
6676
  const content = fs4.readFileSync(templatePath, "utf-8");
6553
6677
  fs4.writeFileSync(knowledgeBasePath, content, "utf-8");
6554
6678
  }
6555
6679
  }
6556
- const knowledgeMaintenancePath = path5.join(cwd, ".bugzy/runtime/knowledge-maintenance-guide.md");
6680
+ const knowledgeMaintenancePath = path6.join(cwd, ".bugzy/runtime/knowledge-maintenance-guide.md");
6557
6681
  if (!fs4.existsSync(knowledgeMaintenancePath)) {
6558
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/knowledge-maintenance-guide.md");
6682
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/knowledge-maintenance-guide.md");
6559
6683
  if (fs4.existsSync(templatePath)) {
6560
6684
  const content = fs4.readFileSync(templatePath, "utf-8");
6561
6685
  fs4.writeFileSync(knowledgeMaintenancePath, content, "utf-8");
6562
6686
  }
6563
6687
  }
6564
- const subagentMemoryPath = path5.join(cwd, ".bugzy/runtime/subagent-memory-guide.md");
6688
+ const subagentMemoryPath = path6.join(cwd, ".bugzy/runtime/subagent-memory-guide.md");
6565
6689
  if (!fs4.existsSync(subagentMemoryPath)) {
6566
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/subagent-memory-guide.md");
6690
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/subagent-memory-guide.md");
6567
6691
  if (fs4.existsSync(templatePath)) {
6568
6692
  const content = fs4.readFileSync(templatePath, "utf-8");
6569
6693
  fs4.writeFileSync(subagentMemoryPath, content, "utf-8");
6570
6694
  }
6571
6695
  }
6572
- const testExecutionStrategyPath = path5.join(cwd, ".bugzy/runtime/test-execution-strategy.md");
6696
+ const testExecutionStrategyPath = path6.join(cwd, ".bugzy/runtime/test-execution-strategy.md");
6573
6697
  if (!fs4.existsSync(testExecutionStrategyPath)) {
6574
- const templatePath = path5.join(templatesDir, ".bugzy/runtime/test-execution-strategy.md");
6698
+ const templatePath = path6.join(templatesDir, ".bugzy/runtime/test-execution-strategy.md");
6575
6699
  if (fs4.existsSync(templatePath)) {
6576
6700
  const content = fs4.readFileSync(templatePath, "utf-8");
6577
6701
  fs4.writeFileSync(testExecutionStrategyPath, content, "utf-8");
@@ -6581,18 +6705,28 @@ async function createRuntimeFiles() {
6581
6705
  async function generateClaudeMd() {
6582
6706
  const cwd = process.cwd();
6583
6707
  const templatesDir = getTemplatesDir();
6584
- const claudeMdPath = path5.join(cwd, "CLAUDE.md");
6708
+ const claudeMdPath = path6.join(cwd, "CLAUDE.md");
6585
6709
  if (!fs4.existsSync(claudeMdPath)) {
6586
- const templatePath = path5.join(templatesDir, "CLAUDE.md");
6710
+ const templatePath = path6.join(templatesDir, "CLAUDE.md");
6587
6711
  const content = fs4.readFileSync(templatePath, "utf-8");
6588
6712
  fs4.writeFileSync(claudeMdPath, content, "utf-8");
6589
6713
  }
6590
6714
  }
6715
+ async function generateAgentsMd() {
6716
+ const cwd = process.cwd();
6717
+ const templatesDir = getTemplatesDir();
6718
+ const agentsMdPath = path6.join(cwd, "AGENTS.md");
6719
+ if (!fs4.existsSync(agentsMdPath)) {
6720
+ const templatePath = path6.join(templatesDir, "AGENTS.md");
6721
+ const content = fs4.readFileSync(templatePath, "utf-8");
6722
+ fs4.writeFileSync(agentsMdPath, content, "utf-8");
6723
+ }
6724
+ }
6591
6725
  async function updateGitignore() {
6592
6726
  const cwd = process.cwd();
6593
- const gitignorePath = path5.join(cwd, ".gitignore");
6727
+ const gitignorePath = path6.join(cwd, ".gitignore");
6594
6728
  const templatesDir = getTemplatesDir();
6595
- const templatePath = path5.join(templatesDir, ".gitignore-template");
6729
+ const templatePath = path6.join(templatesDir, ".gitignore-template");
6596
6730
  const bugzyEntries = fs4.readFileSync(templatePath, "utf-8");
6597
6731
  if (fs4.existsSync(gitignorePath)) {
6598
6732
  const content = fs4.readFileSync(gitignorePath, "utf-8");
@@ -6608,7 +6742,7 @@ async function updateGitignore() {
6608
6742
  init_esm_shims();
6609
6743
  init_tasks();
6610
6744
  import * as fs5 from "fs";
6611
- import * as path6 from "path";
6745
+ import * as path7 from "path";
6612
6746
 
6613
6747
  // src/core/task-builder.ts
6614
6748
  init_esm_shims();
@@ -6657,22 +6791,80 @@ function buildTaskDefinition(taskSlug, projectSubAgents) {
6657
6791
  };
6658
6792
  }
6659
6793
 
6794
+ // src/core/tool-strings.ts
6795
+ init_esm_shims();
6796
+ var TOOL_STRINGS = {
6797
+ "claude-code": {
6798
+ INVOKE_TEST_RUNNER: "Use the test-runner subagent to execute the tests",
6799
+ INVOKE_TEST_DEBUGGER_FIXER: "Use the test-debugger-fixer subagent to debug and fix the failing test",
6800
+ INVOKE_TEST_CODE_GENERATOR: "Use the test-code-generator subagent to generate automated test code",
6801
+ INVOKE_TEAM_COMMUNICATOR: "Use the team-communicator subagent to notify the team",
6802
+ INVOKE_ISSUE_TRACKER: "Use the issue-tracker subagent to create or update issues",
6803
+ INVOKE_DOCUMENTATION_RESEARCHER: "Use the documentation-researcher subagent to search and gather documentation"
6804
+ },
6805
+ "cursor": {
6806
+ INVOKE_TEST_RUNNER: 'Run the test-runner agent:\n```bash\ncursor-agent -p "$(cat .cursor/agents/test-runner.md)" --output-format text\n```',
6807
+ INVOKE_TEST_DEBUGGER_FIXER: 'Run the test-debugger-fixer agent:\n```bash\ncursor-agent -p "$(cat .cursor/agents/test-debugger-fixer.md)" --output-format text\n```',
6808
+ INVOKE_TEST_CODE_GENERATOR: 'Run the test-code-generator agent:\n```bash\ncursor-agent -p "$(cat .cursor/agents/test-code-generator.md)" --output-format text\n```',
6809
+ INVOKE_TEAM_COMMUNICATOR: 'Run the team-communicator agent:\n```bash\ncursor-agent -p "$(cat .cursor/agents/team-communicator.md)" --output-format text\n```',
6810
+ INVOKE_ISSUE_TRACKER: 'Run the issue-tracker agent:\n```bash\ncursor-agent -p "$(cat .cursor/agents/issue-tracker.md)" --output-format text\n```',
6811
+ INVOKE_DOCUMENTATION_RESEARCHER: 'Run the documentation-researcher agent:\n```bash\ncursor-agent -p "$(cat .cursor/agents/documentation-researcher.md)" --output-format text\n```'
6812
+ },
6813
+ "codex": {
6814
+ INVOKE_TEST_RUNNER: 'Run the test-runner agent:\n```bash\ncodex -p "$(cat .codex/agents/test-runner.md)"\n```',
6815
+ INVOKE_TEST_DEBUGGER_FIXER: 'Run the test-debugger-fixer agent:\n```bash\ncodex -p "$(cat .codex/agents/test-debugger-fixer.md)"\n```',
6816
+ INVOKE_TEST_CODE_GENERATOR: 'Run the test-code-generator agent:\n```bash\ncodex -p "$(cat .codex/agents/test-code-generator.md)"\n```',
6817
+ INVOKE_TEAM_COMMUNICATOR: 'Run the team-communicator agent:\n```bash\ncodex -p "$(cat .codex/agents/team-communicator.md)"\n```',
6818
+ INVOKE_ISSUE_TRACKER: 'Run the issue-tracker agent:\n```bash\ncodex -p "$(cat .codex/agents/issue-tracker.md)"\n```',
6819
+ INVOKE_DOCUMENTATION_RESEARCHER: 'Run the documentation-researcher agent:\n```bash\ncodex -p "$(cat .codex/agents/documentation-researcher.md)"\n```'
6820
+ }
6821
+ };
6822
+ function getToolString(toolId, key) {
6823
+ const toolStrings = TOOL_STRINGS[toolId];
6824
+ if (!toolStrings) {
6825
+ throw new Error(`Unknown tool: ${toolId}`);
6826
+ }
6827
+ const value = toolStrings[key];
6828
+ if (!value) {
6829
+ throw new Error(`Unknown string key: ${key} for tool: ${toolId}`);
6830
+ }
6831
+ return value;
6832
+ }
6833
+ function replaceInvocationPlaceholders(content, toolId) {
6834
+ let result = content;
6835
+ const keys = [
6836
+ "INVOKE_TEST_RUNNER",
6837
+ "INVOKE_TEST_DEBUGGER_FIXER",
6838
+ "INVOKE_TEST_CODE_GENERATOR",
6839
+ "INVOKE_TEAM_COMMUNICATOR",
6840
+ "INVOKE_ISSUE_TRACKER",
6841
+ "INVOKE_DOCUMENTATION_RESEARCHER"
6842
+ ];
6843
+ for (const key of keys) {
6844
+ const placeholder = `{{${key}}}`;
6845
+ const replacement = getToolString(toolId, key);
6846
+ result = result.replace(new RegExp(placeholder, "g"), replacement);
6847
+ }
6848
+ return result;
6849
+ }
6850
+
6660
6851
  // src/cli/generators/commands.ts
6661
6852
  var COMMAND_FILTER = {
6662
6853
  // Cloud-only commands (skip in local environment)
6663
6854
  "handle-message": false,
6664
6855
  "process-event": false
6665
6856
  };
6666
- async function generateCommands(subagents) {
6857
+ async function generateCommands(subagents, tool = DEFAULT_TOOL) {
6667
6858
  const cwd = process.cwd();
6668
- const commandsDir = path6.join(cwd, ".claude", "commands");
6859
+ const toolProfile = getToolProfile(tool);
6860
+ const commandsDir = path7.join(cwd, toolProfile.commandsDir);
6669
6861
  if (!fs5.existsSync(commandsDir)) {
6670
6862
  fs5.mkdirSync(commandsDir, { recursive: true });
6671
6863
  }
6672
6864
  const existingFiles = fs5.readdirSync(commandsDir);
6673
6865
  for (const file of existingFiles) {
6674
6866
  if (file.endsWith(".md")) {
6675
- fs5.unlinkSync(path6.join(commandsDir, file));
6867
+ fs5.unlinkSync(path7.join(commandsDir, file));
6676
6868
  }
6677
6869
  }
6678
6870
  const projectSubAgents = Object.entries(subagents).map(
@@ -6686,18 +6878,35 @@ async function generateCommands(subagents) {
6686
6878
  const outputSlug = typeof filterValue === "string" ? filterValue : slug;
6687
6879
  try {
6688
6880
  const taskDef = buildTaskDefinition(slug, projectSubAgents);
6689
- const content = formatCommandMarkdown(taskDef.frontmatter, taskDef.content);
6690
- const filePath = path6.join(commandsDir, `${outputSlug}.md`);
6881
+ const processedContent = replaceInvocationPlaceholders(taskDef.content, tool);
6882
+ const content = formatCommandMarkdown(taskDef.frontmatter, processedContent, toolProfile.commandFrontmatter);
6883
+ const fileName = `${outputSlug}${toolProfile.commandExtension}`;
6884
+ const filePath = path7.join(commandsDir, fileName);
6691
6885
  fs5.writeFileSync(filePath, content, "utf-8");
6692
6886
  } catch (error) {
6693
- const content = formatCommandMarkdown(template.frontmatter, template.baseContent);
6694
- const filePath = path6.join(commandsDir, `${outputSlug}.md`);
6887
+ const processedContent = replaceInvocationPlaceholders(template.baseContent, tool);
6888
+ const content = formatCommandMarkdown(template.frontmatter, processedContent, toolProfile.commandFrontmatter);
6889
+ const fileName = `${outputSlug}${toolProfile.commandExtension}`;
6890
+ const filePath = path7.join(commandsDir, fileName);
6695
6891
  fs5.writeFileSync(filePath, content, "utf-8");
6696
6892
  console.warn(`Warning: Generated ${outputSlug} without required subagents: ${error.message}`);
6697
6893
  }
6698
6894
  }
6699
6895
  }
6700
- function formatCommandMarkdown(frontmatter, content) {
6896
+ function formatCommandMarkdown(frontmatter, content, includeFrontmatter) {
6897
+ if (!includeFrontmatter) {
6898
+ const lines2 = [];
6899
+ if (frontmatter.description) {
6900
+ lines2.push(`# ${frontmatter.description}`);
6901
+ lines2.push("");
6902
+ }
6903
+ if (frontmatter["argument-hint"]) {
6904
+ lines2.push(`**Arguments**: ${frontmatter["argument-hint"]}`);
6905
+ lines2.push("");
6906
+ }
6907
+ lines2.push(content);
6908
+ return lines2.join("\n");
6909
+ }
6701
6910
  const lines = ["---"];
6702
6911
  for (const [key, value] of Object.entries(frontmatter)) {
6703
6912
  if (value !== void 0 && value !== null) {
@@ -6714,17 +6923,18 @@ function formatCommandMarkdown(frontmatter, content) {
6714
6923
  // src/cli/generators/agents.ts
6715
6924
  init_esm_shims();
6716
6925
  import * as fs6 from "fs";
6717
- import * as path7 from "path";
6718
- async function generateAgents(subagents) {
6926
+ import * as path8 from "path";
6927
+ async function generateAgents(subagents, tool = DEFAULT_TOOL) {
6719
6928
  const cwd = process.cwd();
6720
- const agentsDir = path7.join(cwd, ".claude", "agents");
6929
+ const toolProfile = getToolProfile(tool);
6930
+ const agentsDir = path8.join(cwd, toolProfile.agentsDir);
6721
6931
  if (!fs6.existsSync(agentsDir)) {
6722
6932
  fs6.mkdirSync(agentsDir, { recursive: true });
6723
6933
  }
6724
6934
  const existingFiles = fs6.readdirSync(agentsDir);
6725
6935
  for (const file of existingFiles) {
6726
6936
  if (file.endsWith(".md")) {
6727
- fs6.unlinkSync(path7.join(agentsDir, file));
6937
+ fs6.unlinkSync(path8.join(agentsDir, file));
6728
6938
  }
6729
6939
  }
6730
6940
  for (const [role, integration] of Object.entries(subagents)) {
@@ -6733,12 +6943,16 @@ async function generateAgents(subagents) {
6733
6943
  console.warn(`Warning: Could not load template for ${role} with ${integration}`);
6734
6944
  continue;
6735
6945
  }
6736
- const content = formatAgentMarkdown(config.frontmatter, config.content);
6737
- const filePath = path7.join(agentsDir, `${role}.md`);
6946
+ const content = formatAgentMarkdown(config.frontmatter, config.content, toolProfile.agentFrontmatter);
6947
+ const fileName = `${role}${toolProfile.agentExtension}`;
6948
+ const filePath = path8.join(agentsDir, fileName);
6738
6949
  fs6.writeFileSync(filePath, content, "utf-8");
6739
6950
  }
6740
6951
  }
6741
- function formatAgentMarkdown(frontmatter, content) {
6952
+ function formatAgentMarkdown(frontmatter, content, includeFrontmatter) {
6953
+ if (!includeFrontmatter) {
6954
+ return content;
6955
+ }
6742
6956
  const lines = ["---"];
6743
6957
  for (const [key, value] of Object.entries(frontmatter)) {
6744
6958
  if (value !== void 0 && value !== null) {
@@ -6759,7 +6973,8 @@ function formatAgentMarkdown(frontmatter, content) {
6759
6973
  // src/cli/generators/mcp.ts
6760
6974
  init_esm_shims();
6761
6975
  import * as fs7 from "fs";
6762
- import * as path8 from "path";
6976
+ import * as path9 from "path";
6977
+ import { execSync } from "child_process";
6763
6978
 
6764
6979
  // src/mcp/index.ts
6765
6980
  init_esm_shims();
@@ -6928,12 +7143,59 @@ function buildMCPConfig(requiredServers, target = "container") {
6928
7143
  }
6929
7144
 
6930
7145
  // src/cli/generators/mcp.ts
6931
- async function generateMCPConfig(mcpServers) {
7146
+ async function generateMCPConfig(mcpServers, tool = DEFAULT_TOOL) {
6932
7147
  const cwd = process.cwd();
6933
- const mcpConfigPath = path8.join(cwd, ".mcp.json");
6934
- const mcpConfig = buildMCPConfig(mcpServers, "local");
6935
- const content = JSON.stringify(mcpConfig, null, 2);
6936
- fs7.writeFileSync(mcpConfigPath, content, "utf-8");
7148
+ const toolProfile = getToolProfile(tool);
7149
+ if (toolProfile.mcpFormat === "json") {
7150
+ const mcpConfigPath = path9.join(cwd, toolProfile.mcpConfigPath);
7151
+ const mcpDir = path9.dirname(mcpConfigPath);
7152
+ if (!fs7.existsSync(mcpDir)) {
7153
+ fs7.mkdirSync(mcpDir, { recursive: true });
7154
+ }
7155
+ const mcpConfig = buildMCPConfig(mcpServers, "local");
7156
+ const content = JSON.stringify(mcpConfig, null, 2);
7157
+ fs7.writeFileSync(mcpConfigPath, content, "utf-8");
7158
+ } else if (toolProfile.mcpFormat === "toml") {
7159
+ return;
7160
+ }
7161
+ }
7162
+ function buildCodexMCPCommand(serverName) {
7163
+ const serverTemplate = MCP_SERVERS[serverName];
7164
+ if (!serverTemplate) {
7165
+ throw new Error(`Unknown MCP server: ${serverName}`);
7166
+ }
7167
+ const args = ["mcp", "add", `bugzy-${serverName}`];
7168
+ const envVars = [];
7169
+ if (serverTemplate.config.env) {
7170
+ for (const [key, value] of Object.entries(serverTemplate.config.env)) {
7171
+ const match = value.match(/\$\{([A-Z_]+)\}/);
7172
+ if (match) {
7173
+ args.push("--env", `${key}=$${match[1]}`);
7174
+ envVars.push(match[1]);
7175
+ }
7176
+ }
7177
+ }
7178
+ args.push("--", serverTemplate.config.command);
7179
+ if (serverTemplate.config.args?.length) {
7180
+ args.push(...serverTemplate.config.args);
7181
+ }
7182
+ return { args, envVars };
7183
+ }
7184
+ async function getConfiguredCodexMCPServers() {
7185
+ try {
7186
+ const output = execSync("codex mcp list", {
7187
+ encoding: "utf-8",
7188
+ env: { ...process.env, CODEX_HOME: path9.join(process.cwd(), ".codex") },
7189
+ stdio: ["pipe", "pipe", "pipe"]
7190
+ });
7191
+ const lines = output.split("\n");
7192
+ return lines.filter((line) => line.includes("bugzy-")).map((line) => {
7193
+ const match = line.match(/bugzy-([a-z-]+)/);
7194
+ return match ? match[1] : null;
7195
+ }).filter((name) => name !== null);
7196
+ } catch {
7197
+ return [];
7198
+ }
6937
7199
  }
6938
7200
  function getMCPServersFromSubagents(subagents) {
6939
7201
  const mcps = /* @__PURE__ */ new Set();
@@ -6943,13 +7205,16 @@ function getMCPServersFromSubagents(subagents) {
6943
7205
  return Array.from(mcps);
6944
7206
  }
6945
7207
 
7208
+ // src/cli/commands/setup.ts
7209
+ import { execSync as execSync3 } from "child_process";
7210
+
6946
7211
  // src/cli/generators/env.ts
6947
7212
  init_esm_shims();
6948
7213
  import * as fs8 from "fs";
6949
- import * as path9 from "path";
7214
+ import * as path10 from "path";
6950
7215
  async function generateEnvExample(mcpServers) {
6951
7216
  const cwd = process.cwd();
6952
- const envExamplePath = path9.join(cwd, ".env.example");
7217
+ const envExamplePath = path10.join(cwd, ".env.example");
6953
7218
  const header = `# ============================================
6954
7219
  # Bugzy OSS - Environment Variables
6955
7220
  # ============================================
@@ -7009,8 +7274,8 @@ GITHUB_TOKEN=`
7009
7274
  // src/cli/generators/scaffold-playwright.ts
7010
7275
  init_esm_shims();
7011
7276
  import * as fs9 from "fs";
7012
- import * as path10 from "path";
7013
- import { execSync } from "child_process";
7277
+ import * as path11 from "path";
7278
+ import { execSync as execSync2 } from "child_process";
7014
7279
  async function scaffoldPlaywrightProject(options) {
7015
7280
  const { projectName, targetDir, skipInstall = false } = options;
7016
7281
  console.log("\n\u{1F3AD} Scaffolding Playwright test automation project...\n");
@@ -7043,7 +7308,7 @@ async function createDirectoryStructure(targetDir) {
7043
7308
  "test-runs"
7044
7309
  ];
7045
7310
  for (const dir of directories) {
7046
- const fullPath = path10.join(targetDir, dir);
7311
+ const fullPath = path11.join(targetDir, dir);
7047
7312
  if (!fs9.existsSync(fullPath)) {
7048
7313
  fs9.mkdirSync(fullPath, { recursive: true });
7049
7314
  console.log(` \u2713 Created ${dir}/`);
@@ -7052,11 +7317,11 @@ async function createDirectoryStructure(targetDir) {
7052
7317
  }
7053
7318
  async function copyTemplateFiles(targetDir, projectName) {
7054
7319
  const possiblePaths = [
7055
- path10.join(__dirname, "../../templates/playwright"),
7320
+ path11.join(__dirname, "../../templates/playwright"),
7056
7321
  // When running from dist
7057
- path10.join(process.cwd(), "templates/playwright"),
7322
+ path11.join(process.cwd(), "templates/playwright"),
7058
7323
  // When running from project root
7059
- path10.join(__dirname, "../../../templates/playwright")
7324
+ path11.join(__dirname, "../../../templates/playwright")
7060
7325
  // When running tests
7061
7326
  ];
7062
7327
  let templatesDir = "";
@@ -7069,9 +7334,9 @@ async function copyTemplateFiles(targetDir, projectName) {
7069
7334
  if (!templatesDir) {
7070
7335
  throw new Error("Templates directory not found. Searched paths: " + possiblePaths.join(", "));
7071
7336
  }
7072
- const initTemplatesDir = path10.join(__dirname, "../../templates/init");
7073
- const testRunsReadmeSrc = path10.join(initTemplatesDir, "test-runs/README.md");
7074
- const testRunsReadmeDest = path10.join(targetDir, "test-runs/README.md");
7337
+ const initTemplatesDir = path11.join(__dirname, "../../templates/init");
7338
+ const testRunsReadmeSrc = path11.join(initTemplatesDir, "test-runs/README.md");
7339
+ const testRunsReadmeDest = path11.join(targetDir, "test-runs/README.md");
7075
7340
  if (fs9.existsSync(testRunsReadmeSrc)) {
7076
7341
  const content = fs9.readFileSync(testRunsReadmeSrc, "utf-8");
7077
7342
  fs9.writeFileSync(testRunsReadmeDest, content, "utf-8");
@@ -7115,8 +7380,8 @@ async function copyTemplateFiles(targetDir, projectName) {
7115
7380
  }
7116
7381
  ];
7117
7382
  for (const template of templates) {
7118
- const srcPath = path10.join(templatesDir, template.src);
7119
- const destPath = path10.join(targetDir, template.dest);
7383
+ const srcPath = path11.join(templatesDir, template.src);
7384
+ const destPath = path11.join(targetDir, template.dest);
7120
7385
  if (fs9.existsSync(srcPath)) {
7121
7386
  let content = fs9.readFileSync(srcPath, "utf-8");
7122
7387
  if (template.process) {
@@ -7126,7 +7391,7 @@ async function copyTemplateFiles(targetDir, projectName) {
7126
7391
  DATE: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
7127
7392
  });
7128
7393
  }
7129
- const destDir = path10.dirname(destPath);
7394
+ const destDir = path11.dirname(destPath);
7130
7395
  if (!fs9.existsSync(destDir)) {
7131
7396
  fs9.mkdirSync(destDir, { recursive: true });
7132
7397
  }
@@ -7146,7 +7411,7 @@ function processTemplate(content, values) {
7146
7411
  return processed;
7147
7412
  }
7148
7413
  async function updateGitignore2(targetDir) {
7149
- const gitignorePath = path10.join(targetDir, ".gitignore");
7414
+ const gitignorePath = path11.join(targetDir, ".gitignore");
7150
7415
  const playwrightEntries = `
7151
7416
  # Playwright
7152
7417
  test-results/
@@ -7166,7 +7431,7 @@ tests/.auth/
7166
7431
  }
7167
7432
  }
7168
7433
  async function createPackageJson(targetDir, projectName) {
7169
- const packageJsonPath = path10.join(targetDir, "package.json");
7434
+ const packageJsonPath = path11.join(targetDir, "package.json");
7170
7435
  const recommendedDeps = {
7171
7436
  "@playwright/test": "^1.48.0",
7172
7437
  "@types/node": "^20.0.0",
@@ -7222,7 +7487,7 @@ async function installDependencies(targetDir) {
7222
7487
  console.log(`
7223
7488
  \u{1F4E6} Installing dependencies using ${packageManager}...`);
7224
7489
  const installCommand = packageManager === "pnpm" ? "pnpm install" : packageManager === "yarn" ? "yarn install" : "npm install";
7225
- execSync(installCommand, {
7490
+ execSync2(installCommand, {
7226
7491
  cwd: targetDir,
7227
7492
  stdio: "inherit"
7228
7493
  });
@@ -7233,17 +7498,17 @@ async function installDependencies(targetDir) {
7233
7498
  }
7234
7499
  }
7235
7500
  function detectPackageManager(targetDir) {
7236
- if (fs9.existsSync(path10.join(targetDir, "pnpm-lock.yaml"))) {
7501
+ if (fs9.existsSync(path11.join(targetDir, "pnpm-lock.yaml"))) {
7237
7502
  return "pnpm";
7238
7503
  }
7239
- if (fs9.existsSync(path10.join(targetDir, "yarn.lock"))) {
7504
+ if (fs9.existsSync(path11.join(targetDir, "yarn.lock"))) {
7240
7505
  return "yarn";
7241
7506
  }
7242
7507
  return "npm";
7243
7508
  }
7244
7509
  function isPlaywrightScaffolded(targetDir) {
7245
- const playwrightConfig = path10.join(targetDir, "playwright.config.ts");
7246
- const testsDir = path10.join(targetDir, "tests");
7510
+ const playwrightConfig = path11.join(targetDir, "playwright.config.ts");
7511
+ const testsDir = path11.join(targetDir, "tests");
7247
7512
  return fs9.existsSync(playwrightConfig) && fs9.existsSync(testsDir);
7248
7513
  }
7249
7514
 
@@ -7287,9 +7552,26 @@ async function setupProject(cliArgs = []) {
7287
7552
  async function firstTimeSetup(cliSubagents) {
7288
7553
  console.log(getBanner());
7289
7554
  console.log(chalk2.cyan(" Project Setup\n"));
7555
+ const toolOptions = getToolOptions();
7556
+ const { selectedTool } = await inquirer.prompt([{
7557
+ type: "list",
7558
+ name: "selectedTool",
7559
+ message: "Which AI coding assistant do you use?",
7560
+ choices: toolOptions.map((opt) => ({
7561
+ name: opt.hint ? `${opt.label} - ${chalk2.gray(opt.hint)}` : opt.label,
7562
+ value: opt.value
7563
+ })),
7564
+ default: DEFAULT_TOOL
7565
+ }]);
7566
+ const tool = selectedTool;
7567
+ const toolProfile = getToolProfile(tool);
7568
+ console.log(chalk2.gray(`
7569
+ \u2713 Using ${toolProfile.name}
7570
+ `));
7290
7571
  let spinner = ora2("Creating project structure").start();
7291
- await createProjectStructure();
7292
- spinner.succeed(chalk2.green("Created .bugzy/ and .claude/ directories"));
7572
+ await createProjectStructure(tool);
7573
+ const toolDirName = path12.dirname(toolProfile.commandsDir);
7574
+ spinner.succeed(chalk2.green(`Created .bugzy/ and ${toolDirName}/ directories`));
7293
7575
  const subagents = {};
7294
7576
  if (cliSubagents) {
7295
7577
  console.log(chalk2.cyan("\nConfiguring subagents from CLI arguments:\n"));
@@ -7347,15 +7629,19 @@ async function firstTimeSetup(cliSubagents) {
7347
7629
  }
7348
7630
  }
7349
7631
  spinner = ora2("Saving configuration").start();
7350
- const projectName = path11.basename(process.cwd());
7351
- const config = createDefaultConfig(projectName);
7632
+ const projectName = path12.basename(process.cwd());
7633
+ const config = createDefaultConfig(projectName, tool);
7352
7634
  config.subagents = subagents;
7353
7635
  saveConfig(config);
7354
7636
  spinner.succeed(chalk2.green("Saved to .bugzy/config.json"));
7355
- await regenerateAll(subagents);
7356
- spinner = ora2("Creating CLAUDE.md").start();
7357
- await generateClaudeMd();
7358
- spinner.succeed(chalk2.green("Created CLAUDE.md"));
7637
+ await regenerateAll(subagents, tool);
7638
+ spinner = ora2(`Creating ${toolProfile.memoryFile}`).start();
7639
+ if (tool === "claude-code") {
7640
+ await generateClaudeMd();
7641
+ } else {
7642
+ await generateAgentsMd();
7643
+ }
7644
+ spinner.succeed(chalk2.green(`Created ${toolProfile.memoryFile}`));
7359
7645
  spinner = ora2("Updating .gitignore").start();
7360
7646
  await updateGitignore();
7361
7647
  spinner.succeed(chalk2.green("Updated .gitignore"));
@@ -7382,11 +7668,38 @@ async function reconfigureProject() {
7382
7668
  console.error(chalk2.red("Error: Could not load existing configuration"));
7383
7669
  process.exit(1);
7384
7670
  }
7671
+ const currentTool = getToolFromConfig(existingConfig);
7672
+ const currentToolProfile = getToolProfile(currentTool);
7385
7673
  console.log(chalk2.gray("Current configuration:"));
7674
+ console.log(chalk2.gray(` Tool: ${currentToolProfile.name}`));
7386
7675
  for (const [role, integration] of Object.entries(existingConfig.subagents)) {
7387
7676
  console.log(chalk2.gray(` \u2022 ${role}: ${integration}`));
7388
7677
  }
7389
7678
  console.log();
7679
+ const toolOptions = getToolOptions();
7680
+ const { changeTool } = await inquirer.prompt([{
7681
+ type: "confirm",
7682
+ name: "changeTool",
7683
+ message: `Keep using ${currentToolProfile.name}?`,
7684
+ default: true
7685
+ }]);
7686
+ let tool = currentTool;
7687
+ if (!changeTool) {
7688
+ const { selectedTool } = await inquirer.prompt([{
7689
+ type: "list",
7690
+ name: "selectedTool",
7691
+ message: "Which AI coding assistant do you want to use?",
7692
+ choices: toolOptions.map((opt) => ({
7693
+ name: opt.hint ? `${opt.label} - ${chalk2.gray(opt.hint)}` : opt.label,
7694
+ value: opt.value
7695
+ })),
7696
+ default: currentTool
7697
+ }]);
7698
+ tool = selectedTool;
7699
+ console.log(chalk2.gray(`
7700
+ \u2713 Switching to ${getToolProfile(tool).name}
7701
+ `));
7702
+ }
7390
7703
  const allSubAgents = getAllSubAgents();
7391
7704
  const newSubagents = {};
7392
7705
  for (const subagent of allSubAgents) {
@@ -7462,13 +7775,19 @@ async function reconfigureProject() {
7462
7775
  }
7463
7776
  }
7464
7777
  let spinner = ora2("Updating configuration").start();
7778
+ existingConfig.tool = tool;
7465
7779
  existingConfig.subagents = newSubagents;
7466
7780
  await saveConfig(existingConfig);
7467
7781
  spinner.succeed(chalk2.green("Updated .bugzy/config.json"));
7468
- await regenerateAll(newSubagents);
7469
- spinner = ora2("Creating CLAUDE.md").start();
7470
- await generateClaudeMd();
7471
- spinner.succeed(chalk2.green("Created CLAUDE.md"));
7782
+ await regenerateAll(newSubagents, tool);
7783
+ const toolProfile = getToolProfile(tool);
7784
+ spinner = ora2(`Creating ${toolProfile.memoryFile}`).start();
7785
+ if (tool === "claude-code") {
7786
+ await generateClaudeMd();
7787
+ } else {
7788
+ await generateAgentsMd();
7789
+ }
7790
+ spinner.succeed(chalk2.green(`Created ${toolProfile.memoryFile}`));
7472
7791
  console.log(chalk2.green.bold("\n\u2705 Reconfiguration complete!\n"));
7473
7792
  console.log(chalk2.yellow("Next steps:"));
7474
7793
  console.log(chalk2.white("1. Check .env.example for new required secrets"));
@@ -7476,23 +7795,52 @@ async function reconfigureProject() {
7476
7795
  console.log(chalk2.white("3. Run:"), chalk2.cyan("bugzy"));
7477
7796
  console.log();
7478
7797
  }
7479
- async function regenerateAll(subagents) {
7798
+ async function regenerateAll(subagents, tool = DEFAULT_TOOL) {
7799
+ const toolProfile = getToolProfile(tool);
7480
7800
  let spinner = ora2("Generating task commands").start();
7481
- await generateCommands(subagents);
7801
+ await generateCommands(subagents, tool);
7482
7802
  const taskCount = Object.keys((init_tasks(), __toCommonJS(tasks_exports)).TASK_TEMPLATES).length;
7483
- spinner.succeed(chalk2.green(`Generated ${taskCount} task commands in .claude/commands/`));
7803
+ spinner.succeed(chalk2.green(`Generated ${taskCount} task commands in ${toolProfile.commandsDir}/`));
7484
7804
  spinner = ora2("Generating subagent configurations").start();
7485
- await generateAgents(subagents);
7805
+ await generateAgents(subagents, tool);
7486
7806
  const subagentCount = Object.keys(subagents).length;
7487
- spinner.succeed(chalk2.green(`Generated ${subagentCount} subagent configurations in .claude/agents/`));
7807
+ spinner.succeed(chalk2.green(`Generated ${subagentCount} subagent configurations in ${toolProfile.agentsDir}/`));
7488
7808
  spinner = ora2("Generating MCP configuration").start();
7489
7809
  const mcpServers = getMCPServersFromSubagents(subagents);
7490
- await generateMCPConfig(mcpServers);
7491
- spinner.succeed(chalk2.green(`Generated .mcp.json (${mcpServers.length} servers)`));
7810
+ await generateMCPConfig(mcpServers, tool);
7811
+ if (toolProfile.mcpFormat === "json") {
7812
+ spinner.succeed(chalk2.green(`Generated ${toolProfile.mcpConfigPath} (${mcpServers.length} servers)`));
7813
+ } else if (toolProfile.mcpFormat === "toml") {
7814
+ spinner.succeed(chalk2.green("MCP configuration ready"));
7815
+ await setupCodexMCP(mcpServers);
7816
+ }
7492
7817
  spinner = ora2("Creating environment template").start();
7493
7818
  await generateEnvExample(mcpServers);
7494
7819
  spinner.succeed(chalk2.green("Created .env.example"));
7495
7820
  }
7821
+ async function setupCodexMCP(mcpServers) {
7822
+ const existingServers = await getConfiguredCodexMCPServers();
7823
+ const newServers = mcpServers.filter((s) => !existingServers.includes(s));
7824
+ if (newServers.length === 0) {
7825
+ console.log(chalk2.gray("\n\u2713 All MCP servers already configured"));
7826
+ return;
7827
+ }
7828
+ console.log();
7829
+ for (const serverName of newServers) {
7830
+ const spinner = ora2(`Configuring ${serverName}`).start();
7831
+ try {
7832
+ const { args } = buildCodexMCPCommand(serverName);
7833
+ execSync3(["codex", ...args].join(" "), {
7834
+ stdio: "pipe",
7835
+ env: { ...process.env, CODEX_HOME: path12.join(process.cwd(), ".codex") }
7836
+ });
7837
+ spinner.succeed(chalk2.green(`Configured ${serverName}`));
7838
+ } catch (error) {
7839
+ spinner.fail(chalk2.red(`Failed to configure ${serverName}`));
7840
+ console.error(chalk2.gray(error.message));
7841
+ }
7842
+ }
7843
+ }
7496
7844
 
7497
7845
  // src/cli/index.ts
7498
7846
  process.on("uncaughtException", (error) => {
@@ -7503,9 +7851,9 @@ process.on("unhandledRejection", (reason) => {
7503
7851
  console.error(chalk3.red("\n\u2717 Error:"), reason?.message || reason);
7504
7852
  process.exit(1);
7505
7853
  });
7506
- var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
7854
+ var __dirname2 = dirname7(fileURLToPath2(import.meta.url));
7507
7855
  var packageJson = JSON.parse(
7508
- readFileSync5(join10(__dirname2, "../../package.json"), "utf-8")
7856
+ readFileSync5(join12(__dirname2, "../../package.json"), "utf-8")
7509
7857
  );
7510
7858
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
7511
7859
  console.log(getBanner());