agdi 3.1.2 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +100 -64
  2. package/dist/index.js +385 -379
  3. package/package.json +13 -5
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
 
7
7
  // src/index.ts
8
8
  import { Command } from "commander";
9
- import chalk15 from "chalk";
9
+ import chalk14 from "chalk";
10
10
  import ora6 from "ora";
11
11
 
12
12
  // src/core/llm/index.ts
@@ -449,8 +449,8 @@ async function smartConfirm(message, defaultValue = false) {
449
449
  console.warn(chalk.yellow("\u26A0\uFE0F Non-interactive session detected. Use --yes to approve actions."));
450
450
  return false;
451
451
  }
452
- const { confirm: confirm2 } = await import("@inquirer/prompts");
453
- return confirm2({ message, default: defaultValue });
452
+ const { confirm } = await import("@inquirer/prompts");
453
+ return confirm({ message, default: defaultValue });
454
454
  }
455
455
  async function smartSelect(message, choices, defaultValue) {
456
456
  if (!process.stdout.isTTY || flags.headless) {
@@ -460,8 +460,8 @@ async function smartSelect(message, choices, defaultValue) {
460
460
  }
461
461
  return result || null;
462
462
  }
463
- const { select: select4 } = await import("@inquirer/prompts");
464
- return select4({ message, choices });
463
+ const { select: select3 } = await import("@inquirer/prompts");
464
+ return select3({ message, choices });
465
465
  }
466
466
  var ui = {
467
467
  renderBanner,
@@ -1782,8 +1782,8 @@ async function runOnboarding() {
1782
1782
  console.log(chalk7.gray("We collect anonymous error logs (like a ") + chalk7.yellow("'Check Engine'") + chalk7.gray(" light)"));
1783
1783
  console.log(chalk7.gray("to fix bugs faster. We ") + chalk7.green.bold("never") + chalk7.gray(" see your code or keys.\n"));
1784
1784
  console.log(chalk7.dim("Verify anytime: agdi config telemetry --dry-run\n"));
1785
- const { confirm: confirm2 } = await import("@inquirer/prompts");
1786
- const enableTelemetry = await confirm2({
1785
+ const { confirm } = await import("@inquirer/prompts");
1786
+ const enableTelemetry = await confirm({
1787
1787
  message: "Enable crash reporting? (helps us fix bugs)",
1788
1788
  default: false
1789
1789
  });
@@ -2749,6 +2749,8 @@ Requirements:
2749
2749
  // src/agents/core/qa-agent.ts
2750
2750
  import { exec } from "child_process";
2751
2751
  import { promisify } from "util";
2752
+ import * as fs3 from "fs/promises";
2753
+ import * as path3 from "path";
2752
2754
  var execAsync = promisify(exec);
2753
2755
  var QA_SYSTEM_PROMPT = `You are a SENIOR QA ENGINEER and DEBUGGER with expertise in:
2754
2756
  - TypeScript/JavaScript debugging
@@ -2758,10 +2760,10 @@ var QA_SYSTEM_PROMPT = `You are a SENIOR QA ENGINEER and DEBUGGER with expertise
2758
2760
  - ESLint / TypeScript errors
2759
2761
 
2760
2762
  Your responsibilities:
2761
- 1. Run npm build and analyze errors
2762
- 2. Diagnose the root cause of failures
2763
- 3. Generate fixes for the errors
2764
- 4. Re-run until the build passes
2763
+ 1. Analyze build/test errors
2764
+ 2. Read the provided source code context
2765
+ 3. Diagnose the root cause (e.g., missing prop, type mismatch, bad import)
2766
+ 4. Generate SURGICAL fixes
2765
2767
 
2766
2768
  When fixing errors, you MUST:
2767
2769
  1. Quote the exact error message
@@ -2776,10 +2778,13 @@ import React from 'react';
2776
2778
  // ... fixed code
2777
2779
  \`\`\`
2778
2780
 
2779
- Sources:
2780
- - If you reference any external fixes or docs, list their URLs in a "Sources:" section at the end.
2781
+ IMPORTANT:
2782
+ - Do NOT generate "placeholder" fixes.
2783
+ - If an import is missing, make sure the file actually exists or fix the path.
2784
+ - If a type is missing, check where it is defined.
2781
2785
 
2782
- Be surgical. Only change what's broken.`;
2786
+ Sources:
2787
+ - If you reference any external fixes or docs, list their URLs in a "Sources:" section at the end.`;
2783
2788
  var QAAgent = class extends BaseAgent {
2784
2789
  maxFixAttempts = 3;
2785
2790
  constructor(llm, options = {}) {
@@ -2796,8 +2801,8 @@ var QAAgent = class extends BaseAgent {
2796
2801
  try {
2797
2802
  const { stdout, stderr } = await execAsync("npm run build", {
2798
2803
  cwd,
2799
- timeout: 12e4
2800
- // 2 minutes
2804
+ timeout: 3e5
2805
+ // 5 minutes
2801
2806
  });
2802
2807
  return {
2803
2808
  success: true,
@@ -2819,7 +2824,7 @@ var QAAgent = class extends BaseAgent {
2819
2824
  try {
2820
2825
  const { stdout, stderr } = await execAsync("npm test -- --run", {
2821
2826
  cwd,
2822
- timeout: 12e4
2827
+ timeout: 3e5
2823
2828
  });
2824
2829
  return { success: true, output: stdout + stderr };
2825
2830
  } catch (error) {
@@ -2830,27 +2835,61 @@ var QAAgent = class extends BaseAgent {
2830
2835
  };
2831
2836
  }
2832
2837
  }
2838
+ /**
2839
+ * Extract file paths from error log
2840
+ */
2841
+ extractFilePaths(errorLog) {
2842
+ const paths = /* @__PURE__ */ new Set();
2843
+ const regex = /((?:src|app|pages|lib|components)\/[a-zA-Z0-9_\-\/\.]+\.(?:ts|tsx|js|jsx|json))/g;
2844
+ const matches = errorLog.matchAll(regex);
2845
+ for (const match of matches) {
2846
+ paths.add(match[1]);
2847
+ }
2848
+ return paths;
2849
+ }
2850
+ /**
2851
+ * Read file content for context
2852
+ */
2853
+ async getFileContext(cwd, paths) {
2854
+ if (paths.size === 0) return "";
2855
+ const contextParts = [];
2856
+ for (const filePath of paths) {
2857
+ try {
2858
+ const fullPath = path3.join(cwd, filePath);
2859
+ const content = await fs3.readFile(fullPath, "utf-8");
2860
+ contextParts.push(`// File: ${filePath}
2861
+ ${content}`);
2862
+ } catch (e) {
2863
+ }
2864
+ }
2865
+ return contextParts.join("\n\n");
2866
+ }
2833
2867
  /**
2834
2868
  * Diagnose build errors and generate fixes
2835
2869
  */
2836
- async diagnoseAndFix(errorOutput) {
2837
- this.log("Analyzing errors...");
2870
+ async diagnoseAndFix(cwd, errorOutput) {
2871
+ this.log("Analyzing errors and reading source files...");
2872
+ const problematicFiles = this.extractFilePaths(errorOutput);
2873
+ this.log(`Identified ${problematicFiles.size} problematic files: ${Array.from(problematicFiles).join(", ")}`);
2874
+ const fileContext = await this.getFileContext(cwd, problematicFiles);
2838
2875
  const response = await this.think(`
2839
2876
  The following build/test errors occurred:
2840
2877
 
2841
2878
  \`\`\`
2842
- ${errorOutput.slice(0, 4e3)}
2879
+ ${errorOutput.slice(0, 5e3)}
2843
2880
  \`\`\`
2844
2881
 
2845
- Analyze these errors and provide fixes.
2846
- For each error:
2847
- 1. Quote the exact error
2848
- 2. Identify the file and line
2849
- 3. Explain the fix
2850
- 4. Provide the corrected code
2882
+ Here is the content of the files mentioned in the errors:
2851
2883
 
2852
- Only fix what's broken. Keep the rest intact.
2853
- `);
2884
+ \`\`\`typescript
2885
+ ${fileContext.slice(0, 2e4)}
2886
+ \`\`\`
2887
+
2888
+ Analyze these errors and the provided code.
2889
+ Diagnose the root cause and provide SURGICAL fixes.
2890
+ For example, if a property is missing in a component prop type, fix the interface.
2891
+ If an import is wrong, correct it.
2892
+ `);
2854
2893
  const fixes = this.extractCodeBlocks(response);
2855
2894
  this.log(`Generated ${fixes.length} fixes`, fixes.length > 0 ? "success" : "warn");
2856
2895
  return fixes;
@@ -2882,7 +2921,7 @@ Only fix what's broken. Keep the rest intact.
2882
2921
  commandOutputs
2883
2922
  };
2884
2923
  } else {
2885
- const testFixes = await this.diagnoseAndFix(testResult.output);
2924
+ const testFixes = await this.diagnoseAndFix(cwd, testResult.output);
2886
2925
  allFixes.push(...testFixes);
2887
2926
  if (testFixes.length === 0) {
2888
2927
  errors.push(`Tests failed but couldn't generate fixes: ${testResult.output.slice(0, 500)}`);
@@ -2892,12 +2931,22 @@ Only fix what's broken. Keep the rest intact.
2892
2931
  }
2893
2932
  }
