agdi 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +321 -93
  2. package/dist/index.js +466 -105
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
 
9
9
  // src/index.ts
10
10
  import { Command } from "commander";
11
- import chalk12 from "chalk";
12
- import ora4 from "ora";
11
+ import chalk13 from "chalk";
12
+ import ora5 from "ora";
13
13
 
14
14
  // src/core/llm/index.ts
15
15
  var PuterProvider = class {
@@ -574,6 +574,50 @@ var MALICIOUS_PATTERNS = [
574
574
  category: "dangerous",
575
575
  description: "Template literal with shell commands",
576
576
  severity: "medium"
577
+ },
578
+ // ==================== PROMPT INJECTION DEFENSE ====================
579
+ {
580
+ pattern: /ignore\s+(all\s+)?(previous|above|prior)\s+(instructions?|prompts?|rules?)/gi,
581
+ category: "suspicious",
582
+ description: "Prompt injection: instruction override attempt",
583
+ severity: "critical"
584
+ },
585
+ {
586
+ pattern: /\[SYSTEM\]|\[INST\]|<\|im_start\|>|<\|endoftext\|>/gi,
587
+ category: "suspicious",
588
+ description: "Prompt injection: model control tokens",
589
+ severity: "critical"
590
+ },
591
+ {
592
+ pattern: /you\s+are\s+(now\s+)?(a|an|the)\s+\w+\s+(that|who|which)/gi,
593
+ category: "suspicious",
594
+ description: "Prompt injection: role override attempt",
595
+ severity: "high"
596
+ },
597
+ {
598
+ pattern: /disregard\s+(all\s+)?safety|bypass\s+(all\s+)?security|disable\s+(all\s+)?restrictions/gi,
599
+ category: "suspicious",
600
+ description: "Prompt injection: safety bypass attempt",
601
+ severity: "critical"
602
+ },
603
+ {
604
+ pattern: /\bexec\s*\(\s*[`'"].*?\$\(.*?\)/g,
605
+ category: "dangerous",
606
+ description: "Command injection via exec with substitution",
607
+ severity: "critical"
608
+ },
609
+ // ==================== NETWORK EXFILTRATION ====================
610
+ {
611
+ pattern: /\bfetch\s*\(\s*['"`]https?:\/\/[^'"`]*\.(ru|cn|tk|ml|ga)\//gi,
612
+ category: "suspicious",
613
+ description: "Request to suspicious TLD",
614
+ severity: "high"
615
+ },
616
+ {
617
+ pattern: /\bnew\s+WebSocket\s*\(\s*['"`]wss?:\/\/(?!localhost|127\.0\.0\.1)/g,
618
+ category: "suspicious",
619
+ description: "WebSocket to external server",
620
+ severity: "medium"
577
621
  }
578
622
  ];
579
623
  function scanCode(code, filename) {
@@ -1370,9 +1414,9 @@ async function selectModel() {
1370
1414
  }
1371
1415
 
1372
1416
  // src/commands/agdi-dev.ts
1373
- import { input as input4, confirm as confirm2 } from "@inquirer/prompts";
1374
- import chalk11 from "chalk";
1375
- import ora3 from "ora";
1417
+ import { input as input5, confirm as confirm3 } from "@inquirer/prompts";
1418
+ import chalk12 from "chalk";
1419
+ import ora4 from "ora";
1376
1420
 
1377
1421
  // src/actions/plan-executor.ts
1378
1422
  import { select as select4, confirm } from "@inquirer/prompts";
@@ -1430,6 +1474,40 @@ function summarizePlan(plan) {
1430
1474
  riskTier: maxRiskTier
1431
1475
  };
1432
1476
  }
1477
+ function validateAction(action) {
1478
+ if (!action || typeof action !== "object") return null;
1479
+ const obj = action;
1480
+ const type = obj.type;
1481
+ if (type === "mkdir") {
1482
+ if (typeof obj.path !== "string" || !obj.path) return null;
1483
+ if (obj.path.includes("..") || obj.path.startsWith("/") || /^[A-Z]:/i.test(obj.path)) return null;
1484
+ return { type: "mkdir", path: obj.path };
1485
+ }
1486
+ if (type === "writeFile") {
1487
+ if (typeof obj.path !== "string" || !obj.path) return null;
1488
+ if (typeof obj.content !== "string") return null;
1489
+ if (obj.path.includes("..") || obj.path.startsWith("/") || /^[A-Z]:/i.test(obj.path)) return null;
1490
+ return { type: "writeFile", path: obj.path, content: obj.content };
1491
+ }
1492
+ if (type === "deleteFile") {
1493
+ if (typeof obj.path !== "string" || !obj.path) return null;
1494
+ if (obj.path.includes("..") || obj.path.startsWith("/") || /^[A-Z]:/i.test(obj.path)) return null;
1495
+ return { type: "deleteFile", path: obj.path };
1496
+ }
1497
+ if (type === "exec") {
1498
+ if (!Array.isArray(obj.argv) || obj.argv.length === 0) return null;
1499
+ if (!obj.argv.every((a) => typeof a === "string")) return null;
1500
+ const dangerous = ["sudo", "su", "rm -rf /", "format", "mkfs", "dd", ":(){"];
1501
+ const cmdStr = obj.argv.join(" ").toLowerCase();
1502
+ if (dangerous.some((d) => cmdStr.includes(d))) return null;
1503
+ return {
1504
+ type: "exec",
1505
+ argv: obj.argv,
1506
+ cwd: typeof obj.cwd === "string" ? obj.cwd : void 0
1507
+ };
1508
+ }
1509
+ return null;
1510
+ }
1433
1511
  function parseActionPlan(response) {
1434
1512
  try {
1435
1513
  const jsonMatch = response.match(/\{[\s\S]*"actions"[\s\S]*\}/);
@@ -1440,10 +1518,23 @@ function parseActionPlan(response) {
1440
1518
  if (!parsed.actions || !Array.isArray(parsed.actions)) {
1441
1519
  return null;
1442
1520
  }
1521
+ const validatedActions = [];
1522
+ for (const action of parsed.actions) {
1523
+ const validated = validateAction(action);
1524
+ if (!validated) {
1525
+ console.warn(`Invalid action rejected: ${JSON.stringify(action).slice(0, 100)}`);
1526
+ continue;
1527
+ }
1528
+ validatedActions.push(validated);
1529
+ }
1530
+ if (validatedActions.length === 0) {
1531
+ return null;
1532
+ }
1533
+ const projectName = typeof parsed.projectName === "string" ? parsed.projectName.replace(/[^a-zA-Z0-9-_]/g, "-").slice(0, 50) : "generated-app";
1443
1534
  return {
1444
- projectName: parsed.projectName || "generated-app",
1445
- actions: parsed.actions,
1446
- nextSteps: parsed.nextSteps
1535
+ projectName,
1536
+ actions: validatedActions,
1537
+ nextSteps: typeof parsed.nextSteps === "string" ? parsed.nextSteps : void 0
1447
1538
  };
1448
1539
  } catch {
1449
1540
  return null;
@@ -1760,6 +1851,68 @@ async function deleteFileTool(path4) {
1760
1851
  return { success: false, error: msg };
1761
1852
  }
1762
1853
  }
1854
+ async function applyPatchTool(path4, unifiedDiff) {
1855
+ const validation = validatePath(path4);
1856
+ if (!validation.valid) {
1857
+ return { success: false, error: validation.error };
1858
+ }
1859
+ try {
1860
+ const current = existsSync3(validation.resolved) ? await readFile(validation.resolved, "utf-8") : "";
1861
+ const patched = applySimplePatch(current, unifiedDiff);
1862
+ if (patched === null) {
1863
+ return { success: false, error: "Failed to apply patch" };
1864
+ }
1865
+ await writeFile(validation.resolved, patched, "utf-8");
1866
+ logEvent({
1867
+ eventType: "command_result",
1868
+ command: `applyPatch ${path4}`,
1869
+ result: { exitCode: 0 },
1870
+ metadata: {
1871
+ tool: "applyPatchTool",
1872
+ path: validation.resolved,
1873
+ patchLength: unifiedDiff.length
1874
+ }
1875
+ });
1876
+ return { success: true };
1877
+ } catch (error) {
1878
+ const msg = error instanceof Error ? error.message : String(error);
1879
+ return { success: false, error: msg };
1880
+ }
1881
+ }
1882
+ function applySimplePatch(content, diff) {
1883
+ const lines = content.split("\n");
1884
+ const diffLines = diff.split("\n");
1885
+ let lineIndex = 0;
1886
+ const result = [];
1887
+ for (const diffLine of diffLines) {
1888
+ if (diffLine.startsWith("---") || diffLine.startsWith("+++") || diffLine.startsWith("@@")) {
1889
+ continue;
1890
+ }
1891
+ if (diffLine.startsWith("-")) {
1892
+ lineIndex++;
1893
+ } else if (diffLine.startsWith("+")) {
1894
+ result.push(diffLine.slice(1));
1895
+ } else if (diffLine.startsWith(" ") || diffLine === "") {
1896
+ if (lineIndex < lines.length) {
1897
+ result.push(lines[lineIndex]);
1898
+ lineIndex++;
1899
+ }
1900
+ }
1901
+ }
1902
+ while (lineIndex < lines.length) {
1903
+ result.push(lines[lineIndex]);
1904
+ lineIndex++;
1905
+ }
1906
+ return result.join("\n");
1907
+ }
1908
+ function resolvePath(path4) {
1909
+ const env = getEnvironment();
1910
+ return resolve(env.workspaceRoot, path4);
1911
+ }
1912
+ function fileExists(path4) {
1913
+ const resolved = resolvePath(path4);
1914
+ return existsSync3(resolved);
1915
+ }
1763
1916
 
1764
1917
  // src/security/permission-gate.ts
1765
1918
  import { resolve as resolve2, relative as relative2, isAbsolute as isAbsolute2 } from "path";
@@ -3455,6 +3608,204 @@ function clearConversation() {
3455
3608
  }
3456
3609
  }
3457
3610
 
3611
+ // src/core/file-editor.ts
3612
+ import { readFile as readFile2 } from "fs/promises";
3613
+ import { existsSync as existsSync9 } from "fs";
3614
+ import chalk11 from "chalk";
3615
+ import ora3 from "ora";
3616
+ import { input as input4, confirm as confirm2 } from "@inquirer/prompts";
3617
+ var EDIT_SYSTEM_PROMPT = `You are a surgical code editor. Given a file's content and an edit instruction, output ONLY a unified diff patch.
3618
+
3619
+ ## Output Format (STRICT)
3620
+ \`\`\`diff
3621
+ --- a/filename
3622
+ +++ b/filename
3623
+ @@ -start,count +start,count @@
3624
+ context line (unchanged)
3625
+ -removed line
3626
+ +added line
3627
+ context line (unchanged)
3628
+ \`\`\`
3629
+
3630
+ ## Rules
3631
+ 1. Output ONLY the diff block, no explanation before or after
3632
+ 2. Include 3 context lines around each change
3633
+ 3. Use proper unified diff format with @@ hunk headers
3634
+ 4. Preserve exact indentation (spaces/tabs)
3635
+ 5. Multiple changes = multiple @@ hunks
3636
+ 6. Line numbers in @@ must be accurate
3637
+
3638
+ ## Example
3639
+ Input: "Add a console.log at line 5"
3640
+ Output:
3641
+ \`\`\`diff
3642
+ --- a/index.ts
3643
+ +++ b/index.ts
3644
+ @@ -3,6 +3,7 @@
3645
+ import { foo } from './foo';
3646
+
3647
+ function main() {
3648
+ + console.log('Debug point');
3649
+ const result = foo();
3650
+ return result;
3651
+ }
3652
+ \`\`\``;
3653
+ async function readFileForEdit(filePath) {
3654
+ const resolved = resolvePath(filePath);
3655
+ if (!existsSync9(resolved)) {
3656
+ return null;
3657
+ }
3658
+ try {
3659
+ const content = await readFile2(resolved, "utf-8");
3660
+ const lines = content.split("\n");
3661
+ const numbered = lines.map((line, i) => `${String(i + 1).padStart(4, " ")} \u2502 ${line}`).join("\n");
3662
+ return { content, numbered };
3663
+ } catch {
3664
+ return null;
3665
+ }
3666
+ }
3667
+ function extractDiff(response) {
3668
+ const diffMatch = response.match(/```diff\n([\s\S]*?)```/);
3669
+ if (diffMatch) {
3670
+ return diffMatch[1].trim();
3671
+ }
3672
+ const codeMatch = response.match(/```\n([\s\S]*?)```/);
3673
+ if (codeMatch && codeMatch[1].includes("@@")) {
3674
+ return codeMatch[1].trim();
3675
+ }
3676
+ if (response.includes("@@") && (response.includes("---") || response.includes("+++"))) {
3677
+ return response.trim();
3678
+ }
3679
+ return null;
3680
+ }
3681
+ function previewDiff(diff) {
3682
+ console.log(chalk11.cyan.bold("\n\u{1F4DD} Proposed Changes:\n"));
3683
+ const lines = diff.split("\n");
3684
+ for (const line of lines) {
3685
+ if (line.startsWith("+++") || line.startsWith("---")) {
3686
+ console.log(chalk11.gray(line));
3687
+ } else if (line.startsWith("@@")) {
3688
+ console.log(chalk11.cyan(line));
3689
+ } else if (line.startsWith("+")) {
3690
+ console.log(chalk11.green(line));
3691
+ } else if (line.startsWith("-")) {
3692
+ console.log(chalk11.red(line));
3693
+ } else {
3694
+ console.log(chalk11.gray(line));
3695
+ }
3696
+ }
3697
+ console.log("");
3698
+ }
3699
+ function countChanges(diff) {
3700
+ const lines = diff.split("\n");
3701
+ let added = 0;
3702
+ let removed = 0;
3703
+ for (const line of lines) {
3704
+ if (line.startsWith("+") && !line.startsWith("+++")) {
3705
+ added++;
3706
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
3707
+ removed++;
3708
+ }
3709
+ }
3710
+ return { added, removed };
3711
+ }
3712
+ async function handleFileEdit(filePath, llm) {
3713
+ const env = getEnvironment();
3714
+ if (!fileExists(filePath)) {
3715
+ console.log(chalk11.red(`
3716
+ \u2717 File not found: ${filePath}
3717
+ `));
3718
+ return { success: false, error: "File not found" };
3719
+ }
3720
+ const fileData = await readFileForEdit(filePath);
3721
+ if (!fileData) {
3722
+ console.log(chalk11.red(`
3723
+ \u2717 Could not read file: ${filePath}
3724
+ `));
3725
+ return { success: false, error: "Could not read file" };
3726
+ }
3727
+ const previewLines = fileData.numbered.split("\n").slice(0, 20);
3728
+ console.log(chalk11.cyan.bold(`
3729
+ \u{1F4C4} ${filePath}
3730
+ `));
3731
+ console.log(chalk11.gray(previewLines.join("\n")));
3732
+ if (fileData.content.split("\n").length > 20) {
3733
+ console.log(chalk11.gray(` ... (${fileData.content.split("\n").length - 20} more lines)`));
3734
+ }
3735
+ console.log("");
3736
+ const instruction = await input4({
3737
+ message: chalk11.yellow("Describe the edit:")
3738
+ });
3739
+ if (!instruction.trim()) {
3740
+ console.log(chalk11.gray("\n(no instruction provided)\n"));
3741
+ return { success: false, error: "No instruction" };
3742
+ }
3743
+ const spinner = ora3("Generating edit...").start();
3744
+ try {
3745
+ const prompt = `File: ${filePath}
3746
+
3747
+ Content:
3748
+ \`\`\`
3749
+ ${fileData.content}
3750
+ \`\`\`
3751
+
3752
+ Edit instruction: ${instruction}
3753
+
3754
+ Generate the unified diff to make this change.`;
3755
+ const response = await llm.generate(prompt, EDIT_SYSTEM_PROMPT);
3756
+ spinner.stop();
3757
+ const diff = extractDiff(response.text);
3758
+ if (!diff) {
3759
+ console.log(chalk11.yellow("\n\u26A0\uFE0F Could not generate a valid diff.\n"));
3760
+ console.log(chalk11.gray("AI response:\n" + response.text.slice(0, 500)));
3761
+ return { success: false, error: "Invalid diff generated" };
3762
+ }
3763
+ previewDiff(diff);
3764
+ const changes = countChanges(diff);
3765
+ console.log(chalk11.gray(` ${chalk11.green(`+${changes.added}`)} additions, ${chalk11.red(`-${changes.removed}`)} deletions
3766
+ `));
3767
+ const shouldApply = await confirm2({
3768
+ message: "Apply these changes?",
3769
+ default: true
3770
+ });
3771
+ if (!shouldApply) {
3772
+ console.log(chalk11.gray("\n\u{1F44B} Edit cancelled.\n"));
3773
+ return { success: false, error: "Cancelled by user" };
3774
+ }
3775
+ const applySpinner = ora3("Applying changes...").start();
3776
+ const result = await applyPatchTool(filePath, diff);
3777
+ applySpinner.stop();
3778
+ if (result.success) {
3779
+ console.log(chalk11.green(`
3780
+ \u2713 Successfully edited ${filePath}
3781
+ `));
3782
+ logEvent({
3783
+ eventType: "command_result",
3784
+ command: `/edit ${filePath}`,
3785
+ result: { exitCode: 0 },
3786
+ metadata: {
3787
+ instruction,
3788
+ added: changes.added,
3789
+ removed: changes.removed
3790
+ }
3791
+ });
3792
+ return { success: true, linesChanged: changes.added + changes.removed };
3793
+ } else {
3794
+ console.log(chalk11.red(`
3795
+ \u2717 Failed to apply changes: ${result.error}
3796
+ `));
3797
+ return { success: false, error: result.error };
3798
+ }
3799
+ } catch (error) {
3800
+ spinner.stop();
3801
+ const msg = error instanceof Error ? error.message : String(error);
3802
+ console.log(chalk11.red(`
3803
+ \u2717 Error: ${msg}
3804
+ `));
3805
+ return { success: false, error: msg };
3806
+ }
3807
+ }
3808
+
3458
3809
  // src/commands/agdi-dev.ts
3459
3810
  var BASE_CHAT_PROMPT = `You are Agdi dev, an elite AI coding assistant. You help developers write code, debug issues, and build applications.
3460
3811
 
@@ -3524,7 +3875,7 @@ Instead, output a single JSON object with this exact structure:
3524
3875
  async function startCodingMode() {
3525
3876
  const activeConfig = getActiveProvider();
3526
3877
  if (!activeConfig) {
3527
- console.log(chalk11.red("\u274C No API key configured. Run: agdi"));
3878
+ console.log(chalk12.red("\u274C No API key configured. Run: agdi"));
3528
3879
  return;
3529
3880
  }
3530
3881
  const { provider, apiKey, model } = activeConfig;
@@ -3536,24 +3887,24 @@ async function startCodingMode() {
3536
3887
  process.exit(0);
3537
3888
  }
3538
3889
  logSessionStart(env.workspaceRoot, env.trustLevel);
3539
- console.log(chalk11.cyan.bold("\u26A1 Agdi dev\n"));
3540
- console.log(chalk11.gray(`Model: ${chalk11.cyan(model)}`));
3541
- console.log(chalk11.gray(`Workspace: ${chalk11.cyan(env.workspaceRoot)}`));
3542
- console.log(chalk11.gray("Commands: /status, /diff, /commit, /build, /clear, /history, /model, /help, /exit\n"));
3543
- console.log(chalk11.gray("\u2500".repeat(50) + "\n"));
3890
+ console.log(chalk12.cyan.bold("\u26A1 Agdi dev\n"));
3891
+ console.log(chalk12.gray(`Model: ${chalk12.cyan(model)}`));
3892
+ console.log(chalk12.gray(`Workspace: ${chalk12.cyan(env.workspaceRoot)}`));
3893
+ console.log(chalk12.gray("Commands: /status, /diff, /commit, /build, /clear, /history, /model, /help, /exit\n"));
3894
+ console.log(chalk12.gray("\u2500".repeat(50) + "\n"));
3544
3895
  const pm = new ProjectManager();
3545
3896
  let llm = createLLMProvider(provider, { apiKey, model });
3546
3897
  const conversation = getConversation();
3547
3898
  conversation.setSystemPrompt(buildContextAwarePrompt());
3548
3899
  while (true) {
3549
3900
  try {
3550
- const userInput = await input4({
3551
- message: chalk11.green("\u2192")
3901
+ const userInput = await input5({
3902
+ message: chalk12.green("\u2192")
3552
3903
  });
3553
3904
  const trimmed = userInput.trim().toLowerCase();
3554
3905
  if (trimmed === "/exit" || trimmed === "exit" || trimmed === "quit") {
3555
3906
  logSessionEnd();
3556
- console.log(chalk11.gray("\n\u{1F44B} Goodbye!\n"));
3907
+ console.log(chalk12.gray("\n\u{1F44B} Goodbye!\n"));
3557
3908
  break;
3558
3909
  }
3559
3910
  if (trimmed === "/help") {
@@ -3568,34 +3919,34 @@ async function startCodingMode() {
3568
3919
  apiKey: newConfig.apiKey,
3569
3920
  model: newConfig.model
3570
3921
  });
3571
- console.log(chalk11.gray(`Now using: ${chalk11.cyan(newConfig.model)}
3922
+ console.log(chalk12.gray(`Now using: ${chalk12.cyan(newConfig.model)}
3572
3923
  `));
3573
3924
  }
3574
3925
  continue;
3575
3926
  }
3576
3927
  if (trimmed === "/chat") {
3577
- console.log(chalk11.gray("\nSwitching to chat mode. Type /code to return.\n"));
3928
+ console.log(chalk12.gray("\nSwitching to chat mode. Type /code to return.\n"));
3578
3929
  await chatMode(llm);
3579
3930
  continue;
3580
3931
  }
3581
3932
  if (trimmed === "/clear") {
3582
3933
  clearConversation();
3583
3934
  conversation.setSystemPrompt(buildContextAwarePrompt());
3584
- console.log(chalk11.green("\n\u2713 Conversation cleared.\n"));
3935
+ console.log(chalk12.green("\n\u2713 Conversation cleared.\n"));
3585
3936
  continue;
3586
3937
  }
3587
3938
  if (trimmed === "/history") {
3588
3939
  const messages = conversation.getMessages();
3589
3940
  if (messages.length === 0) {
3590
- console.log(chalk11.gray("\n(no conversation history)\n"));
3941
+ console.log(chalk12.gray("\n(no conversation history)\n"));
3591
3942
  } else {
3592
- console.log(chalk11.cyan.bold("\n\u{1F4DC} Conversation History\n"));
3593
- console.log(chalk11.gray(conversation.getSummary()));
3943
+ console.log(chalk12.cyan.bold("\n\u{1F4DC} Conversation History\n"));
3944
+ console.log(chalk12.gray(conversation.getSummary()));
3594
3945
  console.log("");
3595
3946
  for (const msg of messages.slice(-6)) {
3596
- const role = msg.role === "user" ? chalk11.green("You") : chalk11.cyan("AI");
3947
+ const role = msg.role === "user" ? chalk12.green("You") : chalk12.cyan("AI");
3597
3948
  const preview = msg.content.slice(0, 80) + (msg.content.length > 80 ? "..." : "");
3598
- console.log(` ${role}: ${chalk11.gray(preview)}`);
3949
+ console.log(` ${role}: ${chalk12.gray(preview)}`);
3599
3950
  }
3600
3951
  console.log("");
3601
3952
  }
@@ -3613,12 +3964,21 @@ async function startCodingMode() {
3613
3964
  await handleGitCommit(llm);
3614
3965
  continue;
3615
3966
  }
3967
+ if (trimmed.startsWith("/edit ")) {
3968
+ const filePath = userInput.slice(6).trim();
3969
+ if (filePath) {
3970
+ await handleFileEdit(filePath, llm);
3971
+ } else {
3972
+ console.log(chalk12.yellow("\nUsage: /edit <file>\n"));
3973
+ }
3974
+ continue;
3975
+ }
3616
3976
  if (trimmed.startsWith("/build ") || trimmed.startsWith("build ")) {
3617
3977
  const prompt = userInput.replace(/^\/?build\s+/i, "").trim();
3618
3978
  if (prompt) {
3619
3979
  await buildAppWithPlan(prompt, llm);
3620
3980
  } else {
3621
- console.log(chalk11.yellow("\nUsage: /build <description>\n"));
3981
+ console.log(chalk12.yellow("\nUsage: /build <description>\n"));
3622
3982
  }
3623
3983
  continue;
3624
3984
  }
@@ -3630,7 +3990,7 @@ async function startCodingMode() {
3630
3990
  await buildAppWithPlan(userInput, llm);
3631
3991
  continue;
3632
3992
  }
3633
- const spinner = ora3("Thinking...").start();
3993
+ const spinner = ora4("Thinking...").start();
3634
3994
  try {
3635
3995
  conversation.addUserMessage(userInput);
3636
3996
  let response;
@@ -3650,7 +4010,7 @@ async function startCodingMode() {
3650
4010
  } catch (error) {
3651
4011
  if (error.name === "ExitPromptError") {
3652
4012
  logSessionEnd();
3653
- console.log(chalk11.gray("\n\n\u{1F44B} Goodbye!\n"));
4013
+ console.log(chalk12.gray("\n\n\u{1F44B} Goodbye!\n"));
3654
4014
  process.exit(0);
3655
4015
  }
3656
4016
  throw error;
@@ -3658,13 +4018,13 @@ async function startCodingMode() {
3658
4018
  }
3659
4019
  }
3660
4020
  async function buildAppWithPlan(prompt, llm) {
3661
- const spinner = ora3("Generating action plan...").start();
4021
+ const spinner = ora4("Generating action plan...").start();
3662
4022
  try {
3663
4023
  const response = await llm.generate(prompt, BUILD_SYSTEM_PROMPT);
3664
4024
  spinner.stop();
3665
4025
  const result = await parseAndExecutePlan(response.text);
3666
4026
  if (!result) {
3667
- console.log(chalk11.yellow("\n\u26A0\uFE0F Model did not return an action plan. Showing response:\n"));
4027
+ console.log(chalk12.yellow("\n\u26A0\uFE0F Model did not return an action plan. Showing response:\n"));
3668
4028
  console.log(formatResponse(response.text) + "\n");
3669
4029
  }
3670
4030
  } catch (error) {
@@ -3675,15 +4035,15 @@ async function buildAppWithPlan(prompt, llm) {
3675
4035
  async function chatMode(llm) {
3676
4036
  while (true) {
3677
4037
  try {
3678
- const userInput = await input4({
3679
- message: chalk11.blue("\u{1F4AC}")
4038
+ const userInput = await input5({
4039
+ message: chalk12.blue("\u{1F4AC}")
3680
4040
  });
3681
4041
  if (userInput.toLowerCase() === "/code" || userInput.toLowerCase() === "/exit") {
3682
- console.log(chalk11.gray("\nBack to Agdi dev mode.\n"));
4042
+ console.log(chalk12.gray("\nBack to Agdi dev mode.\n"));
3683
4043
  return;
3684
4044
  }
3685
4045
  if (!userInput.trim()) continue;
3686
- const spinner = ora3("...").start();
4046
+ const spinner = ora4("...").start();
3687
4047
  const response = await llm.generate(userInput, "You are a helpful assistant. Be friendly and concise.");
3688
4048
  spinner.stop();
3689
4049
  console.log("\n" + response.text + "\n");
@@ -3697,10 +4057,10 @@ async function chatMode(llm) {
3697
4057
  }
3698
4058
  async function handleGitStatus(llm) {
3699
4059
  if (!isGitRepo()) {
3700
- console.log(chalk11.yellow("\n\u26A0\uFE0F Not a git repository\n"));
4060
+ console.log(chalk12.yellow("\n\u26A0\uFE0F Not a git repository\n"));
3701
4061
  return;
3702
4062
  }
3703
- const spinner = ora3("Analyzing git status...").start();
4063
+ const spinner = ora4("Analyzing git status...").start();
3704
4064
  try {
3705
4065
  const status = getStatus();
3706
4066
  const statusText = formatStatusForPrompt(status);
@@ -3709,9 +4069,9 @@ async function handleGitStatus(llm) {
3709
4069
  ${statusText}`;
3710
4070
  const response = await llm.generate(prompt, BASE_CHAT_PROMPT);
3711
4071
  spinner.stop();
3712
- console.log(chalk11.cyan.bold("\n\u{1F4CA} Git Status Analysis\n"));
3713
- console.log(chalk11.gray(statusText));
3714
- console.log(chalk11.cyan("\n\u2500\u2500\u2500 AI Analysis \u2500\u2500\u2500\n"));
4072
+ console.log(chalk12.cyan.bold("\n\u{1F4CA} Git Status Analysis\n"));
4073
+ console.log(chalk12.gray(statusText));
4074
+ console.log(chalk12.cyan("\n\u2500\u2500\u2500 AI Analysis \u2500\u2500\u2500\n"));
3715
4075
  console.log(formatResponse(response.text) + "\n");
3716
4076
  } catch (error) {
3717
4077
  spinner.fail("Error analyzing status");
@@ -3720,16 +4080,16 @@ ${statusText}`;
3720
4080
  }
3721
4081
  async function handleGitDiff(llm) {
3722
4082
  if (!isGitRepo()) {
3723
- console.log(chalk11.yellow("\n\u26A0\uFE0F Not a git repository\n"));
4083
+ console.log(chalk12.yellow("\n\u26A0\uFE0F Not a git repository\n"));
3724
4084
  return;
3725
4085
  }
3726
- const spinner = ora3("Analyzing changes...").start();
4086
+ const spinner = ora4("Analyzing changes...").start();
3727
4087
  try {
3728
4088
  const stagedDiff = getDiff(true);
3729
4089
  const unstagedDiff = getDiff(false);
3730
4090
  if (stagedDiff.files.length === 0 && unstagedDiff.files.length === 0) {
3731
4091
  spinner.stop();
3732
- console.log(chalk11.gray("\n(no changes to analyze)\n"));
4092
+ console.log(chalk12.gray("\n(no changes to analyze)\n"));
3733
4093
  return;
3734
4094
  }
3735
4095
  let diffContext = "";
@@ -3744,7 +4104,7 @@ async function handleGitDiff(llm) {
3744
4104
  ${diffContext}`;
3745
4105
  const response = await llm.generate(prompt, BASE_CHAT_PROMPT);
3746
4106
  spinner.stop();
3747
- console.log(chalk11.cyan.bold("\n\u{1F50D} Diff Analysis\n"));
4107
+ console.log(chalk12.cyan.bold("\n\u{1F50D} Diff Analysis\n"));
3748
4108
  console.log(formatResponse(response.text) + "\n");
3749
4109
  } catch (error) {
3750
4110
  spinner.fail("Error analyzing diff");
@@ -3753,15 +4113,15 @@ ${diffContext}`;
3753
4113
  }
3754
4114
  async function handleGitCommit(llm) {
3755
4115
  if (!isGitRepo()) {
3756
- console.log(chalk11.yellow("\n\u26A0\uFE0F Not a git repository\n"));
4116
+ console.log(chalk12.yellow("\n\u26A0\uFE0F Not a git repository\n"));
3757
4117
  return;
3758
4118
  }
3759
4119
  const status = getStatus();
3760
4120
  if (status.staged.length === 0) {
3761
- console.log(chalk11.yellow("\n\u26A0\uFE0F No staged changes. Stage some changes first with `git add`.\n"));
4121
+ console.log(chalk12.yellow("\n\u26A0\uFE0F No staged changes. Stage some changes first with `git add`.\n"));
3762
4122
  return;
3763
4123
  }
3764
- const spinner = ora3("Generating commit message...").start();
4124
+ const spinner = ora4("Generating commit message...").start();
3765
4125
  try {
3766
4126
  const stagedDiff = getDiff(true);
3767
4127
  const diffText = formatDiffForPrompt(stagedDiff);
@@ -3776,10 +4136,10 @@ ${diffText}`;
3776
4136
  const response = await llm.generate(prompt, "You are a git commit message generator. Output ONLY the commit message, no explanation.");
3777
4137
  spinner.stop();
3778
4138
  const commitMessage = response.text.trim().split("\n")[0];
3779
- console.log(chalk11.cyan.bold("\n\u{1F4AC} Generated Commit Message\n"));
3780
- console.log(chalk11.white(` ${commitMessage}
4139
+ console.log(chalk12.cyan.bold("\n\u{1F4AC} Generated Commit Message\n"));
4140
+ console.log(chalk12.white(` ${commitMessage}
3781
4141
  `));
3782
- const shouldCommit = await confirm2({
4142
+ const shouldCommit = await confirm3({
3783
4143
  message: "Commit with this message?",
3784
4144
  default: true
3785
4145
  });
@@ -3791,12 +4151,12 @@ ${diffText}`;
3791
4151
  cwd: env.workspaceRoot,
3792
4152
  stdio: "inherit"
3793
4153
  });
3794
- console.log(chalk11.green("\n\u2713 Committed successfully!\n"));
4154
+ console.log(chalk12.green("\n\u2713 Committed successfully!\n"));
3795
4155
  } catch (gitError) {
3796
- console.log(chalk11.red("\n\u2717 Commit failed. Check git output above.\n"));
4156
+ console.log(chalk12.red("\n\u2717 Commit failed. Check git output above.\n"));
3797
4157
  }
3798
4158
  } else {
3799
- console.log(chalk11.gray("\n\u{1F44B} Commit cancelled.\n"));
4159
+ console.log(chalk12.gray("\n\u{1F44B} Commit cancelled.\n"));
3800
4160
  }
3801
4161
  } catch (error) {
3802
4162
  spinner.fail("Error generating commit");
@@ -3805,61 +4165,62 @@ ${diffText}`;
3805
4165
  }
3806
4166
  function formatResponse(text) {
3807
4167
  return text.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => {
3808
- const header = lang ? chalk11.gray(`\u2500\u2500 ${lang} \u2500\u2500`) : chalk11.gray("\u2500\u2500 code \u2500\u2500");
4168
+ const header = lang ? chalk12.gray(`\u2500\u2500 ${lang} \u2500\u2500`) : chalk12.gray("\u2500\u2500 code \u2500\u2500");
3809
4169
  return `
3810
4170
  ${header}
3811
- ${chalk11.white(code.trim())}
3812
- ${chalk11.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
4171
+ ${chalk12.white(code.trim())}
4172
+ ${chalk12.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
3813
4173
  `;
3814
4174
  });
3815
4175
  }
3816
4176
  function showHelp() {
3817
- console.log(chalk11.cyan.bold("\n\u{1F4D6} Commands\n"));
3818
- console.log(chalk11.cyan(" Git Commands:"));
3819
- console.log(chalk11.gray(" /status ") + "AI analysis of git status");
3820
- console.log(chalk11.gray(" /diff ") + "AI explanation of current changes");
3821
- console.log(chalk11.gray(" /commit ") + "Generate and run git commit");
4177
+ console.log(chalk12.cyan.bold("\n\u{1F4D6} Commands\n"));
4178
+ console.log(chalk12.cyan(" Git Commands:"));
4179
+ console.log(chalk12.gray(" /status ") + "AI analysis of git status");
4180
+ console.log(chalk12.gray(" /diff ") + "AI explanation of current changes");
4181
+ console.log(chalk12.gray(" /commit ") + "Generate and run git commit");
3822
4182
  console.log("");
3823
- console.log(chalk11.cyan(" Build Commands:"));
3824
- console.log(chalk11.gray(" /build ") + "Generate and execute an application");
4183
+ console.log(chalk12.cyan(" Build Commands:"));
4184
+ console.log(chalk12.gray(" /build ") + "Generate and execute an application");
4185
+ console.log(chalk12.gray(" /edit ") + "AI-powered surgical file editing");
3825
4186
  console.log("");
3826
- console.log(chalk11.cyan(" Conversation:"));
3827
- console.log(chalk11.gray(" /clear ") + "Clear conversation history");
3828
- console.log(chalk11.gray(" /history ") + "Show recent conversation");
4187
+ console.log(chalk12.cyan(" Conversation:"));
4188
+ console.log(chalk12.gray(" /clear ") + "Clear conversation history");
4189
+ console.log(chalk12.gray(" /history ") + "Show recent conversation");
3829
4190
  console.log("");
3830
- console.log(chalk11.cyan(" General:"));
3831
- console.log(chalk11.gray(" /model ") + "Change AI model");
3832
- console.log(chalk11.gray(" /chat ") + "Switch to chat mode");
3833
- console.log(chalk11.gray(" /help ") + "Show this help");
3834
- console.log(chalk11.gray(" /exit ") + "Exit Agdi");
3835
- console.log(chalk11.gray("\n Or just type your coding question!\n"));
3836
- console.log(chalk11.gray('Tip: "Create a todo app" will generate & write files.\n'));
4191
+ console.log(chalk12.cyan(" General:"));
4192
+ console.log(chalk12.gray(" /model ") + "Change AI model");
4193
+ console.log(chalk12.gray(" /chat ") + "Switch to chat mode");
4194
+ console.log(chalk12.gray(" /help ") + "Show this help");
4195
+ console.log(chalk12.gray(" /exit ") + "Exit Agdi");
4196
+ console.log(chalk12.gray("\n Or just type your coding question!\n"));
4197
+ console.log(chalk12.gray('Tip: "Create a todo app" will generate & write files.\n'));
3837
4198
  }
3838
4199
  function handleError(error) {
3839
4200
  const msg = error instanceof Error ? error.message : String(error);
3840
4201
  if (msg.includes("429") || msg.includes("quota")) {
3841
- console.log(chalk11.yellow("\n\u26A0\uFE0F Quota exceeded. Run /model to switch.\n"));
4202
+ console.log(chalk12.yellow("\n\u26A0\uFE0F Quota exceeded. Run /model to switch.\n"));
3842
4203
  } else if (msg.includes("401") || msg.includes("403")) {
3843
- console.log(chalk11.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
4204
+ console.log(chalk12.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
3844
4205
  } else {
3845
- console.log(chalk11.red("\n" + msg + "\n"));
4206
+ console.log(chalk12.red("\n" + msg + "\n"));
3846
4207
  }
3847
4208
  }
3848
4209
 
3849
4210
  // src/index.ts
3850
4211
  var BANNER = `
3851
- ${chalk12.cyan(` ___ __ _ `)}
3852
- ${chalk12.cyan(` / | ____ _____/ /(_) `)}
3853
- ${chalk12.cyan(` / /| | / __ \`/ __ // / `)}
3854
- ${chalk12.cyan(` / ___ |/ /_/ / /_/ // / `)}
3855
- ${chalk12.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
3856
- ${chalk12.cyan(` /____/ `)}
4212
+ ${chalk13.cyan(` ___ __ _ `)}
4213
+ ${chalk13.cyan(` / | ____ _____/ /(_) `)}
4214
+ ${chalk13.cyan(` / /| | / __ \`/ __ // / `)}
4215
+ ${chalk13.cyan(` / ___ |/ /_/ / /_/ // / `)}
4216
+ ${chalk13.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
4217
+ ${chalk13.cyan(` /____/ `)}
3857
4218
  `;
3858
4219
  var program = new Command();
3859
- program.name("agdi").description(chalk12.cyan("\u{1F680} AI-powered coding assistant")).version("2.2.2").configureHelp({
4220
+ program.name("agdi").description(chalk13.cyan("\u{1F680} AI-powered coding assistant")).version("2.2.2").configureHelp({
3860
4221
  // Show banner only when help is requested
3861
4222
  formatHelp: (cmd, helper) => {
3862
- return BANNER + "\n" + chalk12.gray(" The Open Source AI Architect") + "\n" + chalk12.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") + "\n" + helper.formatHelp(cmd, helper);
4223
+ return BANNER + "\n" + chalk13.gray(" The Open Source AI Architect") + "\n" + chalk13.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") + "\n" + helper.formatHelp(cmd, helper);
3863
4224
  }
3864
4225
  });
3865
4226
  program.action(async () => {
@@ -3870,7 +4231,7 @@ program.action(async () => {
3870
4231
  await startCodingMode();
3871
4232
  } catch (error) {
3872
4233
  if (error.name === "ExitPromptError") {
3873
- console.log(chalk12.gray("\n\n\u{1F44B} Goodbye!\n"));
4234
+ console.log(chalk13.gray("\n\n\u{1F44B} Goodbye!\n"));
3874
4235
  process.exit(0);
3875
4236
  }
3876
4237
  throw error;
@@ -3885,7 +4246,7 @@ program.command("auth").description("Configure API keys").option("--status", "Sh
3885
4246
  }
3886
4247
  } catch (error) {
3887
4248
  if (error.name === "ExitPromptError") {
3888
- console.log(chalk12.gray("\n\n\u{1F44B} Cancelled.\n"));
4249
+ console.log(chalk13.gray("\n\n\u{1F44B} Cancelled.\n"));
3889
4250
  process.exit(0);
3890
4251
  }
3891
4252
  throw error;
@@ -3896,7 +4257,7 @@ program.command("model").alias("models").description("Change AI model").action(a
3896
4257
  await selectModel();
3897
4258
  } catch (error) {
3898
4259
  if (error.name === "ExitPromptError") {
3899
- console.log(chalk12.gray("\n\n\u{1F44B} Cancelled.\n"));
4260
+ console.log(chalk13.gray("\n\n\u{1F44B} Cancelled.\n"));
3900
4261
  process.exit(0);
3901
4262
  }
3902
4263
  throw error;
@@ -3910,7 +4271,7 @@ program.command("chat").description("Start a chat session").action(async () => {
3910
4271
  await startChat();
3911
4272
  } catch (error) {
3912
4273
  if (error.name === "ExitPromptError") {
3913
- console.log(chalk12.gray("\n\n\u{1F44B} Goodbye!\n"));
4274
+ console.log(chalk13.gray("\n\n\u{1F44B} Goodbye!\n"));
3914
4275
  process.exit(0);
3915
4276
  }
3916
4277
  throw error;
@@ -3921,7 +4282,7 @@ program.command("run [directory]").description("Run a generated project").action
3921
4282
  await runProject(directory);
3922
4283
  } catch (error) {
3923
4284
  if (error.name === "ExitPromptError") {
3924
- console.log(chalk12.gray("\n\n\u{1F44B} Cancelled.\n"));
4285
+ console.log(chalk13.gray("\n\n\u{1F44B} Cancelled.\n"));
3925
4286
  process.exit(0);
3926
4287
  }
3927
4288
  throw error;
@@ -3934,10 +4295,10 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
3934
4295
  }
3935
4296
  const activeConfig = getActiveProvider();
3936
4297
  if (!activeConfig) {
3937
- console.log(chalk12.red("\u274C No API key configured. Run: agdi auth"));
4298
+ console.log(chalk13.red("\u274C No API key configured. Run: agdi auth"));
3938
4299
  return;
3939
4300
  }
3940
- const spinner = ora4("Generating application...").start();
4301
+ const spinner = ora5("Generating application...").start();
3941
4302
  try {
3942
4303
  const llm = createLLMProvider(activeConfig.provider, {
3943
4304
  apiKey: activeConfig.apiKey,
@@ -3946,30 +4307,30 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
3946
4307
  const pm = new ProjectManager();
3947
4308
  pm.create(options.output.replace("./", ""), prompt);
3948
4309
  const { plan, files } = await generateApp(prompt, llm, (step, file) => {
3949
- spinner.text = file ? `${step} ${chalk12.gray(file)}` : step;
4310
+ spinner.text = file ? `${step} ${chalk13.gray(file)}` : step;
3950
4311
  });
3951
4312
  pm.updateFiles(files);
3952
4313
  pm.updateDependencies(plan.dependencies);
3953
4314
  await writeProject(pm.get(), options.output);
3954
- spinner.succeed(chalk12.green("App generated!"));
3955
- console.log(chalk12.gray(`
3956
- \u{1F4C1} Created ${files.length} files in ${chalk12.cyan(options.output)}`));
3957
- console.log(chalk12.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
4315
+ spinner.succeed(chalk13.green("App generated!"));
4316
+ console.log(chalk13.gray(`
4317
+ \u{1F4C1} Created ${files.length} files in ${chalk13.cyan(options.output)}`));
4318
+ console.log(chalk13.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
3958
4319
  } catch (error) {
3959
4320
  spinner.fail("Generation failed");
3960
4321
  const msg = error instanceof Error ? error.message : String(error);
3961
4322
  if (msg.includes("429") || msg.includes("quota")) {
3962
- console.log(chalk12.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
4323
+ console.log(chalk13.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
3963
4324
  } else if (msg.includes("401") || msg.includes("403")) {
3964
- console.log(chalk12.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
4325
+ console.log(chalk13.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
3965
4326
  } else {
3966
- console.error(chalk12.red("\n" + msg + "\n"));
4327
+ console.error(chalk13.red("\n" + msg + "\n"));
3967
4328
  }
3968
4329
  process.exit(1);
3969
4330
  }
3970
4331
  } catch (error) {
3971
4332
  if (error.name === "ExitPromptError") {
3972
- console.log(chalk12.gray("\n\n\u{1F44B} Cancelled.\n"));
4333
+ console.log(chalk13.gray("\n\n\u{1F44B} Cancelled.\n"));
3973
4334
  process.exit(0);
3974
4335
  }
3975
4336
  throw error;
@@ -3978,11 +4339,11 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
3978
4339
  program.command("config").description("Show configuration").action(async () => {
3979
4340
  const config = loadConfig();
3980
4341
  const active = getActiveProvider();
3981
- console.log(chalk12.cyan.bold("\n\u2699\uFE0F Configuration\n"));
3982
- console.log(chalk12.gray(" Provider: ") + chalk12.cyan(config.defaultProvider || "not set"));
3983
- console.log(chalk12.gray(" Model: ") + chalk12.cyan(config.defaultModel || "not set"));
3984
- console.log(chalk12.gray(" Config: ") + chalk12.gray("~/.agdi/config.json"));
3985
- console.log(chalk12.cyan.bold("\n\u{1F510} API Keys\n"));
4342
+ console.log(chalk13.cyan.bold("\n\u2699\uFE0F Configuration\n"));
4343
+ console.log(chalk13.gray(" Provider: ") + chalk13.cyan(config.defaultProvider || "not set"));
4344
+ console.log(chalk13.gray(" Model: ") + chalk13.cyan(config.defaultModel || "not set"));
4345
+ console.log(chalk13.gray(" Config: ") + chalk13.gray("~/.agdi/config.json"));
4346
+ console.log(chalk13.cyan.bold("\n\u{1F510} API Keys\n"));
3986
4347
  const keys = [
3987
4348
  ["Gemini", config.geminiApiKey],
3988
4349
  ["OpenRouter", config.openrouterApiKey],
@@ -3991,7 +4352,7 @@ program.command("config").description("Show configuration").action(async () => {
3991
4352
  ["DeepSeek", config.deepseekApiKey]
3992
4353
  ];
3993
4354
  for (const [name, key] of keys) {
3994
- const status = key ? chalk12.green("\u2713") : chalk12.gray("\u2717");
4355
+ const status = key ? chalk13.green("\u2713") : chalk13.gray("\u2717");
3995
4356
  console.log(` ${status} ${name}`);
3996
4357
  }
3997
4358
  console.log("");