agdi 3.4.5 → 4.0.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.
Files changed (3) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +2127 -491
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,8 +11,8 @@ import "./chunk-4VNS5WPM.js";
11
11
 
12
12
  // src/index.ts
13
13
  import { Command } from "commander";
14
- import chalk21 from "chalk";
15
- import ora7 from "ora";
14
+ import chalk27 from "chalk";
15
+ import ora12 from "ora";
16
16
 
17
17
  // src/core/llm/index.ts
18
18
  var PuterProvider = class {
@@ -2229,6 +2229,14 @@ var BaseAgent = class {
2229
2229
  ${context}`;
2230
2230
  }
2231
2231
  }
2232
+ if (this.context?.architectureRules && !prompt.includes("# Project Architecture Rules")) {
2233
+ fullPrompt += `
2234
+
2235
+ # Project Architecture Rules
2236
+ CRITICAL: You MUST strictly adhere to the following architectural rules when generating code. Violations will be rejected.
2237
+
2238
+ ${this.context.architectureRules}`;
2239
+ }
2232
2240
  const response = await Promise.race([
2233
2241
  this.llm.generate(fullPrompt, this.getSystemPrompt()),
2234
2242
  new Promise(
@@ -3483,6 +3491,8 @@ URL: ${deployResult.url || "See dashboard"}`,
3483
3491
  import * as fs7 from "fs/promises";
3484
3492
  import * as path7 from "path";
3485
3493
  import crypto2 from "crypto";
3494
+ import { exec as execCb } from "child_process";
3495
+ import { promisify as promisify3 } from "util";
3486
3496
 
3487
3497
  // src/context/repository-indexer.ts
3488
3498
  import fs6 from "fs/promises";
@@ -3527,80 +3537,80 @@ var TypeScriptParser = class extends CodeParser {
3527
3537
  const dependencies = /* @__PURE__ */ new Set();
3528
3538
  traverse(ast, {
3529
3539
  // Functions
3530
- FunctionDeclaration(path15) {
3531
- if (path15.node.id) {
3540
+ FunctionDeclaration(path20) {
3541
+ if (path20.node.id) {
3532
3542
  symbols.push({
3533
- name: path15.node.id.name,
3543
+ name: path20.node.id.name,
3534
3544
  type: "function",
3535
- line: path15.node.loc?.start.line || 0,
3536
- endLine: path15.node.loc?.end.line,
3537
- signature: generateSignature(path15.node),
3538
- isExported: isExported(path15),
3539
- docstring: extractDocstring(path15.node)
3545
+ line: path20.node.loc?.start.line || 0,
3546
+ endLine: path20.node.loc?.end.line,
3547
+ signature: generateSignature(path20.node),
3548
+ isExported: isExported(path20),
3549
+ docstring: extractDocstring(path20.node)
3540
3550
  });
3541
3551
  }
3542
3552
  },
3543
3553
  // Classes
3544
- ClassDeclaration(path15) {
3545
- if (path15.node.id) {
3554
+ ClassDeclaration(path20) {
3555
+ if (path20.node.id) {
3546
3556
  symbols.push({
3547
- name: path15.node.id.name,
3557
+ name: path20.node.id.name,
3548
3558
  type: "class",
3549
- line: path15.node.loc?.start.line || 0,
3550
- endLine: path15.node.loc?.end.line,
3551
- signature: `class ${path15.node.id.name}`,
3552
- isExported: isExported(path15),
3553
- docstring: extractDocstring(path15.node)
3559
+ line: path20.node.loc?.start.line || 0,
3560
+ endLine: path20.node.loc?.end.line,
3561
+ signature: `class ${path20.node.id.name}`,
3562
+ isExported: isExported(path20),
3563
+ docstring: extractDocstring(path20.node)
3554
3564
  });
3555
3565
  }
3556
3566
  },
3557
3567
  // Variables (const/let/var)
3558
- VariableDeclaration(path15) {
3559
- path15.node.declarations.forEach((decl) => {
3568
+ VariableDeclaration(path20) {
3569
+ path20.node.declarations.forEach((decl) => {
3560
3570
  if (t.isIdentifier(decl.id)) {
3561
3571
  symbols.push({
3562
3572
  name: decl.id.name,
3563
3573
  type: "variable",
3564
- line: path15.node.loc?.start.line || 0,
3565
- endLine: path15.node.loc?.end.line,
3566
- signature: `${path15.node.kind} ${decl.id.name}`,
3567
- isExported: isExported(path15),
3568
- docstring: extractDocstring(path15.node)
3574
+ line: path20.node.loc?.start.line || 0,
3575
+ endLine: path20.node.loc?.end.line,
3576
+ signature: `${path20.node.kind} ${decl.id.name}`,
3577
+ isExported: isExported(path20),
3578
+ docstring: extractDocstring(path20.node)
3569
3579
  });
3570
3580
  }
3571
3581
  });
3572
3582
  },
3573
3583
  // Interfaces
3574
- TSInterfaceDeclaration(path15) {
3584
+ TSInterfaceDeclaration(path20) {
3575
3585
  symbols.push({
3576
- name: path15.node.id.name,
3586
+ name: path20.node.id.name,
3577
3587
  type: "interface",
3578
- line: path15.node.loc?.start.line || 0,
3579
- endLine: path15.node.loc?.end.line,
3580
- signature: `interface ${path15.node.id.name}`,
3581
- isExported: isExported(path15),
3582
- docstring: extractDocstring(path15.node)
3588
+ line: path20.node.loc?.start.line || 0,
3589
+ endLine: path20.node.loc?.end.line,
3590
+ signature: `interface ${path20.node.id.name}`,
3591
+ isExported: isExported(path20),
3592
+ docstring: extractDocstring(path20.node)
3583
3593
  });
3584
3594
  },
3585
3595
  // Types
3586
- TSTypeAliasDeclaration(path15) {
3596
+ TSTypeAliasDeclaration(path20) {
3587
3597
  symbols.push({
3588
- name: path15.node.id.name,
3598
+ name: path20.node.id.name,
3589
3599
  type: "type",
3590
- line: path15.node.loc?.start.line || 0,
3591
- endLine: path15.node.loc?.end.line,
3592
- signature: `type ${path15.node.id.name}`,
3593
- isExported: isExported(path15),
3594
- docstring: extractDocstring(path15.node)
3600
+ line: path20.node.loc?.start.line || 0,
3601
+ endLine: path20.node.loc?.end.line,
3602
+ signature: `type ${path20.node.id.name}`,
3603
+ isExported: isExported(path20),
3604
+ docstring: extractDocstring(path20.node)
3595
3605
  });
3596
3606
  },
3597
3607
  // Imports
3598
- ImportDeclaration(path15) {
3599
- const source = path15.node.source.value;
3608
+ ImportDeclaration(path20) {
3609
+ const source = path20.node.source.value;
3600
3610
  dependencies.add(source);
3601
3611
  const names = [];
3602
3612
  let isDefault = false;
3603
- path15.node.specifiers.forEach((specifier) => {
3613
+ path20.node.specifiers.forEach((specifier) => {
3604
3614
  if (t.isImportDefaultSpecifier(specifier)) {
3605
3615
  isDefault = true;
3606
3616
  names.push(specifier.local.name);
@@ -3618,34 +3628,34 @@ var TypeScriptParser = class extends CodeParser {
3618
3628
  source,
3619
3629
  names,
3620
3630
  isDefault,
3621
- line: path15.node.loc?.start.line || 0
3631
+ line: path20.node.loc?.start.line || 0
3622
3632
  });
3623
3633
  },
3624
3634
  // Exports
3625
- ExportNamedDeclaration(path15) {
3626
- if (path15.node.declaration) {
3635
+ ExportNamedDeclaration(path20) {
3636
+ if (path20.node.declaration) {
3627
3637
  } else {
3628
- path15.node.specifiers.forEach((specifier) => {
3638
+ path20.node.specifiers.forEach((specifier) => {
3629
3639
  exports.push({
3630
3640
  name: specifier.exported.name,
3631
3641
  type: "named",
3632
- line: path15.node.loc?.start.line || 0
3642
+ line: path20.node.loc?.start.line || 0
3633
3643
  });
3634
3644
  });
3635
3645
  }
3636
- if (path15.node.source) {
3637
- dependencies.add(path15.node.source.value);
3646
+ if (path20.node.source) {
3647
+ dependencies.add(path20.node.source.value);
3638
3648
  }
3639
3649
  },
3640
- ExportDefaultDeclaration(path15) {
3650
+ ExportDefaultDeclaration(path20) {
3641
3651
  let name = "default";
3642
- if (path15.node.declaration && path15.node.declaration.id) {
3643
- name = path15.node.declaration.id.name;
3652
+ if (path20.node.declaration && path20.node.declaration.id) {
3653
+ name = path20.node.declaration.id.name;
3644
3654
  }
3645
3655
  exports.push({
3646
3656
  name,
3647
3657
  type: "default",
3648
- line: path15.node.loc?.start.line || 0
3658
+ line: path20.node.loc?.start.line || 0
3649
3659
  });
3650
3660
  }
3651
3661
  });
@@ -3682,8 +3692,8 @@ var TypeScriptParser = class extends CodeParser {
3682
3692
  return ["ts", "tsx", "js", "jsx", "mjs", "cjs"];
3683
3693
  }
3684
3694
  };
3685
- function isExported(path15) {
3686
- return t.isExportDeclaration(path15.parent) || t.isExportNamedDeclaration(path15.parent) || t.isExportDefaultDeclaration(path15.parent);
3695
+ function isExported(path20) {
3696
+ return t.isExportDeclaration(path20.parent) || t.isExportNamedDeclaration(path20.parent) || t.isExportDefaultDeclaration(path20.parent);
3687
3697
  }
3688
3698
  function extractDocstring(node) {
3689
3699
  if (node.leadingComments && node.leadingComments.length > 0) {
@@ -3784,24 +3794,24 @@ var DependencyGraph = class {
3784
3794
  const cycles = [];
3785
3795
  const visited = /* @__PURE__ */ new Set();
3786
3796
  const recursionStack = /* @__PURE__ */ new Set();
3787
- const path15 = [];
3797
+ const path20 = [];
3788
3798
  const dfs = (file) => {
3789
3799
  visited.add(file);
3790
3800
  recursionStack.add(file);
3791
- path15.push(file);
3801
+ path20.push(file);
3792
3802
  const node = this.nodes.get(file);
3793
3803
  if (node) {
3794
3804
  for (const dep of node.imports) {
3795
3805
  if (!visited.has(dep)) {
3796
3806
  dfs(dep);
3797
3807
  } else if (recursionStack.has(dep)) {
3798
- const cycleStart = path15.indexOf(dep);
3799
- const cycle = path15.slice(cycleStart);
3808
+ const cycleStart = path20.indexOf(dep);
3809
+ const cycle = path20.slice(cycleStart);
3800
3810
  cycles.push([...cycle, dep]);
3801
3811
  }
3802
3812
  }
3803
3813
  }
3804
- path15.pop();
3814
+ path20.pop();
3805
3815
  recursionStack.delete(file);
3806
3816
  };
3807
3817
  for (const file of this.nodes.keys()) {
@@ -3845,8 +3855,8 @@ var DependencyGraph = class {
3845
3855
  /**
3846
3856
  * Normalize path (remove .., etc.)
3847
3857
  */
3848
- normalizePath(path15) {
3849
- const parts = path15.split("/");
3858
+ normalizePath(path20) {
3859
+ const parts = path20.split("/");
3850
3860
  const result = [];
3851
3861
  for (const part of parts) {
3852
3862
  if (part === "..") {
@@ -4057,22 +4067,22 @@ var VectorStore = class {
4057
4067
  /**
4058
4068
  * Get stored hash for a file
4059
4069
  */
4060
- async getFileHash(path15) {
4070
+ async getFileHash(path20) {
4061
4071
  await this.initialize();
4062
4072
  if (!this.db) return null;
4063
- const row = this.db.prepare("SELECT hash FROM indexed_files WHERE path = ?").get(path15);
4073
+ const row = this.db.prepare("SELECT hash FROM indexed_files WHERE path = ?").get(path20);
4064
4074
  return row ? row.hash : null;
4065
4075
  }
4066
4076
  /**
4067
4077
  * Set/Update hash for a file
4068
4078
  */
4069
- async setFileHash(path15, hash) {
4079
+ async setFileHash(path20, hash) {
4070
4080
  await this.initialize();
4071
4081
  if (!this.db) return;
4072
4082
  this.db.prepare(`
4073
4083
  INSERT OR REPLACE INTO indexed_files (path, hash, last_indexed)
4074
4084
  VALUES (?, ?, ?)
4075
- `).run(path15, hash, Date.now());
4085
+ `).run(path20, hash, Date.now());
4076
4086
  }
4077
4087
  /**
4078
4088
  * Add embeddings to store
@@ -4363,8 +4373,8 @@ var RepositoryIndexer = class {
4363
4373
  for (const file of files) {
4364
4374
  try {
4365
4375
  const content = await fs6.readFile(file, "utf-8");
4366
- const crypto3 = await import("crypto");
4367
- const hash = crypto3.createHash("md5").update(content).digest("hex");
4376
+ const crypto4 = await import("crypto");
4377
+ const hash = crypto4.createHash("md5").update(content).digest("hex");
4368
4378
  const existingHash = await this.store.getFileHash(file);
4369
4379
  if (existingHash === hash) {
4370
4380
  skipped++;
@@ -4498,6 +4508,7 @@ ${symbol.docstring || ""}`);
4498
4508
  };
4499
4509
 
4500
4510
  // src/agents/core/squad-orchestrator.ts
4511
+ import * as readline from "readline";
4501
4512
  var SquadOrchestrator = class {
4502
4513
  manager;
4503
4514
  frontend;
@@ -4524,6 +4535,7 @@ var SquadOrchestrator = class {
4524
4535
  maxRetries: 3,
4525
4536
  parallel: true,
4526
4537
  autoDeploy: false,
4538
+ breakpoints: false,
4527
4539
  ...config
4528
4540
  };
4529
4541
  const agentOptions = { verbose: this.config.verbose };
@@ -4546,135 +4558,143 @@ var SquadOrchestrator = class {
4546
4558
  this.log("\u{1F680} Agdi Squad Activated!", "header");
4547
4559
  this.log(`Goal: "${userPrompt}"`, "info");
4548
4560
  try {
4549
- this.log("\u{1F4CB} Phase 1: Planning", "phase");
4550
- const memoryContext = await this.retrieveRepositoryContext(userPrompt);
4551
- const userPromptWithMemory = memoryContext ? `${userPrompt}
4561
+ const didStash = await this.stashUserChanges();
4562
+ try {
4563
+ this.log("\u{1F4CB} Phase 1: Planning", "phase");
4564
+ const memoryContext = await this.retrieveRepositoryContext(userPrompt);
4565
+ const userPromptWithMemory = memoryContext ? `${userPrompt}
4552
4566
 
4553
4567
  ${memoryContext}` : userPrompt;
4554
- const projectSpec = await this.manager.analyzeRequest(userPromptWithMemory);
4555
- this.tasks = this.manager.generateTasks(projectSpec);
4556
- this.context = {
4557
- projectSpec,
4558
- workspaceRoot: this.config.workspaceRoot,
4559
- tasks: this.tasks,
4560
- sharedMemory: /* @__PURE__ */ new Map([["repo_context", memoryContext]])
4561
- };
4562
- this.manager.setContext(this.context);
4563
- this.frontend.setContext(this.context);
4564
- this.backend.setContext(this.context);
4565
- this.qa.setContext(this.context);
4566
- this.devops.setContext(this.context);
4567
- this.log(`Created ${this.tasks.length} tasks`, "success");
4568
- this.log("\u26A1 Phase 2: Execution & Auto-Repair", "phase");
4569
- let attempts = 0;
4570
- let healthy = false;
4571
- while (attempts < this.config.maxRetries && !healthy) {
4572
- attempts++;
4573
- this.log(`
4568
+ const projectSpec = await this.manager.analyzeRequest(userPromptWithMemory);
4569
+ this.tasks = this.manager.generateTasks(projectSpec);
4570
+ this.context = {
4571
+ projectSpec,
4572
+ workspaceRoot: this.config.workspaceRoot,
4573
+ tasks: this.tasks,
4574
+ sharedMemory: /* @__PURE__ */ new Map([["repo_context", memoryContext]]),
4575
+ architectureRules: await this.readArchitectureRules()
4576
+ };
4577
+ this.manager.setContext(this.context);
4578
+ this.frontend.setContext(this.context);
4579
+ this.backend.setContext(this.context);
4580
+ this.qa.setContext(this.context);
4581
+ this.devops.setContext(this.context);
4582
+ this.log(`Created ${this.tasks.length} tasks`, "success");
4583
+ this.log("\u26A1 Phase 2: Execution & Auto-Repair", "phase");
4584
+ let attempts = 0;
4585
+ let healthy = false;
4586
+ while (attempts < this.config.maxRetries && !healthy) {
4587
+ attempts++;
4588
+ this.log(`
4574
4589
  \u{1F504} Cycle ${attempts}/${this.config.maxRetries}`, "phase");
4575
- await this.executeTasks(filesCreated, filesModified, tasksSummary, errors);
4576
- this.log("\u{1F575}\uFE0F QA Check", "task");
4577
- let qaTask = this.tasks.find((t2) => t2.assignee === "qa" && !this.completedTasks.has(t2.id));
4578
- if (!qaTask) {
4579
- qaTask = {
4580
- id: `qa-${attempts}`,
4581
- title: `Quality Assurance Cycle ${attempts}`,
4582
- description: "Review codebase for errors, missing features, and type safety.",
4583
- assignee: "qa",
4584
- dependencies: [],
4585
- priority: "high",
4586
- state: "pending",
4587
- createdAt: Date.now(),
4588
- retryCount: 0
4589
- };
4590
- }
4591
- const qaResult = await this.executeTask(qaTask);
4592
- tasksSummary.push({
4593
- id: qaTask.id,
4594
- title: qaTask.title,
4595
- assignee: qaTask.assignee,
4596
- status: qaResult.success ? "completed" : "failed",
4597
- duration: 0
4598
- // Placeholder
4599
- });
4600
- if (qaResult.files) {
4601
- await this.writeFiles(qaResult.files, filesCreated, filesModified);
4602
- }
4603
- if (qaResult.success) {
4604
- this.log("\u2728 QA Passed - Codebase is healthy", "success");
4605
- healthy = true;
4606
- } else {
4607
- this.log(`\u26A0\uFE0F QA Found Issues: ${qaResult.errors?.length || 0} errors`, "warn");
4608
- if (qaResult.errors) {
4609
- qaResult.errors.forEach((e) => this.log(` - ${e}`, "error"));
4610
- }
4611
- if (attempts < this.config.maxRetries) {
4612
- this.log("\u{1F6E0}\uFE0F Generating Fix Plan...", "info");
4613
- const fixPrompt = `QA found these issues:
4614
- ${qaResult.errors?.join("\n")}
4615
-
4616
- Create specific tasks to fix these errors.`;
4617
- const fixTask = {
4618
- id: `fix-${attempts}`,
4619
- title: `Fix Issues from Cycle ${attempts}`,
4620
- description: `Fix the following errors:
4621
- ${qaResult.errors?.join("\n")}`,
4622
- assignee: "frontend",
4623
- // Defaulting to frontend for now, ideal would be to detect
4590
+ await this.executeTasks(filesCreated, filesModified, tasksSummary, errors);
4591
+ this.log("\u{1F575}\uFE0F QA Check", "task");
4592
+ let qaTask = this.tasks.find((t2) => t2.assignee === "qa" && !this.completedTasks.has(t2.id));
4593
+ if (!qaTask) {
4594
+ qaTask = {
4595
+ id: `qa-${attempts}`,
4596
+ title: `Quality Assurance Cycle ${attempts}`,
4597
+ description: "Review codebase for errors, missing features, and type safety.",
4598
+ assignee: "qa",
4624
4599
  dependencies: [],
4625
- priority: "critical",
4600
+ priority: "high",
4626
4601
  state: "pending",
4627
4602
  createdAt: Date.now(),
4628
4603
  retryCount: 0
4629
4604
  };
4630
- this.tasks.push(fixTask);
4631
- this.log(` + Added repair task: ${fixTask.title}`, "info");
4632
- }
4633
- }
4634
- }
4635
- if (!healthy) {
4636
- this.log("\u26A0\uFE0F Max retries reached. Proceeding with best-effort.", "warn");
4637
- } else {
4638
- this.log("\u2705 Codebase is stable.", "success");
4639
- }
4640
- let deploymentUrl;
4641
- if (healthy && this.config.autoDeploy) {
4642
- this.log("\u{1F680} Phase 4: Deployment", "phase");
4643
- const devopsTask = this.tasks.find((t2) => t2.assignee === "devops");
4644
- if (devopsTask) {
4645
- const deployResult = await this.executeTask(devopsTask);
4646
- if (deployResult.success) {
4647
- const urlMatch = deployResult.content.match(/https:\/\/[^\s]+/);
4648
- deploymentUrl = urlMatch ? urlMatch[0] : void 0;
4649
4605
  }
4606
+ const qaResult = await this.executeTask(qaTask);
4650
4607
  tasksSummary.push({
4651
- id: devopsTask.id,
4652
- title: devopsTask.title,
4653
- assignee: devopsTask.assignee,
4654
- status: deployResult.success ? "completed" : "failed",
4655
- duration: Date.now() - startTime
4608
+ id: qaTask.id,
4609
+ title: qaTask.title,
4610
+ assignee: qaTask.assignee,
4611
+ status: qaResult.success ? "completed" : "failed",
4612
+ duration: 0
4613
+ // Placeholder
4656
4614
  });
4615
+ if (qaResult.files) {
4616
+ await this.writeFiles(qaResult.files, filesCreated, filesModified);
4617
+ }
4618
+ if (qaResult.success) {
4619
+ this.log("\u2728 QA Passed - Codebase is healthy", "success");
4620
+ healthy = true;
4621
+ } else {
4622
+ this.log(`\u26A0\uFE0F QA Found Issues: ${qaResult.errors?.length || 0} errors`, "warn");
4623
+ if (qaResult.errors) {
4624
+ qaResult.errors.forEach((e) => this.log(` - ${e}`, "error"));
4625
+ }
4626
+ if (attempts < this.config.maxRetries) {
4627
+ this.log("\u{1F6E0}\uFE0F Generating Fix Plan...", "info");
4628
+ const fixPrompt = `QA found these issues:
4629
+ ${qaResult.errors?.join("\n")}
4630
+
4631
+ Create specific tasks to fix these errors.`;
4632
+ const fixTask = {
4633
+ id: `fix-${attempts}`,
4634
+ title: `Fix Issues from Cycle ${attempts}`,
4635
+ description: `Fix the following errors:
4636
+ ${qaResult.errors?.join("\n")}`,
4637
+ assignee: "frontend",
4638
+ // Defaulting to frontend for now, ideal would be to detect
4639
+ dependencies: [],
4640
+ priority: "critical",
4641
+ state: "pending",
4642
+ createdAt: Date.now(),
4643
+ retryCount: 0
4644
+ };
4645
+ this.tasks.push(fixTask);
4646
+ this.log(` + Added repair task: ${fixTask.title}`, "info");
4647
+ }
4648
+ }
4649
+ }
4650
+ if (!healthy) {
4651
+ this.log("\u26A0\uFE0F Max retries reached. Proceeding with best-effort.", "warn");
4652
+ } else {
4653
+ this.log("\u2705 Codebase is stable.", "success");
4654
+ }
4655
+ let deploymentUrl;
4656
+ if (healthy && this.config.autoDeploy) {
4657
+ this.log("\u{1F680} Phase 4: Deployment", "phase");
4658
+ const devopsTask = this.tasks.find((t2) => t2.assignee === "devops");
4659
+ if (devopsTask) {
4660
+ const deployResult = await this.executeTask(devopsTask);
4661
+ if (deployResult.success) {
4662
+ const urlMatch = deployResult.content.match(/https:\/\/[^\s]+/);
4663
+ deploymentUrl = urlMatch ? urlMatch[0] : void 0;
4664
+ }
4665
+ tasksSummary.push({
4666
+ id: devopsTask.id,
4667
+ title: devopsTask.title,
4668
+ assignee: devopsTask.assignee,
4669
+ status: deployResult.success ? "completed" : "failed",
4670
+ duration: Date.now() - startTime
4671
+ });
4672
+ }
4673
+ }
4674
+ const duration = Date.now() - startTime;
4675
+ this.log("\u{1F389} Squad Mission Complete!", "header");
4676
+ this.log(`Duration: ${(duration / 1e3).toFixed(1)}s`, "info");
4677
+ this.log(`Files created: ${filesCreated.length}`, "info");
4678
+ if (deploymentUrl) {
4679
+ this.log(`Live URL: ${deploymentUrl}`, "success");
4680
+ }
4681
+ const result = {
4682
+ success: errors.length === 0,
4683
+ projectSpec,
4684
+ filesCreated,
4685
+ filesModified,
4686
+ deploymentUrl,
4687
+ duration,
4688
+ tasksSummary,
4689
+ errors
4690
+ };
4691
+ await this.finalizeRun(result);
4692
+ return result;
4693
+ } finally {
4694
+ if (didStash) {
4695
+ await this.popUserStash();
4657
4696
  }
4658
4697
  }
4659
- const duration = Date.now() - startTime;
4660
- this.log("\u{1F389} Squad Mission Complete!", "header");
4661
- this.log(`Duration: ${(duration / 1e3).toFixed(1)}s`, "info");
4662
- this.log(`Files created: ${filesCreated.length}`, "info");
4663
- if (deploymentUrl) {
4664
- this.log(`Live URL: ${deploymentUrl}`, "success");
4665
- }
4666
- const result = {
4667
- success: errors.length === 0,
4668
- projectSpec,
4669
- filesCreated,
4670
- filesModified,
4671
- deploymentUrl,
4672
- duration,
4673
- tasksSummary,
4674
- errors
4675
- };
4676
- await this.finalizeRun(result);
4677
- return result;
4678
4698
  } catch (error) {
4679
4699
  const errorMsg = error instanceof Error ? error.message : String(error);
4680
4700
  this.log(`Mission failed: ${errorMsg}`, "error");
@@ -4714,7 +4734,10 @@ ${qaResult.errors?.join("\n")}`,
4714
4734
  const result = await this.executeTask(task);
4715
4735
  if (result.files) {
4716
4736
  await this.writeFiles(result.files, filesCreated, filesModified);
4737
+ const writtenPaths = result.files.map((f) => f.path);
4738
+ await this.commitTaskChanges(task, writtenPaths);
4717
4739
  }
4740
+ await this.waitForBreakpoint(task, result);
4718
4741
  this.completedTasks.set(task.id, result);
4719
4742
  tasksSummary.push({
4720
4743
  id: task.id,
@@ -5110,6 +5133,221 @@ ${qaResult.errors?.join("\n")}`,
5110
5133
  });
5111
5134
  return diffPayload;
5112
5135
  }
5136
+ // ==================== ARCHITECTURE ENFORCEMENT ====================
5137
+ /**
5138
+ * Read project architecture rules from ARCHITECTURE.md or .agdi/architecture.md.
5139
+ * Returns the file contents if found, or undefined if no architecture file exists.
5140
+ */
5141
+ async readArchitectureRules() {
5142
+ const candidates = [
5143
+ path7.join(this.config.workspaceRoot, "ARCHITECTURE.md"),
5144
+ path7.join(this.config.workspaceRoot, ".agdi", "architecture.md")
5145
+ ];
5146
+ for (const candidate of candidates) {
5147
+ try {
5148
+ const content = await fs7.readFile(candidate, "utf-8");
5149
+ if (content.trim().length > 0) {
5150
+ this.log(`\u{1F4D0} Loaded architecture rules from ${path7.relative(this.config.workspaceRoot, candidate)}`, "success");
5151
+ await this.appendTrace("architecture_rules_loaded", {
5152
+ source: path7.relative(this.config.workspaceRoot, candidate),
5153
+ length: content.length
5154
+ });
5155
+ return content;
5156
+ }
5157
+ } catch {
5158
+ }
5159
+ }
5160
+ this.log("No ARCHITECTURE.md found \u2014 agents will use default patterns.", "info");
5161
+ return void 0;
5162
+ }
5163
+ // ==================== GIT STASH SAFETY ====================
5164
+ /**
5165
+ * Stash any uncommitted user changes before squad execution.
5166
+ * This prevents the micro-commit feature from accidentally
5167
+ * including user's staged/unstaged work in agent commits.
5168
+ * Returns true if a stash was created.
5169
+ */
5170
+ async stashUserChanges() {
5171
+ const execAsync7 = promisify3(execCb);
5172
+ try {
5173
+ const { stdout: status } = await execAsync7("git status --porcelain", {
5174
+ cwd: this.config.workspaceRoot
5175
+ });
5176
+ if (!status.trim()) {
5177
+ return false;
5178
+ }
5179
+ const stashMsg = `agdi-squad-${this.runId}`;
5180
+ await execAsync7(`git stash push --include-untracked -m "${stashMsg}"`, {
5181
+ cwd: this.config.workspaceRoot
5182
+ });
5183
+ this.log(`\u{1F4E6} Stashed ${status.trim().split("\\n").length} uncommitted change(s) for safety`, "info");
5184
+ await this.appendTrace("stash_created", { message: stashMsg, changes: status.trim().split("\\n").length });
5185
+ return true;
5186
+ } catch {
5187
+ return false;
5188
+ }
5189
+ }
5190
+ /**
5191
+ * Pop the user's stashed changes after squad execution completes.
5192
+ */
5193
+ async popUserStash() {
5194
+ const execAsync7 = promisify3(execCb);
5195
+ try {
5196
+ const stashMsg = `agdi-squad-${this.runId}`;
5197
+ const { stdout: stashList } = await execAsync7("git stash list", {
5198
+ cwd: this.config.workspaceRoot
5199
+ });
5200
+ const lines = stashList.trim().split("\\n");
5201
+ const ourStash = lines.findIndex((l) => l.includes(stashMsg));
5202
+ if (ourStash >= 0) {
5203
+ await execAsync7(`git stash pop stash@{${ourStash}}`, {
5204
+ cwd: this.config.workspaceRoot
5205
+ });
5206
+ this.log("\u{1F4E6} Restored your stashed changes.", "success");
5207
+ await this.appendTrace("stash_restored", { stashIndex: ourStash });
5208
+ }
5209
+ } catch {
5210
+ this.log("\u26A0 Could not restore stashed changes. Run `git stash pop` manually.", "warn");
5211
+ }
5212
+ }
5213
+ // ==================== REVERSIBLE MICRO-COMMITS ====================
5214
+ /**
5215
+ * Atomically commit files generated by a single task.
5216
+ * This creates a granular git history that allows developers to
5217
+ * easily `git revert` any specific AI agent misstep.
5218
+ *
5219
+ * Failures are non-fatal — if git is unavailable or the workspace
5220
+ * is not a repository, the build continues normally.
5221
+ */
5222
+ async commitTaskChanges(task, filePaths) {
5223
+ if (filePaths.length === 0) return;
5224
+ const execAsync7 = promisify3(execCb);
5225
+ const cwd = this.config.workspaceRoot;
5226
+ try {
5227
+ await execAsync7("git rev-parse --is-inside-work-tree", { cwd });
5228
+ const relativePaths = filePaths.map((fp) => {
5229
+ const abs = path7.resolve(cwd, fp);
5230
+ return path7.relative(cwd, abs);
5231
+ });
5232
+ const BATCH_SIZE = 50;
5233
+ for (let i = 0; i < relativePaths.length; i += BATCH_SIZE) {
5234
+ const batch = relativePaths.slice(i, i + BATCH_SIZE);
5235
+ const escaped = batch.map((p) => `"${p.replace(/"/g, '\\"')}"`).join(" ");
5236
+ await execAsync7(`git add ${escaped}`, { cwd });
5237
+ }
5238
+ const { stdout: staged } = await execAsync7("git diff --cached --name-only", { cwd });
5239
+ if (!staged.trim()) {
5240
+ this.log("No staged changes \u2014 skipping micro-commit", "info");
5241
+ return;
5242
+ }
5243
+ const scope = task.assignee;
5244
+ const subject = task.title.length > 72 ? task.title.substring(0, 69) + "..." : task.title;
5245
+ const commitMsg = `feat(${scope}): ${subject}`;
5246
+ const commitBody = [
5247
+ "Auto-generated by Agdi Squad (Reversible Micro-Commit)",
5248
+ "",
5249
+ `Task ID : ${task.id}`,
5250
+ `Agent : ${task.assignee}`,
5251
+ `Priority: ${task.priority}`,
5252
+ `Run ID : ${this.runId}`
5253
+ ].join("\n");
5254
+ await execAsync7(
5255
+ `git commit -m "${commitMsg.replace(/"/g, '\\"')}" -m "${commitBody.replace(/"/g, '\\"')}"`,
5256
+ { cwd }
5257
+ );
5258
+ this.log(`\u{1F4CC} Micro-commit: ${commitMsg}`, "success");
5259
+ await this.appendTrace("micro_commit", {
5260
+ taskId: task.id,
5261
+ message: commitMsg,
5262
+ files: relativePaths
5263
+ });
5264
+ } catch (err) {
5265
+ const msg = err instanceof Error ? err.message : String(err);
5266
+ this.log(`Micro-commit skipped: ${msg.split("\n")[0]}`, "warn");
5267
+ }
5268
+ }
5269
+ // ==================== AI BREAKPOINTS ====================
5270
+ /**
5271
+ * Pause execution after a task completes so the developer can
5272
+ * review generated files before the next agent takes over.
5273
+ *
5274
+ * Actions:
5275
+ * Enter → continue to the next task
5276
+ * 'skip' → disable breakpoints for the remainder of the run
5277
+ * 'abort' → halt the entire squad pipeline immediately
5278
+ */
5279
+ async waitForBreakpoint(task, result) {
5280
+ if (!this.config.breakpoints) return;
5281
+ const { emitAgentEvent: emitAgentEvent2 } = await import("./event-bus-MO5SFUME.js");
5282
+ emitAgentEvent2({
5283
+ type: "breakpoint",
5284
+ agentName: task.assignee,
5285
+ role: task.assignee,
5286
+ message: `Breakpoint hit after task: ${task.title}`,
5287
+ metadata: {
5288
+ taskId: task.id,
5289
+ files: result.files?.map((f) => f.path) ?? [],
5290
+ success: result.success
5291
+ }
5292
+ });
5293
+ console.log("");
5294
+ console.log(chalk11.cyan.bold(`\u23F8 AI Breakpoint \u2014 Task Complete`));
5295
+ console.log(chalk11.gray(` Agent : ${task.assignee}`));
5296
+ console.log(chalk11.gray(` Task : ${task.title}`));
5297
+ console.log(chalk11.gray(` Status : ${result.success ? chalk11.green("\u2713 success") : chalk11.red("\u2717 failed")}`));
5298
+ if (result.files && result.files.length > 0) {
5299
+ console.log(chalk11.gray(` Files (${result.files.length}):`));
5300
+ result.files.slice(0, 8).forEach((f) => {
5301
+ console.log(chalk11.gray(` \u2022 ${f.path}`));
5302
+ });
5303
+ if (result.files.length > 8) {
5304
+ console.log(chalk11.gray(` ... and ${result.files.length - 8} more`));
5305
+ }
5306
+ }
5307
+ console.log("");
5308
+ console.log(chalk11.yellow(" \u25B8 Press Enter to continue"));
5309
+ console.log(chalk11.yellow(' \u25B8 Type "skip" to disable breakpoints for this run'));
5310
+ console.log(chalk11.yellow(' \u25B8 Type "abort" to stop the squad'));
5311
+ console.log("");
5312
+ const answer = await this.promptUser(" \u23F8 ");
5313
+ if (answer.trim().toLowerCase() === "skip") {
5314
+ this.config.breakpoints = false;
5315
+ this.log("Breakpoints disabled for the rest of this run.", "info");
5316
+ } else if (answer.trim().toLowerCase() === "abort") {
5317
+ this.log("Pipeline aborted by developer at breakpoint.", "error");
5318
+ throw new Error("Pipeline aborted by developer at breakpoint");
5319
+ }
5320
+ emitAgentEvent2({
5321
+ type: "breakpoint_resume",
5322
+ agentName: task.assignee,
5323
+ role: task.assignee,
5324
+ message: `Breakpoint resumed after task: ${task.title}`
5325
+ });
5326
+ await this.appendTrace("breakpoint", {
5327
+ taskId: task.id,
5328
+ action: answer.trim().toLowerCase() || "continue"
5329
+ });
5330
+ }
5331
+ /**
5332
+ * Prompt the user for a single line of input via stdin.
5333
+ * If stdin is not a TTY (e.g., running in CI), auto-continues.
5334
+ */
5335
+ promptUser(prompt) {
5336
+ if (!process.stdin.isTTY) {
5337
+ this.log("Non-interactive environment detected \u2014 auto-continuing breakpoint.", "info");
5338
+ return Promise.resolve("");
5339
+ }
5340
+ return new Promise((resolve3) => {
5341
+ const rl = readline.createInterface({
5342
+ input: process.stdin,
5343
+ output: process.stdout
5344
+ });
5345
+ rl.question(prompt, (answer) => {
5346
+ rl.close();
5347
+ resolve3(answer);
5348
+ });
5349
+ });
5350
+ }
5113
5351
  /**
5114
5352
  * Styled console logging
5115
5353
  */
@@ -5168,7 +5406,8 @@ Constraints: Build a production SaaS using Next.js App Router, Prisma, Postgres,
5168
5406
  const config = {
5169
5407
  workspaceRoot: options.output || process.cwd(),
5170
5408
  verbose: options.verbose ?? true,
5171
- autoDeploy: options.deploy ?? false
5409
+ autoDeploy: options.deploy ?? false,
5410
+ breakpoints: options.breakpoints ?? false
5172
5411
  };
5173
5412
  const spinner = ora4("Assembling the squad...").start();
5174
5413
  const orchestrator = new SquadOrchestrator(llm, config);
@@ -5236,8 +5475,8 @@ function parseGitHubUrl(url) {
5236
5475
  if (!cleanUrl.startsWith("github.com/")) {
5237
5476
  throw new Error("Invalid GitHub URL. Please use format: https://github.com/owner/repo");
5238
5477
  }
5239
- const path15 = cleanUrl.replace("github.com/", "");
5240
- const parts = path15.split("/");
5478
+ const path20 = cleanUrl.replace("github.com/", "");
5479
+ const parts = path20.split("/");
5241
5480
  if (parts.length < 2) {
5242
5481
  throw new Error("Invalid GitHub URL. Could not extract owner/repo.");
5243
5482
  }
@@ -5645,115 +5884,1393 @@ async function runReplayCommand(runId, llm, options = {}) {
5645
5884
  }
5646
5885
  }
5647
5886
 
5648
- // src/commands/features.ts
5887
+ // src/commands/audit.ts
5649
5888
  import chalk16 from "chalk";
5650
5889
  import ora6 from "ora";
5651
- import fsExtra from "fs-extra";
5652
- import { resolve as resolve2, join as join6 } from "path";
5653
- var DIRECTORY_COPY_TARGETS = [
5654
- { source: "skills", target: "skills" },
5655
- { source: "extensions", target: "extensions" },
5656
- { source: "src/routing", target: "src/routing" },
5657
- { source: "src/pairing", target: "src/pairing" },
5658
- { source: "src/channels/plugins", target: "src/channels/plugins" },
5659
- { source: "src/channels/allowlists", target: "src/channels/allowlists" }
5660
- ];
5661
- var FILE_COPY_TARGETS = [
5662
- { source: "src/channels/allowlist-match.ts", target: "src/channels/allowlist-match.ts" },
5663
- { source: "src/channels/channel-config.ts", target: "src/channels/channel-config.ts" }
5664
- ];
5665
- var ALL_DIRECTORY_COPY_TARGETS = [
5666
- { source: "dist", target: "dist" },
5667
- { source: "skills", target: "skills" },
5668
- { source: "extensions", target: "extensions" },
5669
- { source: "src", target: "src" },
5670
- { source: "scripts", target: "scripts" },
5671
- { source: "docs", target: "docs" },
5672
- { source: "test", target: "test" },
5673
- { source: "apps", target: "apps" },
5674
- { source: "assets", target: "assets" },
5675
- { source: "prisma", target: "prisma" },
5676
- { source: "ui", target: "ui" },
5677
- { source: "vendor", target: "vendor" },
5678
- { source: "website", target: "website" }
5679
- ];
5680
- var ALL_FILE_COPY_TARGETS = [
5681
- { source: "agdi.mjs", target: "agdi.mjs" },
5682
- { source: "README.md", target: "README.md" },
5683
- { source: "LICENSE", target: "LICENSE" },
5684
- { source: "CHANGELOG.md", target: "CHANGELOG.md" },
5685
- { source: "SECURITY.md", target: "SECURITY.md" },
5686
- { source: "CONTRIBUTING.md", target: "CONTRIBUTING.md" },
5687
- { source: "pnpm-lock.yaml", target: "pnpm-lock.yaml" },
5688
- { source: "pnpm-workspace.yaml", target: "pnpm-workspace.yaml" },
5689
- { source: "tsconfig.json", target: "tsconfig.json" },
5690
- { source: "tsconfig.test.json", target: "tsconfig.test.json" },
5691
- { source: "vitest.config.ts", target: "vitest.config.ts" },
5692
- { source: "vitest.e2e.config.ts", target: "vitest.e2e.config.ts" },
5693
- { source: "vitest.unit.config.ts", target: "vitest.unit.config.ts" }
5694
- ];
5695
- var EXCLUDED_PATH_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".next"]);
5696
- var EXCLUDED_ALL_PATH_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next"]);
5697
- function shouldIncludePath(filePath) {
5698
- const normalized = filePath.replace(/\\/g, "/").toLowerCase();
5699
- const segments = normalized.split("/");
5700
- return !segments.some((segment) => EXCLUDED_PATH_SEGMENTS.has(segment));
5890
+ import * as fs10 from "fs/promises";
5891
+ import * as path10 from "path";
5892
+ var RULES = {
5893
+ // React-specific
5894
+ MISSING_ERROR_BOUNDARY: "react/error-boundary",
5895
+ MISSING_KEY_PROP: "react/key-prop",
5896
+ // Accessibility
5897
+ MISSING_ALT_TEXT: "a11y/alt-text",
5898
+ MISSING_ARIA_LABEL: "a11y/aria-label",
5899
+ MISSING_FORM_LABEL: "a11y/form-label",
5900
+ // Security
5901
+ HARDCODED_SECRET: "security/hardcoded-secret",
5902
+ CONSOLE_LOG_IN_PROD: "security/console-log",
5903
+ // SEO
5904
+ MISSING_META_DESCRIPTION: "seo/meta-description",
5905
+ MISSING_TITLE_TAG: "seo/title-tag",
5906
+ // Quality
5907
+ TODO_IN_CODE: "quality/todo",
5908
+ EMPTY_CATCH: "quality/empty-catch",
5909
+ NO_TESTS: "quality/no-tests"
5910
+ };
5911
+ async function scanDirectory(dir) {
5912
+ const results = [];
5913
+ const ignore = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "runs", "coverage"]);
5914
+ const extensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".html", ".css"]);
5915
+ const walk = async (current) => {
5916
+ let entries;
5917
+ try {
5918
+ entries = await fs10.readdir(current, { withFileTypes: true });
5919
+ } catch {
5920
+ return;
5921
+ }
5922
+ for (const entry of entries) {
5923
+ if (ignore.has(entry.name)) continue;
5924
+ const fullPath = path10.join(current, entry.name);
5925
+ if (entry.isDirectory()) {
5926
+ await walk(fullPath);
5927
+ } else if (entry.isFile() && extensions.has(path10.extname(entry.name))) {
5928
+ results.push(fullPath);
5929
+ }
5930
+ }
5931
+ };
5932
+ await walk(dir);
5933
+ return results;
5701
5934
  }
5702
- async function copyTargetPaths(sourceRepo, targetRoot) {
5703
- for (const target of DIRECTORY_COPY_TARGETS) {
5704
- const sourcePath = join6(sourceRepo, target.source);
5705
- const targetPath = join6(targetRoot, target.target);
5706
- if (!await fsExtra.pathExists(sourcePath)) {
5707
- continue;
5935
+ function analyzeFile(filePath, content, strict) {
5936
+ const issues = [];
5937
+ const lines = content.split("\n");
5938
+ const ext = path10.extname(filePath);
5939
+ const isReact = ext === ".tsx" || ext === ".jsx";
5940
+ const isHTML = ext === ".html";
5941
+ const relPath = filePath;
5942
+ if (isReact) {
5943
+ if (strict && content.includes("export default") && !content.includes("ErrorBoundary") && !content.includes("error-boundary")) {
5944
+ issues.push({
5945
+ file: relPath,
5946
+ severity: "warning",
5947
+ rule: RULES.MISSING_ERROR_BOUNDARY,
5948
+ message: "Component lacks an ErrorBoundary wrapper. Unhandled errors will crash the entire app."
5949
+ });
5708
5950
  }
5709
- await fsExtra.ensureDir(targetPath);
5710
- await fsExtra.copy(sourcePath, targetPath, {
5711
- overwrite: true,
5712
- filter: (source) => shouldIncludePath(source)
5951
+ lines.forEach((line, idx) => {
5952
+ if (line.includes(".map(") && !content.includes("key={") && !content.includes("key=")) {
5953
+ issues.push({
5954
+ file: relPath,
5955
+ line: idx + 1,
5956
+ severity: "warning",
5957
+ rule: RULES.MISSING_KEY_PROP,
5958
+ message: "Array .map() rendered in JSX may be missing a `key` prop."
5959
+ });
5960
+ }
5713
5961
  });
5714
5962
  }
5715
- for (const target of FILE_COPY_TARGETS) {
5716
- const sourcePath = join6(sourceRepo, target.source);
5717
- const targetPath = join6(targetRoot, target.target);
5718
- if (!await fsExtra.pathExists(sourcePath)) {
5719
- continue;
5963
+ if (isReact || isHTML) {
5964
+ lines.forEach((line, idx) => {
5965
+ if (/<img\s/.test(line) && !/alt\s*=/.test(line)) {
5966
+ issues.push({
5967
+ file: relPath,
5968
+ line: idx + 1,
5969
+ severity: strict ? "error" : "warning",
5970
+ rule: RULES.MISSING_ALT_TEXT,
5971
+ message: "<img> element missing `alt` attribute (WCAG 1.1.1)."
5972
+ });
5973
+ }
5974
+ if (/<button\s/.test(line) && !/aria-label/.test(line) && />(\s*)<\/button>/.test(line)) {
5975
+ issues.push({
5976
+ file: relPath,
5977
+ line: idx + 1,
5978
+ severity: "warning",
5979
+ rule: RULES.MISSING_ARIA_LABEL,
5980
+ message: "<button> with no visible text needs `aria-label` (WCAG 4.1.2)."
5981
+ });
5982
+ }
5983
+ if (/<input\s/.test(line) && !/aria-label/.test(line) && !/id\s*=/.test(line)) {
5984
+ issues.push({
5985
+ file: relPath,
5986
+ line: idx + 1,
5987
+ severity: strict ? "error" : "warning",
5988
+ rule: RULES.MISSING_FORM_LABEL,
5989
+ message: "<input> missing `aria-label` or associated <label> (WCAG 1.3.1)."
5990
+ });
5991
+ }
5992
+ });
5993
+ }
5994
+ lines.forEach((line, idx) => {
5995
+ const secretPatterns = [
5996
+ /(?:api[_-]?key|secret|password|token)\s*[:=]\s*['"][A-Za-z0-9_-]{20,}['"]/i,
5997
+ /sk-[a-zA-Z0-9]{20,}/,
5998
+ /ghp_[a-zA-Z0-9]{20,}/
5999
+ ];
6000
+ for (const pattern of secretPatterns) {
6001
+ if (pattern.test(line)) {
6002
+ issues.push({
6003
+ file: relPath,
6004
+ line: idx + 1,
6005
+ severity: "error",
6006
+ rule: RULES.HARDCODED_SECRET,
6007
+ message: "Possible hardcoded secret detected. Use environment variables instead."
6008
+ });
6009
+ break;
6010
+ }
6011
+ }
6012
+ if (strict && /console\.(log|debug|info)\s*\(/.test(line) && !filePath.includes("test") && !filePath.includes("spec")) {
6013
+ issues.push({
6014
+ file: relPath,
6015
+ line: idx + 1,
6016
+ severity: "info",
6017
+ rule: RULES.CONSOLE_LOG_IN_PROD,
6018
+ message: "console.log found in production code. Consider removing or using a logger."
6019
+ });
6020
+ }
6021
+ });
6022
+ lines.forEach((line, idx) => {
6023
+ if (/\/\/\s*(TODO|FIXME|HACK|XXX)/i.test(line)) {
6024
+ issues.push({
6025
+ file: relPath,
6026
+ line: idx + 1,
6027
+ severity: "info",
6028
+ rule: RULES.TODO_IN_CODE,
6029
+ message: `Unresolved ${line.match(/(TODO|FIXME|HACK|XXX)/i)?.[0]} comment.`
6030
+ });
6031
+ }
6032
+ if (/catch\s*\([^)]*\)\s*\{\s*\}/.test(line)) {
6033
+ issues.push({
6034
+ file: relPath,
6035
+ line: idx + 1,
6036
+ severity: "warning",
6037
+ rule: RULES.EMPTY_CATCH,
6038
+ message: "Empty catch block swallows errors. At minimum, log the error."
6039
+ });
6040
+ }
6041
+ });
6042
+ if (isHTML) {
6043
+ if (!content.includes("<title")) {
6044
+ issues.push({
6045
+ file: relPath,
6046
+ severity: "warning",
6047
+ rule: RULES.MISSING_TITLE_TAG,
6048
+ message: "HTML page missing <title> tag."
6049
+ });
6050
+ }
6051
+ if (!content.includes("meta") || !content.includes("description")) {
6052
+ issues.push({
6053
+ file: relPath,
6054
+ severity: strict ? "error" : "warning",
6055
+ rule: RULES.MISSING_META_DESCRIPTION,
6056
+ message: 'HTML page missing <meta name="description"> tag.'
6057
+ });
5720
6058
  }
5721
- await fsExtra.ensureDir(join6(targetPath, ".."));
5722
- await fsExtra.copy(sourcePath, targetPath, { overwrite: true });
5723
6059
  }
6060
+ return issues;
5724
6061
  }
5725
- function shouldIncludeAllPath(filePath) {
5726
- const normalized = filePath.replace(/\\/g, "/").toLowerCase();
5727
- const segments = normalized.split("/");
5728
- return !segments.some((segment) => EXCLUDED_ALL_PATH_SEGMENTS.has(segment));
5729
- }
5730
- async function copyAllTargetPaths(sourceRepo, targetRoot) {
5731
- for (const target of ALL_DIRECTORY_COPY_TARGETS) {
5732
- const sourcePath = join6(sourceRepo, target.source);
5733
- const targetPath = join6(targetRoot, target.target);
5734
- if (!await fsExtra.pathExists(sourcePath)) {
5735
- continue;
6062
+ async function runAuditCommand(options = {}) {
6063
+ const cwd = options.output || process.cwd();
6064
+ const strict = options.prod ?? false;
6065
+ console.log(chalk16.cyan.bold(`
6066
+ \u{1F50D} Agdi Audit${strict ? " \u2014 Production Mode" : ""}
6067
+ `));
6068
+ console.log(chalk16.gray(` Scanning: ${cwd}`));
6069
+ console.log(chalk16.gray(` Mode: ${strict ? chalk16.yellow("STRICT (--prod)") : "Standard"}
6070
+ `));
6071
+ const spinner = ora6("Scanning files...").start();
6072
+ const files = await scanDirectory(cwd);
6073
+ spinner.text = `Analyzing ${files.length} files...`;
6074
+ const allIssues = [];
6075
+ for (const file of files) {
6076
+ try {
6077
+ const content = await fs10.readFile(file, "utf-8");
6078
+ const relPath = path10.relative(cwd, file);
6079
+ const issues = analyzeFile(relPath, content, strict);
6080
+ allIssues.push(...issues);
6081
+ } catch {
5736
6082
  }
5737
- await fsExtra.ensureDir(targetPath);
5738
- await fsExtra.copy(sourcePath, targetPath, {
5739
- overwrite: true,
5740
- filter: (source) => shouldIncludeAllPath(source)
6083
+ }
6084
+ const srcFiles = files.filter((f) => !f.includes("test") && !f.includes("spec") && !f.includes("__tests__"));
6085
+ const testFiles = files.filter((f) => f.includes("test") || f.includes("spec") || f.includes("__tests__"));
6086
+ if (strict && testFiles.length === 0 && srcFiles.length > 5) {
6087
+ allIssues.push({
6088
+ file: "(project)",
6089
+ severity: "error",
6090
+ rule: RULES.NO_TESTS,
6091
+ message: `No test files found. Production apps must have tests.`
5741
6092
  });
5742
6093
  }
5743
- for (const target of ALL_FILE_COPY_TARGETS) {
5744
- const sourcePath = join6(sourceRepo, target.source);
5745
- const targetPath = join6(targetRoot, target.target);
5746
- if (!await fsExtra.pathExists(sourcePath)) {
5747
- continue;
6094
+ const errors = allIssues.filter((i) => i.severity === "error").length;
6095
+ const warnings = allIssues.filter((i) => i.severity === "warning").length;
6096
+ const info = allIssues.filter((i) => i.severity === "info").length;
6097
+ const passed = errors === 0;
6098
+ spinner.stop();
6099
+ if (allIssues.length === 0) {
6100
+ console.log(chalk16.green.bold(" \u2705 No issues found! Your code looks production-ready.\n"));
6101
+ } else {
6102
+ const grouped = /* @__PURE__ */ new Map();
6103
+ for (const issue of allIssues) {
6104
+ const key = issue.severity;
6105
+ if (!grouped.has(key)) grouped.set(key, []);
6106
+ grouped.get(key).push(issue);
6107
+ }
6108
+ for (const severity of ["error", "warning", "info"]) {
6109
+ const items = grouped.get(severity);
6110
+ if (!items || items.length === 0) continue;
6111
+ const icon = severity === "error" ? "\u274C" : severity === "warning" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
6112
+ const color = severity === "error" ? chalk16.red : severity === "warning" ? chalk16.yellow : chalk16.gray;
6113
+ console.log(color.bold(`
6114
+ ${icon} ${severity.toUpperCase()} (${items.length})
6115
+ `));
6116
+ items.slice(0, 15).forEach((issue) => {
6117
+ const loc = issue.line ? `:${issue.line}` : "";
6118
+ console.log(color(` ${issue.file}${loc}`));
6119
+ console.log(chalk16.gray(` [${issue.rule}] ${issue.message}`));
6120
+ });
6121
+ if (items.length > 15) {
6122
+ console.log(color(` ... and ${items.length - 15} more`));
6123
+ }
5748
6124
  }
5749
- await fsExtra.ensureDir(join6(targetPath, ".."));
5750
- await fsExtra.copy(sourcePath, targetPath, { overwrite: true });
5751
6125
  }
6126
+ console.log("");
6127
+ console.log(chalk16.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6128
+ console.log(chalk16.gray(` Files scanned : ${files.length}`));
6129
+ console.log(chalk16.red(` Errors : ${errors}`));
6130
+ console.log(chalk16.yellow(` Warnings : ${warnings}`));
6131
+ console.log(chalk16.gray(` Info : ${info}`));
6132
+ console.log(chalk16.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6133
+ console.log(chalk16.gray(` Verdict : ${passed ? chalk16.green.bold("PASS \u2713") : chalk16.red.bold("FAIL \u2717")}`));
6134
+ console.log("");
6135
+ if (!passed && strict) {
6136
+ console.log(chalk16.yellow(" \u{1F4A1} Run `agdi audit --fix` to auto-fix issues with the QA agent.\n"));
6137
+ }
6138
+ return {
6139
+ issues: allIssues,
6140
+ passed,
6141
+ summary: {
6142
+ errors,
6143
+ warnings,
6144
+ info,
6145
+ filesScanned: files.length
6146
+ }
6147
+ };
5752
6148
  }
5753
- async function writeMirrorPackageJson(targetRoot) {
5754
- const packageJsonPath = join6(targetRoot, "package.json");
5755
- const packageJson = {
5756
- name: "agdi-upstream-mirror",
6149
+
6150
+ // src/commands/api-sandbox.ts
6151
+ import chalk17 from "chalk";
6152
+ import ora7 from "ora";
6153
+ var RATE_LIMIT_DEFAULTS = {
6154
+ /** Delay between consecutive endpoint tests (ms) */
6155
+ delayMs: 500,
6156
+ /** Maximum concurrent requests */
6157
+ maxConcurrent: 1,
6158
+ /** Timeout per request (ms) */
6159
+ timeoutMs: 5e3,
6160
+ /** Max endpoints to test in a single run */
6161
+ maxTestEndpoints: 5
6162
+ };
6163
+ function sleep(ms) {
6164
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
6165
+ }
6166
+ async function fetchAndParse(url) {
6167
+ const response = await fetch(url, {
6168
+ headers: {
6169
+ "User-Agent": "Agdi-Sandbox/1.0",
6170
+ Accept: "text/html, application/json, text/plain"
6171
+ }
6172
+ });
6173
+ if (!response.ok) {
6174
+ throw new Error(`Failed to fetch ${url}: HTTP ${response.status}`);
6175
+ }
6176
+ const contentType = response.headers.get("content-type") || "";
6177
+ const body = await response.text();
6178
+ if (contentType.includes("json") || body.trim().startsWith("{")) {
6179
+ return { title: "OpenAPI Spec", body };
6180
+ }
6181
+ const stripped = body.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<nav[\s\S]*?<\/nav>/gi, "").replace(/<footer[\s\S]*?<\/footer>/gi, "").replace(/<header[\s\S]*?<\/header>/gi, "").replace(/<[^>]+>/g, "\n").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/\n{3,}/g, "\n\n").trim();
6182
+ const titleMatch = body.match(/<title[^>]*>([^<]+)<\/title>/i);
6183
+ const title = titleMatch ? titleMatch[1].trim() : url;
6184
+ return { title, body: stripped };
6185
+ }
6186
+ function extractEndpoints(text) {
6187
+ const endpoints = [];
6188
+ const seen = /* @__PURE__ */ new Set();
6189
+ const patterns = [
6190
+ // REST patterns: GET /api/users, POST /v1/items
6191
+ /\b(GET|POST|PUT|DELETE|PATCH)\s+(\/[a-zA-Z0-9_\-/:{}?&=.]+)/gi,
6192
+ // curl examples
6193
+ /curl\s+.*?(GET|POST|PUT|DELETE|PATCH)\s+['"]?(https?:\/\/[^\s'"]+)/gi
6194
+ ];
6195
+ for (const pattern of patterns) {
6196
+ let match;
6197
+ while ((match = pattern.exec(text)) !== null) {
6198
+ const method = match[1].toUpperCase();
6199
+ const path20 = match[2];
6200
+ const key = `${method} ${path20}`;
6201
+ if (!seen.has(key) && path20.length > 1) {
6202
+ seen.add(key);
6203
+ const idx = text.indexOf(match[0]);
6204
+ const context = text.substring(Math.max(0, idx - 200), Math.min(text.length, idx + 200));
6205
+ const descLine = context.split("\n").find((l) => l.length > 20 && !l.includes(method)) || "";
6206
+ endpoints.push({
6207
+ method,
6208
+ path: path20,
6209
+ description: descLine.trim().substring(0, 120)
6210
+ });
6211
+ }
6212
+ }
6213
+ }
6214
+ try {
6215
+ const json = JSON.parse(text);
6216
+ if (json.paths) {
6217
+ for (const [apiPath, methods] of Object.entries(json.paths)) {
6218
+ for (const [method, spec] of Object.entries(methods)) {
6219
+ if (["get", "post", "put", "delete", "patch"].includes(method)) {
6220
+ const key = `${method.toUpperCase()} ${apiPath}`;
6221
+ if (!seen.has(key)) {
6222
+ seen.add(key);
6223
+ endpoints.push({
6224
+ method: method.toUpperCase(),
6225
+ path: apiPath,
6226
+ description: spec.summary || spec.description || "",
6227
+ parameters: spec.parameters?.map((p) => p.name) || []
6228
+ });
6229
+ }
6230
+ }
6231
+ }
6232
+ }
6233
+ }
6234
+ } catch {
6235
+ }
6236
+ return endpoints;
6237
+ }
6238
+ async function testEndpoint(baseUrl, endpoint, apiKey) {
6239
+ const url = `${baseUrl}${endpoint.path}`.replace(/{[^}]+}/g, "test");
6240
+ const headers = {
6241
+ Accept: "application/json",
6242
+ "User-Agent": "Agdi-Sandbox/1.0"
6243
+ };
6244
+ if (apiKey) {
6245
+ headers["Authorization"] = `Bearer ${apiKey}`;
6246
+ }
6247
+ try {
6248
+ const response = await fetch(url, {
6249
+ method: endpoint.method === "GET" ? "GET" : "HEAD",
6250
+ headers,
6251
+ signal: AbortSignal.timeout(RATE_LIMIT_DEFAULTS.timeoutMs)
6252
+ });
6253
+ let shape = `HTTP ${response.status}`;
6254
+ if (response.ok) {
6255
+ try {
6256
+ const json = await response.json();
6257
+ shape = describeShape(json);
6258
+ } catch {
6259
+ shape = `HTTP ${response.status} (non-JSON)`;
6260
+ }
6261
+ }
6262
+ return { status: response.status, shape };
6263
+ } catch {
6264
+ return { status: 0, shape: "unreachable" };
6265
+ }
6266
+ }
6267
+ function describeShape(obj, depth = 0) {
6268
+ if (depth > 2) return "...";
6269
+ if (obj === null) return "null";
6270
+ if (Array.isArray(obj)) {
6271
+ if (obj.length === 0) return "[]";
6272
+ return `[${describeShape(obj[0], depth + 1)}]`;
6273
+ }
6274
+ if (typeof obj === "object") {
6275
+ const keys = Object.keys(obj).slice(0, 6);
6276
+ const fields = keys.map((k) => `${k}: ${typeof obj[k]}`).join(", ");
6277
+ const extra = Object.keys(obj).length > 6 ? ", ..." : "";
6278
+ return `{ ${fields}${extra} }`;
6279
+ }
6280
+ return typeof obj;
6281
+ }
6282
+ async function runSandboxCommand(url, options = {}) {
6283
+ console.log(chalk17.cyan.bold(`
6284
+ \u{1F9EA} Agdi API Sandbox Reader
6285
+ `));
6286
+ console.log(chalk17.gray(` Target: ${url}`));
6287
+ console.log(chalk17.gray(` Test: ${options.test ? chalk17.yellow("YES") : "no"}
6288
+ `));
6289
+ const spinner = ora7("Fetching API documentation...").start();
6290
+ const { title, body } = await fetchAndParse(url);
6291
+ spinner.text = "Extracting endpoints...";
6292
+ const endpoints = extractEndpoints(body);
6293
+ spinner.stop();
6294
+ console.log(chalk17.green(` \u2713 Found ${endpoints.length} endpoint(s) from "${title}"
6295
+ `));
6296
+ if (endpoints.length > 0) {
6297
+ console.log(chalk17.cyan.bold(" Discovered Endpoints:\n"));
6298
+ endpoints.slice(0, 20).forEach((ep, i) => {
6299
+ const methodColor = ep.method === "GET" ? chalk17.green : ep.method === "POST" ? chalk17.yellow : ep.method === "DELETE" ? chalk17.red : chalk17.blue;
6300
+ console.log(` ${chalk17.gray(`${i + 1}.`)} ${methodColor(ep.method.padEnd(7))} ${chalk17.white(ep.path)}`);
6301
+ if (ep.description) {
6302
+ console.log(chalk17.gray(` ${ep.description}`));
6303
+ }
6304
+ });
6305
+ if (endpoints.length > 20) {
6306
+ console.log(chalk17.gray(` ... and ${endpoints.length - 20} more`));
6307
+ }
6308
+ } else {
6309
+ console.log(chalk17.yellow(" \u26A0 No API endpoints detected. The page may not be API documentation."));
6310
+ }
6311
+ let testResults;
6312
+ if (options.test && endpoints.length > 0) {
6313
+ console.log(chalk17.cyan.bold("\n Live Testing:\n"));
6314
+ const baseUrl = new URL(url).origin;
6315
+ testResults = [];
6316
+ for (const ep of endpoints.slice(0, RATE_LIMIT_DEFAULTS.maxTestEndpoints)) {
6317
+ if (testResults.length > 0) {
6318
+ await sleep(RATE_LIMIT_DEFAULTS.delayMs);
6319
+ }
6320
+ const result = await testEndpoint(baseUrl, ep, options.apiKey);
6321
+ testResults.push({
6322
+ endpoint: `${ep.method} ${ep.path}`,
6323
+ status: result.status,
6324
+ shape: result.shape
6325
+ });
6326
+ const statusColor = result.status >= 200 && result.status < 300 ? chalk17.green : result.status === 0 ? chalk17.red : chalk17.yellow;
6327
+ console.log(` ${statusColor(`${result.status}`)} ${ep.method} ${ep.path}`);
6328
+ console.log(chalk17.gray(` Shape: ${result.shape}`));
6329
+ }
6330
+ }
6331
+ const rawMarkdown = [
6332
+ `# API Documentation: ${title}`,
6333
+ `Source: ${url}`,
6334
+ "",
6335
+ "## Endpoints",
6336
+ ...endpoints.map((ep) => `- **${ep.method}** \`${ep.path}\` \u2014 ${ep.description || "No description"}`),
6337
+ ...testResults ? [
6338
+ "",
6339
+ "## Live Test Results",
6340
+ ...testResults.map((r) => `- \`${r.endpoint}\` \u2192 ${r.status} \u2014 Shape: \`${r.shape}\``)
6341
+ ] : []
6342
+ ].join("\n");
6343
+ console.log("");
6344
+ console.log(chalk17.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6345
+ console.log(chalk17.gray(` Title : ${title}`));
6346
+ console.log(chalk17.gray(` Endpoints : ${endpoints.length}`));
6347
+ console.log(chalk17.gray(` Tested : ${testResults?.length || 0}`));
6348
+ console.log(chalk17.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6349
+ console.log("");
6350
+ if (options.inject) {
6351
+ console.log(chalk17.green(" \u{1F489} Endpoint summary ready for Squad injection.\n"));
6352
+ }
6353
+ return {
6354
+ url,
6355
+ title,
6356
+ endpoints,
6357
+ rawMarkdown,
6358
+ testResults
6359
+ };
6360
+ }
6361
+
6362
+ // src/commands/visual-test.ts
6363
+ import chalk18 from "chalk";
6364
+ import ora8 from "ora";
6365
+ import * as fs11 from "fs/promises";
6366
+ import * as path11 from "path";
6367
+ import { exec as execCb2 } from "child_process";
6368
+ import { promisify as promisify4 } from "util";
6369
+ import crypto3 from "crypto";
6370
+ var execAsync3 = promisify4(execCb2);
6371
+ async function discoverRoutes(cwd) {
6372
+ const routes = ["/"];
6373
+ const routePatterns = [
6374
+ // Next.js app router
6375
+ { glob: "src/app/**/page.tsx", transform: (p) => "/" + p.replace("src/app/", "").replace("/page.tsx", "").replace("page.tsx", "") },
6376
+ { glob: "app/**/page.tsx", transform: (p) => "/" + p.replace("app/", "").replace("/page.tsx", "").replace("page.tsx", "") },
6377
+ // Next.js pages router
6378
+ { glob: "src/pages/**/*.tsx", transform: (p) => "/" + p.replace("src/pages/", "").replace(".tsx", "").replace("/index", "").replace("index", "") },
6379
+ { glob: "pages/**/*.tsx", transform: (p) => "/" + p.replace("pages/", "").replace(".tsx", "").replace("/index", "").replace("index", "") }
6380
+ ];
6381
+ for (const pattern of routePatterns) {
6382
+ try {
6383
+ const { stdout } = await execAsync3(`npx -y glob "${pattern.glob}" --cwd "${cwd}" 2>nul`, { cwd });
6384
+ const files = stdout.trim().split("\n").filter(Boolean);
6385
+ for (const file of files) {
6386
+ const route = pattern.transform(file).replace(/\\/g, "/");
6387
+ if (route && !route.includes("_app") && !route.includes("_document") && !route.includes("api/")) {
6388
+ routes.push(route === "/" ? route : route.replace(/\/$/, ""));
6389
+ }
6390
+ }
6391
+ } catch {
6392
+ }
6393
+ }
6394
+ return [...new Set(routes)];
6395
+ }
6396
+ async function hashFile(filePath) {
6397
+ try {
6398
+ const content = await fs11.readFile(filePath);
6399
+ return crypto3.createHash("md5").update(content).digest("hex");
6400
+ } catch {
6401
+ return "";
6402
+ }
6403
+ }
6404
+ async function takeScreenshot(url, outputPath) {
6405
+ try {
6406
+ await execAsync3(
6407
+ `npx -y playwright screenshot --browser chromium --full-page "${url}" "${outputPath}"`,
6408
+ { timeout: 3e4 }
6409
+ );
6410
+ return true;
6411
+ } catch {
6412
+ }
6413
+ try {
6414
+ const script = `
6415
+ const puppeteer = require('puppeteer');
6416
+ (async () => {
6417
+ const browser = await puppeteer.launch({ headless: 'new' });
6418
+ const page = await browser.newPage();
6419
+ await page.setViewport({ width: 1280, height: 720 });
6420
+ await page.goto('${url}', { waitUntil: 'networkidle0', timeout: 15000 });
6421
+ await page.screenshot({ path: '${outputPath.replace(/\\/g, "\\\\")}', fullPage: true });
6422
+ await browser.close();
6423
+ })();
6424
+ `;
6425
+ await execAsync3(`node -e "${script.replace(/"/g, '\\"').replace(/\n/g, " ")}"`, { timeout: 3e4 });
6426
+ return true;
6427
+ } catch {
6428
+ }
6429
+ return false;
6430
+ }
6431
+ async function runVisualTestCommand(options = {}) {
6432
+ const cwd = options.output || process.cwd();
6433
+ const port = options.port || 3e3;
6434
+ const baseUrl = `http://localhost:${port}`;
6435
+ const baselineDir = path11.join(cwd, ".agdi", "visual-baselines");
6436
+ const currentDir = path11.join(cwd, ".agdi", "visual-current");
6437
+ console.log(chalk18.cyan.bold("\n\u{1F4F8} Agdi Visual Regression Testing\n"));
6438
+ console.log(chalk18.gray(` Base URL : ${baseUrl}`));
6439
+ console.log(chalk18.gray(` Baselines: ${path11.relative(cwd, baselineDir)}`));
6440
+ console.log(chalk18.gray(` Mode : ${options.update ? chalk18.yellow("UPDATE BASELINES") : "Compare"}
6441
+ `));
6442
+ await fs11.mkdir(baselineDir, { recursive: true });
6443
+ await fs11.mkdir(currentDir, { recursive: true });
6444
+ const spinner = ora8("Discovering routes...").start();
6445
+ let routes;
6446
+ if (options.routes) {
6447
+ routes = options.routes.split(",").map((r) => r.trim());
6448
+ } else {
6449
+ routes = await discoverRoutes(cwd);
6450
+ }
6451
+ spinner.stop();
6452
+ console.log(chalk18.gray(` Routes : ${routes.join(", ")}
6453
+ `));
6454
+ let serverRunning = false;
6455
+ try {
6456
+ const response = await fetch(baseUrl, { signal: AbortSignal.timeout(3e3) });
6457
+ serverRunning = response.ok || response.status < 500;
6458
+ } catch {
6459
+ }
6460
+ if (!serverRunning) {
6461
+ console.log(chalk18.yellow(" \u26A0 Dev server not detected. Start it with `npm run dev` first."));
6462
+ console.log(chalk18.gray(` Expected at: ${baseUrl}
6463
+ `));
6464
+ return {
6465
+ results: [{ route: "*", status: "error", error: "Dev server not running" }],
6466
+ passed: 0,
6467
+ failed: 0,
6468
+ newBaselines: 0,
6469
+ errors: 1
6470
+ };
6471
+ }
6472
+ const results = [];
6473
+ let canScreenshot = false;
6474
+ try {
6475
+ await execAsync3("npx -y playwright --version", { timeout: 1e4 });
6476
+ canScreenshot = true;
6477
+ } catch {
6478
+ try {
6479
+ await execAsync3(`node -e "require('puppeteer')"`, { timeout: 5e3 });
6480
+ canScreenshot = true;
6481
+ } catch {
6482
+ }
6483
+ }
6484
+ for (const route of routes) {
6485
+ const safeName = route.replace(/\//g, "_").replace(/^_/, "root") || "root";
6486
+ const currentPath = path11.join(currentDir, `${safeName}.png`);
6487
+ const baselinePath = path11.join(baselineDir, `${safeName}.png`);
6488
+ const url = `${baseUrl}${route}`;
6489
+ process.stdout.write(chalk18.gray(` \u{1F4F7} ${route.padEnd(30)}`));
6490
+ if (canScreenshot) {
6491
+ const success = await takeScreenshot(url, currentPath);
6492
+ if (!success) {
6493
+ console.log(chalk18.red("\u2717 Screenshot failed"));
6494
+ results.push({ route, status: "error", error: "Screenshot capture failed" });
6495
+ continue;
6496
+ }
6497
+ } else {
6498
+ try {
6499
+ const response = await fetch(url, { signal: AbortSignal.timeout(5e3) });
6500
+ const html = await response.text();
6501
+ const htmlHash = crypto3.createHash("md5").update(html).digest("hex");
6502
+ await fs11.writeFile(currentPath.replace(".png", ".hash"), htmlHash, "utf-8");
6503
+ try {
6504
+ const baselineHash = await fs11.readFile(baselinePath.replace(".png", ".hash"), "utf-8");
6505
+ if (options.update) {
6506
+ await fs11.writeFile(baselinePath.replace(".png", ".hash"), htmlHash, "utf-8");
6507
+ console.log(chalk18.blue("\u2B06 Updated"));
6508
+ results.push({ route, status: "updated" });
6509
+ } else if (htmlHash === baselineHash) {
6510
+ console.log(chalk18.green("\u2713 Pass"));
6511
+ results.push({ route, status: "pass" });
6512
+ } else {
6513
+ console.log(chalk18.red("\u2717 Changed"));
6514
+ results.push({ route, status: "fail", diffPercentage: 100 });
6515
+ }
6516
+ } catch {
6517
+ await fs11.writeFile(baselinePath.replace(".png", ".hash"), htmlHash, "utf-8");
6518
+ console.log(chalk18.cyan("\u2605 New baseline"));
6519
+ results.push({ route, status: "new", screenshotPath: currentPath });
6520
+ }
6521
+ continue;
6522
+ } catch (err) {
6523
+ console.log(chalk18.red("\u2717 Unreachable"));
6524
+ results.push({ route, status: "error", error: `Route unreachable: ${route}` });
6525
+ continue;
6526
+ }
6527
+ }
6528
+ try {
6529
+ await fs11.access(baselinePath);
6530
+ const currentHash = await hashFile(currentPath);
6531
+ const baselineHash = await hashFile(baselinePath);
6532
+ if (options.update) {
6533
+ await fs11.copyFile(currentPath, baselinePath);
6534
+ console.log(chalk18.blue("\u2B06 Updated"));
6535
+ results.push({ route, status: "updated", screenshotPath: currentPath, baselinePath });
6536
+ } else if (currentHash === baselineHash) {
6537
+ console.log(chalk18.green("\u2713 Pass"));
6538
+ results.push({ route, status: "pass", screenshotPath: currentPath, baselinePath });
6539
+ } else {
6540
+ console.log(chalk18.red("\u2717 Visual diff detected"));
6541
+ results.push({ route, status: "fail", screenshotPath: currentPath, baselinePath, diffPercentage: 100 });
6542
+ }
6543
+ } catch {
6544
+ await fs11.copyFile(currentPath, baselinePath);
6545
+ console.log(chalk18.cyan("\u2605 New baseline"));
6546
+ results.push({ route, status: "new", screenshotPath: currentPath, baselinePath });
6547
+ }
6548
+ }
6549
+ const passed = results.filter((r) => r.status === "pass").length;
6550
+ const failed = results.filter((r) => r.status === "fail").length;
6551
+ const newBaselines = results.filter((r) => r.status === "new" || r.status === "updated").length;
6552
+ const errors = results.filter((r) => r.status === "error").length;
6553
+ console.log("");
6554
+ console.log(chalk18.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6555
+ console.log(chalk18.green(` Passed : ${passed}`));
6556
+ console.log(chalk18.red(` Failed : ${failed}`));
6557
+ console.log(chalk18.blue(` New/Updated : ${newBaselines}`));
6558
+ console.log(chalk18.gray(` Errors : ${errors}`));
6559
+ console.log(chalk18.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6560
+ console.log(chalk18.gray(` Verdict : ${failed === 0 && errors === 0 ? chalk18.green.bold("PASS \u2713") : chalk18.red.bold("FAIL \u2717")}`));
6561
+ console.log("");
6562
+ if (failed > 0) {
6563
+ console.log(chalk18.yellow(" \u{1F4A1} Run `agdi visual-test --update` to accept current screenshots as new baselines.\n"));
6564
+ }
6565
+ if (!canScreenshot) {
6566
+ console.log(chalk18.gray(" \u2139 Install Playwright for full screenshot support: npx playwright install\n"));
6567
+ }
6568
+ return { results, passed, failed, newBaselines, errors };
6569
+ }
6570
+
6571
+ // src/commands/git-memory.ts
6572
+ import chalk19 from "chalk";
6573
+ import ora9 from "ora";
6574
+ import { exec as execCb3 } from "child_process";
6575
+ import { promisify as promisify5 } from "util";
6576
+ import * as path12 from "path";
6577
+ var execAsync4 = promisify5(execCb3);
6578
+ async function parseGitLog(cwd, depth) {
6579
+ const sep2 = "---AGDI_SEP---";
6580
+ const format = `%H${sep2}%ai${sep2}%an${sep2}%s${sep2}%b${sep2}`;
6581
+ try {
6582
+ const { stdout } = await execAsync4(
6583
+ `git log -${depth} --pretty=format:"${format}" --shortstat`,
6584
+ { cwd, maxBuffer: 10 * 1024 * 1024 }
6585
+ );
6586
+ const commits = [];
6587
+ const entries = stdout.split(format.replace(/%[A-Za-z]+/g, "")).filter(Boolean);
6588
+ const lines = stdout.split("\n");
6589
+ let current = null;
6590
+ for (const line of lines) {
6591
+ if (line.includes(sep2)) {
6592
+ if (current?.hash) {
6593
+ commits.push(current);
6594
+ }
6595
+ const parts = line.split(sep2);
6596
+ current = {
6597
+ hash: (parts[0] || "").trim().slice(0, 8),
6598
+ date: (parts[1] || "").trim(),
6599
+ author: (parts[2] || "").trim(),
6600
+ subject: (parts[3] || "").trim(),
6601
+ body: (parts[4] || "").trim(),
6602
+ filesChanged: 0
6603
+ };
6604
+ } else if (current && /\d+ file/.test(line)) {
6605
+ const match = line.match(/(\d+) file/);
6606
+ if (match) {
6607
+ current.filesChanged = parseInt(match[1], 10);
6608
+ }
6609
+ }
6610
+ }
6611
+ if (current?.hash) {
6612
+ commits.push(current);
6613
+ }
6614
+ return commits;
6615
+ } catch {
6616
+ return [];
6617
+ }
6618
+ }
6619
+ async function getHotFiles(cwd, depth) {
6620
+ try {
6621
+ const { stdout } = await execAsync4(
6622
+ `git log -${depth} --name-only --pretty=format: | sort | uniq -c | sort -rn`,
6623
+ { cwd, maxBuffer: 5 * 1024 * 1024 }
6624
+ );
6625
+ const fileCounts = /* @__PURE__ */ new Map();
6626
+ const { stdout: winStdout } = await execAsync4(
6627
+ `git log -${depth} --name-only --pretty=format:`,
6628
+ { cwd, maxBuffer: 5 * 1024 * 1024 }
6629
+ );
6630
+ winStdout.split("\n").filter(Boolean).forEach((f) => {
6631
+ const file = f.trim();
6632
+ if (file) {
6633
+ fileCounts.set(file, (fileCounts.get(file) || 0) + 1);
6634
+ }
6635
+ });
6636
+ return [...fileCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, changes]) => ({ file, changes }));
6637
+ } catch {
6638
+ return [];
6639
+ }
6640
+ }
6641
+ async function getContributors(cwd) {
6642
+ try {
6643
+ const { stdout } = await execAsync4(
6644
+ 'git log --format="%an" | sort -u',
6645
+ { cwd }
6646
+ );
6647
+ const { stdout: winStdout } = await execAsync4(
6648
+ 'git log --format="%an"',
6649
+ { cwd }
6650
+ );
6651
+ return [...new Set(winStdout.split("\n").map((s) => s.trim()).filter(Boolean))];
6652
+ } catch {
6653
+ return [];
6654
+ }
6655
+ }
6656
+ function detectPatterns(commits) {
6657
+ const patterns = [];
6658
+ const subjects = commits.map((c) => c.subject.toLowerCase());
6659
+ const conventional = subjects.filter((s) => /^(feat|fix|chore|docs|style|refactor|test|ci)\(/.test(s));
6660
+ if (conventional.length > commits.length * 0.3) {
6661
+ patterns.push("Uses Conventional Commits");
6662
+ }
6663
+ const fixes = subjects.filter((s) => s.includes("fix"));
6664
+ if (fixes.length > commits.length * 0.3) {
6665
+ patterns.push(`High fix ratio (${Math.round(fixes.length / commits.length * 100)}%) \u2014 code may need stabilization`);
6666
+ }
6667
+ const refactors = subjects.filter((s) => s.includes("refactor"));
6668
+ if (refactors.length > 3) {
6669
+ patterns.push("Active refactoring \u2014 check for deprecated patterns");
6670
+ }
6671
+ const features = subjects.filter((s) => s.includes("feat") || s.includes("add"));
6672
+ if (features.length > 5) {
6673
+ patterns.push(`Rapid feature development (${features.length} features in last ${commits.length} commits)`);
6674
+ }
6675
+ return patterns;
6676
+ }
6677
+ async function runMemoryCommand(options = {}) {
6678
+ const cwd = options.output || process.cwd();
6679
+ const depth = options.depth || 30;
6680
+ console.log(chalk19.cyan.bold("\n\u{1F9E0} Agdi Git Memory\n"));
6681
+ console.log(chalk19.gray(` Workspace: ${cwd}`));
6682
+ console.log(chalk19.gray(` Depth : last ${depth} commits
6683
+ `));
6684
+ const spinner = ora9("Reading git history...").start();
6685
+ try {
6686
+ await execAsync4("git rev-parse --is-inside-work-tree", { cwd });
6687
+ } catch {
6688
+ spinner.stop();
6689
+ console.log(chalk19.red(" \u2717 Not a git repository.\n"));
6690
+ return {
6691
+ repoName: path12.basename(cwd),
6692
+ totalCommits: 0,
6693
+ contributors: [],
6694
+ recentCommits: [],
6695
+ hotFiles: [],
6696
+ patterns: [],
6697
+ summary: "Not a git repository."
6698
+ };
6699
+ }
6700
+ let repoName = path12.basename(cwd);
6701
+ try {
6702
+ const { stdout } = await execAsync4("git remote get-url origin", { cwd });
6703
+ const match = stdout.trim().match(/\/([^/]+?)(?:\.git)?$/);
6704
+ if (match) repoName = match[1];
6705
+ } catch {
6706
+ }
6707
+ spinner.text = "Parsing commits...";
6708
+ const commits = await parseGitLog(cwd, depth);
6709
+ spinner.text = "Analyzing file activity...";
6710
+ const hotFiles = await getHotFiles(cwd, depth);
6711
+ spinner.text = "Identifying contributors...";
6712
+ const contributors = await getContributors(cwd);
6713
+ spinner.text = "Detecting patterns...";
6714
+ const patterns = detectPatterns(commits);
6715
+ spinner.stop();
6716
+ let totalCommits = commits.length;
6717
+ try {
6718
+ const { stdout } = await execAsync4("git rev-list --count HEAD", { cwd });
6719
+ totalCommits = parseInt(stdout.trim(), 10) || commits.length;
6720
+ } catch {
6721
+ }
6722
+ console.log(chalk19.green(` \u2713 Analyzed ${commits.length} commits from "${repoName}"
6723
+ `));
6724
+ console.log(chalk19.cyan.bold(" Contributors:"));
6725
+ contributors.slice(0, 8).forEach((c) => console.log(chalk19.gray(` \u2022 ${c}`)));
6726
+ if (contributors.length > 8) console.log(chalk19.gray(` ... and ${contributors.length - 8} more`));
6727
+ if (hotFiles.length > 0) {
6728
+ console.log(chalk19.cyan.bold("\n Hot Files (most changed):"));
6729
+ hotFiles.slice(0, 10).forEach((f) => {
6730
+ console.log(chalk19.gray(` ${String(f.changes).padStart(3)} changes ${f.file}`));
6731
+ });
6732
+ }
6733
+ if (patterns.length > 0) {
6734
+ console.log(chalk19.cyan.bold("\n Detected Patterns:"));
6735
+ patterns.forEach((p) => console.log(chalk19.yellow(` \u26A1 ${p}`)));
6736
+ }
6737
+ console.log(chalk19.cyan.bold("\n Recent Commits:"));
6738
+ commits.slice(0, 8).forEach((c) => {
6739
+ console.log(chalk19.gray(` ${c.hash} ${c.subject}`));
6740
+ });
6741
+ const summary = [
6742
+ `Project: ${repoName}`,
6743
+ `Total commits: ${totalCommits}`,
6744
+ `Contributors: ${contributors.join(", ")}`,
6745
+ `Patterns: ${patterns.join("; ") || "none detected"}`,
6746
+ "",
6747
+ "Recent activity:",
6748
+ ...commits.slice(0, 10).map((c) => ` - ${c.subject} (${c.author}, ${c.date})`),
6749
+ "",
6750
+ "Most active files:",
6751
+ ...hotFiles.slice(0, 10).map((f) => ` - ${f.file} (${f.changes} changes)`)
6752
+ ].join("\n");
6753
+ console.log("");
6754
+ console.log(chalk19.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6755
+ console.log(chalk19.gray(` Total Commits : ${totalCommits}`));
6756
+ console.log(chalk19.gray(` Contributors : ${contributors.length}`));
6757
+ console.log(chalk19.gray(` Hot Files : ${hotFiles.length}`));
6758
+ console.log(chalk19.gray(` Patterns : ${patterns.length}`));
6759
+ console.log(chalk19.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6760
+ console.log("");
6761
+ if (options.inject) {
6762
+ console.log(chalk19.green(" \u{1F489} Memory context ready for Squad injection.\n"));
6763
+ }
6764
+ return {
6765
+ repoName,
6766
+ totalCommits,
6767
+ contributors,
6768
+ recentCommits: commits,
6769
+ hotFiles,
6770
+ patterns,
6771
+ summary
6772
+ };
6773
+ }
6774
+
6775
+ // src/commands/deps-scan.ts
6776
+ import chalk20 from "chalk";
6777
+ import ora10 from "ora";
6778
+ import { exec as execCb4 } from "child_process";
6779
+ import { promisify as promisify6 } from "util";
6780
+ import * as fs12 from "fs/promises";
6781
+ import * as path13 from "path";
6782
+ var execAsync5 = promisify6(execCb4);
6783
+ async function runNpmAudit(cwd) {
6784
+ try {
6785
+ const { stdout } = await execAsync5("npm audit --json 2>nul", {
6786
+ cwd,
6787
+ maxBuffer: 10 * 1024 * 1024
6788
+ });
6789
+ const audit = JSON.parse(stdout);
6790
+ const vulns = [];
6791
+ if (audit.vulnerabilities) {
6792
+ for (const [name, info] of Object.entries(audit.vulnerabilities)) {
6793
+ const vuln = info;
6794
+ vulns.push({
6795
+ name,
6796
+ severity: vuln.severity || "info",
6797
+ title: vuln.via?.[0]?.title || vuln.via?.[0] || "Unknown vulnerability",
6798
+ fixAvailable: !!vuln.fixAvailable
6799
+ });
6800
+ }
6801
+ }
6802
+ return vulns;
6803
+ } catch (err) {
6804
+ try {
6805
+ const output = err.stdout || "";
6806
+ const audit = JSON.parse(output);
6807
+ const vulns = [];
6808
+ if (audit.vulnerabilities) {
6809
+ for (const [name, info] of Object.entries(audit.vulnerabilities)) {
6810
+ const vuln = info;
6811
+ vulns.push({
6812
+ name,
6813
+ severity: vuln.severity || "info",
6814
+ title: typeof vuln.via?.[0] === "string" ? vuln.via[0] : vuln.via?.[0]?.title || "Unknown",
6815
+ fixAvailable: !!vuln.fixAvailable
6816
+ });
6817
+ }
6818
+ }
6819
+ return vulns;
6820
+ } catch {
6821
+ return [];
6822
+ }
6823
+ }
6824
+ }
6825
+ async function runNpmOutdated(cwd) {
6826
+ try {
6827
+ const { stdout } = await execAsync5("npm outdated --json 2>nul", {
6828
+ cwd,
6829
+ maxBuffer: 5 * 1024 * 1024
6830
+ });
6831
+ const outdated = JSON.parse(stdout || "{}");
6832
+ return Object.entries(outdated).map(([name, info]) => {
6833
+ const pkg = info;
6834
+ return {
6835
+ name,
6836
+ current: pkg.current || "unknown",
6837
+ wanted: pkg.wanted || "unknown",
6838
+ latest: pkg.latest || "unknown",
6839
+ type: pkg.type || "dependencies"
6840
+ };
6841
+ });
6842
+ } catch (err) {
6843
+ try {
6844
+ const output = err.stdout || "{}";
6845
+ const outdated = JSON.parse(output);
6846
+ return Object.entries(outdated).map(([name, info]) => {
6847
+ const pkg = info;
6848
+ return {
6849
+ name,
6850
+ current: pkg.current || "unknown",
6851
+ wanted: pkg.wanted || "unknown",
6852
+ latest: pkg.latest || "unknown",
6853
+ type: pkg.type || "dependencies"
6854
+ };
6855
+ });
6856
+ } catch {
6857
+ return [];
6858
+ }
6859
+ }
6860
+ }
6861
+ async function countDeps(cwd) {
6862
+ try {
6863
+ const pkgJson = await fs12.readFile(path13.join(cwd, "package.json"), "utf-8");
6864
+ const pkg = JSON.parse(pkgJson);
6865
+ const deps = Object.keys(pkg.dependencies || {}).length;
6866
+ const devDeps = Object.keys(pkg.devDependencies || {}).length;
6867
+ return deps + devDeps;
6868
+ } catch {
6869
+ return 0;
6870
+ }
6871
+ }
6872
+ async function runDepsScanCommand(options = {}) {
6873
+ const cwd = options.output || process.cwd();
6874
+ console.log(chalk20.cyan.bold("\n\u{1F6E1}\uFE0F Agdi Dependency Scanner\n"));
6875
+ console.log(chalk20.gray(` Workspace: ${cwd}`));
6876
+ console.log(chalk20.gray(` Mode : ${options.full ? chalk20.yellow("FULL") : "Quick"}
6877
+ `));
6878
+ try {
6879
+ await fs12.access(path13.join(cwd, "package.json"));
6880
+ } catch {
6881
+ console.log(chalk20.red(" \u2717 No package.json found in workspace.\n"));
6882
+ return {
6883
+ vulnerabilities: [],
6884
+ outdated: [],
6885
+ totalDeps: 0,
6886
+ summary: { critical: 0, high: 0, moderate: 0, low: 0, outdatedCount: 0, fixableCount: 0 },
6887
+ passed: true
6888
+ };
6889
+ }
6890
+ const spinner = ora10("Running vulnerability scan...").start();
6891
+ const vulns = await runNpmAudit(cwd);
6892
+ const totalDeps = await countDeps(cwd);
6893
+ let outdated = [];
6894
+ if (options.full) {
6895
+ spinner.text = "Checking for outdated packages...";
6896
+ outdated = await runNpmOutdated(cwd);
6897
+ }
6898
+ spinner.stop();
6899
+ const critical = vulns.filter((v) => v.severity === "critical").length;
6900
+ const high = vulns.filter((v) => v.severity === "high").length;
6901
+ const moderate = vulns.filter((v) => v.severity === "moderate").length;
6902
+ const low = vulns.filter((v) => v.severity === "low").length;
6903
+ const fixable = vulns.filter((v) => v.fixAvailable).length;
6904
+ if (vulns.length > 0) {
6905
+ console.log(chalk20.red.bold(` \u26A0 Found ${vulns.length} vulnerabilit${vulns.length === 1 ? "y" : "ies"}:
6906
+ `));
6907
+ for (const severity of ["critical", "high", "moderate", "low"]) {
6908
+ const items = vulns.filter((v) => v.severity === severity);
6909
+ if (items.length === 0) continue;
6910
+ const color = severity === "critical" ? chalk20.red.bold : severity === "high" ? chalk20.red : severity === "moderate" ? chalk20.yellow : chalk20.gray;
6911
+ console.log(color(` ${severity.toUpperCase()} (${items.length}):`));
6912
+ items.slice(0, 5).forEach((v) => {
6913
+ const fix = v.fixAvailable ? chalk20.green(" [fixable]") : chalk20.gray(" [manual]");
6914
+ console.log(chalk20.gray(` \u2022 ${v.name} \u2014 ${v.title}${fix}`));
6915
+ });
6916
+ if (items.length > 5) {
6917
+ console.log(chalk20.gray(` ... and ${items.length - 5} more`));
6918
+ }
6919
+ }
6920
+ } else {
6921
+ console.log(chalk20.green(" \u2705 No known vulnerabilities found!\n"));
6922
+ }
6923
+ if (options.full && outdated.length > 0) {
6924
+ console.log(chalk20.yellow.bold(`
6925
+ \u{1F4E6} Outdated Packages (${outdated.length}):
6926
+ `));
6927
+ outdated.slice(0, 10).forEach((pkg) => {
6928
+ const isMajor = pkg.current.split(".")[0] !== pkg.latest.split(".")[0];
6929
+ const urgency = isMajor ? chalk20.red("MAJOR") : chalk20.yellow("minor");
6930
+ console.log(chalk20.gray(` ${pkg.name.padEnd(30)} ${pkg.current} \u2192 ${chalk20.green(pkg.latest)} ${urgency}`));
6931
+ });
6932
+ if (outdated.length > 10) {
6933
+ console.log(chalk20.gray(` ... and ${outdated.length - 10} more`));
6934
+ }
6935
+ }
6936
+ if (options.fix && fixable > 0) {
6937
+ console.log(chalk20.cyan.bold("\n \u{1F527} Running npm audit fix...\n"));
6938
+ try {
6939
+ const { stdout } = await execAsync5("npm audit fix", { cwd });
6940
+ console.log(chalk20.gray(` ${stdout.trim().split("\n").slice(0, 5).join("\n ")}`));
6941
+ console.log(chalk20.green("\n \u2713 Auto-fix complete.\n"));
6942
+ } catch (err) {
6943
+ console.log(chalk20.yellow(" \u26A0 Some fixes require manual intervention.\n"));
6944
+ }
6945
+ }
6946
+ const passed = critical === 0 && high === 0;
6947
+ console.log("");
6948
+ console.log(chalk20.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6949
+ console.log(chalk20.gray(` Total Deps : ${totalDeps}`));
6950
+ console.log(chalk20.red(` Critical : ${critical}`));
6951
+ console.log(chalk20.red(` High : ${high}`));
6952
+ console.log(chalk20.yellow(` Moderate : ${moderate}`));
6953
+ console.log(chalk20.gray(` Low : ${low}`));
6954
+ if (options.full) {
6955
+ console.log(chalk20.yellow(` Outdated : ${outdated.length}`));
6956
+ }
6957
+ console.log(chalk20.green(` Fixable : ${fixable}`));
6958
+ console.log(chalk20.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6959
+ console.log(chalk20.gray(` Verdict : ${passed ? chalk20.green.bold("PASS \u2713") : chalk20.red.bold("FAIL \u2717")}`));
6960
+ console.log("");
6961
+ if (!passed) {
6962
+ console.log(chalk20.yellow(" \u{1F4A1} Run `agdi deps --fix` to auto-fix vulnerabilities.\n"));
6963
+ }
6964
+ return {
6965
+ vulnerabilities: vulns,
6966
+ outdated,
6967
+ totalDeps,
6968
+ summary: { critical, high, moderate, low, outdatedCount: outdated.length, fixableCount: fixable },
6969
+ passed
6970
+ };
6971
+ }
6972
+
6973
+ // src/commands/plugin-loader.ts
6974
+ import chalk21 from "chalk";
6975
+ import * as fs13 from "fs/promises";
6976
+ import * as path14 from "path";
6977
+ import { exec as execCb5 } from "child_process";
6978
+ import { promisify as promisify7 } from "util";
6979
+ var execAsync6 = promisify7(execCb5);
6980
+ async function discoverLocalPlugins(cwd) {
6981
+ const pluginDir = path14.join(cwd, ".agdi", "plugins");
6982
+ const plugins = [];
6983
+ try {
6984
+ const entries = await fs13.readdir(pluginDir, { withFileTypes: true });
6985
+ for (const entry of entries) {
6986
+ if (!entry.isDirectory()) continue;
6987
+ const pluginPath = path14.join(pluginDir, entry.name);
6988
+ const manifestPath = path14.join(pluginPath, "agdi-plugin.json");
6989
+ try {
6990
+ const raw = await fs13.readFile(manifestPath, "utf-8");
6991
+ const manifest = JSON.parse(raw);
6992
+ const entryExists = await fs13.access(path14.join(pluginPath, manifest.entryPoint)).then(() => true).catch(() => false);
6993
+ plugins.push({
6994
+ manifest,
6995
+ path: pluginPath,
6996
+ source: "local",
6997
+ healthy: entryExists
6998
+ });
6999
+ } catch {
7000
+ }
7001
+ }
7002
+ } catch {
7003
+ }
7004
+ return plugins;
7005
+ }
7006
+ async function discoverNpmPlugins(cwd) {
7007
+ const plugins = [];
7008
+ try {
7009
+ const nodeModules = path14.join(cwd, "node_modules");
7010
+ const entries = await fs13.readdir(nodeModules, { withFileTypes: true });
7011
+ for (const entry of entries) {
7012
+ if (!entry.isDirectory() || !entry.name.startsWith("agdi-plugin-")) continue;
7013
+ const pluginPath = path14.join(nodeModules, entry.name);
7014
+ const manifestPath = path14.join(pluginPath, "agdi-plugin.json");
7015
+ try {
7016
+ const raw = await fs13.readFile(manifestPath, "utf-8");
7017
+ const manifest = JSON.parse(raw);
7018
+ plugins.push({
7019
+ manifest,
7020
+ path: pluginPath,
7021
+ source: "npm",
7022
+ healthy: true
7023
+ });
7024
+ } catch {
7025
+ try {
7026
+ const pkgRaw = await fs13.readFile(path14.join(pluginPath, "package.json"), "utf-8");
7027
+ const pkg = JSON.parse(pkgRaw);
7028
+ if (pkg.agdiPlugin) {
7029
+ plugins.push({
7030
+ manifest: {
7031
+ name: pkg.name,
7032
+ version: pkg.version,
7033
+ description: pkg.description || "",
7034
+ entryPoint: pkg.main || "index.js",
7035
+ capabilities: pkg.agdiPlugin.capabilities || [],
7036
+ author: pkg.author || "unknown"
7037
+ },
7038
+ path: pluginPath,
7039
+ source: "npm",
7040
+ healthy: true
7041
+ });
7042
+ }
7043
+ } catch {
7044
+ }
7045
+ }
7046
+ }
7047
+ } catch {
7048
+ }
7049
+ return plugins;
7050
+ }
7051
+ async function createPluginTemplate(cwd) {
7052
+ const pluginDir = path14.join(cwd, ".agdi", "plugins", "my-custom-agent");
7053
+ await fs13.mkdir(pluginDir, { recursive: true });
7054
+ const manifest = {
7055
+ name: "agdi-plugin-custom",
7056
+ version: "1.0.0",
7057
+ description: "My custom Agdi plugin",
7058
+ agent: "custom",
7059
+ entryPoint: "./index.js",
7060
+ capabilities: ["frontend"],
7061
+ author: "developer"
7062
+ };
7063
+ await fs13.writeFile(
7064
+ path14.join(pluginDir, "agdi-plugin.json"),
7065
+ JSON.stringify(manifest, null, 2),
7066
+ "utf-8"
7067
+ );
7068
+ const entryPoint = `/**
7069
+ * Agdi Custom Plugin
7070
+ *
7071
+ * This plugin adds a custom agent capability to Agdi.
7072
+ * Modify the \`execute\` function to define your agent's behavior.
7073
+ */
7074
+
7075
+ module.exports = {
7076
+ name: 'custom',
7077
+
7078
+ /**
7079
+ * Called when the Squad assigns a task to this agent.
7080
+ * @param {object} task - The task object { id, title, description, ... }
7081
+ * @param {object} context - The shared context { projectSpec, workspaceRoot, ... }
7082
+ * @returns {object} - Agent output { success, content, files, errors }
7083
+ */
7084
+ async execute(task, context) {
7085
+ return {
7086
+ success: true,
7087
+ content: 'Custom agent executed successfully!',
7088
+ files: [],
7089
+ errors: [],
7090
+ };
7091
+ },
7092
+
7093
+ /**
7094
+ * System prompt for the LLM when this agent is active.
7095
+ */
7096
+ getSystemPrompt() {
7097
+ return 'You are a custom agent. Follow the project architecture rules.';
7098
+ },
7099
+ };
7100
+ `;
7101
+ await fs13.writeFile(path14.join(pluginDir, "index.js"), entryPoint, "utf-8");
7102
+ return pluginDir;
7103
+ }
7104
+ async function runPluginsCommand(options = {}) {
7105
+ const cwd = options.output || process.cwd();
7106
+ console.log(chalk21.cyan.bold("\n\u{1F50C} Agdi Plugin Manager\n"));
7107
+ if (options.init) {
7108
+ const pluginPath = await createPluginTemplate(cwd);
7109
+ console.log(chalk21.green(` \u2713 Plugin template created at:`));
7110
+ console.log(chalk21.gray(` ${path14.relative(cwd, pluginPath)}/
7111
+ `));
7112
+ console.log(chalk21.gray(" Files created:"));
7113
+ console.log(chalk21.gray(" \u2022 agdi-plugin.json (manifest)"));
7114
+ console.log(chalk21.gray(" \u2022 index.js (entry point)\n"));
7115
+ console.log(chalk21.yellow(" Next steps:"));
7116
+ console.log(chalk21.gray(" 1. Edit agdi-plugin.json with your plugin details"));
7117
+ console.log(chalk21.gray(" 2. Implement your agent logic in index.js"));
7118
+ console.log(chalk21.gray(" 3. Run `agdi plugins` to verify it loads\n"));
7119
+ return;
7120
+ }
7121
+ if (options.install) {
7122
+ const pkgName = options.install.startsWith("agdi-plugin-") ? options.install : `agdi-plugin-${options.install}`;
7123
+ console.log(chalk21.gray(` Installing ${pkgName}...`));
7124
+ try {
7125
+ await execAsync6(`npm install ${pkgName}`, { cwd });
7126
+ console.log(chalk21.green(` \u2713 ${pkgName} installed successfully.
7127
+ `));
7128
+ } catch (err) {
7129
+ console.log(chalk21.red(` \u2717 Failed to install ${pkgName}.`));
7130
+ console.log(chalk21.gray(` ${err.message?.split("\n")[0]}
7131
+ `));
7132
+ }
7133
+ return;
7134
+ }
7135
+ const localPlugins = await discoverLocalPlugins(cwd);
7136
+ const npmPlugins = await discoverNpmPlugins(cwd);
7137
+ const allPlugins = [...localPlugins, ...npmPlugins];
7138
+ if (allPlugins.length === 0) {
7139
+ console.log(chalk21.gray(" No plugins installed.\n"));
7140
+ console.log(chalk21.yellow(" Get started:"));
7141
+ console.log(chalk21.gray(" \u2022 Create a plugin: agdi plugins --init"));
7142
+ console.log(chalk21.gray(" \u2022 Install from npm: agdi plugins --install <name>\n"));
7143
+ return;
7144
+ }
7145
+ console.log(chalk21.gray(` Found ${allPlugins.length} plugin(s):
7146
+ `));
7147
+ for (const plugin of allPlugins) {
7148
+ const status = plugin.healthy ? chalk21.green("\u2713") : chalk21.red("\u2717");
7149
+ const source = plugin.source === "local" ? chalk21.blue("local") : chalk21.magenta("npm");
7150
+ console.log(` ${status} ${chalk21.white.bold(plugin.manifest.name)} ${chalk21.gray(`v${plugin.manifest.version}`)} ${source}`);
7151
+ console.log(chalk21.gray(` ${plugin.manifest.description}`));
7152
+ if (plugin.manifest.capabilities.length > 0) {
7153
+ console.log(chalk21.gray(` Capabilities: ${plugin.manifest.capabilities.join(", ")}`));
7154
+ }
7155
+ }
7156
+ console.log("");
7157
+ console.log(chalk21.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
7158
+ console.log(chalk21.gray(` Local : ${localPlugins.length}`));
7159
+ console.log(chalk21.gray(` NPM : ${npmPlugins.length}`));
7160
+ console.log(chalk21.gray(` Healthy : ${allPlugins.filter((p) => p.healthy).length}/${allPlugins.length}`));
7161
+ console.log(chalk21.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
7162
+ console.log("");
7163
+ }
7164
+
7165
+ // src/commands/features.ts
7166
+ import chalk22 from "chalk";
7167
+ import ora11 from "ora";
7168
+ import fsExtra from "fs-extra";
7169
+ import { resolve as resolve2, join as join10 } from "path";
7170
+ var DIRECTORY_COPY_TARGETS = [
7171
+ { source: "skills", target: "skills" },
7172
+ { source: "extensions", target: "extensions" },
7173
+ { source: "src/routing", target: "src/routing" },
7174
+ { source: "src/pairing", target: "src/pairing" },
7175
+ { source: "src/channels/plugins", target: "src/channels/plugins" },
7176
+ { source: "src/channels/allowlists", target: "src/channels/allowlists" }
7177
+ ];
7178
+ var FILE_COPY_TARGETS = [
7179
+ { source: "src/channels/allowlist-match.ts", target: "src/channels/allowlist-match.ts" },
7180
+ { source: "src/channels/channel-config.ts", target: "src/channels/channel-config.ts" }
7181
+ ];
7182
+ var ALL_DIRECTORY_COPY_TARGETS = [
7183
+ { source: "dist", target: "dist" },
7184
+ { source: "skills", target: "skills" },
7185
+ { source: "extensions", target: "extensions" },
7186
+ { source: "src", target: "src" },
7187
+ { source: "scripts", target: "scripts" },
7188
+ { source: "docs", target: "docs" },
7189
+ { source: "test", target: "test" },
7190
+ { source: "apps", target: "apps" },
7191
+ { source: "assets", target: "assets" },
7192
+ { source: "prisma", target: "prisma" },
7193
+ { source: "ui", target: "ui" },
7194
+ { source: "vendor", target: "vendor" },
7195
+ { source: "website", target: "website" }
7196
+ ];
7197
+ var ALL_FILE_COPY_TARGETS = [
7198
+ { source: "agdi.mjs", target: "agdi.mjs" },
7199
+ { source: "README.md", target: "README.md" },
7200
+ { source: "LICENSE", target: "LICENSE" },
7201
+ { source: "CHANGELOG.md", target: "CHANGELOG.md" },
7202
+ { source: "SECURITY.md", target: "SECURITY.md" },
7203
+ { source: "CONTRIBUTING.md", target: "CONTRIBUTING.md" },
7204
+ { source: "pnpm-lock.yaml", target: "pnpm-lock.yaml" },
7205
+ { source: "pnpm-workspace.yaml", target: "pnpm-workspace.yaml" },
7206
+ { source: "tsconfig.json", target: "tsconfig.json" },
7207
+ { source: "tsconfig.test.json", target: "tsconfig.test.json" },
7208
+ { source: "vitest.config.ts", target: "vitest.config.ts" },
7209
+ { source: "vitest.e2e.config.ts", target: "vitest.e2e.config.ts" },
7210
+ { source: "vitest.unit.config.ts", target: "vitest.unit.config.ts" }
7211
+ ];
7212
+ var EXCLUDED_PATH_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".next"]);
7213
+ var EXCLUDED_ALL_PATH_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next"]);
7214
+ function shouldIncludePath(filePath) {
7215
+ const normalized = filePath.replace(/\\/g, "/").toLowerCase();
7216
+ const segments = normalized.split("/");
7217
+ return !segments.some((segment) => EXCLUDED_PATH_SEGMENTS.has(segment));
7218
+ }
7219
+ async function copyTargetPaths(sourceRepo, targetRoot) {
7220
+ for (const target of DIRECTORY_COPY_TARGETS) {
7221
+ const sourcePath = join10(sourceRepo, target.source);
7222
+ const targetPath = join10(targetRoot, target.target);
7223
+ if (!await fsExtra.pathExists(sourcePath)) {
7224
+ continue;
7225
+ }
7226
+ await fsExtra.ensureDir(targetPath);
7227
+ await fsExtra.copy(sourcePath, targetPath, {
7228
+ overwrite: true,
7229
+ filter: (source) => shouldIncludePath(source)
7230
+ });
7231
+ }
7232
+ for (const target of FILE_COPY_TARGETS) {
7233
+ const sourcePath = join10(sourceRepo, target.source);
7234
+ const targetPath = join10(targetRoot, target.target);
7235
+ if (!await fsExtra.pathExists(sourcePath)) {
7236
+ continue;
7237
+ }
7238
+ await fsExtra.ensureDir(join10(targetPath, ".."));
7239
+ await fsExtra.copy(sourcePath, targetPath, { overwrite: true });
7240
+ }
7241
+ }
7242
+ function shouldIncludeAllPath(filePath) {
7243
+ const normalized = filePath.replace(/\\/g, "/").toLowerCase();
7244
+ const segments = normalized.split("/");
7245
+ return !segments.some((segment) => EXCLUDED_ALL_PATH_SEGMENTS.has(segment));
7246
+ }
7247
+ async function copyAllTargetPaths(sourceRepo, targetRoot) {
7248
+ for (const target of ALL_DIRECTORY_COPY_TARGETS) {
7249
+ const sourcePath = join10(sourceRepo, target.source);
7250
+ const targetPath = join10(targetRoot, target.target);
7251
+ if (!await fsExtra.pathExists(sourcePath)) {
7252
+ continue;
7253
+ }
7254
+ await fsExtra.ensureDir(targetPath);
7255
+ await fsExtra.copy(sourcePath, targetPath, {
7256
+ overwrite: true,
7257
+ filter: (source) => shouldIncludeAllPath(source)
7258
+ });
7259
+ }
7260
+ for (const target of ALL_FILE_COPY_TARGETS) {
7261
+ const sourcePath = join10(sourceRepo, target.source);
7262
+ const targetPath = join10(targetRoot, target.target);
7263
+ if (!await fsExtra.pathExists(sourcePath)) {
7264
+ continue;
7265
+ }
7266
+ await fsExtra.ensureDir(join10(targetPath, ".."));
7267
+ await fsExtra.copy(sourcePath, targetPath, { overwrite: true });
7268
+ }
7269
+ }
7270
+ async function writeMirrorPackageJson(targetRoot) {
7271
+ const packageJsonPath = join10(targetRoot, "package.json");
7272
+ const packageJson = {
7273
+ name: "agdi-upstream-mirror",
5757
7274
  private: true,
5758
7275
  type: "module",
5759
7276
  description: "Mirrored upstream Agdi feature/runtime surface for Agdi-dev"
@@ -5761,8 +7278,8 @@ async function writeMirrorPackageJson(targetRoot) {
5761
7278
  await fsExtra.writeJson(packageJsonPath, packageJson, { spaces: 2 });
5762
7279
  }
5763
7280
  async function ensureMirrorNodeModules(sourceRepo, targetRoot) {
5764
- const sourceNodeModules = join6(sourceRepo, "node_modules");
5765
- const targetNodeModules = join6(targetRoot, "node_modules");
7281
+ const sourceNodeModules = join10(sourceRepo, "node_modules");
7282
+ const targetNodeModules = join10(targetRoot, "node_modules");
5766
7283
  if (!await fsExtra.pathExists(sourceNodeModules)) {
5767
7284
  return "missing";
5768
7285
  }
@@ -5785,7 +7302,7 @@ async function ensureMirrorNodeModules(sourceRepo, targetRoot) {
5785
7302
  }
5786
7303
  }
5787
7304
  async function listSkills(targetRoot) {
5788
- const skillsRoot = join6(targetRoot, "skills");
7305
+ const skillsRoot = join10(targetRoot, "skills");
5789
7306
  if (!await fsExtra.pathExists(skillsRoot)) {
5790
7307
  return [];
5791
7308
  }
@@ -5796,7 +7313,7 @@ async function listSkills(targetRoot) {
5796
7313
  continue;
5797
7314
  }
5798
7315
  const skillName = entry.name;
5799
- const skillFile = join6(skillsRoot, skillName, "SKILL.md");
7316
+ const skillFile = join10(skillsRoot, skillName, "SKILL.md");
5800
7317
  if (await fsExtra.pathExists(skillFile)) {
5801
7318
  result.push(skillName);
5802
7319
  }
@@ -5804,7 +7321,7 @@ async function listSkills(targetRoot) {
5804
7321
  return result.sort();
5805
7322
  }
5806
7323
  async function listExtensions(targetRoot) {
5807
- const extensionsRoot = join6(targetRoot, "extensions");
7324
+ const extensionsRoot = join10(targetRoot, "extensions");
5808
7325
  if (!await fsExtra.pathExists(extensionsRoot)) {
5809
7326
  return [];
5810
7327
  }
@@ -5814,7 +7331,7 @@ async function listExtensions(targetRoot) {
5814
7331
  if (!entry.isDirectory()) {
5815
7332
  continue;
5816
7333
  }
5817
- const manifestPath = join6(extensionsRoot, entry.name, "agdi.plugin.json");
7334
+ const manifestPath = join10(extensionsRoot, entry.name, "agdi.plugin.json");
5818
7335
  if (!await fsExtra.pathExists(manifestPath)) {
5819
7336
  continue;
5820
7337
  }
@@ -5831,7 +7348,7 @@ async function listExtensions(targetRoot) {
5831
7348
  return result.sort();
5832
7349
  }
5833
7350
  async function listTypeScriptFiles(targetRoot, relativePath) {
5834
- const root = join6(targetRoot, relativePath);
7351
+ const root = join10(targetRoot, relativePath);
5835
7352
  if (!await fsExtra.pathExists(root)) {
5836
7353
  return 0;
5837
7354
  }
@@ -5844,7 +7361,7 @@ async function listTypeScriptFiles(targetRoot, relativePath) {
5844
7361
  }
5845
7362
  const entries = await fsExtra.readdir(current, { withFileTypes: true });
5846
7363
  for (const entry of entries) {
5847
- const absolutePath = join6(current, entry.name);
7364
+ const absolutePath = join10(current, entry.name);
5848
7365
  if (!shouldIncludePath(absolutePath)) {
5849
7366
  continue;
5850
7367
  }
@@ -5864,17 +7381,17 @@ async function runFeaturesCommand(options) {
5864
7381
  const sourceRepo = resolve2(cwd, options.from ?? "../Agdi");
5865
7382
  const targetRoot = resolve2(cwd, options.to ?? "./upstream/agdi");
5866
7383
  console.log("");
5867
- console.log(chalk16.cyan.bold(options.all ? "Feature Mirror" : "Feature Packs"));
5868
- console.log(chalk16.gray(` Source: ${sourceRepo}`));
5869
- console.log(chalk16.gray(` Target: ${targetRoot}`));
7384
+ console.log(chalk22.cyan.bold(options.all ? "Feature Mirror" : "Feature Packs"));
7385
+ console.log(chalk22.gray(` Source: ${sourceRepo}`));
7386
+ console.log(chalk22.gray(` Target: ${targetRoot}`));
5870
7387
  console.log("");
5871
7388
  if (options.sync) {
5872
- const spinner = ora6(
7389
+ const spinner = ora11(
5873
7390
  options.all ? "Syncing full upstream Agdi mirror..." : "Syncing feature packs from Agdi..."
5874
7391
  ).start();
5875
7392
  if (!await fsExtra.pathExists(sourceRepo)) {
5876
7393
  spinner.fail("Source Agdi repository not found");
5877
- console.log(chalk16.red(`Expected source path: ${sourceRepo}`));
7394
+ console.log(chalk22.red(`Expected source path: ${sourceRepo}`));
5878
7395
  return;
5879
7396
  }
5880
7397
  try {
@@ -5890,17 +7407,17 @@ async function runFeaturesCommand(options) {
5890
7407
  spinner.succeed("Sync completed");
5891
7408
  if (options.all && nodeModulesStatus !== null) {
5892
7409
  if (nodeModulesStatus === "linked") {
5893
- console.log(chalk16.gray(" Mirror node_modules: linked to source repo"));
7410
+ console.log(chalk22.gray(" Mirror node_modules: linked to source repo"));
5894
7411
  } else if (nodeModulesStatus === "missing") {
5895
- console.log(chalk16.yellow(" Mirror node_modules: source node_modules missing"));
7412
+ console.log(chalk22.yellow(" Mirror node_modules: source node_modules missing"));
5896
7413
  } else {
5897
- console.log(chalk16.yellow(" Mirror node_modules: failed to link (proxy commands may fail)"));
7414
+ console.log(chalk22.yellow(" Mirror node_modules: failed to link (proxy commands may fail)"));
5898
7415
  }
5899
7416
  }
5900
7417
  } catch (error) {
5901
7418
  spinner.fail("Sync failed");
5902
7419
  const message = error instanceof Error ? error.message : String(error);
5903
- console.log(chalk16.red(message));
7420
+ console.log(chalk22.red(message));
5904
7421
  return;
5905
7422
  }
5906
7423
  }
@@ -5911,53 +7428,53 @@ async function runFeaturesCommand(options) {
5911
7428
  listTypeScriptFiles(targetRoot, "src/pairing"),
5912
7429
  listTypeScriptFiles(targetRoot, "src/channels")
5913
7430
  ]);
5914
- console.log(chalk16.white.bold("Imported capabilities:"));
5915
- console.log(chalk16.gray(` Skills: ${skills.length}`));
5916
- console.log(chalk16.gray(` Channel extensions: ${extensions.length}`));
5917
- console.log(chalk16.gray(` Routing TS files: ${routingTs}`));
5918
- console.log(chalk16.gray(` Pairing TS files: ${pairingTs}`));
5919
- console.log(chalk16.gray(` Channel policy TS files: ${channelPolicyTs}`));
7431
+ console.log(chalk22.white.bold("Imported capabilities:"));
7432
+ console.log(chalk22.gray(` Skills: ${skills.length}`));
7433
+ console.log(chalk22.gray(` Channel extensions: ${extensions.length}`));
7434
+ console.log(chalk22.gray(` Routing TS files: ${routingTs}`));
7435
+ console.log(chalk22.gray(` Pairing TS files: ${pairingTs}`));
7436
+ console.log(chalk22.gray(` Channel policy TS files: ${channelPolicyTs}`));
5920
7437
  console.log("");
5921
7438
  if (options.all) {
5922
- const hasRuntimeEntry = await fsExtra.pathExists(join6(targetRoot, "agdi.mjs"));
5923
- const hasRuntimeDist = await fsExtra.pathExists(join6(targetRoot, "dist/entry.js"));
5924
- console.log(chalk16.white.bold("Mirror runtime:"));
5925
- console.log(chalk16.gray(` agdi.mjs present: ${hasRuntimeEntry ? "yes" : "no"}`));
5926
- console.log(chalk16.gray(` dist/entry.js present: ${hasRuntimeDist ? "yes" : "no"}`));
7439
+ const hasRuntimeEntry = await fsExtra.pathExists(join10(targetRoot, "agdi.mjs"));
7440
+ const hasRuntimeDist = await fsExtra.pathExists(join10(targetRoot, "dist/entry.js"));
7441
+ console.log(chalk22.white.bold("Mirror runtime:"));
7442
+ console.log(chalk22.gray(` agdi.mjs present: ${hasRuntimeEntry ? "yes" : "no"}`));
7443
+ console.log(chalk22.gray(` dist/entry.js present: ${hasRuntimeDist ? "yes" : "no"}`));
5927
7444
  console.log("");
5928
7445
  }
5929
7446
  if (extensions.length > 0) {
5930
- console.log(chalk16.white.bold("Top extensions:"));
5931
- console.log(chalk16.gray(` ${extensions.slice(0, 16).join(", ")}`));
7447
+ console.log(chalk22.white.bold("Top extensions:"));
7448
+ console.log(chalk22.gray(` ${extensions.slice(0, 16).join(", ")}`));
5932
7449
  console.log("");
5933
7450
  }
5934
7451
  if (skills.length > 0) {
5935
- console.log(chalk16.white.bold("Top skills:"));
5936
- console.log(chalk16.gray(` ${skills.slice(0, 16).join(", ")}`));
7452
+ console.log(chalk22.white.bold("Top skills:"));
7453
+ console.log(chalk22.gray(` ${skills.slice(0, 16).join(", ")}`));
5937
7454
  console.log("");
5938
7455
  }
5939
7456
  }
5940
7457
 
5941
7458
  // src/commands/channels.ts
5942
- import chalk17 from "chalk";
5943
- import fs12 from "fs-extra";
5944
- import path13 from "path";
7459
+ import chalk23 from "chalk";
7460
+ import fs16 from "fs-extra";
7461
+ import path18 from "path";
5945
7462
 
5946
7463
  // src/extensions/channel-runtime.ts
5947
7464
  import os3 from "os";
5948
- import path12 from "path";
7465
+ import path17 from "path";
5949
7466
 
5950
7467
  // src/extensions/registry.ts
5951
- import fs10 from "fs-extra";
5952
- import path10 from "path";
7468
+ import fs14 from "fs-extra";
7469
+ import path15 from "path";
5953
7470
  function resolveUpstreamExtensionsRoot(startDir = process.cwd()) {
5954
- let current = path10.resolve(startDir);
7471
+ let current = path15.resolve(startDir);
5955
7472
  while (true) {
5956
- const candidate = path10.join(current, "upstream", "agdi", "extensions");
5957
- if (fs10.existsSync(candidate)) {
7473
+ const candidate = path15.join(current, "upstream", "agdi", "extensions");
7474
+ if (fs14.existsSync(candidate)) {
5958
7475
  return candidate;
5959
7476
  }
5960
- const parent = path10.dirname(current);
7477
+ const parent = path15.dirname(current);
5961
7478
  if (parent === current) {
5962
7479
  break;
5963
7480
  }
@@ -5966,22 +7483,22 @@ function resolveUpstreamExtensionsRoot(startDir = process.cwd()) {
5966
7483
  return null;
5967
7484
  }
5968
7485
  async function discoverExtensions(extensionsRoot) {
5969
- if (!await fs10.pathExists(extensionsRoot)) {
7486
+ if (!await fs14.pathExists(extensionsRoot)) {
5970
7487
  return [];
5971
7488
  }
5972
- const entries = await fs10.readdir(extensionsRoot, { withFileTypes: true });
7489
+ const entries = await fs14.readdir(extensionsRoot, { withFileTypes: true });
5973
7490
  const discovered = [];
5974
7491
  for (const entry of entries) {
5975
7492
  if (!entry.isDirectory()) {
5976
7493
  continue;
5977
7494
  }
5978
- const extensionDir = path10.join(extensionsRoot, entry.name);
5979
- const manifestPath = path10.join(extensionDir, "agdi.plugin.json");
5980
- if (!await fs10.pathExists(manifestPath)) {
7495
+ const extensionDir = path15.join(extensionsRoot, entry.name);
7496
+ const manifestPath = path15.join(extensionDir, "agdi.plugin.json");
7497
+ if (!await fs14.pathExists(manifestPath)) {
5981
7498
  continue;
5982
7499
  }
5983
7500
  try {
5984
- const manifest = await fs10.readJson(manifestPath);
7501
+ const manifest = await fs14.readJson(manifestPath);
5985
7502
  if (!manifest || typeof manifest.id !== "string" || manifest.id.trim().length === 0) {
5986
7503
  continue;
5987
7504
  }
@@ -6017,12 +7534,12 @@ function discoverChannels(extensions) {
6017
7534
  }
6018
7535
 
6019
7536
  // src/extensions/whatsapp-runtime.ts
6020
- import fs11 from "fs-extra";
7537
+ import fs15 from "fs-extra";
6021
7538
  import os2 from "os";
6022
- import path11 from "path";
7539
+ import path16 from "path";
6023
7540
  function expandHomeDir(input6) {
6024
7541
  if (input6.startsWith("~/") || input6 === "~") {
6025
- return path11.join(os2.homedir(), input6.slice(2));
7542
+ return path16.join(os2.homedir(), input6.slice(2));
6026
7543
  }
6027
7544
  return input6;
6028
7545
  }
@@ -6045,10 +7562,10 @@ async function closeSocket(socket) {
6045
7562
  }
6046
7563
  async function sendWhatsAppTextMessage(options) {
6047
7564
  const targetJid = normalizeWhatsAppJid(options.to);
6048
- const authDir = path11.resolve(expandHomeDir(options.authDir ?? path11.join(os2.homedir(), ".agdi", "whatsapp-auth")));
7565
+ const authDir = path16.resolve(expandHomeDir(options.authDir ?? path16.join(os2.homedir(), ".agdi", "whatsapp-auth")));
6049
7566
  const timeoutMs = options.timeoutMs ?? 9e4;
6050
7567
  const printQr = options.printQr ?? true;
6051
- await fs11.ensureDir(authDir);
7568
+ await fs15.ensureDir(authDir);
6052
7569
  const baileys = await import("./lib-2XISBYT3.js");
6053
7570
  const makeWASocket = baileys.makeWASocket;
6054
7571
  const useMultiFileAuthState = baileys.useMultiFileAuthState;
@@ -6152,7 +7669,7 @@ function expandHomeDir2(input6) {
6152
7669
  return os3.homedir();
6153
7670
  }
6154
7671
  if (input6.startsWith("~/")) {
6155
- return path12.join(os3.homedir(), input6.slice(2));
7672
+ return path17.join(os3.homedir(), input6.slice(2));
6156
7673
  }
6157
7674
  return input6;
6158
7675
  }
@@ -6164,8 +7681,8 @@ function normalizeBaseUrl(url) {
6164
7681
  return cleaned.replace(/\/+$/, "");
6165
7682
  }
6166
7683
  function resolveChannelRuntimeConfig(config) {
6167
- const whatsappAuthDirRaw = cleanValue(process.env.AGDI_WHATSAPP_AUTH_DIR) || cleanValue(config.channelRuntime?.whatsapp?.authDir) || path12.join(os3.homedir(), ".agdi", "whatsapp-auth");
6168
- const whatsappAuthDir = path12.resolve(expandHomeDir2(whatsappAuthDirRaw));
7684
+ const whatsappAuthDirRaw = cleanValue(process.env.AGDI_WHATSAPP_AUTH_DIR) || cleanValue(config.channelRuntime?.whatsapp?.authDir) || path17.join(os3.homedir(), ".agdi", "whatsapp-auth");
7685
+ const whatsappAuthDir = path17.resolve(expandHomeDir2(whatsappAuthDirRaw));
6169
7686
  return {
6170
7687
  slackWebhook: cleanValue(process.env.AGDI_WEBHOOK_SLACK) || cleanValue(config.channelRuntime?.slack?.webhookUrl),
6171
7688
  discordWebhook: cleanValue(process.env.AGDI_WEBHOOK_DISCORD) || cleanValue(config.channelRuntime?.discord?.webhookUrl),
@@ -6373,31 +7890,31 @@ async function sendChannelMessage(request, config) {
6373
7890
  async function runChannelsListCommand() {
6374
7891
  const { root, statuses } = await getChannelRuntimeStatuses();
6375
7892
  console.log("");
6376
- console.log(chalk17.cyan.bold("Channel Runtime"));
7893
+ console.log(chalk23.cyan.bold("Channel Runtime"));
6377
7894
  if (!root) {
6378
- console.log(chalk17.yellow("No synced upstream extensions found."));
6379
- console.log(chalk17.gray("Run: pnpm features:sync"));
7895
+ console.log(chalk23.yellow("No synced upstream extensions found."));
7896
+ console.log(chalk23.gray("Run: pnpm features:sync"));
6380
7897
  console.log("");
6381
7898
  return;
6382
7899
  }
6383
- console.log(chalk17.gray(` Source: ${root}`));
7900
+ console.log(chalk23.gray(` Source: ${root}`));
6384
7901
  console.log("");
6385
7902
  if (statuses.length === 0) {
6386
- console.log(chalk17.yellow("No channel manifests discovered."));
7903
+ console.log(chalk23.yellow("No channel manifests discovered."));
6387
7904
  console.log("");
6388
7905
  return;
6389
7906
  }
6390
7907
  const executable = statuses.filter((s) => s.executable).length;
6391
7908
  const nonExecutable = statuses.length - executable;
6392
- console.log(chalk17.white(`Channels discovered: ${statuses.length}`));
6393
- console.log(chalk17.green(`Executable now: ${executable}`));
6394
- console.log(chalk17.gray(`Manifest-only: ${nonExecutable}`));
7909
+ console.log(chalk23.white(`Channels discovered: ${statuses.length}`));
7910
+ console.log(chalk23.green(`Executable now: ${executable}`));
7911
+ console.log(chalk23.gray(`Manifest-only: ${nonExecutable}`));
6395
7912
  console.log("");
6396
7913
  for (const status of statuses) {
6397
- const marker = status.executable ? chalk17.green("ready") : chalk17.yellow("manifest");
6398
- const reason = status.reason ? chalk17.gray(` (${status.reason})`) : "";
7914
+ const marker = status.executable ? chalk23.green("ready") : chalk23.yellow("manifest");
7915
+ const reason = status.reason ? chalk23.gray(` (${status.reason})`) : "";
6399
7916
  console.log(
6400
- `${marker} ${status.channelId} ${chalk17.gray(`[${status.extensionId}]`)}${reason}`
7917
+ `${marker} ${status.channelId} ${chalk23.gray(`[${status.extensionId}]`)}${reason}`
6401
7918
  );
6402
7919
  }
6403
7920
  console.log("");
@@ -6421,37 +7938,37 @@ async function runChannelsSendCommand(options) {
6421
7938
  config
6422
7939
  );
6423
7940
  console.log("");
6424
- console.log(chalk17.green.bold("Channel Message Sent"));
6425
- console.log(chalk17.gray(` Channel: ${result.channel}`));
6426
- console.log(chalk17.gray(` Extension: ${result.extensionId}`));
6427
- console.log(chalk17.gray(` Detail: ${result.detail}`));
7941
+ console.log(chalk23.green.bold("Channel Message Sent"));
7942
+ console.log(chalk23.gray(` Channel: ${result.channel}`));
7943
+ console.log(chalk23.gray(` Extension: ${result.extensionId}`));
7944
+ console.log(chalk23.gray(` Detail: ${result.detail}`));
6428
7945
  if (typeof result.responseStatus === "number") {
6429
- console.log(chalk17.gray(` HTTP status: ${result.responseStatus}`));
7946
+ console.log(chalk23.gray(` HTTP status: ${result.responseStatus}`));
6430
7947
  }
6431
7948
  console.log("");
6432
7949
  }
6433
7950
  function renderLevel(level) {
6434
7951
  if (level === "ok") {
6435
- return chalk17.green("ok");
7952
+ return chalk23.green("ok");
6436
7953
  }
6437
7954
  if (level === "warn") {
6438
- return chalk17.yellow("warn");
7955
+ return chalk23.yellow("warn");
6439
7956
  }
6440
- return chalk17.red("error");
7957
+ return chalk23.red("error");
6441
7958
  }
6442
7959
  async function runChannelsDoctorCommand() {
6443
7960
  const config = loadConfig();
6444
7961
  const runtime = resolveChannelRuntimeConfig(config);
6445
7962
  const { root, statuses } = await getChannelRuntimeStatuses();
6446
7963
  console.log("");
6447
- console.log(chalk17.cyan.bold("Channel Doctor"));
7964
+ console.log(chalk23.cyan.bold("Channel Doctor"));
6448
7965
  if (!root) {
6449
- console.log(chalk17.red("upstream/agdi/extensions not found."));
6450
- console.log(chalk17.gray("Run: pnpm features:sync"));
7966
+ console.log(chalk23.red("upstream/agdi/extensions not found."));
7967
+ console.log(chalk23.gray("Run: pnpm features:sync"));
6451
7968
  console.log("");
6452
7969
  return;
6453
7970
  }
6454
- console.log(chalk17.gray(` Source: ${root}`));
7971
+ console.log(chalk23.gray(` Source: ${root}`));
6455
7972
  console.log("");
6456
7973
  const executable = /* @__PURE__ */ new Map();
6457
7974
  for (const status of statuses) {
@@ -6461,7 +7978,7 @@ async function runChannelsDoctorCommand() {
6461
7978
  executable.set(status.channelId, { extensionId: status.extensionId });
6462
7979
  }
6463
7980
  if (executable.size === 0) {
6464
- console.log(chalk17.yellow("No executable channels found."));
7981
+ console.log(chalk23.yellow("No executable channels found."));
6465
7982
  console.log("");
6466
7983
  return;
6467
7984
  }
@@ -6524,8 +8041,8 @@ async function runChannelsDoctorCommand() {
6524
8041
  continue;
6525
8042
  }
6526
8043
  if (channelId === "whatsapp") {
6527
- const credsPath = path13.join(runtime.whatsappAuthDir, "creds.json");
6528
- const hasCreds = await fs12.pathExists(credsPath);
8044
+ const credsPath = path18.join(runtime.whatsappAuthDir, "creds.json");
8045
+ const hasCreds = await fs16.pathExists(credsPath);
6529
8046
  if (!hasCreds && !runtime.whatsappPrintQr) {
6530
8047
  items.push({
6531
8048
  channelId,
@@ -6571,20 +8088,20 @@ async function runChannelsDoctorCommand() {
6571
8088
  const errorCount = items.filter((item) => item.level === "error").length;
6572
8089
  for (const item of items) {
6573
8090
  console.log(
6574
- `${renderLevel(item.level)} ${item.channelId} ${chalk17.gray(`[${item.extensionId}]`)} ${chalk17.gray("-")} ${item.detail}`
8091
+ `${renderLevel(item.level)} ${item.channelId} ${chalk23.gray(`[${item.extensionId}]`)} ${chalk23.gray("-")} ${item.detail}`
6575
8092
  );
6576
8093
  }
6577
8094
  console.log("");
6578
- console.log(chalk17.white(`Checked: ${items.length}`));
6579
- console.log(chalk17.green(`OK: ${okCount}`));
6580
- console.log(chalk17.yellow(`Warnings: ${warnCount}`));
6581
- console.log(chalk17.red(`Errors: ${errorCount}`));
8095
+ console.log(chalk23.white(`Checked: ${items.length}`));
8096
+ console.log(chalk23.green(`OK: ${okCount}`));
8097
+ console.log(chalk23.yellow(`Warnings: ${warnCount}`));
8098
+ console.log(chalk23.red(`Errors: ${errorCount}`));
6582
8099
  console.log("");
6583
8100
  }
6584
8101
 
6585
8102
  // src/commands/gateway.ts
6586
8103
  import net from "net";
6587
- import chalk18 from "chalk";
8104
+ import chalk24 from "chalk";
6588
8105
  function resolveGatewayTarget(options) {
6589
8106
  const host = options.host?.trim() || process.env.AGDI_GATEWAY_HOST || "127.0.0.1";
6590
8107
  const rawPort = options.port ?? process.env.AGDI_GATEWAY_PORT ?? "18789";
@@ -6628,17 +8145,17 @@ async function runGatewayStatusCommand(options) {
6628
8145
  const open = await checkPortOpen(target);
6629
8146
  const health = open ? await fetchGatewayHealth(target) : { ok: false };
6630
8147
  console.log("");
6631
- console.log(chalk18.cyan.bold("Gateway Status (native Agdi-dev)"));
6632
- console.log(chalk18.gray(` Target: ws://${target.host}:${target.port}`));
6633
- console.log(chalk18.gray(` Port open: ${open ? "yes" : "no"}`));
8148
+ console.log(chalk24.cyan.bold("Gateway Status (native Agdi-dev)"));
8149
+ console.log(chalk24.gray(` Target: ws://${target.host}:${target.port}`));
8150
+ console.log(chalk24.gray(` Port open: ${open ? "yes" : "no"}`));
6634
8151
  if (health.status !== void 0) {
6635
- console.log(chalk18.gray(` Health HTTP: ${health.status}`));
8152
+ console.log(chalk24.gray(` Health HTTP: ${health.status}`));
6636
8153
  } else {
6637
- console.log(chalk18.gray(" Health HTTP: unreachable"));
8154
+ console.log(chalk24.gray(" Health HTTP: unreachable"));
6638
8155
  }
6639
8156
  if (health.body && health.body.trim().length > 0) {
6640
8157
  const trimmed = health.body.length > 240 ? `${health.body.slice(0, 240)}...` : health.body;
6641
- console.log(chalk18.gray(` Health body: ${trimmed}`));
8158
+ console.log(chalk24.gray(` Health body: ${trimmed}`));
6642
8159
  }
6643
8160
  console.log("");
6644
8161
  }
@@ -6647,55 +8164,55 @@ async function runGatewayHealthCommand(options) {
6647
8164
  const health = await fetchGatewayHealth(target);
6648
8165
  if (!health.ok) {
6649
8166
  console.log("");
6650
- console.log(chalk18.red("Gateway health check failed."));
6651
- console.log(chalk18.gray(` Target: http://${target.host}:${target.port}/health`));
8167
+ console.log(chalk24.red("Gateway health check failed."));
8168
+ console.log(chalk24.gray(` Target: http://${target.host}:${target.port}/health`));
6652
8169
  if (health.status !== void 0) {
6653
- console.log(chalk18.gray(` Status: ${health.status}`));
8170
+ console.log(chalk24.gray(` Status: ${health.status}`));
6654
8171
  }
6655
8172
  console.log("");
6656
8173
  process.exitCode = 1;
6657
8174
  return;
6658
8175
  }
6659
8176
  console.log("");
6660
- console.log(chalk18.green.bold("Gateway healthy"));
6661
- console.log(chalk18.gray(` Target: http://${target.host}:${target.port}/health`));
6662
- console.log(chalk18.gray(` Status: ${health.status}`));
8177
+ console.log(chalk24.green.bold("Gateway healthy"));
8178
+ console.log(chalk24.gray(` Target: http://${target.host}:${target.port}/health`));
8179
+ console.log(chalk24.gray(` Status: ${health.status}`));
6663
8180
  if (health.body && health.body.trim().length > 0) {
6664
8181
  const trimmed = health.body.length > 240 ? `${health.body.slice(0, 240)}...` : health.body;
6665
- console.log(chalk18.gray(` Body: ${trimmed}`));
8182
+ console.log(chalk24.gray(` Body: ${trimmed}`));
6666
8183
  }
6667
8184
  console.log("");
6668
8185
  }
6669
8186
 
6670
8187
  // src/commands/upstream-proxy.ts
6671
8188
  import { spawn as spawn2 } from "child_process";
6672
- import path14 from "path";
6673
- import fs13 from "fs";
6674
- import chalk19 from "chalk";
8189
+ import path19 from "path";
8190
+ import fs17 from "fs";
8191
+ import chalk25 from "chalk";
6675
8192
  var DEFAULT_UPSTREAM_CANDIDATES = (cwd) => {
6676
8193
  const envRepo = process.env.AGDI_UPSTREAM_REPO?.trim();
6677
8194
  const envBin = process.env.AGDI_UPSTREAM_BIN?.trim();
6678
8195
  const candidates = /* @__PURE__ */ new Set();
6679
8196
  if (envBin) {
6680
- candidates.add(path14.resolve(cwd, envBin));
8197
+ candidates.add(path19.resolve(cwd, envBin));
6681
8198
  }
6682
8199
  if (envRepo) {
6683
- candidates.add(path14.resolve(cwd, envRepo, "agdi.mjs"));
6684
- }
6685
- candidates.add(path14.resolve(cwd, "upstream/agdi/agdi.mjs"));
6686
- candidates.add(path14.resolve(cwd, "../upstream/agdi/agdi.mjs"));
6687
- candidates.add(path14.resolve(cwd, "../../upstream/agdi/agdi.mjs"));
6688
- candidates.add(path14.resolve(cwd, "../../../upstream/agdi/agdi.mjs"));
6689
- candidates.add(path14.resolve(cwd, "../Agdi/agdi.mjs"));
6690
- candidates.add(path14.resolve(cwd, "Agdi/agdi.mjs"));
6691
- candidates.add(path14.resolve(cwd, "../../Agdi/agdi.mjs"));
6692
- candidates.add(path14.resolve(cwd, "../../../Agdi/agdi.mjs"));
6693
- candidates.add(path14.resolve(cwd, "../../../../Agdi/agdi.mjs"));
8200
+ candidates.add(path19.resolve(cwd, envRepo, "agdi.mjs"));
8201
+ }
8202
+ candidates.add(path19.resolve(cwd, "upstream/agdi/agdi.mjs"));
8203
+ candidates.add(path19.resolve(cwd, "../upstream/agdi/agdi.mjs"));
8204
+ candidates.add(path19.resolve(cwd, "../../upstream/agdi/agdi.mjs"));
8205
+ candidates.add(path19.resolve(cwd, "../../../upstream/agdi/agdi.mjs"));
8206
+ candidates.add(path19.resolve(cwd, "../Agdi/agdi.mjs"));
8207
+ candidates.add(path19.resolve(cwd, "Agdi/agdi.mjs"));
8208
+ candidates.add(path19.resolve(cwd, "../../Agdi/agdi.mjs"));
8209
+ candidates.add(path19.resolve(cwd, "../../../Agdi/agdi.mjs"));
8210
+ candidates.add(path19.resolve(cwd, "../../../../Agdi/agdi.mjs"));
6694
8211
  return [...candidates];
6695
8212
  };
6696
8213
  function resolveUpstreamBin(cwd = process.cwd()) {
6697
8214
  for (const candidate of DEFAULT_UPSTREAM_CANDIDATES(cwd)) {
6698
- if (fs13.existsSync(candidate)) {
8215
+ if (fs17.existsSync(candidate)) {
6699
8216
  return candidate;
6700
8217
  }
6701
8218
  }
@@ -6719,12 +8236,12 @@ async function runUpstreamProxy(spec) {
6719
8236
  const upstreamBin = resolveUpstreamBin(process.cwd());
6720
8237
  if (!upstreamBin) {
6721
8238
  console.log("");
6722
- console.log(chalk19.red("Upstream Agdi CLI not found."));
6723
- console.log(chalk19.gray("Expected one of:"));
8239
+ console.log(chalk25.red("Upstream Agdi CLI not found."));
8240
+ console.log(chalk25.gray("Expected one of:"));
6724
8241
  for (const candidate of DEFAULT_UPSTREAM_CANDIDATES(process.cwd())) {
6725
- console.log(chalk19.gray(` - ${candidate}`));
8242
+ console.log(chalk25.gray(` - ${candidate}`));
6726
8243
  }
6727
- console.log(chalk19.gray("Set AGDI_UPSTREAM_REPO or AGDI_UPSTREAM_BIN to override."));
8244
+ console.log(chalk25.gray("Set AGDI_UPSTREAM_REPO or AGDI_UPSTREAM_BIN to override."));
6728
8245
  console.log("");
6729
8246
  process.exitCode = 1;
6730
8247
  return;
@@ -6737,7 +8254,7 @@ async function runUpstreamProxy(spec) {
6737
8254
  env: process.env
6738
8255
  });
6739
8256
  child.on("error", (error) => {
6740
- console.log(chalk19.red(`Failed to run upstream command: ${error.message}`));
8257
+ console.log(chalk25.red(`Failed to run upstream command: ${error.message}`));
6741
8258
  process.exitCode = 1;
6742
8259
  resolve3();
6743
8260
  });
@@ -6745,7 +8262,7 @@ async function runUpstreamProxy(spec) {
6745
8262
  if (typeof code === "number") {
6746
8263
  process.exitCode = code;
6747
8264
  } else if (signal) {
6748
- console.log(chalk19.red(`Upstream command terminated by signal: ${signal}`));
8265
+ console.log(chalk25.red(`Upstream command terminated by signal: ${signal}`));
6749
8266
  process.exitCode = 1;
6750
8267
  }
6751
8268
  resolve3();
@@ -6760,7 +8277,6 @@ var UPSTREAM_PROXY_COMMANDS = [
6760
8277
  { name: "reset", description: "Reset upstream local state/config" },
6761
8278
  { name: "uninstall", description: "Uninstall upstream local service/data" },
6762
8279
  { name: "message", description: "Upstream message operations (send/read/manage)" },
6763
- { name: "memory", description: "Upstream memory commands" },
6764
8280
  { name: "agent", aliases: ["agents"], description: "Upstream agent management commands" },
6765
8281
  { name: "status", description: "Upstream gateway status" },
6766
8282
  { name: "health", description: "Upstream health checks" },
@@ -6776,7 +8292,6 @@ var UPSTREAM_PROXY_COMMANDS = [
6776
8292
  { name: "nodes", description: "Upstream node commands" },
6777
8293
  { name: "devices", description: "Upstream device pairing + token management" },
6778
8294
  { name: "node", description: "Upstream node control" },
6779
- { name: "sandbox", description: "Upstream sandbox tooling" },
6780
8295
  { name: "tui", description: "Upstream terminal UI" },
6781
8296
  { name: "cron", description: "Upstream cron scheduler" },
6782
8297
  { name: "dns", description: "Upstream DNS helpers" },
@@ -6784,7 +8299,6 @@ var UPSTREAM_PROXY_COMMANDS = [
6784
8299
  { name: "hooks", description: "Upstream hooks tooling" },
6785
8300
  { name: "webhooks", description: "Upstream webhook helpers" },
6786
8301
  { name: "pairing", description: "Upstream pairing helpers" },
6787
- { name: "plugins", description: "Upstream plugin management" },
6788
8302
  { name: "directory", description: "Upstream directory commands" },
6789
8303
  { name: "security", description: "Upstream security helpers" },
6790
8304
  { name: "skills", description: "Upstream skills management" },
@@ -6807,7 +8321,7 @@ function visibleWidth(input6) {
6807
8321
  }
6808
8322
 
6809
8323
  // src/cli/theme.ts
6810
- import chalk20, { Chalk } from "chalk";
8324
+ import chalk26, { Chalk } from "chalk";
6811
8325
  var LOBSTER_PALETTE = {
6812
8326
  accent: "#00d5ff",
6813
8327
  accentBright: "#38bdf8",
@@ -6821,7 +8335,7 @@ var LOBSTER_PALETTE = {
6821
8335
  var hasForceColor = typeof process.env.FORCE_COLOR === "string" && process.env.FORCE_COLOR.trim().length > 0 && process.env.FORCE_COLOR.trim() !== "0";
6822
8336
  var hasNoColorArg = process.argv.includes("--no-color");
6823
8337
  var disableColor = (process.env.NO_COLOR || hasNoColorArg) && !hasForceColor;
6824
- var baseChalk = disableColor ? new Chalk({ level: 0 }) : chalk20;
8338
+ var baseChalk = disableColor ? new Chalk({ level: 0 }) : chalk26;
6825
8339
  var hex = (value) => baseChalk.hex(value);
6826
8340
  var theme = {
6827
8341
  accent: hex(LOBSTER_PALETTE.accent),
@@ -7091,8 +8605,8 @@ function formatTerminalLink(label, url, opts) {
7091
8605
  }
7092
8606
  return `\x1B]8;;${safeUrl}\x07${safeLabel}\x1B]8;;\x07`;
7093
8607
  }
7094
- function formatDocsLink(path15, label) {
7095
- const trimmed = path15.trim();
8608
+ function formatDocsLink(path20, label) {
8609
+ const trimmed = path20.trim();
7096
8610
  const url = trimmed.startsWith("http") ? trimmed : `${DOCS_ROOT}${trimmed.startsWith("/") ? trimmed : `/${trimmed}`}`;
7097
8611
  return formatTerminalLink(label ?? url, url, { fallback: url });
7098
8612
  }
@@ -7183,7 +8697,7 @@ program.action(async () => {
7183
8697
  await runWizard();
7184
8698
  } catch (error) {
7185
8699
  if (error.name === "ExitPromptError") {
7186
- console.log(chalk21.gray("\n\n\u{1F44B} Goodbye!\n"));
8700
+ console.log(chalk27.gray("\n\n\u{1F44B} Goodbye!\n"));
7187
8701
  try {
7188
8702
  ui.safeExit(0);
7189
8703
  } catch {
@@ -7205,7 +8719,7 @@ program.command("auth").description("Configure API keys").option("--status", "Sh
7205
8719
  }
7206
8720
  } catch (error) {
7207
8721
  if (error.name === "ExitPromptError") {
7208
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
8722
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7209
8723
  try {
7210
8724
  ui.safeExit(0);
7211
8725
  } catch {
@@ -7223,7 +8737,7 @@ program.command("model").alias("models").description("Change AI model").action(a
7223
8737
  await selectModel();
7224
8738
  } catch (error) {
7225
8739
  if (error.name === "ExitPromptError") {
7226
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
8740
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7227
8741
  try {
7228
8742
  ui.safeExit(0);
7229
8743
  } catch {
@@ -7244,7 +8758,7 @@ program.command("chat").description("Start a chat session").action(async () => {
7244
8758
  await startChat();
7245
8759
  } catch (error) {
7246
8760
  if (error.name === "ExitPromptError") {
7247
- console.log(chalk21.gray("\n\n\u{1F44B} Goodbye!\n"));
8761
+ console.log(chalk27.gray("\n\n\u{1F44B} Goodbye!\n"));
7248
8762
  try {
7249
8763
  ui.safeExit(0);
7250
8764
  } catch {
@@ -7262,7 +8776,7 @@ program.command("run [directory]").description("Run a generated project").action
7262
8776
  await runProject(directory);
7263
8777
  } catch (error) {
7264
8778
  if (error.name === "ExitPromptError") {
7265
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
8779
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7266
8780
  try {
7267
8781
  ui.safeExit(0);
7268
8782
  } catch {
@@ -7285,10 +8799,10 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
7285
8799
  }
7286
8800
  const activeConfig = getActiveProvider();
7287
8801
  if (!activeConfig) {
7288
- console.log(chalk21.red("\u274C No API key configured. Run: agdi auth"));
8802
+ console.log(chalk27.red("\u274C No API key configured. Run: agdi auth"));
7289
8803
  return;
7290
8804
  }
7291
- const spinner = ora7("Generating application...").start();
8805
+ const spinner = ora12("Generating application...").start();
7292
8806
  try {
7293
8807
  const llm = createLLMProvider(activeConfig.provider, {
7294
8808
  apiKey: activeConfig.apiKey,
@@ -7297,52 +8811,52 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
7297
8811
  const pm = new ProjectManager();
7298
8812
  pm.create(options.output.replace("./", ""), prompt);
7299
8813
  const { plan, files } = await generateApp(prompt, llm, (step, file) => {
7300
- spinner.text = file ? `${step} ${chalk21.gray(file)}` : step;
8814
+ spinner.text = file ? `${step} ${chalk27.gray(file)}` : step;
7301
8815
  });
7302
8816
  pm.updateFiles(files);
7303
8817
  pm.updateDependencies(plan.dependencies);
7304
8818
  if (options.dryRun || ui.flags.dryRun) {
7305
8819
  spinner.stop();
7306
- console.log(chalk21.cyan.bold("\n\u{1F6A7} DRY RUN SUMMARY\n"));
7307
- console.log(chalk21.gray(`Project: ${plan.name}
8820
+ console.log(chalk27.cyan.bold("\n\u{1F6A7} DRY RUN SUMMARY\n"));
8821
+ console.log(chalk27.gray(`Project: ${plan.name}
7308
8822
  `));
7309
- console.log(chalk21.cyan("Files to be created:"));
7310
- files.forEach((f) => console.log(chalk21.gray(` \u{1F4C4} ${f.path}`)));
7311
- console.log(chalk21.cyan("\nDependencies:"));
7312
- console.log(chalk21.gray(` \u{1F4E6} ${plan.dependencies.join(", ")}`));
7313
- console.log(chalk21.green("\n\u2713 Dry run complete. No files written.\n"));
8823
+ console.log(chalk27.cyan("Files to be created:"));
8824
+ files.forEach((f) => console.log(chalk27.gray(` \u{1F4C4} ${f.path}`)));
8825
+ console.log(chalk27.cyan("\nDependencies:"));
8826
+ console.log(chalk27.gray(` \u{1F4E6} ${plan.dependencies.join(", ")}`));
8827
+ console.log(chalk27.green("\n\u2713 Dry run complete. No files written.\n"));
7314
8828
  return;
7315
8829
  }
7316
8830
  await writeProject(pm.get(), options.output);
7317
- spinner.succeed(chalk21.green("App generated!"));
7318
- console.log(chalk21.gray(`
7319
- \u{1F4C1} Created ${files.length} files in ${chalk21.cyan(options.output)}`));
8831
+ spinner.succeed(chalk27.green("App generated!"));
8832
+ console.log(chalk27.gray(`
8833
+ \u{1F4C1} Created ${files.length} files in ${chalk27.cyan(options.output)}`));
7320
8834
  if (ui.flags.saas || options.saas) {
7321
- console.log(chalk21.cyan("\nSaaS Quick Start:"));
7322
- console.log(chalk21.gray(` 1) cd ${options.output}`));
7323
- console.log(chalk21.gray(" 2) npm install"));
7324
- console.log(chalk21.gray(" 3) cp .env.example .env"));
7325
- console.log(chalk21.gray(" 4) npx prisma generate"));
7326
- console.log(chalk21.gray(" 5) npx prisma db push"));
7327
- console.log(chalk21.gray(" 6) npm run dev\n"));
8835
+ console.log(chalk27.cyan("\nSaaS Quick Start:"));
8836
+ console.log(chalk27.gray(` 1) cd ${options.output}`));
8837
+ console.log(chalk27.gray(" 2) npm install"));
8838
+ console.log(chalk27.gray(" 3) cp .env.example .env"));
8839
+ console.log(chalk27.gray(" 4) npx prisma generate"));
8840
+ console.log(chalk27.gray(" 5) npx prisma db push"));
8841
+ console.log(chalk27.gray(" 6) npm run dev\n"));
7328
8842
  } else {
7329
- console.log(chalk21.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
8843
+ console.log(chalk27.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
7330
8844
  }
7331
8845
  } catch (error) {
7332
8846
  spinner.fail("Generation failed");
7333
8847
  const msg = error instanceof Error ? error.message : String(error);
7334
8848
  if (msg.includes("429") || msg.includes("quota")) {
7335
- console.log(chalk21.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
8849
+ console.log(chalk27.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
7336
8850
  } else if (msg.includes("401") || msg.includes("403")) {
7337
- console.log(chalk21.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
8851
+ console.log(chalk27.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
7338
8852
  } else {
7339
- console.error(chalk21.red("\n" + msg + "\n"));
8853
+ console.error(chalk27.red("\n" + msg + "\n"));
7340
8854
  }
7341
8855
  ui.safeExit(1);
7342
8856
  }
7343
8857
  } catch (error) {
7344
8858
  if (error.name === "ExitPromptError") {
7345
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
8859
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7346
8860
  try {
7347
8861
  ui.safeExit(0);
7348
8862
  } catch {
@@ -7358,11 +8872,11 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
7358
8872
  program.command("config").description("Show configuration").action(async () => {
7359
8873
  const config = loadConfig();
7360
8874
  const active = getActiveProvider();
7361
- console.log(chalk21.cyan.bold("\n\u2699\uFE0F Configuration\n"));
7362
- console.log(chalk21.gray(" Provider: ") + chalk21.cyan(config.defaultProvider || "not set"));
7363
- console.log(chalk21.gray(" Model: ") + chalk21.cyan(config.defaultModel || "not set"));
7364
- console.log(chalk21.gray(" Config: ") + chalk21.gray("~/.agdi/config.json"));
7365
- console.log(chalk21.cyan.bold("\n\u{1F510} API Keys\n"));
8875
+ console.log(chalk27.cyan.bold("\n\u2699\uFE0F Configuration\n"));
8876
+ console.log(chalk27.gray(" Provider: ") + chalk27.cyan(config.defaultProvider || "not set"));
8877
+ console.log(chalk27.gray(" Model: ") + chalk27.cyan(config.defaultModel || "not set"));
8878
+ console.log(chalk27.gray(" Config: ") + chalk27.gray("~/.agdi/config.json"));
8879
+ console.log(chalk27.cyan.bold("\n\u{1F510} API Keys\n"));
7366
8880
  const keys = [
7367
8881
  ["Gemini", config.geminiApiKey],
7368
8882
  ["OpenRouter", config.openrouterApiKey],
@@ -7371,13 +8885,13 @@ program.command("config").description("Show configuration").action(async () => {
7371
8885
  ["DeepSeek", config.deepseekApiKey]
7372
8886
  ];
7373
8887
  for (const [name, key] of keys) {
7374
- const status = key ? chalk21.green("\u2713") : chalk21.gray("\u2717");
8888
+ const status = key ? chalk27.green("\u2713") : chalk27.gray("\u2717");
7375
8889
  console.log(` ${status} ${name}`);
7376
8890
  }
7377
- console.log(chalk21.cyan.bold("\n\u{1F4CA} Telemetry\n"));
8891
+ console.log(chalk27.cyan.bold("\n\u{1F4CA} Telemetry\n"));
7378
8892
  const telemetryEnabled = config.telemetry?.enabled ?? false;
7379
- console.log(` ${telemetryEnabled ? chalk21.green("\u2713 Enabled") : chalk21.gray("\u2717 Disabled")}`);
7380
- console.log(chalk21.gray(" Change with: agdi config telemetry --enable | --disable"));
8893
+ console.log(` ${telemetryEnabled ? chalk27.green("\u2713 Enabled") : chalk27.gray("\u2717 Disabled")}`);
8894
+ console.log(chalk27.gray(" Change with: agdi config telemetry --enable | --disable"));
7381
8895
  console.log("");
7382
8896
  console.log("");
7383
8897
  });
@@ -7385,59 +8899,59 @@ program.command("config:telemetry").alias("telemetry").description("Manage telem
7385
8899
  const { isTelemetryEnabled, setTelemetryConsent, getTelemetryConfig } = await import("./config-K2XM6D4Z.js");
7386
8900
  const { generateSampleEvent, generateSanitizationDemo } = await import("./telemetry-service-76YPOPDM.js");
7387
8901
  if (options.dryRun || options.test) {
7388
- console.log(chalk21.cyan.bold("\n\u{1F50D} TELEMETRY TRANSPARENCY MODE\n"));
7389
- console.log(chalk21.gray("This is exactly what Agdi sends. Notice there is"));
7390
- console.log(chalk21.green.bold("NO source code, file paths, or API keys.\n"));
7391
- console.log(chalk21.white.bold('\u{1F4CA} Sample "Build Failed" Event:\n'));
8902
+ console.log(chalk27.cyan.bold("\n\u{1F50D} TELEMETRY TRANSPARENCY MODE\n"));
8903
+ console.log(chalk27.gray("This is exactly what Agdi sends. Notice there is"));
8904
+ console.log(chalk27.green.bold("NO source code, file paths, or API keys.\n"));
8905
+ console.log(chalk27.white.bold('\u{1F4CA} Sample "Build Failed" Event:\n'));
7392
8906
  const sample = generateSampleEvent();
7393
- console.log(chalk21.gray(JSON.stringify(sample, null, 2)));
7394
- console.log(chalk21.white.bold("\n\u{1F6E1}\uFE0F Sanitization Demo:\n"));
7395
- console.log(chalk21.gray("Even if sensitive data accidentally enters an error message,"));
7396
- console.log(chalk21.gray("our sanitization layer strips it before transmission:\n"));
8907
+ console.log(chalk27.gray(JSON.stringify(sample, null, 2)));
8908
+ console.log(chalk27.white.bold("\n\u{1F6E1}\uFE0F Sanitization Demo:\n"));
8909
+ console.log(chalk27.gray("Even if sensitive data accidentally enters an error message,"));
8910
+ console.log(chalk27.gray("our sanitization layer strips it before transmission:\n"));
7397
8911
  const demo = generateSanitizationDemo();
7398
- console.log(chalk21.red.bold("BEFORE sanitization (never sent):"));
7399
- console.log(chalk21.gray(JSON.stringify({
8912
+ console.log(chalk27.red.bold("BEFORE sanitization (never sent):"));
8913
+ console.log(chalk27.gray(JSON.stringify({
7400
8914
  errorCode: demo.before.errorCode,
7401
8915
  feedback: demo.before.feedback
7402
8916
  }, null, 2)));
7403
- console.log(chalk21.green.bold("\nAFTER sanitization (what we actually send):"));
7404
- console.log(chalk21.gray(JSON.stringify({
8917
+ console.log(chalk27.green.bold("\nAFTER sanitization (what we actually send):"));
8918
+ console.log(chalk27.gray(JSON.stringify({
7405
8919
  errorCode: demo.after.errorCode,
7406
8920
  feedback: demo.after.feedback
7407
8921
  }, null, 2)));
7408
- console.log(chalk21.cyan("\n\u2705 Your code and secrets are NEVER transmitted."));
7409
- console.log(chalk21.gray(" Learn more: https://agdi-dev.vercel.app/privacy\n"));
8922
+ console.log(chalk27.cyan("\n\u2705 Your code and secrets are NEVER transmitted."));
8923
+ console.log(chalk27.gray(" Learn more: https://agdi-dev.vercel.app/privacy\n"));
7410
8924
  return;
7411
8925
  }
7412
8926
  if (options.enable) {
7413
8927
  setTelemetryConsent(true);
7414
- console.log(chalk21.green("\n\u2705 Telemetry enabled"));
7415
- console.log(chalk21.gray(" We collect: success/fail, error types, model used"));
7416
- console.log(chalk21.gray(" We NEVER collect: source code, API keys, file paths"));
7417
- console.log(chalk21.gray(" Verify anytime: agdi config telemetry --dry-run\n"));
8928
+ console.log(chalk27.green("\n\u2705 Telemetry enabled"));
8929
+ console.log(chalk27.gray(" We collect: success/fail, error types, model used"));
8930
+ console.log(chalk27.gray(" We NEVER collect: source code, API keys, file paths"));
8931
+ console.log(chalk27.gray(" Verify anytime: agdi config telemetry --dry-run\n"));
7418
8932
  } else if (options.disable) {
7419
8933
  setTelemetryConsent(false);
7420
- console.log(chalk21.yellow("\n\u{1F4CA} Telemetry disabled"));
7421
- console.log(chalk21.gray(" You can re-enable anytime with: agdi config telemetry --enable\n"));
8934
+ console.log(chalk27.yellow("\n\u{1F4CA} Telemetry disabled"));
8935
+ console.log(chalk27.gray(" You can re-enable anytime with: agdi config telemetry --enable\n"));
7422
8936
  } else {
7423
8937
  const config = getTelemetryConfig();
7424
- console.log(chalk21.cyan.bold("\n\u{1F4CA} Telemetry Status\n"));
7425
- console.log(chalk21.gray(" Enabled: ") + (config.enabled ? chalk21.green("Yes") : chalk21.gray("No")));
7426
- console.log(chalk21.gray(" Consent: ") + (config.consentAsked ? chalk21.green("Asked") : chalk21.gray("Not asked")));
8938
+ console.log(chalk27.cyan.bold("\n\u{1F4CA} Telemetry Status\n"));
8939
+ console.log(chalk27.gray(" Enabled: ") + (config.enabled ? chalk27.green("Yes") : chalk27.gray("No")));
8940
+ console.log(chalk27.gray(" Consent: ") + (config.consentAsked ? chalk27.green("Asked") : chalk27.gray("Not asked")));
7427
8941
  if (config.anonymousId) {
7428
- console.log(chalk21.gray(" ID: ") + chalk21.gray(config.anonymousId.slice(0, 8) + "..."));
8942
+ console.log(chalk27.gray(" ID: ") + chalk27.gray(config.anonymousId.slice(0, 8) + "..."));
7429
8943
  }
7430
8944
  console.log("");
7431
- console.log(chalk21.gray(" Enable: agdi config telemetry --enable"));
7432
- console.log(chalk21.gray(" Disable: agdi config telemetry --disable"));
7433
- console.log(chalk21.gray(" Verify: agdi config telemetry --dry-run\n"));
8945
+ console.log(chalk27.gray(" Enable: agdi config telemetry --enable"));
8946
+ console.log(chalk27.gray(" Disable: agdi config telemetry --disable"));
8947
+ console.log(chalk27.gray(" Verify: agdi config telemetry --dry-run\n"));
7434
8948
  }
7435
8949
  });
7436
8950
  program.command("doctor").alias("doc").description("Run self-diagnosis checks").action(async () => {
7437
8951
  try {
7438
8952
  await runDoctor();
7439
8953
  } catch (error) {
7440
- console.error(chalk21.red("Diagnostic failed: " + error));
8954
+ console.error(chalk27.red("Diagnostic failed: " + error));
7441
8955
  ui.safeExit(1);
7442
8956
  }
7443
8957
  });
@@ -7446,7 +8960,7 @@ program.command("features").description("List or sync upstream Agdi feature pack
7446
8960
  await runFeaturesCommand(options);
7447
8961
  } catch (error) {
7448
8962
  if (error.name === "ExitPromptError") {
7449
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
8963
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7450
8964
  try {
7451
8965
  ui.safeExit(0);
7452
8966
  } catch {
@@ -7465,7 +8979,7 @@ channelsCommand.command("list").description("List discovered channel extensions
7465
8979
  await runChannelsListCommand();
7466
8980
  } catch (error) {
7467
8981
  if (error.name === "ExitPromptError") {
7468
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
8982
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7469
8983
  try {
7470
8984
  ui.safeExit(0);
7471
8985
  } catch {
@@ -7483,7 +8997,7 @@ channelsCommand.command("send").description("Send a message through a supported
7483
8997
  await runChannelsSendCommand(options);
7484
8998
  } catch (error) {
7485
8999
  const message = error instanceof Error ? error.message : String(error);
7486
- console.log(chalk21.red(`
9000
+ console.log(chalk27.red(`
7487
9001
  ${message}
7488
9002
  `));
7489
9003
  try {
@@ -7497,7 +9011,7 @@ channelsCommand.command("doctor").description("Validate tokens/webhooks/session
7497
9011
  await runChannelsDoctorCommand();
7498
9012
  } catch (error) {
7499
9013
  const message = error instanceof Error ? error.message : String(error);
7500
- console.log(chalk21.red(`
9014
+ console.log(chalk27.red(`
7501
9015
  ${message}
7502
9016
  `));
7503
9017
  try {
@@ -7526,7 +9040,7 @@ function registerUpstreamProxyCommand(spec) {
7526
9040
  if (requestedNames.some((name) => existingNames.has(name))) {
7527
9041
  return;
7528
9042
  }
7529
- const command = program.command(`${spec.name} [args...]`).description(`${spec.description} ${chalk21.gray("(proxied to upstream Agdi)")}`).helpOption(false).allowUnknownOption(true).allowExcessArguments(true).action(async () => {
9043
+ const command = program.command(`${spec.name} [args...]`).description(`${spec.description} ${chalk27.gray("(proxied to upstream Agdi)")}`).helpOption(false).allowUnknownOption(true).allowExcessArguments(true).action(async () => {
7530
9044
  await runUpstreamProxy(spec);
7531
9045
  });
7532
9046
  for (const alias of spec.aliases ?? []) {
@@ -7536,14 +9050,135 @@ function registerUpstreamProxyCommand(spec) {
7536
9050
  for (const spec of UPSTREAM_PROXY_COMMANDS) {
7537
9051
  registerUpstreamProxyCommand(spec);
7538
9052
  }
7539
- program.command("squad [prompt]").alias("s").description("\u{1F9B8} Autonomous multi-agent app builder").option("-d, --deploy", "Auto-deploy to Vercel after build").option("-o, --output <dir>", "Output directory", "./").option("-v, --verbose", "Show detailed agent logs", true).action(async (prompt, options) => {
9053
+ program.command("audit").description("\u{1F50D} Scan codebase for production-readiness issues").option("--prod", "Strict production mode (errors on a11y/security/test gaps)").option("--fix", "Auto-fix issues using the QA agent").option("-o, --output <dir>", "Directory to scan", "./").action(async (options) => {
9054
+ try {
9055
+ const result = await runAuditCommand({
9056
+ prod: options.prod,
9057
+ fix: options.fix,
9058
+ output: options.output
9059
+ });
9060
+ if (!result.passed && options.prod) {
9061
+ ui.safeExit(1);
9062
+ }
9063
+ } catch (error) {
9064
+ if (error.name === "ExitPromptError") {
9065
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
9066
+ try {
9067
+ ui.safeExit(0);
9068
+ } catch {
9069
+ }
9070
+ return;
9071
+ }
9072
+ throw error;
9073
+ }
9074
+ });
9075
+ program.command("sandbox <url>").description("\u{1F9EA} Read live API docs and extract endpoints").option("--test", "Ping discovered endpoints to verify response shape").option("--inject", "Prepare endpoint summary for Squad context injection").option("--api-key <key>", "API key for authenticated endpoints").action(async (url, options) => {
9076
+ try {
9077
+ await runSandboxCommand(url, {
9078
+ test: options.test,
9079
+ inject: options.inject,
9080
+ apiKey: options.apiKey
9081
+ });
9082
+ } catch (error) {
9083
+ if (error.name === "ExitPromptError") {
9084
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
9085
+ try {
9086
+ ui.safeExit(0);
9087
+ } catch {
9088
+ }
9089
+ return;
9090
+ }
9091
+ throw error;
9092
+ }
9093
+ });
9094
+ program.command("visual-test").alias("vt").description("\u{1F4F8} Run visual regression tests on your app").option("--routes <routes>", "Comma-separated routes to test (default: auto-discover)").option("--update", "Update baseline screenshots").option("--port <port>", "Dev server port", "3000").option("-o, --output <dir>", "Workspace directory", "./").action(async (options) => {
9095
+ try {
9096
+ await runVisualTestCommand({
9097
+ routes: options.routes,
9098
+ update: options.update,
9099
+ port: parseInt(options.port, 10),
9100
+ output: options.output
9101
+ });
9102
+ } catch (error) {
9103
+ if (error.name === "ExitPromptError") {
9104
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
9105
+ try {
9106
+ ui.safeExit(0);
9107
+ } catch {
9108
+ }
9109
+ return;
9110
+ }
9111
+ throw error;
9112
+ }
9113
+ });
9114
+ program.command("memory").alias("mem").description("\u{1F9E0} Analyze git history for project context").option("--depth <n>", "Number of commits to analyze", "30").option("--inject", "Prepare context for Squad injection").option("-o, --output <dir>", "Workspace directory", "./").action(async (options) => {
9115
+ try {
9116
+ await runMemoryCommand({
9117
+ depth: parseInt(options.depth, 10),
9118
+ inject: options.inject,
9119
+ output: options.output
9120
+ });
9121
+ } catch (error) {
9122
+ if (error.name === "ExitPromptError") {
9123
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
9124
+ try {
9125
+ ui.safeExit(0);
9126
+ } catch {
9127
+ }
9128
+ return;
9129
+ }
9130
+ throw error;
9131
+ }
9132
+ });
9133
+ program.command("deps").description("\u{1F6E1}\uFE0F Scan dependencies for vulnerabilities").option("--full", "Full audit including outdated packages").option("--fix", "Auto-fix vulnerabilities with npm audit fix").option("-o, --output <dir>", "Workspace directory", "./").action(async (options) => {
9134
+ try {
9135
+ const result = await runDepsScanCommand({
9136
+ full: options.full,
9137
+ fix: options.fix,
9138
+ output: options.output
9139
+ });
9140
+ if (!result.passed) {
9141
+ ui.safeExit(1);
9142
+ }
9143
+ } catch (error) {
9144
+ if (error.name === "ExitPromptError") {
9145
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
9146
+ try {
9147
+ ui.safeExit(0);
9148
+ } catch {
9149
+ }
9150
+ return;
9151
+ }
9152
+ throw error;
9153
+ }
9154
+ });
9155
+ program.command("plugins").alias("plug").description("\u{1F50C} Manage Agdi plugins and extensions").option("--install <name>", "Install a plugin from npm").option("--init", "Create a new plugin template").option("-o, --output <dir>", "Workspace directory", "./").action(async (options) => {
9156
+ try {
9157
+ await runPluginsCommand({
9158
+ install: options.install,
9159
+ init: options.init,
9160
+ output: options.output
9161
+ });
9162
+ } catch (error) {
9163
+ if (error.name === "ExitPromptError") {
9164
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
9165
+ try {
9166
+ ui.safeExit(0);
9167
+ } catch {
9168
+ }
9169
+ return;
9170
+ }
9171
+ throw error;
9172
+ }
9173
+ });
9174
+ program.command("squad [prompt]").alias("s").description("\u{1F9B8} Autonomous multi-agent app builder").option("-d, --deploy", "Auto-deploy to Vercel after build").option("-o, --output <dir>", "Output directory", "./").option("-v, --verbose", "Show detailed agent logs", true).option("--breakpoints", "Pause after each agent task for review (AI Breakpoints)").action(async (prompt, options) => {
7540
9175
  try {
7541
9176
  if (needsOnboarding()) {
7542
9177
  await runOnboarding();
7543
9178
  }
7544
9179
  const activeConfig = getActiveProvider();
7545
9180
  if (!activeConfig) {
7546
- console.log(chalk21.red("\u274C No API key configured. Run: agdi auth"));
9181
+ console.log(chalk27.red("\u274C No API key configured. Run: agdi auth"));
7547
9182
  return;
7548
9183
  }
7549
9184
  const llm = createLLMProvider(activeConfig.provider, {
@@ -7553,11 +9188,12 @@ program.command("squad [prompt]").alias("s").description("\u{1F9B8} Autonomous m
7553
9188
  await runSquadCommand(prompt, llm, {
7554
9189
  deploy: options.deploy,
7555
9190
  output: options.output,
7556
- verbose: options.verbose
9191
+ verbose: options.verbose,
9192
+ breakpoints: options.breakpoints
7557
9193
  });
7558
9194
  } catch (error) {
7559
9195
  if (error.name === "ExitPromptError") {
7560
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
9196
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7561
9197
  try {
7562
9198
  ui.safeExit(0);
7563
9199
  } catch {
@@ -7585,7 +9221,7 @@ program.command("replay <runId>").description("\u{1F501} Replay a previous squad
7585
9221
  }
7586
9222
  const activeConfig = getActiveProvider();
7587
9223
  if (!activeConfig) {
7588
- console.log(chalk21.red("\u274C No API key configured. Run: agdi auth"));
9224
+ console.log(chalk27.red("\u274C No API key configured. Run: agdi auth"));
7589
9225
  return;
7590
9226
  }
7591
9227
  const llm = createLLMProvider(activeConfig.provider, {
@@ -7599,7 +9235,7 @@ program.command("replay <runId>").description("\u{1F501} Replay a previous squad
7599
9235
  });
7600
9236
  } catch (error) {
7601
9237
  if (error.name === "ExitPromptError") {
7602
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
9238
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7603
9239
  try {
7604
9240
  ui.safeExit(0);
7605
9241
  } catch {
@@ -7617,7 +9253,7 @@ program.command("import <url>").alias("i").description("\u{1F4E6} Import a GitHu
7617
9253
  await runImportCommand(url, options.output);
7618
9254
  } catch (error) {
7619
9255
  if (error.name === "ExitPromptError") {
7620
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
9256
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7621
9257
  try {
7622
9258
  ui.safeExit(0);
7623
9259
  } catch {
@@ -7631,7 +9267,7 @@ program.command("wizard").alias("w").description("\u{1F9D9} Start the Agdi Setup
7631
9267
  try {
7632
9268
  await runWizard();
7633
9269
  } catch (error) {
7634
- console.log(chalk21.gray("\n\n\u{1F44B} Cancelled.\n"));
9270
+ console.log(chalk27.gray("\n\n\u{1F44B} Cancelled.\n"));
7635
9271
  try {
7636
9272
  ui.safeExit(0);
7637
9273
  } catch {