2894
2933
  this.log("Build failed, diagnosing...", "warn");
2895
- const fixes = await this.diagnoseAndFix(buildResult.output);
2934
+ const fixes = await this.diagnoseAndFix(cwd, buildResult.output);
2896
2935
  if (fixes.length === 0) {
2897
2936
  errors.push(`Build failed but couldn't generate fixes: ${buildResult.output.slice(0, 500)}`);
2898
2937
  break;
2899
2938
  }
2900
2939
  allFixes.push(...fixes);
2940
+ for (const fix of fixes) {
2941
+ try {
2942
+ const fullPath = path3.join(cwd, fix.path);
2943
+ await fs3.mkdir(path3.dirname(fullPath), { recursive: true });
2944
+ await fs3.writeFile(fullPath, fix.content, "utf-8");
2945
+ this.log(` Applied fix to: ${fix.path}`, "info");
2946
+ } catch (e) {
2947
+ this.log(`Failed to apply fix to ${fix.path}: ${e}`, "error");
2948
+ }
2949
+ }
2901
2950
  }
2902
2951
  return {
2903
2952
  success: false,
@@ -2933,13 +2982,13 @@ Only fix what's broken. Keep the rest intact.
2933
2982
  // src/agents/core/devops-agent.ts
2934
2983
  import { exec as exec2 } from "child_process";
2935
2984
  import { promisify as promisify2 } from "util";
2936
- import fs3 from "fs/promises";
2937
- import path3 from "path";
2985
+ import fs4 from "fs/promises";
2986
+ import path4 from "path";
2938
2987
  var execAsync2 = promisify2(exec2);
2939
2988
  async function detectBuildOutputDir(cwd) {
2940
2989
  try {
2941
- const pkgPath = path3.join(cwd, "package.json");
2942
- const raw = await fs3.readFile(pkgPath, "utf-8");
2990
+ const pkgPath = path4.join(cwd, "package.json");
2991
+ const raw = await fs4.readFile(pkgPath, "utf-8");
2943
2992
  const pkg = JSON.parse(raw);
2944
2993
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
2945
2994
  if (deps["next"]) return ".next";
@@ -3270,8 +3319,8 @@ URL: ${deployResult.url || "See dashboard"}`,
3270
3319
  };
3271
3320
 
3272
3321
  // src/agents/core/squad-orchestrator.ts
3273
- import * as fs4 from "fs/promises";
3274
- import * as path4 from "path";
3322
+ import * as fs5 from "fs/promises";
3323
+ import * as path5 from "path";
3275
3324
  import crypto2 from "crypto";
3276
3325
  var SquadOrchestrator = class {
3277
3326
  manager;
@@ -3409,7 +3458,7 @@ ${qaResult.errors?.join("\n")}`,
3409
3458
  this.log("\u2705 Codebase is stable.", "success");
3410
3459
  }
3411
3460
  let deploymentUrl;
3412
- if (this.config.autoDeploy) {
3461
+ if (healthy && this.config.autoDeploy) {
3413
3462
  this.log("\u{1F680} Phase 4: Deployment", "phase");
3414
3463
  const devopsTask = this.tasks.find((t) => t.assignee === "devops");
3415
3464
  if (devopsTask) {
@@ -3471,7 +3520,15 @@ ${qaResult.errors?.join("\n")}`,
3471
3520
  (t) => t.assignee !== "qa" && t.assignee !== "devops"
3472
3521
  );
3473
3522
  const pending = /* @__PURE__ */ new Map();
3474
- coreTasks.forEach((task) => pending.set(task.id, task));
3523
+ coreTasks.forEach((task) => {
3524
+ if (!this.completedTasks.has(task.id)) {
3525
+ pending.set(task.id, task);
3526
+ }
3527
+ });
3528
+ if (pending.size === 0) {
3529
+ this.log("No new tasks to execute.", "info");
3530
+ return;
3531
+ }
3475
3532
  const runTask = async (task) => {
3476
3533
  const taskStart = Date.now();
3477
3534
  const result = await this.executeTask(task);
@@ -3561,24 +3618,24 @@ ${qaResult.errors?.join("\n")}`,
3561
3618
  */
3562
3619
  async writeFiles(files, filesCreated, filesModified) {
3563
3620
  for (const file of files) {
3564
- const fullPath = path4.join(this.config.workspaceRoot, file.path);
3565
- const dir = path4.dirname(fullPath);
3621
+ const fullPath = path5.join(this.config.workspaceRoot, file.path);
3622
+ const dir = path5.dirname(fullPath);
3566
3623
  try {
3567
- await fs4.mkdir(dir, { recursive: true });
3624
+ await fs5.mkdir(dir, { recursive: true });
3568
3625
  let existed = false;
3569
3626
  let beforeContent = null;
3570
3627
  try {
3571
- await fs4.access(fullPath);
3628
+ await fs5.access(fullPath);
3572
3629
  existed = true;
3573
- beforeContent = await fs4.readFile(fullPath, "utf-8");
3630
+ beforeContent = await fs5.readFile(fullPath, "utf-8");
3574
3631
  } catch {
3575
3632
  existed = false;
3576
3633
  }
3577
- await fs4.writeFile(fullPath, file.content, "utf-8");
3634
+ await fs5.writeFile(fullPath, file.content, "utf-8");
3578
3635
  const afterContent = file.content;
3579
- const outputPath = path4.join(this.runDir, "outputs", file.path);
3580
- await fs4.mkdir(path4.dirname(outputPath), { recursive: true });
3581
- await fs4.writeFile(outputPath, file.content, "utf-8");
3636
+ const outputPath = path5.join(this.runDir, "outputs", file.path);
3637
+ await fs5.mkdir(path5.dirname(outputPath), { recursive: true });
3638
+ await fs5.writeFile(outputPath, file.content, "utf-8");
3582
3639
  const beforeHash = beforeContent ? this.hashContent(beforeContent) : null;
3583
3640
  const afterHash = this.hashContent(afterContent);
3584
3641
  await this.appendTrace("file_write", {
@@ -3605,17 +3662,17 @@ ${qaResult.errors?.join("\n")}`,
3605
3662
  }
3606
3663
  async initializeRun(userPrompt) {
3607
3664
  this.runId = uuidv42();
3608
- this.runDir = path4.join(this.config.workspaceRoot, "runs", this.runId);
3609
- this.tracePath = path4.join(this.runDir, "trace.jsonl");
3610
- this.reportPath = path4.join(this.runDir, "report.md");
3611
- this.sourcesPath = path4.join(this.runDir, "sources.json");
3612
- this.snapshotManifestPath = path4.join(this.runDir, "snapshot", "manifest.json");
3613
- this.runConfigPath = path4.join(this.runDir, "run.json");
3614
- const outputsDir = path4.join(this.runDir, "outputs");
3615
- await fs4.mkdir(this.runDir, { recursive: true });
3616
- await fs4.mkdir(path4.join(this.runDir, "snapshot"), { recursive: true });
3617
- await fs4.mkdir(outputsDir, { recursive: true });
3618
- await fs4.writeFile(this.runConfigPath, JSON.stringify({
3665
+ this.runDir = path5.join(this.config.workspaceRoot, "runs", this.runId);
3666
+ this.tracePath = path5.join(this.runDir, "trace.jsonl");
3667
+ this.reportPath = path5.join(this.runDir, "report.md");
3668
+ this.sourcesPath = path5.join(this.runDir, "sources.json");
3669
+ this.snapshotManifestPath = path5.join(this.runDir, "snapshot", "manifest.json");
3670
+ this.runConfigPath = path5.join(this.runDir, "run.json");
3671
+ const outputsDir = path5.join(this.runDir, "outputs");
3672
+ await fs5.mkdir(this.runDir, { recursive: true });
3673
+ await fs5.mkdir(path5.join(this.runDir, "snapshot"), { recursive: true });
3674
+ await fs5.mkdir(outputsDir, { recursive: true });
3675
+ await fs5.writeFile(this.runConfigPath, JSON.stringify({
3619
3676
  runId: this.runId,
3620
3677
  prompt: userPrompt,
3621
3678
  config: {
@@ -3650,7 +3707,7 @@ ${qaResult.errors?.join("\n")}`,
3650
3707
  errors: result.errors,
3651
3708
  deploymentUrl: result.deploymentUrl
3652
3709
  });
3653
- await fs4.writeFile(this.sourcesPath, JSON.stringify(Array.from(this.sources).sort(), null, 2), "utf-8");
3710
+ await fs5.writeFile(this.sourcesPath, JSON.stringify(Array.from(this.sources).sort(), null, 2), "utf-8");
3654
3711
  const diffSummary = await this.generateDiffReport();
3655
3712
  const reportLines = [
3656
3713
  `# Agdi Squad Run Report`,
@@ -3676,13 +3733,13 @@ ${qaResult.errors?.join("\n")}`,
3676
3733
  result.errors.length ? `## Errors` : void 0,
3677
3734
  ...result.errors.length ? result.errors.map((e) => `- ${e}`) : []
3678
3735
  ].filter(Boolean).join("\n");
3679
- await fs4.writeFile(this.reportPath, reportLines, "utf-8");
3736
+ await fs5.writeFile(this.reportPath, reportLines, "utf-8");
3680
3737
  }
3681
3738
  async appendTrace(event, data) {
3682
3739
  const entry = { timestamp: (/* @__PURE__ */ new Date()).toISOString(), event, data };
3683
3740
  this.traceBuffer.push(entry);
3684
3741
  if (!this.tracePath) return;
3685
- await fs4.appendFile(this.tracePath, JSON.stringify(entry) + "\n", "utf-8");
3742
+ await fs5.appendFile(this.tracePath, JSON.stringify(entry) + "\n", "utf-8");
3686
3743
  }
3687
3744
  hashContent(content) {
3688
3745
  return crypto2.createHash("sha256").update(content).digest("hex");
@@ -3697,15 +3754,15 @@ ${qaResult.errors?.join("\n")}`,
3697
3754
  const ignore = /* @__PURE__ */ new Set(["node_modules", ".git", "runs", "dist", "build", ".next"]);
3698
3755
  const manifest = [];
3699
3756
  const walk = async (dir) => {
3700
- const entries = await fs4.readdir(dir, { withFileTypes: true });
3757
+ const entries = await fs5.readdir(dir, { withFileTypes: true });
3701
3758
  for (const entry of entries) {
3702
3759
  if (ignore.has(entry.name)) continue;
3703
- const fullPath = path4.join(dir, entry.name);
3704
- const relPath = path4.relative(snapshotRoot, fullPath);
3760
+ const fullPath = path5.join(dir, entry.name);
3761
+ const relPath = path5.relative(snapshotRoot, fullPath);
3705
3762
  if (entry.isDirectory()) {
3706
3763
  await walk(fullPath);
3707
3764
  } else if (entry.isFile()) {
3708
- const buffer = await fs4.readFile(fullPath);
3765
+ const buffer = await fs5.readFile(fullPath);
3709
3766
  const content = buffer.toString("utf-8");
3710
3767
  manifest.push({
3711
3768
  path: relPath,
@@ -3716,23 +3773,23 @@ ${qaResult.errors?.join("\n")}`,
3716
3773
  }
3717
3774
  };
3718
3775
  await walk(snapshotRoot);
3719
- await fs4.writeFile(this.snapshotManifestPath, JSON.stringify({
3776
+ await fs5.writeFile(this.snapshotManifestPath, JSON.stringify({
3720
3777
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3721
3778
  root: snapshotRoot,
3722
3779
  files: manifest
3723
3780
  }, null, 2), "utf-8");
3724
3781
  await this.appendTrace("snapshot", {
3725
3782
  files: manifest.length,
3726
- manifestPath: path4.relative(this.config.workspaceRoot, this.snapshotManifestPath)
3783
+ manifestPath: path5.relative(this.config.workspaceRoot, this.snapshotManifestPath)
3727
3784
  });
3728
3785
  }
3729
3786
  async generateDiffReport() {
3730
3787
  const snapshotRoot = this.config.workspaceRoot;
3731
3788
  const ignore = /* @__PURE__ */ new Set(["node_modules", ".git", "runs", "dist", "build", ".next"]);
3732
- const diffPath = path4.join(this.runDir, "diff.json");
3789
+ const diffPath = path5.join(this.runDir, "diff.json");
3733
3790
  let snapshotData = null;
3734
3791
  try {
3735
- const raw = await fs4.readFile(this.snapshotManifestPath, "utf-8");
3792
+ const raw = await fs5.readFile(this.snapshotManifestPath, "utf-8");
3736
3793
  snapshotData = JSON.parse(raw);
3737
3794
  } catch {
3738
3795
  snapshotData = { files: [] };
@@ -3740,15 +3797,15 @@ ${qaResult.errors?.join("\n")}`,
3740
3797
  const snapshotMap = new Map(snapshotData.files.map((file) => [file.path, file.hash]));
3741
3798
  const currentMap = /* @__PURE__ */ new Map();
3742
3799
  const walk = async (dir) => {
3743
- const entries = await fs4.readdir(dir, { withFileTypes: true });
3800
+ const entries = await fs5.readdir(dir, { withFileTypes: true });
3744
3801
  for (const entry of entries) {
3745
3802
  if (ignore.has(entry.name)) continue;
3746
- const fullPath = path4.join(dir, entry.name);
3747
- const relPath = path4.relative(snapshotRoot, fullPath);
3803
+ const fullPath = path5.join(dir, entry.name);
3804
+ const relPath = path5.relative(snapshotRoot, fullPath);
3748
3805
  if (entry.isDirectory()) {
3749
3806
  await walk(fullPath);
3750
3807
  } else if (entry.isFile()) {
3751
- const buffer = await fs4.readFile(fullPath);
3808
+ const buffer = await fs5.readFile(fullPath);
3752
3809
  const content = buffer.toString("utf-8");
3753
3810
  currentMap.set(relPath, this.hashContent(content));
3754
3811
  }
@@ -3775,12 +3832,12 @@ ${qaResult.errors?.join("\n")}`,
3775
3832
  modified,
3776
3833
  deleted
3777
3834
  };
3778
- await fs4.writeFile(diffPath, JSON.stringify(diffPayload, null, 2), "utf-8");
3835
+ await fs5.writeFile(diffPath, JSON.stringify(diffPayload, null, 2), "utf-8");
3779
3836
  await this.appendTrace("diff", {
3780
3837
  created: created.length,
3781
3838
  modified: modified.length,
3782
3839
  deleted: deleted.length,
3783
- diffPath: path4.relative(this.config.workspaceRoot, diffPath)
3840
+ diffPath: path5.relative(this.config.workspaceRoot, diffPath)
3784
3841
  });
3785
3842
  return diffPayload;
3786
3843
  }
@@ -3892,8 +3949,8 @@ Constraints: Build a production SaaS using Next.js App Router, Prisma, Postgres,
3892
3949
  // src/commands/import.ts
3893
3950
  import chalk12 from "chalk";
3894
3951
  import ora5 from "ora";
3895
- import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
3896
- import { join as join3, dirname as dirname2 } from "path";
3952
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
3953
+ import { join as join4, dirname as dirname3 } from "path";
3897
3954
  function sanitizeRelativePath(p) {
3898
3955
  if (!p || p.startsWith("/") || p.match(/^[A-Za-z]:/)) return null;
3899
3956
  const normalized = p.replace(/\\/g, "/");
@@ -3910,8 +3967,8 @@ function parseGitHubUrl(url) {
3910
3967
  if (!cleanUrl.startsWith("github.com/")) {
3911
3968
  throw new Error("Invalid GitHub URL. Please use format: https://github.com/owner/repo");
3912
3969
  }
3913
- const path7 = cleanUrl.replace("github.com/", "");
3914
- const parts = path7.split("/");
3970
+ const path8 = cleanUrl.replace("github.com/", "");
3971
+ const parts = path8.split("/");
3915
3972
  if (parts.length < 2) {
3916
3973
  throw new Error("Invalid GitHub URL. Could not extract owner/repo.");
3917
3974
  }
@@ -3979,10 +4036,10 @@ async function extractZipToFiles(buffer) {
3979
4036
  }
3980
4037
  async function writeFiles(files, outputDir) {
3981
4038
  for (const file of files) {
3982
- const fullPath = join3(outputDir, file.path);
3983
- const dir = dirname2(fullPath);
3984
- await mkdir2(dir, { recursive: true });
3985
- await writeFile2(fullPath, file.content, "utf-8");
4039
+ const fullPath = join4(outputDir, file.path);
4040
+ const dir = dirname3(fullPath);
4041
+ await mkdir3(dir, { recursive: true });
4042
+ await writeFile3(fullPath, file.content, "utf-8");
3986
4043
  }
3987
4044
  }
3988
4045
  async function runImportCommand(url, outputDir) {
@@ -4018,214 +4075,8 @@ ${msg}
4018
4075
  }
4019
4076
  }
4020
4077
 
4021
- // src/commands/wizard.ts
4022
- import { input as input5, select as select3, confirm, password as password3 } from "@inquirer/prompts";
4023
- import chalk13 from "chalk";
4024
- import path5 from "path";
4025
- import os from "os";
4026
- import fs5 from "fs";
4027
- async function runWizard() {
4028
- await checkSafety();
4029
- if (needsOnboarding()) {
4030
- await runOnboarding();
4031
- }
4032
- await setupOptionalTools();
4033
- const wantsSaas = await confirm({
4034
- message: "Enable SaaS Blueprint mode? (Next.js + Prisma + Postgres + Stripe)",
4035
- default: true
4036
- });
4037
- if (wantsSaas) {
4038
- ui.setFlags({ saas: true });
4039
- }
4040
- console.log(chalk13.cyan.bold("\n\u{1F3AF} Mission Control"));
4041
- console.log(chalk13.gray("Describe your app in detail. The Agdi Squad will handle the rest."));
4042
- let prompt = await input5({
4043
- message: "What are we building today?",
4044
- validate: (value) => value.length > 5 ? true : "Please provide more detail (min 5 chars)"
4045
- });
4046
- if (wantsSaas) {
4047
- prompt = `${prompt}
4048
-
4049
- Constraints: Build a production SaaS using Next.js App Router, Prisma, Postgres, and Stripe. Include auth, billing, multi-tenant orgs, and a dashboard.`;
4050
- }
4051
- const activeConfig = getActiveProvider();
4052
- if (!activeConfig) {
4053
- console.log(chalk13.red("\u274C No API key configured. Run: agdi auth"));
4054
- return;
4055
- }
4056
- const llm = createLLMProvider(activeConfig.provider, {
4057
- apiKey: activeConfig.apiKey,
4058
- model: activeConfig.model
4059
- });
4060
- console.log(chalk13.cyan("\n\u{1F914} Analyzing requirements..."));
4061
- try {
4062
- const analysis = await llm.generate(prompt, `
4063
- You are a Senior Product Manager. Analyze this app idea.
4064
- If it is vague (e.g., "make a CRM"), generate 3 specific questions to clarify scope, tech stack, and features.
4065
- If it is detailed enough, return "DETAILED".
4066
-
4067
- Format:
4068
- Questions:
4069
- 1. [Question 1]
4070
- 2. [Question 2]
4071
- 3. [Question 3]
4072
- `);
4073
- if (!analysis.text.includes("DETAILED")) {
4074
- console.log(chalk13.yellow("\n\u{1F4A1} I need a few more details to build exactly what you want:"));
4075
- const questions = analysis.text.split("\n").filter((line) => line.match(/^\d+\./));
4076
- const answers = [];
4077
- for (const q of questions.slice(0, 3)) {
4078
- const answer = await input5({ message: q.replace(/^\d+\.\s*/, "") });
4079
- answers.push({ q, a: answer });
4080
- }
4081
- console.log(chalk13.cyan("\n\u{1F504} Synthesizing Master Plan..."));
4082
- const synthesis = await llm.generate(
4083
- `Original Request: ${prompt}
4084
-
4085
- Clarifications:
4086
- ${answers.map((x) => `Q: ${x.q}
4087
- A: ${x.a}`).join("\n")}
4088
-
4089
- Rewrite the original request into a comprehensive technical specification for a developer squad.`,
4090
- "You are a Technical Architect."
4091
- );
4092
- prompt = synthesis.text;
4093
- console.log(chalk13.gray("\nUpdated Spec: " + prompt.substring(0, 100) + "...\n"));
4094
- }
4095
- } catch (err) {
4096
- console.log(chalk13.gray("Skipping analysis (API unreachable), proceeding with raw prompt."));
4097
- }
4098
- console.log(chalk13.cyan("\n\u{1F680} Assembling the Squad..."));
4099
- console.log(chalk13.gray("Frontend, Backend, QA, and DevOps agents are coming online.\n"));
4100
- const config = loadConfig();
4101
- const canDeploy = !!(config.vercelToken || config.netlifyToken || config.railwayToken);
4102
- await runSquadCommand(prompt, llm, {
4103
- deploy: canDeploy,
4104
- // Auto-deploy if configured
4105
- output: "./",
4106
- // Current directory (safe because we checked safety earlier)
4107
- verbose: true
4108
- });
4109
- if (ui.flags.saas) {
4110
- console.log(chalk13.cyan("\nSaaS Quick Start:"));
4111
- console.log(chalk13.gray(" 1) npm install"));
4112
- console.log(chalk13.gray(" 2) cp .env.example .env"));
4113
- console.log(chalk13.gray(" 3) npx prisma generate"));
4114
- console.log(chalk13.gray(" 4) npx prisma db push"));
4115
- console.log(chalk13.gray(" 5) npm run dev\n"));
4116
- }
4117
- }
4118
- async function checkSafety() {
4119
- const cwd = process.cwd();
4120
- const home = os.homedir();
4121
- const root = path5.parse(cwd).root;
4122
- const isUnsafe = cwd === home || cwd === root;
4123
- if (isUnsafe) {
4124
- console.log(chalk13.yellow.bold("\n\u26A0\uFE0F Safety Warning"));
4125
- console.log(chalk13.gray(`You are running Agdi in ${chalk13.cyan(cwd)}.`));
4126
- console.log(chalk13.gray("Agdi writes files and runs commands. It needs its own space.\n"));
4127
- const action = await select3({
4128
- message: "What would you like to do?",
4129
- choices: [
4130
- { name: "\u{1F4C2} Create a new project folder (Recommended)", value: "create" },
4131
- { name: "\u{1F525} Run here anyway (Dangerous)", value: "unsafe" },
4132
- { name: "\u274C Cancel", value: "cancel" }
4133
- ]
4134
- });
4135
- if (action === "cancel") {
4136
- ui.safeExit(0);
4137
- }
4138
- if (action === "create") {
4139
- const folderName = await input5({
4140
- message: "Project name:",
4141
- default: "my-agdi-app",
4142
- validate: (v) => /^[a-z0-9-_]+$/i.test(v) ? true : "Invalid folder name"
4143
- });
4144
- const newPath = path5.join(cwd, folderName);
4145
- if (!fs5.existsSync(newPath)) {
4146
- fs5.mkdirSync(newPath);
4147
- }
4148
- process.chdir(newPath);
4149
- console.log(chalk13.green(`
4150
- \u{1F4C2} Switched to: ${chalk13.cyan(newPath)}
4151
- `));
4152
- }
4153
- }
4154
- }
4155
- async function setupOptionalTools() {
4156
- const config = loadConfig();
4157
- let configChanged = false;
4158
- if (!config.searchApiKey) {
4159
- console.log(chalk13.white.bold("\n\u{1F310} Web Access (Optional)\n"));
4160
- console.log(chalk13.gray("Giving Agdi web access allows it to find up-to-date docs and fix tricky bugs."));
4161
- const wantsSearch = await confirm({
4162
- message: "Enable web search capabilities?",
4163
- default: true
4164
- });
4165
- if (wantsSearch) {
4166
- const provider = await select3({
4167
- message: "Select Search Provider:",
4168
- choices: [
4169
- { name: "Brave Search (Recommended)", value: "brave" },
4170
- { name: "Tavily", value: "tavily" }
4171
- ]
4172
- });
4173
- const keyUrl = provider === "brave" ? "https://brave.com/search/api/" : "https://tavily.com/";
4174
- console.log(chalk13.gray(`Get your key at: ${chalk13.cyan(keyUrl)}
4175
- `));
4176
- const apiKey = await password3({
4177
- message: `Enter your ${provider} API key:`,
4178
- mask: "*"
4179
- });
4180
- if (apiKey) {
4181
- config.searchApiKey = apiKey;
4182
- config.searchProvider = provider;
4183
- config.searchEnabled = true;
4184
- configChanged = true;
4185
- console.log(chalk13.green("\u2705 Web search enabled!"));
4186
- }
4187
- }
4188
- }
4189
- if (!config.vercelToken && !config.netlifyToken && !config.railwayToken) {
4190
- console.log(chalk13.white.bold("\n\u{1F680} Deployment (Optional)\n"));
4191
- console.log(chalk13.gray("Agdi can automatically deploy your app to the cloud when finished."));
4192
- const wantsDeploy = await confirm({
4193
- message: "Enable auto-deployment?",
4194
- default: true
4195
- });
4196
- if (wantsDeploy) {
4197
- const provider = await select3({
4198
- message: "Select Deployment Target:",
4199
- choices: [
4200
- { name: "Vercel", value: "vercel" },
4201
- { name: "Railway", value: "railway" },
4202
- { name: "Netlify", value: "netlify" }
4203
- ]
4204
- });
4205
- const keyUrl = provider === "vercel" ? "https://vercel.com/account/tokens" : provider === "railway" ? "https://railway.app/account/tokens" : "https://app.netlify.com/user/applications#personal-access-tokens";
4206
- console.log(chalk13.gray(`Get your key at: ${chalk13.cyan(keyUrl)}
4207
- `));
4208
- const apiKey = await password3({
4209
- message: `Enter your ${provider} API token:`,
4210
- mask: "*"
4211
- });
4212
- if (apiKey) {
4213
- if (provider === "vercel") config.vercelToken = apiKey;
4214
- if (provider === "netlify") config.netlifyToken = apiKey;
4215
- if (provider === "railway") config.railwayToken = apiKey;
4216
- config.deploymentProvider = provider;
4217
- configChanged = true;
4218
- console.log(chalk13.green("\u2705 Auto-deployment enabled!"));
4219
- }
4220
- }
4221
- }
4222
- if (configChanged) {
4223
- saveConfig(config);
4224
- }
4225
- }
4226
-
4227
4078
  // src/commands/replay.ts
4228
- import chalk14 from "chalk";
4079
+ import chalk13 from "chalk";
4229
4080
  import * as fs6 from "fs/promises";
4230
4081
  import * as path6 from "path";
4231
4082
  async function runReplayCommand(runId, llm, options = {}) {
@@ -4236,7 +4087,7 @@ async function runReplayCommand(runId, llm, options = {}) {
4236
4087
  const raw = await fs6.readFile(runConfigPath, "utf-8");
4237
4088
  const runConfig = JSON.parse(raw);
4238
4089
  if (!runConfig.prompt) {
4239
- console.log(chalk14.red("\u274C Invalid run.json (missing prompt)"));
4090
+ console.log(chalk13.red("\u274C Invalid run.json (missing prompt)"));
4240
4091
  return null;
4241
4092
  }
4242
4093
  const config = {
@@ -4245,7 +4096,7 @@ async function runReplayCommand(runId, llm, options = {}) {
4245
4096
  verbose: options.verbose ?? runConfig.config?.verbose ?? true,
4246
4097
  autoDeploy: options.deploy ?? runConfig.config?.autoDeploy ?? false
4247
4098
  };
4248
- console.log(chalk14.cyan(`
4099
+ console.log(chalk13.cyan(`
4249
4100
  \u{1F501} Replaying run ${runId}`));
4250
4101
  if (options.exact !== false) {
4251
4102
  try {
@@ -4269,7 +4120,7 @@ async function runReplayCommand(runId, llm, options = {}) {
4269
4120
  }
4270
4121
  };
4271
4122
  await copyOutputs(outputsDir);
4272
- console.log(chalk14.green("\u2705 Exact replay applied from stored outputs."));
4123
+ console.log(chalk13.green("\u2705 Exact replay applied from stored outputs."));
4273
4124
  return {
4274
4125
  success: true,
4275
4126
  projectSpec: { name: "replay", description: "Exact replay", type: "web-app", stack: {}, features: [] },
@@ -4280,34 +4131,190 @@ async function runReplayCommand(runId, llm, options = {}) {
4280
4131
  errors: []
4281
4132
  };
4282
4133
  } catch (error) {
4283
- console.log(chalk14.yellow("\u26A0\uFE0F Exact replay failed, falling back to rerun."));
4284
- console.log(chalk14.gray(String(error)));
4134
+ console.log(chalk13.yellow("\u26A0\uFE0F Exact replay failed, falling back to rerun."));
4135
+ console.log(chalk13.gray(String(error)));
4285
4136
  }
4286
4137
  }
4287
4138
  if (!llm) {
4288
- console.log(chalk14.red("\u274C No LLM provider available for replay."));
4139
+ console.log(chalk13.red("\u274C No LLM provider available for replay."));
4289
4140
  return null;
4290
4141
  }
4291
4142
  const orchestrator = new SquadOrchestrator(llm, config);
4292
4143
  return await orchestrator.run(runConfig.prompt);
4293
4144
  } catch (error) {
4294
- console.log(chalk14.red("\u274C Failed to load run config for replay"));
4295
- console.log(chalk14.gray(String(error)));
4145
+ console.log(chalk13.red("\u274C Failed to load run config for replay"));
4146
+ console.log(chalk13.gray(String(error)));
4296
4147
  return null;
4297
4148
  }
4298
4149
  }
4299
4150
 
4151
+ // src/commands/tui-entry.tsx
4152
+ import { render } from "ink";
4153
+
4154
+ // src/ui/tui.tsx
4155
+ import { useState, useEffect } from "react";
4156
+ import { Box, Text, useApp } from "ink";
4157
+ import BigText from "ink-big-text";
4158
+ import TextInput from "ink-text-input";
4159
+ import fs7 from "fs";
4160
+ import path7 from "path";
4161
+ import { jsx, jsxs } from "react/jsx-runtime";
4162
+ var BootScreen = ({ onComplete }) => {
4163
+ const [progress, setProgress] = useState(0);
4164
+ const [log, setLog] = useState("");
4165
+ const logs = [
4166
+ "Initializing core systems...",
4167
+ "Loading neural modules...",
4168
+ "Connecting to Agdi Cloud...",
4169
+ "Verifying agent credentials...",
4170
+ "Mounting virtual filesystem...",
4171
+ "Establishing secure uplink...",
4172
+ "Syncing global state...",
4173
+ "Boot sequence complete."
4174
+ ];
4175
+ useEffect(() => {
4176
+ const timer = setInterval(() => {
4177
+ setProgress((prev) => {
4178
+ const next = prev + 2;
4179
+ if (next >= 100) {
4180
+ clearInterval(timer);
4181
+ setTimeout(onComplete, 500);
4182
+ return 100;
4183
+ }
4184
+ return next;
4185
+ });
4186
+ const index = Math.floor(progress / 100 * logs.length);
4187
+ setLog(logs[index] || logs[logs.length - 1]);
4188
+ }, 30);
4189
+ return () => clearInterval(timer);
4190
+ }, [progress]);
4191
+ const width = 60;
4192
+ const filled = Math.floor(width * progress / 100);
4193
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
4194
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 20, children: [
4195
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(BigText, { text: "AGDI OS", font: "block", align: "center", colors: ["cyan", "blue"] }) }),
4196
+ /* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
4197
+ bar,
4198
+ " ",
4199
+ progress,
4200
+ "%"
4201
+ ] }) }),
4202
+ /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
4203
+ "\u279C ",
4204
+ log
4205
+ ] })
4206
+ ] });
4207
+ };
4208
+ var FileExplorer = ({ cwd }) => {
4209
+ const [files, setFiles] = useState([]);
4210
+ useEffect(() => {
4211
+ try {
4212
+ const list = fs7.readdirSync(cwd).filter((f) => !f.startsWith(".")).slice(0, 15);
4213
+ setFiles(list);
4214
+ } catch {
4215
+ setFiles(["<Error reading dir>"]);
4216
+ }
4217
+ }, [cwd]);
4218
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", title: " FILESYSTEM ", width: "30%", children: [
4219
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
4220
+ " ",
4221
+ cwd
4222
+ ] }),
4223
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
4224
+ files.map((f, i) => /* @__PURE__ */ jsxs(Text, { color: "white", children: [
4225
+ fs7.statSync(path7.join(cwd, f)).isDirectory() ? "\u{1F4C1} " : "\u{1F4C4} ",
4226
+ f
4227
+ ] }, i)),
4228
+ files.length === 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: " (empty)" })
4229
+ ] })
4230
+ ] });
4231
+ };
4232
+ var LogPanel = ({ logs }) => {
4233
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", title: " AGENT LOGS ", width: "70%", marginLeft: 1, children: [
4234
+ logs.slice(-10).map((log, i) => /* @__PURE__ */ jsxs(Text, { color: "green", children: [
4235
+ /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
4236
+ "[",
4237
+ (/* @__PURE__ */ new Date()).toLocaleTimeString(),
4238
+ "]"
4239
+ ] }),
4240
+ " ",
4241
+ log
4242
+ ] }, i)),
4243
+ logs.length === 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: "Waiting for agent activity..." })
4244
+ ] });
4245
+ };
4246
+ var ChatPanel = ({ history, onSend }) => {
4247
+ const [query, setQuery] = useState("");
4248
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", title: " MISSION CONTROL ", height: 12, children: [
4249
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, justifyContent: "flex-end", children: history.slice(-5).map((msg, i) => /* @__PURE__ */ jsxs(Box, { marginY: 0, children: [
4250
+ /* @__PURE__ */ jsx(Text, { color: msg.role === "user" ? "cyan" : "magenta", bold: true, children: msg.role === "user" ? "YOU \u203A " : "AGDI \u203A " }),
4251
+ /* @__PURE__ */ jsx(Text, { children: msg.text })
4252
+ ] }, i)) }),
4253
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "gray", marginTop: 1, children: [
4254
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u279C " }),
4255
+ /* @__PURE__ */ jsx(
4256
+ TextInput,
4257
+ {
4258
+ value: query,
4259
+ onChange: setQuery,
4260
+ onSubmit: (val) => {
4261
+ onSend(val);
4262
+ setQuery("");
4263
+ }
4264
+ }
4265
+ )
4266
+ ] })
4267
+ ] });
4268
+ };
4269
+ var Dashboard = () => {
4270
+ const { exit } = useApp();
4271
+ const [logs, setLogs] = useState(["System ready.", "Waiting for instructions."]);
4272
+ const [chatHistory, setChatHistory] = useState([]);
4273
+ const addLog = (msg) => setLogs((prev) => [...prev, msg]);
4274
+ const handleCommand = (cmd) => {
4275
+ if (cmd === "/exit") exit();
4276
+ setChatHistory((prev) => [...prev, { role: "user", text: cmd }]);
4277
+ addLog(`Processing command: ${cmd}`);
4278
+ setTimeout(() => {
4279
+ setChatHistory((prev) => [...prev, { role: "ai", text: `I'm analyzing your request to "${cmd}"...` }]);
4280
+ addLog("Agent [Manager] started planning phase.");
4281
+ }, 800);
4282
+ };
4283
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, height: "100%", children: [
4284
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [
4285
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "AGDI v3.3.0 [ONLINE]" }),
4286
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "CPU: 12% | MEM: 450MB | NET: CONNECTED" })
4287
+ ] }),
4288
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [
4289
+ /* @__PURE__ */ jsx(FileExplorer, { cwd: process.cwd() }),
4290
+ /* @__PURE__ */ jsx(LogPanel, { logs })
4291
+ ] }),
4292
+ /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsx(ChatPanel, { history: chatHistory, onSend: handleCommand }) })
4293
+ ] });
4294
+ };
4295
+ var App = () => {
4296
+ const [screen, setScreen] = useState("boot");
4297
+ return screen === "boot" ? /* @__PURE__ */ jsx(BootScreen, { onComplete: () => setScreen("dashboard") }) : /* @__PURE__ */ jsx(Dashboard, {});
4298
+ };
4299
+
4300
+ // src/commands/tui-entry.tsx
4301
+ import { jsx as jsx2 } from "react/jsx-runtime";
4302
+ function runTUI() {
4303
+ console.clear();
4304
+ render(/* @__PURE__ */ jsx2(App, {}));
4305
+ }
4306
+
4300
4307
  // src/index.ts
