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.
@@ -35,9 +35,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
35
35
  ));
36
36
 
37
37
  // ../core/dist/index.js
38
- import { randomUUID as randomUUID2 } from "crypto";
38
+ import { randomUUID as randomUUID3 } from "crypto";
39
39
  import { mkdir as mkdir3 } from "fs/promises";
40
- import { existsSync as existsSync3 } from "fs";
40
+ import { existsSync as existsSync4 } from "fs";
41
41
  import { resolve } from "path";
42
42
  import { execSync } from "child_process";
43
43
  import os from "os";
@@ -52,6 +52,11 @@ import { randomUUID } from "crypto";
52
52
  import { mkdir as mkdir2, writeFile as writeFile3, readFile as readFile3 } from "fs/promises";
53
53
  import { existsSync as existsSync2 } from "fs";
54
54
  import { join as join3 } from "path";
55
+ import { execa as execa3 } from "execa";
56
+ import { existsSync as existsSync3 } from "fs";
57
+ import { readFile as readFile4 } from "fs/promises";
58
+ import { join as join4 } from "path";
59
+ import { randomUUID as randomUUID2 } from "crypto";
55
60
 
56
61
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
57
62
  var external_exports = {};
@@ -4095,10 +4100,12 @@ var coerce = {
4095
4100
  var NEVER = INVALID;
4096
4101
 
4097
4102
  // ../core/dist/index.js
4098
- import { execa as execa3 } from "execa";
4099
- import { existsSync as existsSync4 } from "fs";
4100
- import { readFile as readFile4 } from "fs/promises";
4101
- import { join as join5 } from "path";
4103
+ import { execa as execa4 } from "execa";
4104
+ import { existsSync as existsSync5 } from "fs";
4105
+ import { join as join6 } from "path";
4106
+ import { randomUUID as randomUUID4 } from "crypto";
4107
+ import { execa as execa5 } from "execa";
4108
+ import { execa as execa6 } from "execa";
4102
4109
  function detectOs() {
4103
4110
  const platform = os.platform();
4104
4111
  if (platform === "darwin") return "darwin";
@@ -4938,6 +4945,16 @@ async function listSnapshots(basePath, environmentId) {
4938
4945
  }
4939
4946
  return state.snapshots;
4940
4947
  }
4948
+ async function getLatestSnapshot(basePath, environmentId) {
4949
+ const snapshots = await listSnapshots(basePath, environmentId);
4950
+ if (snapshots.length === 0) {
4951
+ return void 0;
4952
+ }
4953
+ snapshots.sort(
4954
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
4955
+ );
4956
+ return snapshots[0];
4957
+ }
4941
4958
  var DEFAULT_MODEL = "llama-3.3-70b";
4942
4959
  var DEFAULT_MAX_TOKENS = 2048;
4943
4960
  var DEFAULT_TEMPERATURE = 0.2;
@@ -5320,6 +5337,450 @@ Only include the JSON block if you're recommending specific package changes.`;
5320
5337
  return result;
5321
5338
  }
5322
5339
  };
5340
+ var KNOWN_CONFLICTS = [
5341
+ {
5342
+ packages: ["tensorflow", "torch"],
5343
+ reason: "Both use significant GPU memory and may conflict",
5344
+ suggestion: "Use separate environments for TensorFlow and PyTorch projects"
5345
+ },
5346
+ {
5347
+ packages: ["numpy", "jax"],
5348
+ reason: "JAX includes its own numpy-like API that may conflict",
5349
+ suggestion: "Use jax.numpy instead of importing both"
5350
+ },
5351
+ {
5352
+ packages: ["PIL", "pillow"],
5353
+ reason: "PIL and Pillow are the same library, cannot coexist",
5354
+ suggestion: "Remove PIL and use Pillow instead"
5355
+ },
5356
+ {
5357
+ packages: ["opencv-python", "opencv-contrib-python"],
5358
+ reason: "These packages overlap and can cause import conflicts",
5359
+ suggestion: "Use opencv-contrib-python only (includes all opencv-python features)"
5360
+ },
5361
+ {
5362
+ packages: ["tensorflow", "tensorflow-gpu"],
5363
+ reason: "TensorFlow 2.x already includes GPU support",
5364
+ suggestion: "Remove tensorflow-gpu and use tensorflow only"
5365
+ },
5366
+ {
5367
+ packages: ["requests", "urllib3"],
5368
+ reason: "Requests includes urllib3, may cause version conflicts",
5369
+ suggestion: "Let requests manage its own urllib3 dependency"
5370
+ },
5371
+ {
5372
+ packages: ["pandas", "numpy"],
5373
+ reason: "Version mismatch between pandas and numpy can cause errors",
5374
+ suggestion: "Ensure compatible versions or let pandas manage numpy version"
5375
+ },
5376
+ {
5377
+ packages: ["jupyter", "ipython"],
5378
+ reason: "Conflicting IPython versions",
5379
+ suggestion: "Use jupyter only, which includes IPython"
5380
+ },
5381
+ {
5382
+ packages: ["scikit-learn", "sklearn"],
5383
+ reason: "sklearn is deprecated, scikit-learn is the correct package",
5384
+ suggestion: "Remove sklearn and use scikit-learn only"
5385
+ },
5386
+ {
5387
+ packages: ["pyyaml", "yaml"],
5388
+ reason: "Different YAML parsers can conflict",
5389
+ suggestion: "Use pyyaml only"
5390
+ },
5391
+ // Node.js conflicts
5392
+ {
5393
+ packages: ["moment", "dayjs"],
5394
+ reason: "Both date libraries serve same purpose, adds bundle bloat",
5395
+ suggestion: "Choose one date library (dayjs is lighter)"
5396
+ },
5397
+ {
5398
+ packages: ["axios", "node-fetch"],
5399
+ reason: "Both HTTP clients serve same purpose",
5400
+ suggestion: "Choose one HTTP client"
5401
+ },
5402
+ {
5403
+ packages: ["lodash", "underscore"],
5404
+ reason: "Both utility libraries serve same purpose",
5405
+ suggestion: "Choose one utility library (lodash is more feature-complete)"
5406
+ },
5407
+ {
5408
+ packages: ["express", "koa"],
5409
+ reason: "Both web frameworks, cannot be used together",
5410
+ suggestion: "Choose one web framework"
5411
+ },
5412
+ {
5413
+ packages: ["react", "vue"],
5414
+ reason: "Cannot use multiple frontend frameworks simultaneously",
5415
+ suggestion: "Choose one frontend framework"
5416
+ },
5417
+ {
5418
+ packages: ["webpack", "vite"],
5419
+ reason: "Both bundlers serve same purpose",
5420
+ suggestion: "Choose one bundler (vite is faster for development)"
5421
+ },
5422
+ {
5423
+ packages: ["eslint", "tslint"],
5424
+ reason: "TSLint is deprecated in favor of ESLint",
5425
+ suggestion: "Remove tslint and use eslint with @typescript-eslint"
5426
+ },
5427
+ {
5428
+ packages: ["node-sass", "sass"],
5429
+ reason: "node-sass is deprecated, sass is the new implementation",
5430
+ suggestion: "Remove node-sass and use sass (dart-sass)"
5431
+ }
5432
+ ];
5433
+ async function getInstalledPackages(path, runtime) {
5434
+ if (runtime === "python") {
5435
+ return getPythonPackages(path);
5436
+ } else {
5437
+ return getNodePackages(path);
5438
+ }
5439
+ }
5440
+ async function getPythonPackages(path) {
5441
+ const venvPath = join4(path, ".venv");
5442
+ if (!existsSync3(venvPath)) {
5443
+ return [];
5444
+ }
5445
+ try {
5446
+ const pipPath = join4(venvPath, "bin", "pip");
5447
+ const result = await execa3(pipPath, ["list", "--format=json"], { cwd: path });
5448
+ const packages = JSON.parse(result.stdout);
5449
+ return packages.map((pkg) => ({
5450
+ name: pkg.name,
5451
+ version: pkg.version
5452
+ }));
5453
+ } catch {
5454
+ try {
5455
+ const result = await execa3(
5456
+ "uv",
5457
+ ["pip", "list", "--format=json", "--python", join4(venvPath, "bin", "python")],
5458
+ { cwd: path }
5459
+ );
5460
+ const packages = JSON.parse(result.stdout);
5461
+ return packages.map((pkg) => ({
5462
+ name: pkg.name,
5463
+ version: pkg.version
5464
+ }));
5465
+ } catch {
5466
+ return [];
5467
+ }
5468
+ }
5469
+ }
5470
+ async function getNodePackages(path) {
5471
+ const packageJsonPath = join4(path, "package.json");
5472
+ if (!existsSync3(packageJsonPath)) {
5473
+ return [];
5474
+ }
5475
+ try {
5476
+ const content = await readFile4(packageJsonPath, "utf-8");
5477
+ const packageJson = JSON.parse(content);
5478
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
5479
+ const packages = [];
5480
+ for (const [name, version] of Object.entries(deps)) {
5481
+ packages.push({
5482
+ name,
5483
+ version: String(version).replace(/^[\^~]/, "")
5484
+ });
5485
+ }
5486
+ return packages;
5487
+ } catch {
5488
+ return [];
5489
+ }
5490
+ }
5491
+ function detectConflicts(packages) {
5492
+ const conflicts = [];
5493
+ const packageNames = packages.map((p) => p.name.toLowerCase());
5494
+ for (const known of KNOWN_CONFLICTS) {
5495
+ const [pkg1, pkg2] = known.packages;
5496
+ if (packageNames.includes(pkg1) && packageNames.includes(pkg2)) {
5497
+ conflicts.push({
5498
+ package1: pkg1,
5499
+ package2: pkg2,
5500
+ reason: known.reason,
5501
+ severity: "warning",
5502
+ suggestion: known.suggestion
5503
+ });
5504
+ }
5505
+ }
5506
+ return conflicts;
5507
+ }
5508
+ function analyzeHealth(packages, conflicts) {
5509
+ const issues = [];
5510
+ let score = 100;
5511
+ for (const conflict of conflicts) {
5512
+ issues.push({
5513
+ type: "conflict",
5514
+ severity: conflict.severity,
5515
+ title: `${conflict.package1} and ${conflict.package2} may conflict`,
5516
+ description: conflict.reason,
5517
+ fix: conflict.suggestion,
5518
+ autoFixable: false
5519
+ });
5520
+ score -= conflict.severity === "error" ? 20 : 10;
5521
+ }
5522
+ const commonOutdated = ["requests", "axios", "lodash"];
5523
+ for (const pkg of packages) {
5524
+ if (commonOutdated.includes(pkg.name.toLowerCase())) {
5525
+ }
5526
+ }
5527
+ return {
5528
+ healthy: issues.filter((i) => i.severity === "error").length === 0,
5529
+ issues,
5530
+ score: Math.max(0, score)
5531
+ };
5532
+ }
5533
+ async function detectRealConflicts(path, runtime) {
5534
+ const conflicts = [];
5535
+ if (runtime === "python") {
5536
+ const venvPath = join4(path, ".venv");
5537
+ if (!existsSync3(venvPath)) {
5538
+ return conflicts;
5539
+ }
5540
+ try {
5541
+ const pipPath = join4(venvPath, "bin", "pip");
5542
+ const result = await execa3(pipPath, ["check"], { cwd: path, reject: false });
5543
+ if (result.exitCode !== 0 && result.stdout) {
5544
+ const lines = result.stdout.split("\n");
5545
+ for (const line of lines) {
5546
+ const match = line.match(/(\S+)\s+[\d.]+\s+requires\s+(\S+)\s*([>=<]+[\d.]+)?.*but.*you.*have\s+(\S+)\s+([\d.]+)/);
5547
+ if (match) {
5548
+ const [, pkg1, pkg2, requiredVersion, , actualVersion] = match;
5549
+ if (pkg1 && pkg2) {
5550
+ conflicts.push({
5551
+ package1: pkg1,
5552
+ package2: pkg2,
5553
+ reason: `${pkg1} requires ${pkg2}${requiredVersion || ""}, but ${actualVersion || "unknown version"} is installed`,
5554
+ severity: "error",
5555
+ suggestion: `Upgrade ${pkg2} to meet ${pkg1}'s requirements`
5556
+ });
5557
+ }
5558
+ }
5559
+ }
5560
+ }
5561
+ } catch {
5562
+ }
5563
+ } else {
5564
+ try {
5565
+ const result = await execa3("npm", ["ls", "--json"], {
5566
+ cwd: path,
5567
+ reject: false
5568
+ });
5569
+ if (result.stdout) {
5570
+ try {
5571
+ const output = JSON.parse(result.stdout);
5572
+ if (output.problems && Array.isArray(output.problems)) {
5573
+ for (const problem of output.problems) {
5574
+ const problemStr = String(problem);
5575
+ const match = problemStr.match(/(\S+)@[\d.]+\s+requires\s+(\S+)@([^\s]+)/);
5576
+ if (match) {
5577
+ const [, pkg1, pkg2, version] = match;
5578
+ if (pkg1 && pkg2) {
5579
+ conflicts.push({
5580
+ package1: pkg1,
5581
+ package2: pkg2,
5582
+ reason: `${pkg1} requires ${pkg2}@${version || "unknown"}`,
5583
+ severity: "warning",
5584
+ suggestion: `Install compatible version of ${pkg2}`
5585
+ });
5586
+ }
5587
+ } else {
5588
+ conflicts.push({
5589
+ package1: "unknown",
5590
+ package2: "unknown",
5591
+ reason: problemStr,
5592
+ severity: "warning"
5593
+ });
5594
+ }
5595
+ }
5596
+ }
5597
+ } catch {
5598
+ }
5599
+ }
5600
+ } catch {
5601
+ }
5602
+ }
5603
+ return conflicts;
5604
+ }
5605
+ async function checkPreInstallConflicts(path, runtime, newPackages) {
5606
+ const conflicts = [];
5607
+ const suggestions = [];
5608
+ const installed = await getInstalledPackages(path, runtime);
5609
+ const installedNames = installed.map((p) => p.name.toLowerCase());
5610
+ for (const newPkg of newPackages) {
5611
+ for (const known of KNOWN_CONFLICTS) {
5612
+ const [pkg1, pkg2] = known.packages;
5613
+ const newPkgName = newPkg.name.toLowerCase();
5614
+ if (newPkgName === pkg1 && installedNames.includes(pkg2)) {
5615
+ conflicts.push({
5616
+ package1: pkg1,
5617
+ package2: pkg2,
5618
+ reason: known.reason,
5619
+ severity: "warning",
5620
+ suggestion: known.suggestion
5621
+ });
5622
+ } else if (newPkgName === pkg2 && installedNames.includes(pkg1)) {
5623
+ conflicts.push({
5624
+ package1: pkg1,
5625
+ package2: pkg2,
5626
+ reason: known.reason,
5627
+ severity: "warning",
5628
+ suggestion: known.suggestion
5629
+ });
5630
+ }
5631
+ }
5632
+ }
5633
+ const newPackageNames = newPackages.map((p) => p.name.toLowerCase());
5634
+ for (const known of KNOWN_CONFLICTS) {
5635
+ const [pkg1, pkg2] = known.packages;
5636
+ if (newPackageNames.includes(pkg1) && newPackageNames.includes(pkg2)) {
5637
+ conflicts.push({
5638
+ package1: pkg1,
5639
+ package2: pkg2,
5640
+ reason: known.reason,
5641
+ severity: "warning",
5642
+ suggestion: known.suggestion
5643
+ });
5644
+ }
5645
+ }
5646
+ if (runtime === "python") {
5647
+ const venvPath = join4(path, ".venv");
5648
+ if (existsSync3(venvPath)) {
5649
+ try {
5650
+ const pipPath = join4(venvPath, "bin", "pip");
5651
+ const packageSpecs = newPackages.map(
5652
+ (p) => p.version ? `${p.name}==${p.version}` : p.name
5653
+ );
5654
+ const result = await execa3(
5655
+ pipPath,
5656
+ ["install", "--dry-run", ...packageSpecs],
5657
+ { cwd: path, reject: false }
5658
+ );
5659
+ if (result.exitCode !== 0 && result.stderr) {
5660
+ if (result.stderr.includes("ResolutionImpossible") || result.stderr.includes("incompatible")) {
5661
+ conflicts.push({
5662
+ package1: "new packages",
5663
+ package2: "existing packages",
5664
+ reason: "Package versions are incompatible",
5665
+ severity: "error",
5666
+ suggestion: "Review package version requirements"
5667
+ });
5668
+ }
5669
+ }
5670
+ } catch {
5671
+ }
5672
+ }
5673
+ } else {
5674
+ try {
5675
+ const packageSpecs = newPackages.map(
5676
+ (p) => p.version ? `${p.name}@${p.version}` : p.name
5677
+ );
5678
+ const result = await execa3(
5679
+ "npm",
5680
+ ["install", "--dry-run", ...packageSpecs],
5681
+ { cwd: path, reject: false }
5682
+ );
5683
+ if (result.exitCode !== 0 && result.stderr) {
5684
+ if (result.stderr.includes("ERESOLVE") || result.stderr.includes("conflict")) {
5685
+ conflicts.push({
5686
+ package1: "new packages",
5687
+ package2: "existing packages",
5688
+ reason: "Dependency resolution conflict",
5689
+ severity: "error",
5690
+ suggestion: "Review peer dependencies"
5691
+ });
5692
+ }
5693
+ }
5694
+ } catch {
5695
+ }
5696
+ }
5697
+ for (const conflict of conflicts) {
5698
+ if (conflict.package1 !== "unknown" && conflict.package2 !== "unknown") {
5699
+ suggestions.push({
5700
+ conflictId: randomUUID2(),
5701
+ strategy: "separate-env",
5702
+ packages: [],
5703
+ description: `Create separate environment for ${conflict.package1} and ${conflict.package2}`,
5704
+ friendlyDescription: "Use different projects for these packages",
5705
+ automated: false,
5706
+ risk: "safe"
5707
+ });
5708
+ }
5709
+ }
5710
+ return {
5711
+ willConflict: conflicts.length > 0,
5712
+ conflicts,
5713
+ suggestions
5714
+ };
5715
+ }
5716
+ async function resolveConflicts(path, runtime, conflicts, onProgress) {
5717
+ const resolved = [];
5718
+ const remaining = [];
5719
+ onProgress?.("Analyzing conflicts...");
5720
+ for (const conflict of conflicts) {
5721
+ if (conflict.package1 === "PIL" && conflict.package2 === "pillow") {
5722
+ onProgress?.("Removing deprecated PIL package...");
5723
+ try {
5724
+ if (runtime === "python") {
5725
+ const pipPath = join4(path, ".venv", "bin", "pip");
5726
+ await execa3(pipPath, ["uninstall", "-y", "PIL"], { cwd: path });
5727
+ resolved.push(`Removed ${conflict.package1}`);
5728
+ }
5729
+ } catch {
5730
+ remaining.push(conflict);
5731
+ }
5732
+ } else if (conflict.package1 === "tensorflow" && conflict.package2 === "tensorflow-gpu") {
5733
+ onProgress?.("Removing deprecated tensorflow-gpu...");
5734
+ try {
5735
+ if (runtime === "python") {
5736
+ const pipPath = join4(path, ".venv", "bin", "pip");
5737
+ await execa3(pipPath, ["uninstall", "-y", "tensorflow-gpu"], { cwd: path });
5738
+ resolved.push(`Removed ${conflict.package2}`);
5739
+ }
5740
+ } catch {
5741
+ remaining.push(conflict);
5742
+ }
5743
+ } else if (conflict.package1 === "sklearn" && conflict.package2 === "scikit-learn") {
5744
+ onProgress?.("Removing deprecated sklearn...");
5745
+ try {
5746
+ if (runtime === "python") {
5747
+ const pipPath = join4(path, ".venv", "bin", "pip");
5748
+ await execa3(pipPath, ["uninstall", "-y", "sklearn"], { cwd: path });
5749
+ resolved.push(`Removed ${conflict.package1}`);
5750
+ }
5751
+ } catch {
5752
+ remaining.push(conflict);
5753
+ }
5754
+ } else if (conflict.package1 === "tslint" && conflict.package2 === "eslint") {
5755
+ onProgress?.("Removing deprecated tslint...");
5756
+ try {
5757
+ if (runtime === "node") {
5758
+ await execa3("npm", ["uninstall", "tslint"], { cwd: path });
5759
+ resolved.push(`Removed ${conflict.package1}`);
5760
+ }
5761
+ } catch {
5762
+ remaining.push(conflict);
5763
+ }
5764
+ } else if (conflict.package1 === "node-sass" && conflict.package2 === "sass") {
5765
+ onProgress?.("Removing deprecated node-sass...");
5766
+ try {
5767
+ if (runtime === "node") {
5768
+ await execa3("npm", ["uninstall", "node-sass"], { cwd: path });
5769
+ resolved.push(`Removed ${conflict.package1}`);
5770
+ }
5771
+ } catch {
5772
+ remaining.push(conflict);
5773
+ }
5774
+ } else {
5775
+ remaining.push(conflict);
5776
+ }
5777
+ }
5778
+ return {
5779
+ success: remaining.length === 0,
5780
+ resolved,
5781
+ remaining
5782
+ };
5783
+ }
5323
5784
  var Komodo = class {
5324
5785
  hardware;
5325
5786
  ai = null;
@@ -5417,18 +5878,43 @@ var Komodo = class {
5417
5878
  aiAnalysis
5418
5879
  };
5419
5880
  }
