agdi 3.1.2 β†’ 3.2.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 +98 -63
  2. package/dist/index.js +153 -96
  3. package/package.json +13 -5
package/README.md CHANGED
@@ -1,111 +1,146 @@
1
1
  # Agdi CLI β€” The Autonomous AI Employee
2
2
 
3
3
  <div align="center">
4
- <img src="https://agdi-dev.vercel.app/logo.svg" alt="Agdi Logo" width="80" height="80">
4
+ <img src="https://agdi-dev.vercel.app/logo.svg" alt="Agdi Logo" width="120" height="120">
5
5
 
6
- ### 🦸 The Autonomous AI Employee
7
- ### Build, Test, and Deploy Full-Stack Apps from a Single Prompt
6
+ # 🦸 AGDI
7
+ ### The Autonomous AI Software Squad in Your Terminal
8
8
 
9
- [![npm version](https://img.shields.io/npm/v/agdi.svg?style=for-the-badge&logo=npm&color=CB3837)](https://www.npmjs.com/package/agdi)
10
- [![npm downloads](https://img.shields.io/npm/dw/agdi.svg?style=for-the-badge&logo=npm&color=4FC08D)](https://www.npmjs.com/package/agdi)
9
+ [![npm version](https://img.shields.io/npm/v/agdi.svg?style=for-the-badge&logo=npm&color=cyan)](https://www.npmjs.com/package/agdi)
10
+ [![License](https://img.shields.io/npm/l/agdi.svg?style=for-the-badge&color=blue)](https://github.com/anassagd432/Agdi-dev)
11
+ [![Downloads](https://img.shields.io/npm/dt/agdi.svg?style=for-the-badge&color=purple)](https://www.npmjs.com/package/agdi)
11
12
 
13
+ <p align="center">
14
+ <b>Build, Test, and Deploy Full-Stack SaaS Apps with a Single Prompt.</b><br>
15
+ Powered by Gemini 3, GPT-5, and Claude 4.5.
16
+ </p>
12
17
  </div>
13
18
 
14
19
  ---
15
20
 
16
- ## πŸš€ Installation
21
+ ## ⚑ What is Agdi?
17
22
 
18
- ```bash
19
- npm install -g agdi
20
- ```
23
+ Agdi isn't just a code generator. It's an **autonomous software agency** that lives in your terminal. When you give it a prompt, it doesn't just spit out codeβ€”it spins up a **Squad** of specialized AI agents that work together:
21
24
 
22
- ## πŸ“– Usage
25
+ - **🧠 The Manager:** Plans the architecture, breaks down tasks, and oversees the project.
26
+ - **🎨 The Designer:** Builds beautiful, responsive UI components (React + Tailwind + Shadcn).
27
+ - **βš™οΈ The Engineer:** Implements secure APIs, database schemas, and backend logic.
28
+ - **πŸ•΅οΈ The QA:** Runs builds, writes tests, detects errors, and **auto-fixes** them before you ever see the code.
29
+ - **πŸš€ The DevOps:** Handles deployment to Vercel/Netlify automatically.
23
30
 
24
- ### Interactive Mode (Recommended)
31
+ ---
32
+
33
+ ## πŸš€ Getting Started
34
+
35
+ ### 1. Install via npm
25
36
  ```bash
26
- agdi
37
+ npm install -g agdi
27
38
  ```
28
39
 
29
- The wizard guides you through:
30
- 1. **πŸ›‘οΈ Safety Check** β€” Prevents accidental writes to sensitive directories
31
- 2. **πŸ”‘ API Setup** β€” Configure Gemini/OpenAI/Anthropic keys
32
- 3. **🧠 Prompt** β€” Describe what you want to build
33
-
34
- ### Direct Mode
40
+ ### 2. Run the Wizard
41
+ The easiest way to start is the interactive wizard. It will guide you through authentication and project setup.
35
42
  ```bash
36
- agdi "Create a todo app with user authentication"
43
+ agdi
37
44
  ```
38
45
 
39
- ### With Options
46
+ ### 3. Build a SaaS in Seconds
47
+ Want to go fast? Use the direct build command:
40
48
  ```bash
41
- agdi --provider anthropic --model claude-opus-4 "Build a blog platform"
49
+ agdi build "A project management tool with Kanban boards and team chat"
42
50
  ```
43
51
 
44
52
  ---
45
53
 
46
- ## βš™οΈ Configuration
54
+ ## πŸ› οΈ Features
55
+
56
+ ### 🏒 The SaaS Blueprint (`--saas`)
57
+ Generate a production-ready SaaS foundation instantly:
58
+ - **Framework:** Next.js 15 (App Router)
59
+ - **Database:** Prisma + PostgreSQL
60
+ - **Auth:** Clerk / NextAuth
61
+ - **Payments:** Stripe Subscription integration
62
+ - **Styling:** Tailwind CSS + Shadcn UI
63
+
64
+ ```bash
65
+ agdi build "AI-powered CRM for real estate agents" --saas
66
+ ```
47
67
 
48
- Config is stored in `~/.agdi/config.json`:
68
+ ### πŸ”„ Auto-Healing Code
69
+ Agdi's QA agent runs in a loop. If the build fails:
70
+ 1. It analyzes the error logs.
71
+ 2. It reads the source code.
72
+ 3. It generates a surgical fix.
73
+ 4. It re-runs the build.
74
+ *You get working code, not error messages.*
49
75
 
50
- ```json
51
- {
52
- "defaultProvider": "gemini",
53
- "geminiApiKey": "your-key",
54
- "modelRouting": {
55
- "planning": "claude-opus-4",
56
- "coding": "gemini-2.5-pro",
57
- "chat": "groq/llama-3"
58
- }
59
- }
76
+ ### πŸ“¦ Import & Refactor
77
+ Have an existing repo? Import it and ask Agdi to add features.
78
+ ```bash
79
+ agdi import https://github.com/user/repo
60
80
  ```
61
81
 
62
82
  ---
63
83
 
64
- ## πŸ›‘οΈ Security Features
84
+ ## πŸ€– Supported Models
65
85
 
66
- The CLI includes **Zero-Trust** protections:
86
+ Agdi supports the absolute bleeding edge of AI models via a unified interface:
67
87
 
68
- | Feature | Description |
69
- |---------|-------------|
70
- | **PermissionGate** | Dangerous operations require explicit approval |
71
- | **Deny List** | Auto-blocks `.env`, `id_rsa`, and secrets |
72
- | **Sandbox Mode** | Option to run in isolated environment |
88
+ | Provider | Models Supported | Best For |
89
+ |----------|------------------|----------|
90
+ | **Google** | Gemini 3 Pro, 2.5 Flash | **Speed & Context** (Recommended) |
91
+ | **OpenAI** | GPT-5, GPT-4o, o1 | **Complex Logic** |
92
+ | **Anthropic** | Claude 3.5 Sonnet, Opus | **Coding & Architecture** |
93
+ | **DeepSeek** | DeepSeek V3, R1 | **Reasoning & Cost Efficiency** |
94
+ | **OpenRouter**| 100+ Models | **Flexibility** |
73
95
 
74
96
  ---
75
97
 
76
- ## 🧩 Core Modules
98
+ ## βš™οΈ Advanced Usage
77
99
 
78
- The CLI leverages Agdi's modular core:
100
+ ### Squad Mode (Multi-Agent)
101
+ For complex projects, invoke the full squad explicitly:
102
+ ```bash
103
+ agdi squad "Crypto portfolio tracker with real-time websocket updates"
104
+ ```
79
105
 
80
- - **🧠 Intelligence** β€” ThinkingEngine, ModelRouter, SkillManager
81
- - **πŸ’Ύ Memory** β€” VectorStore for project context
82
- - **πŸ› οΈ Skills** β€” Dynamic skill loading from `~/.agdi/skills/`
83
- - **🌐 Browser** β€” Playwright for web research
106
+ ### Config Management
107
+ View or edit your API keys and settings:
108
+ ```bash
109
+ agdi config
110
+ ```
111
+ Enable telemetry to help us debug crashes (fully anonymous):
112
+ ```bash
113
+ agdi config telemetry --enable
114
+ ```
84
115
 
85
116
  ---
86
117
 
87
- ## πŸ“¦ Commands
118
+ ## πŸ›‘οΈ Security
88
119
 
89
- | Command | Description |
90
- |---------|-------------|
91
- | `agdi` | Interactive wizard |
92
- | `agdi <prompt>` | Direct build from prompt |
93
- | `agdi --help` | Show all options |
94
- | `agdi --version` | Show version |
120
+ Agdi takes security seriously.
121
+ - **Zero-Trust:** It never executes dangerous shell commands without permission (unless `--yes` is used).
122
+ - **Local Keys:** API keys are stored in `~/.agdi/config.json` with `0600` permissions (readable only by you).
123
+ - **Sandboxed:** Code generation happens in your specified directory, never outside it.
95
124
 
96
125
  ---
97
126
 
98
- ## πŸ”Œ Environment Variables
127
+ ## ❓ Troubleshooting
99
128
 
100
- | Variable | Description |
101
- |----------|-------------|
102
- | `GEMINI_API_KEY` | Google Gemini API key |
103
- | `OPENAI_API_KEY` | OpenAI API key |
104
- | `ANTHROPIC_API_KEY` | Anthropic API key |
105
- | `AGDI_DEBUG` | Enable debug logging |
129
+ **"API Key Invalid"**
130
+ Run `agdi auth` to re-enter your keys. Ensure your plan covers the model you selected.
106
131
 
107
- ---
132
+ **"Build Failed"**
133
+ If the auto-healer gives up, check the logs in `runs/<id>/report.md`. You can often fix the small typo manually and run `npm run dev`.
108
134
 
109
- ## πŸ“„ License
135
+ **"Quota Exceeded"**
136
+ Switch to a cheaper model or provider:
137
+ ```bash
138
+ agdi model
139
+ ```
110
140
 
111
- MIT Β© 2026 Agdi Systems.
141
+ ---
142
+
143
+ <div align="center">
144
+ <p>Built with ❀️ by Agdi Systems Inc.</p>
145
+ <p>2026 Edition</p>
146
+ </div>
package/dist/index.js CHANGED
@@ -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) {
@@ -4021,9 +4078,9 @@ ${msg}
4021
4078
  // src/commands/wizard.ts
4022
4079
  import { input as input5, select as select3, confirm, password as password3 } from "@inquirer/prompts";
4023
4080
  import chalk13 from "chalk";
4024
- import path5 from "path";
4081
+ import path6 from "path";
4025
4082
  import os from "os";
4026
- import fs5 from "fs";
4083
+ import fs6 from "fs";
4027
4084
  async function runWizard() {
4028
4085
  await checkSafety();
4029
4086
  if (needsOnboarding()) {
@@ -4118,7 +4175,7 @@ Rewrite the original request into a comprehensive technical specification for a
4118
4175
  async function checkSafety() {
4119
4176
  const cwd = process.cwd();
4120
4177
  const home = os.homedir();
4121
- const root = path5.parse(cwd).root;
4178
+ const root = path6.parse(cwd).root;
4122
4179
  const isUnsafe = cwd === home || cwd === root;
4123
4180
  if (isUnsafe) {
4124
4181
  console.log(chalk13.yellow.bold("\n\u26A0\uFE0F Safety Warning"));
@@ -4141,9 +4198,9 @@ async function checkSafety() {
4141
4198
  default: "my-agdi-app",
4142
4199
  validate: (v) => /^[a-z0-9-_]+$/i.test(v) ? true : "Invalid folder name"
4143
4200
  });
4144
- const newPath = path5.join(cwd, folderName);
4145
- if (!fs5.existsSync(newPath)) {
4146
- fs5.mkdirSync(newPath);
4201
+ const newPath = path6.join(cwd, folderName);
4202
+ if (!fs6.existsSync(newPath)) {
4203
+ fs6.mkdirSync(newPath);
4147
4204
  }
4148
4205
  process.chdir(newPath);
4149
4206
  console.log(chalk13.green(`
@@ -4226,14 +4283,14 @@ async function setupOptionalTools() {
4226
4283
 
4227
4284
  // src/commands/replay.ts
4228
4285
  import chalk14 from "chalk";
4229
- import * as fs6 from "fs/promises";
4230
- import * as path6 from "path";
4286
+ import * as fs7 from "fs/promises";
4287
+ import * as path7 from "path";
4231
4288
  async function runReplayCommand(runId, llm, options = {}) {
4232
4289
  const workspaceRoot = options.workspace || process.cwd();
4233
- const runConfigPath = path6.join(workspaceRoot, "runs", runId, "run.json");
4234
- const outputsDir = path6.join(workspaceRoot, "runs", runId, "outputs");
4290
+ const runConfigPath = path7.join(workspaceRoot, "runs", runId, "run.json");
4291
+ const outputsDir = path7.join(workspaceRoot, "runs", runId, "outputs");
4235
4292
  try {
4236
- const raw = await fs6.readFile(runConfigPath, "utf-8");
4293
+ const raw = await fs7.readFile(runConfigPath, "utf-8");
4237
4294
  const runConfig = JSON.parse(raw);
4238
4295
  if (!runConfig.prompt) {
4239
4296
  console.log(chalk14.red("\u274C Invalid run.json (missing prompt)"));
@@ -4249,22 +4306,22 @@ async function runReplayCommand(runId, llm, options = {}) {
4249
4306
  \u{1F501} Replaying run ${runId}`));
4250
4307
  if (options.exact !== false) {
4251
4308
  try {
4252
- const entries = await fs6.readdir(outputsDir, { withFileTypes: true });
4309
+ const entries = await fs7.readdir(outputsDir, { withFileTypes: true });
4253
4310
  if (entries.length === 0) {
4254
4311
  throw new Error("No outputs found");
4255
4312
  }
4256
4313
  const copyOutputs = async (dir, rel = "") => {
4257
- const files = await fs6.readdir(dir, { withFileTypes: true });
4314
+ const files = await fs7.readdir(dir, { withFileTypes: true });
4258
4315
  for (const file of files) {
4259
- const sourcePath = path6.join(dir, file.name);
4260
- const targetRel = path6.join(rel, file.name);
4261
- const targetPath = path6.join(workspaceRoot, targetRel);
4316
+ const sourcePath = path7.join(dir, file.name);
4317
+ const targetRel = path7.join(rel, file.name);
4318
+ const targetPath = path7.join(workspaceRoot, targetRel);
4262
4319
  if (file.isDirectory()) {
4263
4320
  await copyOutputs(sourcePath, targetRel);
4264
4321
  } else if (file.isFile()) {
4265
- await fs6.mkdir(path6.dirname(targetPath), { recursive: true });
4266
- const content = await fs6.readFile(sourcePath, "utf-8");
4267
- await fs6.writeFile(targetPath, content, "utf-8");
4322
+ await fs7.mkdir(path7.dirname(targetPath), { recursive: true });
4323
+ const content = await fs7.readFile(sourcePath, "utf-8");
4324
+ await fs7.writeFile(targetPath, content, "utf-8");
4268
4325
  }
4269
4326
  }
4270
4327
  };
@@ -4307,7 +4364,7 @@ ${chalk15.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
4307
4364
  ${chalk15.cyan(` /____/ `)}
4308
4365
  `;
4309
4366
  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)");
4367
+ program.name("agdi").description(chalk15.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.2.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
4368
  program.hook("preAction", (thisCommand) => {
4312
4369
  const opts = thisCommand.opts();
4313
4370
  if (opts.yes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agdi",
3
- "version": "3.1.2",
3
+ "version": "3.2.0",
4
4
  "description": "AI-powered app generator - build full-stack apps from natural language in your terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "build": "tsup src/index.ts --format esm --clean",
22
22
  "dev": "tsx src/index.ts",
23
- "start": "node bin/agdi.js",
23
+ "start": "tsx src/dev.ts",
24
24
  "test": "vitest run",
25
25
  "typecheck": "tsc --noEmit",
26
26
  "prepublishOnly": "npm run build"
@@ -60,7 +60,7 @@
60
60
  "@types/node": "^20.0.0",
61
61
  "@types/uuid": "^10.0.0",
62
62
  "tsup": "^8.0.0",
63
- "tsx": "^4.7.0",
63
+ "tsx": "^4.21.0",
64
64
  "typescript": "^5.4.0",
65
65
  "vitest": "^3.0.0"
66
66
  },
@@ -70,7 +70,7 @@
70
70
  "@babel/traverse": "^7.28.6",
71
71
  "@babel/types": "^7.28.6",
72
72
  "@google/genai": "^1.0.0",
73
- "@inquirer/prompts": "^5.0.0",
73
+ "@inquirer/prompts": "^8.2.0",
74
74
  "better-sqlite3": "^12.6.2",
75
75
  "boxen": "^8.0.1",
76
76
  "chalk": "^5.3.0",
@@ -78,8 +78,16 @@
78
78
  "figlet": "^1.9.4",
79
79
  "fs-extra": "^11.2.0",
80
80
  "gradient-string": "^3.0.0",
81
+ "ink": "^6.6.0",
82
+ "ink-big-text": "^2.0.0",
83
+ "ink-gradient": "^3.0.0",
84
+ "ink-spinner": "^5.0.0",
85
+ "ink-text-input": "^6.0.0",
86
+ "ink-use-stdout-dimensions": "^1.0.5",
81
87
  "jszip": "^3.10.0",
82
88
  "ora": "^8.0.0",
89
+ "react": "^19.2.4",
90
+ "ts-node": "^10.9.2",
83
91
  "uuid": "^13.0.0"
84
92
  }
85
- }
93
+ }