@defai.digital/automatosx 12.4.1 → 12.5.1

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.js CHANGED
@@ -9431,7 +9431,7 @@ var PRECOMPILED_CONFIG = {
9431
9431
  "enableFreeTierPrioritization": true,
9432
9432
  "enableWorkloadAwareRouting": true
9433
9433
  },
9434
- "version": "12.4.1"
9434
+ "version": "12.5.1"
9435
9435
  };
9436
9436
 
9437
9437
  // src/core/config/schemas.ts
@@ -14272,9 +14272,9 @@ async function initializeGitRepository(projectDir) {
14272
14272
  logger.info("Git repository already exists, skipping initialization");
14273
14273
  return true;
14274
14274
  }
14275
- const { spawn: spawn11 } = await import('child_process');
14275
+ const { spawn: spawn12 } = await import('child_process');
14276
14276
  await new Promise((resolve13, reject) => {
14277
- const child = spawn11("git", ["init"], {
14277
+ const child = spawn12("git", ["init"], {
14278
14278
  cwd: projectDir,
14279
14279
  stdio: "pipe",
14280
14280
  shell: false
@@ -14342,8 +14342,8 @@ async function updateGitignore(projectDir) {
14342
14342
  try {
14343
14343
  const exists = await checkExists2(gitignorePath);
14344
14344
  if (exists) {
14345
- const { readFile: readFile22 } = await import('fs/promises');
14346
- const content = await readFile22(gitignorePath, "utf-8");
14345
+ const { readFile: readFile24 } = await import('fs/promises');
14346
+ const content = await readFile24(gitignorePath, "utf-8");
14347
14347
  if (content.includes("# AutomatosX")) {
14348
14348
  return;
14349
14349
  }
@@ -16506,12 +16506,12 @@ var listCommand = {
16506
16506
  };
16507
16507
  async function listAgents(pathResolver, format) {
16508
16508
  const agentsDir = pathResolver.getAgentsDirectory();
16509
- const { existsSync: existsSync28 } = await import('fs');
16509
+ const { existsSync: existsSync29 } = await import('fs');
16510
16510
  const projectDir = await detectProjectRoot();
16511
16511
  const examplesDir = join(projectDir, "examples", "agents");
16512
16512
  try {
16513
16513
  const agentFiles = [];
16514
- if (existsSync28(agentsDir)) {
16514
+ if (existsSync29(agentsDir)) {
16515
16515
  const files = await readdir(agentsDir);
16516
16516
  for (const file of files) {
16517
16517
  if (file.endsWith(".yaml") || file.endsWith(".yml")) {
@@ -16523,7 +16523,7 @@ async function listAgents(pathResolver, format) {
16523
16523
  }
16524
16524
  }
16525
16525
  }
16526
- if (existsSync28(examplesDir)) {
16526
+ if (existsSync29(examplesDir)) {
16527
16527
  const files = await readdir(examplesDir);
16528
16528
  for (const file of files) {
16529
16529
  if (file.endsWith(".yaml") || file.endsWith(".yml")) {
@@ -16549,12 +16549,12 @@ async function listAgents(pathResolver, format) {
16549
16549
  return;
16550
16550
  }
16551
16551
  const { load: load6 } = await import('js-yaml');
16552
- const { readFile: readFile22 } = await import('fs/promises');
16552
+ const { readFile: readFile24 } = await import('fs/promises');
16553
16553
  agentFiles.sort((a, b) => a.file.localeCompare(b.file));
16554
16554
  const agents = [];
16555
16555
  for (const { file, path: agentPath, source } of agentFiles) {
16556
16556
  try {
16557
- const content = await readFile22(agentPath, "utf-8");
16557
+ const content = await readFile24(agentPath, "utf-8");
16558
16558
  const agent = load6(content);
16559
16559
  const name = agent.displayName || agent.name || file.replace(/\.(yaml|yml)$/, "");
16560
16560
  const description = agent.description || "No description";
@@ -16607,12 +16607,12 @@ async function listAbilities(pathResolver, format) {
16607
16607
  }
16608
16608
  return;
16609
16609
  }
16610
- const { readFile: readFile22 } = await import('fs/promises');
16610
+ const { readFile: readFile24 } = await import('fs/promises');
16611
16611
  const abilities = [];
16612
16612
  for (const file of abilityFiles.sort()) {
16613
16613
  const abilityPath = join(abilitiesDir, file);
16614
16614
  try {
16615
- const content = await readFile22(abilityPath, "utf-8");
16615
+ const content = await readFile24(abilityPath, "utf-8");
16616
16616
  const lines = content.split("\n");
16617
16617
  const titleLine = lines.find((l) => l.startsWith("# "));
16618
16618
  const descLine = lines.find((l) => l.startsWith("## Description"));
@@ -20487,9 +20487,9 @@ var MemoryManager = class _MemoryManager {
20487
20487
  throw new MemoryError("Memory manager not initialized", "DATABASE_ERROR");
20488
20488
  }
20489
20489
  try {
20490
- const { mkdir: mkdir12 } = await import('fs/promises');
20490
+ const { mkdir: mkdir13 } = await import('fs/promises');
20491
20491
  const destDir = dirname4(destPath);
20492
- await mkdir12(destDir, { recursive: true });
20492
+ await mkdir13(destDir, { recursive: true });
20493
20493
  await this.db.backup(destPath);
20494
20494
  logger.info("Database backup created", { destPath: normalizePath(destPath) });
20495
20495
  } catch (error) {
@@ -20524,8 +20524,8 @@ var MemoryManager = class _MemoryManager {
20524
20524
  tempDb.prepare("SELECT COUNT(*) FROM memory_entries").get();
20525
20525
  tempDb.close();
20526
20526
  } catch (verifyError) {
20527
- const { unlink: unlink6 } = await import('fs/promises');
20528
- await unlink6(tempPath).catch(() => {
20527
+ const { unlink: unlink7 } = await import('fs/promises');
20528
+ await unlink7(tempPath).catch(() => {
20529
20529
  });
20530
20530
  throw new MemoryError(
20531
20531
  `Backup file verification failed: ${verifyError.message}`,
@@ -20645,9 +20645,9 @@ var MemoryManager = class _MemoryManager {
20645
20645
  },
20646
20646
  entries
20647
20647
  };
20648
- const { writeFile: writeFile15 } = await import('fs/promises');
20648
+ const { writeFile: writeFile16 } = await import('fs/promises');
20649
20649
  const json = pretty ? JSON.stringify(exportData, null, 2) : JSON.stringify(exportData);
20650
- await writeFile15(filePath, json, "utf-8");
20650
+ await writeFile16(filePath, json, "utf-8");
20651
20651
  const sizeBytes = Buffer.byteLength(json, "utf-8");
20652
20652
  logger.info("Memory exported to JSON", {
20653
20653
  filePath: normalizePath(filePath),
@@ -20696,8 +20696,8 @@ var MemoryManager = class _MemoryManager {
20696
20696
  { filePath }
20697
20697
  );
20698
20698
  }
20699
- const { readFile: readFile22 } = await import('fs/promises');
20700
- const content = await readFile22(filePath, "utf-8");
20699
+ const { readFile: readFile24 } = await import('fs/promises');
20700
+ const content = await readFile24(filePath, "utf-8");
20701
20701
  const importData = JSON.parse(content);
20702
20702
  const SUPPORTED_VERSIONS = ["1.0", "4.0.0", "4.11.0"];
20703
20703
  if (!importData.version || !SUPPORTED_VERSIONS.includes(importData.version)) {
@@ -26006,6 +26006,232 @@ ${context.task}`;
26006
26006
  }
26007
26007
  };
26008
26008
 
26009
+ // src/agents/agent-selector.ts
26010
+ init_esm_shims();
26011
+ init_logger();
26012
+ function scoreAgent(task, profile) {
26013
+ let score = 0;
26014
+ const taskLower = task.toLowerCase();
26015
+ if (profile.selectionMetadata?.primaryIntents) {
26016
+ for (const intent of profile.selectionMetadata.primaryIntents) {
26017
+ const intentKeywords = intent.toLowerCase().split(/\s+/);
26018
+ const matchedKeywords = intentKeywords.filter(
26019
+ (keyword) => taskLower.includes(keyword) && keyword.length > 3
26020
+ );
26021
+ if (matchedKeywords.length >= 2) {
26022
+ score += 10;
26023
+ } else if (matchedKeywords.length === 1) {
26024
+ score += 5;
26025
+ }
26026
+ }
26027
+ }
26028
+ if (profile.selectionMetadata?.secondarySignals) {
26029
+ for (const signal of profile.selectionMetadata.secondarySignals) {
26030
+ if (taskLower.includes(signal.toLowerCase())) {
26031
+ score += 5;
26032
+ }
26033
+ }
26034
+ }
26035
+ if (profile.selectionMetadata?.negativeIntents) {
26036
+ for (const negative of profile.selectionMetadata.negativeIntents) {
26037
+ const keywords = negative.split("(")[0]?.toLowerCase() || "";
26038
+ const negativeKeywords = keywords.split(/\s+/).filter((k) => k.length > 3);
26039
+ const matchedNegative = negativeKeywords.filter(
26040
+ (keyword) => taskLower.includes(keyword)
26041
+ );
26042
+ if (matchedNegative.length >= 2) {
26043
+ score -= 20;
26044
+ }
26045
+ }
26046
+ }
26047
+ if (profile.selectionMetadata?.redirectWhen) {
26048
+ for (const rule of profile.selectionMetadata.redirectWhen) {
26049
+ try {
26050
+ const regex = new RegExp(rule.phrase, "i");
26051
+ if (regex.test(task)) {
26052
+ score -= 15;
26053
+ }
26054
+ } catch (error) {
26055
+ logger.debug("Invalid regex pattern in redirectWhen rule", {
26056
+ pattern: rule.phrase,
26057
+ error: error instanceof Error ? error.message : String(error)
26058
+ });
26059
+ }
26060
+ }
26061
+ }
26062
+ if (profile.abilitySelection?.taskBased) {
26063
+ for (const keyword of Object.keys(profile.abilitySelection.taskBased)) {
26064
+ if (taskLower.includes(keyword.toLowerCase())) {
26065
+ score += 3;
26066
+ }
26067
+ }
26068
+ }
26069
+ return Math.max(0, score);
26070
+ }
26071
+ function buildRationale(task, profile) {
26072
+ const rationale = [];
26073
+ const taskLower = task.toLowerCase();
26074
+ if (profile.selectionMetadata?.primaryIntents) {
26075
+ const matchedIntents = profile.selectionMetadata.primaryIntents.filter((intent) => {
26076
+ const keywords = intent.toLowerCase().split(/\s+/);
26077
+ return keywords.some((k) => taskLower.includes(k) && k.length > 3);
26078
+ });
26079
+ if (matchedIntents.length > 0) {
26080
+ rationale.push(`Matches: ${matchedIntents.slice(0, 2).join(", ")}`);
26081
+ }
26082
+ }
26083
+ if (profile.selectionMetadata?.secondarySignals) {
26084
+ const matchedSignals = profile.selectionMetadata.secondarySignals.filter(
26085
+ (signal) => taskLower.includes(signal.toLowerCase())
26086
+ );
26087
+ if (matchedSignals.length > 0) {
26088
+ rationale.push(`Keywords: ${matchedSignals.slice(0, 3).join(", ")}`);
26089
+ }
26090
+ }
26091
+ if (profile.abilitySelection?.taskBased) {
26092
+ const matchedAbilities = Object.keys(profile.abilitySelection.taskBased).filter(
26093
+ (keyword) => taskLower.includes(keyword.toLowerCase())
26094
+ );
26095
+ if (matchedAbilities.length > 0) {
26096
+ rationale.push(`Abilities: ${matchedAbilities.slice(0, 2).join(", ")}`);
26097
+ }
26098
+ }
26099
+ if (rationale.length === 0) {
26100
+ rationale.push("General capability match");
26101
+ }
26102
+ return rationale;
26103
+ }
26104
+ function getConfidence(score) {
26105
+ if (score >= 30) return "high";
26106
+ if (score >= 15) return "medium";
26107
+ return "low";
26108
+ }
26109
+ var MIN_SELECTION_SCORE = 5;
26110
+ var FALLBACK_AGENT = "standard";
26111
+ var AgentSelector = class {
26112
+ profileLoader;
26113
+ constructor(profileLoader) {
26114
+ this.profileLoader = profileLoader;
26115
+ }
26116
+ /**
26117
+ * Select the best agent for a task
26118
+ *
26119
+ * @param task - Task description
26120
+ * @returns Selection result with agent, score, confidence, and rationale
26121
+ */
26122
+ async selectAgent(task) {
26123
+ logger.debug("[AgentSelector] Selecting agent for task", {
26124
+ taskPreview: task.substring(0, 100)
26125
+ });
26126
+ const agentNames = await this.profileLoader.listProfiles();
26127
+ if (agentNames.length === 0) {
26128
+ logger.warn("[AgentSelector] No agents found, using fallback");
26129
+ return this.createFallbackResult(task);
26130
+ }
26131
+ const profileResults = await Promise.all(
26132
+ agentNames.map(async (name) => {
26133
+ try {
26134
+ const profile = await this.profileLoader.loadProfile(name);
26135
+ return { name, profile, score: scoreAgent(task, profile) };
26136
+ } catch (error) {
26137
+ logger.debug(`[AgentSelector] Failed to load profile: ${name}`, { error });
26138
+ return null;
26139
+ }
26140
+ })
26141
+ );
26142
+ const scoredAgents = profileResults.filter(
26143
+ (result) => result !== null
26144
+ );
26145
+ if (scoredAgents.length === 0) {
26146
+ logger.warn("[AgentSelector] Failed to load any profiles, using fallback");
26147
+ return this.createFallbackResult(task);
26148
+ }
26149
+ scoredAgents.sort((a, b) => b.score - a.score);
26150
+ const best = scoredAgents[0];
26151
+ const alternatives = scoredAgents.slice(1, 4).map((a) => ({
26152
+ agent: a.name,
26153
+ score: a.score
26154
+ }));
26155
+ if (best.score < MIN_SELECTION_SCORE) {
26156
+ logger.info("[AgentSelector] Best score below threshold, using fallback", {
26157
+ bestAgent: best.name,
26158
+ bestScore: best.score,
26159
+ threshold: MIN_SELECTION_SCORE
26160
+ });
26161
+ const standardAgent = scoredAgents.find((a) => a.name === FALLBACK_AGENT);
26162
+ if (standardAgent) {
26163
+ return {
26164
+ agent: standardAgent.name,
26165
+ displayName: standardAgent.profile.displayName || standardAgent.name,
26166
+ role: standardAgent.profile.role,
26167
+ score: standardAgent.score,
26168
+ confidence: "low",
26169
+ rationale: ["No strong match found, using general-purpose agent"],
26170
+ alternatives: scoredAgents.filter((a) => a.name !== FALLBACK_AGENT).slice(0, 3).map((a) => ({ agent: a.name, score: a.score })),
26171
+ usedFallback: true
26172
+ };
26173
+ }
26174
+ return {
26175
+ agent: best.name,
26176
+ displayName: best.profile.displayName || best.name,
26177
+ role: best.profile.role,
26178
+ score: best.score,
26179
+ confidence: "low",
26180
+ rationale: ["Low confidence match - consider specifying agent explicitly"],
26181
+ alternatives,
26182
+ usedFallback: false
26183
+ };
26184
+ }
26185
+ const rationale = buildRationale(task, best.profile);
26186
+ const confidence = getConfidence(best.score);
26187
+ logger.info("[AgentSelector] Agent selected", {
26188
+ agent: best.name,
26189
+ score: best.score,
26190
+ confidence,
26191
+ rationale
26192
+ });
26193
+ return {
26194
+ agent: best.name,
26195
+ displayName: best.profile.displayName || best.name,
26196
+ role: best.profile.role,
26197
+ score: best.score,
26198
+ confidence,
26199
+ rationale,
26200
+ alternatives,
26201
+ usedFallback: false
26202
+ };
26203
+ }
26204
+ /**
26205
+ * Create a fallback result when no agents are available
26206
+ */
26207
+ async createFallbackResult(_task) {
26208
+ try {
26209
+ const profile = await this.profileLoader.loadProfile(FALLBACK_AGENT);
26210
+ return {
26211
+ agent: FALLBACK_AGENT,
26212
+ displayName: profile.displayName || FALLBACK_AGENT,
26213
+ role: profile.role,
26214
+ score: 0,
26215
+ confidence: "low",
26216
+ rationale: ["No agents available, using fallback"],
26217
+ alternatives: [],
26218
+ usedFallback: true
26219
+ };
26220
+ } catch {
26221
+ return {
26222
+ agent: FALLBACK_AGENT,
26223
+ displayName: "Standard",
26224
+ role: "General-purpose agent",
26225
+ score: 0,
26226
+ confidence: "low",
26227
+ rationale: ["No agents available"],
26228
+ alternatives: [],
26229
+ usedFallback: true
26230
+ };
26231
+ }
26232
+ }
26233
+ };
26234
+
26009
26235
  // src/mcp/tools/run-agent.ts
26010
26236
  init_logger();
26011
26237
 
@@ -26305,22 +26531,41 @@ async function buildAgentContext(agentName, task, deps, callerProvider, bestProv
26305
26531
  }
26306
26532
  function createRunAgentHandler(deps) {
26307
26533
  return async (input, context) => {
26308
- const { agent, task, provider, no_memory, mode = "auto" } = input;
26534
+ const { task, provider, no_memory, mode = "auto" } = input;
26535
+ let { agent } = input;
26309
26536
  if (context?.signal?.aborted) {
26310
26537
  throw new Error("Request was cancelled");
26311
26538
  }
26312
- validateAgentName(agent);
26313
26539
  validateStringParameter(task, "task", {
26314
26540
  required: true,
26315
26541
  minLength: 1,
26316
26542
  maxLength: 1e4
26317
26543
  });
26544
+ let autoSelected = false;
26545
+ if (!agent && deps.profileLoader) {
26546
+ const selector = new AgentSelector(deps.profileLoader);
26547
+ const selection = await selector.selectAgent(task);
26548
+ agent = selection.agent;
26549
+ autoSelected = true;
26550
+ logger.info("[MCP] run_agent auto-selected agent", {
26551
+ task: task.substring(0, 100),
26552
+ selectedAgent: agent,
26553
+ confidence: selection.confidence,
26554
+ score: selection.score,
26555
+ rationale: selection.rationale
26556
+ });
26557
+ }
26558
+ if (!agent) {
26559
+ throw new Error("Agent name is required when profileLoader is not available");
26560
+ }
26561
+ validateAgentName(agent);
26318
26562
  const actualProvider = mapMcpProviderToActual(provider);
26319
26563
  const session = deps.getSession?.() || null;
26320
26564
  const callerProvider = session?.normalizedProvider || "unknown";
26321
26565
  const callerActual = mapNormalizedCallerToActual(callerProvider);
26322
26566
  logger.info("[MCP] run_agent called (Smart Routing v10.5.0)", {
26323
26567
  agent,
26568
+ autoSelected,
26324
26569
  task: task.substring(0, 100),
26325
26570
  mcpProvider: provider,
26326
26571
  actualProvider,
@@ -27133,20 +27378,35 @@ init_logger();
27133
27378
  function createGetAgentContextHandler(deps) {
27134
27379
  return async (input) => {
27135
27380
  const {
27136
- agent,
27137
27381
  task,
27138
27382
  includeMemory = true,
27139
27383
  maxMemoryResults = 5
27140
27384
  } = input;
27385
+ let { agent } = input;
27141
27386
  const startTime = Date.now();
27142
- validateAgentName(agent);
27143
27387
  validateStringParameter(task, "task", {
27144
27388
  required: true,
27145
27389
  minLength: 1,
27146
27390
  maxLength: 1e4
27147
27391
  });
27392
+ let autoSelected = false;
27393
+ if (!agent) {
27394
+ const selector = new AgentSelector(deps.profileLoader);
27395
+ const selection = await selector.selectAgent(task);
27396
+ agent = selection.agent;
27397
+ autoSelected = true;
27398
+ logger.info("[MCP] get_agent_context auto-selected agent", {
27399
+ task: task.substring(0, 100),
27400
+ selectedAgent: agent,
27401
+ confidence: selection.confidence,
27402
+ score: selection.score,
27403
+ rationale: selection.rationale
27404
+ });
27405
+ }
27406
+ validateAgentName(agent);
27148
27407
  logger.info("[MCP] get_agent_context called", {
27149
27408
  agent,
27409
+ autoSelected,
27150
27410
  task: task.substring(0, 100),
27151
27411
  includeMemory,
27152
27412
  maxMemoryResults
@@ -31697,17 +31957,24 @@ var McpServer = class _McpServer {
31697
31957
  return [
31698
31958
  {
31699
31959
  name: "run_agent",
31700
- description: "Execute an AutomatosX agent with a specific task. Uses Smart Routing: returns context for same-provider calls, spawns cross-provider execution.",
31960
+ description: `Execute an AutomatosX agent with a specific task.
31961
+
31962
+ v12.5.1: Agent auto-selection - if agent is omitted, system automatically selects the best agent based on task keywords.
31963
+ Uses Smart Routing: returns context for same-provider calls, spawns cross-provider execution.
31964
+
31965
+ Examples:
31966
+ - With agent: run_agent({ agent: "backend", task: "implement API" })
31967
+ - Auto-select: run_agent({ task: "fix bugs in the codebase" }) \u2192 selects "quality" agent`,
31701
31968
  inputSchema: {
31702
31969
  type: "object",
31703
31970
  properties: {
31704
- agent: { type: "string", description: "The name of the agent to run (e.g., backend, Paris, Bob)" },
31971
+ agent: { type: "string", description: "Optional: Agent name (e.g., backend, quality). If omitted, best agent is auto-selected based on task." },
31705
31972
  task: { type: "string", description: "The task for the agent to perform" },
31706
31973
  provider: { type: "string", description: "Optional: Override the AI provider", enum: ["claude", "gemini", "openai"] },
31707
31974
  no_memory: { type: "boolean", description: "Optional: Skip memory injection", default: false },
31708
31975
  mode: { type: "string", description: "Optional: Execution mode - auto (default), context (always return context), execute (always spawn)", enum: ["auto", "context", "execute"], default: "auto" }
31709
31976
  },
31710
- required: ["agent", "task"]
31977
+ required: ["task"]
31711
31978
  }
31712
31979
  },
31713
31980
  {
@@ -31819,16 +32086,18 @@ Use this tool first to understand what AutomatosX offers.`,
31819
32086
  // v10.5.0: Smart Routing - Explicit context retrieval
31820
32087
  {
31821
32088
  name: "get_agent_context",
31822
- description: "Get agent context without executing. Returns profile, relevant memory, and enhanced prompt for AI assistant to execute directly.",
32089
+ description: `Get agent context without executing. Returns profile, relevant memory, and enhanced prompt for AI assistant to execute directly.
32090
+
32091
+ v12.5.1: Agent auto-selection - if agent is omitted, system automatically selects the best agent based on task keywords.`,
31823
32092
  inputSchema: {
31824
32093
  type: "object",
31825
32094
  properties: {
31826
- agent: { type: "string", description: "The name of the agent (e.g., backend, Paris, Bob)" },
32095
+ agent: { type: "string", description: "Optional: Agent name (e.g., backend, quality). If omitted, best agent is auto-selected based on task." },
31827
32096
  task: { type: "string", description: "The task description for context building" },
31828
32097
  includeMemory: { type: "boolean", description: "Include relevant memory entries (default: true)", default: true },
31829
32098
  maxMemoryResults: { type: "number", description: "Maximum memory entries to return (default: 5)", default: 5 }
31830
32099
  },
31831
- required: ["agent", "task"]
32100
+ required: ["task"]
31832
32101
  }
31833
32102
  },
31834
32103
  // v11.3.5: Task Engine tools
@@ -31837,6 +32106,8 @@ Use this tool first to understand what AutomatosX offers.`,
31837
32106
  getTaskResultSchema,
31838
32107
  listTasksSchema,
31839
32108
  deleteTaskSchema
32109
+ // v12.4.0: Bugfix tools intentionally NOT exposed via MCP
32110
+ // Access via: run_agent({ agent: "quality", task: "scan for bugs" })
31840
32111
  ];
31841
32112
  }
31842
32113
  /**
@@ -35187,232 +35458,6 @@ var memoryCommand = {
35187
35458
  // src/cli/commands/run.ts
35188
35459
  init_esm_shims();
35189
35460
 
35190
- // src/agents/agent-selector.ts
35191
- init_esm_shims();
35192
- init_logger();
35193
- function scoreAgent(task, profile) {
35194
- let score = 0;
35195
- const taskLower = task.toLowerCase();
35196
- if (profile.selectionMetadata?.primaryIntents) {
35197
- for (const intent of profile.selectionMetadata.primaryIntents) {
35198
- const intentKeywords = intent.toLowerCase().split(/\s+/);
35199
- const matchedKeywords = intentKeywords.filter(
35200
- (keyword) => taskLower.includes(keyword) && keyword.length > 3
35201
- );
35202
- if (matchedKeywords.length >= 2) {
35203
- score += 10;
35204
- } else if (matchedKeywords.length === 1) {
35205
- score += 5;
35206
- }
35207
- }
35208
- }
35209
- if (profile.selectionMetadata?.secondarySignals) {
35210
- for (const signal of profile.selectionMetadata.secondarySignals) {
35211
- if (taskLower.includes(signal.toLowerCase())) {
35212
- score += 5;
35213
- }
35214
- }
35215
- }
35216
- if (profile.selectionMetadata?.negativeIntents) {
35217
- for (const negative of profile.selectionMetadata.negativeIntents) {
35218
- const keywords = negative.split("(")[0]?.toLowerCase() || "";
35219
- const negativeKeywords = keywords.split(/\s+/).filter((k) => k.length > 3);
35220
- const matchedNegative = negativeKeywords.filter(
35221
- (keyword) => taskLower.includes(keyword)
35222
- );
35223
- if (matchedNegative.length >= 2) {
35224
- score -= 20;
35225
- }
35226
- }
35227
- }
35228
- if (profile.selectionMetadata?.redirectWhen) {
35229
- for (const rule of profile.selectionMetadata.redirectWhen) {
35230
- try {
35231
- const regex = new RegExp(rule.phrase, "i");
35232
- if (regex.test(task)) {
35233
- score -= 15;
35234
- }
35235
- } catch (error) {
35236
- logger.debug("Invalid regex pattern in redirectWhen rule", {
35237
- pattern: rule.phrase,
35238
- error: error instanceof Error ? error.message : String(error)
35239
- });
35240
- }
35241
- }
35242
- }
35243
- if (profile.abilitySelection?.taskBased) {
35244
- for (const keyword of Object.keys(profile.abilitySelection.taskBased)) {
35245
- if (taskLower.includes(keyword.toLowerCase())) {
35246
- score += 3;
35247
- }
35248
- }
35249
- }
35250
- return Math.max(0, score);
35251
- }
35252
- function buildRationale(task, profile) {
35253
- const rationale = [];
35254
- const taskLower = task.toLowerCase();
35255
- if (profile.selectionMetadata?.primaryIntents) {
35256
- const matchedIntents = profile.selectionMetadata.primaryIntents.filter((intent) => {
35257
- const keywords = intent.toLowerCase().split(/\s+/);
35258
- return keywords.some((k) => taskLower.includes(k) && k.length > 3);
35259
- });
35260
- if (matchedIntents.length > 0) {
35261
- rationale.push(`Matches: ${matchedIntents.slice(0, 2).join(", ")}`);
35262
- }
35263
- }
35264
- if (profile.selectionMetadata?.secondarySignals) {
35265
- const matchedSignals = profile.selectionMetadata.secondarySignals.filter(
35266
- (signal) => taskLower.includes(signal.toLowerCase())
35267
- );
35268
- if (matchedSignals.length > 0) {
35269
- rationale.push(`Keywords: ${matchedSignals.slice(0, 3).join(", ")}`);
35270
- }
35271
- }
35272
- if (profile.abilitySelection?.taskBased) {
35273
- const matchedAbilities = Object.keys(profile.abilitySelection.taskBased).filter(
35274
- (keyword) => taskLower.includes(keyword.toLowerCase())
35275
- );
35276
- if (matchedAbilities.length > 0) {
35277
- rationale.push(`Abilities: ${matchedAbilities.slice(0, 2).join(", ")}`);
35278
- }
35279
- }
35280
- if (rationale.length === 0) {
35281
- rationale.push("General capability match");
35282
- }
35283
- return rationale;
35284
- }
35285
- function getConfidence(score) {
35286
- if (score >= 30) return "high";
35287
- if (score >= 15) return "medium";
35288
- return "low";
35289
- }
35290
- var MIN_SELECTION_SCORE = 5;
35291
- var FALLBACK_AGENT = "standard";
35292
- var AgentSelector = class {
35293
- profileLoader;
35294
- constructor(profileLoader) {
35295
- this.profileLoader = profileLoader;
35296
- }
35297
- /**
35298
- * Select the best agent for a task
35299
- *
35300
- * @param task - Task description
35301
- * @returns Selection result with agent, score, confidence, and rationale
35302
- */
35303
- async selectAgent(task) {
35304
- logger.debug("[AgentSelector] Selecting agent for task", {
35305
- taskPreview: task.substring(0, 100)
35306
- });
35307
- const agentNames = await this.profileLoader.listProfiles();
35308
- if (agentNames.length === 0) {
35309
- logger.warn("[AgentSelector] No agents found, using fallback");
35310
- return this.createFallbackResult(task);
35311
- }
35312
- const profileResults = await Promise.all(
35313
- agentNames.map(async (name) => {
35314
- try {
35315
- const profile = await this.profileLoader.loadProfile(name);
35316
- return { name, profile, score: scoreAgent(task, profile) };
35317
- } catch (error) {
35318
- logger.debug(`[AgentSelector] Failed to load profile: ${name}`, { error });
35319
- return null;
35320
- }
35321
- })
35322
- );
35323
- const scoredAgents = profileResults.filter(
35324
- (result) => result !== null
35325
- );
35326
- if (scoredAgents.length === 0) {
35327
- logger.warn("[AgentSelector] Failed to load any profiles, using fallback");
35328
- return this.createFallbackResult(task);
35329
- }
35330
- scoredAgents.sort((a, b) => b.score - a.score);
35331
- const best = scoredAgents[0];
35332
- const alternatives = scoredAgents.slice(1, 4).map((a) => ({
35333
- agent: a.name,
35334
- score: a.score
35335
- }));
35336
- if (best.score < MIN_SELECTION_SCORE) {
35337
- logger.info("[AgentSelector] Best score below threshold, using fallback", {
35338
- bestAgent: best.name,
35339
- bestScore: best.score,
35340
- threshold: MIN_SELECTION_SCORE
35341
- });
35342
- const standardAgent = scoredAgents.find((a) => a.name === FALLBACK_AGENT);
35343
- if (standardAgent) {
35344
- return {
35345
- agent: standardAgent.name,
35346
- displayName: standardAgent.profile.displayName || standardAgent.name,
35347
- role: standardAgent.profile.role,
35348
- score: standardAgent.score,
35349
- confidence: "low",
35350
- rationale: ["No strong match found, using general-purpose agent"],
35351
- alternatives: scoredAgents.filter((a) => a.name !== FALLBACK_AGENT).slice(0, 3).map((a) => ({ agent: a.name, score: a.score })),
35352
- usedFallback: true
35353
- };
35354
- }
35355
- return {
35356
- agent: best.name,
35357
- displayName: best.profile.displayName || best.name,
35358
- role: best.profile.role,
35359
- score: best.score,
35360
- confidence: "low",
35361
- rationale: ["Low confidence match - consider specifying agent explicitly"],
35362
- alternatives,
35363
- usedFallback: false
35364
- };
35365
- }
35366
- const rationale = buildRationale(task, best.profile);
35367
- const confidence = getConfidence(best.score);
35368
- logger.info("[AgentSelector] Agent selected", {
35369
- agent: best.name,
35370
- score: best.score,
35371
- confidence,
35372
- rationale
35373
- });
35374
- return {
35375
- agent: best.name,
35376
- displayName: best.profile.displayName || best.name,
35377
- role: best.profile.role,
35378
- score: best.score,
35379
- confidence,
35380
- rationale,
35381
- alternatives,
35382
- usedFallback: false
35383
- };
35384
- }
35385
- /**
35386
- * Create a fallback result when no agents are available
35387
- */
35388
- async createFallbackResult(_task) {
35389
- try {
35390
- const profile = await this.profileLoader.loadProfile(FALLBACK_AGENT);
35391
- return {
35392
- agent: FALLBACK_AGENT,
35393
- displayName: profile.displayName || FALLBACK_AGENT,
35394
- role: profile.role,
35395
- score: 0,
35396
- confidence: "low",
35397
- rationale: ["No agents available, using fallback"],
35398
- alternatives: [],
35399
- usedFallback: true
35400
- };
35401
- } catch {
35402
- return {
35403
- agent: FALLBACK_AGENT,
35404
- displayName: "Standard",
35405
- role: "General-purpose agent",
35406
- score: 0,
35407
- confidence: "low",
35408
- rationale: ["No agents available"],
35409
- alternatives: [],
35410
- usedFallback: true
35411
- };
35412
- }
35413
- }
35414
- };
35415
-
35416
35461
  // src/core/stage-execution-controller.ts
35417
35462
  init_esm_shims();
35418
35463
 
@@ -42317,8 +42362,8 @@ async function getProjectInfo(projectDir) {
42317
42362
  return {};
42318
42363
  }
42319
42364
  try {
42320
- const { readFile: readFile22 } = await import('fs/promises');
42321
- const content = await readFile22(packageJsonPath, "utf-8");
42365
+ const { readFile: readFile24 } = await import('fs/promises');
42366
+ const content = await readFile24(packageJsonPath, "utf-8");
42322
42367
  const pkg = JSON.parse(content);
42323
42368
  return {
42324
42369
  name: pkg.name,
@@ -42431,13 +42476,13 @@ async function getCurrentVersion() {
42431
42476
  const result = JSON.parse(stdout);
42432
42477
  return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
42433
42478
  } catch (error) {
42434
- const { readFile: readFile22 } = await import('fs/promises');
42435
- const { dirname: dirname16, join: join52 } = await import('path');
42479
+ const { readFile: readFile24 } = await import('fs/promises');
42480
+ const { dirname: dirname17, join: join54 } = await import('path');
42436
42481
  const { fileURLToPath: fileURLToPath5 } = await import('url');
42437
42482
  const __filename3 = fileURLToPath5(import.meta.url);
42438
- const __dirname4 = dirname16(__filename3);
42439
- const pkgPath = join52(__dirname4, "../../../package.json");
42440
- const content = await readFile22(pkgPath, "utf-8");
42483
+ const __dirname4 = dirname17(__filename3);
42484
+ const pkgPath = join54(__dirname4, "../../../package.json");
42485
+ const content = await readFile24(pkgPath, "utf-8");
42441
42486
  const pkg = JSON.parse(content);
42442
42487
  return pkg.version;
42443
42488
  }
@@ -49488,10 +49533,10 @@ async function handleReset() {
49488
49533
  `));
49489
49534
  }
49490
49535
  async function handleTrace(workspacePath, argv) {
49491
- const { existsSync: existsSync28, readFileSync: readFileSync10, watchFile } = await import('fs');
49492
- const { join: join52 } = await import('path');
49493
- const traceFile = join52(workspacePath, ".automatosx/logs/router.trace.jsonl");
49494
- if (!existsSync28(traceFile)) {
49536
+ const { existsSync: existsSync29, readFileSync: readFileSync10, watchFile } = await import('fs');
49537
+ const { join: join54 } = await import('path');
49538
+ const traceFile = join54(workspacePath, ".automatosx/logs/router.trace.jsonl");
49539
+ if (!existsSync29(traceFile)) {
49495
49540
  console.log(chalk5.yellow("\n\u26A0\uFE0F No trace log found\n"));
49496
49541
  console.log(chalk5.gray(`Expected location: ${traceFile}
49497
49542
  `));
@@ -50225,8 +50270,8 @@ async function runCodexDiagnostics(verbose) {
50225
50270
  const configPath = join(workingDir, "ax.config.json");
50226
50271
  if (existsSync(configPath)) {
50227
50272
  try {
50228
- const { readFile: readFile22 } = await import('fs/promises');
50229
- const configContent = await readFile22(configPath, "utf-8");
50273
+ const { readFile: readFile24 } = await import('fs/promises');
50274
+ const configContent = await readFile24(configPath, "utf-8");
50230
50275
  const config = JSON.parse(configContent);
50231
50276
  providerConfigured = config?.providers?.openai?.enabled === true;
50232
50277
  } catch {
@@ -50910,8 +50955,8 @@ async function runMcpDiagnostics(verbose) {
50910
50955
  let serverStarts = false;
50911
50956
  if (cliAvailable) {
50912
50957
  try {
50913
- const { spawn: spawn11 } = await import('child_process');
50914
- const proc = spawn11("automatosx", ["mcp", "server"], { stdio: ["pipe", "pipe", "pipe"] });
50958
+ const { spawn: spawn12 } = await import('child_process');
50959
+ const proc = spawn12("automatosx", ["mcp", "server"], { stdio: ["pipe", "pipe", "pipe"] });
50915
50960
  const initResult = await new Promise((resolve13) => {
50916
50961
  let output = "";
50917
50962
  proc.stderr?.on("data", (data) => {
@@ -55574,6 +55619,1641 @@ var reviewCommand = {
55574
55619
  }
55575
55620
  };
55576
55621
 
55622
+ // src/cli/commands/bugfix.ts
55623
+ init_esm_shims();
55624
+
55625
+ // src/core/bugfix/index.ts
55626
+ init_esm_shims();
55627
+
55628
+ // src/core/bugfix/types.ts
55629
+ init_esm_shims();
55630
+
55631
+ // src/core/bugfix/bug-detector.ts
55632
+ init_esm_shims();
55633
+ init_logger();
55634
+ var DEFAULT_DETECTION_RULES = [
55635
+ // Timer leak: setInterval without .unref()
55636
+ {
55637
+ id: "timer-leak-interval",
55638
+ type: "timer_leak",
55639
+ name: "setInterval without unref",
55640
+ description: "setInterval() without .unref() blocks process exit",
55641
+ pattern: "setInterval\\s*\\(",
55642
+ negativePattern: "\\.unref\\s*\\(\\)",
55643
+ withinLines: 5,
55644
+ confidence: 0.9,
55645
+ severity: "high",
55646
+ autoFixable: true,
55647
+ fixTemplate: "add_unref",
55648
+ fileExtensions: [".ts", ".js", ".mts", ".mjs"]
55649
+ },
55650
+ // Timer leak: setTimeout in promise without cleanup
55651
+ {
55652
+ id: "timer-leak-timeout-promise",
55653
+ type: "promise_timeout_leak",
55654
+ name: "setTimeout in Promise without cleanup",
55655
+ description: "setTimeout in Promise should be cleared in finally block",
55656
+ pattern: "new\\s+Promise[^}]*setTimeout\\s*\\(",
55657
+ negativePattern: "finally|clearTimeout",
55658
+ withinLines: 20,
55659
+ confidence: 0.7,
55660
+ severity: "medium",
55661
+ autoFixable: false,
55662
+ // Complex, needs manual review
55663
+ fileExtensions: [".ts", ".js", ".mts", ".mjs"]
55664
+ },
55665
+ // Missing destroy: EventEmitter without destroy method
55666
+ {
55667
+ id: "missing-destroy-eventemitter",
55668
+ type: "missing_destroy",
55669
+ name: "EventEmitter without destroy",
55670
+ description: "Classes extending EventEmitter should have destroy() method",
55671
+ pattern: "class\\s+\\w+\\s+extends\\s+(?:EventEmitter|DisposableEventEmitter)",
55672
+ negativePattern: "destroy\\s*\\(\\s*\\)",
55673
+ withinLines: 100,
55674
+ confidence: 0.85,
55675
+ severity: "high",
55676
+ autoFixable: true,
55677
+ fixTemplate: "add_destroy_method",
55678
+ fileExtensions: [".ts", ".js", ".mts", ".mjs"]
55679
+ },
55680
+ // Event leak: .on() without corresponding cleanup
55681
+ {
55682
+ id: "event-leak-on",
55683
+ type: "event_leak",
55684
+ name: "Event listener without cleanup",
55685
+ description: ".on() or .addListener() without corresponding .off() or .removeListener()",
55686
+ pattern: "\\.(on|addListener)\\s*\\(['\"`]\\w+['\"`]",
55687
+ negativePattern: "\\.(off|removeListener|removeAllListeners)\\s*\\(",
55688
+ withinLines: 50,
55689
+ confidence: 0.6,
55690
+ // Lower confidence - may have false positives
55691
+ severity: "medium",
55692
+ autoFixable: false,
55693
+ fileExtensions: [".ts", ".js", ".mts", ".mjs"]
55694
+ },
55695
+ // Uncaught promise: Promise without catch
55696
+ {
55697
+ id: "uncaught-promise",
55698
+ type: "uncaught_promise",
55699
+ name: "Promise without error handling",
55700
+ description: "Promise should have .catch() or be awaited in try/catch",
55701
+ pattern: "new\\s+Promise\\s*\\([^)]+\\)",
55702
+ negativePattern: "\\.catch\\s*\\(|try\\s*\\{",
55703
+ withinLines: 10,
55704
+ confidence: 0.5,
55705
+ // Low confidence - many false positives
55706
+ severity: "low",
55707
+ autoFixable: false,
55708
+ fileExtensions: [".ts", ".js", ".mts", ".mjs"]
55709
+ }
55710
+ ];
55711
+ var BugDetector = class {
55712
+ rules;
55713
+ config;
55714
+ constructor(config, customRules) {
55715
+ this.config = config;
55716
+ this.rules = customRules || DEFAULT_DETECTION_RULES;
55717
+ if (config.bugTypes.length > 0) {
55718
+ this.rules = this.rules.filter(
55719
+ (rule) => config.bugTypes.includes(rule.type)
55720
+ );
55721
+ }
55722
+ logger.debug("BugDetector initialized", {
55723
+ ruleCount: this.rules.length,
55724
+ bugTypes: config.bugTypes,
55725
+ scope: config.scope
55726
+ });
55727
+ }
55728
+ /**
55729
+ * Scan codebase for bugs
55730
+ *
55731
+ * @param rootDir - Root directory to scan
55732
+ * @returns Array of bug findings
55733
+ */
55734
+ async scan(rootDir) {
55735
+ const startTime = Date.now();
55736
+ const findings = [];
55737
+ logger.info("Starting bug scan", {
55738
+ rootDir,
55739
+ scope: this.config.scope,
55740
+ ruleCount: this.rules.length
55741
+ });
55742
+ const scanDir = this.config.scope ? join(rootDir, this.config.scope) : rootDir;
55743
+ const files = await this.getFilesToScan(scanDir, rootDir);
55744
+ logger.debug("Files to scan", { count: files.length });
55745
+ for (const file of files) {
55746
+ try {
55747
+ const fileFindings = await this.scanFile(file, rootDir);
55748
+ findings.push(...fileFindings);
55749
+ } catch (error) {
55750
+ logger.warn("Error scanning file", {
55751
+ file,
55752
+ error: error.message
55753
+ });
55754
+ }
55755
+ }
55756
+ const filteredFindings = this.filterBySeverity(findings);
55757
+ filteredFindings.sort((a, b) => {
55758
+ const severityOrder = {
55759
+ critical: 4,
55760
+ high: 3,
55761
+ medium: 2,
55762
+ low: 1
55763
+ };
55764
+ const severityDiff = severityOrder[b.severity] - severityOrder[a.severity];
55765
+ if (severityDiff !== 0) return severityDiff;
55766
+ return b.confidence - a.confidence;
55767
+ });
55768
+ const duration = Date.now() - startTime;
55769
+ logger.info("Bug scan complete", {
55770
+ totalFindings: filteredFindings.length,
55771
+ filesScanned: files.length,
55772
+ durationMs: duration
55773
+ });
55774
+ return filteredFindings;
55775
+ }
55776
+ /**
55777
+ * Scan a single file for bugs
55778
+ */
55779
+ async scanFile(filePath, rootDir) {
55780
+ const findings = [];
55781
+ const content = await readFile(filePath, "utf-8");
55782
+ const lines = content.split("\n");
55783
+ const relativePath = relative(rootDir, filePath);
55784
+ for (const rule of this.rules) {
55785
+ if (rule.fileExtensions && rule.fileExtensions.length > 0) {
55786
+ const ext = extname$1(filePath);
55787
+ if (!rule.fileExtensions.includes(ext)) {
55788
+ continue;
55789
+ }
55790
+ }
55791
+ const ruleFindings = this.applyRule(rule, content, lines, relativePath);
55792
+ findings.push(...ruleFindings);
55793
+ }
55794
+ return findings;
55795
+ }
55796
+ /**
55797
+ * Apply a detection rule to file content
55798
+ */
55799
+ applyRule(rule, content, lines, filePath) {
55800
+ const findings = [];
55801
+ if (!rule.pattern) {
55802
+ return findings;
55803
+ }
55804
+ try {
55805
+ const regex = new RegExp(rule.pattern, "g");
55806
+ let match;
55807
+ while ((match = regex.exec(content)) !== null) {
55808
+ const beforeMatch = content.substring(0, match.index);
55809
+ const lineNumber = beforeMatch.split("\n").length;
55810
+ if (rule.negativePattern) {
55811
+ const withinLines = rule.withinLines || 5;
55812
+ const startLine = Math.max(0, lineNumber - 1);
55813
+ const endLine = Math.min(lines.length, lineNumber + withinLines);
55814
+ const contextLines = lines.slice(startLine, endLine).join("\n");
55815
+ const negativeRegex = new RegExp(rule.negativePattern);
55816
+ if (negativeRegex.test(contextLines)) {
55817
+ continue;
55818
+ }
55819
+ }
55820
+ const contextStart = Math.max(0, lineNumber - 3);
55821
+ const contextEnd = Math.min(lines.length, lineNumber + 3);
55822
+ const context = lines.slice(contextStart, contextEnd).join("\n");
55823
+ const finding = {
55824
+ id: randomUUID(),
55825
+ file: filePath,
55826
+ lineStart: lineNumber,
55827
+ lineEnd: lineNumber + (rule.withinLines ? Math.min(rule.withinLines, 5) : 1),
55828
+ type: rule.type,
55829
+ severity: rule.severity,
55830
+ message: rule.description,
55831
+ context,
55832
+ fixStrategy: rule.autoFixable ? rule.fixTemplate : void 0,
55833
+ confidence: rule.confidence,
55834
+ detectionMethod: "regex",
55835
+ metadata: {
55836
+ ruleId: rule.id,
55837
+ ruleName: rule.name,
55838
+ matchedText: match[0].substring(0, 100)
55839
+ },
55840
+ detectedAt: (/* @__PURE__ */ new Date()).toISOString()
55841
+ };
55842
+ findings.push(finding);
55843
+ logger.debug("Bug detected", {
55844
+ file: filePath,
55845
+ line: lineNumber,
55846
+ type: rule.type,
55847
+ rule: rule.id
55848
+ });
55849
+ }
55850
+ } catch (error) {
55851
+ logger.warn("Rule application failed", {
55852
+ ruleId: rule.id,
55853
+ file: filePath,
55854
+ error: error.message
55855
+ });
55856
+ }
55857
+ return findings;
55858
+ }
55859
+ /**
55860
+ * Get all files to scan
55861
+ */
55862
+ async getFilesToScan(scanDir, rootDir) {
55863
+ const files = [];
55864
+ const scanDirectory = async (dir) => {
55865
+ try {
55866
+ const entries = await readdir(dir);
55867
+ for (const entry of entries) {
55868
+ const fullPath = join(dir, entry);
55869
+ const relativePath = relative(rootDir, fullPath);
55870
+ if (this.isExcluded(relativePath)) {
55871
+ continue;
55872
+ }
55873
+ const stats = await stat(fullPath);
55874
+ if (stats.isDirectory()) {
55875
+ await scanDirectory(fullPath);
55876
+ } else if (stats.isFile()) {
55877
+ const ext = extname$1(fullPath);
55878
+ if ([".ts", ".js", ".mts", ".mjs", ".tsx", ".jsx"].includes(ext)) {
55879
+ files.push(fullPath);
55880
+ }
55881
+ }
55882
+ }
55883
+ } catch (error) {
55884
+ logger.warn("Error reading directory", {
55885
+ dir,
55886
+ error: error.message
55887
+ });
55888
+ }
55889
+ };
55890
+ await scanDirectory(scanDir);
55891
+ return files;
55892
+ }
55893
+ /**
55894
+ * Check if a path should be excluded
55895
+ */
55896
+ isExcluded(relativePath) {
55897
+ const defaultExclusions = [
55898
+ "node_modules",
55899
+ "dist",
55900
+ "build",
55901
+ ".git",
55902
+ "coverage",
55903
+ ".nyc_output",
55904
+ "*.test.ts",
55905
+ "*.spec.ts",
55906
+ "__tests__",
55907
+ "__mocks__"
55908
+ ];
55909
+ const exclusions = [...defaultExclusions, ...this.config.excludePatterns];
55910
+ for (const pattern of exclusions) {
55911
+ if (pattern.includes("*")) {
55912
+ const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
55913
+ const regex = new RegExp(regexPattern);
55914
+ if (regex.test(relativePath)) {
55915
+ return true;
55916
+ }
55917
+ } else {
55918
+ if (relativePath.includes(pattern)) {
55919
+ return true;
55920
+ }
55921
+ }
55922
+ }
55923
+ return false;
55924
+ }
55925
+ /**
55926
+ * Filter findings by severity threshold
55927
+ */
55928
+ filterBySeverity(findings) {
55929
+ const severityOrder = {
55930
+ low: 1,
55931
+ medium: 2,
55932
+ high: 3,
55933
+ critical: 4
55934
+ };
55935
+ const threshold = severityOrder[this.config.severityThreshold];
55936
+ return findings.filter(
55937
+ (finding) => severityOrder[finding.severity] >= threshold
55938
+ );
55939
+ }
55940
+ /**
55941
+ * Get detection rules
55942
+ */
55943
+ getRules() {
55944
+ return [...this.rules];
55945
+ }
55946
+ /**
55947
+ * Add a custom detection rule
55948
+ */
55949
+ addRule(rule) {
55950
+ this.rules.push(rule);
55951
+ logger.debug("Detection rule added", { ruleId: rule.id });
55952
+ }
55953
+ /**
55954
+ * Load rules from YAML file
55955
+ */
55956
+ async loadRulesFromFile(filePath) {
55957
+ try {
55958
+ const content = await readFile(filePath, "utf-8");
55959
+ const yaml5 = await import('js-yaml');
55960
+ const parsed = yaml5.load(content);
55961
+ if (parsed.rules && Array.isArray(parsed.rules)) {
55962
+ for (const rule of parsed.rules) {
55963
+ this.addRule(rule);
55964
+ }
55965
+ logger.info("Detection rules loaded from file", {
55966
+ filePath,
55967
+ ruleCount: parsed.rules.length
55968
+ });
55969
+ }
55970
+ } catch (error) {
55971
+ logger.warn("Failed to load detection rules", {
55972
+ filePath,
55973
+ error: error.message
55974
+ });
55975
+ }
55976
+ }
55977
+ };
55978
+ function createDefaultBugfixConfig(overrides) {
55979
+ return {
55980
+ maxBugs: 10,
55981
+ maxDurationMinutes: 60,
55982
+ maxTokens: 5e5,
55983
+ maxRetriesPerBug: 3,
55984
+ minConfidence: 0.7,
55985
+ bugTypes: ["timer_leak", "missing_destroy", "promise_timeout_leak"],
55986
+ severityThreshold: "medium",
55987
+ excludePatterns: [],
55988
+ dryRun: false,
55989
+ requireTests: true,
55990
+ requireTypecheck: true,
55991
+ generateTests: false,
55992
+ verbose: false,
55993
+ ...overrides
55994
+ };
55995
+ }
55996
+
55997
+ // src/core/bugfix/bug-fixer.ts
55998
+ init_esm_shims();
55999
+ init_logger();
56000
+ var DEFAULT_FIX_TEMPLATES = [
56001
+ {
56002
+ id: "add_unref",
56003
+ name: "Add .unref() to interval",
56004
+ description: "Add .unref() call after setInterval to prevent blocking process exit",
56005
+ bugType: "timer_leak",
56006
+ template: `
56007
+ // Replace setInterval with createSafeInterval for automatic cleanup
56008
+ import { createSafeInterval } from '@/shared/utils';
56009
+
56010
+ // Or add .unref() manually:
56011
+ // const interval = setInterval(callback, ms);
56012
+ // if (interval.unref) interval.unref();
56013
+ `,
56014
+ imports: ['createSafeInterval from "@/shared/utils"'],
56015
+ confidence: 0.9
56016
+ },
56017
+ {
56018
+ id: "add_destroy_method",
56019
+ name: "Add destroy() method",
56020
+ description: "Add destroy() method that calls removeAllListeners()",
56021
+ bugType: "missing_destroy",
56022
+ template: `
56023
+ /**
56024
+ * Clean up resources and remove all event listeners.
56025
+ */
56026
+ destroy(): void {
56027
+ this.removeAllListeners();
56028
+ }
56029
+ `,
56030
+ confidence: 0.85
56031
+ },
56032
+ {
56033
+ id: "use_disposable_eventemitter",
56034
+ name: "Extend DisposableEventEmitter",
56035
+ description: "Replace EventEmitter with DisposableEventEmitter for automatic cleanup",
56036
+ bugType: "missing_destroy",
56037
+ template: `
56038
+ // Change: extends EventEmitter
56039
+ // To: extends DisposableEventEmitter
56040
+
56041
+ import { DisposableEventEmitter } from '@/shared/utils';
56042
+
56043
+ // Then implement onDestroy() hook:
56044
+ protected onDestroy(): void {
56045
+ // Custom cleanup logic
56046
+ }
56047
+ `,
56048
+ imports: ['DisposableEventEmitter from "@/shared/utils"'],
56049
+ confidence: 0.9
56050
+ },
56051
+ {
56052
+ id: "wrap_with_timeout",
56053
+ name: "Wrap with withTimeout",
56054
+ description: "Use withTimeout() utility for automatic cleanup",
56055
+ bugType: "promise_timeout_leak",
56056
+ template: `
56057
+ import { withTimeout } from '@/shared/utils';
56058
+
56059
+ // Replace manual timeout handling with:
56060
+ const result = await withTimeout(promise, timeoutMs, {
56061
+ message: 'Operation timed out'
56062
+ });
56063
+ `,
56064
+ imports: ['withTimeout from "@/shared/utils"'],
56065
+ confidence: 0.85
56066
+ }
56067
+ ];
56068
+ var BugFixer = class {
56069
+ templates;
56070
+ backupDir;
56071
+ backups;
56072
+ // filePath -> backupPath
56073
+ constructor(backupDir) {
56074
+ this.templates = /* @__PURE__ */ new Map();
56075
+ this.backupDir = backupDir || join(process.cwd(), ".automatosx", "backups");
56076
+ this.backups = /* @__PURE__ */ new Map();
56077
+ for (const template of DEFAULT_FIX_TEMPLATES) {
56078
+ this.templates.set(template.id, template);
56079
+ }
56080
+ logger.debug("BugFixer initialized", {
56081
+ templateCount: this.templates.size,
56082
+ backupDir: this.backupDir
56083
+ });
56084
+ }
56085
+ /**
56086
+ * Apply a fix for a bug finding
56087
+ *
56088
+ * @param finding - Bug finding to fix
56089
+ * @param rootDir - Root directory of the project
56090
+ * @param dryRun - If true, don't actually modify files
56091
+ * @returns Fix attempt result
56092
+ */
56093
+ async applyFix(finding, rootDir, dryRun = false) {
56094
+ const startTime = Date.now();
56095
+ const attemptId = randomUUID();
56096
+ const filePath = join(rootDir, finding.file);
56097
+ logger.info("Applying fix", {
56098
+ bugId: finding.id,
56099
+ file: finding.file,
56100
+ type: finding.type,
56101
+ dryRun
56102
+ });
56103
+ try {
56104
+ const originalContent = await readFile(filePath, "utf-8");
56105
+ const lines = originalContent.split("\n");
56106
+ const strategy = this.determineStrategy(finding);
56107
+ if (!strategy) {
56108
+ return this.createAttempt(attemptId, finding.id, 1, "manual_review", "", "skipped", startTime, "No automatic fix available");
56109
+ }
56110
+ const { fixedContent, diff } = await this.generateFix(finding, originalContent, lines, strategy);
56111
+ if (!fixedContent || fixedContent === originalContent) {
56112
+ return this.createAttempt(attemptId, finding.id, 1, strategy, "", "skipped", startTime, "No changes needed");
56113
+ }
56114
+ if (dryRun) {
56115
+ logger.info("Dry run - fix not applied", {
56116
+ bugId: finding.id,
56117
+ strategy,
56118
+ diffLength: diff.length
56119
+ });
56120
+ return this.createAttempt(attemptId, finding.id, 1, strategy, diff, "applied", startTime);
56121
+ }
56122
+ await this.createBackup(filePath);
56123
+ await writeFile(filePath, fixedContent, "utf-8");
56124
+ logger.info("Fix applied", {
56125
+ bugId: finding.id,
56126
+ file: finding.file,
56127
+ strategy
56128
+ });
56129
+ return this.createAttempt(attemptId, finding.id, 1, strategy, diff, "applied", startTime);
56130
+ } catch (error) {
56131
+ logger.error("Fix application failed", {
56132
+ bugId: finding.id,
56133
+ file: finding.file,
56134
+ error: error.message
56135
+ });
56136
+ return this.createAttempt(attemptId, finding.id, 1, "unknown", "", "failed", startTime, error.message);
56137
+ }
56138
+ }
56139
+ /**
56140
+ * Rollback a fix
56141
+ *
56142
+ * @param filePath - File to rollback
56143
+ * @returns True if rollback successful
56144
+ */
56145
+ async rollback(filePath) {
56146
+ const backupPath = this.backups.get(filePath);
56147
+ if (!backupPath || !existsSync(backupPath)) {
56148
+ logger.warn("No backup found for rollback", { filePath });
56149
+ return false;
56150
+ }
56151
+ try {
56152
+ await copyFile(backupPath, filePath);
56153
+ await unlink(backupPath);
56154
+ this.backups.delete(filePath);
56155
+ logger.info("Fix rolled back", { filePath });
56156
+ return true;
56157
+ } catch (error) {
56158
+ logger.error("Rollback failed", {
56159
+ filePath,
56160
+ error: error.message
56161
+ });
56162
+ return false;
56163
+ }
56164
+ }
56165
+ /**
56166
+ * Rollback all fixes in this session
56167
+ */
56168
+ async rollbackAll() {
56169
+ let rolledBack = 0;
56170
+ for (const filePath of this.backups.keys()) {
56171
+ if (await this.rollback(filePath)) {
56172
+ rolledBack++;
56173
+ }
56174
+ }
56175
+ logger.info("All fixes rolled back", { count: rolledBack });
56176
+ return rolledBack;
56177
+ }
56178
+ /**
56179
+ * Clean up backups (call after successful verification)
56180
+ */
56181
+ async cleanupBackups() {
56182
+ for (const [filePath, backupPath] of this.backups.entries()) {
56183
+ try {
56184
+ if (existsSync(backupPath)) {
56185
+ await unlink(backupPath);
56186
+ }
56187
+ this.backups.delete(filePath);
56188
+ } catch (error) {
56189
+ logger.warn("Failed to cleanup backup", {
56190
+ filePath,
56191
+ backupPath,
56192
+ error: error.message
56193
+ });
56194
+ }
56195
+ }
56196
+ logger.debug("Backups cleaned up");
56197
+ }
56198
+ /**
56199
+ * Determine fix strategy for a finding
56200
+ */
56201
+ determineStrategy(finding) {
56202
+ if (finding.fixStrategy) {
56203
+ return finding.fixStrategy;
56204
+ }
56205
+ for (const template of this.templates.values()) {
56206
+ if (template.bugType === finding.type) {
56207
+ return template.id;
56208
+ }
56209
+ }
56210
+ const autoFixableTypes = ["timer_leak", "missing_destroy"];
56211
+ if (autoFixableTypes.includes(finding.type)) {
56212
+ return `auto_fix_${finding.type}`;
56213
+ }
56214
+ return null;
56215
+ }
56216
+ /**
56217
+ * Generate fix for a finding
56218
+ */
56219
+ async generateFix(finding, originalContent, lines, strategy) {
56220
+ let fixedContent = originalContent;
56221
+ let diff = "";
56222
+ switch (strategy) {
56223
+ case "add_unref":
56224
+ ({ fixedContent, diff } = this.applyAddUnrefFix(finding, originalContent, lines));
56225
+ break;
56226
+ case "add_destroy_method":
56227
+ ({ fixedContent, diff } = this.applyAddDestroyMethodFix(finding, originalContent, lines));
56228
+ break;
56229
+ case "use_disposable_eventemitter":
56230
+ ({ fixedContent, diff } = this.applyUseDisposableEventEmitterFix(finding, originalContent, lines));
56231
+ break;
56232
+ case "auto_fix_timer_leak":
56233
+ ({ fixedContent, diff } = this.applyAddUnrefFix(finding, originalContent, lines));
56234
+ break;
56235
+ case "auto_fix_missing_destroy":
56236
+ ({ fixedContent, diff } = this.applyAddDestroyMethodFix(finding, originalContent, lines));
56237
+ break;
56238
+ default:
56239
+ logger.warn("Unknown fix strategy", { strategy });
56240
+ }
56241
+ return { fixedContent, diff };
56242
+ }
56243
+ /**
56244
+ * Apply add .unref() fix
56245
+ */
56246
+ applyAddUnrefFix(finding, originalContent, lines) {
56247
+ const lineIndex = finding.lineStart - 1;
56248
+ const line = lines[lineIndex];
56249
+ if (!line) {
56250
+ return { fixedContent: originalContent, diff: "" };
56251
+ }
56252
+ const setIntervalPattern = /(\w+)\s*=\s*setInterval\s*\([^)]+\)\s*;?/;
56253
+ const match = line.match(setIntervalPattern);
56254
+ if (match) {
56255
+ const varName = match[1];
56256
+ const nextLines = lines.slice(lineIndex + 1, lineIndex + 5).join("\n");
56257
+ if (nextLines.includes(`${varName}.unref`) || nextLines.includes(`${varName}?.unref`)) {
56258
+ return { fixedContent: originalContent, diff: "" };
56259
+ }
56260
+ const indent = line.match(/^(\s*)/)?.[1] || "";
56261
+ const unrefLine = `${indent}if (${varName}.unref) ${varName}.unref();`;
56262
+ const newLines = [...lines];
56263
+ newLines.splice(lineIndex + 1, 0, unrefLine);
56264
+ const fixedContent = newLines.join("\n");
56265
+ const diff = `@@ -${finding.lineStart},1 +${finding.lineStart},2 @@
56266
+ ${line}
56267
+ +${unrefLine}`;
56268
+ return { fixedContent, diff };
56269
+ }
56270
+ const directSetIntervalPattern = /setInterval\s*\(/;
56271
+ if (directSetIntervalPattern.test(line)) {
56272
+ line.match(/^(\s*)/)?.[1] || "";
56273
+ const newLine = line.replace(
56274
+ /(setInterval\s*\([^)]+\))/,
56275
+ "const _interval = $1; if (_interval.unref) _interval.unref()"
56276
+ );
56277
+ const newLines = [...lines];
56278
+ newLines[lineIndex] = newLine;
56279
+ const fixedContent = newLines.join("\n");
56280
+ const diff = `@@ -${finding.lineStart},1 +${finding.lineStart},1 @@
56281
+ -${line}
56282
+ +${newLine}`;
56283
+ return { fixedContent, diff };
56284
+ }
56285
+ return { fixedContent: originalContent, diff: "" };
56286
+ }
56287
+ /**
56288
+ * Apply add destroy() method fix
56289
+ */
56290
+ applyAddDestroyMethodFix(finding, originalContent, lines) {
56291
+ const classPattern = /class\s+(\w+)\s+extends\s+(?:EventEmitter|DisposableEventEmitter)/;
56292
+ let classStartLine = -1;
56293
+ for (let i = 0; i < lines.length; i++) {
56294
+ const currentLine = lines[i];
56295
+ if (!currentLine) continue;
56296
+ const match = currentLine.match(classPattern);
56297
+ if (match && match[1]) {
56298
+ classStartLine = i;
56299
+ match[1];
56300
+ break;
56301
+ }
56302
+ }
56303
+ if (classStartLine === -1) {
56304
+ return { fixedContent: originalContent, diff: "" };
56305
+ }
56306
+ let braceCount = 0;
56307
+ let classEndLine = -1;
56308
+ for (let i = classStartLine; i < lines.length; i++) {
56309
+ const line = lines[i];
56310
+ if (!line) continue;
56311
+ braceCount += (line.match(/{/g) || []).length;
56312
+ braceCount -= (line.match(/}/g) || []).length;
56313
+ if (braceCount === 0 && i > classStartLine) {
56314
+ classEndLine = i;
56315
+ break;
56316
+ }
56317
+ }
56318
+ if (classEndLine === -1) {
56319
+ return { fixedContent: originalContent, diff: "" };
56320
+ }
56321
+ let indent = " ";
56322
+ for (let i = classStartLine + 1; i < classEndLine; i++) {
56323
+ const indentLine = lines[i];
56324
+ if (!indentLine) continue;
56325
+ const indentMatch = indentLine.match(/^(\s+)\S/);
56326
+ if (indentMatch && indentMatch[1]) {
56327
+ indent = indentMatch[1];
56328
+ break;
56329
+ }
56330
+ }
56331
+ const destroyMethod = [
56332
+ "",
56333
+ `${indent}/**`,
56334
+ `${indent} * Clean up resources and remove all event listeners.`,
56335
+ `${indent} */`,
56336
+ `${indent}destroy(): void {`,
56337
+ `${indent} this.removeAllListeners();`,
56338
+ `${indent}}`
56339
+ ].join("\n");
56340
+ const newLines = [...lines];
56341
+ newLines.splice(classEndLine, 0, destroyMethod);
56342
+ const fixedContent = newLines.join("\n");
56343
+ const diff = `@@ -${classEndLine + 1},1 +${classEndLine + 1},8 @@
56344
+ +${destroyMethod}
56345
+ ${lines[classEndLine]}`;
56346
+ return { fixedContent, diff };
56347
+ }
56348
+ /**
56349
+ * Apply use DisposableEventEmitter fix
56350
+ */
56351
+ applyUseDisposableEventEmitterFix(finding, originalContent, lines) {
56352
+ let fixedContent = originalContent.replace(
56353
+ /extends\s+EventEmitter\b/g,
56354
+ "extends DisposableEventEmitter"
56355
+ );
56356
+ if (!originalContent.includes("DisposableEventEmitter")) {
56357
+ const importPattern = /^import\s+.*from\s+['"][^'"]+['"];?\s*$/m;
56358
+ const lastImportMatch = originalContent.match(new RegExp(importPattern.source + "(?!.*" + importPattern.source + ")", "s"));
56359
+ if (lastImportMatch) {
56360
+ const importStatement = `import { DisposableEventEmitter } from '@/shared/utils';`;
56361
+ const insertPos = lastImportMatch.index + lastImportMatch[0].length;
56362
+ fixedContent = fixedContent.slice(0, insertPos) + "\n" + importStatement + fixedContent.slice(insertPos);
56363
+ }
56364
+ }
56365
+ const diff = '--- EventEmitter\n+++ DisposableEventEmitter\n+ import { DisposableEventEmitter } from "@/shared/utils";';
56366
+ return { fixedContent, diff };
56367
+ }
56368
+ /**
56369
+ * Create backup of a file
56370
+ */
56371
+ async createBackup(filePath) {
56372
+ if (!existsSync(this.backupDir)) {
56373
+ await mkdir(this.backupDir, { recursive: true });
56374
+ }
56375
+ const backupName = `${basename(filePath)}.${Date.now()}.bak`;
56376
+ const backupPath = join(this.backupDir, backupName);
56377
+ await copyFile(filePath, backupPath);
56378
+ this.backups.set(filePath, backupPath);
56379
+ logger.debug("Backup created", { filePath, backupPath });
56380
+ return backupPath;
56381
+ }
56382
+ /**
56383
+ * Create a fix attempt result
56384
+ */
56385
+ createAttempt(id, bugId, attemptNumber, strategy, diff, status, startTime, error) {
56386
+ return {
56387
+ id,
56388
+ bugId,
56389
+ attemptNumber,
56390
+ strategy,
56391
+ diff,
56392
+ status,
56393
+ error,
56394
+ attemptedAt: (/* @__PURE__ */ new Date()).toISOString(),
56395
+ durationMs: Date.now() - startTime
56396
+ };
56397
+ }
56398
+ /**
56399
+ * Add a custom fix template
56400
+ */
56401
+ addTemplate(template) {
56402
+ this.templates.set(template.id, template);
56403
+ logger.debug("Fix template added", { templateId: template.id });
56404
+ }
56405
+ /**
56406
+ * Get all fix templates
56407
+ */
56408
+ getTemplates() {
56409
+ return Array.from(this.templates.values());
56410
+ }
56411
+ };
56412
+
56413
+ // src/core/bugfix/verification-gate.ts
56414
+ init_esm_shims();
56415
+ init_logger();
56416
+ var DEFAULT_OPTIONS4 = {
56417
+ typecheck: true,
56418
+ tests: true,
56419
+ checkNewErrors: true,
56420
+ checkCoverage: false,
56421
+ timeout: 12e4,
56422
+ // 2 minutes
56423
+ testCommand: "npm test",
56424
+ typecheckCommand: "npm run typecheck",
56425
+ cwd: process.cwd()
56426
+ };
56427
+ var VerificationGate = class {
56428
+ options;
56429
+ constructor(options) {
56430
+ this.options = { ...DEFAULT_OPTIONS4, ...options };
56431
+ logger.debug("VerificationGate initialized", {
56432
+ typecheck: this.options.typecheck,
56433
+ tests: this.options.tests,
56434
+ timeout: this.options.timeout
56435
+ });
56436
+ }
56437
+ /**
56438
+ * Verify a fix passes all gates
56439
+ *
56440
+ * @param finding - Bug finding that was fixed
56441
+ * @param affectedFiles - Files affected by the fix
56442
+ * @returns Verification result
56443
+ */
56444
+ async verify(finding, affectedFiles) {
56445
+ const startTime = Date.now();
56446
+ logger.info("Starting verification", {
56447
+ bugId: finding.id,
56448
+ file: finding.file,
56449
+ affectedFiles
56450
+ });
56451
+ const result = {
56452
+ success: true,
56453
+ typecheckPassed: true,
56454
+ testsPassed: true,
56455
+ noNewErrors: true,
56456
+ affectedTests: [],
56457
+ failedTests: [],
56458
+ newErrors: [],
56459
+ durationMs: 0
56460
+ };
56461
+ try {
56462
+ if (this.options.typecheck) {
56463
+ logger.debug("Running typecheck gate");
56464
+ const typecheckResult = await this.runTypecheck();
56465
+ result.typecheckPassed = typecheckResult.success;
56466
+ if (!typecheckResult.success) {
56467
+ result.success = false;
56468
+ result.newErrors = typecheckResult.errors;
56469
+ logger.warn("Typecheck failed", {
56470
+ bugId: finding.id,
56471
+ errors: typecheckResult.errors.slice(0, 5)
56472
+ });
56473
+ } else {
56474
+ logger.debug("Typecheck passed");
56475
+ }
56476
+ }
56477
+ if (this.options.tests && result.typecheckPassed) {
56478
+ logger.debug("Running test gate");
56479
+ const testResult = await this.runTests(affectedFiles);
56480
+ result.testsPassed = testResult.success;
56481
+ result.affectedTests = testResult.affectedTests;
56482
+ result.failedTests = testResult.failedTests;
56483
+ if (!testResult.success) {
56484
+ result.success = false;
56485
+ logger.warn("Tests failed", {
56486
+ bugId: finding.id,
56487
+ failed: testResult.failedTests.slice(0, 5)
56488
+ });
56489
+ } else {
56490
+ logger.debug("Tests passed", { count: testResult.affectedTests.length });
56491
+ }
56492
+ }
56493
+ if (this.options.checkNewErrors && result.typecheckPassed && result.testsPassed) {
56494
+ result.noNewErrors = true;
56495
+ }
56496
+ if (this.options.checkCoverage && result.success) {
56497
+ result.coverageMaintained = true;
56498
+ }
56499
+ } catch (error) {
56500
+ result.success = false;
56501
+ result.newErrors = [error.message];
56502
+ logger.error("Verification error", {
56503
+ bugId: finding.id,
56504
+ error: error.message
56505
+ });
56506
+ }
56507
+ result.durationMs = Date.now() - startTime;
56508
+ logger.info("Verification complete", {
56509
+ bugId: finding.id,
56510
+ success: result.success,
56511
+ typecheckPassed: result.typecheckPassed,
56512
+ testsPassed: result.testsPassed,
56513
+ durationMs: result.durationMs
56514
+ });
56515
+ return result;
56516
+ }
56517
+ /**
56518
+ * Run TypeScript typecheck
56519
+ */
56520
+ async runTypecheck() {
56521
+ return this.runCommand(this.options.typecheckCommand, "typecheck");
56522
+ }
56523
+ /**
56524
+ * Run tests for affected files
56525
+ */
56526
+ async runTests(affectedFiles) {
56527
+ const result = await this.runCommand(this.options.testCommand, "test");
56528
+ return {
56529
+ success: result.success,
56530
+ affectedTests: affectedFiles.map((f) => `${f} tests`),
56531
+ failedTests: result.success ? [] : result.errors
56532
+ };
56533
+ }
56534
+ /**
56535
+ * Run a shell command
56536
+ */
56537
+ async runCommand(command, name) {
56538
+ return new Promise((resolve13) => {
56539
+ const parts = command.split(" ");
56540
+ const cmd = parts[0];
56541
+ const args = parts.slice(1);
56542
+ const errors = [];
56543
+ let stderr = "";
56544
+ if (!cmd) {
56545
+ resolve13({ success: false, errors: ["Empty command"] });
56546
+ return;
56547
+ }
56548
+ logger.debug(`Running ${name}`, { command });
56549
+ const proc = spawn(cmd, args, {
56550
+ cwd: this.options.cwd,
56551
+ shell: true,
56552
+ stdio: ["ignore", "pipe", "pipe"]
56553
+ });
56554
+ const timeoutId = setTimeout(() => {
56555
+ proc.kill("SIGTERM");
56556
+ errors.push(`${name} timed out after ${this.options.timeout}ms`);
56557
+ }, this.options.timeout);
56558
+ if (timeoutId.unref) {
56559
+ timeoutId.unref();
56560
+ }
56561
+ proc.stderr?.on("data", (data) => {
56562
+ stderr += data.toString();
56563
+ });
56564
+ proc.on("close", (code) => {
56565
+ clearTimeout(timeoutId);
56566
+ if (code === 0) {
56567
+ resolve13({ success: true, errors: [] });
56568
+ } else {
56569
+ const errorLines = stderr.split("\n").filter((line) => line.includes("error") || line.includes("Error") || line.includes("FAIL")).slice(0, 10);
56570
+ resolve13({
56571
+ success: false,
56572
+ errors: errorLines.length > 0 ? errorLines : [`${name} failed with exit code ${code}`]
56573
+ });
56574
+ }
56575
+ });
56576
+ proc.on("error", (err) => {
56577
+ clearTimeout(timeoutId);
56578
+ resolve13({
56579
+ success: false,
56580
+ errors: [err.message]
56581
+ });
56582
+ });
56583
+ });
56584
+ }
56585
+ /**
56586
+ * Quick verification (typecheck only)
56587
+ */
56588
+ async quickVerify(finding) {
56589
+ const result = await this.runTypecheck();
56590
+ return result.success;
56591
+ }
56592
+ /**
56593
+ * Full verification (all gates)
56594
+ */
56595
+ async fullVerify(finding, affectedFiles) {
56596
+ return this.verify(finding, affectedFiles);
56597
+ }
56598
+ };
56599
+
56600
+ // src/core/bugfix/bugfix-controller.ts
56601
+ init_esm_shims();
56602
+ init_logger();
56603
+ var BugfixController = class {
56604
+ config;
56605
+ rootDir;
56606
+ state = "IDLE";
56607
+ sessionId;
56608
+ startTime = 0;
56609
+ totalTokens = 0;
56610
+ // Components
56611
+ detector;
56612
+ fixer;
56613
+ verifier;
56614
+ // Session data
56615
+ findings = [];
56616
+ attempts = [];
56617
+ currentBugIndex = 0;
56618
+ retryCount = /* @__PURE__ */ new Map();
56619
+ // Callbacks
56620
+ onProgress;
56621
+ onBugFound;
56622
+ onFixApplied;
56623
+ onVerification;
56624
+ constructor(options = {}) {
56625
+ this.config = createDefaultBugfixConfig(options.config);
56626
+ this.rootDir = options.rootDir || process.cwd();
56627
+ this.sessionId = randomUUID();
56628
+ this.detector = new BugDetector(this.config);
56629
+ this.fixer = new BugFixer();
56630
+ this.verifier = new VerificationGate({
56631
+ typecheck: this.config.requireTypecheck,
56632
+ tests: this.config.requireTests,
56633
+ cwd: this.rootDir
56634
+ });
56635
+ this.onProgress = options.onProgress;
56636
+ this.onBugFound = options.onBugFound;
56637
+ this.onFixApplied = options.onFixApplied;
56638
+ this.onVerification = options.onVerification;
56639
+ logger.debug("BugfixController initialized", {
56640
+ sessionId: this.sessionId,
56641
+ rootDir: this.rootDir,
56642
+ config: this.config
56643
+ });
56644
+ }
56645
+ /**
56646
+ * Execute autonomous bugfix workflow
56647
+ *
56648
+ * @returns Bugfix session result
56649
+ */
56650
+ async execute() {
56651
+ this.startTime = Date.now();
56652
+ this.state = "SCANNING";
56653
+ logger.info("Starting bugfix session", {
56654
+ sessionId: this.sessionId,
56655
+ rootDir: this.rootDir,
56656
+ maxBugs: this.config.maxBugs,
56657
+ dryRun: this.config.dryRun
56658
+ });
56659
+ this.emitProgress("Starting bug scan...");
56660
+ try {
56661
+ while (this.shouldContinue()) {
56662
+ const currentState = this.state;
56663
+ if (currentState === "IDLE") {
56664
+ this.state = "SCANNING";
56665
+ } else if (currentState === "SCANNING") {
56666
+ await this.handleScanning();
56667
+ } else if (currentState === "ANALYZING") {
56668
+ await this.handleAnalyzing();
56669
+ } else if (currentState === "PLANNING") {
56670
+ await this.handlePlanning();
56671
+ } else if (currentState === "FIXING") {
56672
+ await this.handleFixing();
56673
+ } else if (currentState === "VERIFYING") {
56674
+ await this.handleVerifying();
56675
+ } else if (currentState === "LEARNING") {
56676
+ await this.handleLearning();
56677
+ } else if (currentState === "ITERATING") {
56678
+ await this.handleIterating();
56679
+ } else if (currentState === "COMPLETE" || currentState === "FAILED") {
56680
+ break;
56681
+ }
56682
+ }
56683
+ return this.buildResult();
56684
+ } catch (error) {
56685
+ this.state = "FAILED";
56686
+ logger.error("Bugfix session failed", {
56687
+ sessionId: this.sessionId,
56688
+ error: error.message,
56689
+ state: this.state
56690
+ });
56691
+ return this.buildResult(error.message);
56692
+ }
56693
+ }
56694
+ /**
56695
+ * Check if execution should continue
56696
+ */
56697
+ shouldContinue() {
56698
+ if (this.state === "COMPLETE" || this.state === "FAILED") {
56699
+ return false;
56700
+ }
56701
+ const elapsedMinutes = (Date.now() - this.startTime) / 1e3 / 60;
56702
+ if (elapsedMinutes >= this.config.maxDurationMinutes) {
56703
+ logger.warn("Time limit exceeded", {
56704
+ elapsed: elapsedMinutes,
56705
+ limit: this.config.maxDurationMinutes
56706
+ });
56707
+ this.state = "COMPLETE";
56708
+ return false;
56709
+ }
56710
+ if (this.totalTokens >= this.config.maxTokens) {
56711
+ logger.warn("Token limit exceeded", {
56712
+ tokens: this.totalTokens,
56713
+ limit: this.config.maxTokens
56714
+ });
56715
+ this.state = "COMPLETE";
56716
+ return false;
56717
+ }
56718
+ const fixedCount = this.attempts.filter((a) => a.status === "verified").length;
56719
+ if (fixedCount >= this.config.maxBugs) {
56720
+ logger.info("Max bugs fixed", {
56721
+ fixed: fixedCount,
56722
+ limit: this.config.maxBugs
56723
+ });
56724
+ this.state = "COMPLETE";
56725
+ return false;
56726
+ }
56727
+ return true;
56728
+ }
56729
+ /**
56730
+ * Handle SCANNING state
56731
+ */
56732
+ async handleScanning() {
56733
+ this.emitProgress("Scanning for bugs...");
56734
+ this.findings = await this.detector.scan(this.rootDir);
56735
+ if (this.findings.length === 0) {
56736
+ this.emitProgress("No bugs found!");
56737
+ this.state = "COMPLETE";
56738
+ return;
56739
+ }
56740
+ this.emitProgress(`Found ${this.findings.length} bugs`);
56741
+ for (const finding of this.findings) {
56742
+ this.onBugFound?.(finding);
56743
+ }
56744
+ this.state = "ANALYZING";
56745
+ }
56746
+ /**
56747
+ * Handle ANALYZING state
56748
+ */
56749
+ async handleAnalyzing() {
56750
+ this.emitProgress("Analyzing bugs...");
56751
+ this.findings = this.findings.filter((f) => f.confidence >= this.config.minConfidence);
56752
+ if (this.findings.length === 0) {
56753
+ this.emitProgress("No bugs above confidence threshold");
56754
+ this.state = "COMPLETE";
56755
+ return;
56756
+ }
56757
+ this.emitProgress(`${this.findings.length} bugs to fix`);
56758
+ this.currentBugIndex = 0;
56759
+ this.state = "PLANNING";
56760
+ }
56761
+ /**
56762
+ * Handle PLANNING state
56763
+ */
56764
+ async handlePlanning() {
56765
+ const finding = this.findings[this.currentBugIndex];
56766
+ if (!finding) {
56767
+ this.state = "COMPLETE";
56768
+ return;
56769
+ }
56770
+ this.emitProgress(`Planning fix for bug ${this.currentBugIndex + 1}/${this.findings.length}`, {
56771
+ file: finding.file,
56772
+ type: finding.type,
56773
+ severity: finding.severity
56774
+ });
56775
+ if (!finding.fixStrategy) {
56776
+ logger.info("Bug requires manual review", {
56777
+ bugId: finding.id,
56778
+ type: finding.type
56779
+ });
56780
+ const skippedAttempt = {
56781
+ id: randomUUID(),
56782
+ bugId: finding.id,
56783
+ attemptNumber: 1,
56784
+ strategy: "manual_review",
56785
+ diff: "",
56786
+ status: "skipped",
56787
+ error: "No automatic fix available",
56788
+ attemptedAt: (/* @__PURE__ */ new Date()).toISOString(),
56789
+ durationMs: 0
56790
+ };
56791
+ this.attempts.push(skippedAttempt);
56792
+ this.currentBugIndex++;
56793
+ if (this.currentBugIndex >= this.findings.length) {
56794
+ this.state = "COMPLETE";
56795
+ }
56796
+ return;
56797
+ }
56798
+ this.state = "FIXING";
56799
+ }
56800
+ /**
56801
+ * Handle FIXING state
56802
+ */
56803
+ async handleFixing() {
56804
+ const finding = this.findings[this.currentBugIndex];
56805
+ if (!finding) {
56806
+ this.state = "COMPLETE";
56807
+ return;
56808
+ }
56809
+ this.emitProgress(`Fixing: ${finding.file}:${finding.lineStart}`, {
56810
+ type: finding.type,
56811
+ strategy: finding.fixStrategy
56812
+ });
56813
+ const attempt = await this.fixer.applyFix(finding, this.rootDir, this.config.dryRun);
56814
+ this.attempts.push(attempt);
56815
+ this.onFixApplied?.(finding, attempt);
56816
+ if (attempt.status === "applied") {
56817
+ this.state = "VERIFYING";
56818
+ } else if (attempt.status === "skipped") {
56819
+ this.emitProgress(`Skipped: ${attempt.error || "No changes needed"}`);
56820
+ this.currentBugIndex++;
56821
+ this.state = this.currentBugIndex >= this.findings.length ? "COMPLETE" : "PLANNING";
56822
+ } else {
56823
+ this.state = "ITERATING";
56824
+ }
56825
+ }
56826
+ /**
56827
+ * Handle VERIFYING state
56828
+ */
56829
+ async handleVerifying() {
56830
+ const finding = this.findings[this.currentBugIndex];
56831
+ const lastAttempt = this.attempts[this.attempts.length - 1];
56832
+ if (!finding || !lastAttempt) {
56833
+ this.state = "COMPLETE";
56834
+ return;
56835
+ }
56836
+ this.emitProgress(`Verifying fix for ${finding.file}...`);
56837
+ if (!this.config.dryRun) {
56838
+ const result = await this.verifier.verify(finding, [finding.file]);
56839
+ lastAttempt.verificationResult = result;
56840
+ this.onVerification?.(finding, result.success);
56841
+ if (result.success) {
56842
+ lastAttempt.status = "verified";
56843
+ this.emitProgress("Fix verified!", {
56844
+ typecheck: result.typecheckPassed,
56845
+ tests: result.testsPassed
56846
+ });
56847
+ this.state = "LEARNING";
56848
+ } else {
56849
+ lastAttempt.status = "failed";
56850
+ lastAttempt.error = result.newErrors.join("; ") || "Verification failed";
56851
+ this.emitProgress("Verification failed, rolling back...", {
56852
+ errors: result.newErrors
56853
+ });
56854
+ await this.fixer.rollback(finding.file);
56855
+ this.state = "ITERATING";
56856
+ }
56857
+ } else {
56858
+ lastAttempt.status = "verified";
56859
+ this.emitProgress("Dry run - skipping verification");
56860
+ this.state = "LEARNING";
56861
+ }
56862
+ }
56863
+ /**
56864
+ * Handle LEARNING state
56865
+ */
56866
+ async handleLearning() {
56867
+ const finding = this.findings[this.currentBugIndex];
56868
+ if (!finding) {
56869
+ this.state = "COMPLETE";
56870
+ return;
56871
+ }
56872
+ this.emitProgress("Storing pattern to knowledge base...");
56873
+ logger.info("Pattern learned", {
56874
+ bugId: finding.id,
56875
+ type: finding.type,
56876
+ file: finding.file
56877
+ });
56878
+ this.currentBugIndex++;
56879
+ if (this.currentBugIndex >= this.findings.length) {
56880
+ this.state = "COMPLETE";
56881
+ } else {
56882
+ this.state = "PLANNING";
56883
+ }
56884
+ }
56885
+ /**
56886
+ * Handle ITERATING state (retry logic)
56887
+ */
56888
+ async handleIterating() {
56889
+ const finding = this.findings[this.currentBugIndex];
56890
+ if (!finding) {
56891
+ this.state = "COMPLETE";
56892
+ return;
56893
+ }
56894
+ const currentRetries = this.retryCount.get(finding.id) || 0;
56895
+ if (currentRetries >= this.config.maxRetriesPerBug) {
56896
+ this.emitProgress(`Max retries reached for ${finding.file}`, {
56897
+ retries: currentRetries
56898
+ });
56899
+ this.currentBugIndex++;
56900
+ if (this.currentBugIndex >= this.findings.length) {
56901
+ this.state = "COMPLETE";
56902
+ } else {
56903
+ this.state = "PLANNING";
56904
+ }
56905
+ return;
56906
+ }
56907
+ this.retryCount.set(finding.id, currentRetries + 1);
56908
+ this.emitProgress(`Retrying fix (attempt ${currentRetries + 2})...`);
56909
+ this.state = "FIXING";
56910
+ }
56911
+ /**
56912
+ * Build final result
56913
+ */
56914
+ buildResult(error) {
56915
+ const stats = this.calculateStats();
56916
+ return {
56917
+ sessionId: this.sessionId,
56918
+ startedAt: new Date(this.startTime).toISOString(),
56919
+ endedAt: (/* @__PURE__ */ new Date()).toISOString(),
56920
+ config: this.config,
56921
+ findings: this.findings,
56922
+ attempts: this.attempts,
56923
+ stats,
56924
+ finalState: this.state,
56925
+ error
56926
+ };
56927
+ }
56928
+ /**
56929
+ * Calculate session statistics
56930
+ */
56931
+ calculateStats() {
56932
+ const verified = this.attempts.filter((a) => a.status === "verified").length;
56933
+ const failed = this.attempts.filter((a) => a.status === "failed").length;
56934
+ const skipped = this.attempts.filter((a) => a.status === "skipped").length;
56935
+ const bugsByType = {
56936
+ timer_leak: 0,
56937
+ missing_destroy: 0,
56938
+ promise_timeout_leak: 0,
56939
+ event_leak: 0,
56940
+ resource_leak: 0,
56941
+ race_condition: 0,
56942
+ memory_leak: 0,
56943
+ uncaught_promise: 0,
56944
+ deprecated_api: 0,
56945
+ security_issue: 0,
56946
+ type_error: 0,
56947
+ test_failure: 0,
56948
+ custom: 0
56949
+ };
56950
+ for (const finding of this.findings) {
56951
+ bugsByType[finding.type] = (bugsByType[finding.type] || 0) + 1;
56952
+ }
56953
+ const bugsBySeverity = {
56954
+ low: 0,
56955
+ medium: 0,
56956
+ high: 0,
56957
+ critical: 0
56958
+ };
56959
+ for (const finding of this.findings) {
56960
+ bugsBySeverity[finding.severity] = (bugsBySeverity[finding.severity] || 0) + 1;
56961
+ }
56962
+ let stopReason = "complete";
56963
+ const elapsedMinutes = (Date.now() - this.startTime) / 1e3 / 60;
56964
+ if (this.state === "FAILED") {
56965
+ stopReason = "error";
56966
+ } else if (verified >= this.config.maxBugs) {
56967
+ stopReason = "max_bugs";
56968
+ } else if (elapsedMinutes >= this.config.maxDurationMinutes) {
56969
+ stopReason = "max_time";
56970
+ } else if (this.totalTokens >= this.config.maxTokens) {
56971
+ stopReason = "max_tokens";
56972
+ }
56973
+ return {
56974
+ bugsFound: this.findings.length,
56975
+ bugsFixed: verified,
56976
+ bugsFailed: failed,
56977
+ bugsSkipped: skipped,
56978
+ totalAttempts: this.attempts.length,
56979
+ successRate: this.attempts.length > 0 ? verified / this.attempts.length : 0,
56980
+ totalDurationMs: Date.now() - this.startTime,
56981
+ totalTokens: this.totalTokens,
56982
+ patternsLearned: verified,
56983
+ // Each verified fix is a learned pattern
56984
+ regressions: 0,
56985
+ // Should always be 0 with verification
56986
+ stopReason,
56987
+ bugsByType,
56988
+ bugsBySeverity
56989
+ };
56990
+ }
56991
+ /**
56992
+ * Emit progress update
56993
+ */
56994
+ emitProgress(message, data) {
56995
+ logger.info(message, { sessionId: this.sessionId, ...data });
56996
+ this.onProgress?.(message, data);
56997
+ }
56998
+ /**
56999
+ * Get current state
57000
+ */
57001
+ getState() {
57002
+ return this.state;
57003
+ }
57004
+ /**
57005
+ * Get current statistics
57006
+ */
57007
+ getStats() {
57008
+ return this.calculateStats();
57009
+ }
57010
+ /**
57011
+ * Stop execution
57012
+ */
57013
+ async stop() {
57014
+ logger.info("Stopping bugfix session", { sessionId: this.sessionId });
57015
+ this.state = "COMPLETE";
57016
+ await this.fixer.cleanupBackups();
57017
+ }
57018
+ };
57019
+
57020
+ // src/cli/commands/bugfix.ts
57021
+ init_logger();
57022
+ init_path_resolver();
57023
+ function formatSeverity(severity) {
57024
+ switch (severity) {
57025
+ case "critical":
57026
+ return chalk5.bgRed.white(" CRITICAL ");
57027
+ case "high":
57028
+ return chalk5.red("[HIGH]");
57029
+ case "medium":
57030
+ return chalk5.yellow("[MED]");
57031
+ case "low":
57032
+ return chalk5.gray("[LOW]");
57033
+ default: {
57034
+ const s = severity;
57035
+ return `[${s.toUpperCase()}]`;
57036
+ }
57037
+ }
57038
+ }
57039
+ function formatBugType(type) {
57040
+ const typeNames = {
57041
+ timer_leak: "Timer leak",
57042
+ missing_destroy: "Missing destroy()",
57043
+ promise_timeout_leak: "Promise timeout leak",
57044
+ event_leak: "Event listener leak",
57045
+ resource_leak: "Resource leak",
57046
+ race_condition: "Race condition",
57047
+ memory_leak: "Memory leak",
57048
+ uncaught_promise: "Uncaught promise",
57049
+ deprecated_api: "Deprecated API",
57050
+ security_issue: "Security issue",
57051
+ type_error: "Type error",
57052
+ test_failure: "Test failure",
57053
+ custom: "Custom"
57054
+ };
57055
+ return typeNames[type] || type;
57056
+ }
57057
+ var bugfixCommand = {
57058
+ command: "bugfix",
57059
+ describe: "Find and fix bugs in the codebase",
57060
+ builder: {
57061
+ auto: {
57062
+ type: "boolean",
57063
+ default: false,
57064
+ describe: "Run autonomously without prompts"
57065
+ },
57066
+ scope: {
57067
+ type: "string",
57068
+ describe: "Limit scan to directory (e.g., src/core/)"
57069
+ },
57070
+ severity: {
57071
+ type: "string",
57072
+ choices: ["low", "medium", "high", "critical"],
57073
+ default: "medium",
57074
+ describe: "Minimum severity to fix"
57075
+ },
57076
+ "max-iterations": {
57077
+ type: "number",
57078
+ default: 10,
57079
+ describe: "Maximum bugs to fix per session"
57080
+ },
57081
+ "dry-run": {
57082
+ type: "boolean",
57083
+ default: false,
57084
+ describe: "Preview fixes without applying"
57085
+ },
57086
+ verbose: {
57087
+ type: "boolean",
57088
+ default: false,
57089
+ describe: "Verbose output"
57090
+ },
57091
+ quiet: {
57092
+ type: "boolean",
57093
+ default: false,
57094
+ describe: "Minimal output"
57095
+ },
57096
+ types: {
57097
+ type: "array",
57098
+ describe: "Bug types to scan for",
57099
+ choices: [
57100
+ "timer_leak",
57101
+ "missing_destroy",
57102
+ "promise_timeout_leak",
57103
+ "event_leak",
57104
+ "resource_leak"
57105
+ ]
57106
+ }
57107
+ },
57108
+ handler: async (argv) => {
57109
+ const spinner = ora8();
57110
+ const findings = [];
57111
+ const fixedBugs = [];
57112
+ const failedBugs = [];
57113
+ const skippedBugs = [];
57114
+ const rootDir = await detectProjectRoot() || process.cwd();
57115
+ const config = {
57116
+ maxBugs: argv.maxIterations || 10,
57117
+ severityThreshold: argv.severity || "medium",
57118
+ scope: argv.scope,
57119
+ dryRun: argv.dryRun || false,
57120
+ verbose: argv.verbose || false,
57121
+ bugTypes: argv.types || ["timer_leak", "missing_destroy", "promise_timeout_leak"]
57122
+ };
57123
+ if (!argv.quiet) {
57124
+ console.log(chalk5.cyan("\n\u{1F527} AutomatosX Bug Fixer\n"));
57125
+ if (argv.dryRun) {
57126
+ console.log(chalk5.yellow(" \u26A0\uFE0F Dry run mode - no changes will be made\n"));
57127
+ }
57128
+ if (argv.scope) {
57129
+ console.log(chalk5.gray(` Scope: ${argv.scope}`));
57130
+ }
57131
+ console.log(chalk5.gray(` Severity threshold: ${argv.severity}`));
57132
+ console.log(chalk5.gray(` Max bugs: ${argv.maxIterations}`));
57133
+ console.log();
57134
+ }
57135
+ try {
57136
+ const controller = new BugfixController({
57137
+ config,
57138
+ rootDir,
57139
+ onProgress: (message, data) => {
57140
+ if (argv.quiet) return;
57141
+ if (message.startsWith("Scanning")) {
57142
+ spinner.start(message);
57143
+ } else if (message.includes("Found")) {
57144
+ spinner.succeed(message);
57145
+ } else if (message.startsWith("Fixing")) {
57146
+ spinner.start(message);
57147
+ } else if (message.includes("verified") || message.includes("Verified")) {
57148
+ spinner.succeed(message);
57149
+ } else if (message.includes("failed") || message.includes("Failed")) {
57150
+ spinner.fail(message);
57151
+ } else if (message.includes("Skipped")) {
57152
+ spinner.info(message);
57153
+ } else if (argv.verbose) {
57154
+ spinner.info(message);
57155
+ }
57156
+ },
57157
+ onBugFound: (finding) => {
57158
+ findings.push(finding);
57159
+ },
57160
+ onFixApplied: (finding, attempt) => {
57161
+ if (attempt.status === "verified") {
57162
+ fixedBugs.push(finding);
57163
+ } else if (attempt.status === "failed") {
57164
+ failedBugs.push(finding);
57165
+ } else if (attempt.status === "skipped") {
57166
+ skippedBugs.push(finding);
57167
+ }
57168
+ },
57169
+ onVerification: (finding, success) => {
57170
+ if (!argv.quiet && argv.verbose) {
57171
+ const icon = success ? "\u2713" : "\u2717";
57172
+ console.log(chalk5.gray(` ${icon} Verification: ${success ? "passed" : "failed"}`));
57173
+ }
57174
+ }
57175
+ });
57176
+ const result = await controller.execute();
57177
+ if (!argv.quiet) {
57178
+ spinner.stop();
57179
+ console.log("\n" + chalk5.cyan("\u2501".repeat(50)));
57180
+ console.log(chalk5.cyan.bold(" Results"));
57181
+ console.log(chalk5.cyan("\u2501".repeat(50)) + "\n");
57182
+ if (findings.length > 0) {
57183
+ console.log(chalk5.bold("\u{1F4CB} Bugs found:\n"));
57184
+ for (const finding of findings.slice(0, 20)) {
57185
+ const severity = formatSeverity(finding.severity);
57186
+ const type = formatBugType(finding.type);
57187
+ console.log(` ${severity} ${type} in ${chalk5.cyan(finding.file)}:${finding.lineStart}`);
57188
+ }
57189
+ if (findings.length > 20) {
57190
+ console.log(chalk5.gray(` ... and ${findings.length - 20} more`));
57191
+ }
57192
+ console.log();
57193
+ }
57194
+ if (fixedBugs.length > 0) {
57195
+ console.log(chalk5.green.bold(`\u2705 Fixed: ${fixedBugs.length} bug(s)
57196
+ `));
57197
+ for (const bug of fixedBugs.slice(0, 10)) {
57198
+ console.log(chalk5.green(` \u2713 ${formatBugType(bug.type)} in ${bug.file}:${bug.lineStart}`));
57199
+ }
57200
+ if (fixedBugs.length > 10) {
57201
+ console.log(chalk5.gray(` ... and ${fixedBugs.length - 10} more`));
57202
+ }
57203
+ console.log();
57204
+ }
57205
+ if (failedBugs.length > 0) {
57206
+ console.log(chalk5.red.bold(`\u274C Failed: ${failedBugs.length} bug(s)
57207
+ `));
57208
+ for (const bug of failedBugs.slice(0, 5)) {
57209
+ console.log(chalk5.red(` \u2717 ${formatBugType(bug.type)} in ${bug.file}:${bug.lineStart}`));
57210
+ }
57211
+ console.log();
57212
+ }
57213
+ if (skippedBugs.length > 0) {
57214
+ console.log(chalk5.yellow.bold(`\u26A0\uFE0F Skipped (manual review needed): ${skippedBugs.length} bug(s)
57215
+ `));
57216
+ for (const bug of skippedBugs.slice(0, 5)) {
57217
+ console.log(chalk5.yellow(` \u2192 ${formatBugType(bug.type)} in ${bug.file}:${bug.lineStart}`));
57218
+ }
57219
+ console.log();
57220
+ }
57221
+ console.log(chalk5.cyan("\u2501".repeat(50)));
57222
+ console.log(chalk5.bold(" Summary"));
57223
+ console.log(chalk5.cyan("\u2501".repeat(50)));
57224
+ console.log();
57225
+ console.log(` Bugs found: ${result.stats.bugsFound}`);
57226
+ console.log(` Bugs fixed: ${chalk5.green(result.stats.bugsFixed.toString())}`);
57227
+ console.log(` Bugs failed: ${result.stats.bugsFailed > 0 ? chalk5.red(result.stats.bugsFailed.toString()) : "0"}`);
57228
+ console.log(` Bugs skipped: ${result.stats.bugsSkipped}`);
57229
+ console.log(` Success rate: ${(result.stats.successRate * 100).toFixed(1)}%`);
57230
+ console.log(` Duration: ${(result.stats.totalDurationMs / 1e3).toFixed(1)}s`);
57231
+ console.log(` Stop reason: ${result.stats.stopReason}`);
57232
+ console.log();
57233
+ if (argv.dryRun) {
57234
+ console.log(chalk5.yellow(" \u2139\uFE0F This was a dry run. Run without --dry-run to apply fixes.\n"));
57235
+ }
57236
+ } else {
57237
+ console.log(JSON.stringify({
57238
+ found: result.stats.bugsFound,
57239
+ fixed: result.stats.bugsFixed,
57240
+ failed: result.stats.bugsFailed,
57241
+ skipped: result.stats.bugsSkipped,
57242
+ successRate: result.stats.successRate,
57243
+ durationMs: result.stats.totalDurationMs
57244
+ }));
57245
+ }
57246
+ if (result.stats.bugsFailed > 0) {
57247
+ process.exitCode = 1;
57248
+ }
57249
+ } catch (error) {
57250
+ spinner.fail(chalk5.red(`Error: ${error.message}`));
57251
+ logger.error("Bugfix command failed", { error: error.message });
57252
+ process.exitCode = 1;
57253
+ }
57254
+ }
57255
+ };
57256
+
55577
57257
  // src/cli/index.ts
55578
57258
  installExitHandlers();
55579
57259
  var VERSION2 = getVersion();
@@ -55596,7 +57276,7 @@ globalTracker.mark("cli_start");
55596
57276
  type: "string",
55597
57277
  description: "Path to custom config file",
55598
57278
  global: true
55599
- }).command(setupCommand).command(initCommand).command(configureCommand).command(agentCommand).command(listCommand).command(runCommand).command(resumeCommand).command(runsCommand).command(sessionCommand).command(workspaceCommand).command(cacheCommand).command(configCommand).command(statusCommand4).command(doctorCommand2).command(cleanupCommand2).command(analyticsCommand).command(memoryCommand).command(mcpCommand).command(geminiCommand).command(providerLimitsCommand).command(providersCommand).command(flagsCommand).command(specCommand).command(genCommand).command(updateCommand).command(uninstallCommand).command(modeCommand).command(debugInstructionsCommand).command(planCommand).command(iterateCommand).command(reviewCommand).demandCommand(1, "You must provide a command. Run --help for usage.").help().version(VERSION2).alias("h", "help").alias("v", "version").strict().wrap(Math.min(120, yargs().terminalWidth())).parse();
57279
+ }).command(setupCommand).command(initCommand).command(configureCommand).command(agentCommand).command(listCommand).command(runCommand).command(resumeCommand).command(runsCommand).command(sessionCommand).command(workspaceCommand).command(cacheCommand).command(configCommand).command(statusCommand4).command(doctorCommand2).command(cleanupCommand2).command(analyticsCommand).command(memoryCommand).command(mcpCommand).command(geminiCommand).command(providerLimitsCommand).command(providersCommand).command(flagsCommand).command(specCommand).command(genCommand).command(updateCommand).command(uninstallCommand).command(modeCommand).command(debugInstructionsCommand).command(planCommand).command(iterateCommand).command(reviewCommand).command(bugfixCommand).demandCommand(1, "You must provide a command. Run --help for usage.").help().version(VERSION2).alias("h", "help").alias("v", "version").strict().wrap(Math.min(120, yargs().terminalWidth())).parse();
55600
57280
  globalTracker.mark("yargs_parse_end");
55601
57281
  globalTracker.measure("yargs_parsing", "yargs_parse_start", "yargs_parse_end");
55602
57282
  globalTracker.mark("options_setup_start");