5420
- if (dryRun) {
5421
- return {
5422
- success: true,
5423
- resolution,
5424
- hardware: this.hardware,
5425
- aiAnalysis
5426
- };
5881
+ if (resolution.packages.length > 0 && existsSync4(targetPath)) {
5882
+ onProgress?.("Checking for potential conflicts...");
5883
+ try {
5884
+ const preCheck = await checkPreInstallConflicts(
5885
+ targetPath,
5886
+ resolution.runtime,
5887
+ resolution.packages
5888
+ );
5889
+ if (preCheck.willConflict) {
5890
+ for (const conflict of preCheck.conflicts) {
5891
+ const warningMsg = `Warning: ${conflict.package1} and ${conflict.package2} may conflict - ${conflict.reason}`;
5892
+ onWarning?.(warningMsg);
5893
+ resolution.warnings.push(warningMsg);
5894
+ }
5895
+ const criticalConflicts = preCheck.conflicts.filter((c) => c.severity === "error");
5896
+ if (criticalConflicts.length > 0) {
5897
+ resolution.explanations.push(
5898
+ `Found ${criticalConflicts.length} potential conflict(s). Installation may fail or cause issues.`
5899
+ );
5900
+ }
5901
+ }
5902
+ } catch (error) {
5903
+ onWarning?.("Could not check for conflicts, proceeding anyway");
5904
+ }
5905
+ }
5906
+ if (dryRun) {
5907
+ return {
5908
+ success: true,
5909
+ resolution,
5910
+ hardware: this.hardware,
5911
+ aiAnalysis
5912
+ };
5427
5913
  }
5428
- if (!existsSync3(targetPath)) {
5914
+ if (!existsSync4(targetPath)) {
5429
5915
  await mkdir3(targetPath, { recursive: true });
5430
5916
  }
5431
- const environmentId = randomUUID2();
5917
+ const environmentId = randomUUID3();
5432
5918
  const environmentName = aiAnalysis?.goal ?? intent.slice(0, 50);
5433
5919
  const environment = {
5434
5920
  id: environmentId,
@@ -5591,127 +6077,2618 @@ var GpuTypeSchema = external_exports.enum(["nvidia", "amd", "apple-silicon", "in
5591
6077
  var OsSchema = external_exports.enum(["darwin", "linux", "windows"]);
5592
6078
  var ArchSchema = external_exports.enum(["x64", "arm64"]);
5593
6079
  var RuntimeSchema = external_exports.enum(["python", "node"]);
5594
- var KNOWN_CONFLICTS = [
5595
- {
5596
- packages: ["tensorflow", "torch"],
5597
- reason: "Both use significant GPU memory and may conflict",
5598
- suggestion: "Use separate environments for TensorFlow and PyTorch projects"
5599
- },
5600
- {
5601
- packages: ["numpy", "jax"],
5602
- reason: "JAX includes its own numpy-like API that may conflict",
5603
- suggestion: "Use jax.numpy instead of importing both"
5604
- }
6080
+ var KNOWN_VULNERABILITIES = {
6081
+ "torch": [
6082
+ { versions: "<1.13.0", severity: "high", description: "Arbitrary code execution via pickle deserialization", fix: ">=2.0.0" }
6083
+ ],
6084
+ "numpy": [
6085
+ { versions: "<1.22.0", severity: "medium", description: "Buffer overflow in array operations", fix: ">=1.22.0" }
6086
+ ],
6087
+ "pillow": [
6088
+ { versions: "<9.0.0", severity: "high", description: "Multiple image parsing vulnerabilities", fix: ">=10.0.0" }
6089
+ ],
6090
+ "requests": [
6091
+ { versions: "<2.31.0", severity: "medium", description: "Potential credential leak in redirects", fix: ">=2.31.0" }
6092
+ ],
6093
+ "urllib3": [
6094
+ { versions: "<2.0.0", severity: "medium", description: "Cookie handling vulnerability", fix: ">=2.0.0" }
6095
+ ],
6096
+ "django": [
6097
+ { versions: "<4.2.0", severity: "high", description: "SQL injection and XSS vulnerabilities", fix: ">=4.2.0" }
6098
+ ],
6099
+ "flask": [
6100
+ { versions: "<2.3.0", severity: "medium", description: "Security header improvements needed", fix: ">=2.3.0" }
6101
+ ],
6102
+ "lodash": [
6103
+ { versions: "<4.17.21", severity: "high", description: "Prototype pollution vulnerability", fix: ">=4.17.21" }
6104
+ ],
6105
+ "axios": [
6106
+ { versions: "<1.6.0", severity: "medium", description: "SSRF vulnerability", fix: ">=1.6.0" }
6107
+ ],
6108
+ "express": [
6109
+ { versions: "<4.19.0", severity: "medium", description: "Open redirect vulnerability", fix: ">=4.19.0" }
6110
+ ]
6111
+ };
6112
+ var HEAVY_PACKAGES = {
6113
+ "moment": { alternative: "dayjs", savingsMb: 280, note: "dayjs is 2KB vs moment's 300KB, same API" },
6114
+ "lodash": { alternative: "lodash-es or native", savingsMb: 70, note: "Use lodash-es for tree-shaking or native JS methods" },
6115
+ "jquery": { alternative: "native DOM APIs", savingsMb: 85, note: "Modern browsers don't need jQuery" },
6116
+ "underscore": { alternative: "lodash-es or native", savingsMb: 20, note: "lodash is a superset with better performance" },
6117
+ "request": { alternative: "axios or fetch", savingsMb: 50, note: "request is deprecated, use axios or native fetch" },
6118
+ "bluebird": { alternative: "native Promises", savingsMb: 80, note: "Native Promises are now performant enough" }
6119
+ };
6120
+ var CONFLICT_PAIRS = [
6121
+ ["tensorflow", "torch", "Both are heavy ML frameworks - pick one unless you need both"],
6122
+ ["tensorflow-gpu", "tensorflow", "Use tensorflow>=2.0 which includes GPU support"],
6123
+ ["opencv-python", "opencv-python-headless", "Use headless version for servers"],
6124
+ ["pillow", "PIL", "PIL is deprecated, Pillow is the maintained fork"]
5605
6125
  ];
5606
- async function getInstalledPackages(path, runtime) {
5607
- if (runtime === "python") {
5608
- return getPythonPackages(path);
5609
- } else {
5610
- return getNodePackages(path);
6126
+ function compareVersions(v1, v2) {
6127
+ const parts1 = v1.replace(/[^0-9.]/g, "").split(".").map(Number);
6128
+ const parts2 = v2.replace(/[^0-9.]/g, "").split(".").map(Number);
6129
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
6130
+ const p1 = parts1[i] || 0;
6131
+ const p2 = parts2[i] || 0;
6132
+ if (p1 < p2) return -1;
6133
+ if (p1 > p2) return 1;
6134
+ }
6135
+ return 0;
6136
+ }
6137
+ function checkVersionConstraint(version, constraint) {
6138
+ const cleanVersion = version.replace(/[^0-9.]/g, "");
6139
+ if (constraint.startsWith("<")) {
6140
+ const targetVersion = constraint.slice(1);
6141
+ return compareVersions(cleanVersion, targetVersion) < 0;
6142
+ }
6143
+ if (constraint.startsWith(">=")) {
6144
+ const targetVersion = constraint.slice(2);
6145
+ return compareVersions(cleanVersion, targetVersion) >= 0;
6146
+ }
6147
+ if (constraint.startsWith("<=")) {
6148
+ const targetVersion = constraint.slice(2);
6149
+ return compareVersions(cleanVersion, targetVersion) <= 0;
6150
+ }
6151
+ if (constraint.startsWith(">")) {
6152
+ const targetVersion = constraint.slice(1);
6153
+ return compareVersions(cleanVersion, targetVersion) > 0;
5611
6154
  }
6155
+ return false;
5612
6156
  }
5613
- async function getPythonPackages(path) {
5614
- const venvPath = join5(path, ".venv");
5615
- if (!existsSync4(venvPath)) {
5616
- return [];
6157
+ function runDoctor(packages, hardware, options = {}) {
6158
+ const {
6159
+ checkSecurity = true,
6160
+ checkPerformance = true,
6161
+ checkOutdated = true,
6162
+ checkUnused = true
6163
+ } = options;
6164
+ const issues = [];
6165
+ let vulnerabilityCount = 0;
6166
+ let outdatedCount = 0;
6167
+ let unusedCount = 0;
6168
+ if (checkSecurity) {
6169
+ for (const pkg of packages) {
6170
+ const vulns = KNOWN_VULNERABILITIES[pkg.name.toLowerCase()];
6171
+ if (vulns) {
6172
+ for (const vuln of vulns) {
6173
+ if (checkVersionConstraint(pkg.version, vuln.versions)) {
6174
+ vulnerabilityCount++;
6175
+ issues.push({
6176
+ id: `vuln-${pkg.name}-${vuln.severity}`,
6177
+ severity: vuln.severity === "high" ? "critical" : "warning",
6178
+ category: "security",
6179
+ title: `Security vulnerability in ${pkg.name}`,
6180
+ description: vuln.description,
6181
+ package: pkg.name,
6182
+ fix: {
6183
+ type: "upgrade",
6184
+ packages: [`${pkg.name}${vuln.fix}`],
6185
+ description: `Upgrade to ${pkg.name}${vuln.fix}`,
6186
+ automated: true
6187
+ },
6188
+ learnMore: `https://security.snyk.io/package/pip/${pkg.name}`
6189
+ });
6190
+ }
6191
+ }
6192
+ }
6193
+ }
5617
6194
  }
5618
- try {
5619
- const pipPath = join5(venvPath, "bin", "pip");
5620
- const result = await execa3(pipPath, ["list", "--format=json"], { cwd: path });
5621
- const packages = JSON.parse(result.stdout);
5622
- return packages.map((pkg) => ({
5623
- name: pkg.name,
5624
- version: pkg.version
5625
- }));
5626
- } catch {
5627
- try {
5628
- const result = await execa3(
5629
- "uv",
5630
- ["pip", "list", "--format=json", "--python", join5(venvPath, "bin", "python")],
5631
- { cwd: path }
6195
+ if (checkPerformance) {
6196
+ for (const pkg of packages) {
6197
+ const heavy = HEAVY_PACKAGES[pkg.name.toLowerCase()];
6198
+ if (heavy) {
6199
+ issues.push({
6200
+ id: `perf-${pkg.name}`,
6201
+ severity: "suggestion",
6202
+ category: "performance",
6203
+ title: `${pkg.name} has a lighter alternative`,
6204
+ description: heavy.note,
6205
+ package: pkg.name,
6206
+ fix: {
6207
+ type: "install",
6208
+ packages: [heavy.alternative.split(" ")[0] || heavy.alternative],
6209
+ description: `Consider replacing with ${heavy.alternative} (saves ~${heavy.savingsMb}KB)`,
6210
+ automated: false
6211
+ }
6212
+ });
6213
+ }
6214
+ }
6215
+ const packageNames = packages.map((p) => p.name.toLowerCase());
6216
+ for (const [pkg1, pkg2, reason] of CONFLICT_PAIRS) {
6217
+ if (packageNames.includes(pkg1) && packageNames.includes(pkg2)) {
6218
+ issues.push({
6219
+ id: `dup-${pkg1}-${pkg2}`,
6220
+ severity: "warning",
6221
+ category: "performance",
6222
+ title: `Potentially redundant packages: ${pkg1} + ${pkg2}`,
6223
+ description: reason,
6224
+ fix: {
6225
+ type: "uninstall",
6226
+ description: `Consider removing one of these packages`,
6227
+ automated: false
6228
+ }
6229
+ });
6230
+ }
6231
+ }
6232
+ if (hardware.gpu === "none") {
6233
+ const gpuPackages = packages.filter(
6234
+ (p) => p.name.toLowerCase().includes("cuda") || p.name.toLowerCase().includes("-gpu") || p.name.toLowerCase().includes("cudf")
5632
6235
  );
5633
- const packages = JSON.parse(result.stdout);
5634
- return packages.map((pkg) => ({
5635
- name: pkg.name,
5636
- version: pkg.version
5637
- }));
5638
- } catch {
5639
- return [];
6236
+ for (const pkg of gpuPackages) {
6237
+ issues.push({
6238
+ id: `hw-${pkg.name}`,
6239
+ severity: "warning",
6240
+ category: "compatibility",
6241
+ title: `GPU package on CPU-only system`,
6242
+ description: `${pkg.name} requires a GPU but none was detected`,
6243
+ package: pkg.name,
6244
+ fix: {
6245
+ type: "uninstall",
6246
+ packages: [pkg.name],
6247
+ description: `Remove ${pkg.name} or switch to CPU version`,
6248
+ automated: true
6249
+ }
6250
+ });
6251
+ }
6252
+ }
6253
+ if (hardware.totalMemoryGb < 8) {
6254
+ const heavyMLPackages = packages.filter(
6255
+ (p) => ["tensorflow", "torch", "pytorch", "transformers", "jax"].includes(p.name.toLowerCase())
6256
+ );
6257
+ if (heavyMLPackages.length > 0) {
6258
+ issues.push({
6259
+ id: "hw-memory-ml",
6260
+ severity: "warning",
6261
+ category: "compatibility",
6262
+ title: "Heavy ML packages with limited RAM",
6263
+ description: `You have ${hardware.totalMemoryGb}GB RAM but ML packages typically need 8GB+`,
6264
+ fix: {
6265
+ type: "config",
6266
+ description: "Consider using cloud instances or reducing model sizes",
6267
+ automated: false
6268
+ }
6269
+ });
6270
+ }
5640
6271
  }
5641
6272
  }
5642
- }
5643
- async function getNodePackages(path) {
5644
- const packageJsonPath = join5(path, "package.json");
5645
- if (!existsSync4(packageJsonPath)) {
5646
- return [];
6273
+ const conflicts = detectConflicts(packages);
6274
+ for (const conflict of conflicts) {
6275
+ issues.push({
6276
+ id: `conflict-${conflict.package1}-${conflict.package2}`,
6277
+ severity: conflict.severity === "error" ? "critical" : "warning",
6278
+ category: "compatibility",
6279
+ title: `Conflict: ${conflict.package1} and ${conflict.package2}`,
6280
+ description: conflict.reason,
6281
+ fix: conflict.suggestion ? {
6282
+ type: "command",
6283
+ command: conflict.suggestion,
6284
+ description: conflict.suggestion,
6285
+ automated: false
6286
+ } : void 0
6287
+ });
5647
6288
  }
5648
- try {
5649
- const content = await readFile4(packageJsonPath, "utf-8");
5650
- const packageJson = JSON.parse(content);
5651
- const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
5652
- const packages = [];
5653
- for (const [name, version] of Object.entries(deps)) {
5654
- packages.push({
5655
- name,
5656
- version: String(version).replace(/^[\^~]/, "")
5657
- });
6289
+ if (checkOutdated) {
6290
+ const oldVersionIndicators = ["0.", "1.0", "1.1", "1.2"];
6291
+ for (const pkg of packages) {
6292
+ if (oldVersionIndicators.some((v) => pkg.version.startsWith(v))) {
6293
+ outdatedCount++;
6294
+ issues.push({
6295
+ id: `outdated-${pkg.name}`,
6296
+ severity: "info",
6297
+ category: "outdated",
6298
+ title: `${pkg.name} may be outdated`,
6299
+ description: `Version ${pkg.version} - consider checking for updates`,
6300
+ package: pkg.name,
6301
+ fix: {
6302
+ type: "upgrade",
6303
+ packages: [pkg.name],
6304
+ description: `Run: pip install --upgrade ${pkg.name}`,
6305
+ automated: true
6306
+ }
6307
+ });
6308
+ }
5658
6309
  }
5659
- return packages;
5660
- } catch {
5661
- return [];
5662
6310
  }
6311
+ const criticalCount = issues.filter((i) => i.severity === "critical").length;
6312
+ const warningCount = issues.filter((i) => i.severity === "warning").length;
6313
+ const suggestionCount = issues.filter((i) => i.severity === "suggestion" || i.severity === "info").length;
6314
+ let score = 100;
6315
+ score -= criticalCount * 20;
6316
+ score -= warningCount * 10;
6317
+ score -= suggestionCount * 2;
6318
+ score = Math.max(0, Math.min(100, score));
6319
+ const grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 60 ? "D" : "F";
6320
+ const recommendations = [];
6321
+ if (vulnerabilityCount > 0) {
6322
+ recommendations.push(`Fix ${vulnerabilityCount} security vulnerabilities immediately`);
6323
+ }
6324
+ if (criticalCount > 0) {
6325
+ recommendations.push(`Address ${criticalCount} critical issues`);
6326
+ }
6327
+ if (hardware.gpu !== "none" && !packages.some((p) => p.name.toLowerCase().includes("cuda"))) {
6328
+ recommendations.push("You have a GPU - consider installing CUDA packages for acceleration");
6329
+ }
6330
+ if (packages.length > 50) {
6331
+ recommendations.push("Large number of packages - consider auditing for unused dependencies");
6332
+ }
6333
+ return {
6334
+ timestamp: /* @__PURE__ */ new Date(),
6335
+ score,
6336
+ grade,
6337
+ issues,
6338
+ summary: {
6339
+ critical: criticalCount,
6340
+ warnings: warningCount,
6341
+ suggestions: suggestionCount,
6342
+ packagesScanned: packages.length,
6343
+ vulnerabilities: vulnerabilityCount,
6344
+ outdated: outdatedCount,
6345
+ unused: unusedCount
6346
+ },
6347
+ hardware,
6348
+ recommendations
6349
+ };
5663
6350
  }
5664
- function detectConflicts(packages) {
5665
- const conflicts = [];
5666
- const packageNames = packages.map((p) => p.name.toLowerCase());
5667
- for (const known of KNOWN_CONFLICTS) {
5668
- const [pkg1, pkg2] = known.packages;
5669
- if (packageNames.includes(pkg1) && packageNames.includes(pkg2)) {
5670
- conflicts.push({
5671
- package1: pkg1,
5672
- package2: pkg2,
5673
- reason: known.reason,
5674
- severity: "warning",
5675
- suggestion: known.suggestion
5676
- });
6351
+ function formatDoctorReport(report, colorize = true) {
6352
+ const lines = [];
6353
+ const colors = {
6354
+ reset: colorize ? "\x1B[0m" : "",
6355
+ red: colorize ? "\x1B[31m" : "",
6356
+ yellow: colorize ? "\x1B[33m" : "",
6357
+ green: colorize ? "\x1B[32m" : "",
6358
+ blue: colorize ? "\x1B[34m" : "",
6359
+ cyan: colorize ? "\x1B[36m" : "",
6360
+ bold: colorize ? "\x1B[1m" : "",
6361
+ dim: colorize ? "\x1B[2m" : ""
6362
+ };
6363
+ lines.push("");
6364
+ lines.push(`${colors.bold}Komodo Doctor Report${colors.reset}`);
6365
+ lines.push(`${colors.dim}${report.timestamp.toLocaleString()}${colors.reset}`);
6366
+ lines.push("");
6367
+ const gradeColor = report.grade === "A" ? colors.green : report.grade === "B" ? colors.green : report.grade === "C" ? colors.yellow : colors.red;
6368
+ lines.push(`${colors.bold}Health Score: ${gradeColor}${report.score}/100 (${report.grade})${colors.reset}`);
6369
+ lines.push("");
6370
+ lines.push(`${colors.bold}Summary${colors.reset}`);
6371
+ lines.push(` Packages scanned: ${report.summary.packagesScanned}`);
6372
+ if (report.summary.critical > 0) {
6373
+ lines.push(` ${colors.red}Critical issues: ${report.summary.critical}${colors.reset}`);
6374
+ }
6375
+ if (report.summary.warnings > 0) {
6376
+ lines.push(` ${colors.yellow}Warnings: ${report.summary.warnings}${colors.reset}`);
6377
+ }
6378
+ if (report.summary.vulnerabilities > 0) {
6379
+ lines.push(` ${colors.red}Vulnerabilities: ${report.summary.vulnerabilities}${colors.reset}`);
6380
+ }
6381
+ lines.push(` Suggestions: ${report.summary.suggestions}`);
6382
+ lines.push("");
6383
+ const criticalIssues = report.issues.filter((i) => i.severity === "critical");
6384
+ const warningIssues = report.issues.filter((i) => i.severity === "warning");
6385
+ const infoIssues = report.issues.filter((i) => i.severity === "info" || i.severity === "suggestion");
6386
+ if (criticalIssues.length > 0) {
6387
+ lines.push(`${colors.red}${colors.bold}Critical Issues${colors.reset}`);
6388
+ for (const issue of criticalIssues) {
6389
+ lines.push(` ${colors.red}\u2716${colors.reset} ${issue.title}`);
6390
+ lines.push(` ${colors.dim}${issue.description}${colors.reset}`);
6391
+ if (issue.fix) {
6392
+ lines.push(` ${colors.cyan}Fix: ${issue.fix.description}${colors.reset}`);
6393
+ }
5677
6394
  }
6395
+ lines.push("");
5678
6396
  }
5679
- return conflicts;
6397
+ if (warningIssues.length > 0) {
6398
+ lines.push(`${colors.yellow}${colors.bold}Warnings${colors.reset}`);
6399
+ for (const issue of warningIssues) {
6400
+ lines.push(` ${colors.yellow}\u26A0${colors.reset} ${issue.title}`);
6401
+ lines.push(` ${colors.dim}${issue.description}${colors.reset}`);
6402
+ if (issue.fix) {
6403
+ lines.push(` ${colors.cyan}Fix: ${issue.fix.description}${colors.reset}`);
6404
+ }
6405
+ }
6406
+ lines.push("");
6407
+ }
6408
+ if (infoIssues.length > 0) {
6409
+ lines.push(`${colors.blue}${colors.bold}Suggestions${colors.reset}`);
6410
+ for (const issue of infoIssues.slice(0, 5)) {
6411
+ lines.push(` ${colors.blue}\u2139${colors.reset} ${issue.title}`);
6412
+ if (issue.fix) {
6413
+ lines.push(` ${colors.dim}${issue.fix.description}${colors.reset}`);
6414
+ }
6415
+ }
6416
+ if (infoIssues.length > 5) {
6417
+ lines.push(` ${colors.dim}...and ${infoIssues.length - 5} more suggestions${colors.reset}`);
6418
+ }
6419
+ lines.push("");
6420
+ }
6421
+ if (report.recommendations.length > 0) {
6422
+ lines.push(`${colors.bold}Recommendations${colors.reset}`);
6423
+ for (const rec of report.recommendations) {
6424
+ lines.push(` ${colors.green}\u2192${colors.reset} ${rec}`);
6425
+ }
6426
+ lines.push("");
6427
+ }
6428
+ lines.push(`${colors.dim}System: ${report.hardware.os} | ${report.hardware.gpuName || report.hardware.gpu} | ${report.hardware.totalMemoryGb}GB RAM${colors.reset}`);
6429
+ return lines.join("\n");
5680
6430
  }
5681
- function analyzeHealth(packages, conflicts) {
5682
- const issues = [];
5683
- let score = 100;
5684
- for (const conflict of conflicts) {
5685
- issues.push({
5686
- type: "conflict",
5687
- severity: conflict.severity,
5688
- title: `${conflict.package1} and ${conflict.package2} may conflict`,
5689
- description: conflict.reason,
5690
- fix: conflict.suggestion,
5691
- autoFixable: false
6431
+ var COMMON_DEPENDENCIES = {
6432
+ "torch": ["numpy", "typing-extensions", "sympy", "networkx", "jinja2", "filelock"],
6433
+ "torchvision": ["torch", "numpy", "pillow", "requests"],
6434
+ "tensorflow": ["numpy", "protobuf", "absl-py", "grpcio", "h5py", "keras"],
6435
+ "transformers": ["torch", "numpy", "tokenizers", "safetensors", "huggingface-hub", "regex", "requests", "tqdm"],
6436
+ "pandas": ["numpy", "python-dateutil", "pytz", "tzdata"],
6437
+ "numpy": [],
6438
+ "scikit-learn": ["numpy", "scipy", "joblib", "threadpoolctl"],
6439
+ "matplotlib": ["numpy", "pillow", "pyparsing", "cycler", "kiwisolver", "fonttools", "packaging"],
6440
+ "requests": ["urllib3", "certifi", "charset-normalizer", "idna"],
6441
+ "flask": ["werkzeug", "jinja2", "itsdangerous", "click", "blinker"],
6442
+ "django": ["asgiref", "sqlparse", "tzdata"],
6443
+ "fastapi": ["starlette", "pydantic", "typing-extensions"],
6444
+ "react": [],
6445
+ "next": ["react", "react-dom"],
6446
+ "express": [],
6447
+ "axios": [],
6448
+ "lodash": []
6449
+ };
6450
+ function buildDependencyTree(packages) {
6451
+ const nodes = /* @__PURE__ */ new Map();
6452
+ const packageNames = new Set(packages.map((p) => p.name.toLowerCase()));
6453
+ for (const pkg of packages) {
6454
+ const name = pkg.name.toLowerCase();
6455
+ const deps = COMMON_DEPENDENCIES[name] || [];
6456
+ const filteredDeps = deps.filter((d) => packageNames.has(d.toLowerCase()));
6457
+ nodes.set(name, {
6458
+ name: pkg.name,
6459
+ version: pkg.version,
6460
+ depth: 0,
6461
+ dependencies: [],
6462
+ dependents: [],
6463
+ size: estimatePackageSize(pkg.name)
5692
6464
  });
5693
- score -= conflict.severity === "error" ? 20 : 10;
5694
6465
  }
5695
- const commonOutdated = ["requests", "axios", "lodash"];
5696
6466
  for (const pkg of packages) {
5697
- if (commonOutdated.includes(pkg.name.toLowerCase())) {
6467
+ const name = pkg.name.toLowerCase();
6468
+ const node = nodes.get(name);
6469
+ const deps = COMMON_DEPENDENCIES[name] || [];
6470
+ for (const depName of deps) {
6471
+ const depNode = nodes.get(depName.toLowerCase());
6472
+ if (depNode) {
6473
+ node.dependencies.push(depNode);
6474
+ depNode.dependents.push(pkg.name);
6475
+ }
6476
+ }
6477
+ }
6478
+ const rootPackages = [...nodes.values()].filter((n) => n.dependents.length === 0);
6479
+ let maxDepth = 0;
6480
+ function setDepths(node, depth, visited) {
6481
+ if (visited.has(node.name.toLowerCase())) return;
6482
+ visited.add(node.name.toLowerCase());
6483
+ node.depth = Math.max(node.depth, depth);
6484
+ maxDepth = Math.max(maxDepth, depth);
6485
+ for (const dep of node.dependencies) {
6486
+ setDepths(dep, depth + 1, visited);
6487
+ }
6488
+ }
6489
+ for (const root of rootPackages) {
6490
+ setDepths(root, 0, /* @__PURE__ */ new Set());
6491
+ }
6492
+ const conflicts = [];
6493
+ const depVersions = /* @__PURE__ */ new Map();
6494
+ for (const node of nodes.values()) {
6495
+ if (!depVersions.has(node.name)) {
6496
+ depVersions.set(node.name, /* @__PURE__ */ new Set());
5698
6497
  }
6498
+ depVersions.get(node.name).add(node.version);
5699
6499
  }
5700
6500
  return {
5701
- healthy: issues.filter((i) => i.severity === "error").length === 0,
5702
- issues,
5703
- score: Math.max(0, score)
6501
+ root: rootPackages[0]?.name || "project",
6502
+ nodes,
6503
+ totalPackages: packages.length,
6504
+ maxDepth,
6505
+ conflicts
5704
6506
  };
5705
6507
  }
5706
-
5707
- export {
5708
- __require,
5709
- __commonJS,
5710
- __toESM,
5711
- loadState,
5712
- KomodoChat,
5713
- Komodo,
5714
- getInstalledPackages,
5715
- detectConflicts,
5716
- analyzeHealth
6508
+ function estimatePackageSize(name) {
6509
+ const sizes = {
6510
+ "torch": 85e4,
6511
+ "tensorflow": 55e4,
6512
+ "transformers": 45e3,
6513
+ "numpy": 15e3,
6514
+ "pandas": 12e3,
6515
+ "scipy": 35e3,
6516
+ "matplotlib": 8e3,
6517
+ "pillow": 3e3,
6518
+ "scikit-learn": 25e3,
6519
+ "opencv-python": 45e3,
6520
+ "requests": 500,
6521
+ "flask": 800,
6522
+ "django": 8e3,
6523
+ "fastapi": 400,
6524
+ "react": 150,
6525
+ "next": 5e3,
6526
+ "express": 200,
6527
+ "lodash": 1500,
6528
+ "axios": 100
6529
+ };
6530
+ return sizes[name.toLowerCase()] || 500;
6531
+ }
6532
+ function visualizeTree(tree, options = {}) {
6533
+ const { maxDepth = 5, showSizes = true, colorize = true } = options;
6534
+ const colors = {
6535
+ reset: colorize ? "\x1B[0m" : "",
6536
+ red: colorize ? "\x1B[31m" : "",
6537
+ yellow: colorize ? "\x1B[33m" : "",
6538
+ green: colorize ? "\x1B[32m" : "",
6539
+ blue: colorize ? "\x1B[34m" : "",
6540
+ cyan: colorize ? "\x1B[36m" : "",
6541
+ dim: colorize ? "\x1B[2m" : "",
6542
+ bold: colorize ? "\x1B[1m" : ""
6543
+ };
6544
+ const lines = [];
6545
+ const visited = /* @__PURE__ */ new Set();
6546
+ const roots = [...tree.nodes.values()].filter((n) => n.dependents.length === 0);
6547
+ function formatSize(kb) {
6548
+ if (kb >= 1e3) return `${(kb / 1e3).toFixed(1)}MB`;
6549
+ return `${kb}KB`;
6550
+ }
6551
+ function drawNode(node, prefix, isLast, depth) {
6552
+ if (depth > maxDepth) return;
6553
+ if (visited.has(node.name.toLowerCase())) {
6554
+ lines.push(`${prefix}${isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${colors.dim}${node.name} (circular)${colors.reset}`);
6555
+ return;
6556
+ }
6557
+ visited.add(node.name.toLowerCase());
6558
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
6559
+ const sizeStr = showSizes && node.size ? ` ${colors.dim}(${formatSize(node.size)})${colors.reset}` : "";
6560
+ const conflictStr = node.isConflict ? ` ${colors.red}\u26A0 conflicts with ${node.conflictWith}${colors.reset}` : "";
6561
+ const nameColor = node.dependencies.length > 0 ? colors.cyan : colors.green;
6562
+ lines.push(`${prefix}${connector}${nameColor}${node.name}${colors.reset}@${colors.dim}${node.version}${colors.reset}${sizeStr}${conflictStr}`);
6563
+ const newPrefix = prefix + (isLast ? " " : "\u2502 ");
6564
+ const deps = node.dependencies;
6565
+ for (let i = 0; i < deps.length; i++) {
6566
+ const dep = deps[i];
6567
+ if (dep) drawNode(dep, newPrefix, i === deps.length - 1, depth + 1);
6568
+ }
6569
+ }
6570
+ lines.push(`${colors.bold}Dependency Tree${colors.reset}`);
6571
+ lines.push(`${colors.dim}${tree.totalPackages} packages, max depth ${tree.maxDepth}${colors.reset}`);
6572
+ lines.push("");
6573
+ for (let i = 0; i < roots.length; i++) {
6574
+ const root = roots[i];
6575
+ if (!root) continue;
6576
+ lines.push(`${colors.bold}${root.name}${colors.reset}@${root.version}`);
6577
+ const deps = root.dependencies;
6578
+ for (let j = 0; j < deps.length; j++) {
6579
+ const dep = deps[j];
6580
+ if (dep) drawNode(dep, "", j === deps.length - 1, 1);
6581
+ }
6582
+ if (i < roots.length - 1) lines.push("");
6583
+ }
6584
+ const orphans = [...tree.nodes.values()].filter(
6585
+ (n) => n.dependencies.length === 0 && n.dependents.length === 0
6586
+ );
6587
+ if (orphans.length > 0 && orphans.length < tree.totalPackages) {
6588
+ lines.push("");
6589
+ lines.push(`${colors.dim}Standalone packages:${colors.reset}`);
6590
+ for (const orphan of orphans.slice(0, 10)) {
6591
+ lines.push(` ${orphan.name}@${orphan.version}`);
6592
+ }
6593
+ if (orphans.length > 10) {
6594
+ lines.push(` ${colors.dim}...and ${orphans.length - 10} more${colors.reset}`);
6595
+ }
6596
+ }
6597
+ const directDeps = roots.reduce((sum, r) => sum + r.dependencies.length, 0);
6598
+ const transitiveDeps = tree.totalPackages - roots.length - directDeps;
6599
+ const sortedBySize = [...tree.nodes.values()].filter((n) => n.size).sort((a, b) => (b.size || 0) - (a.size || 0)).slice(0, 5);
6600
+ return {
6601
+ ascii: lines.join("\n"),
6602
+ json: {
6603
+ roots: roots.map((r) => ({
6604
+ name: r.name,
6605
+ version: r.version,
6606
+ dependencies: r.dependencies.map((d) => d.name)
6607
+ })),
6608
+ totalPackages: tree.totalPackages,
6609
+ maxDepth: tree.maxDepth
6610
+ },
6611
+ stats: {
6612
+ totalPackages: tree.totalPackages,
6613
+ directDependencies: directDeps,
6614
+ transitiveDependencies: Math.max(0, transitiveDeps),
6615
+ maxDepth: tree.maxDepth,
6616
+ largestPackages: sortedBySize.map((n) => ({ name: n.name, size: n.size || 0 }))
6617
+ }
6618
+ };
6619
+ }
6620
+ function compareVersions2(v1, v2) {
6621
+ const parts1 = v1.replace(/[^0-9.]/g, "").split(".").map(Number);
6622
+ const parts2 = v2.replace(/[^0-9.]/g, "").split(".").map(Number);
6623
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
6624
+ const p1 = parts1[i] || 0;
6625
+ const p2 = parts2[i] || 0;
6626
+ if (p1 < p2) return -1;
6627
+ if (p1 > p2) return 1;
6628
+ }
6629
+ return 0;
6630
+ }
6631
+ function diffPackages(oldPackages, newPackages) {
6632
+ const changes = [];
6633
+ const riskReasons = [];
6634
+ const oldMap = new Map(oldPackages.map((p) => [p.name.toLowerCase(), p]));
6635
+ const newMap = new Map(newPackages.map((p) => [p.name.toLowerCase(), p]));
6636
+ for (const [name, newPkg] of newMap) {
6637
+ const oldPkg = oldMap.get(name);
6638
+ if (!oldPkg) {
6639
+ changes.push({
6640
+ name: newPkg.name,
6641
+ type: "added",
6642
+ newVersion: newPkg.version
6643
+ });
6644
+ } else if (oldPkg.version !== newPkg.version) {
6645
+ const cmp = compareVersions2(oldPkg.version, newPkg.version);
6646
+ changes.push({
6647
+ name: newPkg.name,
6648
+ type: cmp < 0 ? "upgraded" : "downgraded",
6649
+ oldVersion: oldPkg.version,
6650
+ newVersion: newPkg.version
6651
+ });
6652
+ } else {
6653
+ changes.push({
6654
+ name: newPkg.name,
6655
+ type: "unchanged",
6656
+ oldVersion: oldPkg.version,
6657
+ newVersion: newPkg.version
6658
+ });
6659
+ }
6660
+ }
6661
+ for (const [name, oldPkg] of oldMap) {
6662
+ if (!newMap.has(name)) {
6663
+ changes.push({
6664
+ name: oldPkg.name,
6665
+ type: "removed",
6666
+ oldVersion: oldPkg.version
6667
+ });
6668
+ }
6669
+ }
6670
+ const summary = {
6671
+ added: changes.filter((c) => c.type === "added").length,
6672
+ removed: changes.filter((c) => c.type === "removed").length,
6673
+ upgraded: changes.filter((c) => c.type === "upgraded").length,
6674
+ downgraded: changes.filter((c) => c.type === "downgraded").length,
6675
+ unchanged: changes.filter((c) => c.type === "unchanged").length
6676
+ };
6677
+ let riskLevel = "safe";
6678
+ const criticalPackages = ["torch", "tensorflow", "numpy", "pandas", "react", "vue", "angular"];
6679
+ const majorChanges = changes.filter((c) => {
6680
+ if (c.type === "upgraded" || c.type === "downgraded") {
6681
+ const oldMajor = parseInt(c.oldVersion?.split(".")[0] || "0");
6682
+ const newMajor = parseInt(c.newVersion?.split(".")[0] || "0");
6683
+ return oldMajor !== newMajor;
6684
+ }
6685
+ return false;
6686
+ });
6687
+ if (majorChanges.length > 0) {
6688
+ riskLevel = "moderate";
6689
+ for (const change of majorChanges) {
6690
+ riskReasons.push(`Major version change: ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}`);
6691
+ }
6692
+ }
6693
+ const criticalChanges = changes.filter(
6694
+ (c) => criticalPackages.includes(c.name.toLowerCase()) && (c.type === "removed" || c.type === "downgraded")
6695
+ );
6696
+ if (criticalChanges.length > 0) {
6697
+ riskLevel = "risky";
6698
+ for (const change of criticalChanges) {
6699
+ if (change.type === "removed") {
6700
+ riskReasons.push(`Critical package removed: ${change.name}`);
6701
+ } else {
6702
+ riskReasons.push(`Critical package downgraded: ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}`);
6703
+ }
6704
+ }
6705
+ }
6706
+ if (summary.removed > 5) {
6707
+ riskLevel = riskLevel === "safe" ? "moderate" : riskLevel;
6708
+ riskReasons.push(`${summary.removed} packages removed`);
6709
+ }
6710
+ return {
6711
+ from: "old",
6712
+ to: "new",
6713
+ timestamp: /* @__PURE__ */ new Date(),
6714
+ changes: changes.filter((c) => c.type !== "unchanged"),
6715
+ summary,
6716
+ riskLevel,
6717
+ riskReasons
6718
+ };
6719
+ }
6720
+ function formatDiff(diff, colorize = true) {
6721
+ const colors = {
6722
+ reset: colorize ? "\x1B[0m" : "",
6723
+ red: colorize ? "\x1B[31m" : "",
6724
+ yellow: colorize ? "\x1B[33m" : "",
6725
+ green: colorize ? "\x1B[32m" : "",
6726
+ blue: colorize ? "\x1B[34m" : "",
6727
+ cyan: colorize ? "\x1B[36m" : "",
6728
+ dim: colorize ? "\x1B[2m" : "",
6729
+ bold: colorize ? "\x1B[1m" : ""
6730
+ };
6731
+ const lines = [];
6732
+ lines.push(`${colors.bold}Environment Diff${colors.reset}`);
6733
+ lines.push(`${colors.dim}${diff.from} \u2192 ${diff.to}${colors.reset}`);
6734
+ lines.push("");
6735
+ const riskColor = diff.riskLevel === "safe" ? colors.green : diff.riskLevel === "moderate" ? colors.yellow : colors.red;
6736
+ const riskIcon = diff.riskLevel === "safe" ? "\u2713" : diff.riskLevel === "moderate" ? "\u26A0" : "\u2716";
6737
+ lines.push(`${riskColor}${riskIcon} Risk: ${diff.riskLevel.toUpperCase()}${colors.reset}`);
6738
+ if (diff.riskReasons.length > 0) {
6739
+ for (const reason of diff.riskReasons) {
6740
+ lines.push(` ${colors.dim}\u2022 ${reason}${colors.reset}`);
6741
+ }
6742
+ }
6743
+ lines.push("");
6744
+ lines.push(`${colors.bold}Summary${colors.reset}`);
6745
+ if (diff.summary.added > 0) {
6746
+ lines.push(` ${colors.green}+ ${diff.summary.added} added${colors.reset}`);
6747
+ }
6748
+ if (diff.summary.removed > 0) {
6749
+ lines.push(` ${colors.red}- ${diff.summary.removed} removed${colors.reset}`);
6750
+ }
6751
+ if (diff.summary.upgraded > 0) {
6752
+ lines.push(` ${colors.cyan}\u2191 ${diff.summary.upgraded} upgraded${colors.reset}`);
6753
+ }
6754
+ if (diff.summary.downgraded > 0) {
6755
+ lines.push(` ${colors.yellow}\u2193 ${diff.summary.downgraded} downgraded${colors.reset}`);
6756
+ }
6757
+ lines.push(` ${colors.dim}= ${diff.summary.unchanged} unchanged${colors.reset}`);
6758
+ lines.push("");
6759
+ if (diff.changes.length > 0) {
6760
+ lines.push(`${colors.bold}Changes${colors.reset}`);
6761
+ const added = diff.changes.filter((c) => c.type === "added");
6762
+ const removed = diff.changes.filter((c) => c.type === "removed");
6763
+ const upgraded = diff.changes.filter((c) => c.type === "upgraded");
6764
+ const downgraded = diff.changes.filter((c) => c.type === "downgraded");
6765
+ if (added.length > 0) {
6766
+ for (const change of added) {
6767
+ lines.push(` ${colors.green}+ ${change.name}@${change.newVersion}${colors.reset}`);
6768
+ }
6769
+ }
6770
+ if (removed.length > 0) {
6771
+ for (const change of removed) {
6772
+ lines.push(` ${colors.red}- ${change.name}@${change.oldVersion}${colors.reset}`);
6773
+ }
6774
+ }
6775
+ if (upgraded.length > 0) {
6776
+ for (const change of upgraded) {
6777
+ lines.push(` ${colors.cyan}\u2191 ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}${colors.reset}`);
6778
+ }
6779
+ }
6780
+ if (downgraded.length > 0) {
6781
+ for (const change of downgraded) {
6782
+ lines.push(` ${colors.yellow}\u2193 ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}${colors.reset}`);
6783
+ }
6784
+ }
6785
+ } else {
6786
+ lines.push(`${colors.dim}No changes detected${colors.reset}`);
6787
+ }
6788
+ return lines.join("\n");
6789
+ }
6790
+ var PACKAGE_SIZES = {
6791
+ "torch": 85e4,
6792
+ "tensorflow": 55e4,
6793
+ "transformers": 45e3,
6794
+ "numpy": 15e3,
6795
+ "pandas": 12e3,
6796
+ "scipy": 35e3,
6797
+ "matplotlib": 8e3,
6798
+ "pillow": 3e3,
6799
+ "scikit-learn": 25e3,
6800
+ "opencv-python": 45e3,
6801
+ "opencv-python-headless": 25e3,
6802
+ "requests": 500,
6803
+ "flask": 800,
6804
+ "django": 8e3,
6805
+ "fastapi": 400,
6806
+ "moment": 300,
6807
+ "dayjs": 2,
6808
+ "lodash": 1500,
6809
+ "lodash-es": 500,
6810
+ "jquery": 85,
6811
+ "axios": 100,
6812
+ "react": 150,
6813
+ "next": 5e3,
6814
+ "express": 200,
6815
+ "bluebird": 80,
6816
+ "underscore": 20,
6817
+ "request": 500
6818
+ };
6819
+ var ALTERNATIVES = {
6820
+ "moment": { replacement: "dayjs", savingsPercent: 99, note: "Same API, 2KB vs 300KB" },
6821
+ "lodash": { replacement: "lodash-es", savingsPercent: 70, note: "Tree-shakeable, or use native methods" },
6822
+ "request": { replacement: "axios", savingsPercent: 80, note: "request is deprecated" },
6823
+ "bluebird": { replacement: "native Promise", savingsPercent: 100, note: "Native Promises are performant now" },
6824
+ "underscore": { replacement: "lodash-es", savingsPercent: 50, note: "lodash is more complete" },
6825
+ "jquery": { replacement: "native DOM", savingsPercent: 100, note: "Modern browsers don't need jQuery" },
6826
+ "opencv-python": { replacement: "opencv-python-headless", savingsPercent: 45, note: "For servers without display" }
6827
+ };
6828
+ var DUPLICATE_GROUPS = [
6829
+ ["moment", "dayjs", "date-fns", "luxon"],
6830
+ ["lodash", "underscore", "ramda"],
6831
+ ["axios", "got", "node-fetch", "request", "superagent"],
6832
+ ["express", "koa", "fastify", "hapi"],
6833
+ ["tensorflow", "torch", "jax"],
6834
+ ["pytest", "nose", "unittest2"],
6835
+ ["opencv-python", "opencv-python-headless", "opencv-contrib-python"]
6836
+ ];
6837
+ function getPackageSize(name) {
6838
+ return PACKAGE_SIZES[name.toLowerCase()] || 500;
6839
+ }
6840
+ function analyzeOptimizations(packages, hardware, options = {}) {
6841
+ const { aggressive = false, targetSizeReductionPercent = 20 } = options;
6842
+ const suggestions = [];
6843
+ const unusedPackages = [];
6844
+ const duplicateFunctionality = [];
6845
+ const hardwareOptimizations = [];
6846
+ const packageNames = packages.map((p) => p.name.toLowerCase());
6847
+ let currentSizeKb = packages.reduce((sum, p) => sum + getPackageSize(p.name), 0);
6848
+ let potentialSavingsKb = 0;
6849
+ for (const pkg of packages) {
6850
+ const alt = ALTERNATIVES[pkg.name.toLowerCase()];
6851
+ if (alt) {
6852
+ const currentSize = getPackageSize(pkg.name);
6853
+ const savings = Math.floor(currentSize * (alt.savingsPercent / 100));
6854
+ potentialSavingsKb += savings;
6855
+ suggestions.push({
6856
+ type: "replace",
6857
+ package: pkg.name,
6858
+ reason: alt.note,
6859
+ savingsKb: savings,
6860
+ replacement: alt.replacement,
6861
+ command: alt.replacement.includes("native") ? `pip uninstall ${pkg.name}` : `pip uninstall ${pkg.name} && pip install ${alt.replacement}`,
6862
+ confidence: "high",
6863
+ impact: "test-required"
6864
+ });
6865
+ }
6866
+ }
6867
+ for (const group of DUPLICATE_GROUPS) {
6868
+ const installed = group.filter((p) => packageNames.includes(p.toLowerCase()));
6869
+ if (installed.length > 1) {
6870
+ const sorted = installed.sort((a, b) => getPackageSize(a) - getPackageSize(b));
6871
+ const keep = sorted[0];
6872
+ const remove = sorted.slice(1);
6873
+ const savings = remove.reduce((sum, p) => sum + getPackageSize(p), 0);
6874
+ potentialSavingsKb += savings;
6875
+ duplicateFunctionality.push({
6876
+ packages: installed,
6877
+ recommendation: `Keep ${keep}, consider removing: ${remove.join(", ")}`
6878
+ });
6879
+ for (const pkg of remove) {
6880
+ suggestions.push({
6881
+ type: "remove",
6882
+ package: pkg,
6883
+ reason: `Duplicate functionality with ${keep}`,
6884
+ savingsKb: getPackageSize(pkg),
6885
+ command: `pip uninstall ${pkg}`,
6886
+ confidence: "medium",
6887
+ impact: "test-required"
6888
+ });
6889
+ }
6890
+ }
6891
+ }
6892
+ if (hardware.gpu === "none") {
6893
+ const gpuPackages = packages.filter(
6894
+ (p) => p.name.toLowerCase().includes("cuda") || p.name.toLowerCase().includes("-gpu") || p.name.toLowerCase() === "tensorflow-gpu"
6895
+ );
6896
+ for (const pkg of gpuPackages) {
6897
+ const size = getPackageSize(pkg.name);
6898
+ potentialSavingsKb += size;
6899
+ suggestions.push({
6900
+ type: "remove",
6901
+ package: pkg.name,
6902
+ reason: "GPU package on CPU-only system",
6903
+ savingsKb: size,
6904
+ command: `pip uninstall ${pkg.name}`,
6905
+ confidence: "high",
6906
+ impact: "safe"
6907
+ });
6908
+ hardwareOptimizations.push(`Remove ${pkg.name} - no GPU detected`);
6909
+ }
6910
+ if (packageNames.includes("torch") && hardware.totalMemoryGb < 8) {
6911
+ hardwareOptimizations.push("Consider using torch CPU-only build with reduced memory footprint");
6912
+ }
6913
+ }
6914
+ if (hardware.totalMemoryGb < 8) {
6915
+ const heavyPackages = packages.filter(
6916
+ (p) => getPackageSize(p.name) > 1e5
6917
+ );
6918
+ if (heavyPackages.length > 2) {
6919
+ hardwareOptimizations.push(
6920
+ `System has ${hardware.totalMemoryGb}GB RAM - consider reducing number of heavy ML frameworks`
6921
+ );
6922
+ }
6923
+ }
6924
+ const likelyDevPackages = packages.filter(
6925
+ (p) => p.name.toLowerCase().includes("test") || p.name.toLowerCase().includes("lint") || p.name.toLowerCase().includes("format") || p.name.toLowerCase().includes("debug") || ["pytest", "black", "flake8", "mypy", "pylint", "isort", "coverage", "tox"].includes(p.name.toLowerCase())
6926
+ );
6927
+ if (likelyDevPackages.length > 0 && aggressive) {
6928
+ for (const pkg of likelyDevPackages) {
6929
+ suggestions.push({
6930
+ type: "remove",
6931
+ package: pkg.name,
6932
+ reason: "Likely dev dependency - remove in production",
6933
+ savingsKb: getPackageSize(pkg.name),
6934
+ command: `pip uninstall ${pkg.name}`,
6935
+ confidence: "low",
6936
+ impact: "safe"
6937
+ });
6938
+ }
6939
+ }
6940
+ suggestions.sort((a, b) => b.savingsKb - a.savingsKb);
6941
+ return {
6942
+ currentSizeKb,
6943
+ potentialSavingsKb,
6944
+ suggestions,
6945
+ unusedPackages,
6946
+ duplicateFunctionality,
6947
+ hardwareOptimizations
6948
+ };
6949
+ }
6950
+ function formatOptimizationReport(report, colorize = true) {
6951
+ const colors = {
6952
+ reset: colorize ? "\x1B[0m" : "",
6953
+ red: colorize ? "\x1B[31m" : "",
6954
+ yellow: colorize ? "\x1B[33m" : "",
6955
+ green: colorize ? "\x1B[32m" : "",
6956
+ blue: colorize ? "\x1B[34m" : "",
6957
+ cyan: colorize ? "\x1B[36m" : "",
6958
+ dim: colorize ? "\x1B[2m" : "",
6959
+ bold: colorize ? "\x1B[1m" : ""
6960
+ };
6961
+ const lines = [];
6962
+ function formatSize(kb) {
6963
+ if (kb >= 1e3) return `${(kb / 1e3).toFixed(1)}MB`;
6964
+ return `${kb}KB`;
6965
+ }
6966
+ lines.push(`${colors.bold}Komodo Optimizer Report${colors.reset}`);
6967
+ lines.push("");
6968
+ const savingsPercent = (report.potentialSavingsKb / report.currentSizeKb * 100).toFixed(1);
6969
+ lines.push(`${colors.bold}Size Analysis${colors.reset}`);
6970
+ lines.push(` Current size: ${formatSize(report.currentSizeKb)}`);
6971
+ lines.push(` ${colors.green}Potential savings: ${formatSize(report.potentialSavingsKb)} (${savingsPercent}%)${colors.reset}`);
6972
+ lines.push("");
6973
+ if (report.hardwareOptimizations.length > 0) {
6974
+ lines.push(`${colors.bold}Hardware Optimizations${colors.reset}`);
6975
+ for (const opt of report.hardwareOptimizations) {
6976
+ lines.push(` ${colors.cyan}\u2192${colors.reset} ${opt}`);
6977
+ }
6978
+ lines.push("");
6979
+ }
6980
+ if (report.duplicateFunctionality.length > 0) {
6981
+ lines.push(`${colors.yellow}${colors.bold}Duplicate Functionality Detected${colors.reset}`);
6982
+ for (const dup of report.duplicateFunctionality) {
6983
+ lines.push(` ${colors.yellow}\u26A0${colors.reset} ${dup.packages.join(", ")}`);
6984
+ lines.push(` ${colors.dim}${dup.recommendation}${colors.reset}`);
6985
+ }
6986
+ lines.push("");
6987
+ }
6988
+ if (report.suggestions.length > 0) {
6989
+ lines.push(`${colors.bold}Optimization Suggestions${colors.reset}`);
6990
+ lines.push("");
6991
+ const highConf = report.suggestions.filter((s) => s.confidence === "high");
6992
+ const medConf = report.suggestions.filter((s) => s.confidence === "medium");
6993
+ const lowConf = report.suggestions.filter((s) => s.confidence === "low");
6994
+ if (highConf.length > 0) {
6995
+ lines.push(`${colors.green}High Confidence${colors.reset}`);
6996
+ for (const sug of highConf.slice(0, 5)) {
6997
+ const impactColor = sug.impact === "safe" ? colors.green : sug.impact === "test-required" ? colors.yellow : colors.red;
6998
+ lines.push(` ${sug.type.toUpperCase()} ${colors.bold}${sug.package}${colors.reset}`);
6999
+ lines.push(` ${colors.dim}${sug.reason}${colors.reset}`);
7000
+ lines.push(` Saves: ${colors.green}${formatSize(sug.savingsKb)}${colors.reset} | Impact: ${impactColor}${sug.impact}${colors.reset}`);
7001
+ lines.push(` ${colors.cyan}$ ${sug.command}${colors.reset}`);
7002
+ lines.push("");
7003
+ }
7004
+ }
7005
+ if (medConf.length > 0 && highConf.length < 5) {
7006
+ lines.push(`${colors.yellow}Medium Confidence${colors.reset}`);
7007
+ for (const sug of medConf.slice(0, 3)) {
7008
+ lines.push(` ${sug.type.toUpperCase()} ${colors.bold}${sug.package}${colors.reset}`);
7009
+ lines.push(` ${colors.dim}${sug.reason}${colors.reset}`);
7010
+ lines.push(` Saves: ${colors.green}${formatSize(sug.savingsKb)}${colors.reset}`);
7011
+ lines.push("");
7012
+ }
7013
+ }
7014
+ if (report.suggestions.length > 8) {
7015
+ lines.push(`${colors.dim}...and ${report.suggestions.length - 8} more suggestions${colors.reset}`);
7016
+ }
7017
+ } else {
7018
+ lines.push(`${colors.green}\u2713 No major optimization opportunities found${colors.reset}`);
7019
+ }
7020
+ return lines.join("\n");
7021
+ }
7022
+ function generateOptimizationScript(suggestions, safeOnly = true) {
7023
+ const filtered = safeOnly ? suggestions.filter((s) => s.impact === "safe" && s.confidence === "high") : suggestions;
7024
+ if (filtered.length === 0) {
7025
+ return "# No safe optimizations to apply automatically";
7026
+ }
7027
+ const lines = [
7028
+ "#!/bin/bash",
7029
+ "# Komodo Optimization Script",
7030
+ `# Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
7031
+ "",
7032
+ "set -e",
7033
+ ""
7034
+ ];
7035
+ for (const sug of filtered) {
7036
+ lines.push(`# ${sug.type}: ${sug.package} (saves ${Math.round(sug.savingsKb / 1e3)}MB)`);
7037
+ lines.push(`# Reason: ${sug.reason}`);
7038
+ lines.push(sug.command);
7039
+ lines.push("");
7040
+ }
7041
+ lines.push('echo "Optimization complete!"');
7042
+ return lines.join("\n");
7043
+ }
7044
+ var TEMPLATES = [
7045
+ // ML/AI Templates
7046
+ {
7047
+ id: "pytorch-basic",
7048
+ name: "PyTorch Starter",
7049
+ description: "Basic PyTorch setup for deep learning",
7050
+ category: "ml",
7051
+ runtime: "python",
7052
+ packages: [
7053
+ { name: "torch", note: "Core PyTorch framework" },
7054
+ { name: "torchvision", note: "Image datasets and transforms" },
7055
+ { name: "numpy", note: "Numerical computing" },
7056
+ { name: "matplotlib", optional: true, note: "Visualization" },
7057
+ { name: "tqdm", optional: true, note: "Progress bars" }
7058
+ ],
7059
+ minMemoryGb: 8,
7060
+ tags: ["pytorch", "deep-learning", "ai", "neural-networks"]
7061
+ },
7062
+ {
7063
+ id: "pytorch-nlp",
7064
+ name: "PyTorch NLP",
7065
+ description: "Natural Language Processing with PyTorch and Transformers",
7066
+ category: "ml",
7067
+ runtime: "python",
7068
+ packages: [
7069
+ { name: "torch" },
7070
+ { name: "transformers", note: "Hugging Face Transformers" },
7071
+ { name: "tokenizers", note: "Fast tokenization" },
7072
+ { name: "datasets", note: "Hugging Face datasets" },
7073
+ { name: "sentencepiece", optional: true },
7074
+ { name: "sacremoses", optional: true }
7075
+ ],
7076
+ minMemoryGb: 16,
7077
+ tags: ["nlp", "transformers", "bert", "gpt", "language-models"]
7078
+ },
7079
+ {
7080
+ id: "tensorflow-basic",
7081
+ name: "TensorFlow Starter",
7082
+ description: "TensorFlow 2.x for machine learning",
7083
+ category: "ml",
7084
+ runtime: "python",
7085
+ packages: [
7086
+ { name: "tensorflow", note: "TensorFlow 2.x with Keras" },
7087
+ { name: "numpy" },
7088
+ { name: "pandas", optional: true },
7089
+ { name: "matplotlib", optional: true },
7090
+ { name: "tensorboard", optional: true, note: "Training visualization" }
7091
+ ],
7092
+ minMemoryGb: 8,
7093
+ tags: ["tensorflow", "keras", "deep-learning", "ai"]
7094
+ },
7095
+ {
7096
+ id: "computer-vision",
7097
+ name: "Computer Vision",
7098
+ description: "Image processing and computer vision",
7099
+ category: "ml",
7100
+ runtime: "python",
7101
+ packages: [
7102
+ { name: "torch" },
7103
+ { name: "torchvision" },
7104
+ { name: "opencv-python-headless", note: "OpenCV for servers" },
7105
+ { name: "pillow", note: "Image processing" },
7106
+ { name: "albumentations", optional: true, note: "Image augmentation" },
7107
+ { name: "timm", optional: true, note: "PyTorch Image Models" }
7108
+ ],
7109
+ minMemoryGb: 8,
7110
+ requiresGpu: true,
7111
+ tags: ["vision", "images", "opencv", "cnn"]
7112
+ },
7113
+ {
7114
+ id: "llm-local",
7115
+ name: "Local LLM",
7116
+ description: "Run large language models locally with llama.cpp",
7117
+ category: "ml",
7118
+ runtime: "python",
7119
+ packages: [
7120
+ { name: "llama-cpp-python", note: "llama.cpp Python bindings" },
7121
+ { name: "huggingface-hub", note: "Download models" },
7122
+ { name: "gradio", optional: true, note: "Web UI" }
7123
+ ],
7124
+ minMemoryGb: 16,
7125
+ tags: ["llm", "llama", "local-ai", "chatbot"]
7126
+ },
7127
+ // Data Science Templates
7128
+ {
7129
+ id: "data-analysis",
7130
+ name: "Data Analysis",
7131
+ description: "Data analysis and visualization toolkit",
7132
+ category: "data",
7133
+ runtime: "python",
7134
+ packages: [
7135
+ { name: "pandas", note: "Data manipulation" },
7136
+ { name: "numpy", note: "Numerical computing" },
7137
+ { name: "matplotlib", note: "Basic plotting" },
7138
+ { name: "seaborn", note: "Statistical visualization" },
7139
+ { name: "scipy", optional: true, note: "Scientific computing" },
7140
+ { name: "jupyter", optional: true, note: "Interactive notebooks" }
7141
+ ],
7142
+ tags: ["data", "analytics", "visualization", "pandas"]
7143
+ },
7144
+ {
7145
+ id: "data-engineering",
7146
+ name: "Data Engineering",
7147
+ description: "Data pipelines and processing",
7148
+ category: "data",
7149
+ runtime: "python",
7150
+ packages: [
7151
+ { name: "pandas" },
7152
+ { name: "polars", note: "Fast DataFrames" },
7153
+ { name: "pyarrow", note: "Columnar data format" },
7154
+ { name: "duckdb", note: "Embedded analytics DB" },
7155
+ { name: "sqlalchemy", note: "Database toolkit" },
7156
+ { name: "boto3", optional: true, note: "AWS SDK" }
7157
+ ],
7158
+ tags: ["etl", "pipelines", "databases", "big-data"]
7159
+ },
7160
+ // Web Development Templates
7161
+ {
7162
+ id: "nextjs-app",
7163
+ name: "Next.js App",
7164
+ description: "Modern React with Next.js",
7165
+ category: "web",
7166
+ runtime: "node",
7167
+ packages: [
7168
+ { name: "next", note: "Next.js framework" },
7169
+ { name: "react" },
7170
+ { name: "react-dom" },
7171
+ { name: "typescript", optional: true },
7172
+ { name: "tailwindcss", optional: true, note: "Utility CSS" },
7173
+ { name: "@types/react", optional: true }
7174
+ ],
7175
+ setupCommands: ["npx create-next-app@latest"],
7176
+ tags: ["react", "nextjs", "frontend", "ssr"]
7177
+ },
7178
+ {
7179
+ id: "react-spa",
7180
+ name: "React SPA",
7181
+ description: "Single-page React application with Vite",
7182
+ category: "web",
7183
+ runtime: "node",
7184
+ packages: [
7185
+ { name: "react" },
7186
+ { name: "react-dom" },
7187
+ { name: "vite", note: "Fast build tool" },
7188
+ { name: "react-router-dom", optional: true },
7189
+ { name: "zustand", optional: true, note: "State management" }
7190
+ ],
7191
+ setupCommands: ["npm create vite@latest -- --template react-ts"],
7192
+ tags: ["react", "spa", "vite", "frontend"]
7193
+ },
7194
+ // API Templates
7195
+ {
7196
+ id: "fastapi",
7197
+ name: "FastAPI",
7198
+ description: "Modern Python API with FastAPI",
7199
+ category: "api",
7200
+ runtime: "python",
7201
+ packages: [
7202
+ { name: "fastapi", note: "Web framework" },
7203
+ { name: "uvicorn", note: "ASGI server" },
7204
+ { name: "pydantic", note: "Data validation" },
7205
+ { name: "python-multipart", optional: true, note: "Form data" },
7206
+ { name: "sqlalchemy", optional: true, note: "Database ORM" },
7207
+ { name: "httpx", optional: true, note: "HTTP client" }
7208
+ ],
7209
+ tags: ["api", "rest", "async", "python"]
7210
+ },
7211
+ {
7212
+ id: "flask-api",
7213
+ name: "Flask API",
7214
+ description: "Lightweight Python web API",
7215
+ category: "api",
7216
+ runtime: "python",
7217
+ packages: [
7218
+ { name: "flask", note: "Web framework" },
7219
+ { name: "flask-cors", optional: true },
7220
+ { name: "flask-sqlalchemy", optional: true },
7221
+ { name: "gunicorn", note: "Production server" },
7222
+ { name: "python-dotenv", optional: true }
7223
+ ],
7224
+ tags: ["api", "flask", "rest", "python"]
7225
+ },
7226
+ {
7227
+ id: "express-api",
7228
+ name: "Express API",
7229
+ description: "Node.js REST API with Express",
7230
+ category: "api",
7231
+ runtime: "node",
7232
+ packages: [
7233
+ { name: "express", note: "Web framework" },
7234
+ { name: "cors" },
7235
+ { name: "helmet", note: "Security headers" },
7236
+ { name: "dotenv", note: "Environment variables" },
7237
+ { name: "prisma", optional: true, note: "Database ORM" }
7238
+ ],
7239
+ tags: ["api", "express", "nodejs", "rest"]
7240
+ },
7241
+ // Full Stack Templates
7242
+ {
7243
+ id: "fullstack-python",
7244
+ name: "Full Stack Python",
7245
+ description: "FastAPI backend + React frontend",
7246
+ category: "web",
7247
+ runtime: "mixed",
7248
+ packages: [
7249
+ { name: "fastapi" },
7250
+ { name: "uvicorn" },
7251
+ { name: "sqlalchemy" },
7252
+ { name: "react" },
7253
+ { name: "vite" },
7254
+ { name: "axios", note: "HTTP client" }
7255
+ ],
7256
+ tags: ["fullstack", "fastapi", "react"]
7257
+ },
7258
+ // DevOps Templates
7259
+ {
7260
+ id: "automation",
7261
+ name: "Automation & Scripting",
7262
+ description: "Python automation toolkit",
7263
+ category: "devops",
7264
+ runtime: "python",
7265
+ packages: [
7266
+ { name: "requests", note: "HTTP requests" },
7267
+ { name: "beautifulsoup4", note: "HTML parsing" },
7268
+ { name: "selenium", optional: true, note: "Browser automation" },
7269
+ { name: "paramiko", optional: true, note: "SSH" },
7270
+ { name: "schedule", optional: true, note: "Job scheduling" },
7271
+ { name: "click", note: "CLI framework" }
7272
+ ],
7273
+ tags: ["automation", "scripting", "cli", "devops"]
7274
+ }
7275
+ ];
7276
+ function getTemplateById(id) {
7277
+ return TEMPLATES.find((t) => t.id === id);
7278
+ }
7279
+ function searchTemplates(query) {
7280
+ const lower = query.toLowerCase();
7281
+ return TEMPLATES.filter(
7282
+ (t) => t.name.toLowerCase().includes(lower) || t.description.toLowerCase().includes(lower) || t.tags.some((tag) => tag.includes(lower)) || t.packages.some((p) => p.name.toLowerCase().includes(lower))
7283
+ );
7284
+ }
7285
+ function formatTemplateList(templates, colorize = true) {
7286
+ const colors = {
7287
+ reset: colorize ? "\x1B[0m" : "",
7288
+ bold: colorize ? "\x1B[1m" : "",
7289
+ dim: colorize ? "\x1B[2m" : "",
7290
+ cyan: colorize ? "\x1B[36m" : "",
7291
+ green: colorize ? "\x1B[32m" : "",
7292
+ yellow: colorize ? "\x1B[33m" : ""
7293
+ };
7294
+ const lines = [];
7295
+ const categories = [...new Set(templates.map((t) => t.category))];
7296
+ for (const category of categories) {
7297
+ const categoryTemplates = templates.filter((t) => t.category === category);
7298
+ const categoryName = category.charAt(0).toUpperCase() + category.slice(1);
7299
+ lines.push(`${colors.bold}${categoryName}${colors.reset}`);
7300
+ for (const template of categoryTemplates) {
7301
+ const requirements = [];
7302
+ if (template.minMemoryGb) requirements.push(`${template.minMemoryGb}GB+ RAM`);
7303
+ if (template.requiresGpu) requirements.push("GPU");
7304
+ lines.push(` ${colors.cyan}${template.id}${colors.reset} - ${template.name}`);
7305
+ lines.push(` ${colors.dim}${template.description}${colors.reset}`);
7306
+ if (requirements.length > 0) {
7307
+ lines.push(` ${colors.yellow}Requires: ${requirements.join(", ")}${colors.reset}`);
7308
+ }
7309
+ lines.push(` ${colors.dim}Packages: ${template.packages.filter((p) => !p.optional).map((p) => p.name).join(", ")}${colors.reset}`);
7310
+ }
7311
+ lines.push("");
7312
+ }
7313
+ return lines.join("\n");
7314
+ }
7315
+ function getInstallCommandsForTemplate(template) {
7316
+ const commands = [];
7317
+ if (template.runtime === "python" || template.runtime === "mixed") {
7318
+ const pythonPackages = template.packages.filter((p) => !p.optional).map((p) => p.version ? `${p.name}==${p.version}` : p.name);
7319
+ if (pythonPackages.length > 0) {
7320
+ commands.push(`pip install ${pythonPackages.join(" ")}`);
7321
+ }
7322
+ }
7323
+ if (template.runtime === "node" || template.runtime === "mixed") {
7324
+ const nodePackages = template.packages.filter((p) => !p.optional).map((p) => p.version ? `${p.name}@${p.version}` : p.name);
7325
+ if (nodePackages.length > 0) {
7326
+ commands.push(`npm install ${nodePackages.join(" ")}`);
7327
+ }
7328
+ }
7329
+ if (template.setupCommands) {
7330
+ commands.push(...template.setupCommands);
7331
+ }
7332
+ return commands;
7333
+ }
7334
+ var PACKAGE_DATABASE = {
7335
+ // ML/AI Packages
7336
+ "torch": {
7337
+ summary: "PyTorch - Deep learning framework",
7338
+ description: "PyTorch is an open-source machine learning library developed by Meta AI. It provides tensor computation with GPU acceleration and a tape-based automatic differentiation system for building neural networks.",
7339
+ category: "Machine Learning",
7340
+ useCases: [
7341
+ "Deep learning model training",
7342
+ "Computer vision applications",
7343
+ "Natural language processing",
7344
+ "Reinforcement learning",
7345
+ "Research and prototyping"
7346
+ ],
7347
+ commonlyUsedWith: ["torchvision", "numpy", "transformers", "tqdm", "tensorboard"],
7348
+ alternatives: [
7349
+ { name: "tensorflow", note: "Google's ML framework, more production-focused" },
7350
+ { name: "jax", note: "Google's newer framework, good for research" }
7351
+ ],
7352
+ documentation: "https://pytorch.org/docs/",
7353
+ popularity: "very-high",
7354
+ maintenance: "active"
7355
+ },
7356
+ "tensorflow": {
7357
+ summary: "TensorFlow - End-to-end ML platform",
7358
+ description: "TensorFlow is Google's open-source platform for machine learning. TensorFlow 2.x includes Keras as the high-level API, making it easier to build and train models.",
7359
+ category: "Machine Learning",
7360
+ useCases: [
7361
+ "Production ML deployment",
7362
+ "Mobile ML (TensorFlow Lite)",
7363
+ "Web ML (TensorFlow.js)",
7364
+ "Large-scale distributed training"
7365
+ ],
7366
+ commonlyUsedWith: ["keras", "numpy", "tensorboard", "tensorflow-hub"],
7367
+ alternatives: [
7368
+ { name: "torch", note: "More Pythonic, popular in research" },
7369
+ { name: "jax", note: "Newer Google framework" }
7370
+ ],
7371
+ documentation: "https://www.tensorflow.org/api_docs",
7372
+ popularity: "very-high",
7373
+ maintenance: "active"
7374
+ },
7375
+ "transformers": {
7376
+ summary: "Hugging Face Transformers - State-of-the-art NLP",
7377
+ description: "Provides thousands of pretrained models for natural language processing, computer vision, audio, and multimodal tasks. Easy to fine-tune and deploy.",
7378
+ category: "Machine Learning",
7379
+ useCases: [
7380
+ "Text classification",
7381
+ "Named entity recognition",
7382
+ "Question answering",
7383
+ "Text generation",
7384
+ "Translation"
7385
+ ],
7386
+ commonlyUsedWith: ["torch", "tokenizers", "datasets", "accelerate"],
7387
+ alternatives: [
7388
+ { name: "spacy", note: "Faster for production NLP pipelines" },
7389
+ { name: "flair", note: "Simpler API for NLP" }
7390
+ ],
7391
+ documentation: "https://huggingface.co/docs/transformers",
7392
+ popularity: "very-high",
7393
+ maintenance: "active"
7394
+ },
7395
+ // Data Science
7396
+ "numpy": {
7397
+ summary: "NumPy - Fundamental array computing",
7398
+ description: "The fundamental package for scientific computing in Python. Provides support for large, multi-dimensional arrays and matrices, along with mathematical functions.",
7399
+ category: "Data Science",
7400
+ useCases: [
7401
+ "Numerical computations",
7402
+ "Array manipulation",
7403
+ "Linear algebra",
7404
+ "Random number generation",
7405
+ "Foundation for other libraries"
7406
+ ],
7407
+ commonlyUsedWith: ["pandas", "scipy", "matplotlib", "scikit-learn"],
7408
+ alternatives: [
7409
+ { name: "cupy", note: "GPU-accelerated NumPy" },
7410
+ { name: "jax.numpy", note: "Auto-diff enabled NumPy" }
7411
+ ],
7412
+ documentation: "https://numpy.org/doc/",
7413
+ popularity: "very-high",
7414
+ maintenance: "active"
7415
+ },
7416
+ "pandas": {
7417
+ summary: "Pandas - Data analysis and manipulation",
7418
+ description: "Powerful data structures for data analysis, time series, and statistics. The DataFrame is the primary data structure, similar to a spreadsheet or SQL table.",
7419
+ category: "Data Science",
7420
+ useCases: [
7421
+ "Data cleaning and preparation",
7422
+ "Exploratory data analysis",
7423
+ "Time series analysis",
7424
+ "CSV/Excel file handling",
7425
+ "Data transformation"
7426
+ ],
7427
+ commonlyUsedWith: ["numpy", "matplotlib", "seaborn", "scikit-learn"],
7428
+ alternatives: [
7429
+ { name: "polars", note: "Faster, Rust-based alternative" },
7430
+ { name: "dask", note: "Parallel computing for large datasets" }
7431
+ ],
7432
+ documentation: "https://pandas.pydata.org/docs/",
7433
+ popularity: "very-high",
7434
+ maintenance: "active"
7435
+ },
7436
+ // Web Frameworks
7437
+ "fastapi": {
7438
+ summary: "FastAPI - Modern Python web framework",
7439
+ description: "High-performance web framework for building APIs with Python 3.7+. Based on standard Python type hints, automatic API documentation, and async support.",
7440
+ category: "Web Framework",
7441
+ useCases: [
7442
+ "REST API development",
7443
+ "Microservices",
7444
+ "Real-time applications",
7445
+ "ML model serving"
7446
+ ],
7447
+ commonlyUsedWith: ["uvicorn", "pydantic", "sqlalchemy", "httpx"],
7448
+ alternatives: [
7449
+ { name: "flask", note: "Simpler, more flexible" },
7450
+ { name: "django", note: "Full-featured, batteries included" }
7451
+ ],
7452
+ documentation: "https://fastapi.tiangolo.com/",
7453
+ popularity: "very-high",
7454
+ maintenance: "active"
7455
+ },
7456
+ "flask": {
7457
+ summary: "Flask - Lightweight web framework",
7458
+ description: "Micro web framework that doesn't require particular tools or libraries. Keeps the core simple but extensible through plugins.",
7459
+ category: "Web Framework",
7460
+ useCases: [
7461
+ "Simple web applications",
7462
+ "REST APIs",
7463
+ "Prototyping",
7464
+ "Microservices"
7465
+ ],
7466
+ commonlyUsedWith: ["werkzeug", "jinja2", "flask-sqlalchemy", "gunicorn"],
7467
+ alternatives: [
7468
+ { name: "fastapi", note: "Modern, async, auto-docs" },
7469
+ { name: "django", note: "Full-featured framework" }
7470
+ ],
7471
+ documentation: "https://flask.palletsprojects.com/",
7472
+ popularity: "very-high",
7473
+ maintenance: "active"
7474
+ },
7475
+ "django": {
7476
+ summary: "Django - Full-featured web framework",
7477
+ description: "High-level Python web framework that encourages rapid development. Includes ORM, admin interface, authentication, and more out of the box.",
7478
+ category: "Web Framework",
7479
+ useCases: [
7480
+ "Full-stack web applications",
7481
+ "Content management systems",
7482
+ "E-commerce platforms",
7483
+ "Admin interfaces"
7484
+ ],
7485
+ commonlyUsedWith: ["django-rest-framework", "celery", "redis", "postgresql"],
7486
+ alternatives: [
7487
+ { name: "flask", note: "Lighter weight, more flexible" },
7488
+ { name: "fastapi", note: "API-focused, async" }
7489
+ ],
7490
+ documentation: "https://docs.djangoproject.com/",
7491
+ popularity: "very-high",
7492
+ maintenance: "active"
7493
+ },
7494
+ // Node.js packages
7495
+ "react": {
7496
+ summary: "React - UI component library",
7497
+ description: "JavaScript library for building user interfaces. Uses a component-based architecture and virtual DOM for efficient updates.",
7498
+ category: "Frontend",
7499
+ useCases: [
7500
+ "Single-page applications",
7501
+ "Component libraries",
7502
+ "Mobile apps (React Native)",
7503
+ "Server-side rendering"
7504
+ ],
7505
+ commonlyUsedWith: ["react-dom", "react-router-dom", "redux", "next"],
7506
+ alternatives: [
7507
+ { name: "vue", note: "Easier learning curve" },
7508
+ { name: "svelte", note: "Compile-time framework, smaller bundles" }
7509
+ ],
7510
+ documentation: "https://react.dev/",
7511
+ popularity: "very-high",
7512
+ maintenance: "active"
7513
+ },
7514
+ "next": {
7515
+ summary: "Next.js - React framework",
7516
+ description: "Production-ready React framework with server-side rendering, static site generation, API routes, and more.",
7517
+ category: "Frontend Framework",
7518
+ useCases: [
7519
+ "Server-side rendered apps",
7520
+ "Static websites",
7521
+ "Full-stack applications",
7522
+ "E-commerce sites"
7523
+ ],
7524
+ commonlyUsedWith: ["react", "tailwindcss", "prisma", "vercel"],
7525
+ alternatives: [
7526
+ { name: "remix", note: "Web standards focused" },
7527
+ { name: "gatsby", note: "Static site generator" }
7528
+ ],
7529
+ documentation: "https://nextjs.org/docs",
7530
+ popularity: "very-high",
7531
+ maintenance: "active"
7532
+ },
7533
+ "express": {
7534
+ summary: "Express - Node.js web framework",
7535
+ description: "Minimal and flexible Node.js web application framework providing robust features for web and mobile applications.",
7536
+ category: "Backend Framework",
7537
+ useCases: [
7538
+ "REST APIs",
7539
+ "Web servers",
7540
+ "Middleware-based apps",
7541
+ "Backend services"
7542
+ ],
7543
+ commonlyUsedWith: ["cors", "helmet", "mongoose", "passport"],
7544
+ alternatives: [
7545
+ { name: "fastify", note: "Faster, lower overhead" },
7546
+ { name: "koa", note: "Modern, lighter weight" }
7547
+ ],
7548
+ documentation: "https://expressjs.com/",
7549
+ popularity: "very-high",
7550
+ maintenance: "stable"
7551
+ },
7552
+ // Utilities
7553
+ "requests": {
7554
+ summary: "Requests - HTTP for humans",
7555
+ description: "Elegant and simple HTTP library for Python. Makes HTTP requests intuitive with a clean API.",
7556
+ category: "Networking",
7557
+ useCases: [
7558
+ "API consumption",
7559
+ "Web scraping",
7560
+ "File downloads",
7561
+ "REST client"
7562
+ ],
7563
+ commonlyUsedWith: ["beautifulsoup4", "lxml", "urllib3"],
7564
+ alternatives: [
7565
+ { name: "httpx", note: "Async support, HTTP/2" },
7566
+ { name: "aiohttp", note: "Async HTTP client/server" }
7567
+ ],
7568
+ documentation: "https://requests.readthedocs.io/",
7569
+ popularity: "very-high",
7570
+ maintenance: "active"
7571
+ },
7572
+ "axios": {
7573
+ summary: "Axios - HTTP client",
7574
+ description: "Promise-based HTTP client for the browser and Node.js. Supports request/response interceptors and automatic JSON parsing.",
7575
+ category: "Networking",
7576
+ useCases: [
7577
+ "API requests",
7578
+ "Form submissions",
7579
+ "File uploads",
7580
+ "REST client"
7581
+ ],
7582
+ commonlyUsedWith: ["react", "vue", "typescript"],
7583
+ alternatives: [
7584
+ { name: "fetch", note: "Native browser API" },
7585
+ { name: "got", note: "Node.js focused" }
7586
+ ],
7587
+ documentation: "https://axios-http.com/docs/intro",
7588
+ popularity: "very-high",
7589
+ maintenance: "active"
7590
+ },
7591
+ // Deprecated/Legacy
7592
+ "moment": {
7593
+ summary: "Moment.js - Date library (Legacy)",
7594
+ description: "Date manipulation library. Now in maintenance mode - the team recommends using alternatives for new projects.",
7595
+ category: "Utilities",
7596
+ useCases: [
7597
+ "Date parsing",
7598
+ "Date formatting",
7599
+ "Date manipulation"
7600
+ ],
7601
+ commonlyUsedWith: ["moment-timezone"],
7602
+ alternatives: [
7603
+ { name: "dayjs", note: "2KB, same API - recommended" },
7604
+ { name: "date-fns", note: "Modular, tree-shakeable" },
7605
+ { name: "luxon", note: "By Moment team, modern" }
7606
+ ],
7607
+ warnings: [
7608
+ "Moment.js is in maintenance mode",
7609
+ "Large bundle size (300KB)",
7610
+ "Not tree-shakeable"
7611
+ ],
7612
+ documentation: "https://momentjs.com/docs/",
7613
+ popularity: "high",
7614
+ maintenance: "minimal"
7615
+ },
7616
+ "request": {
7617
+ summary: "Request - HTTP client (Deprecated)",
7618
+ description: "Simplified HTTP request client. DEPRECATED - no longer maintained.",
7619
+ category: "Networking",
7620
+ useCases: ["Legacy HTTP requests"],
7621
+ commonlyUsedWith: [],
7622
+ alternatives: [
7623
+ { name: "axios", note: "Modern, Promise-based" },
7624
+ { name: "got", note: "Modern Node.js client" },
7625
+ { name: "node-fetch", note: "Fetch API for Node" }
7626
+ ],
7627
+ warnings: [
7628
+ "DEPRECATED - do not use for new projects",
7629
+ "No security updates"
7630
+ ],
7631
+ documentation: "https://github.com/request/request",
7632
+ popularity: "medium",
7633
+ maintenance: "deprecated"
7634
+ }
7635
+ };
7636
+ function explainPackage(name) {
7637
+ const normalizedName = name.toLowerCase();
7638
+ const info = PACKAGE_DATABASE[normalizedName];
7639
+ if (!info) {
7640
+ return null;
7641
+ }
7642
+ const runtime = ["torch", "tensorflow", "numpy", "pandas", "fastapi", "flask", "django", "requests"].includes(normalizedName) ? "python" : "node";
7643
+ return {
7644
+ name,
7645
+ ...info,
7646
+ installCommand: runtime === "python" ? `pip install ${name}` : `npm install ${name}`
7647
+ };
7648
+ }
7649
+ function formatExplanation(explanation, colorize = true) {
7650
+ const colors = {
7651
+ reset: colorize ? "\x1B[0m" : "",
7652
+ bold: colorize ? "\x1B[1m" : "",
7653
+ dim: colorize ? "\x1B[2m" : "",
7654
+ cyan: colorize ? "\x1B[36m" : "",
7655
+ green: colorize ? "\x1B[32m" : "",
7656
+ yellow: colorize ? "\x1B[33m" : "",
7657
+ red: colorize ? "\x1B[31m" : "",
7658
+ blue: colorize ? "\x1B[34m" : ""
7659
+ };
7660
+ const lines = [];
7661
+ lines.push(`${colors.bold}${explanation.name}${colors.reset} ${colors.dim}(${explanation.category})${colors.reset}`);
7662
+ lines.push(`${colors.cyan}${explanation.summary}${colors.reset}`);
7663
+ lines.push("");
7664
+ lines.push(explanation.description);
7665
+ lines.push("");
7666
+ const popColor = explanation.popularity === "very-high" ? colors.green : colors.yellow;
7667
+ const maintColor = explanation.maintenance === "active" ? colors.green : explanation.maintenance === "stable" ? colors.blue : explanation.maintenance === "deprecated" ? colors.red : colors.yellow;
7668
+ lines.push(`${colors.dim}Popularity:${colors.reset} ${popColor}${explanation.popularity}${colors.reset} | ${colors.dim}Maintenance:${colors.reset} ${maintColor}${explanation.maintenance}${colors.reset}`);
7669
+ lines.push("");
7670
+ if (explanation.warnings && explanation.warnings.length > 0) {
7671
+ lines.push(`${colors.red}${colors.bold}Warnings${colors.reset}`);
7672
+ for (const warning of explanation.warnings) {
7673
+ lines.push(` ${colors.red}\u26A0${colors.reset} ${warning}`);
7674
+ }
7675
+ lines.push("");
7676
+ }
7677
+ lines.push(`${colors.bold}Use Cases${colors.reset}`);
7678
+ for (const useCase of explanation.useCases) {
7679
+ lines.push(` ${colors.green}\u2022${colors.reset} ${useCase}`);
7680
+ }
7681
+ lines.push("");
7682
+ if (explanation.commonlyUsedWith.length > 0) {
7683
+ lines.push(`${colors.bold}Commonly Used With${colors.reset}`);
7684
+ lines.push(` ${explanation.commonlyUsedWith.join(", ")}`);
7685
+ lines.push("");
7686
+ }
7687
+ if (explanation.alternatives.length > 0) {
7688
+ lines.push(`${colors.bold}Alternatives${colors.reset}`);
7689
+ for (const alt of explanation.alternatives) {
7690
+ lines.push(` ${colors.cyan}${alt.name}${colors.reset} - ${colors.dim}${alt.note}${colors.reset}`);
7691
+ }
7692
+ lines.push("");
7693
+ }
7694
+ lines.push(`${colors.bold}Install${colors.reset}`);
7695
+ lines.push(` ${colors.cyan}$ ${explanation.installCommand}${colors.reset}`);
7696
+ lines.push("");
7697
+ lines.push(`${colors.dim}Documentation: ${explanation.documentation}${colors.reset}`);
7698
+ return lines.join("\n");
7699
+ }
7700
+ function parseGitHubUrl(url) {
7701
+ const patterns = [
7702
+ /github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?(?:\/tree\/([^\/]+))?$/,
7703
+ /github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/
7704
+ ];
7705
+ for (const pattern of patterns) {
7706
+ const match = url.match(pattern);
7707
+ if (match && match[1] && match[2]) {
7708
+ return {
7709
+ owner: match[1],
7710
+ repo: match[2].replace(/\.git$/, ""),
7711
+ branch: match[3]
7712
+ };
7713
+ }
7714
+ }
7715
+ return null;
7716
+ }
7717
+ function parseRequirementsTxt(content) {
7718
+ const packages = [];
7719
+ const lines = content.split("\n");
7720
+ for (const line of lines) {
7721
+ const trimmed = line.trim();
7722
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) {
7723
+ continue;
7724
+ }
7725
+ const match = trimmed.match(/^([a-zA-Z0-9_-]+)(?:\[.*\])?(?:([<>=!~]+)(.+))?$/);
7726
+ if (match && match[1]) {
7727
+ packages.push({
7728
+ name: match[1],
7729
+ version: match[3]?.trim()
7730
+ });
7731
+ }
7732
+ }
7733
+ return { packages, source: "requirements.txt" };
7734
+ }
7735
+ function parsePackageJson(content) {
7736
+ const packages = [];
7737
+ try {
7738
+ const pkg = JSON.parse(content);
7739
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
7740
+ for (const [name, version] of Object.entries(deps)) {
7741
+ packages.push({
7742
+ name,
7743
+ version: String(version).replace(/^[\^~]/, "")
7744
+ });
7745
+ }
7746
+ } catch {
7747
+ }
7748
+ return { packages, source: "package.json" };
7749
+ }
7750
+ function parsePyprojectToml(content) {
7751
+ const packages = [];
7752
+ const depsMatch = content.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
7753
+ if (depsMatch && depsMatch[1]) {
7754
+ const depsContent = depsMatch[1];
7755
+ const depLines = depsContent.match(/"([^"]+)"|'([^']+)'/g);
7756
+ if (depLines) {
7757
+ for (const dep of depLines) {
7758
+ const cleanDep = dep.replace(/["']/g, "").trim();
7759
+ const match = cleanDep.match(/^([a-zA-Z0-9_-]+)(?:\[.*\])?(?:([<>=!~]+)(.+))?$/);
7760
+ if (match && match[1]) {
7761
+ packages.push({
7762
+ name: match[1],
7763
+ version: match[3]?.trim()
7764
+ });
7765
+ }
7766
+ }
7767
+ }
7768
+ }
7769
+ return { packages, source: "pyproject.toml" };
7770
+ }
7771
+ function parseSetupPy(content) {
7772
+ const packages = [];
7773
+ const requiresMatch = content.match(/install_requires\s*=\s*\[([\s\S]*?)\]/);
7774
+ if (requiresMatch && requiresMatch[1]) {
7775
+ const requiresContent = requiresMatch[1];
7776
+ const depLines = requiresContent.match(/"([^"]+)"|'([^']+)'/g);
7777
+ if (depLines) {
7778
+ for (const dep of depLines) {
7779
+ const cleanDep = dep.replace(/["']/g, "").trim();
7780
+ const match = cleanDep.match(/^([a-zA-Z0-9_-]+)/);
7781
+ if (match && match[1]) {
7782
+ packages.push({ name: match[1] });
7783
+ }
7784
+ }
7785
+ }
7786
+ }
7787
+ return { packages, source: "setup.py" };
7788
+ }
7789
+ function analyzeRepoStructure(files) {
7790
+ const packages = [];
7791
+ const setupCommands = [];
7792
+ const envVars = [];
7793
+ const notes = [];
7794
+ let runtime = "unknown";
7795
+ let confidence = "low";
7796
+ const hasPython = "requirements.txt" in files || "pyproject.toml" in files || "setup.py" in files;
7797
+ const hasNode = "package.json" in files;
7798
+ if (hasPython && hasNode) {
7799
+ runtime = "mixed";
7800
+ } else if (hasPython) {
7801
+ runtime = "python";
7802
+ } else if (hasNode) {
7803
+ runtime = "node";
7804
+ }
7805
+ if (files["requirements.txt"]) {
7806
+ const parsed = parseRequirementsTxt(files["requirements.txt"]);
7807
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "requirements.txt" })));
7808
+ setupCommands.push("pip install -r requirements.txt");
7809
+ confidence = "high";
7810
+ }
7811
+ if (files["pyproject.toml"]) {
7812
+ const parsed = parsePyprojectToml(files["pyproject.toml"]);
7813
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "pyproject.toml" })));
7814
+ if (!setupCommands.some((c) => c.includes("pip"))) {
7815
+ setupCommands.push("pip install -e .");
7816
+ }
7817
+ confidence = "high";
7818
+ }
7819
+ if (files["setup.py"] && packages.length === 0) {
7820
+ const parsed = parseSetupPy(files["setup.py"]);
7821
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "setup.py" })));
7822
+ setupCommands.push("pip install -e .");
7823
+ confidence = "medium";
7824
+ }
7825
+ if (files["package.json"]) {
7826
+ const parsed = parsePackageJson(files["package.json"]);
7827
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "package.json" })));
7828
+ if (files["pnpm-lock.yaml"]) {
7829
+ setupCommands.push("pnpm install");
7830
+ } else if (files["yarn.lock"]) {
7831
+ setupCommands.push("yarn install");
7832
+ } else {
7833
+ setupCommands.push("npm install");
7834
+ }
7835
+ confidence = "high";
7836
+ }
7837
+ if (files[".env.example"] || files[".env.sample"]) {
7838
+ const envContent = files[".env.example"] || files[".env.sample"] || "";
7839
+ const envMatches = envContent.match(/^([A-Z_]+)=/gm);
7840
+ if (envMatches) {
7841
+ envVars.push(...envMatches.map((m) => m.replace("=", "")));
7842
+ }
7843
+ notes.push("This project uses environment variables - check .env.example");
7844
+ }
7845
+ if (files["Dockerfile"] || files["docker-compose.yml"]) {
7846
+ notes.push("Docker setup available - you can use `docker-compose up` instead");
7847
+ }
7848
+ if (files["README.md"]) {
7849
+ if (files["README.md"].includes("pip install")) {
7850
+ notes.push("Check README.md for additional setup instructions");
7851
+ }
7852
+ }
7853
+ const uniquePackages = /* @__PURE__ */ new Map();
7854
+ for (const pkg of packages) {
7855
+ if (!uniquePackages.has(pkg.name.toLowerCase())) {
7856
+ uniquePackages.set(pkg.name.toLowerCase(), pkg);
7857
+ }
7858
+ }
7859
+ return {
7860
+ url: "",
7861
+ name: "",
7862
+ runtime,
7863
+ packages: Array.from(uniquePackages.values()),
7864
+ setupCommands,
7865
+ envVars,
7866
+ notes,
7867
+ confidence
7868
+ };
7869
+ }
7870
+ function formatRepoAnalysis(analysis, colorize = true) {
7871
+ const colors = {
7872
+ reset: colorize ? "\x1B[0m" : "",
7873
+ bold: colorize ? "\x1B[1m" : "",
7874
+ dim: colorize ? "\x1B[2m" : "",
7875
+ cyan: colorize ? "\x1B[36m" : "",
7876
+ green: colorize ? "\x1B[32m" : "",
7877
+ yellow: colorize ? "\x1B[33m" : ""
7878
+ };
7879
+ const lines = [];
7880
+ lines.push(`${colors.bold}Repository Analysis: ${analysis.name}${colors.reset}`);
7881
+ lines.push(`${colors.dim}${analysis.url}${colors.reset}`);
7882
+ lines.push("");
7883
+ const runtimeLabel = {
7884
+ python: "Python",
7885
+ node: "Node.js",
7886
+ mixed: "Python + Node.js",
7887
+ unknown: "Unknown"
7888
+ }[analysis.runtime];
7889
+ lines.push(`${colors.bold}Runtime:${colors.reset} ${runtimeLabel}`);
7890
+ lines.push(`${colors.bold}Confidence:${colors.reset} ${analysis.confidence}`);
7891
+ lines.push("");
7892
+ if (analysis.packages.length > 0) {
7893
+ lines.push(`${colors.bold}Dependencies (${analysis.packages.length})${colors.reset}`);
7894
+ const bySource = /* @__PURE__ */ new Map();
7895
+ for (const pkg of analysis.packages) {
7896
+ if (!bySource.has(pkg.source)) {
7897
+ bySource.set(pkg.source, []);
7898
+ }
7899
+ bySource.get(pkg.source).push(pkg);
7900
+ }
7901
+ for (const [source, pkgs] of bySource) {
7902
+ lines.push(` ${colors.cyan}${source}:${colors.reset}`);
7903
+ const displayed = pkgs.slice(0, 10);
7904
+ for (const pkg of displayed) {
7905
+ const version = pkg.version ? `@${pkg.version}` : "";
7906
+ lines.push(` ${pkg.name}${colors.dim}${version}${colors.reset}`);
7907
+ }
7908
+ if (pkgs.length > 10) {
7909
+ lines.push(` ${colors.dim}...and ${pkgs.length - 10} more${colors.reset}`);
7910
+ }
7911
+ }
7912
+ lines.push("");
7913
+ }
7914
+ if (analysis.setupCommands.length > 0) {
7915
+ lines.push(`${colors.bold}Setup Commands${colors.reset}`);
7916
+ for (const cmd of analysis.setupCommands) {
7917
+ lines.push(` ${colors.cyan}$ ${cmd}${colors.reset}`);
7918
+ }
7919
+ lines.push("");
7920
+ }
7921
+ if (analysis.envVars.length > 0) {
7922
+ lines.push(`${colors.yellow}${colors.bold}Environment Variables Required${colors.reset}`);
7923
+ for (const envVar of analysis.envVars.slice(0, 5)) {
7924
+ lines.push(` ${colors.yellow}\u2022${colors.reset} ${envVar}`);
7925
+ }
7926
+ if (analysis.envVars.length > 5) {
7927
+ lines.push(` ${colors.dim}...and ${analysis.envVars.length - 5} more${colors.reset}`);
7928
+ }
7929
+ lines.push("");
7930
+ }
7931
+ if (analysis.notes.length > 0) {
7932
+ lines.push(`${colors.bold}Notes${colors.reset}`);
7933
+ for (const note of analysis.notes) {
7934
+ lines.push(` ${colors.green}\u2192${colors.reset} ${note}`);
7935
+ }
7936
+ }
7937
+ return lines.join("\n");
7938
+ }
7939
+ var PACKAGE_CATEGORIES = {
7940
+ "torch": "ML/AI",
7941
+ "tensorflow": "ML/AI",
7942
+ "transformers": "ML/AI",
7943
+ "scikit-learn": "ML/AI",
7944
+ "numpy": "Data Science",
7945
+ "pandas": "Data Science",
7946
+ "matplotlib": "Data Science",
7947
+ "seaborn": "Data Science",
7948
+ "scipy": "Data Science",
7949
+ "fastapi": "Web Backend",
7950
+ "flask": "Web Backend",
7951
+ "django": "Web Backend",
7952
+ "express": "Web Backend",
7953
+ "react": "Web Frontend",
7954
+ "vue": "Web Frontend",
7955
+ "next": "Web Frontend",
7956
+ "angular": "Web Frontend",
7957
+ "requests": "Networking",
7958
+ "httpx": "Networking",
7959
+ "axios": "Networking",
7960
+ "pytest": "Testing",
7961
+ "jest": "Testing",
7962
+ "black": "Dev Tools",
7963
+ "eslint": "Dev Tools",
7964
+ "prettier": "Dev Tools"
7965
+ };
7966
+ var PACKAGE_SIZES2 = {
7967
+ "torch": 85e4,
7968
+ "tensorflow": 55e4,
7969
+ "transformers": 45e3,
7970
+ "numpy": 15e3,
7971
+ "pandas": 12e3,
7972
+ "scipy": 35e3,
7973
+ "matplotlib": 8e3,
7974
+ "scikit-learn": 25e3,
7975
+ "opencv-python": 45e3,
7976
+ "pillow": 3e3,
7977
+ "requests": 500,
7978
+ "flask": 800,
7979
+ "django": 8e3,
7980
+ "fastapi": 400,
7981
+ "react": 150,
7982
+ "next": 5e3,
7983
+ "express": 200,
7984
+ "lodash": 1500,
7985
+ "axios": 100
7986
+ };
7987
+ function getPackageSize2(name) {
7988
+ return PACKAGE_SIZES2[name.toLowerCase()] || 500;
7989
+ }
7990
+ function getPackageCategory(name) {
7991
+ return PACKAGE_CATEGORIES[name.toLowerCase()] || "Other";
7992
+ }
7993
+ function analyzeEnvironment(packages, snapshots) {
7994
+ const stats = calculateStats(packages);
7995
+ const timeline = snapshots ? buildTimeline(snapshots) : void 0;
7996
+ const recommendations = [];
7997
+ const achievements = [];
7998
+ const warnings = [];
7999
+ if (stats.technicalDebtScore > 50) {
8000
+ recommendations.push("High technical debt detected - consider updating outdated packages");
8001
+ }
8002
+ if (stats.totalPackages > 100) {
8003
+ recommendations.push("Large number of packages - audit for unused dependencies");
8004
+ }
8005
+ const largestPkg = stats.largestPackages[0];
8006
+ if (stats.largestPackages.length > 0 && largestPkg && largestPkg.sizeKb > 5e5) {
8007
+ recommendations.push(`${largestPkg.name} is very large - ensure it's necessary`);
8008
+ }
8009
+ const mlPackages = packages.filter((p) => getPackageCategory(p.name) === "ML/AI");
8010
+ if (mlPackages.length > 2) {
8011
+ recommendations.push("Multiple ML frameworks detected - consider consolidating");
8012
+ }
8013
+ if (stats.technicalDebtScore < 20) {
8014
+ achievements.push("Low technical debt - great maintenance!");
8015
+ }
8016
+ if (stats.totalPackages < 20) {
8017
+ achievements.push("Minimal dependencies - clean project structure");
8018
+ }
8019
+ if (timeline && timeline.averageChangesPerWeek > 1) {
8020
+ achievements.push("Active maintenance - regular updates");
8021
+ }
8022
+ if (stats.technicalDebtScore > 70) {
8023
+ warnings.push("Critical technical debt - multiple packages need updates");
8024
+ }
8025
+ for (const reason of stats.technicalDebtReasons) {
8026
+ warnings.push(reason);
8027
+ }
8028
+ return {
8029
+ stats,
8030
+ timeline,
8031
+ recommendations,
8032
+ achievements,
8033
+ warnings
8034
+ };
8035
+ }
8036
+ function calculateStats(packages) {
8037
+ const totalPackages = packages.length;
8038
+ let totalSizeKb = 0;
8039
+ const runtimeBreakdown = { python: 0, node: 0 };
8040
+ const categoryBreakdown = {};
8041
+ const technicalDebtReasons = [];
8042
+ let debtScore = 0;
8043
+ const pythonIndicators = ["torch", "numpy", "pandas", "flask", "django", "fastapi", "requests"];
8044
+ const nodeIndicators = ["react", "vue", "next", "express", "lodash", "axios"];
8045
+ for (const pkg of packages) {
8046
+ const name = pkg.name.toLowerCase();
8047
+ const size = getPackageSize2(name);
8048
+ totalSizeKb += size;
8049
+ const category = getPackageCategory(name);
8050
+ categoryBreakdown[category] = (categoryBreakdown[category] || 0) + 1;
8051
+ if (pythonIndicators.includes(name)) {
8052
+ runtimeBreakdown["python"] = (runtimeBreakdown["python"] || 0) + 1;
8053
+ } else if (nodeIndicators.includes(name)) {
8054
+ runtimeBreakdown["node"] = (runtimeBreakdown["node"] || 0) + 1;
8055
+ }
8056
+ const majorVersion = parseInt(pkg.version.split(".")[0] || "0");
8057
+ if (majorVersion === 0) {
8058
+ debtScore += 2;
8059
+ }
8060
+ if (majorVersion < 2 && ["torch", "tensorflow", "react"].includes(name)) {
8061
+ debtScore += 10;
8062
+ technicalDebtReasons.push(`${pkg.name} v${pkg.version} is significantly outdated`);
8063
+ }
8064
+ }
8065
+ debtScore = Math.min(100, Math.max(0, debtScore));
8066
+ const sortedBySize = [...packages].map((p) => ({ name: p.name, sizeKb: getPackageSize2(p.name) })).sort((a, b) => b.sizeKb - a.sizeKb).slice(0, 5);
8067
+ const updateFrequency = totalPackages > 50 ? "frequent" : totalPackages > 20 ? "regular" : "rare";
8068
+ return {
8069
+ totalPackages,
8070
+ totalSizeKb,
8071
+ runtimeBreakdown,
8072
+ categoryBreakdown,
8073
+ largestPackages: sortedBySize,
8074
+ updateFrequency,
8075
+ technicalDebtScore: debtScore,
8076
+ technicalDebtReasons
8077
+ };
8078
+ }
8079
+ function buildTimeline(snapshots) {
8080
+ const sorted = [...snapshots].sort(
8081
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
8082
+ );
8083
+ const timelineEntries = sorted.map((s) => ({
8084
+ id: s.id,
8085
+ date: new Date(s.createdAt),
8086
+ packageCount: s.packages.length,
8087
+ description: s.description,
8088
+ changeType: determineChangeType(s.description)
8089
+ }));
8090
+ const now = /* @__PURE__ */ new Date();
8091
+ const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
8092
+ const recentChanges = timelineEntries.filter((t) => t.date > oneWeekAgo);
8093
+ const packageChanges = /* @__PURE__ */ new Map();
8094
+ for (let i = 0; i < sorted.length - 1; i++) {
8095
+ const currentSnapshot = sorted[i];
8096
+ const previousSnapshot = sorted[i + 1];
8097
+ if (!currentSnapshot || !previousSnapshot) continue;
8098
+ const current = new Set(currentSnapshot.packages.map((p) => p.name));
8099
+ const previous = new Set(previousSnapshot.packages.map((p) => p.name));
8100
+ for (const pkg of current) {
8101
+ if (!previous.has(pkg)) {
8102
+ packageChanges.set(pkg, (packageChanges.get(pkg) || 0) + 1);
8103
+ }
8104
+ }
8105
+ }
8106
+ const mostChanged = [...packageChanges.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name]) => name);
8107
+ return {
8108
+ snapshots: timelineEntries,
8109
+ averageChangesPerWeek: recentChanges.length,
8110
+ lastChange: timelineEntries[0]?.date,
8111
+ mostFrequentlyChanged: mostChanged
8112
+ };
8113
+ }
8114
+ function determineChangeType(description) {
8115
+ const lower = description.toLowerCase();
8116
+ if (lower.includes("rollback") || lower.includes("revert")) return "rollback";
8117
+ if (lower.includes("remove") || lower.includes("uninstall")) return "remove";
8118
+ if (lower.includes("update") || lower.includes("upgrade")) return "update";
8119
+ return "install";
8120
+ }
8121
+ function formatInsights(insights, colorize = true) {
8122
+ const colors = {
8123
+ reset: colorize ? "\x1B[0m" : "",
8124
+ bold: colorize ? "\x1B[1m" : "",
8125
+ dim: colorize ? "\x1B[2m" : "",
8126
+ cyan: colorize ? "\x1B[36m" : "",
8127
+ green: colorize ? "\x1B[32m" : "",
8128
+ yellow: colorize ? "\x1B[33m" : "",
8129
+ red: colorize ? "\x1B[31m" : "",
8130
+ blue: colorize ? "\x1B[34m" : ""
8131
+ };
8132
+ const lines = [];
8133
+ function formatSize(kb) {
8134
+ if (kb >= 1e6) return `${(kb / 1e6).toFixed(1)}GB`;
8135
+ if (kb >= 1e3) return `${(kb / 1e3).toFixed(1)}MB`;
8136
+ return `${kb}KB`;
8137
+ }
8138
+ lines.push(`${colors.bold}Environment Analytics${colors.reset}`);
8139
+ lines.push("");
8140
+ lines.push(`${colors.bold}Overview${colors.reset}`);
8141
+ lines.push(` Packages: ${colors.cyan}${insights.stats.totalPackages}${colors.reset}`);
8142
+ lines.push(` Total Size: ${colors.cyan}${formatSize(insights.stats.totalSizeKb)}${colors.reset}`);
8143
+ const debtColor = insights.stats.technicalDebtScore < 30 ? colors.green : insights.stats.technicalDebtScore < 60 ? colors.yellow : colors.red;
8144
+ lines.push(` Technical Debt: ${debtColor}${insights.stats.technicalDebtScore}/100${colors.reset}`);
8145
+ lines.push("");
8146
+ if (Object.keys(insights.stats.categoryBreakdown).length > 0) {
8147
+ lines.push(`${colors.bold}Categories${colors.reset}`);
8148
+ const sortedCategories = Object.entries(insights.stats.categoryBreakdown).sort((a, b) => b[1] - a[1]);
8149
+ for (const [category, count] of sortedCategories) {
8150
+ const bar = "\u2588".repeat(Math.min(20, Math.ceil(count / insights.stats.totalPackages * 40)));
8151
+ lines.push(` ${category.padEnd(15)} ${colors.cyan}${bar}${colors.reset} ${count}`);
8152
+ }
8153
+ lines.push("");
8154
+ }
8155
+ if (insights.stats.largestPackages.length > 0) {
8156
+ lines.push(`${colors.bold}Largest Packages${colors.reset}`);
8157
+ for (const pkg of insights.stats.largestPackages) {
8158
+ lines.push(` ${pkg.name.padEnd(20)} ${colors.dim}${formatSize(pkg.sizeKb)}${colors.reset}`);
8159
+ }
8160
+ lines.push("");
8161
+ }
8162
+ if (insights.timeline) {
8163
+ lines.push(`${colors.bold}Activity${colors.reset}`);
8164
+ lines.push(` Changes this week: ${insights.timeline.averageChangesPerWeek}`);
8165
+ if (insights.timeline.lastChange) {
8166
+ lines.push(` Last change: ${insights.timeline.lastChange.toLocaleDateString()}`);
8167
+ }
8168
+ if (insights.timeline.mostFrequentlyChanged.length > 0) {
8169
+ lines.push(` Most changed: ${insights.timeline.mostFrequentlyChanged.join(", ")}`);
8170
+ }
8171
+ lines.push("");
8172
+ }
8173
+ if (insights.achievements.length > 0) {
8174
+ lines.push(`${colors.green}${colors.bold}Achievements${colors.reset}`);
8175
+ for (const achievement of insights.achievements) {
8176
+ lines.push(` ${colors.green}\u2605${colors.reset} ${achievement}`);
8177
+ }
8178
+ lines.push("");
8179
+ }
8180
+ if (insights.warnings.length > 0) {
8181
+ lines.push(`${colors.yellow}${colors.bold}Warnings${colors.reset}`);
8182
+ for (const warning of insights.warnings) {
8183
+ lines.push(` ${colors.yellow}\u26A0${colors.reset} ${warning}`);
8184
+ }
8185
+ lines.push("");
8186
+ }
8187
+ if (insights.recommendations.length > 0) {
8188
+ lines.push(`${colors.bold}Recommendations${colors.reset}`);
8189
+ for (const rec of insights.recommendations) {
8190
+ lines.push(` ${colors.cyan}\u2192${colors.reset} ${rec}`);
8191
+ }
8192
+ }
8193
+ return lines.join("\n");
8194
+ }
8195
+ async function diagnoseEnvironment(path, runtime, onProgress) {
8196
+ const id = randomUUID4();
8197
+ const problems = [];
8198
+ let detectedRuntime = null;
8199
+ onProgress?.("Scanning environment...");
8200
+ if (!runtime) {
8201
+ if (existsSync5(join6(path, ".venv")) || existsSync5(join6(path, "requirements.txt"))) {
8202
+ detectedRuntime = "python";
8203
+ } else if (existsSync5(join6(path, "node_modules")) || existsSync5(join6(path, "package.json"))) {
8204
+ detectedRuntime = "node";
8205
+ }
8206
+ } else {
8207
+ detectedRuntime = runtime;
8208
+ }
8209
+ if (!detectedRuntime) {
8210
+ problems.push({
8211
+ id: randomUUID4(),
8212
+ severity: "critical",
8213
+ category: "missing-env",
8214
+ title: "No environment detected",
8215
+ friendlyTitle: "No project setup found",
8216
+ description: "Could not detect a Python or Node.js environment",
8217
+ technicalDetail: "No .venv, node_modules, requirements.txt, or package.json found"
8218
+ });
8219
+ return {
8220
+ id,
8221
+ timestamp: /* @__PURE__ */ new Date(),
8222
+ runtime: "python",
8223
+ // default fallback
8224
+ status: "missing",
8225
+ problems,
8226
+ repairPlan: [],
8227
+ estimatedTime: "Unknown",
8228
+ confidence: 0
8229
+ };
8230
+ }
8231
+ onProgress?.(`Checking ${detectedRuntime} environment...`);
8232
+ const envExists = await checkEnvironmentExists(path, detectedRuntime, problems);
8233
+ if (envExists) {
8234
+ await checkRuntimeFunctional(path, detectedRuntime, problems, onProgress);
8235
+ }
8236
+ if (envExists) {
8237
+ await checkDependencyConflicts(path, detectedRuntime, problems, onProgress);
8238
+ }
8239
+ if (envExists) {
8240
+ await checkLockfileDrift(path, detectedRuntime, problems, onProgress);
8241
+ }
8242
+ if (envExists) {
8243
+ await checkKnownConflicts(path, detectedRuntime, problems, onProgress);
8244
+ }
8245
+ const hasSnapshot = await checkSnapshotAvailable(path, onProgress);
8246
+ const criticalCount = problems.filter((p) => p.severity === "critical").length;
8247
+ const warningCount = problems.filter((p) => p.severity === "warning").length;
8248
+ let status;
8249
+ if (criticalCount > 0) {
8250
+ status = "broken";
8251
+ } else if (warningCount > 0) {
8252
+ status = "degraded";
8253
+ } else if (problems.length === 0) {
8254
+ status = "healthy";
8255
+ } else {
8256
+ status = "degraded";
8257
+ }
8258
+ const repairPlan = createRepairPlan({
8259
+ id,
8260
+ timestamp: /* @__PURE__ */ new Date(),
8261
+ runtime: detectedRuntime,
8262
+ status,
8263
+ problems,
8264
+ repairPlan: [],
8265
+ estimatedTime: "",
8266
+ confidence: 0
8267
+ });
8268
+ const estimatedTime = estimateRepairTime(repairPlan);
8269
+ const confidence = calculateConfidence(problems, hasSnapshot);
8270
+ return {
8271
+ id,
8272
+ timestamp: /* @__PURE__ */ new Date(),
8273
+ runtime: detectedRuntime,
8274
+ status,
8275
+ problems,
8276
+ repairPlan,
8277
+ estimatedTime,
8278
+ confidence
8279
+ };
8280
+ }
8281
+ function createRepairPlan(diagnosis) {
8282
+ const steps = [];
8283
+ let order = 1;
8284
+ const missingEnv = diagnosis.problems.find((p) => p.category === "missing-env");
8285
+ if (missingEnv) {
8286
+ steps.push({
8287
+ id: randomUUID4(),
8288
+ order: order++,
8289
+ action: "recreate-venv",
8290
+ description: `Create new ${diagnosis.runtime} virtual environment`,
8291
+ friendlyDescription: "Setting up your project from scratch...",
8292
+ automated: true,
8293
+ risk: "safe"
8294
+ });
8295
+ return steps;
8296
+ }
8297
+ const corruptVenv = diagnosis.problems.find((p) => p.category === "corrupt-venv");
8298
+ if (corruptVenv) {
8299
+ steps.push({
8300
+ id: randomUUID4(),
8301
+ order: order++,
8302
+ action: "recreate-venv",
8303
+ description: `Recreate corrupted ${diagnosis.runtime} environment`,
8304
+ friendlyDescription: "Rebuilding your project setup...",
8305
+ automated: true,
8306
+ risk: "moderate"
8307
+ });
8308
+ return steps;
8309
+ }
8310
+ const hasSnapshotProblem = diagnosis.problems.some(
8311
+ (p) => p.category === "broken-deps" || p.category === "version-conflict"
8312
+ );
8313
+ if (hasSnapshotProblem) {
8314
+ steps.push({
8315
+ id: randomUUID4(),
8316
+ order: order++,
8317
+ action: "restore-snapshot",
8318
+ description: "Restore from last known working state",
8319
+ friendlyDescription: "Going back to when things were working...",
8320
+ automated: true,
8321
+ risk: "safe"
8322
+ });
8323
+ }
8324
+ const versionConflicts = diagnosis.problems.filter(
8325
+ (p) => p.category === "version-conflict"
8326
+ );
8327
+ for (const conflict of versionConflicts) {
8328
+ steps.push({
8329
+ id: randomUUID4(),
8330
+ order: order++,
8331
+ action: "resolve-conflicts",
8332
+ description: conflict.description,
8333
+ friendlyDescription: "Fixing incompatible package versions...",
8334
+ automated: true,
8335
+ risk: "moderate"
8336
+ });
8337
+ }
8338
+ const staleLockfile = diagnosis.problems.find((p) => p.category === "stale-lockfile");
8339
+ if (staleLockfile) {
8340
+ steps.push({
8341
+ id: randomUUID4(),
8342
+ order: order++,
8343
+ action: "reinstall-deps",
8344
+ description: "Reinstall packages from lockfile",
8345
+ friendlyDescription: "Updating your packages...",
8346
+ automated: true,
8347
+ risk: "safe"
8348
+ });
8349
+ }
8350
+ return steps;
8351
+ }
8352
+ async function executeRepair(path, diagnosis, options) {
8353
+ const { autoBackup = true, dryRun = false, onProgress } = options || {};
8354
+ const { repairPlan, runtime } = diagnosis;
8355
+ if (repairPlan.length === 0) {
8356
+ return {
8357
+ success: true,
8358
+ stepsCompleted: 0,
8359
+ stepsTotal: 0,
8360
+ problemsFixed: [],
8361
+ problemsRemaining: []
8362
+ };
8363
+ }
8364
+ let snapshotId;
8365
+ const problemsFixed = [];
8366
+ const problemsRemaining = [];
8367
+ let stepsCompleted = 0;
8368
+ if (autoBackup && !dryRun) {
8369
+ try {
8370
+ const state = await loadState(path);
8371
+ if (state.activeEnvironmentId) {
8372
+ const env = state.environments.find((e) => e.id === state.activeEnvironmentId);
8373
+ if (env) {
8374
+ const snapshot = await createSnapshot(
8375
+ path,
8376
+ env,
8377
+ "Pre-repair backup"
8378
+ );
8379
+ snapshotId = snapshot.id;
8380
+ }
8381
+ }
8382
+ } catch {
8383
+ }
8384
+ }
8385
+ for (const step of repairPlan) {
8386
+ if (dryRun) {
8387
+ onProgress?.(step, "starting");
8388
+ onProgress?.(step, "complete");
8389
+ stepsCompleted++;
8390
+ continue;
8391
+ }
8392
+ try {
8393
+ onProgress?.(step, "starting");
8394
+ switch (step.action) {
8395
+ case "recreate-venv":
8396
+ await recreateEnvironment(path, runtime);
8397
+ problemsFixed.push("Recreated environment");
8398
+ break;
8399
+ case "restore-snapshot":
8400
+ await restoreFromSnapshot(path, runtime);
8401
+ problemsFixed.push("Restored from backup");
8402
+ break;
8403
+ case "reinstall-deps":
8404
+ await reinstallDependencies(path, runtime);
8405
+ problemsFixed.push("Reinstalled dependencies");
8406
+ break;
8407
+ case "resolve-conflicts":
8408
+ await resolveConflicts2(path, runtime);
8409
+ problemsFixed.push("Resolved conflicts");
8410
+ break;
8411
+ default:
8412
+ break;
8413
+ }
8414
+ onProgress?.(step, "complete");
8415
+ stepsCompleted++;
8416
+ } catch (error) {
8417
+ onProgress?.(step, "failed");
8418
+ problemsRemaining.push(
8419
+ error instanceof Error ? error.message : "Unknown error"
8420
+ );
8421
+ }
8422
+ }
8423
+ return {
8424
+ success: stepsCompleted === repairPlan.length,
8425
+ stepsCompleted,
8426
+ stepsTotal: repairPlan.length,
8427
+ snapshotId,
8428
+ problemsFixed,
8429
+ problemsRemaining
8430
+ };
8431
+ }
8432
+ async function checkEnvironmentExists(path, runtime, problems) {
8433
+ if (runtime === "python") {
8434
+ const venvPath = join6(path, ".venv");
8435
+ if (!existsSync5(venvPath)) {
8436
+ problems.push({
8437
+ id: randomUUID4(),
8438
+ severity: "critical",
8439
+ category: "missing-env",
8440
+ title: "Virtual environment missing",
8441
+ friendlyTitle: "Project setup is missing",
8442
+ description: "No .venv directory found",
8443
+ technicalDetail: `Expected ${venvPath} to exist`
8444
+ });
8445
+ return false;
8446
+ }
8447
+ } else {
8448
+ const nodeModulesPath = join6(path, "node_modules");
8449
+ if (!existsSync5(nodeModulesPath)) {
8450
+ problems.push({
8451
+ id: randomUUID4(),
8452
+ severity: "critical",
8453
+ category: "missing-env",
8454
+ title: "node_modules missing",
8455
+ friendlyTitle: "Dependencies not installed",
8456
+ description: "No node_modules directory found",
8457
+ technicalDetail: `Expected ${nodeModulesPath} to exist`
8458
+ });
8459
+ return false;
8460
+ }
8461
+ }
8462
+ return true;
8463
+ }
8464
+ async function checkRuntimeFunctional(path, runtime, problems, onProgress) {
8465
+ onProgress?.("Checking if runtime is functional...");
8466
+ try {
8467
+ if (runtime === "python") {
8468
+ const pythonPath = join6(path, ".venv", "bin", "python");
8469
+ await execa4(pythonPath, ["--version"], { cwd: path });
8470
+ } else {
8471
+ await execa4("node", ["--version"], { cwd: path });
8472
+ }
8473
+ } catch {
8474
+ problems.push({
8475
+ id: randomUUID4(),
8476
+ severity: "critical",
8477
+ category: "corrupt-venv",
8478
+ title: `${runtime} runtime not functional`,
8479
+ friendlyTitle: "Something is broken",
8480
+ description: `Cannot invoke ${runtime} from environment`,
8481
+ technicalDetail: runtime === "python" ? "Python executable in .venv/bin/python is not working" : "Node.js is not accessible"
8482
+ });
8483
+ }
8484
+ }
8485
+ async function checkDependencyConflicts(path, runtime, problems, onProgress) {
8486
+ onProgress?.("Checking for dependency conflicts...");
8487
+ try {
8488
+ if (runtime === "python") {
8489
+ const pipPath = join6(path, ".venv", "bin", "pip");
8490
+ const result = await execa4(pipPath, ["check"], { cwd: path, reject: false });
8491
+ if (result.exitCode !== 0 && result.stdout) {
8492
+ const lines = result.stdout.split("\n").filter((l) => l.trim());
8493
+ for (const line of lines) {
8494
+ if (line.includes("requires") || line.includes("incompatible")) {
8495
+ problems.push({
8496
+ id: randomUUID4(),
8497
+ severity: "critical",
8498
+ category: "broken-deps",
8499
+ title: "Dependency conflict detected",
8500
+ friendlyTitle: "Packages don't work together",
8501
+ description: line,
8502
+ technicalDetail: "pip check failed"
8503
+ });
8504
+ }
8505
+ }
8506
+ }
8507
+ } else {
8508
+ const result = await execa4("npm", ["ls", "--json"], {
8509
+ cwd: path,
8510
+ reject: false
8511
+ });
8512
+ if (result.stdout) {
8513
+ try {
8514
+ const output = JSON.parse(result.stdout);
8515
+ if (output.problems && output.problems.length > 0) {
8516
+ for (const problem of output.problems) {
8517
+ problems.push({
8518
+ id: randomUUID4(),
8519
+ severity: "warning",
8520
+ category: "broken-deps",
8521
+ title: "Dependency issue detected",
8522
+ friendlyTitle: "Package issue found",
8523
+ description: String(problem),
8524
+ technicalDetail: "npm ls reported issues"
8525
+ });
8526
+ }
8527
+ }
8528
+ } catch {
8529
+ }
8530
+ }
8531
+ }
8532
+ } catch {
8533
+ }
8534
+ }
8535
+ async function checkLockfileDrift(path, runtime, problems, onProgress) {
8536
+ onProgress?.("Checking lockfile consistency...");
8537
+ try {
8538
+ if (runtime === "python") {
8539
+ const lockfilePath = join6(path, "requirements.lock");
8540
+ if (existsSync5(lockfilePath)) {
8541
+ const installed = await getInstalledPackages(path, runtime);
8542
+ if (installed.length === 0) {
8543
+ problems.push({
8544
+ id: randomUUID4(),
8545
+ severity: "warning",
8546
+ category: "stale-lockfile",
8547
+ title: "Lockfile exists but no packages installed",
8548
+ friendlyTitle: "Packages need to be installed",
8549
+ description: "requirements.lock exists but environment is empty"
8550
+ });
8551
+ }
8552
+ }
8553
+ } else {
8554
+ const lockfilePath = join6(path, "package-lock.json");
8555
+ if (existsSync5(lockfilePath)) {
8556
+ const nodeModulesPath = join6(path, "node_modules");
8557
+ if (!existsSync5(nodeModulesPath)) {
8558
+ problems.push({
8559
+ id: randomUUID4(),
8560
+ severity: "warning",
8561
+ category: "stale-lockfile",
8562
+ title: "Lockfile exists but node_modules missing",
8563
+ friendlyTitle: "Dependencies need installation",
8564
+ description: "package-lock.json exists but node_modules is missing"
8565
+ });
8566
+ }
8567
+ }
8568
+ }
8569
+ } catch {
8570
+ }
8571
+ }
8572
+ async function checkKnownConflicts(path, runtime, problems, onProgress) {
8573
+ onProgress?.("Checking for known conflicts...");
8574
+ try {
8575
+ const installed = await getInstalledPackages(path, runtime);
8576
+ const conflicts = detectConflicts(installed);
8577
+ for (const conflict of conflicts) {
8578
+ const severity = conflict.severity === "error" ? "critical" : conflict.severity;
8579
+ problems.push({
8580
+ id: randomUUID4(),
8581
+ severity,
8582
+ category: "version-conflict",
8583
+ title: `${conflict.package1} conflicts with ${conflict.package2}`,
8584
+ friendlyTitle: "Incompatible packages detected",
8585
+ description: conflict.reason,
8586
+ technicalDetail: conflict.suggestion
8587
+ });
8588
+ }
8589
+ } catch {
8590
+ }
8591
+ }
8592
+ async function checkSnapshotAvailable(path, onProgress) {
8593
+ onProgress?.("Checking for backups...");
8594
+ try {
8595
+ const state = await loadState(path);
8596
+ if (state.activeEnvironmentId) {
8597
+ const snapshot = await getLatestSnapshot(path, state.activeEnvironmentId);
8598
+ return snapshot !== null;
8599
+ }
8600
+ return false;
8601
+ } catch {
8602
+ return false;
8603
+ }
8604
+ }
8605
+ async function recreateEnvironment(path, runtime) {
8606
+ if (runtime === "python") {
8607
+ const venvPath = join6(path, ".venv");
8608
+ if (existsSync5(venvPath)) {
8609
+ await execa4("rm", ["-rf", venvPath], { cwd: path });
8610
+ }
8611
+ await execa4("python3", ["-m", "venv", ".venv"], { cwd: path });
8612
+ } else {
8613
+ await execa4("npm", ["install"], { cwd: path });
8614
+ }
8615
+ }
8616
+ async function restoreFromSnapshot(path, runtime) {
8617
+ const state = await loadState(path);
8618
+ if (!state.activeEnvironmentId) {
8619
+ throw new Error("No active environment found");
8620
+ }
8621
+ const snapshot = await getLatestSnapshot(path, state.activeEnvironmentId);
8622
+ if (!snapshot) {
8623
+ throw new Error("No snapshot available");
8624
+ }
8625
+ if (runtime === "python") {
8626
+ await restorePythonEnvironment(path, snapshot.lockfileContent);
8627
+ } else {
8628
+ await restoreNodeEnvironment(path, snapshot.lockfileContent);
8629
+ }
8630
+ }
8631
+ async function reinstallDependencies(path, runtime) {
8632
+ if (runtime === "python") {
8633
+ const lockfilePath = join6(path, "requirements.lock");
8634
+ if (existsSync5(lockfilePath)) {
8635
+ const pipPath = join6(path, ".venv", "bin", "pip");
8636
+ await execa4(pipPath, ["install", "-r", "requirements.lock"], { cwd: path });
8637
+ }
8638
+ } else {
8639
+ await execa4("npm", ["ci"], { cwd: path });
8640
+ }
8641
+ }
8642
+ async function resolveConflicts2(path, runtime) {
8643
+ await reinstallDependencies(path, runtime);
8644
+ }
8645
+ function estimateRepairTime(steps) {
8646
+ if (steps.length === 0) return "None needed";
8647
+ if (steps.length === 1) return "~1 minute";
8648
+ if (steps.length <= 3) return "~2 minutes";
8649
+ return "~5 minutes";
8650
+ }
8651
+ function calculateConfidence(problems, hasSnapshot) {
8652
+ if (problems.length === 0) return 1;
8653
+ const criticalCount = problems.filter((p) => p.severity === "critical").length;
8654
+ if (criticalCount > 2) return hasSnapshot ? 0.7 : 0.5;
8655
+ if (criticalCount > 0) return hasSnapshot ? 0.85 : 0.7;
8656
+ return 0.9;
8657
+ }
8658
+
8659
+ export {
8660
+ __require,
8661
+ __commonJS,
8662
+ __toESM,
8663
+ loadState,
8664
+ KomodoChat,
8665
+ getInstalledPackages,
8666
+ detectConflicts,
8667
+ analyzeHealth,
8668
+ detectRealConflicts,
8669
+ resolveConflicts,
8670
+ Komodo,
8671
+ runDoctor,
8672
+ formatDoctorReport,
8673
+ buildDependencyTree,
8674
+ visualizeTree,
8675
+ diffPackages,
8676
+ formatDiff,
8677
+ analyzeOptimizations,
8678
+ formatOptimizationReport,
8679
+ generateOptimizationScript,
8680
+ TEMPLATES,
8681
+ getTemplateById,
8682
+ searchTemplates,
8683
+ formatTemplateList,
8684
+ getInstallCommandsForTemplate,
8685
+ explainPackage,
8686
+ formatExplanation,
8687
+ parseGitHubUrl,
8688
+ analyzeRepoStructure,
8689
+ formatRepoAnalysis,
8690
+ analyzeEnvironment,
8691
+ formatInsights,
8692
+ diagnoseEnvironment,
8693
+ executeRepair
5717
8694
  };