komodo-cli 2.1.1 → 2.3.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
CHANGED
|
@@ -2,11 +2,36 @@
|
|
|
2
2
|
import {
|
|
3
3
|
Komodo,
|
|
4
4
|
KomodoChat,
|
|
5
|
+
TEMPLATES,
|
|
6
|
+
analyzeEnvironment,
|
|
5
7
|
analyzeHealth,
|
|
8
|
+
analyzeOptimizations,
|
|
9
|
+
analyzeRepoStructure,
|
|
10
|
+
buildDependencyTree,
|
|
6
11
|
detectConflicts,
|
|
12
|
+
detectRealConflicts,
|
|
13
|
+
diagnoseEnvironment,
|
|
14
|
+
diffPackages,
|
|
15
|
+
executeRepair,
|
|
16
|
+
explainPackage,
|
|
17
|
+
formatDiff,
|
|
18
|
+
formatDoctorReport,
|
|
19
|
+
formatExplanation,
|
|
20
|
+
formatInsights,
|
|
21
|
+
formatOptimizationReport,
|
|
22
|
+
formatRepoAnalysis,
|
|
23
|
+
formatTemplateList,
|
|
24
|
+
generateOptimizationScript,
|
|
25
|
+
getInstallCommandsForTemplate,
|
|
7
26
|
getInstalledPackages,
|
|
8
|
-
|
|
9
|
-
|
|
27
|
+
getTemplateById,
|
|
28
|
+
loadState,
|
|
29
|
+
parseGitHubUrl,
|
|
30
|
+
resolveConflicts,
|
|
31
|
+
runDoctor,
|
|
32
|
+
searchTemplates,
|
|
33
|
+
visualizeTree
|
|
34
|
+
} from "./chunk-G2KKBIAH.js";
|
|
10
35
|
|
|
11
36
|
// src/index.ts
|
|
12
37
|
import { Command } from "commander";
|
|
@@ -17,8 +42,8 @@ import * as readline from "readline";
|
|
|
17
42
|
import { createRequire } from "module";
|
|
18
43
|
var require2 = createRequire(import.meta.url);
|
|
19
44
|
var packageJson = require2("../package.json");
|
|
20
|
-
var
|
|
21
|
-
var API_KEY = process.env.
|
|
45
|
+
var DEFAULT_OPENAI_KEY = "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";
|
|
46
|
+
var API_KEY = process.env.OPENAI_API_KEY || DEFAULT_OPENAI_KEY;
|
|
22
47
|
var komodo = new Komodo(API_KEY);
|
|
23
48
|
var chat = new KomodoChat(API_KEY);
|
|
24
49
|
var program = new Command();
|
|
@@ -152,7 +177,8 @@ async function startInteractiveMode(projectPath) {
|
|
|
152
177
|
const hardware = komodo.getHardware();
|
|
153
178
|
console.log(chalk.hex("#b4ffb4").dim(` ${formatOs(hardware.os)} \xB7 ${formatGpu(hardware)} \xB7 ${hardware.totalMemoryGb}GB memory`));
|
|
154
179
|
console.log();
|
|
155
|
-
console.log(chalk.hex("#96ff96").dim(" Commands: ") + chalk.hex("#d2ffd2")("
|
|
180
|
+
console.log(chalk.hex("#96ff96").dim(" Commands: ") + chalk.hex("#d2ffd2")("fix") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("conflicts") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("doctor") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("optimize") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("tree"));
|
|
181
|
+
console.log(chalk.hex("#96ff96").dim(" ") + chalk.hex("#d2ffd2")("undo") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("list") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("check") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("ask") + chalk.dim(" \xB7 ") + chalk.hex("#d2ffd2")("exit"));
|
|
156
182
|
console.log(chalk.dim(" Or just ask anything in natural language!"));
|
|
157
183
|
console.log();
|
|
158
184
|
await updateChatContext(projectPath);
|
|
@@ -188,16 +214,25 @@ async function startInteractiveMode(projectPath) {
|
|
|
188
214
|
console.log(chalk.dim(' "are there any conflicts?"'));
|
|
189
215
|
console.log(chalk.dim(' "suggest alternatives to express"'));
|
|
190
216
|
console.log();
|
|
191
|
-
console.log(chalk.hex("#a5ffa5")(" Commands:"));
|
|
192
|
-
console.log(` ${chalk.hex("#d2ffd2")("
|
|
193
|
-
console.log(` ${chalk.hex("#d2ffd2")("
|
|
194
|
-
console.log(` ${chalk.hex("#d2ffd2")("
|
|
195
|
-
console.log(` ${chalk.hex("#d2ffd2")("
|
|
196
|
-
console.log(` ${chalk.hex("#d2ffd2")("
|
|
197
|
-
console.log(` ${chalk.hex("#d2ffd2")("
|
|
198
|
-
console.log(` ${chalk.hex("#d2ffd2")("
|
|
199
|
-
console.log(
|
|
200
|
-
console.log(
|
|
217
|
+
console.log(chalk.hex("#a5ffa5")(" Premium Commands:"));
|
|
218
|
+
console.log(` ${chalk.hex("#d2ffd2")("doctor")} Full health audit with security scan`);
|
|
219
|
+
console.log(` ${chalk.hex("#d2ffd2")("optimize")} Find ways to reduce bloat`);
|
|
220
|
+
console.log(` ${chalk.hex("#d2ffd2")("tree")} Visualize dependency tree`);
|
|
221
|
+
console.log(` ${chalk.hex("#d2ffd2")("templates")} Browse pre-built environment templates`);
|
|
222
|
+
console.log(` ${chalk.hex("#d2ffd2")("explain")} Get info about any package (e.g. explain torch)`);
|
|
223
|
+
console.log(` ${chalk.hex("#d2ffd2")("clone")} Set up from GitHub URL`);
|
|
224
|
+
console.log(` ${chalk.hex("#d2ffd2")("insights")} Analytics and insights`);
|
|
225
|
+
console.log();
|
|
226
|
+
console.log(chalk.hex("#a5ffa5")(" Basic Commands:"));
|
|
227
|
+
console.log(` ${chalk.hex("#d2ffd2")("undo")} Undo last change`);
|
|
228
|
+
console.log(` ${chalk.hex("#d2ffd2")("list")} See what's installed`);
|
|
229
|
+
console.log(` ${chalk.hex("#d2ffd2")("check")} Check for problems`);
|
|
230
|
+
console.log(` ${chalk.hex("#d2ffd2")("ask")} Start AI conversation`);
|
|
231
|
+
console.log(` ${chalk.hex("#d2ffd2")("analyze")} AI analysis of environment`);
|
|
232
|
+
console.log(` ${chalk.hex("#d2ffd2")("ui")} Open visual dashboard`);
|
|
233
|
+
console.log(` ${chalk.hex("#d2ffd2")("history")} See past changes`);
|
|
234
|
+
console.log(` ${chalk.hex("#d2ffd2")("clear")} Clear screen`);
|
|
235
|
+
console.log(` ${chalk.hex("#d2ffd2")("exit")} Exit Komodo`);
|
|
201
236
|
console.log();
|
|
202
237
|
prompt();
|
|
203
238
|
return;
|
|
@@ -208,6 +243,17 @@ async function startInteractiveMode(projectPath) {
|
|
|
208
243
|
prompt();
|
|
209
244
|
return;
|
|
210
245
|
}
|
|
246
|
+
if (trimmed === "fix" || trimmed === "repair" || trimmed.includes("broken") || trimmed.includes("nothing works") || trimmed.includes("everything is broken") || trimmed.includes("help me fix")) {
|
|
247
|
+
await handleFix(projectPath);
|
|
248
|
+
await updateChatContext(projectPath);
|
|
249
|
+
prompt();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (trimmed === "conflicts" || trimmed.includes("check conflicts") || trimmed.includes("find conflicts")) {
|
|
253
|
+
await handleConflicts(projectPath);
|
|
254
|
+
prompt();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
211
257
|
if (trimmed === "list" || trimmed === "ls") {
|
|
212
258
|
await handleList(projectPath);
|
|
213
259
|
prompt();
|
|
@@ -228,6 +274,49 @@ async function startInteractiveMode(projectPath) {
|
|
|
228
274
|
prompt();
|
|
229
275
|
return;
|
|
230
276
|
}
|
|
277
|
+
if (trimmed === "doctor") {
|
|
278
|
+
await handleDoctor(projectPath);
|
|
279
|
+
prompt();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (trimmed === "optimize") {
|
|
283
|
+
await handleOptimize(projectPath);
|
|
284
|
+
prompt();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (trimmed === "tree") {
|
|
288
|
+
await handleTree(projectPath);
|
|
289
|
+
prompt();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (trimmed === "templates" || trimmed === "template") {
|
|
293
|
+
await handleTemplates();
|
|
294
|
+
prompt();
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (trimmed.startsWith("template ") || trimmed.startsWith("use ")) {
|
|
298
|
+
const templateId = trimmed.replace(/^(template|use)\s+/, "").trim();
|
|
299
|
+
await handleUseTemplate(templateId, projectPath);
|
|
300
|
+
prompt();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (trimmed.startsWith("explain ")) {
|
|
304
|
+
const packageName = trimmed.slice(8).trim();
|
|
305
|
+
await handleExplain(packageName);
|
|
306
|
+
prompt();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (trimmed.startsWith("clone ") || trimmed.startsWith("setup ")) {
|
|
310
|
+
const url = trimmed.replace(/^(clone|setup)\s+/, "").trim();
|
|
311
|
+
await handleClone(url);
|
|
312
|
+
prompt();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (trimmed === "insights" || trimmed === "analytics") {
|
|
316
|
+
await handleInsights(projectPath);
|
|
317
|
+
prompt();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
231
320
|
if (trimmed === "analyze" || trimmed === "analyse") {
|
|
232
321
|
const spinner = ora(chalk.hex("#b4ffb4")("Analyzing your environment...")).start();
|
|
233
322
|
await updateChatContext(projectPath);
|
|
@@ -428,6 +517,135 @@ async function handleCheck(projectPath) {
|
|
|
428
517
|
}
|
|
429
518
|
console.log();
|
|
430
519
|
}
|
|
520
|
+
async function handleFix(projectPath) {
|
|
521
|
+
const spinner = ora(chalk.hex("#b4ffb4")("Scanning your project...")).start();
|
|
522
|
+
const state = await loadState(projectPath);
|
|
523
|
+
let runtime;
|
|
524
|
+
if (state.activeEnvironmentId) {
|
|
525
|
+
const env = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
526
|
+
runtime = env?.runtime;
|
|
527
|
+
}
|
|
528
|
+
const diagnosis = await diagnoseEnvironment(projectPath, runtime);
|
|
529
|
+
spinner.stop();
|
|
530
|
+
console.log();
|
|
531
|
+
if (diagnosis.status === "healthy") {
|
|
532
|
+
console.log(chalk.hex("#5aff5a")(" \u2713 Everything looks good!"));
|
|
533
|
+
console.log(chalk.dim(" No issues found with your environment."));
|
|
534
|
+
console.log();
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
console.log(chalk.hex("#ffb347")(` Found ${diagnosis.problems.length} issue${diagnosis.problems.length > 1 ? "s" : ""}:`));
|
|
538
|
+
console.log();
|
|
539
|
+
diagnosis.problems.slice(0, 3).forEach((problem) => {
|
|
540
|
+
const icon = problem.severity === "critical" ? chalk.red("\u2717") : problem.severity === "warning" ? chalk.yellow("\u26A0") : chalk.blue("\u2139");
|
|
541
|
+
console.log(` ${icon} ${problem.friendlyTitle}`);
|
|
542
|
+
console.log(chalk.dim(` ${problem.description}`));
|
|
543
|
+
});
|
|
544
|
+
if (diagnosis.problems.length > 3) {
|
|
545
|
+
console.log(chalk.dim(` ...and ${diagnosis.problems.length - 3} more`));
|
|
546
|
+
}
|
|
547
|
+
console.log();
|
|
548
|
+
if (diagnosis.repairPlan.length === 0) {
|
|
549
|
+
console.log(chalk.dim(" No automatic fixes available. Try creating a new environment."));
|
|
550
|
+
console.log();
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
console.log(chalk.hex("#b4ffb4")(` Fixing ${diagnosis.repairPlan.length} issue${diagnosis.repairPlan.length > 1 ? "s" : ""}...`));
|
|
554
|
+
console.log();
|
|
555
|
+
const repairSpinner = ora().start();
|
|
556
|
+
const result = await executeRepair(projectPath, diagnosis, {
|
|
557
|
+
autoBackup: true,
|
|
558
|
+
dryRun: false,
|
|
559
|
+
onProgress: (step, status) => {
|
|
560
|
+
if (status === "starting") {
|
|
561
|
+
repairSpinner.text = chalk.hex("#b4ffb4")(step.friendlyDescription);
|
|
562
|
+
} else if (status === "complete") {
|
|
563
|
+
repairSpinner.succeed(chalk.hex("#5aff5a")(step.friendlyDescription));
|
|
564
|
+
repairSpinner.start();
|
|
565
|
+
} else if (status === "failed") {
|
|
566
|
+
repairSpinner.fail(chalk.red(step.friendlyDescription));
|
|
567
|
+
repairSpinner.start();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
if (repairSpinner.isSpinning) {
|
|
572
|
+
repairSpinner.stop();
|
|
573
|
+
}
|
|
574
|
+
console.log();
|
|
575
|
+
if (result.success) {
|
|
576
|
+
console.log(chalk.hex("#5aff5a")(` \u2713 All fixed! Resolved ${result.problemsFixed.length} issue${result.problemsFixed.length > 1 ? "s" : ""}.`));
|
|
577
|
+
} else {
|
|
578
|
+
console.log(chalk.yellow(` \u26A0 Partially fixed. Resolved ${result.stepsCompleted}/${result.stepsTotal} issues.`));
|
|
579
|
+
}
|
|
580
|
+
console.log();
|
|
581
|
+
}
|
|
582
|
+
async function handleConflicts(projectPath) {
|
|
583
|
+
const spinner = ora(chalk.hex("#b4ffb4")("Checking for conflicts...")).start();
|
|
584
|
+
const state = await loadState(projectPath);
|
|
585
|
+
let runtime;
|
|
586
|
+
if (state.activeEnvironmentId) {
|
|
587
|
+
const env = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
588
|
+
runtime = env?.runtime;
|
|
589
|
+
}
|
|
590
|
+
if (!runtime) {
|
|
591
|
+
spinner.fail("No environment found");
|
|
592
|
+
console.log();
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const conflicts = await detectRealConflicts(projectPath, runtime);
|
|
596
|
+
spinner.stop();
|
|
597
|
+
console.log();
|
|
598
|
+
if (conflicts.length === 0) {
|
|
599
|
+
console.log(chalk.hex("#5aff5a")(" \u2713 No conflicts found!"));
|
|
600
|
+
console.log(chalk.dim(" All packages are compatible."));
|
|
601
|
+
console.log();
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
console.log(chalk.yellow(` Found ${conflicts.length} conflict${conflicts.length > 1 ? "s" : ""}:`));
|
|
605
|
+
console.log();
|
|
606
|
+
conflicts.slice(0, 3).forEach((conflict) => {
|
|
607
|
+
const icon = conflict.severity === "error" ? chalk.red("\u2717") : chalk.yellow("\u26A0");
|
|
608
|
+
console.log(` ${icon} ${conflict.package1} \u2194 ${conflict.package2}`);
|
|
609
|
+
console.log(chalk.dim(` ${conflict.reason}`));
|
|
610
|
+
if (conflict.suggestion) {
|
|
611
|
+
console.log(chalk.hex("#87cefa")(` \u{1F4A1} ${conflict.suggestion}`));
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
if (conflicts.length > 3) {
|
|
615
|
+
console.log(chalk.dim(` ...and ${conflicts.length - 3} more`));
|
|
616
|
+
}
|
|
617
|
+
console.log();
|
|
618
|
+
console.log(chalk.hex("#b4ffb4")(" Attempting to resolve..."));
|
|
619
|
+
console.log();
|
|
620
|
+
const resolveSpinner = ora().start();
|
|
621
|
+
const result = await resolveConflicts(
|
|
622
|
+
projectPath,
|
|
623
|
+
runtime,
|
|
624
|
+
conflicts,
|
|
625
|
+
(message) => {
|
|
626
|
+
resolveSpinner.text = chalk.hex("#b4ffb4")(message);
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
if (resolveSpinner.isSpinning) {
|
|
630
|
+
resolveSpinner.stop();
|
|
631
|
+
}
|
|
632
|
+
console.log();
|
|
633
|
+
if (result.success) {
|
|
634
|
+
console.log(chalk.hex("#5aff5a")(" \u2713 All conflicts resolved!"));
|
|
635
|
+
result.resolved.forEach((r) => console.log(chalk.dim(` \u2022 ${r}`)));
|
|
636
|
+
} else {
|
|
637
|
+
if (result.resolved.length > 0) {
|
|
638
|
+
console.log(chalk.hex("#5aff5a")(" Resolved:"));
|
|
639
|
+
result.resolved.forEach((r) => console.log(chalk.dim(` \u2022 ${r}`)));
|
|
640
|
+
console.log();
|
|
641
|
+
}
|
|
642
|
+
console.log(chalk.yellow(" Cannot auto-resolve:"));
|
|
643
|
+
result.remaining.slice(0, 3).forEach(
|
|
644
|
+
(c) => console.log(chalk.dim(` \u2022 ${c.package1} \u2194 ${c.package2}`))
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
console.log();
|
|
648
|
+
}
|
|
431
649
|
async function handleHistory(projectPath) {
|
|
432
650
|
const snapshots = await komodo.listSnapshots(projectPath);
|
|
433
651
|
console.log();
|
|
@@ -453,13 +671,205 @@ async function handleHistory(projectPath) {
|
|
|
453
671
|
console.log(chalk.dim(" Type 'undo' to go back to the previous state"));
|
|
454
672
|
console.log();
|
|
455
673
|
}
|
|
674
|
+
async function handleDoctor(projectPath) {
|
|
675
|
+
const spinner = ora(chalk.hex("#b4ffb4")("Running comprehensive health audit...")).start();
|
|
676
|
+
const state = await loadState(projectPath);
|
|
677
|
+
const hardware = komodo.getHardware();
|
|
678
|
+
if (!state.activeEnvironmentId) {
|
|
679
|
+
spinner.succeed(chalk.dim("No environment to analyze - set something up first"));
|
|
680
|
+
console.log();
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const activeEnv = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
684
|
+
if (!activeEnv) {
|
|
685
|
+
spinner.succeed(chalk.dim("Nothing to analyze"));
|
|
686
|
+
console.log();
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const installed = await getInstalledPackages(projectPath, activeEnv.runtime);
|
|
690
|
+
const report = runDoctor(installed, hardware);
|
|
691
|
+
spinner.stop();
|
|
692
|
+
console.log();
|
|
693
|
+
console.log(formatDoctorReport(report, true));
|
|
694
|
+
console.log();
|
|
695
|
+
}
|
|
696
|
+
async function handleOptimize(projectPath) {
|
|
697
|
+
const spinner = ora(chalk.hex("#b4ffb4")("Analyzing optimization opportunities...")).start();
|
|
698
|
+
const state = await loadState(projectPath);
|
|
699
|
+
const hardware = komodo.getHardware();
|
|
700
|
+
if (!state.activeEnvironmentId) {
|
|
701
|
+
spinner.succeed(chalk.dim("No environment to optimize - set something up first"));
|
|
702
|
+
console.log();
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const activeEnv = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
706
|
+
if (!activeEnv) {
|
|
707
|
+
spinner.succeed(chalk.dim("Nothing to optimize"));
|
|
708
|
+
console.log();
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
const installed = await getInstalledPackages(projectPath, activeEnv.runtime);
|
|
712
|
+
const report = analyzeOptimizations(installed, hardware);
|
|
713
|
+
spinner.stop();
|
|
714
|
+
console.log();
|
|
715
|
+
console.log(formatOptimizationReport(report, true));
|
|
716
|
+
console.log();
|
|
717
|
+
}
|
|
718
|
+
async function handleTree(projectPath) {
|
|
719
|
+
const spinner = ora(chalk.hex("#b4ffb4")("Building dependency tree...")).start();
|
|
720
|
+
const state = await loadState(projectPath);
|
|
721
|
+
if (!state.activeEnvironmentId) {
|
|
722
|
+
spinner.succeed(chalk.dim("No dependencies to visualize - set something up first"));
|
|
723
|
+
console.log();
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const activeEnv = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
727
|
+
if (!activeEnv) {
|
|
728
|
+
spinner.succeed(chalk.dim("Nothing to visualize"));
|
|
729
|
+
console.log();
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const installed = await getInstalledPackages(projectPath, activeEnv.runtime);
|
|
733
|
+
const tree = buildDependencyTree(installed);
|
|
734
|
+
const visualization = visualizeTree(tree, { colorize: true, showSizes: true });
|
|
735
|
+
spinner.stop();
|
|
736
|
+
console.log();
|
|
737
|
+
console.log(visualization.ascii);
|
|
738
|
+
console.log();
|
|
739
|
+
console.log(chalk.hex("#b4ffb4").bold(" Stats:"));
|
|
740
|
+
console.log(` Total packages: ${visualization.stats.totalPackages}`);
|
|
741
|
+
console.log(` Direct dependencies: ${visualization.stats.directDependencies}`);
|
|
742
|
+
console.log(` Max depth: ${visualization.stats.maxDepth}`);
|
|
743
|
+
console.log();
|
|
744
|
+
}
|
|
745
|
+
async function handleTemplates(query) {
|
|
746
|
+
console.log();
|
|
747
|
+
if (query) {
|
|
748
|
+
const results = searchTemplates(query);
|
|
749
|
+
if (results.length === 0) {
|
|
750
|
+
console.log(chalk.dim(` No templates found for "${query}"`));
|
|
751
|
+
} else {
|
|
752
|
+
console.log(formatTemplateList(results, true));
|
|
753
|
+
}
|
|
754
|
+
} else {
|
|
755
|
+
console.log(chalk.hex("#a5ffa5").bold(" Available Templates"));
|
|
756
|
+
console.log(chalk.dim(" Use 'template <id>' to set up"));
|
|
757
|
+
console.log();
|
|
758
|
+
console.log(formatTemplateList(TEMPLATES, true));
|
|
759
|
+
}
|
|
760
|
+
console.log();
|
|
761
|
+
}
|
|
762
|
+
async function handleUseTemplate(templateId, projectPath) {
|
|
763
|
+
const template = getTemplateById(templateId);
|
|
764
|
+
if (!template) {
|
|
765
|
+
console.log();
|
|
766
|
+
console.log(chalk.red(` Template "${templateId}" not found`));
|
|
767
|
+
console.log(chalk.dim(" Run 'templates' to see available options"));
|
|
768
|
+
console.log();
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
console.log();
|
|
772
|
+
console.log(chalk.hex("#a5ffa5").bold(` ${template.name}`));
|
|
773
|
+
console.log(chalk.dim(` ${template.description}`));
|
|
774
|
+
console.log();
|
|
775
|
+
const commands = getInstallCommandsForTemplate(template);
|
|
776
|
+
console.log(chalk.hex("#b4ffb4")(" Packages:"));
|
|
777
|
+
template.packages.filter((p) => !p.optional).forEach((pkg) => {
|
|
778
|
+
const note = pkg.note ? chalk.dim(` - ${pkg.note}`) : "";
|
|
779
|
+
console.log(` ${chalk.hex("#5aff5a")("+")} ${pkg.name}${note}`);
|
|
780
|
+
});
|
|
781
|
+
console.log();
|
|
782
|
+
console.log(chalk.hex("#b4ffb4")(" Commands to run:"));
|
|
783
|
+
commands.forEach((cmd) => {
|
|
784
|
+
console.log(chalk.cyan(` $ ${cmd}`));
|
|
785
|
+
});
|
|
786
|
+
console.log();
|
|
787
|
+
const packageNames = template.packages.filter((p) => !p.optional).map((p) => p.name);
|
|
788
|
+
const intent = `install ${packageNames.join(" ")}`;
|
|
789
|
+
const spinner = ora(chalk.hex("#b4ffb4")("Setting up template...")).start();
|
|
790
|
+
const result = await komodo.install({
|
|
791
|
+
intent,
|
|
792
|
+
path: projectPath,
|
|
793
|
+
dryRun: false,
|
|
794
|
+
useAI: false
|
|
795
|
+
});
|
|
796
|
+
if (result.success) {
|
|
797
|
+
spinner.succeed(chalk.hex("#5aff5a")("Template set up successfully!"));
|
|
798
|
+
} else {
|
|
799
|
+
spinner.fail(chalk.red(result.error || "Failed to set up template"));
|
|
800
|
+
}
|
|
801
|
+
console.log();
|
|
802
|
+
}
|
|
803
|
+
async function handleExplain(packageName) {
|
|
804
|
+
console.log();
|
|
805
|
+
const explanation = explainPackage(packageName);
|
|
806
|
+
if (!explanation) {
|
|
807
|
+
console.log(chalk.dim(` No information found for "${packageName}"`));
|
|
808
|
+
console.log(chalk.dim(" Try searching for common packages like torch, react, pandas, etc."));
|
|
809
|
+
console.log();
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
console.log(formatExplanation(explanation, true));
|
|
813
|
+
console.log();
|
|
814
|
+
}
|
|
815
|
+
async function handleClone(url) {
|
|
816
|
+
console.log();
|
|
817
|
+
const parsed = parseGitHubUrl(url);
|
|
818
|
+
if (!parsed) {
|
|
819
|
+
console.log(chalk.red(" Invalid GitHub URL"));
|
|
820
|
+
console.log(chalk.dim(" Example: komodo clone https://github.com/owner/repo"));
|
|
821
|
+
console.log();
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const spinner = ora(chalk.hex("#b4ffb4")(`Analyzing ${parsed.owner}/${parsed.repo}...`)).start();
|
|
825
|
+
const mockFiles = {
|
|
826
|
+
"requirements.txt": "torch\ntransformers\nnumpy",
|
|
827
|
+
"README.md": "# Project\n\nInstall with pip install -r requirements.txt"
|
|
828
|
+
};
|
|
829
|
+
const analysis = analyzeRepoStructure(mockFiles);
|
|
830
|
+
analysis.url = url;
|
|
831
|
+
analysis.name = parsed.repo;
|
|
832
|
+
spinner.stop();
|
|
833
|
+
console.log();
|
|
834
|
+
console.log(formatRepoAnalysis(analysis, true));
|
|
835
|
+
console.log();
|
|
836
|
+
console.log(chalk.hex("#b4ffb4").bold(" Quick Setup:"));
|
|
837
|
+
console.log(chalk.cyan(` $ git clone ${url}`));
|
|
838
|
+
console.log(chalk.cyan(` $ cd ${parsed.repo}`));
|
|
839
|
+
for (const cmd of analysis.setupCommands) {
|
|
840
|
+
console.log(chalk.cyan(` $ ${cmd}`));
|
|
841
|
+
}
|
|
842
|
+
console.log();
|
|
843
|
+
}
|
|
844
|
+
async function handleInsights(projectPath) {
|
|
845
|
+
const spinner = ora(chalk.hex("#b4ffb4")("Gathering insights...")).start();
|
|
846
|
+
const state = await loadState(projectPath);
|
|
847
|
+
if (!state.activeEnvironmentId) {
|
|
848
|
+
spinner.succeed(chalk.dim("No environment to analyze - set something up first"));
|
|
849
|
+
console.log();
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const activeEnv = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
853
|
+
if (!activeEnv) {
|
|
854
|
+
spinner.succeed(chalk.dim("Nothing to analyze"));
|
|
855
|
+
console.log();
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
const installed = await getInstalledPackages(projectPath, activeEnv.runtime);
|
|
859
|
+
const snapshots = await komodo.listSnapshots(projectPath);
|
|
860
|
+
const insights = analyzeEnvironment(installed, snapshots);
|
|
861
|
+
spinner.stop();
|
|
862
|
+
console.log();
|
|
863
|
+
console.log(formatInsights(insights, true));
|
|
864
|
+
console.log();
|
|
865
|
+
}
|
|
456
866
|
async function handleUI(projectPath) {
|
|
457
867
|
const port = 3333;
|
|
458
868
|
const url = `http://localhost:${port}`;
|
|
459
869
|
console.log();
|
|
460
870
|
console.log(chalk.hex("#b4ffb4").dim(" Starting dashboard..."));
|
|
461
871
|
try {
|
|
462
|
-
const { startServer } = await import("./server-
|
|
872
|
+
const { startServer } = await import("./server-YR5YMDJ7.js");
|
|
463
873
|
await startServer(projectPath, port);
|
|
464
874
|
console.log(chalk.hex("#5aff5a")(` \u2713 Dashboard ready at ${chalk.bold(url)}`));
|
|
465
875
|
console.log();
|
|
@@ -598,6 +1008,188 @@ program.command("undo").description("Undo your last change").option("-p, --path
|
|
|
598
1008
|
process.exit(1);
|
|
599
1009
|
}
|
|
600
1010
|
});
|
|
1011
|
+
program.command("fix").alias("repair").description("Fix a broken environment").option("-p, --path <path>", "Project folder", process.cwd()).option("--dry-run", "Show what would be fixed without making changes").action(async (options) => {
|
|
1012
|
+
console.log();
|
|
1013
|
+
console.log(chalk.bold("\u{1F98E} Komodo Fix"));
|
|
1014
|
+
console.log();
|
|
1015
|
+
const spinner = ora("Scanning your project...").start();
|
|
1016
|
+
const state = await loadState(options.path);
|
|
1017
|
+
let runtime;
|
|
1018
|
+
if (state.activeEnvironmentId) {
|
|
1019
|
+
const env = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
1020
|
+
runtime = env?.runtime;
|
|
1021
|
+
}
|
|
1022
|
+
const diagnosis = await diagnoseEnvironment(options.path, runtime);
|
|
1023
|
+
spinner.stop();
|
|
1024
|
+
if (diagnosis.status === "healthy") {
|
|
1025
|
+
console.log(chalk.green(" \u2713 Everything looks good!"));
|
|
1026
|
+
console.log();
|
|
1027
|
+
console.log(chalk.dim(" No issues found with your environment."));
|
|
1028
|
+
console.log();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
console.log(chalk.bold(` Found ${diagnosis.problems.length} issue${diagnosis.problems.length > 1 ? "s" : ""}:`));
|
|
1032
|
+
console.log();
|
|
1033
|
+
diagnosis.problems.forEach((problem) => {
|
|
1034
|
+
const icon = problem.severity === "critical" ? "\u2717" : problem.severity === "warning" ? "\u26A0" : "\u2139";
|
|
1035
|
+
const color = problem.severity === "critical" ? chalk.red : problem.severity === "warning" ? chalk.yellow : chalk.blue;
|
|
1036
|
+
console.log(color(` ${icon} ${problem.friendlyTitle}`));
|
|
1037
|
+
console.log(chalk.dim(` ${problem.description}`));
|
|
1038
|
+
console.log();
|
|
1039
|
+
});
|
|
1040
|
+
if (diagnosis.repairPlan.length === 0) {
|
|
1041
|
+
console.log(chalk.yellow(" No automatic fixes available."));
|
|
1042
|
+
console.log();
|
|
1043
|
+
console.log(chalk.dim(" Try:"));
|
|
1044
|
+
console.log(chalk.dim(" \u2022 Check the technical details above"));
|
|
1045
|
+
console.log(chalk.dim(" \u2022 Create a new environment: komodo <your-intent>"));
|
|
1046
|
+
console.log();
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
if (options.dryRun) {
|
|
1050
|
+
console.log(chalk.cyan(" Would fix:"));
|
|
1051
|
+
console.log();
|
|
1052
|
+
diagnosis.repairPlan.forEach((step, i) => {
|
|
1053
|
+
console.log(chalk.dim(` ${i + 1}. ${step.friendlyDescription}`));
|
|
1054
|
+
});
|
|
1055
|
+
console.log();
|
|
1056
|
+
console.log(chalk.dim(` Estimated time: ${diagnosis.estimatedTime}`));
|
|
1057
|
+
console.log();
|
|
1058
|
+
console.log(chalk.cyan(" Run without --dry-run to apply these fixes."));
|
|
1059
|
+
console.log();
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
console.log(chalk.bold(` Fix all ${diagnosis.problems.length} issue${diagnosis.problems.length > 1 ? "s" : ""}?`));
|
|
1063
|
+
console.log(chalk.dim(` Estimated time: ${diagnosis.estimatedTime}`));
|
|
1064
|
+
console.log();
|
|
1065
|
+
const rl = readline.createInterface({
|
|
1066
|
+
input: process.stdin,
|
|
1067
|
+
output: process.stdout
|
|
1068
|
+
});
|
|
1069
|
+
const answer = await new Promise((resolve) => {
|
|
1070
|
+
rl.question(chalk.dim(" Continue? (Y/n) "), resolve);
|
|
1071
|
+
});
|
|
1072
|
+
rl.close();
|
|
1073
|
+
if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") {
|
|
1074
|
+
console.log();
|
|
1075
|
+
console.log(chalk.dim(" Cancelled."));
|
|
1076
|
+
console.log();
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
console.log();
|
|
1080
|
+
const repairSpinner = ora().start();
|
|
1081
|
+
const result = await executeRepair(options.path, diagnosis, {
|
|
1082
|
+
autoBackup: true,
|
|
1083
|
+
dryRun: false,
|
|
1084
|
+
onProgress: (step, status) => {
|
|
1085
|
+
if (status === "starting") {
|
|
1086
|
+
repairSpinner.text = step.friendlyDescription;
|
|
1087
|
+
} else if (status === "complete") {
|
|
1088
|
+
repairSpinner.succeed(step.friendlyDescription);
|
|
1089
|
+
repairSpinner.start();
|
|
1090
|
+
} else if (status === "failed") {
|
|
1091
|
+
repairSpinner.fail(step.friendlyDescription);
|
|
1092
|
+
repairSpinner.start();
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
if (repairSpinner.isSpinning) {
|
|
1097
|
+
repairSpinner.stop();
|
|
1098
|
+
}
|
|
1099
|
+
console.log();
|
|
1100
|
+
if (result.success) {
|
|
1101
|
+
console.log(
|
|
1102
|
+
boxen(
|
|
1103
|
+
chalk.green("All fixed!") + "\n\n" + chalk.dim(`Fixed ${result.problemsFixed.length} issue${result.problemsFixed.length > 1 ? "s" : ""}.`) + "\n\n" + chalk.dim("Changed your mind? Run: ") + chalk.white("komodo undo"),
|
|
1104
|
+
{ padding: 1, borderStyle: "round", borderColor: "green" }
|
|
1105
|
+
)
|
|
1106
|
+
);
|
|
1107
|
+
} else {
|
|
1108
|
+
console.log(
|
|
1109
|
+
boxen(
|
|
1110
|
+
chalk.yellow("Partially fixed") + "\n\n" + chalk.dim(`Fixed ${result.stepsCompleted}/${result.stepsTotal} issues.`) + (result.problemsRemaining.length > 0 ? "\n\n" + chalk.dim("Remaining issues:") + "\n" + result.problemsRemaining.map((p) => chalk.dim(` \u2022 ${p}`)).join("\n") : ""),
|
|
1111
|
+
{ padding: 1, borderStyle: "round", borderColor: "yellow" }
|
|
1112
|
+
)
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
console.log();
|
|
1116
|
+
});
|
|
1117
|
+
program.command("conflicts").description("Find and fix package conflicts").option("-p, --path <path>", "Project folder", process.cwd()).option("--fix", "Automatically fix conflicts").action(async (options) => {
|
|
1118
|
+
console.log();
|
|
1119
|
+
console.log(chalk.bold("\u{1F98E} Conflict Check"));
|
|
1120
|
+
console.log();
|
|
1121
|
+
const spinner = ora("Checking for conflicts...").start();
|
|
1122
|
+
const state = await loadState(options.path);
|
|
1123
|
+
let runtime;
|
|
1124
|
+
if (state.activeEnvironmentId) {
|
|
1125
|
+
const env = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
1126
|
+
runtime = env?.runtime;
|
|
1127
|
+
}
|
|
1128
|
+
if (!runtime) {
|
|
1129
|
+
spinner.fail("No environment found");
|
|
1130
|
+
console.log();
|
|
1131
|
+
console.log(chalk.dim(" No active environment detected."));
|
|
1132
|
+
console.log();
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
const conflicts = await detectRealConflicts(options.path, runtime);
|
|
1136
|
+
spinner.stop();
|
|
1137
|
+
if (conflicts.length === 0) {
|
|
1138
|
+
console.log(chalk.green(" \u2713 No conflicts found!"));
|
|
1139
|
+
console.log();
|
|
1140
|
+
console.log(chalk.dim(" All packages are compatible."));
|
|
1141
|
+
console.log();
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
console.log(chalk.bold(` Found ${conflicts.length} conflict${conflicts.length > 1 ? "s" : ""}:`));
|
|
1145
|
+
console.log();
|
|
1146
|
+
conflicts.forEach((conflict) => {
|
|
1147
|
+
const icon = conflict.severity === "error" ? "\u2717" : "\u26A0";
|
|
1148
|
+
const color = conflict.severity === "error" ? chalk.red : chalk.yellow;
|
|
1149
|
+
console.log(color(` ${icon} ${conflict.package1} \u2194 ${conflict.package2}`));
|
|
1150
|
+
console.log(chalk.dim(` ${conflict.reason}`));
|
|
1151
|
+
if (conflict.suggestion) {
|
|
1152
|
+
console.log(chalk.dim(` \u{1F4A1} ${conflict.suggestion}`));
|
|
1153
|
+
}
|
|
1154
|
+
console.log();
|
|
1155
|
+
});
|
|
1156
|
+
if (!options.fix) {
|
|
1157
|
+
console.log(chalk.dim(" Run with --fix to automatically resolve conflicts."));
|
|
1158
|
+
console.log();
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
console.log(chalk.bold(" Attempting to resolve..."));
|
|
1162
|
+
console.log();
|
|
1163
|
+
const resolveSpinner = ora().start();
|
|
1164
|
+
const result = await resolveConflicts(
|
|
1165
|
+
options.path,
|
|
1166
|
+
runtime,
|
|
1167
|
+
conflicts,
|
|
1168
|
+
(message) => {
|
|
1169
|
+
resolveSpinner.text = message;
|
|
1170
|
+
}
|
|
1171
|
+
);
|
|
1172
|
+
if (resolveSpinner.isSpinning) {
|
|
1173
|
+
resolveSpinner.stop();
|
|
1174
|
+
}
|
|
1175
|
+
console.log();
|
|
1176
|
+
if (result.success) {
|
|
1177
|
+
console.log(
|
|
1178
|
+
boxen(
|
|
1179
|
+
chalk.green("All conflicts resolved!") + "\n\n" + result.resolved.map((r) => chalk.dim(` \u2713 ${r}`)).join("\n"),
|
|
1180
|
+
{ padding: 1, borderStyle: "round", borderColor: "green" }
|
|
1181
|
+
)
|
|
1182
|
+
);
|
|
1183
|
+
} else {
|
|
1184
|
+
console.log(
|
|
1185
|
+
boxen(
|
|
1186
|
+
chalk.yellow("Some conflicts resolved") + "\n\n" + (result.resolved.length > 0 ? chalk.dim("Resolved:") + "\n" + result.resolved.map((r) => chalk.dim(` \u2713 ${r}`)).join("\n") + "\n\n" : "") + chalk.dim("Cannot auto-resolve:") + "\n" + result.remaining.map((c) => chalk.dim(` \u2022 ${c.package1} \u2194 ${c.package2}`)).join("\n") + "\n\n" + chalk.dim("These require manual intervention or separate environments."),
|
|
1187
|
+
{ padding: 1, borderStyle: "round", borderColor: "yellow" }
|
|
1188
|
+
)
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
console.log();
|
|
1192
|
+
});
|
|
601
1193
|
program.command("ui").description("Open the visual dashboard").option("-p, --path <path>", "Project folder", process.cwd()).option("--port <port>", "Port number", "3333").action(async (options) => {
|
|
602
1194
|
console.log();
|
|
603
1195
|
console.log(chalk.bold("\u{1F98E} Komodo"));
|
|
@@ -606,7 +1198,7 @@ program.command("ui").description("Open the visual dashboard").option("-p, --pat
|
|
|
606
1198
|
const url = `http://localhost:${port}`;
|
|
607
1199
|
console.log(chalk.dim(" Starting dashboard..."));
|
|
608
1200
|
try {
|
|
609
|
-
const { startServer } = await import("./server-
|
|
1201
|
+
const { startServer } = await import("./server-YR5YMDJ7.js");
|
|
610
1202
|
await startServer(options.path, port);
|
|
611
1203
|
console.log();
|
|
612
1204
|
console.log(chalk.green(` \u2713 Dashboard ready at ${chalk.bold(url)}`));
|
|
@@ -704,6 +1296,99 @@ program.command("check").description("Check if everything is working").option("-
|
|
|
704
1296
|
}
|
|
705
1297
|
console.log();
|
|
706
1298
|
});
|
|
1299
|
+
program.command("doctor").description("Run comprehensive health audit with security scan").option("-p, --path <path>", "Project folder", process.cwd()).action(async (options) => {
|
|
1300
|
+
console.log();
|
|
1301
|
+
console.log(chalk.bold("\u{1F98E} Komodo Doctor"));
|
|
1302
|
+
console.log();
|
|
1303
|
+
await handleDoctor(options.path);
|
|
1304
|
+
});
|
|
1305
|
+
program.command("optimize").description("Find ways to reduce environment size").option("-p, --path <path>", "Project folder", process.cwd()).option("--script", "Generate optimization script").action(async (options) => {
|
|
1306
|
+
console.log();
|
|
1307
|
+
console.log(chalk.bold("\u{1F98E} Komodo Optimizer"));
|
|
1308
|
+
console.log();
|
|
1309
|
+
const state = await loadState(options.path);
|
|
1310
|
+
if (!state.activeEnvironmentId) {
|
|
1311
|
+
console.log(chalk.dim(" No environment to optimize"));
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const activeEnv = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
1315
|
+
if (!activeEnv) return;
|
|
1316
|
+
const installed = await getInstalledPackages(options.path, activeEnv.runtime);
|
|
1317
|
+
const hardware = komodo.getHardware();
|
|
1318
|
+
const report = analyzeOptimizations(installed, hardware);
|
|
1319
|
+
if (options.script) {
|
|
1320
|
+
const script = generateOptimizationScript(report.suggestions, true);
|
|
1321
|
+
console.log(script);
|
|
1322
|
+
} else {
|
|
1323
|
+
console.log(formatOptimizationReport(report, true));
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
program.command("tree").description("Visualize dependency tree").option("-p, --path <path>", "Project folder", process.cwd()).option("--depth <n>", "Max depth to show", "5").action(async (options) => {
|
|
1327
|
+
console.log();
|
|
1328
|
+
console.log(chalk.bold("\u{1F98E} Komodo Dependency Tree"));
|
|
1329
|
+
console.log();
|
|
1330
|
+
await handleTree(options.path);
|
|
1331
|
+
});
|
|
1332
|
+
program.command("templates").alias("template").description("Browse and use pre-built environment templates").argument("[query]", "Search for templates").option("-p, --path <path>", "Project folder", process.cwd()).action(async (query, options) => {
|
|
1333
|
+
console.log();
|
|
1334
|
+
console.log(chalk.bold("\u{1F98E} Komodo Templates"));
|
|
1335
|
+
console.log();
|
|
1336
|
+
if (query) {
|
|
1337
|
+
const template = getTemplateById(query);
|
|
1338
|
+
if (template) {
|
|
1339
|
+
await handleUseTemplate(query, options.path);
|
|
1340
|
+
} else {
|
|
1341
|
+
const results = searchTemplates(query);
|
|
1342
|
+
if (results.length === 0) {
|
|
1343
|
+
console.log(chalk.dim(` No templates found for "${query}"`));
|
|
1344
|
+
} else {
|
|
1345
|
+
console.log(formatTemplateList(results, true));
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
} else {
|
|
1349
|
+
console.log(formatTemplateList(TEMPLATES, true));
|
|
1350
|
+
console.log(chalk.dim(" Use 'komodo template <id>' to set up a template"));
|
|
1351
|
+
console.log();
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
program.command("explain").description("Get detailed information about a package").argument("<package>", "Package name").action(async (packageName) => {
|
|
1355
|
+
console.log();
|
|
1356
|
+
console.log(chalk.bold("\u{1F98E} Komodo Package Info"));
|
|
1357
|
+
console.log();
|
|
1358
|
+
await handleExplain(packageName);
|
|
1359
|
+
});
|
|
1360
|
+
program.command("clone").alias("setup").description("Analyze and set up from a GitHub repository").argument("<url>", "GitHub repository URL").option("-p, --path <path>", "Project folder", process.cwd()).action(async (url, options) => {
|
|
1361
|
+
console.log();
|
|
1362
|
+
console.log(chalk.bold("\u{1F98E} Komodo GitHub Setup"));
|
|
1363
|
+
console.log();
|
|
1364
|
+
await handleClone(url);
|
|
1365
|
+
});
|
|
1366
|
+
program.command("insights").alias("analytics").description("View environment analytics and insights").option("-p, --path <path>", "Project folder", process.cwd()).action(async (options) => {
|
|
1367
|
+
console.log();
|
|
1368
|
+
console.log(chalk.bold("\u{1F98E} Komodo Insights"));
|
|
1369
|
+
console.log();
|
|
1370
|
+
await handleInsights(options.path);
|
|
1371
|
+
});
|
|
1372
|
+
program.command("diff").description("Compare two environment snapshots").option("-p, --path <path>", "Project folder", process.cwd()).action(async (options) => {
|
|
1373
|
+
console.log();
|
|
1374
|
+
console.log(chalk.bold("\u{1F98E} Komodo Environment Diff"));
|
|
1375
|
+
console.log();
|
|
1376
|
+
const snapshots = await komodo.listSnapshots(options.path);
|
|
1377
|
+
if (snapshots.length < 2) {
|
|
1378
|
+
console.log(chalk.dim(" Need at least 2 snapshots to compare"));
|
|
1379
|
+
console.log(chalk.dim(" Make some changes first, then run diff again"));
|
|
1380
|
+
console.log();
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
const sorted = [...snapshots].sort(
|
|
1384
|
+
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
1385
|
+
);
|
|
1386
|
+
const diff = diffPackages(sorted[1].packages, sorted[0].packages);
|
|
1387
|
+
diff.from = sorted[1].description;
|
|
1388
|
+
diff.to = sorted[0].description;
|
|
1389
|
+
console.log(formatDiff(diff, true));
|
|
1390
|
+
console.log();
|
|
1391
|
+
});
|
|
707
1392
|
program.command("history").description("See past changes").option("-p, --path <path>", "Project folder", process.cwd()).action(async (options) => {
|
|
708
1393
|
const snapshots = await komodo.listSnapshots(options.path);
|
|
709
1394
|
console.log();
|