agdi 2.7.1 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +570 -336
- 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,167 @@ 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
|
+
function safeExit(code = 0) {
|
|
396
|
+
if (activeReadlineInterface) {
|
|
397
|
+
try {
|
|
398
|
+
activeReadlineInterface.close?.();
|
|
399
|
+
activeReadlineInterface.destroy?.();
|
|
400
|
+
} catch {
|
|
401
|
+
}
|
|
402
|
+
activeReadlineInterface = null;
|
|
403
|
+
}
|
|
404
|
+
setImmediate(() => {
|
|
405
|
+
process.exit(code);
|
|
406
|
+
});
|
|
407
|
+
throw new Error("Process exiting");
|
|
408
|
+
}
|
|
409
|
+
async function smartConfirm(message, defaultValue = false) {
|
|
410
|
+
if (flags.yes || flags.headless || process.env.CI === "true" || process.env.CI === "1") {
|
|
411
|
+
console.log(chalk.gray(` [Auto-approved: ${message}]`));
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
if (!process.stdout.isTTY) {
|
|
415
|
+
console.warn(chalk.yellow("\u26A0\uFE0F Non-interactive session detected. Use --yes to approve actions."));
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
419
|
+
return confirm4({ message, default: defaultValue });
|
|
420
|
+
}
|
|
421
|
+
async function smartSelect(message, choices, defaultValue) {
|
|
422
|
+
if (!process.stdout.isTTY || flags.headless) {
|
|
423
|
+
const result = defaultValue || choices[0]?.value;
|
|
424
|
+
if (result) {
|
|
425
|
+
console.log(chalk.gray(` [Auto-selected: ${result}]`));
|
|
426
|
+
}
|
|
427
|
+
return result || null;
|
|
428
|
+
}
|
|
429
|
+
const { select: select5 } = await import("@inquirer/prompts");
|
|
430
|
+
return select5({ message, choices });
|
|
431
|
+
}
|
|
432
|
+
var ui = {
|
|
433
|
+
renderBanner,
|
|
434
|
+
renderBox,
|
|
435
|
+
renderAlert,
|
|
436
|
+
printUserMessage,
|
|
437
|
+
printAIMessage,
|
|
438
|
+
createSpinner,
|
|
439
|
+
printIter,
|
|
440
|
+
brandGradient,
|
|
441
|
+
THEME,
|
|
442
|
+
// Safety & Automation
|
|
443
|
+
safeExit,
|
|
444
|
+
smartConfirm,
|
|
445
|
+
smartSelect,
|
|
446
|
+
setFlags,
|
|
447
|
+
flags,
|
|
448
|
+
registerActivePrompt
|
|
449
|
+
};
|
|
450
|
+
|
|
290
451
|
// src/core/architect/index.ts
|
|
291
452
|
var SYSTEM_PROMPT = `You are Agdi Architect, an expert software architect AI.
|
|
292
453
|
Your job is to generate complete, production-ready React applications.
|
|
@@ -294,7 +455,7 @@ Always use TypeScript, Tailwind CSS, and Vite.
|
|
|
294
455
|
Generate all necessary files including package.json, tsconfig.json, vite.config.ts.
|
|
295
456
|
Make the UI beautiful with modern design patterns.`;
|
|
296
457
|
async function generatePlan(prompt, llm) {
|
|
297
|
-
|
|
458
|
+
let planPrompt = `Create a detailed plan for: ${prompt}
|
|
298
459
|
|
|
299
460
|
Return a JSON object with:
|
|
300
461
|
{
|
|
@@ -306,6 +467,25 @@ Return a JSON object with:
|
|
|
306
467
|
}
|
|
307
468
|
|
|
308
469
|
Return ONLY valid JSON, no markdown.`;
|
|
470
|
+
if (ui.flags.minimal) {
|
|
471
|
+
planPrompt = `Create a minimal plan for: ${prompt}
|
|
472
|
+
|
|
473
|
+
CRITICAL: Minimal mode matches user request exactly.
|
|
474
|
+
- If user asks for "hello.ts", generate ONLY "hello.ts".
|
|
475
|
+
- Do NOT scaffold a full React app unless explicitly asked.
|
|
476
|
+
- Do NOT add boilerplate headers/footers.
|
|
477
|
+
|
|
478
|
+
Return a JSON object with:
|
|
479
|
+
{
|
|
480
|
+
"name": "minimal-project",
|
|
481
|
+
"description": "Minimal generation",
|
|
482
|
+
"files": [{"path": "requested-file.ts", "description": "Requested logic"}],
|
|
483
|
+
"dependencies": [],
|
|
484
|
+
"architecture": "Single file script"
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
Return ONLY valid JSON.`;
|
|
488
|
+
}
|
|
309
489
|
const response = await llm.generate(planPrompt, SYSTEM_PROMPT);
|
|
310
490
|
try {
|
|
311
491
|
const jsonMatch = response.text.match(/\{[\s\S]*\}/);
|
|
@@ -356,50 +536,52 @@ async function generateApp(prompt, llm, onProgress) {
|
|
|
356
536
|
const file = await generateFile(fileSpec.path, fileSpec.description, plan, llm);
|
|
357
537
|
files.push(file);
|
|
358
538
|
}
|
|
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
|
-
|
|
539
|
+
if (!ui.flags.minimal) {
|
|
540
|
+
onProgress?.("Creating package.json...", "package.json");
|
|
541
|
+
files.push({
|
|
542
|
+
path: "package.json",
|
|
543
|
+
content: JSON.stringify({
|
|
544
|
+
name: plan.name,
|
|
545
|
+
version: "0.1.0",
|
|
546
|
+
type: "module",
|
|
547
|
+
scripts: {
|
|
548
|
+
dev: "vite",
|
|
549
|
+
build: "tsc -b && vite build",
|
|
550
|
+
preview: "vite preview"
|
|
551
|
+
},
|
|
552
|
+
dependencies: {
|
|
553
|
+
"react": "^18.3.1",
|
|
554
|
+
"react-dom": "^18.3.1",
|
|
555
|
+
...plan.dependencies.reduce((acc, dep) => {
|
|
556
|
+
if (dep !== "react" && dep !== "react-dom") {
|
|
557
|
+
acc[dep] = "latest";
|
|
558
|
+
}
|
|
559
|
+
return acc;
|
|
560
|
+
}, {})
|
|
561
|
+
},
|
|
562
|
+
devDependencies: {
|
|
563
|
+
"@types/react": "^18.3.0",
|
|
564
|
+
"@types/react-dom": "^18.3.0",
|
|
565
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
566
|
+
"autoprefixer": "^10.4.20",
|
|
567
|
+
"postcss": "^8.4.45",
|
|
568
|
+
"tailwindcss": "^3.4.10",
|
|
569
|
+
"typescript": "~5.5.0",
|
|
570
|
+
"vite": "^5.4.0"
|
|
571
|
+
}
|
|
572
|
+
}, null, 2)
|
|
573
|
+
});
|
|
574
|
+
}
|
|
393
575
|
return { plan, files };
|
|
394
576
|
}
|
|
395
577
|
|
|
396
578
|
// src/utils/fs.ts
|
|
397
579
|
import fs from "fs-extra";
|
|
398
580
|
import path from "path";
|
|
399
|
-
import
|
|
581
|
+
import chalk3 from "chalk";
|
|
400
582
|
|
|
401
583
|
// src/security/code-firewall.ts
|
|
402
|
-
import
|
|
584
|
+
import chalk2 from "chalk";
|
|
403
585
|
var MALICIOUS_PATTERNS = [
|
|
404
586
|
// ==================== HARDCODED SECRETS ====================
|
|
405
587
|
{
|
|
@@ -648,35 +830,35 @@ function shouldBlockCode(result) {
|
|
|
648
830
|
}
|
|
649
831
|
function displayScanResults(result, filename) {
|
|
650
832
|
if (result.safe) {
|
|
651
|
-
console.log(
|
|
833
|
+
console.log(chalk2.green("\u2705 No malicious patterns detected"));
|
|
652
834
|
return;
|
|
653
835
|
}
|
|
654
|
-
console.log(
|
|
836
|
+
console.log(chalk2.red.bold("\n\u{1F6A8} SECURITY SCAN FAILED"));
|
|
655
837
|
if (filename) {
|
|
656
|
-
console.log(
|
|
838
|
+
console.log(chalk2.gray(`File: ${filename}`));
|
|
657
839
|
}
|
|
658
840
|
console.log("");
|
|
659
841
|
const criticals = result.matches.filter((m) => m.severity === "critical");
|
|
660
842
|
const highs = result.matches.filter((m) => m.severity === "high");
|
|
661
843
|
const others = result.matches.filter((m) => m.severity !== "critical" && m.severity !== "high");
|
|
662
844
|
if (criticals.length > 0) {
|
|
663
|
-
console.log(
|
|
845
|
+
console.log(chalk2.red("\u{1F534} CRITICAL:"));
|
|
664
846
|
for (const m of criticals) {
|
|
665
|
-
console.log(
|
|
666
|
-
console.log(
|
|
847
|
+
console.log(chalk2.red(` Line ${m.line}: ${m.description}`));
|
|
848
|
+
console.log(chalk2.gray(` Found: ${m.match}`));
|
|
667
849
|
}
|
|
668
850
|
}
|
|
669
851
|
if (highs.length > 0) {
|
|
670
|
-
console.log(
|
|
852
|
+
console.log(chalk2.yellow("\n\u{1F7E0} HIGH:"));
|
|
671
853
|
for (const m of highs) {
|
|
672
|
-
console.log(
|
|
673
|
-
console.log(
|
|
854
|
+
console.log(chalk2.yellow(` Line ${m.line}: ${m.description}`));
|
|
855
|
+
console.log(chalk2.gray(` Found: ${m.match}`));
|
|
674
856
|
}
|
|
675
857
|
}
|
|
676
858
|
if (others.length > 0) {
|
|
677
|
-
console.log(
|
|
859
|
+
console.log(chalk2.cyan("\n\u{1F7E1} WARNINGS:"));
|
|
678
860
|
for (const m of others) {
|
|
679
|
-
console.log(
|
|
861
|
+
console.log(chalk2.cyan(` Line ${m.line}: ${m.description}`));
|
|
680
862
|
}
|
|
681
863
|
}
|
|
682
864
|
console.log("");
|
|
@@ -686,11 +868,11 @@ function validateCodeBeforeWrite(code, filename) {
|
|
|
686
868
|
if (!result.safe) {
|
|
687
869
|
displayScanResults(result, filename);
|
|
688
870
|
if (shouldBlockCode(result)) {
|
|
689
|
-
console.log(
|
|
690
|
-
console.log(
|
|
871
|
+
console.log(chalk2.red.bold("\u{1F6A8} BLOCKED: Code contains critical security issues"));
|
|
872
|
+
console.log(chalk2.gray("The file will NOT be written to disk.\n"));
|
|
691
873
|
return false;
|
|
692
874
|
} else {
|
|
693
|
-
console.log(
|
|
875
|
+
console.log(chalk2.yellow("\u26A0\uFE0F Warning: Code contains potential issues but will be written.\n"));
|
|
694
876
|
}
|
|
695
877
|
}
|
|
696
878
|
return true;
|
|
@@ -706,7 +888,7 @@ async function writeProject(project, outputDir) {
|
|
|
706
888
|
const isSafe = validateCodeBeforeWrite(file.content, file.path);
|
|
707
889
|
if (!isSafe) {
|
|
708
890
|
blockedCount++;
|
|
709
|
-
console.log(
|
|
891
|
+
console.log(chalk3.red(`\u26D4 BLOCKED: ${file.path}`));
|
|
710
892
|
continue;
|
|
711
893
|
}
|
|
712
894
|
await fs.ensureDir(path.dirname(filePath));
|
|
@@ -715,16 +897,16 @@ async function writeProject(project, outputDir) {
|
|
|
715
897
|
}
|
|
716
898
|
console.log("");
|
|
717
899
|
if (blockedCount > 0) {
|
|
718
|
-
console.log(
|
|
900
|
+
console.log(chalk3.yellow(`\u26A0\uFE0F ${blockedCount} file(s) blocked by security scan`));
|
|
719
901
|
}
|
|
720
|
-
console.log(
|
|
902
|
+
console.log(chalk3.green(`\u2705 ${writtenCount} file(s) written successfully`));
|
|
721
903
|
}
|
|
722
904
|
|
|
723
905
|
// src/utils/config.ts
|
|
724
906
|
import fs2 from "fs-extra";
|
|
725
907
|
import path2 from "path";
|
|
726
908
|
import os from "os";
|
|
727
|
-
import
|
|
909
|
+
import chalk4 from "chalk";
|
|
728
910
|
var CONFIG_DIR = path2.join(os.homedir(), ".agdi");
|
|
729
911
|
var CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
|
|
730
912
|
var SECURE_FILE_MODE = 384;
|
|
@@ -741,11 +923,11 @@ function checkPermissions() {
|
|
|
741
923
|
}
|
|
742
924
|
const isWorldReadable = (mode & 36) !== 0;
|
|
743
925
|
if (isWorldReadable) {
|
|
744
|
-
console.log(
|
|
745
|
-
console.log(
|
|
746
|
-
console.log(
|
|
747
|
-
console.log(
|
|
748
|
-
console.log(
|
|
926
|
+
console.log(chalk4.yellow("\n\u26A0\uFE0F SECURITY WARNING"));
|
|
927
|
+
console.log(chalk4.gray("Your config file is readable by other users!"));
|
|
928
|
+
console.log(chalk4.gray(`File: ${CONFIG_FILE}`));
|
|
929
|
+
console.log(chalk4.gray("Run the following to fix:"));
|
|
930
|
+
console.log(chalk4.cyan(` chmod 600 "${CONFIG_FILE}"
|
|
749
931
|
`));
|
|
750
932
|
return false;
|
|
751
933
|
}
|
|
@@ -781,16 +963,16 @@ function saveConfig(config) {
|
|
|
781
963
|
fs2.writeJsonSync(CONFIG_FILE, config, { spaces: 2 });
|
|
782
964
|
setSecurePermissions();
|
|
783
965
|
} catch (error) {
|
|
784
|
-
console.error(
|
|
966
|
+
console.error(chalk4.red("Failed to save config:"), error);
|
|
785
967
|
}
|
|
786
968
|
}
|
|
787
969
|
|
|
788
970
|
// src/commands/auth.ts
|
|
789
971
|
import { input, select, password } from "@inquirer/prompts";
|
|
790
|
-
import
|
|
972
|
+
import chalk5 from "chalk";
|
|
791
973
|
async function login() {
|
|
792
|
-
console.log(
|
|
793
|
-
console.log(
|
|
974
|
+
console.log(chalk5.cyan.bold("\n\u{1F510} Agdi Authentication\n"));
|
|
975
|
+
console.log(chalk5.gray("Configure your API key to use Agdi CLI.\n"));
|
|
794
976
|
try {
|
|
795
977
|
const config = loadConfig();
|
|
796
978
|
const provider = await select({
|
|
@@ -812,8 +994,8 @@ async function login() {
|
|
|
812
994
|
config.ollamaUrl = ollamaUrl;
|
|
813
995
|
config.defaultProvider = "ollama";
|
|
814
996
|
saveConfig(config);
|
|
815
|
-
console.log(
|
|
816
|
-
console.log(
|
|
997
|
+
console.log(chalk5.green("\n\u2705 Ollama configured"));
|
|
998
|
+
console.log(chalk5.gray(`Server: ${ollamaUrl}
|
|
817
999
|
`));
|
|
818
1000
|
return;
|
|
819
1001
|
}
|
|
@@ -840,12 +1022,12 @@ async function login() {
|
|
|
840
1022
|
}
|
|
841
1023
|
config.defaultProvider = provider;
|
|
842
1024
|
saveConfig(config);
|
|
843
|
-
console.log(
|
|
1025
|
+
console.log(chalk5.green(`
|
|
844
1026
|
\u2705 ${provider} API key saved securely`));
|
|
845
|
-
console.log(
|
|
1027
|
+
console.log(chalk5.gray("Keys stored in ~/.agdi/config.json\n"));
|
|
846
1028
|
} catch (error) {
|
|
847
1029
|
if (error.name === "ExitPromptError") {
|
|
848
|
-
console.log(
|
|
1030
|
+
console.log(chalk5.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
849
1031
|
process.exit(0);
|
|
850
1032
|
}
|
|
851
1033
|
throw error;
|
|
@@ -853,7 +1035,7 @@ async function login() {
|
|
|
853
1035
|
}
|
|
854
1036
|
async function showStatus() {
|
|
855
1037
|
const config = loadConfig();
|
|
856
|
-
console.log(
|
|
1038
|
+
console.log(chalk5.cyan.bold("\n\u{1F4CA} Authentication Status\n"));
|
|
857
1039
|
const providers = [
|
|
858
1040
|
{ name: "Gemini", key: config.geminiApiKey },
|
|
859
1041
|
{ name: "OpenRouter", key: config.openrouterApiKey },
|
|
@@ -862,19 +1044,19 @@ async function showStatus() {
|
|
|
862
1044
|
{ name: "DeepSeek", key: config.deepseekApiKey }
|
|
863
1045
|
];
|
|
864
1046
|
for (const p of providers) {
|
|
865
|
-
const status = p.key ?
|
|
1047
|
+
const status = p.key ? chalk5.green("\u2713 Configured") : chalk5.gray("\u2717 Not set");
|
|
866
1048
|
console.log(` ${p.name.padEnd(12)} ${status}`);
|
|
867
1049
|
}
|
|
868
|
-
console.log(
|
|
1050
|
+
console.log(chalk5.cyan(`
|
|
869
1051
|
Default: ${config.defaultProvider || "gemini"}
|
|
870
1052
|
`));
|
|
871
|
-
console.log(
|
|
1053
|
+
console.log(chalk5.gray('\u{1F4A1} Tip: Use "agdi auth" to reconfigure\n'));
|
|
872
1054
|
}
|
|
873
1055
|
|
|
874
1056
|
// src/commands/chat.ts
|
|
875
1057
|
import { input as input2 } from "@inquirer/prompts";
|
|
876
|
-
import
|
|
877
|
-
import
|
|
1058
|
+
import chalk6 from "chalk";
|
|
1059
|
+
import ora2 from "ora";
|
|
878
1060
|
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
1061
|
|
|
880
1062
|
# Core Expertise
|
|
@@ -1028,8 +1210,8 @@ When asked to build something:
|
|
|
1028
1210
|
|
|
1029
1211
|
You build software that works, scales, and follows industry best practices. Every solution is complete, tested, and ready for production deployment.`;
|
|
1030
1212
|
async function startChat() {
|
|
1031
|
-
console.log(
|
|
1032
|
-
console.log(
|
|
1213
|
+
console.log(chalk6.cyan.bold("\n\u{1F4AC} Agdi Interactive Mode\n"));
|
|
1214
|
+
console.log(chalk6.gray('Type your coding requests. Type "exit" to quit.\n'));
|
|
1033
1215
|
const config = loadConfig();
|
|
1034
1216
|
let provider;
|
|
1035
1217
|
let apiKey = "";
|
|
@@ -1039,33 +1221,33 @@ async function startChat() {
|
|
|
1039
1221
|
} else if (config.openrouterApiKey) {
|
|
1040
1222
|
provider = "openrouter";
|
|
1041
1223
|
apiKey = config.openrouterApiKey;
|
|
1042
|
-
console.log(
|
|
1224
|
+
console.log(chalk6.gray("Using OpenRouter (100+ models available)\n"));
|
|
1043
1225
|
} else if (config.defaultProvider === "puter") {
|
|
1044
|
-
console.log(
|
|
1045
|
-
console.log(
|
|
1046
|
-
console.log(
|
|
1047
|
-
console.log(
|
|
1226
|
+
console.log(chalk6.yellow("\u26A0\uFE0F Puter.com FREE mode requires browser authentication."));
|
|
1227
|
+
console.log(chalk6.gray("For CLI usage, please configure an API key:\n"));
|
|
1228
|
+
console.log(chalk6.cyan(" agdi auth"));
|
|
1229
|
+
console.log(chalk6.gray("\nSupported providers: Gemini, OpenRouter, OpenAI, Anthropic, DeepSeek\n"));
|
|
1048
1230
|
return;
|
|
1049
1231
|
} else {
|
|
1050
|
-
console.log(
|
|
1051
|
-
console.log(
|
|
1232
|
+
console.log(chalk6.yellow("\u26A0\uFE0F No API key configured."));
|
|
1233
|
+
console.log(chalk6.gray('Run "agdi auth" to configure your API key.\n'));
|
|
1052
1234
|
return;
|
|
1053
1235
|
}
|
|
1054
|
-
console.log(
|
|
1055
|
-
console.log(
|
|
1236
|
+
console.log(chalk6.gray(`Using provider: ${chalk6.cyan(provider)}`));
|
|
1237
|
+
console.log(chalk6.gray("\u2500".repeat(50) + "\n"));
|
|
1056
1238
|
const pm = new ProjectManager();
|
|
1057
1239
|
while (true) {
|
|
1058
1240
|
const userInput = await input2({
|
|
1059
|
-
message:
|
|
1241
|
+
message: chalk6.cyan("You:")
|
|
1060
1242
|
});
|
|
1061
1243
|
if (userInput.toLowerCase() === "exit" || userInput.toLowerCase() === "quit") {
|
|
1062
|
-
console.log(
|
|
1244
|
+
console.log(chalk6.gray("\n\u{1F44B} Goodbye!\n"));
|
|
1063
1245
|
break;
|
|
1064
1246
|
}
|
|
1065
1247
|
if (!userInput.trim()) {
|
|
1066
1248
|
continue;
|
|
1067
1249
|
}
|
|
1068
|
-
const spinner =
|
|
1250
|
+
const spinner = ora2("Thinking...").start();
|
|
1069
1251
|
try {
|
|
1070
1252
|
const llm = createLLMProvider(provider, { apiKey, model: config.defaultModel });
|
|
1071
1253
|
if (userInput.toLowerCase().includes("create") || userInput.toLowerCase().includes("build") || userInput.toLowerCase().includes("make")) {
|
|
@@ -1076,9 +1258,9 @@ async function startChat() {
|
|
|
1076
1258
|
});
|
|
1077
1259
|
pm.updateFiles(files);
|
|
1078
1260
|
spinner.succeed("Application generated!");
|
|
1079
|
-
console.log(
|
|
1261
|
+
console.log(chalk6.green("\n\u{1F4C1} Files created:"));
|
|
1080
1262
|
for (const file of files) {
|
|
1081
|
-
console.log(
|
|
1263
|
+
console.log(chalk6.gray(` - ${file.path}`));
|
|
1082
1264
|
}
|
|
1083
1265
|
const shouldWrite = await input2({
|
|
1084
1266
|
message: "Write files to disk? (y/n):",
|
|
@@ -1090,38 +1272,38 @@ async function startChat() {
|
|
|
1090
1272
|
default: "./generated-app"
|
|
1091
1273
|
});
|
|
1092
1274
|
await writeProject(pm.get(), dir);
|
|
1093
|
-
console.log(
|
|
1275
|
+
console.log(chalk6.green(`
|
|
1094
1276
|
\u2705 Files written to ${dir}
|
|
1095
1277
|
`));
|
|
1096
1278
|
}
|
|
1097
1279
|
} else {
|
|
1098
1280
|
const response = await llm.generate(userInput, SYSTEM_PROMPT2);
|
|
1099
1281
|
spinner.stop();
|
|
1100
|
-
console.log(
|
|
1282
|
+
console.log(chalk6.cyan("\nAgdi: ") + response.text + "\n");
|
|
1101
1283
|
}
|
|
1102
1284
|
} catch (error) {
|
|
1103
1285
|
if (error.name === "ExitPromptError") {
|
|
1104
|
-
console.log(
|
|
1286
|
+
console.log(chalk6.gray("\n\n\u{1F44B} Goodbye!\n"));
|
|
1105
1287
|
process.exit(0);
|
|
1106
1288
|
}
|
|
1107
1289
|
spinner.fail("Error");
|
|
1108
1290
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1109
1291
|
if (errorMessage.includes("429") || errorMessage.includes("quota") || errorMessage.includes("Resource exhausted") || errorMessage.includes("ResourceExhausted")) {
|
|
1110
|
-
console.log(
|
|
1111
|
-
console.log(
|
|
1112
|
-
console.log(
|
|
1292
|
+
console.log(chalk6.yellow("\n\u26A0\uFE0F API quota exceeded!"));
|
|
1293
|
+
console.log(chalk6.gray("Your API key has run out of credits."));
|
|
1294
|
+
console.log(chalk6.gray("Try: Use a different API key or wait for quota reset.\n"));
|
|
1113
1295
|
} else if (errorMessage.includes("401") || errorMessage.includes("Unauthorized") || errorMessage.includes("Invalid API key")) {
|
|
1114
|
-
console.log(
|
|
1115
|
-
console.log(
|
|
1116
|
-
console.log(
|
|
1296
|
+
console.log(chalk6.red("\n\u{1F511} Invalid API key"));
|
|
1297
|
+
console.log(chalk6.gray("Please reconfigure your API key:"));
|
|
1298
|
+
console.log(chalk6.cyan(" agdi auth\n"));
|
|
1117
1299
|
} else if (errorMessage.includes("403") || errorMessage.includes("Forbidden")) {
|
|
1118
|
-
console.log(
|
|
1119
|
-
console.log(
|
|
1300
|
+
console.log(chalk6.red("\n\u{1F6AB} Access denied"));
|
|
1301
|
+
console.log(chalk6.gray("Your API key doesn't have permission for this operation.\n"));
|
|
1120
1302
|
} else if (errorMessage.includes("network") || errorMessage.includes("fetch") || errorMessage.includes("ENOTFOUND")) {
|
|
1121
|
-
console.log(
|
|
1122
|
-
console.log(
|
|
1303
|
+
console.log(chalk6.red("\n\u{1F310} Network error"));
|
|
1304
|
+
console.log(chalk6.gray("Please check your internet connection.\n"));
|
|
1123
1305
|
} else {
|
|
1124
|
-
console.log(
|
|
1306
|
+
console.log(chalk6.red("\n" + errorMessage + "\n"));
|
|
1125
1307
|
}
|
|
1126
1308
|
}
|
|
1127
1309
|
}
|
|
@@ -1129,23 +1311,23 @@ async function startChat() {
|
|
|
1129
1311
|
|
|
1130
1312
|
// src/commands/run.ts
|
|
1131
1313
|
import { spawn } from "child_process";
|
|
1132
|
-
import
|
|
1314
|
+
import chalk7 from "chalk";
|
|
1133
1315
|
import fs3 from "fs-extra";
|
|
1134
1316
|
import path3 from "path";
|
|
1135
|
-
import
|
|
1317
|
+
import ora3 from "ora";
|
|
1136
1318
|
async function runProject(targetDir) {
|
|
1137
1319
|
const dir = targetDir || process.cwd();
|
|
1138
1320
|
const absoluteDir = path3.resolve(dir);
|
|
1139
|
-
console.log(
|
|
1321
|
+
console.log(chalk7.cyan.bold("\n\u{1F680} Agdi Run\n"));
|
|
1140
1322
|
if (!fs3.existsSync(absoluteDir)) {
|
|
1141
|
-
console.log(
|
|
1142
|
-
console.log(
|
|
1323
|
+
console.log(chalk7.red(`\u274C Directory not found: ${absoluteDir}`));
|
|
1324
|
+
console.log(chalk7.gray("Create a project first with: agdi init"));
|
|
1143
1325
|
return;
|
|
1144
1326
|
}
|
|
1145
1327
|
const packageJsonPath = path3.join(absoluteDir, "package.json");
|
|
1146
1328
|
if (!fs3.existsSync(packageJsonPath)) {
|
|
1147
|
-
console.log(
|
|
1148
|
-
console.log(
|
|
1329
|
+
console.log(chalk7.red(`\u274C No package.json found in: ${absoluteDir}`));
|
|
1330
|
+
console.log(chalk7.gray("This doesn't appear to be a Node.js project."));
|
|
1149
1331
|
return;
|
|
1150
1332
|
}
|
|
1151
1333
|
const packageJson = fs3.readJsonSync(packageJsonPath);
|
|
@@ -1154,14 +1336,14 @@ async function runProject(targetDir) {
|
|
|
1154
1336
|
if (!scripts.dev && scripts.start) {
|
|
1155
1337
|
runScript = "start";
|
|
1156
1338
|
} else if (!scripts.dev && !scripts.start) {
|
|
1157
|
-
console.log(
|
|
1158
|
-
console.log(
|
|
1339
|
+
console.log(chalk7.red('\u274C No "dev" or "start" script found in package.json'));
|
|
1340
|
+
console.log(chalk7.gray('Add a script like: "dev": "vite" or "start": "node index.js"'));
|
|
1159
1341
|
return;
|
|
1160
1342
|
}
|
|
1161
1343
|
const nodeModulesPath = path3.join(absoluteDir, "node_modules");
|
|
1162
1344
|
if (!fs3.existsSync(nodeModulesPath)) {
|
|
1163
|
-
console.log(
|
|
1164
|
-
const installSpinner =
|
|
1345
|
+
console.log(chalk7.yellow("\u{1F4E6} Installing dependencies...\n"));
|
|
1346
|
+
const installSpinner = ora3("Running npm install...").start();
|
|
1165
1347
|
await new Promise((resolve5, reject) => {
|
|
1166
1348
|
const install = spawn("npm", ["install"], {
|
|
1167
1349
|
cwd: absoluteDir,
|
|
@@ -1181,11 +1363,11 @@ async function runProject(targetDir) {
|
|
|
1181
1363
|
});
|
|
1182
1364
|
console.log("");
|
|
1183
1365
|
}
|
|
1184
|
-
console.log(
|
|
1185
|
-
console.log(
|
|
1366
|
+
console.log(chalk7.green(`\u25B6 Running: npm run ${runScript}`));
|
|
1367
|
+
console.log(chalk7.gray(` Directory: ${absoluteDir}
|
|
1186
1368
|
`));
|
|
1187
|
-
console.log(
|
|
1188
|
-
console.log(
|
|
1369
|
+
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
1370
|
+
console.log(chalk7.gray("Press Ctrl+C to stop\n"));
|
|
1189
1371
|
const child = spawn("npm", ["run", runScript], {
|
|
1190
1372
|
cwd: absoluteDir,
|
|
1191
1373
|
stdio: "inherit",
|
|
@@ -1193,12 +1375,12 @@ async function runProject(targetDir) {
|
|
|
1193
1375
|
});
|
|
1194
1376
|
process.on("SIGINT", () => {
|
|
1195
1377
|
child.kill("SIGINT");
|
|
1196
|
-
console.log(
|
|
1378
|
+
console.log(chalk7.gray("\n\n\u{1F44B} Server stopped."));
|
|
1197
1379
|
process.exit(0);
|
|
1198
1380
|
});
|
|
1199
1381
|
child.on("close", (code) => {
|
|
1200
1382
|
if (code !== 0) {
|
|
1201
|
-
console.log(
|
|
1383
|
+
console.log(chalk7.red(`
|
|
1202
1384
|
\u274C Process exited with code ${code}`));
|
|
1203
1385
|
}
|
|
1204
1386
|
process.exit(code || 0);
|
|
@@ -1207,7 +1389,7 @@ async function runProject(targetDir) {
|
|
|
1207
1389
|
|
|
1208
1390
|
// src/commands/onboarding.ts
|
|
1209
1391
|
import { select as select2, password as password2 } from "@inquirer/prompts";
|
|
1210
|
-
import
|
|
1392
|
+
import chalk8 from "chalk";
|
|
1211
1393
|
var PROVIDER_MODELS = {
|
|
1212
1394
|
gemini: [
|
|
1213
1395
|
{ name: "\u26A1 Gemini 2.5 Flash (Fast)", value: "gemini-2.5-flash" },
|
|
@@ -1284,10 +1466,10 @@ function getActiveProvider() {
|
|
|
1284
1466
|
return null;
|
|
1285
1467
|
}
|
|
1286
1468
|
async function runOnboarding() {
|
|
1287
|
-
console.log(
|
|
1288
|
-
console.log(
|
|
1469
|
+
console.log(chalk8.cyan.bold("\n\u{1F680} Welcome to Agdi!\n"));
|
|
1470
|
+
console.log(chalk8.gray("Let's set up your AI provider in 3 quick steps.\n"));
|
|
1289
1471
|
const config = loadConfig();
|
|
1290
|
-
console.log(
|
|
1472
|
+
console.log(chalk8.white.bold("Step 1/3: Select your AI provider\n"));
|
|
1291
1473
|
const provider = await select2({
|
|
1292
1474
|
message: "Which AI provider would you like to use?",
|
|
1293
1475
|
choices: [
|
|
@@ -1298,7 +1480,7 @@ async function runOnboarding() {
|
|
|
1298
1480
|
{ name: "\u{1F30A} DeepSeek", value: "deepseek" }
|
|
1299
1481
|
]
|
|
1300
1482
|
});
|
|
1301
|
-
console.log(
|
|
1483
|
+
console.log(chalk8.white.bold("\nStep 2/3: Enter your API key\n"));
|
|
1302
1484
|
const keyUrls = {
|
|
1303
1485
|
gemini: "https://aistudio.google.com/apikey",
|
|
1304
1486
|
openrouter: "https://openrouter.ai/keys",
|
|
@@ -1306,7 +1488,7 @@ async function runOnboarding() {
|
|
|
1306
1488
|
anthropic: "https://console.anthropic.com/",
|
|
1307
1489
|
deepseek: "https://platform.deepseek.com/"
|
|
1308
1490
|
};
|
|
1309
|
-
console.log(
|
|
1491
|
+
console.log(chalk8.gray(`Get your key at: ${chalk8.cyan(keyUrls[provider])}
|
|
1310
1492
|
`));
|
|
1311
1493
|
const apiKey = await password2({
|
|
1312
1494
|
message: `Enter your ${provider} API key:`,
|
|
@@ -1330,7 +1512,7 @@ async function runOnboarding() {
|
|
|
1330
1512
|
break;
|
|
1331
1513
|
}
|
|
1332
1514
|
config.defaultProvider = provider;
|
|
1333
|
-
console.log(
|
|
1515
|
+
console.log(chalk8.white.bold("\nStep 3/3: Choose your default model\n"));
|
|
1334
1516
|
const models = PROVIDER_MODELS[provider] || PROVIDER_MODELS.gemini;
|
|
1335
1517
|
const model = await select2({
|
|
1336
1518
|
message: "Select your default model:",
|
|
@@ -1338,18 +1520,18 @@ async function runOnboarding() {
|
|
|
1338
1520
|
});
|
|
1339
1521
|
config.defaultModel = model;
|
|
1340
1522
|
saveConfig(config);
|
|
1341
|
-
console.log(
|
|
1342
|
-
console.log(
|
|
1343
|
-
console.log(
|
|
1523
|
+
console.log(chalk8.green("\n\u2705 Setup complete!"));
|
|
1524
|
+
console.log(chalk8.gray(`Provider: ${chalk8.cyan(provider)}`));
|
|
1525
|
+
console.log(chalk8.gray(`Model: ${chalk8.cyan(model)}
|
|
1344
1526
|
`));
|
|
1345
1527
|
return { provider, apiKey, model };
|
|
1346
1528
|
}
|
|
1347
1529
|
async function selectModel() {
|
|
1348
1530
|
const config = loadConfig();
|
|
1349
1531
|
const provider = config.defaultProvider || "gemini";
|
|
1350
|
-
console.log(
|
|
1351
|
-
console.log(
|
|
1352
|
-
console.log(
|
|
1532
|
+
console.log(chalk8.cyan.bold("\n\u{1F504} Change Model\n"));
|
|
1533
|
+
console.log(chalk8.gray(`Current provider: ${chalk8.cyan(provider)}`));
|
|
1534
|
+
console.log(chalk8.gray(`Current model: ${chalk8.cyan(config.defaultModel || "not set")}
|
|
1353
1535
|
`));
|
|
1354
1536
|
const changeProvider = await select2({
|
|
1355
1537
|
message: "What would you like to do?",
|
|
@@ -1360,7 +1542,7 @@ async function selectModel() {
|
|
|
1360
1542
|
]
|
|
1361
1543
|
});
|
|
1362
1544
|
if (changeProvider === "cancel") {
|
|
1363
|
-
console.log(
|
|
1545
|
+
console.log(chalk8.gray("\nCancelled.\n"));
|
|
1364
1546
|
return;
|
|
1365
1547
|
}
|
|
1366
1548
|
let selectedProvider = provider;
|
|
@@ -1383,7 +1565,7 @@ async function selectModel() {
|
|
|
1383
1565
|
deepseek: config.deepseekApiKey
|
|
1384
1566
|
};
|
|
1385
1567
|
if (!keyMap[selectedProvider]) {
|
|
1386
|
-
console.log(
|
|
1568
|
+
console.log(chalk8.yellow(`
|
|
1387
1569
|
\u26A0\uFE0F No API key configured for ${selectedProvider}
|
|
1388
1570
|
`));
|
|
1389
1571
|
const apiKey = await password2({
|
|
@@ -1417,9 +1599,9 @@ async function selectModel() {
|
|
|
1417
1599
|
});
|
|
1418
1600
|
config.defaultModel = model;
|
|
1419
1601
|
saveConfig(config);
|
|
1420
|
-
console.log(
|
|
1421
|
-
console.log(
|
|
1422
|
-
console.log(
|
|
1602
|
+
console.log(chalk8.green("\n\u2705 Model changed!"));
|
|
1603
|
+
console.log(chalk8.gray(`Provider: ${chalk8.cyan(selectedProvider)}`));
|
|
1604
|
+
console.log(chalk8.gray(`Model: ${chalk8.cyan(model)}
|
|
1423
1605
|
`));
|
|
1424
1606
|
}
|
|
1425
1607
|
|
|
@@ -1427,165 +1609,6 @@ async function selectModel() {
|
|
|
1427
1609
|
import { input as input5, confirm as confirm3 } from "@inquirer/prompts";
|
|
1428
1610
|
import chalk13 from "chalk";
|
|
1429
1611
|
|
|
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
1612
|
// src/actions/plan-executor.ts
|
|
1590
1613
|
import { select as select4, confirm } from "@inquirer/prompts";
|
|
1591
1614
|
import chalk11 from "chalk";
|
|
@@ -3165,10 +3188,37 @@ Target: ${env.workspaceRoot}`
|
|
|
3165
3188
|
console.log(chalk11.green("\u2713 Trusted for this session\n"));
|
|
3166
3189
|
}
|
|
3167
3190
|
}
|
|
3168
|
-
const
|
|
3169
|
-
|
|
3170
|
-
|
|
3191
|
+
const dangerousKeywords = ["rm", "rimraf", "del", "npm install", "pnpm install", "yarn install", "chmod", "chown"];
|
|
3192
|
+
const dangerousActions = plan.actions.filter((a) => {
|
|
3193
|
+
if (a.type !== "exec") return false;
|
|
3194
|
+
const cmd = a.argv.join(" ");
|
|
3195
|
+
return dangerousKeywords.some((k) => cmd.includes(k));
|
|
3171
3196
|
});
|
|
3197
|
+
if (dangerousActions.length > 0) {
|
|
3198
|
+
ui.renderAlert(
|
|
3199
|
+
"\u26A0\uFE0F DANGEROUS COMMANDS DETECTED",
|
|
3200
|
+
dangerousActions.map((a) => `\u2022 ${a.argv.join(" ")}`).join("\n") + "\n\nThese commands can modify your system or delete files."
|
|
3201
|
+
);
|
|
3202
|
+
const allowDangerous = await confirm({
|
|
3203
|
+
message: "Are you SURE you want to execute these dangerous commands?",
|
|
3204
|
+
default: false
|
|
3205
|
+
});
|
|
3206
|
+
if (!allowDangerous) {
|
|
3207
|
+
console.log(chalk11.yellow("\n\u{1F44B} Execution cancelled for safety.\n"));
|
|
3208
|
+
return { success: false, results, filesCreated, commandsRun, errors: ["User cancelled dangerous commands"] };
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
let approved = false;
|
|
3212
|
+
if (ui.flags.dryRun) {
|
|
3213
|
+
console.log(chalk11.yellow("\n\u{1F6A7} DRY RUN MODE ENABLED"));
|
|
3214
|
+
console.log(chalk11.gray(" No files will be written and no commands will be executed.\n"));
|
|
3215
|
+
approved = true;
|
|
3216
|
+
} else {
|
|
3217
|
+
approved = await confirm({
|
|
3218
|
+
message: `Execute ${plan.actions.length} actions?`,
|
|
3219
|
+
default: true
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3172
3222
|
if (!approved) {
|
|
3173
3223
|
console.log(chalk11.yellow("\n\u{1F44B} Plan cancelled.\n"));
|
|
3174
3224
|
return { success: false, results, filesCreated, commandsRun, errors: ["User cancelled"] };
|
|
@@ -3185,6 +3235,11 @@ Target: ${env.workspaceRoot}`
|
|
|
3185
3235
|
for (let i = 0; i < plan.actions.length; i++) {
|
|
3186
3236
|
const action = plan.actions[i];
|
|
3187
3237
|
displayActionProgress(action, i, plan.actions.length);
|
|
3238
|
+
if (ui.flags.dryRun) {
|
|
3239
|
+
console.log(chalk11.yellow(` [DRY RUN] Would execute: ${action.type} ${action.path || action.argv?.join(" ")}`));
|
|
3240
|
+
results.push({ action, success: true, output: "(dry run)" });
|
|
3241
|
+
continue;
|
|
3242
|
+
}
|
|
3188
3243
|
const result = await executeAction(action);
|
|
3189
3244
|
results.push(result);
|
|
3190
3245
|
if (result.success) {
|
|
@@ -4040,8 +4095,8 @@ registerTool({
|
|
|
4040
4095
|
{ name: "path", type: "string", description: "Directory path", required: false, default: "." }
|
|
4041
4096
|
],
|
|
4042
4097
|
execute: async (params) => {
|
|
4043
|
-
const { readdirSync: readdirSync2, statSync:
|
|
4044
|
-
const { resolve: resolve5, join:
|
|
4098
|
+
const { readdirSync: readdirSync2, statSync: statSync3 } = await import("fs");
|
|
4099
|
+
const { resolve: resolve5, join: join9 } = await import("path");
|
|
4045
4100
|
try {
|
|
4046
4101
|
const dirPath = resolve5(process.cwd(), params.path || ".");
|
|
4047
4102
|
const entries = readdirSync2(dirPath);
|
|
@@ -4049,7 +4104,7 @@ registerTool({
|
|
|
4049
4104
|
const dirs = [];
|
|
4050
4105
|
for (const entry of entries) {
|
|
4051
4106
|
try {
|
|
4052
|
-
const stat =
|
|
4107
|
+
const stat = statSync3(join9(dirPath, entry));
|
|
4053
4108
|
if (stat.isDirectory()) {
|
|
4054
4109
|
dirs.push(entry + "/");
|
|
4055
4110
|
} else {
|
|
@@ -4649,25 +4704,183 @@ function handleError(error) {
|
|
|
4649
4704
|
}
|
|
4650
4705
|
}
|
|
4651
4706
|
|
|
4707
|
+
// src/commands/doctor.ts
|
|
4708
|
+
import chalk14 from "chalk";
|
|
4709
|
+
import { existsSync as existsSync11, statSync as statSync2 } from "fs";
|
|
4710
|
+
import { homedir as homedir7, platform as platform2 } from "os";
|
|
4711
|
+
import { join as join8 } from "path";
|
|
4712
|
+
async function runDoctor() {
|
|
4713
|
+
console.log("");
|
|
4714
|
+
ui.renderBox("AGDI DOCTOR", "Running diagnostics...", "info");
|
|
4715
|
+
console.log("");
|
|
4716
|
+
const results = [];
|
|
4717
|
+
const nodeVersion = process.version;
|
|
4718
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0], 10);
|
|
4719
|
+
if (majorVersion >= 18) {
|
|
4720
|
+
results.push({
|
|
4721
|
+
name: "Node.js Version",
|
|
4722
|
+
status: "pass",
|
|
4723
|
+
message: `${nodeVersion} (\u226518 required)`
|
|
4724
|
+
});
|
|
4725
|
+
} else {
|
|
4726
|
+
results.push({
|
|
4727
|
+
name: "Node.js Version",
|
|
4728
|
+
status: "fail",
|
|
4729
|
+
message: `${nodeVersion} - Node.js 18+ required!`
|
|
4730
|
+
});
|
|
4731
|
+
}
|
|
4732
|
+
const configDir = join8(homedir7(), ".agdi");
|
|
4733
|
+
const configPath = join8(configDir, "config.json");
|
|
4734
|
+
if (existsSync11(configPath)) {
|
|
4735
|
+
if (platform2() !== "win32") {
|
|
4736
|
+
try {
|
|
4737
|
+
const stats = statSync2(configPath);
|
|
4738
|
+
const mode = (stats.mode & parseInt("777", 8)).toString(8);
|
|
4739
|
+
if (mode === "600") {
|
|
4740
|
+
results.push({
|
|
4741
|
+
name: "Config Permissions",
|
|
4742
|
+
status: "pass",
|
|
4743
|
+
message: `${configPath} (mode: 600)`
|
|
4744
|
+
});
|
|
4745
|
+
} else {
|
|
4746
|
+
results.push({
|
|
4747
|
+
name: "Config Permissions",
|
|
4748
|
+
status: "warn",
|
|
4749
|
+
message: `${configPath} (mode: ${mode}) - Should be 600!`
|
|
4750
|
+
});
|
|
4751
|
+
}
|
|
4752
|
+
} catch {
|
|
4753
|
+
results.push({
|
|
4754
|
+
name: "Config Permissions",
|
|
4755
|
+
status: "warn",
|
|
4756
|
+
message: "Could not check permissions"
|
|
4757
|
+
});
|
|
4758
|
+
}
|
|
4759
|
+
} else {
|
|
4760
|
+
results.push({
|
|
4761
|
+
name: "Config File",
|
|
4762
|
+
status: "pass",
|
|
4763
|
+
message: `${configPath} exists`
|
|
4764
|
+
});
|
|
4765
|
+
}
|
|
4766
|
+
} else {
|
|
4767
|
+
results.push({
|
|
4768
|
+
name: "Config File",
|
|
4769
|
+
status: "warn",
|
|
4770
|
+
message: "No config found. Run: agdi auth"
|
|
4771
|
+
});
|
|
4772
|
+
}
|
|
4773
|
+
const activeConfig = getActiveProvider();
|
|
4774
|
+
if (activeConfig) {
|
|
4775
|
+
const keyPreview = activeConfig.apiKey.slice(0, 8) + "..." + activeConfig.apiKey.slice(-4);
|
|
4776
|
+
results.push({
|
|
4777
|
+
name: "API Key",
|
|
4778
|
+
status: "pass",
|
|
4779
|
+
message: `${activeConfig.provider} (${keyPreview})`
|
|
4780
|
+
});
|
|
4781
|
+
results.push({
|
|
4782
|
+
name: "Active Model",
|
|
4783
|
+
status: "pass",
|
|
4784
|
+
message: activeConfig.model
|
|
4785
|
+
});
|
|
4786
|
+
} else {
|
|
4787
|
+
results.push({
|
|
4788
|
+
name: "API Key",
|
|
4789
|
+
status: "fail",
|
|
4790
|
+
message: "No API key configured. Run: agdi auth"
|
|
4791
|
+
});
|
|
4792
|
+
}
|
|
4793
|
+
const auditPath = join8(configDir, "audit.jsonl");
|
|
4794
|
+
if (existsSync11(auditPath)) {
|
|
4795
|
+
results.push({
|
|
4796
|
+
name: "Audit Log",
|
|
4797
|
+
status: "pass",
|
|
4798
|
+
message: `Logging to ${auditPath}`
|
|
4799
|
+
});
|
|
4800
|
+
} else {
|
|
4801
|
+
results.push({
|
|
4802
|
+
name: "Audit Log",
|
|
4803
|
+
status: "warn",
|
|
4804
|
+
message: "No audit log found (will be created on first action)"
|
|
4805
|
+
});
|
|
4806
|
+
}
|
|
4807
|
+
const trustPath = join8(configDir, "trusted-workspaces.json");
|
|
4808
|
+
if (existsSync11(trustPath)) {
|
|
4809
|
+
results.push({
|
|
4810
|
+
name: "Trust Store",
|
|
4811
|
+
status: "pass",
|
|
4812
|
+
message: "Workspace trust store found"
|
|
4813
|
+
});
|
|
4814
|
+
} else {
|
|
4815
|
+
results.push({
|
|
4816
|
+
name: "Trust Store",
|
|
4817
|
+
status: "warn",
|
|
4818
|
+
message: "No trusted workspaces yet"
|
|
4819
|
+
});
|
|
4820
|
+
}
|
|
4821
|
+
console.log(chalk14.bold("Diagnostic Results:\n"));
|
|
4822
|
+
for (const result of results) {
|
|
4823
|
+
let icon;
|
|
4824
|
+
let color;
|
|
4825
|
+
switch (result.status) {
|
|
4826
|
+
case "pass":
|
|
4827
|
+
icon = "\u2705";
|
|
4828
|
+
color = chalk14.green;
|
|
4829
|
+
break;
|
|
4830
|
+
case "warn":
|
|
4831
|
+
icon = "\u26A0\uFE0F";
|
|
4832
|
+
color = chalk14.yellow;
|
|
4833
|
+
break;
|
|
4834
|
+
case "fail":
|
|
4835
|
+
icon = "\u274C";
|
|
4836
|
+
color = chalk14.red;
|
|
4837
|
+
break;
|
|
4838
|
+
}
|
|
4839
|
+
console.log(` ${icon} ${chalk14.bold(result.name)}`);
|
|
4840
|
+
console.log(` ${color(result.message)}
|
|
4841
|
+
`);
|
|
4842
|
+
}
|
|
4843
|
+
const passed = results.filter((r) => r.status === "pass").length;
|
|
4844
|
+
const warnings = results.filter((r) => r.status === "warn").length;
|
|
4845
|
+
const failed = results.filter((r) => r.status === "fail").length;
|
|
4846
|
+
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4847
|
+
console.log(`
|
|
4848
|
+
${chalk14.bold("Summary:")} ${chalk14.green(passed + " passed")}, ${chalk14.yellow(warnings + " warnings")}, ${chalk14.red(failed + " failed")}
|
|
4849
|
+
`);
|
|
4850
|
+
if (failed > 0) {
|
|
4851
|
+
console.log(chalk14.red(" \u26A0\uFE0F There are critical issues that need attention.\n"));
|
|
4852
|
+
} else if (warnings > 0) {
|
|
4853
|
+
console.log(chalk14.yellow(" \u2139\uFE0F There are some warnings, but Agdi should work.\n"));
|
|
4854
|
+
} else {
|
|
4855
|
+
console.log(chalk14.green(" \u{1F389} All checks passed! Agdi is ready to use.\n"));
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
|
|
4652
4859
|
// src/index.ts
|
|
4653
4860
|
var BANNER = `
|
|
4654
|
-
${
|
|
4655
|
-
${
|
|
4656
|
-
${
|
|
4657
|
-
${
|
|
4658
|
-
${
|
|
4659
|
-
${
|
|
4861
|
+
${chalk15.cyan(` ___ __ _ `)}
|
|
4862
|
+
${chalk15.cyan(` / | ____ _____/ /(_) `)}
|
|
4863
|
+
${chalk15.cyan(` / /| | / __ \`/ __ // / `)}
|
|
4864
|
+
${chalk15.cyan(` / ___ |/ /_/ / /_/ // / `)}
|
|
4865
|
+
${chalk15.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
|
|
4866
|
+
${chalk15.cyan(` /____/ `)}
|
|
4660
4867
|
`;
|
|
4661
4868
|
var program = new Command();
|
|
4662
|
-
program.name("agdi").description(
|
|
4869
|
+
program.name("agdi").description(chalk15.cyan("\u{1F680} AI-powered coding assistant")).version("2.8.0").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files");
|
|
4663
4870
|
program.hook("preAction", (thisCommand) => {
|
|
4664
4871
|
const opts = thisCommand.opts();
|
|
4665
4872
|
if (opts.yes) {
|
|
4666
4873
|
ui.setFlags({ yes: true, headless: true });
|
|
4667
4874
|
}
|
|
4875
|
+
if (opts.minimal) {
|
|
4876
|
+
ui.setFlags({ minimal: true });
|
|
4877
|
+
}
|
|
4878
|
+
if (opts.dryRun) {
|
|
4879
|
+
ui.setFlags({ dryRun: true });
|
|
4880
|
+
}
|
|
4668
4881
|
});
|
|
4669
4882
|
program.addHelpText("beforeAll", () => {
|
|
4670
|
-
return BANNER + "\n" +
|
|
4883
|
+
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
4884
|
});
|
|
4672
4885
|
program.action(async () => {
|
|
4673
4886
|
try {
|
|
@@ -4678,7 +4891,7 @@ program.action(async () => {
|
|
|
4678
4891
|
await startCodingMode();
|
|
4679
4892
|
} catch (error) {
|
|
4680
4893
|
if (error.name === "ExitPromptError") {
|
|
4681
|
-
console.log(
|
|
4894
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Goodbye!\n"));
|
|
4682
4895
|
ui.safeExit(0);
|
|
4683
4896
|
}
|
|
4684
4897
|
throw error;
|
|
@@ -4693,7 +4906,7 @@ program.command("auth").description("Configure API keys").option("--status", "Sh
|
|
|
4693
4906
|
}
|
|
4694
4907
|
} catch (error) {
|
|
4695
4908
|
if (error.name === "ExitPromptError") {
|
|
4696
|
-
console.log(
|
|
4909
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
4697
4910
|
ui.safeExit(0);
|
|
4698
4911
|
}
|
|
4699
4912
|
throw error;
|
|
@@ -4704,7 +4917,7 @@ program.command("model").alias("models").description("Change AI model").action(a
|
|
|
4704
4917
|
await selectModel();
|
|
4705
4918
|
} catch (error) {
|
|
4706
4919
|
if (error.name === "ExitPromptError") {
|
|
4707
|
-
console.log(
|
|
4920
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
4708
4921
|
ui.safeExit(0);
|
|
4709
4922
|
}
|
|
4710
4923
|
throw error;
|
|
@@ -4718,7 +4931,7 @@ program.command("chat").description("Start a chat session").action(async () => {
|
|
|
4718
4931
|
await startChat();
|
|
4719
4932
|
} catch (error) {
|
|
4720
4933
|
if (error.name === "ExitPromptError") {
|
|
4721
|
-
console.log(
|
|
4934
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Goodbye!\n"));
|
|
4722
4935
|
ui.safeExit(0);
|
|
4723
4936
|
}
|
|
4724
4937
|
throw error;
|
|
@@ -4729,20 +4942,20 @@ program.command("run [directory]").description("Run a generated project").action
|
|
|
4729
4942
|
await runProject(directory);
|
|
4730
4943
|
} catch (error) {
|
|
4731
4944
|
if (error.name === "ExitPromptError") {
|
|
4732
|
-
console.log(
|
|
4945
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
4733
4946
|
ui.safeExit(0);
|
|
4734
4947
|
}
|
|
4735
4948
|
throw error;
|
|
4736
4949
|
}
|
|
4737
4950
|
});
|
|
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) => {
|
|
4951
|
+
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
4952
|
try {
|
|
4740
4953
|
if (needsOnboarding()) {
|
|
4741
4954
|
await runOnboarding();
|
|
4742
4955
|
}
|
|
4743
4956
|
const activeConfig = getActiveProvider();
|
|
4744
4957
|
if (!activeConfig) {
|
|
4745
|
-
console.log(
|
|
4958
|
+
console.log(chalk15.red("\u274C No API key configured. Run: agdi auth"));
|
|
4746
4959
|
return;
|
|
4747
4960
|
}
|
|
4748
4961
|
const spinner = ora5("Generating application...").start();
|
|
@@ -4754,30 +4967,42 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
|
|
|
4754
4967
|
const pm = new ProjectManager();
|
|
4755
4968
|
pm.create(options.output.replace("./", ""), prompt);
|
|
4756
4969
|
const { plan, files } = await generateApp(prompt, llm, (step, file) => {
|
|
4757
|
-
spinner.text = file ? `${step} ${
|
|
4970
|
+
spinner.text = file ? `${step} ${chalk15.gray(file)}` : step;
|
|
4758
4971
|
});
|
|
4759
4972
|
pm.updateFiles(files);
|
|
4760
4973
|
pm.updateDependencies(plan.dependencies);
|
|
4974
|
+
if (options.dryRun || ui.flags.dryRun) {
|
|
4975
|
+
spinner.stop();
|
|
4976
|
+
console.log(chalk15.cyan.bold("\n\u{1F6A7} DRY RUN SUMMARY\n"));
|
|
4977
|
+
console.log(chalk15.gray(`Project: ${plan.name}
|
|
4978
|
+
`));
|
|
4979
|
+
console.log(chalk15.cyan("Files to be created:"));
|
|
4980
|
+
files.forEach((f) => console.log(chalk15.gray(` \u{1F4C4} ${f.path}`)));
|
|
4981
|
+
console.log(chalk15.cyan("\nDependencies:"));
|
|
4982
|
+
console.log(chalk15.gray(` \u{1F4E6} ${plan.dependencies.join(", ")}`));
|
|
4983
|
+
console.log(chalk15.green("\n\u2713 Dry run complete. No files written.\n"));
|
|
4984
|
+
return;
|
|
4985
|
+
}
|
|
4761
4986
|
await writeProject(pm.get(), options.output);
|
|
4762
|
-
spinner.succeed(
|
|
4763
|
-
console.log(
|
|
4764
|
-
\u{1F4C1} Created ${files.length} files in ${
|
|
4765
|
-
console.log(
|
|
4987
|
+
spinner.succeed(chalk15.green("App generated!"));
|
|
4988
|
+
console.log(chalk15.gray(`
|
|
4989
|
+
\u{1F4C1} Created ${files.length} files in ${chalk15.cyan(options.output)}`));
|
|
4990
|
+
console.log(chalk15.gray("\nNext: cd " + options.output + " && npm install && npm run dev\n"));
|
|
4766
4991
|
} catch (error) {
|
|
4767
4992
|
spinner.fail("Generation failed");
|
|
4768
4993
|
const msg = error instanceof Error ? error.message : String(error);
|
|
4769
4994
|
if (msg.includes("429") || msg.includes("quota")) {
|
|
4770
|
-
console.log(
|
|
4995
|
+
console.log(chalk15.yellow("\n\u26A0\uFE0F Quota exceeded. Run: agdi model\n"));
|
|
4771
4996
|
} else if (msg.includes("401") || msg.includes("403")) {
|
|
4772
|
-
console.log(
|
|
4997
|
+
console.log(chalk15.red("\n\u{1F511} Invalid API key. Run: agdi auth\n"));
|
|
4773
4998
|
} else {
|
|
4774
|
-
console.error(
|
|
4999
|
+
console.error(chalk15.red("\n" + msg + "\n"));
|
|
4775
5000
|
}
|
|
4776
5001
|
ui.safeExit(1);
|
|
4777
5002
|
}
|
|
4778
5003
|
} catch (error) {
|
|
4779
5004
|
if (error.name === "ExitPromptError") {
|
|
4780
|
-
console.log(
|
|
5005
|
+
console.log(chalk15.gray("\n\n\u{1F44B} Cancelled.\n"));
|
|
4781
5006
|
ui.safeExit(0);
|
|
4782
5007
|
}
|
|
4783
5008
|
throw error;
|
|
@@ -4786,11 +5011,11 @@ program.command("build <prompt>").alias("b").description("Generate an app from a
|
|
|
4786
5011
|
program.command("config").description("Show configuration").action(async () => {
|
|
4787
5012
|
const config = loadConfig();
|
|
4788
5013
|
const active = getActiveProvider();
|
|
4789
|
-
console.log(
|
|
4790
|
-
console.log(
|
|
4791
|
-
console.log(
|
|
4792
|
-
console.log(
|
|
4793
|
-
console.log(
|
|
5014
|
+
console.log(chalk15.cyan.bold("\n\u2699\uFE0F Configuration\n"));
|
|
5015
|
+
console.log(chalk15.gray(" Provider: ") + chalk15.cyan(config.defaultProvider || "not set"));
|
|
5016
|
+
console.log(chalk15.gray(" Model: ") + chalk15.cyan(config.defaultModel || "not set"));
|
|
5017
|
+
console.log(chalk15.gray(" Config: ") + chalk15.gray("~/.agdi/config.json"));
|
|
5018
|
+
console.log(chalk15.cyan.bold("\n\u{1F510} API Keys\n"));
|
|
4794
5019
|
const keys = [
|
|
4795
5020
|
["Gemini", config.geminiApiKey],
|
|
4796
5021
|
["OpenRouter", config.openrouterApiKey],
|
|
@@ -4799,9 +5024,18 @@ program.command("config").description("Show configuration").action(async () => {
|
|
|
4799
5024
|
["DeepSeek", config.deepseekApiKey]
|
|
4800
5025
|
];
|
|
4801
5026
|
for (const [name, key] of keys) {
|
|
4802
|
-
const status = key ?
|
|
5027
|
+
const status = key ? chalk15.green("\u2713") : chalk15.gray("\u2717");
|
|
4803
5028
|
console.log(` ${status} ${name}`);
|
|
4804
5029
|
}
|
|
4805
5030
|
console.log("");
|
|
5031
|
+
console.log("");
|
|
5032
|
+
});
|
|
5033
|
+
program.command("doctor").alias("doc").description("Run self-diagnosis checks").action(async () => {
|
|
5034
|
+
try {
|
|
5035
|
+
await runDoctor();
|
|
5036
|
+
} catch (error) {
|
|
5037
|
+
console.error(chalk15.red("Diagnostic failed: " + error));
|
|
5038
|
+
ui.safeExit(1);
|
|
5039
|
+
}
|
|
4806
5040
|
});
|
|
4807
5041
|
program.parse();
|