4301
4308
  var BANNER = `
4302
- ${chalk15.cyan(` ___ __ _ `)}
4303
- ${chalk15.cyan(` / | ____ _____/ /(_) `)}
4304
- ${chalk15.cyan(` / /| | / __ \`/ __ // / `)}
4305
- ${chalk15.cyan(` / ___ |/ /_/ / /_/ // / `)}
4306
- ${chalk15.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
4307
- ${chalk15.cyan(` /____/ `)}
4309
+ ${chalk14.cyan(` ___ __ _ `)}
4310
+ ${chalk14.cyan(` / | ____ _____/ /(_) `)}
4311
+ ${chalk14.cyan(` / /| | / __ \`/ __ // / `)}
4312
+ ${chalk14.cyan(` / ___ |/ /_/ / /_/ // / `)}
4313
+ ${chalk14.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
4314
+ ${chalk14.cyan(` /____/ `)}
4308
4315
  `;
4309
4316
  var program = new Command();
4310
- program.name("agdi").description(chalk15.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.0.0").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files").option("--saas", "Generate a production SaaS blueprint (Next.js + Prisma + Postgres + Stripe)");
4317
+ program.name("agdi").description(chalk14.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.3.0").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files").option("--saas", "Generate a production SaaS blueprint (Next.js + Prisma + Postgres + Stripe)");
4311
4318
  program.hook("preAction", (thisCommand) => {
4312
4319
  const opts = thisCommand.opts();
4313
4320
  if (opts.yes) {
@@ -4324,15 +4331,14 @@ program.hook("preAction", (thisCommand) => {
4324
4331
  }
4325
4332
  });
4326
4333
  program.addHelpText("beforeAll", () => {
4327
- return BANNER + "\n" + chalk15.gray(" The Autonomous AI Employee") + "\n" + chalk15.gray(" \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\n");
4334
+ return BANNER + "\n" + chalk14.gray(" The Autonomous AI Employee") + "\n" + chalk14.gray(" \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\n");
4328
4335
  });
4329
4336
  program.action(async () => {
4330
4337
  try {
4331
- await ui.renderBanner();
4332
- await runWizard();
4338
+ runTUI();
4333
4339
  } catch (error) {
4334
4340
  if (error.name === "ExitPromptError") {
4335
- console.log(chalk15.gray("\n\n\u{1F44B} Goodbye!\n"));
4341
+ console.log(chalk14.gray("\n\n\u{1F44B} Goodbye!\n"));
4336
4342
  try {
4337
4343
  ui.safeExit(0);
4338
4344
  } catch {
@@ -4354,7 +4360,7 @@ program.command("auth").description("Configure API keys").option("--status", "Sh
4354
4360
  }
4355
4361
  } catch (error) {
4356
4362
  if (error.name === "ExitPromptError") {
4357
- console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
4363
+ console.log(chalk14.gray("\n\n\u{1F44B} Cancelled.\n"));
4358
4364
  try {
4359
4365
  ui.safeExit(0);
4360
4366
  } catch {
@@ -4372,7 +4378,7 @@ program.command("model").alias("models").description("Change AI model").action(a
4372
4378
  await selectModel();
4373
4379
  } catch (error) {
4374
4380
  if (error.name === "ExitPromptError") {
4375
- console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
4381
+ console.log(chalk14.gray("\n\n\u{1F44B} Cancelled.\n"));
4376
4382
  try {
4377
4383
  ui.safeExit(0);
4378
4384
  } catch {
@@ -4393,7 +4399,7 @@ program.command("chat").description("Start a chat session").action(async () => {
4393
4399
  await startChat();
4394
4400
  } catch (error) {
4395
4401
  if (error.name === "ExitPromptError") {
4396
- console.log(chalk15.gray("\n\n\u{1F44B} Goodbye!\n"));
4402
+ console.log(chalk14.gray("\n\n\u{1F44B} Goodbye!\n"));
4397
4403
  try {
4398
4404
  ui.safeExit(0);
4399
4405
  } catch {
@@ -4411,7 +4417,7 @@ program.command("run [directory]").description("Run a generated project").action
4411
4417
  await runProject(directory);
4412
4418
  } catch (error) {
4413
4419
  if (error.name === "ExitPromptError") {
4414
- console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
4420
+ console.log(chalk14.gray("\n\n\u{1F44B} Cancelled.\n"));
4415
4421
  try {
4416
4422
  ui.safeExit(0);
4417
4423
  } catch {
@@ -4434,7 +4440,7 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
4434
4440
  }
4435
4441
  const activeConfig = getActiveProvider();
4436
4442
  if (!activeConfig) {
4437
- console.log(chalk15.red("\u274C No API key configured. Run: agdi auth"));
4443
+ console.log(chalk14.red("\u274C No API key configured. Run: agdi auth"));
4438
4444
  return;
4439
4445
  }
4440
4446
  const spinner = ora6("Generating application...").start();
@@ -4446,52 +4452,52 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
4446
4452
  const pm = new ProjectManager();
4447
4453
  pm.create(options.output.replace("./", ""), prompt);
4448
4454
  const { plan, files } = await generateApp(prompt, llm, (step, file) => {
4449
- spinner.text = file ? `${step} ${chalk15.gray(file)}` : step;
4455
+ spinner.text = file ? `${step} ${chalk14.gray(file)}` : step;
4450
4456
  });
4451
4457
  pm.updateFiles(files);
4452
4458
  pm.updateDependencies(plan.dependencies);
4453
4459
  if (options.dryRun || ui.flags.dryRun) {
4454
4460
  spinner.stop();
4455
- console.log(chalk15.cyan.bold("\n\u{1F6A7} DRY RUN SUMMARY\n"));
4456
- console.log(chalk15.gray(`Project: ${plan.name}
4461
+ console.log(chalk14.cyan.bold("\n\u{1F6A7} DRY RUN SUMMARY\n"));
4462
+ console.log(chalk14.gray(`Project: ${plan.name}
4457
4463
  `));
4458
- console.log(chalk15.cyan("Files to be created:"));
4459
- files.forEach((f) => console.log(chalk15.gray(` \u{1F4C4} ${f.path}`)));
4460
- console.log(chalk15.cyan("\nDependencies:"));
4461
- console.log(chalk15.gray(` \u{1F4E6} ${plan.dependencies.join(", ")}`));
4462
- console.log(chalk15.green("\n\u2713 Dry run complete. No files written.\n"));
4464
+ console.log(chalk14.cyan("Files to be created:"));
4465
+ files.forEach((f) => console.log(chalk14.gray(` \u{1F4C4} ${f.path}`)));
4466
+ console.log(chalk14.cyan("\nDependencies:"));
4467
+ console.log(chalk14.gray(` \u{1F4E6} ${plan.dependencies.join(", ")}`));
4468
+ console.log(chalk14.green("\n\u2713 Dry run complete. No files written.\n"));
4463
4469
  return;
4464
4470
  }
4465
4471
  await writeProject(pm.get(), options.output);
4466
- spinner.succeed(chalk15.green("App generated!"));
4467
- console.log(chalk15.gray(`
4468
- \u{1F4C1} Created ${files.length} files in ${chalk15.cyan(options.output)}`));
4472
+ spinner.succeed(chalk14.green("App generated!"));
4473
+ console.log(chalk14.gray(`
4474
+ \u{1F4C1} Created ${files.length} files in ${chalk14.cyan(options.output)}`));
4469
4475
  if (ui.flags.saas || options.saas) {
4470
- console.log(chalk15.cyan("\nSaaS Quick Start:"));
4471
- console.log(chalk15.gray(` 1) cd ${options.output}`));
4472
- console.log(chalk15.gray(" 2) npm install"));
4473
- console.log(chalk15.gray(" 3) cp .env.example .env"));
4474
- console.log(chalk15.gray(" 4) npx prisma generate"));
4475
- console.log(chalk15.gray(" 5) npx prisma db push"));
4476
- console.log(chalk15.gray(" 6) npm run dev\n"));
4476
+ console.log(chalk14.cyan("\nSaaS Quick Start:"));
4477
+ console.log(chalk14.gray(` 1) cd ${options.output}`));
4478
+ console.log(chalk14.gray(" 2) npm install"));
4479
+ console.log(chalk14.gray(" 3) cp .env.example .env"));
4480
+ console.log(chalk14.gray(" 4) npx prisma generate"));
4481
+ console.log(chalk14.gray(" 5) npx prisma db push"));
4482
+ console.log(chalk14.gray(" 6) npm run dev\n"));
4477
4483
  } else {
4478
- console.log(chalk15.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
4484
+ console.log(chalk14.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
4479
4485
  }
4480
4486
  } catch (error) {
4481
4487
  spinner.fail("Generation failed");
4482
4488
  const msg = error instanceof Error ? error.message : String(error);
4483
4489
  if (msg.includes("429") || msg.includes("quota")) {
4484
- console.log(chalk15.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
4490
+ console.log(chalk14.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
4485
4491
  } else if (msg.includes("401") || msg.includes("403")) {
4486
- console.log(chalk15.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
4492
+ console.log(chalk14.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
4487
4493
  } else {
4488
- console.error(chalk15.red("\n" + msg + "\n"));
4494
+ console.error(chalk14.red("\n" + msg + "\n"));
4489
4495
  }
4490
4496
  ui.safeExit(1);
4491
4497
  }
4492
4498
  } catch (error) {
4493
4499
  if (error.name === "ExitPromptError") {
4494
- console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
4500
+ console.log(chalk14.gray("\n\n\u{1F44B} Cancelled.\n"));
4495
4501
  try {
4496
4502
  ui.safeExit(0);
4497
4503
  } catch {
@@ -4507,11 +4513,11 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
4507
4513
  program.command("config").description("Show configuration").action(async () => {
4508
4514
  const config = loadConfig();
4509
4515
  const active = getActiveProvider();
4510
- console.log(chalk15.cyan.bold("\n\u2699\uFE0F Configuration\n"));
4511
- console.log(chalk15.gray(" Provider: ") + chalk15.cyan(config.defaultProvider || "not set"));
4512
- console.log(chalk15.gray(" Model: ") + chalk15.cyan(config.defaultModel || "not set"));
4513
- console.log(chalk15.gray(" Config: ") + chalk15.gray("~/.agdi/config.json"));
4514
- console.log(chalk15.cyan.bold("\n\u{1F510} API Keys\n"));
4516
+ console.log(chalk14.cyan.bold("\n\u2699\uFE0F Configuration\n"));
4517
+ console.log(chalk14.gray(" Provider: ") + chalk14.cyan(config.defaultProvider || "not set"));
4518
+ console.log(chalk14.gray(" Model: ") + chalk14.cyan(config.defaultModel || "not set"));
4519
+ console.log(chalk14.gray(" Config: ") + chalk14.gray("~/.agdi/config.json"));
4520
+ console.log(chalk14.cyan.bold("\n\u{1F510} API Keys\n"));
4515
4521
  const keys = [
4516
4522
  ["Gemini", config.geminiApiKey],
4517
4523
  ["OpenRouter", config.openrouterApiKey],
@@ -4520,13 +4526,13 @@ program.command("config").description("Show configuration").action(async () => {
4520
4526
  ["DeepSeek", config.deepseekApiKey]
4521
4527
  ];
4522
4528
  for (const [name, key] of keys) {
4523
- const status = key ? chalk15.green("\u2713") : chalk15.gray("\u2717");
4529
+ const status = key ? chalk14.green("\u2713") : chalk14.gray("\u2717");
4524
4530
  console.log(` ${status} ${name}`);
4525
4531
  }
4526
- console.log(chalk15.cyan.bold("\n\u{1F4CA} Telemetry\n"));
4532
+ console.log(chalk14.cyan.bold("\n\u{1F4CA} Telemetry\n"));
4527
4533
  const telemetryEnabled = config.telemetry?.enabled ?? false;
4528
- console.log(` ${telemetryEnabled ? chalk15.green("\u2713 Enabled") : chalk15.gray("\u2717 Disabled")}`);
4529
- console.log(chalk15.gray(" Change with: agdi config telemetry --enable | --disable"));
4534
+ console.log(` ${telemetryEnabled ? chalk14.green("\u2713 Enabled") : chalk14.gray("\u2717 Disabled")}`);
4535
+ console.log(chalk14.gray(" Change with: agdi config telemetry --enable | --disable"));
4530
4536
  console.log("");
4531
4537
  console.log("");
4532
4538
  });
@@ -4534,59 +4540,59 @@ program.command("config:telemetry").alias("telemetry").description("Manage telem
4534
4540
  const { isTelemetryEnabled, setTelemetryConsent, getTelemetryConfig } = await import("./config-ZFU7TSU2.js");
4535
4541
  const { generateSampleEvent, generateSanitizationDemo } = await import("./telemetry-service-OHU5NKON.js");
4536
4542
  if (options.dryRun || options.test) {
4537
- console.log(chalk15.cyan.bold("\n\u{1F50D} TELEMETRY TRANSPARENCY MODE\n"));
4538
- console.log(chalk15.gray("This is exactly what Agdi sends. Notice there is"));
4539
- console.log(chalk15.green.bold("NO source code, file paths, or API keys.\n"));
4540
- console.log(chalk15.white.bold('\u{1F4CA} Sample "Build Failed" Event:\n'));
4543
+ console.log(chalk14.cyan.bold("\n\u{1F50D} TELEMETRY TRANSPARENCY MODE\n"));
4544
+ console.log(chalk14.gray("This is exactly what Agdi sends. Notice there is"));
4545
+ console.log(chalk14.green.bold("NO source code, file paths, or API keys.\n"));
4546
+ console.log(chalk14.white.bold('\u{1F4CA} Sample "Build Failed" Event:\n'));
4541
4547
  const sample = generateSampleEvent();
4542
- console.log(chalk15.gray(JSON.stringify(sample, null, 2)));
4543
- console.log(chalk15.white.bold("\n\u{1F6E1}\uFE0F Sanitization Demo:\n"));
4544
- console.log(chalk15.gray("Even if sensitive data accidentally enters an error message,"));
4545
- console.log(chalk15.gray("our sanitization layer strips it before transmission:\n"));
4548
+ console.log(chalk14.gray(JSON.stringify(sample, null, 2)));
4549
+ console.log(chalk14.white.bold("\n\u{1F6E1}\uFE0F Sanitization Demo:\n"));
4550
+ console.log(chalk14.gray("Even if sensitive data accidentally enters an error message,"));
4551
+ console.log(chalk14.gray("our sanitization layer strips it before transmission:\n"));
4546
4552
  const demo = generateSanitizationDemo();
4547
- console.log(chalk15.red.bold("BEFORE sanitization (never sent):"));
4548
- console.log(chalk15.gray(JSON.stringify({
4553
+ console.log(chalk14.red.bold("BEFORE sanitization (never sent):"));
4554
+ console.log(chalk14.gray(JSON.stringify({
4549
4555
  errorCode: demo.before.errorCode,
4550
4556
  feedback: demo.before.feedback
4551
4557
  }, null, 2)));
4552
- console.log(chalk15.green.bold("\nAFTER sanitization (what we actually send):"));
4553
- console.log(chalk15.gray(JSON.stringify({
4558
+ console.log(chalk14.green.bold("\nAFTER sanitization (what we actually send):"));
4559
+ console.log(chalk14.gray(JSON.stringify({
4554
4560
  errorCode: demo.after.errorCode,
4555
4561
  feedback: demo.after.feedback
4556
4562
  }, null, 2)));
4557
- console.log(chalk15.cyan("\n\u2705 Your code and secrets are NEVER transmitted."));
4558
- console.log(chalk15.gray(" Learn more: https://agdi.dev/privacy\n"));
4563
+ console.log(chalk14.cyan("\n\u2705 Your code and secrets are NEVER transmitted."));
4564
+ console.log(chalk14.gray(" Learn more: https://agdi.dev/privacy\n"));
4559
4565
  return;
4560
4566
  }
4561
4567
  if (options.enable) {
4562
4568
  setTelemetryConsent(true);
4563
- console.log(chalk15.green("\n\u2705 Telemetry enabled"));
4564
- console.log(chalk15.gray(" We collect: success/fail, error types, model used"));
4565
- console.log(chalk15.gray(" We NEVER collect: source code, API keys, file paths"));
4566
- console.log(chalk15.gray(" Verify anytime: agdi config telemetry --dry-run\n"));
4569
+ console.log(chalk14.green("\n\u2705 Telemetry enabled"));
4570
+ console.log(chalk14.gray(" We collect: success/fail, error types, model used"));
4571
+ console.log(chalk14.gray(" We NEVER collect: source code, API keys, file paths"));
4572
+ console.log(chalk14.gray(" Verify anytime: agdi config telemetry --dry-run\n"));
4567
4573
  } else if (options.disable) {
4568
4574
  setTelemetryConsent(false);
4569
- console.log(chalk15.yellow("\n\u{1F4CA} Telemetry disabled"));
4570
- console.log(chalk15.gray(" You can re-enable anytime with: agdi config telemetry --enable\n"));
4575
+ console.log(chalk14.yellow("\n\u{1F4CA} Telemetry disabled"));
4576
+ console.log(chalk14.gray(" You can re-enable anytime with: agdi config telemetry --enable\n"));
4571
4577
  } else {
4572
4578
  const config = getTelemetryConfig();
4573
- console.log(chalk15.cyan.bold("\n\u{1F4CA} Telemetry Status\n"));
4574
- console.log(chalk15.gray(" Enabled: ") + (config.enabled ? chalk15.green("Yes") : chalk15.gray("No")));
4575
- console.log(chalk15.gray(" Consent: ") + (config.consentAsked ? chalk15.green("Asked") : chalk15.gray("Not asked")));
4579
+ console.log(chalk14.cyan.bold("\n\u{1F4CA} Telemetry Status\n"));
4580
+ console.log(chalk14.gray(" Enabled: ") + (config.enabled ? chalk14.green("Yes") : chalk14.gray("No")));
4581
+ console.log(chalk14.gray(" Consent: ") + (config.consentAsked ? chalk14.green("Asked") : chalk14.gray("Not asked")));
4576
4582
  if (config.anonymousId) {
4577
- console.log(chalk15.gray(" ID: ") + chalk15.gray(config.anonymousId.slice(0, 8) + "..."));
4583
+ console.log(chalk14.gray(" ID: ") + chalk14.gray(config.anonymousId.slice(0, 8) + "..."));
4578
4584
  }
4579
4585
  console.log("");
4580
- console.log(chalk15.gray(" Enable: agdi config telemetry --enable"));
4581
- console.log(chalk15.gray(" Disable: agdi config telemetry --disable"));
4582
- console.log(chalk15.gray(" Verify: agdi config telemetry --dry-run\n"));
4586
+ console.log(chalk14.gray(" Enable: agdi config telemetry --enable"));
4587
+ console.log(chalk14.gray(" Disable: agdi config telemetry --disable"));
4588
+ console.log(chalk14.gray(" Verify: agdi config telemetry --dry-run\n"));
4583
4589
  }
4584
4590
  });
4585
4591
  program.command("doctor").alias("doc").description("Run self-diagnosis checks").action(async () => {
4586
4592
  try {
4587
4593
  await runDoctor();
4588
4594
  } catch (error) {
4589
- console.error(chalk15.red("Diagnostic failed: " + error));
4595
+ console.error(chalk14.red("Diagnostic failed: " + error));
4590
4596
  ui.safeExit(1);
4591
4597
  }
4592
4598
  });
@@ -4597,7 +4603,7 @@ program.command("squad [prompt]").alias("s").description("\u{1F9B8} Autonomous m
4597
4603
  }
4598
4604
  const activeConfig = getActiveProvider();
4599
4605
  if (!activeConfig) {
4600
- console.log(chalk15.red("\u274C No API key configured. Run: agdi auth"));
4606
+ console.log(chalk14.red("\u274C No API key configured. Run: agdi auth"));
4601
4607
  return;
4602
4608
  }
4603
4609
  const llm = createLLMProvider(activeConfig.provider, {
@@ -4611,7 +4617,7 @@ program.command("squad [prompt]").alias("s").description("\u{1F9B8} Autonomous m
4611
4617
  });
4612
4618
  } catch (error) {
4613
4619
  if (error.name === "ExitPromptError") {
4614
- console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
4620
+ console.log(chalk14.gray("\n\n\u{1F44B} Cancelled.\n"));
4615
4621
  try {
4616
4622
  ui.safeExit(0);
4617
4623
  } catch {
@@ -4639,7 +4645,7 @@ program.command("replay <runId>").description("\u{1F501} Replay a previous squad
4639
4645
  }
4640
4646
  const activeConfig = getActiveProvider();
4641
4647
  if (!activeConfig) {
4642
- console.log(chalk15.red("\u274C No API key configured. Run: agdi auth"));
4648
+ console.log(chalk14.red("\u274C No API key configured. Run: agdi auth"));
4643
4649
  return;
4644
4650
  }
4645
4651
  const llm = createLLMProvider(activeConfig.provider, {
@@ -4653,7 +4659,7 @@ program.command("replay <runId>").description("\u{1F501} Replay a previous squad
4653
4659
  });
4654
4660
  } catch (error) {
4655
4661
  if (error.name === "ExitPromptError") {
4656
- console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
4662
+ console.log(chalk14.gray("\n\n\u{1F44B} Cancelled.\n"));
4657
4663
  try {
4658
4664
  ui.safeExit(0);
4659
4665
  } catch {
@@ -4671,7 +4677,7 @@ program.command("import <url>").alias("i").description("\u{1F4E6} Import a GitHu
4671
4677
  await runImportCommand(url, options.output);
4672
4678
  } catch (error) {
4673
4679
  if (error.name === "ExitPromptError") {
4674
- console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
4680
+ console.log(chalk14.gray("\n\n\u{1F44B} Cancelled.\n"));
4675
4681
  try {
4676
4682
  ui.safeExit(0);
4677
4683
  } catch {