agdi 2.7.1 → 2.8.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/dist/index.js +624 -342
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import { Command } from "commander";
|
|
10
|
-
import
|
|
10
|
+
import chalk15 from "chalk";
|
|
11
11
|
import ora5 from "ora";
|
|
12
12
|
|
|
13
13
|
// src/core/llm/index.ts
|
|
@@ -287,6 +287,173 @@ var ProjectManager = class {
|
|
|
287
287
|
// src/core/io/index.ts
|
|
288
288
|
import JSZip from "jszip";
|
|
289
289
|
|
|
290
|
+
// src/utils/ui.ts
|
|
291
|
+
import chalk from "chalk";
|
|
292
|
+
import gradient from "gradient-string";
|
|
293
|
+
import boxen from "boxen";
|
|
294
|
+
import figlet from "figlet";
|
|
295
|
+
import ora from "ora";
|
|
296
|
+
var THEME = {
|
|
297
|
+
cyan: "#06b6d4",
|
|
298
|
+
// Cyan-500
|
|
299
|
+
purple: "#8b5cf6",
|
|
300
|
+
// Violet-500
|
|
301
|
+
red: "#ef4444",
|
|
302
|
+
// Red-500
|
|
303
|
+
yellow: "#eab308",
|
|
304
|
+
// Yellow-500
|
|
305
|
+
gray: "#71717a",
|
|
306
|
+
// Zinc-500
|
|
307
|
+
dim: "#52525b"
|
|
308
|
+
// Zinc-600
|
|
309
|
+
};
|
|
310
|
+
var brandGradient = gradient([THEME.cyan, THEME.purple]);
|
|
311
|
+
var errorGradient = gradient([THEME.red, "#b91c1c"]);
|
|
312
|
+
var goldGradient = gradient([THEME.yellow, "#fbbf24"]);
|
|
313
|
+
async function renderBanner(version = "v2.6.0") {
|
|
314
|
+
console.clear();
|
|
315
|
+
const text = await new Promise((resolve5) => {
|
|
316
|
+
figlet("AGDI", { font: "Slant" }, (err, data) => {
|
|
317
|
+
resolve5(data || "AGDI");
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
console.log(brandGradient.multiline(text));
|
|
321
|
+
console.log(chalk.hex(THEME.dim)(` ${version} [ARCHITECT ONLINE]
|
|
322
|
+
`));
|
|
323
|
+
}
|
|
324
|
+
function renderBox(title, content, style = "info") {
|
|
325
|
+
let borderColor = THEME.cyan;
|
|
326
|
+
let titleColor = chalk.cyan;
|
|
327
|
+
if (style === "success") {
|
|
328
|
+
borderColor = THEME.cyan;
|
|
329
|
+
} else if (style === "warning") {
|
|
330
|
+
borderColor = THEME.yellow;
|
|
331
|
+
titleColor = chalk.yellow;
|
|
332
|
+
} else if (style === "error") {
|
|
333
|
+
borderColor = THEME.red;
|
|
334
|
+
titleColor = chalk.red;
|
|
335
|
+
}
|
|
336
|
+
const box = boxen(content, {
|
|
337
|
+
title: titleColor.bold(title),
|
|
338
|
+
padding: 1,
|
|
339
|
+
margin: 1,
|
|
340
|
+
borderStyle: "round",
|
|
341
|
+
borderColor,
|
|
342
|
+
dimBorder: false,
|
|
343
|
+
float: "left"
|
|
344
|
+
});
|
|
345
|
+
console.log(box);
|
|
346
|
+
}
|
|
347
|
+
function renderAlert(title, message) {
|
|
348
|
+
console.log("");
|
|
349
|
+
const box = boxen(chalk.white(message), {
|
|
350
|
+
title: chalk.red.bold(`\u{1F6E1}\uFE0F ${title.toUpperCase()} `),
|
|
351
|
+
padding: 1,
|
|
352
|
+
borderStyle: "double",
|
|
353
|
+
borderColor: "red",
|
|
354
|
+
textAlignment: "center"
|
|
355
|
+
});
|
|
356
|
+
console.log(box);
|
|
357
|
+
console.log("");
|
|
358
|
+
}
|
|
359
|
+
function printUserMessage(message) {
|
|
360
|
+
console.log("");
|
|
361
|
+
console.log(chalk.cyan.bold("\u{1F464} YOU \u203A ") + chalk.white(message));
|
|
362
|
+
console.log("");
|
|
363
|
+
}
|
|
364
|
+
function printAIMessage(message) {
|
|
365
|
+
console.log("");
|
|
366
|
+
console.log(brandGradient.multiline("\u26A1 AGDI \u203A "));
|
|
367
|
+
console.log(message.trim());
|
|
368
|
+
console.log("");
|
|
369
|
+
}
|
|
370
|
+
function createSpinner(text) {
|
|
371
|
+
return ora({
|
|
372
|
+
text: chalk.hex(THEME.gray)(text),
|
|
373
|
+
color: "cyan",
|
|
374
|
+
spinner: "dots",
|
|
375
|
+
discardStdin: false
|
|
376
|
+
// Important for allowing interruption if needed
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
function printIter() {
|
|
380
|
+
console.log(chalk.hex(THEME.dim)("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
381
|
+
}
|
|
382
|
+
var activeReadlineInterface = null;
|
|
383
|
+
function registerActivePrompt(rl) {
|
|
384
|
+
activeReadlineInterface = rl;
|
|
385
|
+
}
|
|
386
|
+
var flags = {
|
|
387
|
+
yes: false,
|
|
388
|
+
headless: false,
|
|
389
|
+
minimal: false,
|
|
390
|
+
dryRun: false
|
|
391
|
+
};
|
|
392
|
+
function setFlags(newFlags) {
|
|
393
|
+
Object.assign(flags, newFlags);
|
|
394
|
+
}
|
|
395
|
+
var GracefulExitError = class extends Error {
|
|
396
|
+
constructor() {
|
|
397
|
+
super("Process exiting");
|
|
398
|
+
this.name = "GracefulExitError";
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
function safeExit(code = 0) {
|
|
402
|
+
if (activeReadlineInterface) {
|
|
403
|
+
try {
|
|
404
|
+
activeReadlineInterface.close?.();
|
|
405
|
+
activeReadlineInterface.destroy?.();
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
activeReadlineInterface = null;
|
|
409
|
+
}
|
|
410
|
+
setImmediate(() => {
|
|
411
|
+
process.exit(code);
|
|
412
|
+
});
|
|
413
|
+
throw new GracefulExitError();
|
|
414
|
+
}
|
|
415
|
+
async function smartConfirm(message, defaultValue = false) {
|
|
416
|
+
if (flags.yes || flags.headless || process.env.CI === "true" || process.env.CI === "1") {
|
|
417
|
+
console.log(chalk.gray(` [Auto-approved: ${message}]`));
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
if (!process.stdout.isTTY) {
|
|
421
|
+
console.warn(chalk.yellow("\u26A0\uFE0F Non-interactive session detected. Use --yes to approve actions."));
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
425
|
+
return confirm4({ message, default: defaultValue });
|
|
426
|
+
}
|
|
427
|
+
async function smartSelect(message, choices, defaultValue) {
|
|
428
|
+
if (!process.stdout.isTTY || flags.headless) {
|
|
429
|
+
const result = defaultValue || choices[0]?.value;
|
|
430
|
+
if (result) {
|
|
431
|
+
console.log(chalk.gray(` [Auto-selected: ${result}]`));
|
|
432
|
+
}
|
|
433
|
+
return result || null;
|
|
434
|
+
}
|
|
435
|
+
const { select: select5 } = await import("@inquirer/prompts");
|
|
436
|
+
return select5({ message, choices });
|
|
437
|
+
}
|
|
438
|
+
var ui = {
|
|
439
|
+
renderBanner,
|
|
440
|
+
renderBox,
|
|
441
|
+
renderAlert,
|
|
442
|
+
printUserMessage,
|
|
443
|
+
printAIMessage,
|
|
444
|
+
createSpinner,
|
|
445
|
+
printIter,
|
|
446
|
+
brandGradient,
|
|
447
|
+
THEME,
|
|
448
|
+
// Safety & Automation
|
|
449
|
+
safeExit,
|
|
450
|
+
smartConfirm,
|
|
451
|
+
smartSelect,
|
|
452
|
+
setFlags,
|
|
453
|
+
flags,
|
|
454
|
+
registerActivePrompt
|
|
455
|
+
};
|
|
456
|
+
|
|
290
457
|
// src/core/architect/index.ts
|
|
291
458
|
var SYSTEM_PROMPT = `You are Agdi Architect, an expert software architect AI.
|
|
292
459
|
Your job is to generate complete, production-ready React applications.
|
|
@@ -294,7 +461,7 @@ Always use TypeScript, Tailwind CSS, and Vite.
|
|
|
294
461
|
Generate all necessary files including package.json, tsconfig.json, vite.config.ts.
|
|
295
462
|
Make the UI beautiful with modern design patterns.`;
|
|
296
463
|
async function generatePlan(prompt, llm) {
|
|
297
|
-
|
|
464
|
+
let planPrompt = `Create a detailed plan for: ${prompt}
|
|
298
465
|
|
|
299
466
|
Return a JSON object with:
|
|
300
467
|
{
|
|
@@ -306,6 +473,25 @@ Return a JSON object with:
|
|
|
306
473
|
}
|
|
307
474
|
|
|
308
475
|
Return ONLY valid JSON, no markdown.`;
|
|
476
|
+
if (ui.flags.minimal) {
|
|
477
|
+
planPrompt = `Create a minimal plan for: ${prompt}
|
|
478
|
+
|
|
479
|
+
CRITICAL: Minimal mode matches user request exactly.
|
|
480
|
+
- If user asks for "hello.ts", generate ONLY "hello.ts".
|
|
481
|
+
- Do NOT scaffold a full React app unless explicitly asked.
|
|
482
|
+
- Do NOT add boilerplate headers/footers.
|
|
483
|
+
|
|
484
|
+
Return a JSON object with:
|
|
485
|
+
{
|
|
486
|
+
"name": "minimal-project",
|
|
487
|
+
"description": "Minimal generation",
|
|
488
|
+
"files": [{"path": "requested-file.ts", "description": "Requested logic"}],
|
|
489
|
+
"dependencies": [],
|
|
490
|
+
"architecture": "Single file script"
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
Return ONLY valid JSON.`;
|
|
494
|
+
}
|
|
309
495
|
const response = await llm.generate(planPrompt, SYSTEM_PROMPT);
|
|
310
496
|
try {
|
|
311
497
|
const jsonMatch = response.text.match(/\{[\s\S]*\}/);
|
|
@@ -356,50 +542,52 @@ async function generateApp(prompt, llm, onProgress) {
|
|
|
356
542
|
const file = await generateFile(fileSpec.path, fileSpec.description, plan, llm);
|
|
357
543
|
files.push(file);
|
|
358
544
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
545
|
+
if (!ui.flags.minimal) {
|
|
546
|
+
onProgress?.("Creating package.json...", "package.json");
|
|
547
|
+
files.push({
|
|
548
|
+
path: "package.json",
|
|
549
|
+
content: JSON.stringify({
|
|
550
|
+
name: plan.name,
|
|
551
|
+
version: "0.1.0",
|
|
552
|
+
type: "module",
|
|
553
|
+
scripts: {
|
|
554
|
+
dev: "vite",
|
|
555
|
+
build: "tsc -b && vite build",
|
|
556
|
+
preview: "vite preview"
|
|
557
|
+
},
|
|
558
|
+
dependencies: {
|
|
559
|
+
"react": "^18.3.1",
|
|
560
|
+
"react-dom": "^18.3.1",
|
|
561
|
+
...plan.dependencies.reduce((acc, dep) => {
|
|
562
|
+
if (dep !== "react" && dep !== "react-dom") {
|
|
563
|
+
acc[dep] = "latest";
|
|
564
|
+
}
|
|
565
|
+
return acc;
|
|
566
|
+
}, {})
|
|
567
|
+
},
|
|
568
|
+
devDependencies: {
|
|
569
|
+
"@types/react": "^18.3.0",
|
|
570
|
+
"@types/react-dom": "^18.3.0",
|
|
571
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
572
|
+
"autoprefixer": "^10.4.20",
|
|
573
|
+
"postcss": "^8.4.45",
|
|
574
|
+
"tailwindcss": "^3.4.10",
|
|
575
|
+
"typescript": "~5.5.0",
|
|
576
|
+
"vite": "^5.4.0"
|
|
577
|
+
}
|
|
578
|
+
}, null, 2)
|
|
579
|
+
});
|
|
580
|
+
}
|
|
393
581
|
return { plan, files };
|
|
394
582
|
}
|
|
395
583
|
|
|
396
584
|
// src/utils/fs.ts
|
|
397
585
|
import fs from "fs-extra";
|
|
398
586
|
import path from "path";
|
|
399
|
-
import
|
|
587
|
+
import chalk3 from "chalk";
|
|
400
588
|
|
|
401
589
|
// src/security/code-firewall.ts
|
|
402
|
-
import
|
|
590
|
+
import chalk2 from "chalk";
|
|
403
591
|
var MALICIOUS_PATTERNS = [
|
|
404
592
|
// ==================== HARDCODED SECRETS ====================
|
|
405
593
|
{
|
|
@@ -648,35 +836,35 @@ function shouldBlockCode(result) {
|
|
|
648
836
|
}
|
|
649
837
|
function displayScanResults(result, filename) {
|
|
650
838
|
if (result.safe) {
|
|
651
|
-
console.log(
|
|
839
|
+
console.log(chalk2.green("\u2705 No malicious patterns detected"));
|
|
652
840
|
return;
|
|
653
841
|
}
|
|
654
|
-
console.log(
|
|
842
|
+
console.log(chalk2.red.bold("\n\u{1F6A8} SECURITY SCAN FAILED"));
|
|
655
843
|
if (filename) {
|
|
656
|
-
console.log(
|
|
844
|
+
console.log(chalk2.gray(`File: ${filename}`));
|
|
657
845
|
}
|
|
658
846
|
console.log("");
|
|
659
847
|
const criticals = result.matches.filter((m) => m.severity === "critical");
|
|
660
848
|
const highs = result.matches.filter((m) => m.severity === "high");
|
|
661
849
|
const others = result.matches.filter((m) => m.severity !== "critical" && m.severity !== "high");
|
|
662
850
|
if (criticals.length > 0) {
|
|
663
|
-
console.log(
|
|
851
|
+
console.log(chalk2.red("\u{1F534} CRITICAL:"));
|
|
664
852
|
for (const m of criticals) {
|
|
665
|
-
console.log(
|
|
666
|
-
console.log(
|
|
853
|
+
console.log(chalk2.red(` Line ${m.line}: ${m.description}`));
|
|
854
|
+
console.log(chalk2.gray(` Found: ${m.match}`));
|
|
667
855
|
}
|
|
668
856
|
}
|
|
669
857
|
if (highs.length > 0) {
|
|
670
|
-
console.log(
|
|
858
|
+
console.log(chalk2.yellow("\n\u{1F7E0} HIGH:"));
|
|
671
859
|
for (const m of highs) {
|
|
672
|
-
console.log(
|
|
673
|
-
console.log(
|
|
860
|
+
console.log(chalk2.yellow(` Line ${m.line}: ${m.description}`));
|
|
861
|
+
console.log(chalk2.gray(` Found: ${m.match}`));
|
|
674
862
|
}
|
|
675
863
|
}
|
|
676
864
|
if (others.length > 0) {
|
|
677
|
-
console.log(
|
|
865
|
+
console.log(chalk2.cyan("\n\u{1F7E1} WARNINGS:"));
|
|
678
866
|
for (const m of others) {
|
|
679
|
-
console.log(
|
|
867
|
+
console.log(chalk2.cyan(` Line ${m.line}: ${m.description}`));
|
|
680
868
|
}
|
|
681
869
|
}
|
|
682
870
|
console.log("");
|
|
@@ -686,11 +874,11 @@ function validateCodeBeforeWrite(code, filename) {
|
|
|
686
874
|
if (!result.safe) {
|
|
687
875
|
displayScanResults(result, filename);
|
|
688
876
|
if (shouldBlockCode(result)) {
|
|
689
|
-
console.log(
|
|
690
|
-
console.log(
|
|
877
|
+
console.log(chalk2.red.bold("\u{1F6A8} BLOCKED: Code contains critical security issues"));
|
|
878
|
+
console.log(chalk2.gray("The file will NOT be written to disk.\n"));
|
|
691
879
|
return false;
|
|
692
880
|
} else {
|
|
693
|
-
console.log(
|
|
881
|
+
console.log(chalk2.yellow("\u26A0\uFE0F Warning: Code contains potential issues but will be written.\n"));
|
|
694
882
|
}
|
|
695
883
|
}
|
|
696
884
|
return true;
|
|
@@ -706,7 +894,7 @@ async function writeProject(project, outputDir) {
|
|
|
706
894
|
const isSafe = validateCodeBeforeWrite(file.content, file.path);
|
|
707
895
|
if (!isSafe) {
|
|
708
896
|
blockedCount++;
|
|
709
|
-
console.log(
|
|
897
|
+
console.log(chalk3.red(`\u26D4 BLOCKED: ${file.path}`));
|
|
710
898
|
continue;
|
|
711
899
|
}
|
|
712
900
|
await fs.ensureDir(path.dirname(filePath));
|
|
@@ -715,16 +903,16 @@ async function writeProject(project, outputDir) {
|
|
|
715
903
|
}
|
|
716
904
|
console.log("");
|
|
717
905
|
if (blockedCount > 0) {
|
|
718
|
-
console.log(
|
|
906
|
+
console.log(chalk3.yellow(`\u26A0\uFE0F ${blockedCount} file(s) blocked by security scan`));
|
|
719
907
|
}
|
|
720
|
-
console.log(
|
|
908
|
+
console.log(chalk3.green(`\u2705 ${writtenCount} file(s) written successfully`));
|
|
721
909
|
}
|
|
722
910
|
|
|
723
911
|
// src/utils/config.ts
|
|
724
912
|
import fs2 from "fs-extra";
|
|
725
913
|
import path2 from "path";
|
|
726
914
|
import os from "os";
|
|
727
|
-
import
|
|
915
|
+
import chalk4 from "chalk";
|
|
728
916
|
var CONFIG_DIR = path2.join(os.homedir(), ".agdi");
|
|
729
917
|
var CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
|
|
730
918
|
var SECURE_FILE_MODE = 384;
|
|
@@ -741,11 +929,11 @@ function checkPermissions() {
|
|
|
741
929
|
}
|
|
742
930
|
const isWorldReadable = (mode & 36) !== 0;
|
|
743
931
|
if (isWorldReadable) {
|
|
744
|
-
console.log(
|
|
745
|
-
console.log(
|
|
746
|
-
console.log(
|
|
747
|
-
console.log(
|
|
748
|
-
console.log(
|
|
932
|
+
console.log(chalk4.yellow("\n\u26A0\uFE0F SECURITY WARNING"));
|
|
933
|
+
console.log(chalk4.gray("Your config file is readable by other users!"));
|
|
934
|
+
console.log(chalk4.gray(`File: ${CONFIG_FILE}`));
|
|
935
|
+
console.log(chalk4.gray("Run the following to fix:"));
|
|
936
|
+
console.log(chalk4.cyan(` chmod 600 "${CONFIG_FILE}"
|
|
749
937
|
`));
|
|
750
938
|
return false;
|
|
751
939
|
}
|
|
@@ -781,16 +969,16 @@ function saveConfig(config) {
|
|
|
781
969
|
fs2.writeJsonSync(CONFIG_FILE, config, { spaces: 2 });
|
|
782
970
|
setSecurePermissions();
|
|
783
971
|
} catch (error) {
|
|
784
|
-
console.error(
|
|
972
|
+
console.error(chalk4.red("Failed to save config:"), error);
|
|
785
973
|
}
|
|
786
974
|
}
|
|
787
975
|
|
|
788
976
|
// src/commands/auth.ts
|
|
789
977
|
import { input, select, password } from "@inquirer/prompts";
|
|
790
|
-
import
|
|
978
|
+
import chalk5 from "chalk";
|
|
791
979
|
async function login() {
|
|
792
|
-
console.log(
|
|
793
|
-
console.log(
|
|
980
|
+
console.log(chalk5.cyan.bold("\n\u{1F510} Agdi Authentication\n"));
|
|
981
|
+
console.log(chalk5.gray("Configure your API key to use Agdi CLI.\n"));
|
|
794
982
|
try {
|
|
795
983
|
const config = loadConfig();
|
|
796
984
|
const provider = await select({
|
|
@@ -812,8 +1000,8 @@ async function login() {
|
|
|
812
1000
|
config.ollamaUrl = ollamaUrl;
|
|
813
1001
|
config.defaultProvider = "ollama";
|
|
814
1002
|
saveConfig(config);
|
|
815
|
-
console.log(
|
|
816
|
-
console.log(
|
|
1003
|
+
console.log(chalk5.green("\n\u2705 Ollama configured"));
|
|
1004
|
+
console.log(chalk5.gray(`Server: ${ollamaUrl}
|
|
817
1005
|
`));
|
|
818
1006
|
return;
|
|
819
1007
|
}
|
|
@@ -840,12 +1028,12 @@ async function login() {
|
|
|
840
1028
|
}
|
|
841
1029
|
config.defaultProvider = provider;
|
|
842
1030
|
saveConfig(config);
|
|
843
|
-
console.log(
|
|
1031
|
+
console.log(chalk5.green(`
|
|
844
1032
|
\u2705 ${provider} API key saved securely`));
|
|
845
|
-
console.log(
|
|
1033
|
+
console.log(chalk5.gray("Keys stored in ~/.agdi/config.json\n"));
|
|
846
1034
|
} catch (error) {
|
|
847
1035
|
if (error.name === "ExitPromptError") {
|
|
848
|
-
console.log(
|
|
1036
|
+
console.log(chalk5.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
849
1037
|
process.exit(0);
|
|
850
1038
|
}
|
|
851
1039
|
throw error;
|
|
@@ -853,7 +1041,7 @@ async function login() {
|
|
|
853
1041
|
}
|
|
854
1042
|
async function showStatus() {
|
|
855
1043
|
const config = loadConfig();
|
|
856
|
-
console.log(
|
|
1044
|
+
console.log(chalk5.cyan.bold("\n\u{1F4CA} Authentication Status\n"));
|
|
857
1045
|
const providers = [
|
|
858
1046
|
{ name: "Gemini", key: config.geminiApiKey },
|
|
859
1047
|
{ name: "OpenRouter", key: config.openrouterApiKey },
|
|
@@ -862,19 +1050,19 @@ async function showStatus() {
|
|
|
862
1050
|
{ name: "DeepSeek", key: config.deepseekApiKey }
|
|
863
1051
|
];
|
|
864
1052
|
for (const p of providers) {
|
|
865
|
-
const status = p.key ?
|
|
1053
|
+
const status = p.key ? chalk5.green("\u2713 Configured") : chalk5.gray("\u2717 Not set");
|
|
866
1054
|
console.log(` ${p.name.padEnd(12)} ${status}`);
|
|
867
1055
|
}
|
|
868
|
-
console.log(
|
|
1056
|
+
console.log(chalk5.cyan(`
|
|
869
1057
|
Default: ${config.defaultProvider || "gemini"}
|
|
870
1058
|
`));
|
|
871
|
-
console.log(
|
|
1059
|
+
console.log(chalk5.gray('\u{1F4A1} Tip: Use "agdi auth" to reconfigure\n'));
|
|
872
1060
|
}
|
|
873
1061
|
|
|
874
1062
|
// src/commands/chat.ts
|
|
875
1063
|
import { input as input2 } from "@inquirer/prompts";
|
|
876
|
-
import
|
|
877
|
-
import
|
|
1064
|
+
import chalk6 from "chalk";
|
|
1065
|
+
import ora2 from "ora";
|
|
878
1066
|
var SYSTEM_PROMPT2 = `You are Agdi, an elite full-stack software architect and senior engineer with deep expertise across the entire web development stack.
|
|
879
1067
|
|
|
880
1068
|
# Core Expertise
|
|
@@ -1028,8 +1216,8 @@ When asked to build something:
|
|
|
1028
1216
|
|
|
1029
1217
|
You build software that works, scales, and follows industry best practices. Every solution is complete, tested, and ready for production deployment.`;
|
|
1030
1218
|
async function startChat() {
|
|
1031
|
-
console.log(
|
|
1032
|
-
console.log(
|
|
1219
|
+
console.log(chalk6.cyan.bold("\n\u{1F4AC} Agdi Interactive Mode\n"));
|
|
1220
|
+
console.log(chalk6.gray('Type your coding requests. Type "exit" to quit.\n'));
|
|
1033
1221
|
const config = loadConfig();
|
|
1034
1222
|
let provider;
|
|
1035
1223
|
let apiKey = "";
|
|
@@ -1039,33 +1227,33 @@ async function startChat() {
|
|
|
1039
1227
|
} else if (config.openrouterApiKey) {
|
|
1040
1228
|
provider = "openrouter";
|
|
1041
1229
|
apiKey = config.openrouterApiKey;
|
|
1042
|
-
console.log(
|
|
1230
|
+
console.log(chalk6.gray("Using OpenRouter (100+ models available)\n"));
|
|
1043
1231
|
} else if (config.defaultProvider === "puter") {
|
|
1044
|
-
console.log(
|
|
1045
|
-
console.log(
|
|
1046
|
-
console.log(
|
|
1047
|
-
console.log(
|
|
1232
|
+
console.log(chalk6.yellow("\u26A0\uFE0F Puter.com FREE mode requires browser authentication."));
|
|
1233
|
+
console.log(chalk6.gray("For CLI usage, please configure an API key:\n"));
|
|
1234
|
+
console.log(chalk6.cyan(" agdi auth"));
|
|
1235
|
+
console.log(chalk6.gray("\nSupported providers: Gemini, OpenRouter, OpenAI, Anthropic, DeepSeek\n"));
|
|
1048
1236
|
return;
|
|
1049
1237
|
} else {
|
|
1050
|
-
console.log(
|
|
1051
|
-
console.log(
|
|
1238
|
+
console.log(chalk6.yellow("\u26A0\uFE0F No API key configured."));
|
|
1239
|
+
console.log(chalk6.gray('Run "agdi auth" to configure your API key.\n'));
|
|
1052
1240
|
return;
|
|
1053
1241
|
}
|
|
1054
|
-
console.log(
|
|
1055
|
-
console.log(
|
|
1242
|
+
console.log(chalk6.gray(`Using provider: ${chalk6.cyan(provider)}`));
|
|
1243
|
+
console.log(chalk6.gray("\u2500".repeat(50) + "\n"));
|
|
1056
1244
|
const pm = new ProjectManager();
|
|
1057
1245
|
while (true) {
|
|
1058
1246
|
const userInput = await input2({
|
|
1059
|
-
message:
|
|
1247
|
+
message: chalk6.cyan("You:")
|
|
1060
1248
|
});
|
|
1061
1249
|
if (userInput.toLowerCase() === "exit" || userInput.toLowerCase() === "quit") {
|
|
1062
|
-
console.log(
|
|
1250
|
+
console.log(chalk6.gray("\n\u{1F44B} Goodbye!\n"));
|
|
1063
1251
|
break;
|
|
1064
1252
|
}
|
|
1065
1253
|
if (!userInput.trim()) {
|
|
1066
1254
|
continue;
|
|
1067
1255
|
}
|
|
1068
|
-
const spinner =
|
|
1256
|
+
const spinner = ora2("Thinking...").start();
|
|
1069
1257
|
try {
|
|
1070
1258
|
const llm = createLLMProvider(provider, { apiKey, model: config.defaultModel });
|
|
1071
1259
|
if (userInput.toLowerCase().includes("create") || userInput.toLowerCase().includes("build") || userInput.toLowerCase().includes("make")) {
|
|
@@ -1076,9 +1264,9 @@ async function startChat() {
|
|
|
1076
1264
|
});
|
|
1077
1265
|
pm.updateFiles(files);
|
|
1078
1266
|
spinner.succeed("Application generated!");
|
|
1079
|
-
console.log(
|
|
1267
|
+
console.log(chalk6.green("\n\u{1F4C1} Files created:"));
|
|
1080
1268
|
for (const file of files) {
|
|
1081
|
-
console.log(
|
|
1269
|
+
console.log(chalk6.gray(` - ${file.path}`));
|
|
1082
1270
|
}
|
|
1083
1271
|
const shouldWrite = await input2({
|
|
1084
1272
|
message: "Write files to disk? (y/n):",
|
|
@@ -1090,38 +1278,38 @@ async function startChat() {
|
|
|
1090
1278
|
default: "./generated-app"
|
|
1091
1279
|
});
|
|
1092
1280
|
await writeProject(pm.get(), dir);
|
|
1093
|
-
console.log(
|
|
1281
|
+
console.log(chalk6.green(`
|
|
1094
1282
|
\u2705 Files written to ${dir}
|
|
1095
1283
|
`));
|
|
1096
1284
|
}
|
|
1097
1285
|
} else {
|
|
1098
1286
|
const response = await llm.generate(userInput, SYSTEM_PROMPT2);
|
|
1099
1287
|
spinner.stop();
|
|
1100
|
-
console.log(
|
|
1288
|
+
console.log(chalk6.cyan("\nAgdi: ") + response.text + "\n");
|
|
1101
1289
|
}
|
|
1102
1290
|
} catch (error) {
|
|
1103
1291
|
if (error.name === "ExitPromptError") {
|
|
1104
|
-
console.log(
|
|
1292
|
+
console.log(chalk6.gray("\n\n\u{1F44B} Goodbye!\n"));
|
|
1105
1293
|
process.exit(0);
|
|
1106
1294
|
}
|
|
1107
1295
|
spinner.fail("Error");
|
|
1108
1296
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1109
1297
|
if (errorMessage.includes("429") || errorMessage.includes("quota") || errorMessage.includes("Resource exhausted") || errorMessage.includes("ResourceExhausted")) {
|
|
1110
|
-
console.log(
|
|
1111
|
-
console.log(
|
|
1112
|
-
console.log(
|
|
1298
|
+
console.log(chalk6.yellow("\n\u26A0\uFE0F API quota exceeded!"));
|
|
1299
|
+
console.log(chalk6.gray("Your API key has run out of credits."));
|
|
1300
|
+
console.log(chalk6.gray("Try: Use a different API key or wait for quota reset.\n"));
|
|
1113
1301
|
} else if (errorMessage.includes("401") || errorMessage.includes("Unauthorized") || errorMessage.includes("Invalid API key")) {
|
|
1114
|
-
console.log(
|
|
1115
|
-
console.log(
|
|
1116
|
-
console.log(
|
|
1302
|
+
console.log(chalk6.red("\n\u{1F511} Invalid API key"));
|
|
1303
|
+
console.log(chalk6.gray("Please reconfigure your API key:"));
|
|
1304
|
+
console.log(chalk6.cyan(" agdi auth\n"));
|
|
1117
1305
|
} else if (errorMessage.includes("403") || errorMessage.includes("Forbidden")) {
|
|
1118
|
-
console.log(
|
|
1119
|
-
console.log(
|
|
1306
|
+
console.log(chalk6.red("\n\u{1F6AB} Access denied"));
|
|
1307
|
+
console.log(chalk6.gray("Your API key doesn't have permission for this operation.\n"));
|
|
1120
1308
|
} else if (errorMessage.includes("network") || errorMessage.includes("fetch") || errorMessage.includes("ENOTFOUND")) {
|
|
1121
|
-
console.log(
|
|
1122
|
-
console.log(
|
|
1309
|
+
console.log(chalk6.red("\n\u{1F310} Network error"));
|
|
1310
|
+
console.log(chalk6.gray("Please check your internet connection.\n"));
|
|
1123
1311
|
} else {
|
|
1124
|
-
console.log(
|
|
1312
|
+
console.log(chalk6.red("\n" + errorMessage + "\n"));
|
|
1125
1313
|
}
|
|
1126
1314
|
}
|
|
1127
1315
|
}
|
|
@@ -1129,23 +1317,23 @@ async function startChat() {
|
|
|
1129
1317
|
|
|
1130
1318
|
// src/commands/run.ts
|
|
1131
1319
|
import { spawn } from "child_process";
|
|
1132
|
-
import
|
|
1320
|
+
import chalk7 from "chalk";
|
|
1133
1321
|
import fs3 from "fs-extra";
|
|
1134
1322
|
import path3 from "path";
|
|
1135
|
-
import
|
|
1323
|
+
import ora3 from "ora";
|
|
1136
1324
|
async function runProject(targetDir) {
|
|
1137
1325
|
const dir = targetDir || process.cwd();
|
|
1138
1326
|
const absoluteDir = path3.resolve(dir);
|
|
1139
|
-
console.log(
|
|
1327
|
+
console.log(chalk7.cyan.bold("\n\u{1F680} Agdi Run\n"));
|
|
1140
1328
|
if (!fs3.existsSync(absoluteDir)) {
|
|
1141
|
-
console.log(
|
|
1142
|
-
console.log(
|
|
1329
|
+
console.log(chalk7.red(`\u274C Directory not found: ${absoluteDir}`));
|
|
1330
|
+
console.log(chalk7.gray("Create a project first with: agdi init"));
|
|
1143
1331
|
return;
|
|
1144
1332
|
}
|
|
1145
1333
|
const packageJsonPath = path3.join(absoluteDir, "package.json");
|
|
1146
1334
|
if (!fs3.existsSync(packageJsonPath)) {
|
|
1147
|
-
console.log(
|
|
1148
|
-
console.log(
|
|
1335
|
+
console.log(chalk7.red(`\u274C No package.json found in: ${absoluteDir}`));
|
|
1336
|
+
console.log(chalk7.gray("This doesn't appear to be a Node.js project."));
|
|
1149
1337
|
return;
|
|
1150
1338
|
}
|
|
1151
1339
|
const packageJson = fs3.readJsonSync(packageJsonPath);
|
|
@@ -1154,14 +1342,14 @@ async function runProject(targetDir) {
|
|
|
1154
1342
|
if (!scripts.dev && scripts.start) {
|
|
1155
1343
|
runScript = "start";
|
|
1156
1344
|
} else if (!scripts.dev && !scripts.start) {
|
|
1157
|
-
console.log(
|
|
1158
|
-
console.log(
|
|
1345
|
+
console.log(chalk7.red('\u274C No "dev" or "start" script found in package.json'));
|
|
1346
|
+
console.log(chalk7.gray('Add a script like: "dev": "vite" or "start": "node index.js"'));
|
|
1159
1347
|
return;
|
|
1160
1348
|
}
|
|
1161
1349
|
const nodeModulesPath = path3.join(absoluteDir, "node_modules");
|
|
1162
1350
|
if (!fs3.existsSync(nodeModulesPath)) {
|
|
1163
|
-
console.log(
|
|
1164
|
-
const installSpinner =
|
|
1351
|
+
console.log(chalk7.yellow("\u{1F4E6} Installing dependencies...\n"));
|
|
1352
|
+
const installSpinner = ora3("Running npm install...").start();
|
|
1165
1353
|
await new Promise((resolve5, reject) => {
|
|
1166
1354
|
const install = spawn("npm", ["install"], {
|
|
1167
1355
|
cwd: absoluteDir,
|
|
@@ -1181,11 +1369,11 @@ async function runProject(targetDir) {
|
|
|
1181
1369
|
});
|
|
1182
1370
|
console.log("");
|
|
1183
1371
|
}
|
|
1184
|
-
console.log(
|
|
1185
|
-
console.log(
|
|
1372
|
+
console.log(chalk7.green(`\u25B6 Running: npm run ${runScript}`));
|
|
1373
|
+
console.log(chalk7.gray(` Directory: ${absoluteDir}
|
|
1186
1374
|
`));
|
|
1187
|
-
console.log(
|
|
1188
|
-
console.log(
|
|
1375
|
+
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
1376
|
+
console.log(chalk7.gray("Press Ctrl+C to stop\n"));
|
|
1189
1377
|
const child = spawn("npm", ["run", runScript], {
|
|
1190
1378
|
cwd: absoluteDir,
|
|
1191
1379
|
stdio: "inherit",
|
|
@@ -1193,12 +1381,12 @@ async function runProject(targetDir) {
|
|
|
1193
1381
|
});
|
|
1194
1382
|
process.on("SIGINT", () => {
|
|
1195
1383
|
child.kill("SIGINT");
|
|
1196
|
-
console.log(
|
|
1384
|
+
console.log(chalk7.gray("\n\n\u{1F44B} Server stopped."));
|
|
1197
1385
|
process.exit(0);
|
|
1198
1386
|
});
|
|
1199
1387
|
child.on("close", (code) => {
|
|
1200
1388
|
if (code !== 0) {
|
|
1201
|
-
console.log(
|
|
1389
|
+
console.log(chalk7.red(`
|
|
1202
1390
|
\u274C Process exited with code ${code}`));
|
|
1203
1391
|
}
|
|
1204
1392
|
process.exit(code || 0);
|
|
@@ -1207,7 +1395,7 @@ async function runProject(targetDir) {
|
|
|
1207
1395
|
|
|
1208
1396
|
// src/commands/onboarding.ts
|
|
1209
1397
|
import { select as select2, password as password2 } from "@inquirer/prompts";
|
|
1210
|
-
import
|
|
1398
|
+
import chalk8 from "chalk";
|
|
1211
1399
|
var PROVIDER_MODELS = {
|
|
1212
1400
|
gemini: [
|
|
1213
1401
|
{ name: "\u26A1 Gemini 2.5 Flash (Fast)", value: "gemini-2.5-flash" },
|
|
@@ -1284,10 +1472,10 @@ function getActiveProvider() {
|
|
|
1284
1472
|
return null;
|
|
1285
1473
|
}
|
|
1286
1474
|
async function runOnboarding() {
|
|
1287
|
-
console.log(
|
|
1288
|
-
console.log(
|
|
1475
|
+
console.log(chalk8.cyan.bold("\n\u{1F680} Welcome to Agdi!\n"));
|
|
1476
|
+
console.log(chalk8.gray("Let's set up your AI provider in 3 quick steps.\n"));
|
|
1289
1477
|
const config = loadConfig();
|
|
1290
|
-
console.log(
|
|
1478
|
+
console.log(chalk8.white.bold("Step 1/3: Select your AI provider\n"));
|
|
1291
1479
|
const provider = await select2({
|
|
1292
1480
|
message: "Which AI provider would you like to use?",
|
|
1293
1481
|
choices: [
|
|
@@ -1298,7 +1486,7 @@ async function runOnboarding() {
|
|
|
1298
1486
|
{ name: "\u{1F30A} DeepSeek", value: "deepseek" }
|
|
1299
1487
|
]
|
|
1300
1488
|
});
|
|
1301
|
-
console.log(
|
|
1489
|
+
console.log(chalk8.white.bold("\nStep 2/3: Enter your API key\n"));
|
|
1302
1490
|
const keyUrls = {
|
|
1303
1491
|
gemini: "https://aistudio.google.com/apikey",
|
|
1304
1492
|
openrouter: "https://openrouter.ai/keys",
|
|
@@ -1306,7 +1494,7 @@ async function runOnboarding() {
|
|
|
1306
1494
|
anthropic: "https://console.anthropic.com/",
|
|
1307
1495
|
deepseek: "https://platform.deepseek.com/"
|
|
1308
1496
|
};
|
|
1309
|
-
console.log(
|
|
1497
|
+
console.log(chalk8.gray(`Get your key at: ${chalk8.cyan(keyUrls[provider])}
|
|
1310
1498
|
`));
|
|
1311
1499
|
const apiKey = await password2({
|
|
1312
1500
|
message: `Enter your ${provider} API key:`,
|
|
@@ -1330,7 +1518,7 @@ async function runOnboarding() {
|
|
|
1330
1518
|
break;
|
|
1331
1519
|
}
|
|
1332
1520
|
config.defaultProvider = provider;
|
|
1333
|
-
console.log(
|
|
1521
|
+
console.log(chalk8.white.bold("\nStep 3/3: Choose your default model\n"));
|
|
1334
1522
|
const models = PROVIDER_MODELS[provider] || PROVIDER_MODELS.gemini;
|
|
1335
1523
|
const model = await select2({
|
|
1336
1524
|
message: "Select your default model:",
|
|
@@ -1338,18 +1526,18 @@ async function runOnboarding() {
|
|
|
1338
1526
|
});
|
|
1339
1527
|
config.defaultModel = model;
|
|
1340
1528
|
saveConfig(config);
|
|
1341
|
-
console.log(
|
|
1342
|
-
console.log(
|
|
1343
|
-
console.log(
|
|
1529
|
+
console.log(chalk8.green("\n\u2705 Setup complete!"));
|
|
1530
|
+
console.log(chalk8.gray(`Provider: ${chalk8.cyan(provider)}`));
|
|
1531
|
+
console.log(chalk8.gray(`Model: ${chalk8.cyan(model)}
|
|
1344
1532
|
`));
|
|
1345
1533
|
return { provider, apiKey, model };
|
|
1346
1534
|
}
|
|
1347
1535
|
async function selectModel() {
|
|
1348
1536
|
const config = loadConfig();
|
|
1349
1537
|
const provider = config.defaultProvider || "gemini";
|
|
1350
|
-
console.log(
|
|
1351
|
-
console.log(
|
|
1352
|
-
console.log(
|
|
1538
|
+
console.log(chalk8.cyan.bold("\n\u{1F504} Change Model\n"));
|
|
1539
|
+
console.log(chalk8.gray(`Current provider: ${chalk8.cyan(provider)}`));
|
|
1540
|
+
console.log(chalk8.gray(`Current model: ${chalk8.cyan(config.defaultModel || "not set")}
|
|
1353
1541
|
`));
|
|
1354
1542
|
const changeProvider = await select2({
|
|
1355
1543
|
message: "What would you like to do?",
|
|
@@ -1360,7 +1548,7 @@ async function selectModel() {
|
|
|
1360
1548
|
]
|
|
1361
1549
|
});
|
|
1362
1550
|
if (changeProvider === "cancel") {
|
|
1363
|
-
console.log(
|
|
1551
|
+
console.log(chalk8.gray("\nCancelled.\n"));
|
|
1364
1552
|
return;
|
|
1365
1553
|
}
|
|
1366
1554
|
let selectedProvider = provider;
|
|
@@ -1383,7 +1571,7 @@ async function selectModel() {
|
|
|
1383
1571
|
deepseek: config.deepseekApiKey
|
|
1384
1572
|
};
|
|
1385
1573
|
if (!keyMap[selectedProvider]) {
|
|
1386
|
-
console.log(
|
|
1574
|
+
console.log(chalk8.yellow(`
|
|
1387
1575
|
\u26A0\uFE0F No API key configured for ${selectedProvider}
|
|
1388
1576
|
`));
|
|
1389
1577
|
const apiKey = await password2({
|
|
@@ -1417,9 +1605,9 @@ async function selectModel() {
|
|
|
1417
1605
|
});
|
|
1418
1606
|
config.defaultModel = model;
|
|
1419
1607
|
saveConfig(config);
|
|
1420
|
-
console.log(
|
|
1421
|
-
console.log(
|
|
1422
|
-
console.log(
|
|
1608
|
+
console.log(chalk8.green("\n\u2705 Model changed!"));
|
|
1609
|
+
console.log(chalk8.gray(`Provider: ${chalk8.cyan(selectedProvider)}`));
|
|
1610
|
+
console.log(chalk8.gray(`Model: ${chalk8.cyan(model)}
|
|
1423
1611
|
`));
|
|
1424
1612
|
}
|
|
1425
1613
|
|
|
@@ -1427,165 +1615,6 @@ async function selectModel() {
|
|
|
1427
1615
|
import { input as input5, confirm as confirm3 } from "@inquirer/prompts";
|
|
1428
1616
|
import chalk13 from "chalk";
|
|
1429
1617
|
|
|
1430
|
-
// src/utils/ui.ts
|
|
1431
|
-
import chalk8 from "chalk";
|
|
1432
|
-
import gradient from "gradient-string";
|
|
1433
|
-
import boxen from "boxen";
|
|
1434
|
-
import figlet from "figlet";
|
|
1435
|
-
import ora3 from "ora";
|
|
1436
|
-
var THEME = {
|
|
1437
|
-
cyan: "#06b6d4",
|
|
1438
|
-
// Cyan-500
|
|
1439
|
-
purple: "#8b5cf6",
|
|
1440
|
-
// Violet-500
|
|
1441
|
-
red: "#ef4444",
|
|
1442
|
-
// Red-500
|
|
1443
|
-
yellow: "#eab308",
|
|
1444
|
-
// Yellow-500
|
|
1445
|
-
gray: "#71717a",
|
|
1446
|
-
// Zinc-500
|
|
1447
|
-
dim: "#52525b"
|
|
1448
|
-
// Zinc-600
|
|
1449
|
-
};
|
|
1450
|
-
var brandGradient = gradient([THEME.cyan, THEME.purple]);
|
|
1451
|
-
var errorGradient = gradient([THEME.red, "#b91c1c"]);
|
|
1452
|
-
var goldGradient = gradient([THEME.yellow, "#fbbf24"]);
|
|
1453
|
-
async function renderBanner(version = "v2.6.0") {
|
|
1454
|
-
console.clear();
|
|
1455
|
-
const text = await new Promise((resolve5) => {
|
|
1456
|
-
figlet("AGDI", { font: "Slant" }, (err, data) => {
|
|
1457
|
-
resolve5(data || "AGDI");
|
|
1458
|
-
});
|
|
1459
|
-
});
|
|
1460
|
-
console.log(brandGradient.multiline(text));
|
|
1461
|
-
console.log(chalk8.hex(THEME.dim)(` ${version} [ARCHITECT ONLINE]
|
|
1462
|
-
`));
|
|
1463
|
-
}
|
|
1464
|
-
function renderBox(title, content, style = "info") {
|
|
1465
|
-
let borderColor = THEME.cyan;
|
|
1466
|
-
let titleColor = chalk8.cyan;
|
|
1467
|
-
if (style === "success") {
|
|
1468
|
-
borderColor = THEME.cyan;
|
|
1469
|
-
} else if (style === "warning") {
|
|
1470
|
-
borderColor = THEME.yellow;
|
|
1471
|
-
titleColor = chalk8.yellow;
|
|
1472
|
-
} else if (style === "error") {
|
|
1473
|
-
borderColor = THEME.red;
|
|
1474
|
-
titleColor = chalk8.red;
|
|
1475
|
-
}
|
|
1476
|
-
const box = boxen(content, {
|
|
1477
|
-
title: titleColor.bold(title),
|
|
1478
|
-
padding: 1,
|
|
1479
|
-
margin: 1,
|
|
1480
|
-
borderStyle: "round",
|
|
1481
|
-
borderColor,
|
|
1482
|
-
dimBorder: false,
|
|
1483
|
-
float: "left"
|
|
1484
|
-
});
|
|
1485
|
-
console.log(box);
|
|
1486
|
-
}
|
|
1487
|
-
function renderAlert(title, message) {
|
|
1488
|
-
console.log("");
|
|
1489
|
-
const box = boxen(chalk8.white(message), {
|
|
1490
|
-
title: chalk8.red.bold(`\u{1F6E1}\uFE0F ${title.toUpperCase()} `),
|
|
1491
|
-
padding: 1,
|
|
1492
|
-
borderStyle: "double",
|
|
1493
|
-
borderColor: "red",
|
|
1494
|
-
textAlignment: "center"
|
|
1495
|
-
});
|
|
1496
|
-
console.log(box);
|
|
1497
|
-
console.log("");
|
|
1498
|
-
}
|
|
1499
|
-
function printUserMessage(message) {
|
|
1500
|
-
console.log("");
|
|
1501
|
-
console.log(chalk8.cyan.bold("\u{1F464} YOU \u203A ") + chalk8.white(message));
|
|
1502
|
-
console.log("");
|
|
1503
|
-
}
|
|
1504
|
-
function printAIMessage(message) {
|
|
1505
|
-
console.log("");
|
|
1506
|
-
console.log(brandGradient.multiline("\u26A1 AGDI \u203A "));
|
|
1507
|
-
console.log(message.trim());
|
|
1508
|
-
console.log("");
|
|
1509
|
-
}
|
|
1510
|
-
function createSpinner(text) {
|
|
1511
|
-
return ora3({
|
|
1512
|
-
text: chalk8.hex(THEME.gray)(text),
|
|
1513
|
-
color: "cyan",
|
|
1514
|
-
spinner: "dots",
|
|
1515
|
-
discardStdin: false
|
|
1516
|
-
// Important for allowing interruption if needed
|
|
1517
|
-
});
|
|
1518
|
-
}
|
|
1519
|
-
function printIter() {
|
|
1520
|
-
console.log(chalk8.hex(THEME.dim)("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1521
|
-
}
|
|
1522
|
-
var activeReadlineInterface = null;
|
|
1523
|
-
function registerActivePrompt(rl) {
|
|
1524
|
-
activeReadlineInterface = rl;
|
|
1525
|
-
}
|
|
1526
|
-
var flags = {
|
|
1527
|
-
yes: false,
|
|
1528
|
-
headless: false
|
|
1529
|
-
};
|
|
1530
|
-
function setFlags(newFlags) {
|
|
1531
|
-
Object.assign(flags, newFlags);
|
|
1532
|
-
}
|
|
1533
|
-
function safeExit(code = 0) {
|
|
1534
|
-
if (activeReadlineInterface) {
|
|
1535
|
-
try {
|
|
1536
|
-
activeReadlineInterface.close?.();
|
|
1537
|
-
activeReadlineInterface.destroy?.();
|
|
1538
|
-
} catch {
|
|
1539
|
-
}
|
|
1540
|
-
activeReadlineInterface = null;
|
|
1541
|
-
}
|
|
1542
|
-
setImmediate(() => {
|
|
1543
|
-
process.exit(code);
|
|
1544
|
-
});
|
|
1545
|
-
throw new Error("Process exiting");
|
|
1546
|
-
}
|
|
1547
|
-
async function smartConfirm(message, defaultValue = false) {
|
|
1548
|
-
if (flags.yes || flags.headless || process.env.CI === "true" || process.env.CI === "1") {
|
|
1549
|
-
console.log(chalk8.gray(` [Auto-approved: ${message}]`));
|
|
1550
|
-
return true;
|
|
1551
|
-
}
|
|
1552
|
-
if (!process.stdout.isTTY) {
|
|
1553
|
-
console.warn(chalk8.yellow("\u26A0\uFE0F Non-interactive session detected. Use --yes to approve actions."));
|
|
1554
|
-
return false;
|
|
1555
|
-
}
|
|
1556
|
-
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
1557
|
-
return confirm4({ message, default: defaultValue });
|
|
1558
|
-
}
|
|
1559
|
-
async function smartSelect(message, choices, defaultValue) {
|
|
1560
|
-
if (!process.stdout.isTTY || flags.headless) {
|
|
1561
|
-
const result = defaultValue || choices[0]?.value;
|
|
1562
|
-
if (result) {
|
|
1563
|
-
console.log(chalk8.gray(` [Auto-selected: ${result}]`));
|
|
1564
|
-
}
|
|
1565
|
-
return result || null;
|
|
1566
|
-
}
|
|
1567
|
-
const { select: select5 } = await import("@inquirer/prompts");
|
|
1568
|
-
return select5({ message, choices });
|
|
1569
|
-
}
|
|
1570
|
-
var ui = {
|
|
1571
|
-
renderBanner,
|
|
1572
|
-
renderBox,
|
|
1573
|
-
renderAlert,
|
|
1574
|
-
printUserMessage,
|
|
1575
|
-
printAIMessage,
|
|
1576
|
-
createSpinner,
|
|
1577
|
-
printIter,
|
|
1578
|
-
brandGradient,
|
|
1579
|
-
THEME,
|
|
1580
|
-
// Safety & Automation
|
|
1581
|
-
safeExit,
|
|
1582
|
-
smartConfirm,
|
|
1583
|
-
smartSelect,
|
|
1584
|
-
setFlags,
|
|
1585
|
-
flags,
|
|
1586
|
-
registerActivePrompt
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
1618
|
// src/actions/plan-executor.ts
|
|
1590
1619
|
import { select as select4, confirm } from "@inquirer/prompts";
|
|
1591
1620
|
import chalk11 from "chalk";
|
|
@@ -3165,10 +3194,37 @@ Target: ${env.workspaceRoot}`
|
|
|
3165
3194
|
console.log(chalk11.green("\u2713 Trusted for this session\n"));
|
|
3166
3195
|
}
|
|
3167
3196
|
}
|
|
3168
|
-
const
|
|
3169
|
-
|
|
3170
|
-
|
|
3197
|
+
const dangerousKeywords = ["rm", "rimraf", "del", "npm install", "pnpm install", "yarn install", "chmod", "chown"];
|
|
3198
|
+
const dangerousActions = plan.actions.filter((a) => {
|
|
3199
|
+
if (a.type !== "exec") return false;
|
|
3200
|
+
const cmd = a.argv.join(" ");
|
|
3201
|
+
return dangerousKeywords.some((k) => cmd.includes(k));
|
|
3171
3202
|
});
|
|
3203
|
+
if (dangerousActions.length > 0) {
|
|
3204
|
+
ui.renderAlert(
|
|
3205
|
+
"\u26A0\uFE0F DANGEROUS COMMANDS DETECTED",
|
|
3206
|
+
dangerousActions.map((a) => `\u2022 ${a.argv.join(" ")}`).join("\n") + "\n\nThese commands can modify your system or delete files."
|
|
3207
|
+
);
|
|
3208
|
+
const allowDangerous = await confirm({
|
|
3209
|
+
message: "Are you SURE you want to execute these dangerous commands?",
|
|
3210
|
+
default: false
|
|
3211
|
+
});
|
|
3212
|
+
if (!allowDangerous) {
|
|
3213
|
+
console.log(chalk11.yellow("\n\u{1F44B} Execution cancelled for safety.\n"));
|
|
3214
|
+
return { success: false, results, filesCreated, commandsRun, errors: ["User cancelled dangerous commands"] };
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
let approved = false;
|
|
3218
|
+
if (ui.flags.dryRun) {
|
|
3219
|
+
console.log(chalk11.yellow("\n\u{1F6A7} DRY RUN MODE ENABLED"));
|
|
3220
|
+
console.log(chalk11.gray(" No files will be written and no commands will be executed.\n"));
|
|
3221
|
+
approved = true;
|
|
3222
|
+
} else {
|
|
3223
|
+
approved = await confirm({
|
|
3224
|
+
message: `Execute ${plan.actions.length} actions?`,
|
|
3225
|
+
default: true
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3172
3228
|
if (!approved) {
|
|
3173
3229
|
console.log(chalk11.yellow("\n\u{1F44B} Plan cancelled.\n"));
|
|
3174
3230
|
return { success: false, results, filesCreated, commandsRun, errors: ["User cancelled"] };
|
|
@@ -3185,6 +3241,11 @@ Target: ${env.workspaceRoot}`
|
|
|
3185
3241
|
for (let i = 0; i < plan.actions.length; i++) {
|
|
3186
3242
|
const action = plan.actions[i];
|
|
3187
3243
|
displayActionProgress(action, i, plan.actions.length);
|
|
3244
|
+
if (ui.flags.dryRun) {
|
|
3245
|
+
console.log(chalk11.yellow(` [DRY RUN] Would execute: ${action.type} ${action.path || action.argv?.join(" ")}`));
|
|
3246
|
+
results.push({ action, success: true, output: "(dry run)" });
|
|
3247
|
+
continue;
|
|
3248
|
+
}
|
|
3188
3249
|
const result = await executeAction(action);
|
|
3189
3250
|
results.push(result);
|
|
3190
3251
|
if (result.success) {
|
|
@@ -4040,8 +4101,8 @@ registerTool({
|
|
|
4040
4101
|
{ name: "path", type: "string", description: "Directory path", required: false, default: "." }
|
|
4041
4102
|
],
|
|
4042
4103
|
execute: async (params) => {
|
|
4043
|
-
const { readdirSync: readdirSync2, statSync:
|
|
4044
|
-
const { resolve: resolve5, join:
|
|
4104
|
+
const { readdirSync: readdirSync2, statSync: statSync3 } = await import("fs");
|
|
4105
|
+
const { resolve: resolve5, join: join9 } = await import("path");
|
|
4045
4106
|
try {
|
|
4046
4107
|
const dirPath = resolve5(process.cwd(), params.path || ".");
|
|
4047
4108
|
const entries = readdirSync2(dirPath);
|
|
@@ -4049,7 +4110,7 @@ registerTool({
|
|
|
4049
4110
|
const dirs = [];
|
|
4050
4111
|
for (const entry of entries) {
|
|
4051
4112
|
try {
|
|
4052
|
-
const stat =
|
|
4113
|
+
const stat = statSync3(join9(dirPath, entry));
|
|
4053
4114
|
if (stat.isDirectory()) {
|
|
4054
4115
|
dirs.push(entry + "/");
|
|
4055
4116
|
} else {
|
|
@@ -4649,25 +4710,183 @@ function handleError(error) {
|
|
|
4649
4710
|
}
|
|
4650
4711
|
}
|
|
4651
4712
|
|
|
4713
|
+
// src/commands/doctor.ts
|
|
4714
|
+
import chalk14 from "chalk";
|
|
4715
|
+
import { existsSync as existsSync11, statSync as statSync2 } from "fs";
|
|
4716
|
+
import { homedir as homedir7, platform as platform2 } from "os";
|
|
4717
|
+
import { join as join8 } from "path";
|
|
4718
|
+
async function runDoctor() {
|
|
4719
|
+
console.log("");
|
|
4720
|
+
ui.renderBox("AGDI DOCTOR", "Running diagnostics...", "info");
|
|
4721
|
+
console.log("");
|
|
4722
|
+
const results = [];
|
|
4723
|
+
const nodeVersion = process.version;
|
|
4724
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0], 10);
|
|
4725
|
+
if (majorVersion >= 18) {
|
|
4726
|
+
results.push({
|
|
4727
|
+
name: "Node.js Version",
|
|
4728
|
+
status: "pass",
|
|
4729
|
+
message: `${nodeVersion} (\u226518 required)`
|
|
4730
|
+
});
|
|
4731
|
+
} else {
|
|
4732
|
+
results.push({
|
|
4733
|
+
name: "Node.js Version",
|
|
4734
|
+
status: "fail",
|
|
4735
|
+
message: `${nodeVersion} - Node.js 18+ required!`
|
|
4736
|
+
});
|
|
4737
|
+
}
|
|
4738
|
+
const configDir = join8(homedir7(), ".agdi");
|
|
4739
|
+
const configPath = join8(configDir, "config.json");
|
|
4740
|
+
if (existsSync11(configPath)) {
|
|
4741
|
+
if (platform2() !== "win32") {
|
|
4742
|
+
try {
|
|
4743
|
+
const stats = statSync2(configPath);
|
|
4744
|
+
const mode = (stats.mode & parseInt("777", 8)).toString(8);
|
|
4745
|
+
if (mode === "600") {
|
|
4746
|
+
results.push({
|
|
4747
|
+
name: "Config Permissions",
|
|
4748
|
+
status: "pass",
|
|
4749
|
+
message: `${configPath} (mode: 600)`
|
|
4750
|
+
});
|
|
4751
|
+
} else {
|
|
4752
|
+
results.push({
|
|
4753
|
+
name: "Config Permissions",
|
|
4754
|
+
status: "warn",
|
|
4755
|
+
message: `${configPath} (mode: ${mode}) - Should be 600!`
|
|
4756
|
+
});
|
|
4757
|
+
}
|
|
4758
|
+
} catch {
|
|
4759
|
+
results.push({
|
|
4760
|
+
name: "Config Permissions",
|
|
4761
|
+
status: "warn",
|
|
4762
|
+
message: "Could not check permissions"
|
|
4763
|
+
});
|
|
4764
|
+
}
|
|
4765
|
+
} else {
|
|
4766
|
+
results.push({
|
|
4767
|
+
name: "Config File",
|
|
4768
|
+
status: "pass",
|
|
4769
|
+
message: `${configPath} exists`
|
|
4770
|
+
});
|
|
4771
|
+
}
|
|
4772
|
+
} else {
|
|
4773
|
+
results.push({
|
|
4774
|
+
name: "Config File",
|
|
4775
|
+
status: "warn",
|
|
4776
|
+
message: "No config found. Run: agdi auth"
|
|
4777
|
+
});
|
|
4778
|
+
}
|
|
4779
|
+
const activeConfig = getActiveProvider();
|
|
4780
|
+
if (activeConfig) {
|
|
4781
|
+
const keyPreview = activeConfig.apiKey.slice(0, 8) + "..." + activeConfig.apiKey.slice(-4);
|
|
4782
|
+
results.push({
|
|
4783
|
+
name: "API Key",
|
|
4784
|
+
status: "pass",
|
|
4785
|
+
message: `${activeConfig.provider} (${keyPreview})`
|
|
4786
|
+
});
|
|
4787
|
+
results.push({
|
|
4788
|
+
name: "Active Model",
|
|
4789
|
+
status: "pass",
|
|
4790
|
+
message: activeConfig.model
|
|
4791
|
+
});
|
|
4792
|
+
} else {
|
|
4793
|
+
results.push({
|
|
4794
|
+
name: "API Key",
|
|
4795
|
+
status: "fail",
|
|
4796
|
+
message: "No API key configured. Run: agdi auth"
|
|
4797
|
+
});
|
|
4798
|
+
}
|
|
4799
|
+
const auditPath = join8(configDir, "audit.jsonl");
|
|
4800
|
+
if (existsSync11(auditPath)) {
|
|
4801
|
+
results.push({
|
|
4802
|
+
name: "Audit Log",
|
|
4803
|
+
status: "pass",
|
|
4804
|
+
message: `Logging to ${auditPath}`
|
|
4805
|
+
});
|
|
4806
|
+
} else {
|
|
4807
|
+
results.push({
|
|
4808
|
+
name: "Audit Log",
|
|
4809
|
+
status: "warn",
|
|
4810
|
+
message: "No audit log found (will be created on first action)"
|
|
4811
|
+
});
|
|
4812
|
+
}
|
|
4813
|
+
const trustPath = join8(configDir, "trusted-workspaces.json");
|
|
4814
|
+
if (existsSync11(trustPath)) {
|
|
4815
|
+
results.push({
|
|
4816
|
+
name: "Trust Store",
|
|
4817
|
+
status: "pass",
|
|
4818
|
+
message: "Workspace trust store found"
|
|
4819
|
+
});
|
|
4820
|
+
} else {
|
|
4821
|
+
results.push({
|
|
4822
|
+
name: "Trust Store",
|
|
4823
|
+
status: "warn",
|
|
4824
|
+
message: "No trusted workspaces yet"
|
|
4825
|
+
});
|
|
4826
|
+
}
|
|
4827
|
+
console.log(chalk14.bold("Diagnostic Results:\n"));
|
|
4828
|
+
for (const result of results) {
|
|
4829
|
+
let icon;
|
|
4830
|
+
let color;
|
|
4831
|
+
switch (result.status) {
|
|
4832
|
+
case "pass":
|
|
4833
|
+
icon = "\u2705";
|
|
4834
|
+
color = chalk14.green;
|
|
4835
|
+
break;
|
|
4836
|
+
case "warn":
|
|
4837
|
+
icon = "\u26A0\uFE0F";
|
|
4838
|
+
color = chalk14.yellow;
|
|
4839
|
+
break;
|
|
4840
|
+
case "fail":
|
|
4841
|
+
icon = "\u274C";
|
|
4842
|
+
color = chalk14.red;
|
|
4843
|
+
break;
|
|
4844
|
+
}
|
|
4845
|
+
console.log(` ${icon} ${chalk14.bold(result.name)}`);
|
|
4846
|
+
console.log(` ${color(result.message)}
|
|
4847
|
+
`);
|
|
4848
|
+
}
|
|
4849
|
+
const passed = results.filter((r) => r.status === "pass").length;
|
|
4850
|
+
const warnings = results.filter((r) => r.status === "warn").length;
|
|
4851
|
+
const failed = results.filter((r) => r.status === "fail").length;
|
|
4852
|
+
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4853
|
+
console.log(`
|
|
4854
|
+
${chalk14.bold("Summary:")} ${chalk14.green(passed + " passed")}, ${chalk14.yellow(warnings + " warnings")}, ${chalk14.red(failed + " failed")}
|
|
4855
|
+
`);
|
|
4856
|
+
if (failed > 0) {
|
|
4857
|
+
console.log(chalk14.red(" \u26A0\uFE0F There are critical issues that need attention.\n"));
|
|
4858
|
+
} else if (warnings > 0) {
|
|
4859
|
+
console.log(chalk14.yellow(" \u2139\uFE0F There are some warnings, but Agdi should work.\n"));
|
|
4860
|
+
} else {
|
|
4861
|
+
console.log(chalk14.green(" \u{1F389} All checks passed! Agdi is ready to use.\n"));
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4652
4865
|
// src/index.ts
|
|
4653
4866
|
var BANNER = `
|
|
4654
|
-
${
|
|
4655
|
-
${
|
|
4656
|
-
${
|
|
4657
|
-
${
|
|
4658
|
-
${
|
|
4659
|
-
${
|
|
4867
|
+
${chalk15.cyan(` ___ __ _ `)}
|
|
4868
|
+
${chalk15.cyan(` / | ____ _____/ /(_) `)}
|
|
4869
|
+
${chalk15.cyan(` / /| | / __ \`/ __ // / `)}
|
|
4870
|
+
${chalk15.cyan(` / ___ |/ /_/ / /_/ // / `)}
|
|
4871
|
+
${chalk15.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
|
|
4872
|
+
${chalk15.cyan(` /____/ `)}
|
|
4660
4873
|
`;
|
|
4661
4874
|
var program = new Command();
|
|
4662
|
-
program.name("agdi").description(
|
|
4875
|
+
program.name("agdi").description(chalk15.cyan("\u{1F680} AI-powered coding assistant")).version("2.8.1").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");
|
|
4663
4876
|
program.hook("preAction", (thisCommand) => {
|
|
4664
4877
|
const opts = thisCommand.opts();
|
|
4665
4878
|
if (opts.yes) {
|
|
4666
4879
|
ui.setFlags({ yes: true, headless: true });
|
|
4667
4880
|
}
|
|
4881
|
+
if (opts.minimal) {
|
|
4882
|
+
ui.setFlags({ minimal: true });
|
|
4883
|
+
}
|
|
4884
|
+
if (opts.dryRun) {
|
|
4885
|
+
ui.setFlags({ dryRun: true });
|
|
4886
|
+
}
|
|
4668
4887
|
});
|
|
4669
4888
|
program.addHelpText("beforeAll", () => {
|
|
4670
|
-
return BANNER + "\n" +
|
|
4889
|
+
return BANNER + "\n" + chalk15.gray(" The Open Source AI Architect") + "\n" + chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
4671
4890
|
});
|
|
4672
4891
|
program.action(async () => {
|
|
4673
4892
|
try {
|
|
@@ -4678,8 +4897,15 @@ program.action(async () => {
|
|
|
4678
4897
|
await startCodingMode();
|
|
4679
4898
|
} catch (error) {
|
|
4680
4899
|
if (error.name === "ExitPromptError") {
|
|
4681
|
-
console.log(
|
|
4682
|
-
|
|
4900
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Goodbye!\n"));
|
|
4901
|
+
try {
|
|
4902
|
+
ui.safeExit(0);
|
|
4903
|
+
} catch {
|
|
4904
|
+
}
|
|
4905
|
+
return;
|
|
4906
|
+
}
|
|
4907
|
+
if (error.name === "GracefulExitError") {
|
|
4908
|
+
return;
|
|
4683
4909
|
}
|
|
4684
4910
|
throw error;
|
|
4685
4911
|
}
|
|
@@ -4693,8 +4919,15 @@ program.command("auth").description("Configure API keys").option("--status", "Sh
|
|
|
4693
4919
|
}
|
|
4694
4920
|
} catch (error) {
|
|
4695
4921
|
if (error.name === "ExitPromptError") {
|
|
4696
|
-
console.log(
|
|
4697
|
-
|
|
4922
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
4923
|
+
try {
|
|
4924
|
+
ui.safeExit(0);
|
|
4925
|
+
} catch {
|
|
4926
|
+
}
|
|
4927
|
+
return;
|
|
4928
|
+
}
|
|
4929
|
+
if (error.name === "GracefulExitError") {
|
|
4930
|
+
return;
|
|
4698
4931
|
}
|
|
4699
4932
|
throw error;
|
|
4700
4933
|
}
|
|
@@ -4704,8 +4937,15 @@ program.command("model").alias("models").description("Change AI model").action(a
|
|
|
4704
4937
|
await selectModel();
|
|
4705
4938
|
} catch (error) {
|
|
4706
4939
|
if (error.name === "ExitPromptError") {
|
|
4707
|
-
console.log(
|
|
4708
|
-
|
|
4940
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
4941
|
+
try {
|
|
4942
|
+
ui.safeExit(0);
|
|
4943
|
+
} catch {
|
|
4944
|
+
}
|
|
4945
|
+
return;
|
|
4946
|
+
}
|
|
4947
|
+
if (error.name === "GracefulExitError") {
|
|
4948
|
+
return;
|
|
4709
4949
|
}
|
|
4710
4950
|
throw error;
|
|
4711
4951
|
}
|
|
@@ -4718,8 +4958,15 @@ program.command("chat").description("Start a chat session").action(async () => {
|
|
|
4718
4958
|
await startChat();
|
|
4719
4959
|
} catch (error) {
|
|
4720
4960
|
if (error.name === "ExitPromptError") {
|
|
4721
|
-
console.log(
|
|
4722
|
-
|
|
4961
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Goodbye!\n"));
|
|
4962
|
+
try {
|
|
4963
|
+
ui.safeExit(0);
|
|
4964
|
+
} catch {
|
|
4965
|
+
}
|
|
4966
|
+
return;
|
|
4967
|
+
}
|
|
4968
|
+
if (error.name === "GracefulExitError") {
|
|
4969
|
+
return;
|
|
4723
4970
|
}
|
|
4724
4971
|
throw error;
|
|
4725
4972
|
}
|
|
@@ -4729,20 +4976,27 @@ program.command("run [directory]").description("Run a generated project").action
|
|
|
4729
4976
|
await runProject(directory);
|
|
4730
4977
|
} catch (error) {
|
|
4731
4978
|
if (error.name === "ExitPromptError") {
|
|
4732
|
-
console.log(
|
|
4733
|
-
|
|
4979
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
4980
|
+
try {
|
|
4981
|
+
ui.safeExit(0);
|
|
4982
|
+
} catch {
|
|
4983
|
+
}
|
|
4984
|
+
return;
|
|
4985
|
+
}
|
|
4986
|
+
if (error.name === "GracefulExitError") {
|
|
4987
|
+
return;
|
|
4734
4988
|
}
|
|
4735
4989
|
throw error;
|
|
4736
4990
|
}
|
|
4737
4991
|
});
|
|
4738
|
-
program.command("build <prompt>").alias("b").description("Generate an app from a prompt").option("-o, --output <dir>", "Output directory", "./generated-app").action(async (prompt, options) => {
|
|
4992
|
+
program.command("build <prompt>").alias("b").description("Generate an app from a prompt").option("-o, --output <dir>", "Output directory", "./generated-app").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").action(async (prompt, options) => {
|
|
4739
4993
|
try {
|
|
4740
4994
|
if (needsOnboarding()) {
|
|
4741
4995
|
await runOnboarding();
|
|
4742
4996
|
}
|
|
4743
4997
|
const activeConfig = getActiveProvider();
|
|
4744
4998
|
if (!activeConfig) {
|
|
4745
|
-
console.log(
|
|
4999
|
+
console.log(chalk15.red("\u274C No API key configured. Run: agdi auth"));
|
|
4746
5000
|
return;
|
|
4747
5001
|
}
|
|
4748
5002
|
const spinner = ora5("Generating application...").start();
|
|
@@ -4754,31 +5008,50 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
|
|
|
4754
5008
|
const pm = new ProjectManager();
|
|
4755
5009
|
pm.create(options.output.replace("./", ""), prompt);
|
|
4756
5010
|
const { plan, files } = await generateApp(prompt, llm, (step, file) => {
|
|
4757
|
-
spinner.text = file ? `${step} ${
|
|
5011
|
+
spinner.text = file ? `${step} ${chalk15.gray(file)}` : step;
|
|
4758
5012
|
});
|
|
4759
5013
|
pm.updateFiles(files);
|
|
4760
5014
|
pm.updateDependencies(plan.dependencies);
|
|
5015
|
+
if (options.dryRun || ui.flags.dryRun) {
|
|
5016
|
+
spinner.stop();
|
|
5017
|
+
console.log(chalk15.cyan.bold("\n\u{1F6A7} DRY RUN SUMMARY\n"));
|
|
5018
|
+
console.log(chalk15.gray(`Project: ${plan.name}
|
|
5019
|
+
`));
|
|
5020
|
+
console.log(chalk15.cyan("Files to be created:"));
|
|
5021
|
+
files.forEach((f) => console.log(chalk15.gray(` \u{1F4C4} ${f.path}`)));
|
|
5022
|
+
console.log(chalk15.cyan("\nDependencies:"));
|
|
5023
|
+
console.log(chalk15.gray(` \u{1F4E6} ${plan.dependencies.join(", ")}`));
|
|
5024
|
+
console.log(chalk15.green("\n\u2713 Dry run complete. No files written.\n"));
|
|
5025
|
+
return;
|
|
5026
|
+
}
|
|
4761
5027
|
await writeProject(pm.get(), options.output);
|
|
4762
|
-
spinner.succeed(
|
|
4763
|
-
console.log(
|
|
4764
|
-
\u{1F4C1} Created ${files.length} files in ${
|
|
4765
|
-
console.log(
|
|
5028
|
+
spinner.succeed(chalk15.green("App generated!"));
|
|
5029
|
+
console.log(chalk15.gray(`
|
|
5030
|
+
\u{1F4C1} Created ${files.length} files in ${chalk15.cyan(options.output)}`));
|
|
5031
|
+
console.log(chalk15.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
|
|
4766
5032
|
} catch (error) {
|
|
4767
5033
|
spinner.fail("Generation failed");
|
|
4768
5034
|
const msg = error instanceof Error ? error.message : String(error);
|
|
4769
5035
|
if (msg.includes("429") || msg.includes("quota")) {
|
|
4770
|
-
console.log(
|
|
5036
|
+
console.log(chalk15.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
|
|
4771
5037
|
} else if (msg.includes("401") || msg.includes("403")) {
|
|
4772
|
-
console.log(
|
|
5038
|
+
console.log(chalk15.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
|
|
4773
5039
|
} else {
|
|
4774
|
-
console.error(
|
|
5040
|
+
console.error(chalk15.red("\n" + msg + "\n"));
|
|
4775
5041
|
}
|
|
4776
5042
|
ui.safeExit(1);
|
|
4777
5043
|
}
|
|
4778
5044
|
} catch (error) {
|
|
4779
5045
|
if (error.name === "ExitPromptError") {
|
|
4780
|
-
console.log(
|
|
4781
|
-
|
|
5046
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
5047
|
+
try {
|
|
5048
|
+
ui.safeExit(0);
|
|
5049
|
+
} catch {
|
|
5050
|
+
}
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
if (error.name === "GracefulExitError") {
|
|
5054
|
+
return;
|
|
4782
5055
|
}
|
|
4783
5056
|
throw error;
|
|
4784
5057
|
}
|
|
@@ -4786,11 +5059,11 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
|
|
|
4786
5059
|
program.command("config").description("Show configuration").action(async () => {
|
|
4787
5060
|
const config = loadConfig();
|
|
4788
5061
|
const active = getActiveProvider();
|
|
4789
|
-
console.log(
|
|
4790
|
-
console.log(
|
|
4791
|
-
console.log(
|
|
4792
|
-
console.log(
|
|
4793
|
-
console.log(
|
|
5062
|
+
console.log(chalk15.cyan.bold("\n\u2699\uFE0F Configuration\n"));
|
|
5063
|
+
console.log(chalk15.gray(" Provider: ") + chalk15.cyan(config.defaultProvider || "not set"));
|
|
5064
|
+
console.log(chalk15.gray(" Model: ") + chalk15.cyan(config.defaultModel || "not set"));
|
|
5065
|
+
console.log(chalk15.gray(" Config: ") + chalk15.gray("~/.agdi/config.json"));
|
|
5066
|
+
console.log(chalk15.cyan.bold("\n\u{1F510} API Keys\n"));
|
|
4794
5067
|
const keys = [
|
|
4795
5068
|
["Gemini", config.geminiApiKey],
|
|
4796
5069
|
["OpenRouter", config.openrouterApiKey],
|
|
@@ -4799,9 +5072,18 @@ program.command("config").description("Show configuration").action(async () => {
|
|
|
4799
5072
|
["DeepSeek", config.deepseekApiKey]
|
|
4800
5073
|
];
|
|
4801
5074
|
for (const [name, key] of keys) {
|
|
4802
|
-
const status = key ?
|
|
5075
|
+
const status = key ? chalk15.green("\u2713") : chalk15.gray("\u2717");
|
|
4803
5076
|
console.log(` ${status} ${name}`);
|
|
4804
5077
|
}
|
|
4805
5078
|
console.log("");
|
|
5079
|
+
console.log("");
|
|
5080
|
+
});
|
|
5081
|
+
program.command("doctor").alias("doc").description("Run self-diagnosis checks").action(async () => {
|
|
5082
|
+
try {
|
|
5083
|
+
await runDoctor();
|
|
5084
|
+
} catch (error) {
|
|
5085
|
+
console.error(chalk15.red("Diagnostic failed: " + error));
|
|
5086
|
+
ui.safeExit(1);
|
|
5087
|
+
}
|
|
4806
5088
|
});
|
|
4807
5089
|
program.parse();
|