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.
- package/README.md +321 -93
- package/dist/index.js +466 -105
- 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
|
|
12
|
-
import
|
|
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
|
|
1374
|
-
import
|
|
1375
|
-
import
|
|
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
|
|
1445
|
-
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(
|
|
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(
|
|
3540
|
-
console.log(
|
|
3541
|
-
console.log(
|
|
3542
|
-
console.log(
|
|
3543
|
-
console.log(
|
|
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
|
|
3551
|
-
message:
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
3941
|
+
console.log(chalk12.gray("\n(no conversation history)\n"));
|
|
3591
3942
|
} else {
|
|
3592
|
-
console.log(
|
|
3593
|
-
console.log(
|
|
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" ?
|
|
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}: ${
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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
|
|
3679
|
-
message:
|
|
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(
|
|
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 =
|
|
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(
|
|
4060
|
+
console.log(chalk12.yellow("\n\u26A0\uFE0F Not a git repository\n"));
|
|
3701
4061
|
return;
|
|
3702
4062
|
}
|
|
3703
|
-
const spinner =
|
|
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(
|
|
3713
|
-
console.log(
|
|
3714
|
-
console.log(
|
|
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(
|
|
4083
|
+
console.log(chalk12.yellow("\n\u26A0\uFE0F Not a git repository\n"));
|
|
3724
4084
|
return;
|
|
3725
4085
|
}
|
|
3726
|
-
const spinner =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
3780
|
-
console.log(
|
|
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
|
|
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(
|
|
4154
|
+
console.log(chalk12.green("\n\u2713 Committed successfully!\n"));
|
|
3795
4155
|
} catch (gitError) {
|
|
3796
|
-
console.log(
|
|
4156
|
+
console.log(chalk12.red("\n\u2717 Commit failed. Check git output above.\n"));
|
|
3797
4157
|
}
|
|
3798
4158
|
} else {
|
|
3799
|
-
console.log(
|
|
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 ?
|
|
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
|
-
${
|
|
3812
|
-
${
|
|
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(
|
|
3818
|
-
console.log(
|
|
3819
|
-
console.log(
|
|
3820
|
-
console.log(
|
|
3821
|
-
console.log(
|
|
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(
|
|
3824
|
-
console.log(
|
|
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(
|
|
3827
|
-
console.log(
|
|
3828
|
-
console.log(
|
|
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(
|
|
3831
|
-
console.log(
|
|
3832
|
-
console.log(
|
|
3833
|
-
console.log(
|
|
3834
|
-
console.log(
|
|
3835
|
-
console.log(
|
|
3836
|
-
console.log(
|
|
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(
|
|
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(
|
|
4204
|
+
console.log(chalk12.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
|
|
3844
4205
|
} else {
|
|
3845
|
-
console.log(
|
|
4206
|
+
console.log(chalk12.red("\n" + msg + "\n"));
|
|
3846
4207
|
}
|
|
3847
4208
|
}
|
|
3848
4209
|
|
|
3849
4210
|
// src/index.ts
|
|
3850
4211
|
var BANNER = `
|
|
3851
|
-
${
|
|
3852
|
-
${
|
|
3853
|
-
${
|
|
3854
|
-
${
|
|
3855
|
-
${
|
|
3856
|
-
${
|
|
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(
|
|
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" +
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
4298
|
+
console.log(chalk13.red("\u274C No API key configured. Run: agdi auth"));
|
|
3938
4299
|
return;
|
|
3939
4300
|
}
|
|
3940
|
-
const spinner =
|
|
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} ${
|
|
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(
|
|
3955
|
-
console.log(
|
|
3956
|
-
\u{1F4C1} Created ${files.length} files in ${
|
|
3957
|
-
console.log(
|
|
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(
|
|
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(
|
|
4325
|
+
console.log(chalk13.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
|
|
3965
4326
|
} else {
|
|
3966
|
-
console.error(
|
|
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(
|
|
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(
|
|
3982
|
-
console.log(
|
|
3983
|
-
console.log(
|
|
3984
|
-
console.log(
|
|
3985
|
-
console.log(
|
|
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 ?
|
|
4355
|
+
const status = key ? chalk13.green("\u2713") : chalk13.gray("\u2717");
|
|
3995
4356
|
console.log(` ${status} ${name}`);
|
|
3996
4357
|
}
|
|
3997
4358
|
console.log("");
|