komodo-cli 2.1.1 → 2.2.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.
@@ -5703,6 +5703,2121 @@ function analyzeHealth(packages, conflicts) {
5703
5703
  score: Math.max(0, score)
5704
5704
  };
5705
5705
  }
5706
+ var KNOWN_VULNERABILITIES = {
5707
+ "torch": [
5708
+ { versions: "<1.13.0", severity: "high", description: "Arbitrary code execution via pickle deserialization", fix: ">=2.0.0" }
5709
+ ],
5710
+ "numpy": [
5711
+ { versions: "<1.22.0", severity: "medium", description: "Buffer overflow in array operations", fix: ">=1.22.0" }
5712
+ ],
5713
+ "pillow": [
5714
+ { versions: "<9.0.0", severity: "high", description: "Multiple image parsing vulnerabilities", fix: ">=10.0.0" }
5715
+ ],
5716
+ "requests": [
5717
+ { versions: "<2.31.0", severity: "medium", description: "Potential credential leak in redirects", fix: ">=2.31.0" }
5718
+ ],
5719
+ "urllib3": [
5720
+ { versions: "<2.0.0", severity: "medium", description: "Cookie handling vulnerability", fix: ">=2.0.0" }
5721
+ ],
5722
+ "django": [
5723
+ { versions: "<4.2.0", severity: "high", description: "SQL injection and XSS vulnerabilities", fix: ">=4.2.0" }
5724
+ ],
5725
+ "flask": [
5726
+ { versions: "<2.3.0", severity: "medium", description: "Security header improvements needed", fix: ">=2.3.0" }
5727
+ ],
5728
+ "lodash": [
5729
+ { versions: "<4.17.21", severity: "high", description: "Prototype pollution vulnerability", fix: ">=4.17.21" }
5730
+ ],
5731
+ "axios": [
5732
+ { versions: "<1.6.0", severity: "medium", description: "SSRF vulnerability", fix: ">=1.6.0" }
5733
+ ],
5734
+ "express": [
5735
+ { versions: "<4.19.0", severity: "medium", description: "Open redirect vulnerability", fix: ">=4.19.0" }
5736
+ ]
5737
+ };
5738
+ var HEAVY_PACKAGES = {
5739
+ "moment": { alternative: "dayjs", savingsMb: 280, note: "dayjs is 2KB vs moment's 300KB, same API" },
5740
+ "lodash": { alternative: "lodash-es or native", savingsMb: 70, note: "Use lodash-es for tree-shaking or native JS methods" },
5741
+ "jquery": { alternative: "native DOM APIs", savingsMb: 85, note: "Modern browsers don't need jQuery" },
5742
+ "underscore": { alternative: "lodash-es or native", savingsMb: 20, note: "lodash is a superset with better performance" },
5743
+ "request": { alternative: "axios or fetch", savingsMb: 50, note: "request is deprecated, use axios or native fetch" },
5744
+ "bluebird": { alternative: "native Promises", savingsMb: 80, note: "Native Promises are now performant enough" }
5745
+ };
5746
+ var CONFLICT_PAIRS = [
5747
+ ["tensorflow", "torch", "Both are heavy ML frameworks - pick one unless you need both"],
5748
+ ["tensorflow-gpu", "tensorflow", "Use tensorflow>=2.0 which includes GPU support"],
5749
+ ["opencv-python", "opencv-python-headless", "Use headless version for servers"],
5750
+ ["pillow", "PIL", "PIL is deprecated, Pillow is the maintained fork"]
5751
+ ];
5752
+ function compareVersions(v1, v2) {
5753
+ const parts1 = v1.replace(/[^0-9.]/g, "").split(".").map(Number);
5754
+ const parts2 = v2.replace(/[^0-9.]/g, "").split(".").map(Number);
5755
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
5756
+ const p1 = parts1[i] || 0;
5757
+ const p2 = parts2[i] || 0;
5758
+ if (p1 < p2) return -1;
5759
+ if (p1 > p2) return 1;
5760
+ }
5761
+ return 0;
5762
+ }
5763
+ function checkVersionConstraint(version, constraint) {
5764
+ const cleanVersion = version.replace(/[^0-9.]/g, "");
5765
+ if (constraint.startsWith("<")) {
5766
+ const targetVersion = constraint.slice(1);
5767
+ return compareVersions(cleanVersion, targetVersion) < 0;
5768
+ }
5769
+ if (constraint.startsWith(">=")) {
5770
+ const targetVersion = constraint.slice(2);
5771
+ return compareVersions(cleanVersion, targetVersion) >= 0;
5772
+ }
5773
+ if (constraint.startsWith("<=")) {
5774
+ const targetVersion = constraint.slice(2);
5775
+ return compareVersions(cleanVersion, targetVersion) <= 0;
5776
+ }
5777
+ if (constraint.startsWith(">")) {
5778
+ const targetVersion = constraint.slice(1);
5779
+ return compareVersions(cleanVersion, targetVersion) > 0;
5780
+ }
5781
+ return false;
5782
+ }
5783
+ function runDoctor(packages, hardware, options = {}) {
5784
+ const {
5785
+ checkSecurity = true,
5786
+ checkPerformance = true,
5787
+ checkOutdated = true,
5788
+ checkUnused = true
5789
+ } = options;
5790
+ const issues = [];
5791
+ let vulnerabilityCount = 0;
5792
+ let outdatedCount = 0;
5793
+ let unusedCount = 0;
5794
+ if (checkSecurity) {
5795
+ for (const pkg of packages) {
5796
+ const vulns = KNOWN_VULNERABILITIES[pkg.name.toLowerCase()];
5797
+ if (vulns) {
5798
+ for (const vuln of vulns) {
5799
+ if (checkVersionConstraint(pkg.version, vuln.versions)) {
5800
+ vulnerabilityCount++;
5801
+ issues.push({
5802
+ id: `vuln-${pkg.name}-${vuln.severity}`,
5803
+ severity: vuln.severity === "high" ? "critical" : "warning",
5804
+ category: "security",
5805
+ title: `Security vulnerability in ${pkg.name}`,
5806
+ description: vuln.description,
5807
+ package: pkg.name,
5808
+ fix: {
5809
+ type: "upgrade",
5810
+ packages: [`${pkg.name}${vuln.fix}`],
5811
+ description: `Upgrade to ${pkg.name}${vuln.fix}`,
5812
+ automated: true
5813
+ },
5814
+ learnMore: `https://security.snyk.io/package/pip/${pkg.name}`
5815
+ });
5816
+ }
5817
+ }
5818
+ }
5819
+ }
5820
+ }
5821
+ if (checkPerformance) {
5822
+ for (const pkg of packages) {
5823
+ const heavy = HEAVY_PACKAGES[pkg.name.toLowerCase()];
5824
+ if (heavy) {
5825
+ issues.push({
5826
+ id: `perf-${pkg.name}`,
5827
+ severity: "suggestion",
5828
+ category: "performance",
5829
+ title: `${pkg.name} has a lighter alternative`,
5830
+ description: heavy.note,
5831
+ package: pkg.name,
5832
+ fix: {
5833
+ type: "install",
5834
+ packages: [heavy.alternative.split(" ")[0] || heavy.alternative],
5835
+ description: `Consider replacing with ${heavy.alternative} (saves ~${heavy.savingsMb}KB)`,
5836
+ automated: false
5837
+ }
5838
+ });
5839
+ }
5840
+ }
5841
+ const packageNames = packages.map((p) => p.name.toLowerCase());
5842
+ for (const [pkg1, pkg2, reason] of CONFLICT_PAIRS) {
5843
+ if (packageNames.includes(pkg1) && packageNames.includes(pkg2)) {
5844
+ issues.push({
5845
+ id: `dup-${pkg1}-${pkg2}`,
5846
+ severity: "warning",
5847
+ category: "performance",
5848
+ title: `Potentially redundant packages: ${pkg1} + ${pkg2}`,
5849
+ description: reason,
5850
+ fix: {
5851
+ type: "uninstall",
5852
+ description: `Consider removing one of these packages`,
5853
+ automated: false
5854
+ }
5855
+ });
5856
+ }
5857
+ }
5858
+ if (hardware.gpu === "none") {
5859
+ const gpuPackages = packages.filter(
5860
+ (p) => p.name.toLowerCase().includes("cuda") || p.name.toLowerCase().includes("-gpu") || p.name.toLowerCase().includes("cudf")
5861
+ );
5862
+ for (const pkg of gpuPackages) {
5863
+ issues.push({
5864
+ id: `hw-${pkg.name}`,
5865
+ severity: "warning",
5866
+ category: "compatibility",
5867
+ title: `GPU package on CPU-only system`,
5868
+ description: `${pkg.name} requires a GPU but none was detected`,
5869
+ package: pkg.name,
5870
+ fix: {
5871
+ type: "uninstall",
5872
+ packages: [pkg.name],
5873
+ description: `Remove ${pkg.name} or switch to CPU version`,
5874
+ automated: true
5875
+ }
5876
+ });
5877
+ }
5878
+ }
5879
+ if (hardware.totalMemoryGb < 8) {
5880
+ const heavyMLPackages = packages.filter(
5881
+ (p) => ["tensorflow", "torch", "pytorch", "transformers", "jax"].includes(p.name.toLowerCase())
5882
+ );
5883
+ if (heavyMLPackages.length > 0) {
5884
+ issues.push({
5885
+ id: "hw-memory-ml",
5886
+ severity: "warning",
5887
+ category: "compatibility",
5888
+ title: "Heavy ML packages with limited RAM",
5889
+ description: `You have ${hardware.totalMemoryGb}GB RAM but ML packages typically need 8GB+`,
5890
+ fix: {
5891
+ type: "config",
5892
+ description: "Consider using cloud instances or reducing model sizes",
5893
+ automated: false
5894
+ }
5895
+ });
5896
+ }
5897
+ }
5898
+ }
5899
+ const conflicts = detectConflicts(packages);
5900
+ for (const conflict of conflicts) {
5901
+ issues.push({
5902
+ id: `conflict-${conflict.package1}-${conflict.package2}`,
5903
+ severity: conflict.severity === "error" ? "critical" : "warning",
5904
+ category: "compatibility",
5905
+ title: `Conflict: ${conflict.package1} and ${conflict.package2}`,
5906
+ description: conflict.reason,
5907
+ fix: conflict.suggestion ? {
5908
+ type: "command",
5909
+ command: conflict.suggestion,
5910
+ description: conflict.suggestion,
5911
+ automated: false
5912
+ } : void 0
5913
+ });
5914
+ }
5915
+ if (checkOutdated) {
5916
+ const oldVersionIndicators = ["0.", "1.0", "1.1", "1.2"];
5917
+ for (const pkg of packages) {
5918
+ if (oldVersionIndicators.some((v) => pkg.version.startsWith(v))) {
5919
+ outdatedCount++;
5920
+ issues.push({
5921
+ id: `outdated-${pkg.name}`,
5922
+ severity: "info",
5923
+ category: "outdated",
5924
+ title: `${pkg.name} may be outdated`,
5925
+ description: `Version ${pkg.version} - consider checking for updates`,
5926
+ package: pkg.name,
5927
+ fix: {
5928
+ type: "upgrade",
5929
+ packages: [pkg.name],
5930
+ description: `Run: pip install --upgrade ${pkg.name}`,
5931
+ automated: true
5932
+ }
5933
+ });
5934
+ }
5935
+ }
5936
+ }
5937
+ const criticalCount = issues.filter((i) => i.severity === "critical").length;
5938
+ const warningCount = issues.filter((i) => i.severity === "warning").length;
5939
+ const suggestionCount = issues.filter((i) => i.severity === "suggestion" || i.severity === "info").length;
5940
+ let score = 100;
5941
+ score -= criticalCount * 20;
5942
+ score -= warningCount * 10;
5943
+ score -= suggestionCount * 2;
5944
+ score = Math.max(0, Math.min(100, score));
5945
+ const grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 60 ? "D" : "F";
5946
+ const recommendations = [];
5947
+ if (vulnerabilityCount > 0) {
5948
+ recommendations.push(`Fix ${vulnerabilityCount} security vulnerabilities immediately`);
5949
+ }
5950
+ if (criticalCount > 0) {
5951
+ recommendations.push(`Address ${criticalCount} critical issues`);
5952
+ }
5953
+ if (hardware.gpu !== "none" && !packages.some((p) => p.name.toLowerCase().includes("cuda"))) {
5954
+ recommendations.push("You have a GPU - consider installing CUDA packages for acceleration");
5955
+ }
5956
+ if (packages.length > 50) {
5957
+ recommendations.push("Large number of packages - consider auditing for unused dependencies");
5958
+ }
5959
+ return {
5960
+ timestamp: /* @__PURE__ */ new Date(),
5961
+ score,
5962
+ grade,
5963
+ issues,
5964
+ summary: {
5965
+ critical: criticalCount,
5966
+ warnings: warningCount,
5967
+ suggestions: suggestionCount,
5968
+ packagesScanned: packages.length,
5969
+ vulnerabilities: vulnerabilityCount,
5970
+ outdated: outdatedCount,
5971
+ unused: unusedCount
5972
+ },
5973
+ hardware,
5974
+ recommendations
5975
+ };
5976
+ }
5977
+ function formatDoctorReport(report, colorize = true) {
5978
+ const lines = [];
5979
+ const colors = {
5980
+ reset: colorize ? "\x1B[0m" : "",
5981
+ red: colorize ? "\x1B[31m" : "",
5982
+ yellow: colorize ? "\x1B[33m" : "",
5983
+ green: colorize ? "\x1B[32m" : "",
5984
+ blue: colorize ? "\x1B[34m" : "",
5985
+ cyan: colorize ? "\x1B[36m" : "",
5986
+ bold: colorize ? "\x1B[1m" : "",
5987
+ dim: colorize ? "\x1B[2m" : ""
5988
+ };
5989
+ lines.push("");
5990
+ lines.push(`${colors.bold}Komodo Doctor Report${colors.reset}`);
5991
+ lines.push(`${colors.dim}${report.timestamp.toLocaleString()}${colors.reset}`);
5992
+ lines.push("");
5993
+ const gradeColor = report.grade === "A" ? colors.green : report.grade === "B" ? colors.green : report.grade === "C" ? colors.yellow : colors.red;
5994
+ lines.push(`${colors.bold}Health Score: ${gradeColor}${report.score}/100 (${report.grade})${colors.reset}`);
5995
+ lines.push("");
5996
+ lines.push(`${colors.bold}Summary${colors.reset}`);
5997
+ lines.push(` Packages scanned: ${report.summary.packagesScanned}`);
5998
+ if (report.summary.critical > 0) {
5999
+ lines.push(` ${colors.red}Critical issues: ${report.summary.critical}${colors.reset}`);
6000
+ }
6001
+ if (report.summary.warnings > 0) {
6002
+ lines.push(` ${colors.yellow}Warnings: ${report.summary.warnings}${colors.reset}`);
6003
+ }
6004
+ if (report.summary.vulnerabilities > 0) {
6005
+ lines.push(` ${colors.red}Vulnerabilities: ${report.summary.vulnerabilities}${colors.reset}`);
6006
+ }
6007
+ lines.push(` Suggestions: ${report.summary.suggestions}`);
6008
+ lines.push("");
6009
+ const criticalIssues = report.issues.filter((i) => i.severity === "critical");
6010
+ const warningIssues = report.issues.filter((i) => i.severity === "warning");
6011
+ const infoIssues = report.issues.filter((i) => i.severity === "info" || i.severity === "suggestion");
6012
+ if (criticalIssues.length > 0) {
6013
+ lines.push(`${colors.red}${colors.bold}Critical Issues${colors.reset}`);
6014
+ for (const issue of criticalIssues) {
6015
+ lines.push(` ${colors.red}\u2716${colors.reset} ${issue.title}`);
6016
+ lines.push(` ${colors.dim}${issue.description}${colors.reset}`);
6017
+ if (issue.fix) {
6018
+ lines.push(` ${colors.cyan}Fix: ${issue.fix.description}${colors.reset}`);
6019
+ }
6020
+ }
6021
+ lines.push("");
6022
+ }
6023
+ if (warningIssues.length > 0) {
6024
+ lines.push(`${colors.yellow}${colors.bold}Warnings${colors.reset}`);
6025
+ for (const issue of warningIssues) {
6026
+ lines.push(` ${colors.yellow}\u26A0${colors.reset} ${issue.title}`);
6027
+ lines.push(` ${colors.dim}${issue.description}${colors.reset}`);
6028
+ if (issue.fix) {
6029
+ lines.push(` ${colors.cyan}Fix: ${issue.fix.description}${colors.reset}`);
6030
+ }
6031
+ }
6032
+ lines.push("");
6033
+ }
6034
+ if (infoIssues.length > 0) {
6035
+ lines.push(`${colors.blue}${colors.bold}Suggestions${colors.reset}`);
6036
+ for (const issue of infoIssues.slice(0, 5)) {
6037
+ lines.push(` ${colors.blue}\u2139${colors.reset} ${issue.title}`);
6038
+ if (issue.fix) {
6039
+ lines.push(` ${colors.dim}${issue.fix.description}${colors.reset}`);
6040
+ }
6041
+ }
6042
+ if (infoIssues.length > 5) {
6043
+ lines.push(` ${colors.dim}...and ${infoIssues.length - 5} more suggestions${colors.reset}`);
6044
+ }
6045
+ lines.push("");
6046
+ }
6047
+ if (report.recommendations.length > 0) {
6048
+ lines.push(`${colors.bold}Recommendations${colors.reset}`);
6049
+ for (const rec of report.recommendations) {
6050
+ lines.push(` ${colors.green}\u2192${colors.reset} ${rec}`);
6051
+ }
6052
+ lines.push("");
6053
+ }
6054
+ lines.push(`${colors.dim}System: ${report.hardware.os} | ${report.hardware.gpuName || report.hardware.gpu} | ${report.hardware.totalMemoryGb}GB RAM${colors.reset}`);
6055
+ return lines.join("\n");
6056
+ }
6057
+ var COMMON_DEPENDENCIES = {
6058
+ "torch": ["numpy", "typing-extensions", "sympy", "networkx", "jinja2", "filelock"],
6059
+ "torchvision": ["torch", "numpy", "pillow", "requests"],
6060
+ "tensorflow": ["numpy", "protobuf", "absl-py", "grpcio", "h5py", "keras"],
6061
+ "transformers": ["torch", "numpy", "tokenizers", "safetensors", "huggingface-hub", "regex", "requests", "tqdm"],
6062
+ "pandas": ["numpy", "python-dateutil", "pytz", "tzdata"],
6063
+ "numpy": [],
6064
+ "scikit-learn": ["numpy", "scipy", "joblib", "threadpoolctl"],
6065
+ "matplotlib": ["numpy", "pillow", "pyparsing", "cycler", "kiwisolver", "fonttools", "packaging"],
6066
+ "requests": ["urllib3", "certifi", "charset-normalizer", "idna"],
6067
+ "flask": ["werkzeug", "jinja2", "itsdangerous", "click", "blinker"],
6068
+ "django": ["asgiref", "sqlparse", "tzdata"],
6069
+ "fastapi": ["starlette", "pydantic", "typing-extensions"],
6070
+ "react": [],
6071
+ "next": ["react", "react-dom"],
6072
+ "express": [],
6073
+ "axios": [],
6074
+ "lodash": []
6075
+ };
6076
+ function buildDependencyTree(packages) {
6077
+ const nodes = /* @__PURE__ */ new Map();
6078
+ const packageNames = new Set(packages.map((p) => p.name.toLowerCase()));
6079
+ for (const pkg of packages) {
6080
+ const name = pkg.name.toLowerCase();
6081
+ const deps = COMMON_DEPENDENCIES[name] || [];
6082
+ const filteredDeps = deps.filter((d) => packageNames.has(d.toLowerCase()));
6083
+ nodes.set(name, {
6084
+ name: pkg.name,
6085
+ version: pkg.version,
6086
+ depth: 0,
6087
+ dependencies: [],
6088
+ dependents: [],
6089
+ size: estimatePackageSize(pkg.name)
6090
+ });
6091
+ }
6092
+ for (const pkg of packages) {
6093
+ const name = pkg.name.toLowerCase();
6094
+ const node = nodes.get(name);
6095
+ const deps = COMMON_DEPENDENCIES[name] || [];
6096
+ for (const depName of deps) {
6097
+ const depNode = nodes.get(depName.toLowerCase());
6098
+ if (depNode) {
6099
+ node.dependencies.push(depNode);
6100
+ depNode.dependents.push(pkg.name);
6101
+ }
6102
+ }
6103
+ }
6104
+ const rootPackages = [...nodes.values()].filter((n) => n.dependents.length === 0);
6105
+ let maxDepth = 0;
6106
+ function setDepths(node, depth, visited) {
6107
+ if (visited.has(node.name.toLowerCase())) return;
6108
+ visited.add(node.name.toLowerCase());
6109
+ node.depth = Math.max(node.depth, depth);
6110
+ maxDepth = Math.max(maxDepth, depth);
6111
+ for (const dep of node.dependencies) {
6112
+ setDepths(dep, depth + 1, visited);
6113
+ }
6114
+ }
6115
+ for (const root of rootPackages) {
6116
+ setDepths(root, 0, /* @__PURE__ */ new Set());
6117
+ }
6118
+ const conflicts = [];
6119
+ const depVersions = /* @__PURE__ */ new Map();
6120
+ for (const node of nodes.values()) {
6121
+ if (!depVersions.has(node.name)) {
6122
+ depVersions.set(node.name, /* @__PURE__ */ new Set());
6123
+ }
6124
+ depVersions.get(node.name).add(node.version);
6125
+ }
6126
+ return {
6127
+ root: rootPackages[0]?.name || "project",
6128
+ nodes,
6129
+ totalPackages: packages.length,
6130
+ maxDepth,
6131
+ conflicts
6132
+ };
6133
+ }
6134
+ function estimatePackageSize(name) {
6135
+ const sizes = {
6136
+ "torch": 85e4,
6137
+ "tensorflow": 55e4,
6138
+ "transformers": 45e3,
6139
+ "numpy": 15e3,
6140
+ "pandas": 12e3,
6141
+ "scipy": 35e3,
6142
+ "matplotlib": 8e3,
6143
+ "pillow": 3e3,
6144
+ "scikit-learn": 25e3,
6145
+ "opencv-python": 45e3,
6146
+ "requests": 500,
6147
+ "flask": 800,
6148
+ "django": 8e3,
6149
+ "fastapi": 400,
6150
+ "react": 150,
6151
+ "next": 5e3,
6152
+ "express": 200,
6153
+ "lodash": 1500,
6154
+ "axios": 100
6155
+ };
6156
+ return sizes[name.toLowerCase()] || 500;
6157
+ }
6158
+ function visualizeTree(tree, options = {}) {
6159
+ const { maxDepth = 5, showSizes = true, colorize = true } = options;
6160
+ const colors = {
6161
+ reset: colorize ? "\x1B[0m" : "",
6162
+ red: colorize ? "\x1B[31m" : "",
6163
+ yellow: colorize ? "\x1B[33m" : "",
6164
+ green: colorize ? "\x1B[32m" : "",
6165
+ blue: colorize ? "\x1B[34m" : "",
6166
+ cyan: colorize ? "\x1B[36m" : "",
6167
+ dim: colorize ? "\x1B[2m" : "",
6168
+ bold: colorize ? "\x1B[1m" : ""
6169
+ };
6170
+ const lines = [];
6171
+ const visited = /* @__PURE__ */ new Set();
6172
+ const roots = [...tree.nodes.values()].filter((n) => n.dependents.length === 0);
6173
+ function formatSize(kb) {
6174
+ if (kb >= 1e3) return `${(kb / 1e3).toFixed(1)}MB`;
6175
+ return `${kb}KB`;
6176
+ }
6177
+ function drawNode(node, prefix, isLast, depth) {
6178
+ if (depth > maxDepth) return;
6179
+ if (visited.has(node.name.toLowerCase())) {
6180
+ lines.push(`${prefix}${isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${colors.dim}${node.name} (circular)${colors.reset}`);
6181
+ return;
6182
+ }
6183
+ visited.add(node.name.toLowerCase());
6184
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
6185
+ const sizeStr = showSizes && node.size ? ` ${colors.dim}(${formatSize(node.size)})${colors.reset}` : "";
6186
+ const conflictStr = node.isConflict ? ` ${colors.red}\u26A0 conflicts with ${node.conflictWith}${colors.reset}` : "";
6187
+ const nameColor = node.dependencies.length > 0 ? colors.cyan : colors.green;
6188
+ lines.push(`${prefix}${connector}${nameColor}${node.name}${colors.reset}@${colors.dim}${node.version}${colors.reset}${sizeStr}${conflictStr}`);
6189
+ const newPrefix = prefix + (isLast ? " " : "\u2502 ");
6190
+ const deps = node.dependencies;
6191
+ for (let i = 0; i < deps.length; i++) {
6192
+ const dep = deps[i];
6193
+ if (dep) drawNode(dep, newPrefix, i === deps.length - 1, depth + 1);
6194
+ }
6195
+ }
6196
+ lines.push(`${colors.bold}Dependency Tree${colors.reset}`);
6197
+ lines.push(`${colors.dim}${tree.totalPackages} packages, max depth ${tree.maxDepth}${colors.reset}`);
6198
+ lines.push("");
6199
+ for (let i = 0; i < roots.length; i++) {
6200
+ const root = roots[i];
6201
+ if (!root) continue;
6202
+ lines.push(`${colors.bold}${root.name}${colors.reset}@${root.version}`);
6203
+ const deps = root.dependencies;
6204
+ for (let j = 0; j < deps.length; j++) {
6205
+ const dep = deps[j];
6206
+ if (dep) drawNode(dep, "", j === deps.length - 1, 1);
6207
+ }
6208
+ if (i < roots.length - 1) lines.push("");
6209
+ }
6210
+ const orphans = [...tree.nodes.values()].filter(
6211
+ (n) => n.dependencies.length === 0 && n.dependents.length === 0
6212
+ );
6213
+ if (orphans.length > 0 && orphans.length < tree.totalPackages) {
6214
+ lines.push("");
6215
+ lines.push(`${colors.dim}Standalone packages:${colors.reset}`);
6216
+ for (const orphan of orphans.slice(0, 10)) {
6217
+ lines.push(` ${orphan.name}@${orphan.version}`);
6218
+ }
6219
+ if (orphans.length > 10) {
6220
+ lines.push(` ${colors.dim}...and ${orphans.length - 10} more${colors.reset}`);
6221
+ }
6222
+ }
6223
+ const directDeps = roots.reduce((sum, r) => sum + r.dependencies.length, 0);
6224
+ const transitiveDeps = tree.totalPackages - roots.length - directDeps;
6225
+ const sortedBySize = [...tree.nodes.values()].filter((n) => n.size).sort((a, b) => (b.size || 0) - (a.size || 0)).slice(0, 5);
6226
+ return {
6227
+ ascii: lines.join("\n"),
6228
+ json: {
6229
+ roots: roots.map((r) => ({
6230
+ name: r.name,
6231
+ version: r.version,
6232
+ dependencies: r.dependencies.map((d) => d.name)
6233
+ })),
6234
+ totalPackages: tree.totalPackages,
6235
+ maxDepth: tree.maxDepth
6236
+ },
6237
+ stats: {
6238
+ totalPackages: tree.totalPackages,
6239
+ directDependencies: directDeps,
6240
+ transitiveDependencies: Math.max(0, transitiveDeps),
6241
+ maxDepth: tree.maxDepth,
6242
+ largestPackages: sortedBySize.map((n) => ({ name: n.name, size: n.size || 0 }))
6243
+ }
6244
+ };
6245
+ }
6246
+ function compareVersions2(v1, v2) {
6247
+ const parts1 = v1.replace(/[^0-9.]/g, "").split(".").map(Number);
6248
+ const parts2 = v2.replace(/[^0-9.]/g, "").split(".").map(Number);
6249
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
6250
+ const p1 = parts1[i] || 0;
6251
+ const p2 = parts2[i] || 0;
6252
+ if (p1 < p2) return -1;
6253
+ if (p1 > p2) return 1;
6254
+ }
6255
+ return 0;
6256
+ }
6257
+ function diffPackages(oldPackages, newPackages) {
6258
+ const changes = [];
6259
+ const riskReasons = [];
6260
+ const oldMap = new Map(oldPackages.map((p) => [p.name.toLowerCase(), p]));
6261
+ const newMap = new Map(newPackages.map((p) => [p.name.toLowerCase(), p]));
6262
+ for (const [name, newPkg] of newMap) {
6263
+ const oldPkg = oldMap.get(name);
6264
+ if (!oldPkg) {
6265
+ changes.push({
6266
+ name: newPkg.name,
6267
+ type: "added",
6268
+ newVersion: newPkg.version
6269
+ });
6270
+ } else if (oldPkg.version !== newPkg.version) {
6271
+ const cmp = compareVersions2(oldPkg.version, newPkg.version);
6272
+ changes.push({
6273
+ name: newPkg.name,
6274
+ type: cmp < 0 ? "upgraded" : "downgraded",
6275
+ oldVersion: oldPkg.version,
6276
+ newVersion: newPkg.version
6277
+ });
6278
+ } else {
6279
+ changes.push({
6280
+ name: newPkg.name,
6281
+ type: "unchanged",
6282
+ oldVersion: oldPkg.version,
6283
+ newVersion: newPkg.version
6284
+ });
6285
+ }
6286
+ }
6287
+ for (const [name, oldPkg] of oldMap) {
6288
+ if (!newMap.has(name)) {
6289
+ changes.push({
6290
+ name: oldPkg.name,
6291
+ type: "removed",
6292
+ oldVersion: oldPkg.version
6293
+ });
6294
+ }
6295
+ }
6296
+ const summary = {
6297
+ added: changes.filter((c) => c.type === "added").length,
6298
+ removed: changes.filter((c) => c.type === "removed").length,
6299
+ upgraded: changes.filter((c) => c.type === "upgraded").length,
6300
+ downgraded: changes.filter((c) => c.type === "downgraded").length,
6301
+ unchanged: changes.filter((c) => c.type === "unchanged").length
6302
+ };
6303
+ let riskLevel = "safe";
6304
+ const criticalPackages = ["torch", "tensorflow", "numpy", "pandas", "react", "vue", "angular"];
6305
+ const majorChanges = changes.filter((c) => {
6306
+ if (c.type === "upgraded" || c.type === "downgraded") {
6307
+ const oldMajor = parseInt(c.oldVersion?.split(".")[0] || "0");
6308
+ const newMajor = parseInt(c.newVersion?.split(".")[0] || "0");
6309
+ return oldMajor !== newMajor;
6310
+ }
6311
+ return false;
6312
+ });
6313
+ if (majorChanges.length > 0) {
6314
+ riskLevel = "moderate";
6315
+ for (const change of majorChanges) {
6316
+ riskReasons.push(`Major version change: ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}`);
6317
+ }
6318
+ }
6319
+ const criticalChanges = changes.filter(
6320
+ (c) => criticalPackages.includes(c.name.toLowerCase()) && (c.type === "removed" || c.type === "downgraded")
6321
+ );
6322
+ if (criticalChanges.length > 0) {
6323
+ riskLevel = "risky";
6324
+ for (const change of criticalChanges) {
6325
+ if (change.type === "removed") {
6326
+ riskReasons.push(`Critical package removed: ${change.name}`);
6327
+ } else {
6328
+ riskReasons.push(`Critical package downgraded: ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}`);
6329
+ }
6330
+ }
6331
+ }
6332
+ if (summary.removed > 5) {
6333
+ riskLevel = riskLevel === "safe" ? "moderate" : riskLevel;
6334
+ riskReasons.push(`${summary.removed} packages removed`);
6335
+ }
6336
+ return {
6337
+ from: "old",
6338
+ to: "new",
6339
+ timestamp: /* @__PURE__ */ new Date(),
6340
+ changes: changes.filter((c) => c.type !== "unchanged"),
6341
+ summary,
6342
+ riskLevel,
6343
+ riskReasons
6344
+ };
6345
+ }
6346
+ function formatDiff(diff, colorize = true) {
6347
+ const colors = {
6348
+ reset: colorize ? "\x1B[0m" : "",
6349
+ red: colorize ? "\x1B[31m" : "",
6350
+ yellow: colorize ? "\x1B[33m" : "",
6351
+ green: colorize ? "\x1B[32m" : "",
6352
+ blue: colorize ? "\x1B[34m" : "",
6353
+ cyan: colorize ? "\x1B[36m" : "",
6354
+ dim: colorize ? "\x1B[2m" : "",
6355
+ bold: colorize ? "\x1B[1m" : ""
6356
+ };
6357
+ const lines = [];
6358
+ lines.push(`${colors.bold}Environment Diff${colors.reset}`);
6359
+ lines.push(`${colors.dim}${diff.from} \u2192 ${diff.to}${colors.reset}`);
6360
+ lines.push("");
6361
+ const riskColor = diff.riskLevel === "safe" ? colors.green : diff.riskLevel === "moderate" ? colors.yellow : colors.red;
6362
+ const riskIcon = diff.riskLevel === "safe" ? "\u2713" : diff.riskLevel === "moderate" ? "\u26A0" : "\u2716";
6363
+ lines.push(`${riskColor}${riskIcon} Risk: ${diff.riskLevel.toUpperCase()}${colors.reset}`);
6364
+ if (diff.riskReasons.length > 0) {
6365
+ for (const reason of diff.riskReasons) {
6366
+ lines.push(` ${colors.dim}\u2022 ${reason}${colors.reset}`);
6367
+ }
6368
+ }
6369
+ lines.push("");
6370
+ lines.push(`${colors.bold}Summary${colors.reset}`);
6371
+ if (diff.summary.added > 0) {
6372
+ lines.push(` ${colors.green}+ ${diff.summary.added} added${colors.reset}`);
6373
+ }
6374
+ if (diff.summary.removed > 0) {
6375
+ lines.push(` ${colors.red}- ${diff.summary.removed} removed${colors.reset}`);
6376
+ }
6377
+ if (diff.summary.upgraded > 0) {
6378
+ lines.push(` ${colors.cyan}\u2191 ${diff.summary.upgraded} upgraded${colors.reset}`);
6379
+ }
6380
+ if (diff.summary.downgraded > 0) {
6381
+ lines.push(` ${colors.yellow}\u2193 ${diff.summary.downgraded} downgraded${colors.reset}`);
6382
+ }
6383
+ lines.push(` ${colors.dim}= ${diff.summary.unchanged} unchanged${colors.reset}`);
6384
+ lines.push("");
6385
+ if (diff.changes.length > 0) {
6386
+ lines.push(`${colors.bold}Changes${colors.reset}`);
6387
+ const added = diff.changes.filter((c) => c.type === "added");
6388
+ const removed = diff.changes.filter((c) => c.type === "removed");
6389
+ const upgraded = diff.changes.filter((c) => c.type === "upgraded");
6390
+ const downgraded = diff.changes.filter((c) => c.type === "downgraded");
6391
+ if (added.length > 0) {
6392
+ for (const change of added) {
6393
+ lines.push(` ${colors.green}+ ${change.name}@${change.newVersion}${colors.reset}`);
6394
+ }
6395
+ }
6396
+ if (removed.length > 0) {
6397
+ for (const change of removed) {
6398
+ lines.push(` ${colors.red}- ${change.name}@${change.oldVersion}${colors.reset}`);
6399
+ }
6400
+ }
6401
+ if (upgraded.length > 0) {
6402
+ for (const change of upgraded) {
6403
+ lines.push(` ${colors.cyan}\u2191 ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}${colors.reset}`);
6404
+ }
6405
+ }
6406
+ if (downgraded.length > 0) {
6407
+ for (const change of downgraded) {
6408
+ lines.push(` ${colors.yellow}\u2193 ${change.name} ${change.oldVersion} \u2192 ${change.newVersion}${colors.reset}`);
6409
+ }
6410
+ }
6411
+ } else {
6412
+ lines.push(`${colors.dim}No changes detected${colors.reset}`);
6413
+ }
6414
+ return lines.join("\n");
6415
+ }
6416
+ var PACKAGE_SIZES = {
6417
+ "torch": 85e4,
6418
+ "tensorflow": 55e4,
6419
+ "transformers": 45e3,
6420
+ "numpy": 15e3,
6421
+ "pandas": 12e3,
6422
+ "scipy": 35e3,
6423
+ "matplotlib": 8e3,
6424
+ "pillow": 3e3,
6425
+ "scikit-learn": 25e3,
6426
+ "opencv-python": 45e3,
6427
+ "opencv-python-headless": 25e3,
6428
+ "requests": 500,
6429
+ "flask": 800,
6430
+ "django": 8e3,
6431
+ "fastapi": 400,
6432
+ "moment": 300,
6433
+ "dayjs": 2,
6434
+ "lodash": 1500,
6435
+ "lodash-es": 500,
6436
+ "jquery": 85,
6437
+ "axios": 100,
6438
+ "react": 150,
6439
+ "next": 5e3,
6440
+ "express": 200,
6441
+ "bluebird": 80,
6442
+ "underscore": 20,
6443
+ "request": 500
6444
+ };
6445
+ var ALTERNATIVES = {
6446
+ "moment": { replacement: "dayjs", savingsPercent: 99, note: "Same API, 2KB vs 300KB" },
6447
+ "lodash": { replacement: "lodash-es", savingsPercent: 70, note: "Tree-shakeable, or use native methods" },
6448
+ "request": { replacement: "axios", savingsPercent: 80, note: "request is deprecated" },
6449
+ "bluebird": { replacement: "native Promise", savingsPercent: 100, note: "Native Promises are performant now" },
6450
+ "underscore": { replacement: "lodash-es", savingsPercent: 50, note: "lodash is more complete" },
6451
+ "jquery": { replacement: "native DOM", savingsPercent: 100, note: "Modern browsers don't need jQuery" },
6452
+ "opencv-python": { replacement: "opencv-python-headless", savingsPercent: 45, note: "For servers without display" }
6453
+ };
6454
+ var DUPLICATE_GROUPS = [
6455
+ ["moment", "dayjs", "date-fns", "luxon"],
6456
+ ["lodash", "underscore", "ramda"],
6457
+ ["axios", "got", "node-fetch", "request", "superagent"],
6458
+ ["express", "koa", "fastify", "hapi"],
6459
+ ["tensorflow", "torch", "jax"],
6460
+ ["pytest", "nose", "unittest2"],
6461
+ ["opencv-python", "opencv-python-headless", "opencv-contrib-python"]
6462
+ ];
6463
+ function getPackageSize(name) {
6464
+ return PACKAGE_SIZES[name.toLowerCase()] || 500;
6465
+ }
6466
+ function analyzeOptimizations(packages, hardware, options = {}) {
6467
+ const { aggressive = false, targetSizeReductionPercent = 20 } = options;
6468
+ const suggestions = [];
6469
+ const unusedPackages = [];
6470
+ const duplicateFunctionality = [];
6471
+ const hardwareOptimizations = [];
6472
+ const packageNames = packages.map((p) => p.name.toLowerCase());
6473
+ let currentSizeKb = packages.reduce((sum, p) => sum + getPackageSize(p.name), 0);
6474
+ let potentialSavingsKb = 0;
6475
+ for (const pkg of packages) {
6476
+ const alt = ALTERNATIVES[pkg.name.toLowerCase()];
6477
+ if (alt) {
6478
+ const currentSize = getPackageSize(pkg.name);
6479
+ const savings = Math.floor(currentSize * (alt.savingsPercent / 100));
6480
+ potentialSavingsKb += savings;
6481
+ suggestions.push({
6482
+ type: "replace",
6483
+ package: pkg.name,
6484
+ reason: alt.note,
6485
+ savingsKb: savings,
6486
+ replacement: alt.replacement,
6487
+ command: alt.replacement.includes("native") ? `pip uninstall ${pkg.name}` : `pip uninstall ${pkg.name} && pip install ${alt.replacement}`,
6488
+ confidence: "high",
6489
+ impact: "test-required"
6490
+ });
6491
+ }
6492
+ }
6493
+ for (const group of DUPLICATE_GROUPS) {
6494
+ const installed = group.filter((p) => packageNames.includes(p.toLowerCase()));
6495
+ if (installed.length > 1) {
6496
+ const sorted = installed.sort((a, b) => getPackageSize(a) - getPackageSize(b));
6497
+ const keep = sorted[0];
6498
+ const remove = sorted.slice(1);
6499
+ const savings = remove.reduce((sum, p) => sum + getPackageSize(p), 0);
6500
+ potentialSavingsKb += savings;
6501
+ duplicateFunctionality.push({
6502
+ packages: installed,
6503
+ recommendation: `Keep ${keep}, consider removing: ${remove.join(", ")}`
6504
+ });
6505
+ for (const pkg of remove) {
6506
+ suggestions.push({
6507
+ type: "remove",
6508
+ package: pkg,
6509
+ reason: `Duplicate functionality with ${keep}`,
6510
+ savingsKb: getPackageSize(pkg),
6511
+ command: `pip uninstall ${pkg}`,
6512
+ confidence: "medium",
6513
+ impact: "test-required"
6514
+ });
6515
+ }
6516
+ }
6517
+ }
6518
+ if (hardware.gpu === "none") {
6519
+ const gpuPackages = packages.filter(
6520
+ (p) => p.name.toLowerCase().includes("cuda") || p.name.toLowerCase().includes("-gpu") || p.name.toLowerCase() === "tensorflow-gpu"
6521
+ );
6522
+ for (const pkg of gpuPackages) {
6523
+ const size = getPackageSize(pkg.name);
6524
+ potentialSavingsKb += size;
6525
+ suggestions.push({
6526
+ type: "remove",
6527
+ package: pkg.name,
6528
+ reason: "GPU package on CPU-only system",
6529
+ savingsKb: size,
6530
+ command: `pip uninstall ${pkg.name}`,
6531
+ confidence: "high",
6532
+ impact: "safe"
6533
+ });
6534
+ hardwareOptimizations.push(`Remove ${pkg.name} - no GPU detected`);
6535
+ }
6536
+ if (packageNames.includes("torch") && hardware.totalMemoryGb < 8) {
6537
+ hardwareOptimizations.push("Consider using torch CPU-only build with reduced memory footprint");
6538
+ }
6539
+ }
6540
+ if (hardware.totalMemoryGb < 8) {
6541
+ const heavyPackages = packages.filter(
6542
+ (p) => getPackageSize(p.name) > 1e5
6543
+ );
6544
+ if (heavyPackages.length > 2) {
6545
+ hardwareOptimizations.push(
6546
+ `System has ${hardware.totalMemoryGb}GB RAM - consider reducing number of heavy ML frameworks`
6547
+ );
6548
+ }
6549
+ }
6550
+ const likelyDevPackages = packages.filter(
6551
+ (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())
6552
+ );
6553
+ if (likelyDevPackages.length > 0 && aggressive) {
6554
+ for (const pkg of likelyDevPackages) {
6555
+ suggestions.push({
6556
+ type: "remove",
6557
+ package: pkg.name,
6558
+ reason: "Likely dev dependency - remove in production",
6559
+ savingsKb: getPackageSize(pkg.name),
6560
+ command: `pip uninstall ${pkg.name}`,
6561
+ confidence: "low",
6562
+ impact: "safe"
6563
+ });
6564
+ }
6565
+ }
6566
+ suggestions.sort((a, b) => b.savingsKb - a.savingsKb);
6567
+ return {
6568
+ currentSizeKb,
6569
+ potentialSavingsKb,
6570
+ suggestions,
6571
+ unusedPackages,
6572
+ duplicateFunctionality,
6573
+ hardwareOptimizations
6574
+ };
6575
+ }
6576
+ function formatOptimizationReport(report, colorize = true) {
6577
+ const colors = {
6578
+ reset: colorize ? "\x1B[0m" : "",
6579
+ red: colorize ? "\x1B[31m" : "",
6580
+ yellow: colorize ? "\x1B[33m" : "",
6581
+ green: colorize ? "\x1B[32m" : "",
6582
+ blue: colorize ? "\x1B[34m" : "",
6583
+ cyan: colorize ? "\x1B[36m" : "",
6584
+ dim: colorize ? "\x1B[2m" : "",
6585
+ bold: colorize ? "\x1B[1m" : ""
6586
+ };
6587
+ const lines = [];
6588
+ function formatSize(kb) {
6589
+ if (kb >= 1e3) return `${(kb / 1e3).toFixed(1)}MB`;
6590
+ return `${kb}KB`;
6591
+ }
6592
+ lines.push(`${colors.bold}Komodo Optimizer Report${colors.reset}`);
6593
+ lines.push("");
6594
+ const savingsPercent = (report.potentialSavingsKb / report.currentSizeKb * 100).toFixed(1);
6595
+ lines.push(`${colors.bold}Size Analysis${colors.reset}`);
6596
+ lines.push(` Current size: ${formatSize(report.currentSizeKb)}`);
6597
+ lines.push(` ${colors.green}Potential savings: ${formatSize(report.potentialSavingsKb)} (${savingsPercent}%)${colors.reset}`);
6598
+ lines.push("");
6599
+ if (report.hardwareOptimizations.length > 0) {
6600
+ lines.push(`${colors.bold}Hardware Optimizations${colors.reset}`);
6601
+ for (const opt of report.hardwareOptimizations) {
6602
+ lines.push(` ${colors.cyan}\u2192${colors.reset} ${opt}`);
6603
+ }
6604
+ lines.push("");
6605
+ }
6606
+ if (report.duplicateFunctionality.length > 0) {
6607
+ lines.push(`${colors.yellow}${colors.bold}Duplicate Functionality Detected${colors.reset}`);
6608
+ for (const dup of report.duplicateFunctionality) {
6609
+ lines.push(` ${colors.yellow}\u26A0${colors.reset} ${dup.packages.join(", ")}`);
6610
+ lines.push(` ${colors.dim}${dup.recommendation}${colors.reset}`);
6611
+ }
6612
+ lines.push("");
6613
+ }
6614
+ if (report.suggestions.length > 0) {
6615
+ lines.push(`${colors.bold}Optimization Suggestions${colors.reset}`);
6616
+ lines.push("");
6617
+ const highConf = report.suggestions.filter((s) => s.confidence === "high");
6618
+ const medConf = report.suggestions.filter((s) => s.confidence === "medium");
6619
+ const lowConf = report.suggestions.filter((s) => s.confidence === "low");
6620
+ if (highConf.length > 0) {
6621
+ lines.push(`${colors.green}High Confidence${colors.reset}`);
6622
+ for (const sug of highConf.slice(0, 5)) {
6623
+ const impactColor = sug.impact === "safe" ? colors.green : sug.impact === "test-required" ? colors.yellow : colors.red;
6624
+ lines.push(` ${sug.type.toUpperCase()} ${colors.bold}${sug.package}${colors.reset}`);
6625
+ lines.push(` ${colors.dim}${sug.reason}${colors.reset}`);
6626
+ lines.push(` Saves: ${colors.green}${formatSize(sug.savingsKb)}${colors.reset} | Impact: ${impactColor}${sug.impact}${colors.reset}`);
6627
+ lines.push(` ${colors.cyan}$ ${sug.command}${colors.reset}`);
6628
+ lines.push("");
6629
+ }
6630
+ }
6631
+ if (medConf.length > 0 && highConf.length < 5) {
6632
+ lines.push(`${colors.yellow}Medium Confidence${colors.reset}`);
6633
+ for (const sug of medConf.slice(0, 3)) {
6634
+ lines.push(` ${sug.type.toUpperCase()} ${colors.bold}${sug.package}${colors.reset}`);
6635
+ lines.push(` ${colors.dim}${sug.reason}${colors.reset}`);
6636
+ lines.push(` Saves: ${colors.green}${formatSize(sug.savingsKb)}${colors.reset}`);
6637
+ lines.push("");
6638
+ }
6639
+ }
6640
+ if (report.suggestions.length > 8) {
6641
+ lines.push(`${colors.dim}...and ${report.suggestions.length - 8} more suggestions${colors.reset}`);
6642
+ }
6643
+ } else {
6644
+ lines.push(`${colors.green}\u2713 No major optimization opportunities found${colors.reset}`);
6645
+ }
6646
+ return lines.join("\n");
6647
+ }
6648
+ function generateOptimizationScript(suggestions, safeOnly = true) {
6649
+ const filtered = safeOnly ? suggestions.filter((s) => s.impact === "safe" && s.confidence === "high") : suggestions;
6650
+ if (filtered.length === 0) {
6651
+ return "# No safe optimizations to apply automatically";
6652
+ }
6653
+ const lines = [
6654
+ "#!/bin/bash",
6655
+ "# Komodo Optimization Script",
6656
+ `# Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
6657
+ "",
6658
+ "set -e",
6659
+ ""
6660
+ ];
6661
+ for (const sug of filtered) {
6662
+ lines.push(`# ${sug.type}: ${sug.package} (saves ${Math.round(sug.savingsKb / 1e3)}MB)`);
6663
+ lines.push(`# Reason: ${sug.reason}`);
6664
+ lines.push(sug.command);
6665
+ lines.push("");
6666
+ }
6667
+ lines.push('echo "Optimization complete!"');
6668
+ return lines.join("\n");
6669
+ }
6670
+ var TEMPLATES = [
6671
+ // ML/AI Templates
6672
+ {
6673
+ id: "pytorch-basic",
6674
+ name: "PyTorch Starter",
6675
+ description: "Basic PyTorch setup for deep learning",
6676
+ category: "ml",
6677
+ runtime: "python",
6678
+ packages: [
6679
+ { name: "torch", note: "Core PyTorch framework" },
6680
+ { name: "torchvision", note: "Image datasets and transforms" },
6681
+ { name: "numpy", note: "Numerical computing" },
6682
+ { name: "matplotlib", optional: true, note: "Visualization" },
6683
+ { name: "tqdm", optional: true, note: "Progress bars" }
6684
+ ],
6685
+ minMemoryGb: 8,
6686
+ tags: ["pytorch", "deep-learning", "ai", "neural-networks"]
6687
+ },
6688
+ {
6689
+ id: "pytorch-nlp",
6690
+ name: "PyTorch NLP",
6691
+ description: "Natural Language Processing with PyTorch and Transformers",
6692
+ category: "ml",
6693
+ runtime: "python",
6694
+ packages: [
6695
+ { name: "torch" },
6696
+ { name: "transformers", note: "Hugging Face Transformers" },
6697
+ { name: "tokenizers", note: "Fast tokenization" },
6698
+ { name: "datasets", note: "Hugging Face datasets" },
6699
+ { name: "sentencepiece", optional: true },
6700
+ { name: "sacremoses", optional: true }
6701
+ ],
6702
+ minMemoryGb: 16,
6703
+ tags: ["nlp", "transformers", "bert", "gpt", "language-models"]
6704
+ },
6705
+ {
6706
+ id: "tensorflow-basic",
6707
+ name: "TensorFlow Starter",
6708
+ description: "TensorFlow 2.x for machine learning",
6709
+ category: "ml",
6710
+ runtime: "python",
6711
+ packages: [
6712
+ { name: "tensorflow", note: "TensorFlow 2.x with Keras" },
6713
+ { name: "numpy" },
6714
+ { name: "pandas", optional: true },
6715
+ { name: "matplotlib", optional: true },
6716
+ { name: "tensorboard", optional: true, note: "Training visualization" }
6717
+ ],
6718
+ minMemoryGb: 8,
6719
+ tags: ["tensorflow", "keras", "deep-learning", "ai"]
6720
+ },
6721
+ {
6722
+ id: "computer-vision",
6723
+ name: "Computer Vision",
6724
+ description: "Image processing and computer vision",
6725
+ category: "ml",
6726
+ runtime: "python",
6727
+ packages: [
6728
+ { name: "torch" },
6729
+ { name: "torchvision" },
6730
+ { name: "opencv-python-headless", note: "OpenCV for servers" },
6731
+ { name: "pillow", note: "Image processing" },
6732
+ { name: "albumentations", optional: true, note: "Image augmentation" },
6733
+ { name: "timm", optional: true, note: "PyTorch Image Models" }
6734
+ ],
6735
+ minMemoryGb: 8,
6736
+ requiresGpu: true,
6737
+ tags: ["vision", "images", "opencv", "cnn"]
6738
+ },
6739
+ {
6740
+ id: "llm-local",
6741
+ name: "Local LLM",
6742
+ description: "Run large language models locally with llama.cpp",
6743
+ category: "ml",
6744
+ runtime: "python",
6745
+ packages: [
6746
+ { name: "llama-cpp-python", note: "llama.cpp Python bindings" },
6747
+ { name: "huggingface-hub", note: "Download models" },
6748
+ { name: "gradio", optional: true, note: "Web UI" }
6749
+ ],
6750
+ minMemoryGb: 16,
6751
+ tags: ["llm", "llama", "local-ai", "chatbot"]
6752
+ },
6753
+ // Data Science Templates
6754
+ {
6755
+ id: "data-analysis",
6756
+ name: "Data Analysis",
6757
+ description: "Data analysis and visualization toolkit",
6758
+ category: "data",
6759
+ runtime: "python",
6760
+ packages: [
6761
+ { name: "pandas", note: "Data manipulation" },
6762
+ { name: "numpy", note: "Numerical computing" },
6763
+ { name: "matplotlib", note: "Basic plotting" },
6764
+ { name: "seaborn", note: "Statistical visualization" },
6765
+ { name: "scipy", optional: true, note: "Scientific computing" },
6766
+ { name: "jupyter", optional: true, note: "Interactive notebooks" }
6767
+ ],
6768
+ tags: ["data", "analytics", "visualization", "pandas"]
6769
+ },
6770
+ {
6771
+ id: "data-engineering",
6772
+ name: "Data Engineering",
6773
+ description: "Data pipelines and processing",
6774
+ category: "data",
6775
+ runtime: "python",
6776
+ packages: [
6777
+ { name: "pandas" },
6778
+ { name: "polars", note: "Fast DataFrames" },
6779
+ { name: "pyarrow", note: "Columnar data format" },
6780
+ { name: "duckdb", note: "Embedded analytics DB" },
6781
+ { name: "sqlalchemy", note: "Database toolkit" },
6782
+ { name: "boto3", optional: true, note: "AWS SDK" }
6783
+ ],
6784
+ tags: ["etl", "pipelines", "databases", "big-data"]
6785
+ },
6786
+ // Web Development Templates
6787
+ {
6788
+ id: "nextjs-app",
6789
+ name: "Next.js App",
6790
+ description: "Modern React with Next.js",
6791
+ category: "web",
6792
+ runtime: "node",
6793
+ packages: [
6794
+ { name: "next", note: "Next.js framework" },
6795
+ { name: "react" },
6796
+ { name: "react-dom" },
6797
+ { name: "typescript", optional: true },
6798
+ { name: "tailwindcss", optional: true, note: "Utility CSS" },
6799
+ { name: "@types/react", optional: true }
6800
+ ],
6801
+ setupCommands: ["npx create-next-app@latest"],
6802
+ tags: ["react", "nextjs", "frontend", "ssr"]
6803
+ },
6804
+ {
6805
+ id: "react-spa",
6806
+ name: "React SPA",
6807
+ description: "Single-page React application with Vite",
6808
+ category: "web",
6809
+ runtime: "node",
6810
+ packages: [
6811
+ { name: "react" },
6812
+ { name: "react-dom" },
6813
+ { name: "vite", note: "Fast build tool" },
6814
+ { name: "react-router-dom", optional: true },
6815
+ { name: "zustand", optional: true, note: "State management" }
6816
+ ],
6817
+ setupCommands: ["npm create vite@latest -- --template react-ts"],
6818
+ tags: ["react", "spa", "vite", "frontend"]
6819
+ },
6820
+ // API Templates
6821
+ {
6822
+ id: "fastapi",
6823
+ name: "FastAPI",
6824
+ description: "Modern Python API with FastAPI",
6825
+ category: "api",
6826
+ runtime: "python",
6827
+ packages: [
6828
+ { name: "fastapi", note: "Web framework" },
6829
+ { name: "uvicorn", note: "ASGI server" },
6830
+ { name: "pydantic", note: "Data validation" },
6831
+ { name: "python-multipart", optional: true, note: "Form data" },
6832
+ { name: "sqlalchemy", optional: true, note: "Database ORM" },
6833
+ { name: "httpx", optional: true, note: "HTTP client" }
6834
+ ],
6835
+ tags: ["api", "rest", "async", "python"]
6836
+ },
6837
+ {
6838
+ id: "flask-api",
6839
+ name: "Flask API",
6840
+ description: "Lightweight Python web API",
6841
+ category: "api",
6842
+ runtime: "python",
6843
+ packages: [
6844
+ { name: "flask", note: "Web framework" },
6845
+ { name: "flask-cors", optional: true },
6846
+ { name: "flask-sqlalchemy", optional: true },
6847
+ { name: "gunicorn", note: "Production server" },
6848
+ { name: "python-dotenv", optional: true }
6849
+ ],
6850
+ tags: ["api", "flask", "rest", "python"]
6851
+ },
6852
+ {
6853
+ id: "express-api",
6854
+ name: "Express API",
6855
+ description: "Node.js REST API with Express",
6856
+ category: "api",
6857
+ runtime: "node",
6858
+ packages: [
6859
+ { name: "express", note: "Web framework" },
6860
+ { name: "cors" },
6861
+ { name: "helmet", note: "Security headers" },
6862
+ { name: "dotenv", note: "Environment variables" },
6863
+ { name: "prisma", optional: true, note: "Database ORM" }
6864
+ ],
6865
+ tags: ["api", "express", "nodejs", "rest"]
6866
+ },
6867
+ // Full Stack Templates
6868
+ {
6869
+ id: "fullstack-python",
6870
+ name: "Full Stack Python",
6871
+ description: "FastAPI backend + React frontend",
6872
+ category: "web",
6873
+ runtime: "mixed",
6874
+ packages: [
6875
+ { name: "fastapi" },
6876
+ { name: "uvicorn" },
6877
+ { name: "sqlalchemy" },
6878
+ { name: "react" },
6879
+ { name: "vite" },
6880
+ { name: "axios", note: "HTTP client" }
6881
+ ],
6882
+ tags: ["fullstack", "fastapi", "react"]
6883
+ },
6884
+ // DevOps Templates
6885
+ {
6886
+ id: "automation",
6887
+ name: "Automation & Scripting",
6888
+ description: "Python automation toolkit",
6889
+ category: "devops",
6890
+ runtime: "python",
6891
+ packages: [
6892
+ { name: "requests", note: "HTTP requests" },
6893
+ { name: "beautifulsoup4", note: "HTML parsing" },
6894
+ { name: "selenium", optional: true, note: "Browser automation" },
6895
+ { name: "paramiko", optional: true, note: "SSH" },
6896
+ { name: "schedule", optional: true, note: "Job scheduling" },
6897
+ { name: "click", note: "CLI framework" }
6898
+ ],
6899
+ tags: ["automation", "scripting", "cli", "devops"]
6900
+ }
6901
+ ];
6902
+ function getTemplateById(id) {
6903
+ return TEMPLATES.find((t) => t.id === id);
6904
+ }
6905
+ function searchTemplates(query) {
6906
+ const lower = query.toLowerCase();
6907
+ return TEMPLATES.filter(
6908
+ (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))
6909
+ );
6910
+ }
6911
+ function formatTemplateList(templates, colorize = true) {
6912
+ const colors = {
6913
+ reset: colorize ? "\x1B[0m" : "",
6914
+ bold: colorize ? "\x1B[1m" : "",
6915
+ dim: colorize ? "\x1B[2m" : "",
6916
+ cyan: colorize ? "\x1B[36m" : "",
6917
+ green: colorize ? "\x1B[32m" : "",
6918
+ yellow: colorize ? "\x1B[33m" : ""
6919
+ };
6920
+ const lines = [];
6921
+ const categories = [...new Set(templates.map((t) => t.category))];
6922
+ for (const category of categories) {
6923
+ const categoryTemplates = templates.filter((t) => t.category === category);
6924
+ const categoryName = category.charAt(0).toUpperCase() + category.slice(1);
6925
+ lines.push(`${colors.bold}${categoryName}${colors.reset}`);
6926
+ for (const template of categoryTemplates) {
6927
+ const requirements = [];
6928
+ if (template.minMemoryGb) requirements.push(`${template.minMemoryGb}GB+ RAM`);
6929
+ if (template.requiresGpu) requirements.push("GPU");
6930
+ lines.push(` ${colors.cyan}${template.id}${colors.reset} - ${template.name}`);
6931
+ lines.push(` ${colors.dim}${template.description}${colors.reset}`);
6932
+ if (requirements.length > 0) {
6933
+ lines.push(` ${colors.yellow}Requires: ${requirements.join(", ")}${colors.reset}`);
6934
+ }
6935
+ lines.push(` ${colors.dim}Packages: ${template.packages.filter((p) => !p.optional).map((p) => p.name).join(", ")}${colors.reset}`);
6936
+ }
6937
+ lines.push("");
6938
+ }
6939
+ return lines.join("\n");
6940
+ }
6941
+ function getInstallCommandsForTemplate(template) {
6942
+ const commands = [];
6943
+ if (template.runtime === "python" || template.runtime === "mixed") {
6944
+ const pythonPackages = template.packages.filter((p) => !p.optional).map((p) => p.version ? `${p.name}==${p.version}` : p.name);
6945
+ if (pythonPackages.length > 0) {
6946
+ commands.push(`pip install ${pythonPackages.join(" ")}`);
6947
+ }
6948
+ }
6949
+ if (template.runtime === "node" || template.runtime === "mixed") {
6950
+ const nodePackages = template.packages.filter((p) => !p.optional).map((p) => p.version ? `${p.name}@${p.version}` : p.name);
6951
+ if (nodePackages.length > 0) {
6952
+ commands.push(`npm install ${nodePackages.join(" ")}`);
6953
+ }
6954
+ }
6955
+ if (template.setupCommands) {
6956
+ commands.push(...template.setupCommands);
6957
+ }
6958
+ return commands;
6959
+ }
6960
+ var PACKAGE_DATABASE = {
6961
+ // ML/AI Packages
6962
+ "torch": {
6963
+ summary: "PyTorch - Deep learning framework",
6964
+ 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.",
6965
+ category: "Machine Learning",
6966
+ useCases: [
6967
+ "Deep learning model training",
6968
+ "Computer vision applications",
6969
+ "Natural language processing",
6970
+ "Reinforcement learning",
6971
+ "Research and prototyping"
6972
+ ],
6973
+ commonlyUsedWith: ["torchvision", "numpy", "transformers", "tqdm", "tensorboard"],
6974
+ alternatives: [
6975
+ { name: "tensorflow", note: "Google's ML framework, more production-focused" },
6976
+ { name: "jax", note: "Google's newer framework, good for research" }
6977
+ ],
6978
+ documentation: "https://pytorch.org/docs/",
6979
+ popularity: "very-high",
6980
+ maintenance: "active"
6981
+ },
6982
+ "tensorflow": {
6983
+ summary: "TensorFlow - End-to-end ML platform",
6984
+ 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.",
6985
+ category: "Machine Learning",
6986
+ useCases: [
6987
+ "Production ML deployment",
6988
+ "Mobile ML (TensorFlow Lite)",
6989
+ "Web ML (TensorFlow.js)",
6990
+ "Large-scale distributed training"
6991
+ ],
6992
+ commonlyUsedWith: ["keras", "numpy", "tensorboard", "tensorflow-hub"],
6993
+ alternatives: [
6994
+ { name: "torch", note: "More Pythonic, popular in research" },
6995
+ { name: "jax", note: "Newer Google framework" }
6996
+ ],
6997
+ documentation: "https://www.tensorflow.org/api_docs",
6998
+ popularity: "very-high",
6999
+ maintenance: "active"
7000
+ },
7001
+ "transformers": {
7002
+ summary: "Hugging Face Transformers - State-of-the-art NLP",
7003
+ description: "Provides thousands of pretrained models for natural language processing, computer vision, audio, and multimodal tasks. Easy to fine-tune and deploy.",
7004
+ category: "Machine Learning",
7005
+ useCases: [
7006
+ "Text classification",
7007
+ "Named entity recognition",
7008
+ "Question answering",
7009
+ "Text generation",
7010
+ "Translation"
7011
+ ],
7012
+ commonlyUsedWith: ["torch", "tokenizers", "datasets", "accelerate"],
7013
+ alternatives: [
7014
+ { name: "spacy", note: "Faster for production NLP pipelines" },
7015
+ { name: "flair", note: "Simpler API for NLP" }
7016
+ ],
7017
+ documentation: "https://huggingface.co/docs/transformers",
7018
+ popularity: "very-high",
7019
+ maintenance: "active"
7020
+ },
7021
+ // Data Science
7022
+ "numpy": {
7023
+ summary: "NumPy - Fundamental array computing",
7024
+ description: "The fundamental package for scientific computing in Python. Provides support for large, multi-dimensional arrays and matrices, along with mathematical functions.",
7025
+ category: "Data Science",
7026
+ useCases: [
7027
+ "Numerical computations",
7028
+ "Array manipulation",
7029
+ "Linear algebra",
7030
+ "Random number generation",
7031
+ "Foundation for other libraries"
7032
+ ],
7033
+ commonlyUsedWith: ["pandas", "scipy", "matplotlib", "scikit-learn"],
7034
+ alternatives: [
7035
+ { name: "cupy", note: "GPU-accelerated NumPy" },
7036
+ { name: "jax.numpy", note: "Auto-diff enabled NumPy" }
7037
+ ],
7038
+ documentation: "https://numpy.org/doc/",
7039
+ popularity: "very-high",
7040
+ maintenance: "active"
7041
+ },
7042
+ "pandas": {
7043
+ summary: "Pandas - Data analysis and manipulation",
7044
+ 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.",
7045
+ category: "Data Science",
7046
+ useCases: [
7047
+ "Data cleaning and preparation",
7048
+ "Exploratory data analysis",
7049
+ "Time series analysis",
7050
+ "CSV/Excel file handling",
7051
+ "Data transformation"
7052
+ ],
7053
+ commonlyUsedWith: ["numpy", "matplotlib", "seaborn", "scikit-learn"],
7054
+ alternatives: [
7055
+ { name: "polars", note: "Faster, Rust-based alternative" },
7056
+ { name: "dask", note: "Parallel computing for large datasets" }
7057
+ ],
7058
+ documentation: "https://pandas.pydata.org/docs/",
7059
+ popularity: "very-high",
7060
+ maintenance: "active"
7061
+ },
7062
+ // Web Frameworks
7063
+ "fastapi": {
7064
+ summary: "FastAPI - Modern Python web framework",
7065
+ description: "High-performance web framework for building APIs with Python 3.7+. Based on standard Python type hints, automatic API documentation, and async support.",
7066
+ category: "Web Framework",
7067
+ useCases: [
7068
+ "REST API development",
7069
+ "Microservices",
7070
+ "Real-time applications",
7071
+ "ML model serving"
7072
+ ],
7073
+ commonlyUsedWith: ["uvicorn", "pydantic", "sqlalchemy", "httpx"],
7074
+ alternatives: [
7075
+ { name: "flask", note: "Simpler, more flexible" },
7076
+ { name: "django", note: "Full-featured, batteries included" }
7077
+ ],
7078
+ documentation: "https://fastapi.tiangolo.com/",
7079
+ popularity: "very-high",
7080
+ maintenance: "active"
7081
+ },
7082
+ "flask": {
7083
+ summary: "Flask - Lightweight web framework",
7084
+ description: "Micro web framework that doesn't require particular tools or libraries. Keeps the core simple but extensible through plugins.",
7085
+ category: "Web Framework",
7086
+ useCases: [
7087
+ "Simple web applications",
7088
+ "REST APIs",
7089
+ "Prototyping",
7090
+ "Microservices"
7091
+ ],
7092
+ commonlyUsedWith: ["werkzeug", "jinja2", "flask-sqlalchemy", "gunicorn"],
7093
+ alternatives: [
7094
+ { name: "fastapi", note: "Modern, async, auto-docs" },
7095
+ { name: "django", note: "Full-featured framework" }
7096
+ ],
7097
+ documentation: "https://flask.palletsprojects.com/",
7098
+ popularity: "very-high",
7099
+ maintenance: "active"
7100
+ },
7101
+ "django": {
7102
+ summary: "Django - Full-featured web framework",
7103
+ description: "High-level Python web framework that encourages rapid development. Includes ORM, admin interface, authentication, and more out of the box.",
7104
+ category: "Web Framework",
7105
+ useCases: [
7106
+ "Full-stack web applications",
7107
+ "Content management systems",
7108
+ "E-commerce platforms",
7109
+ "Admin interfaces"
7110
+ ],
7111
+ commonlyUsedWith: ["django-rest-framework", "celery", "redis", "postgresql"],
7112
+ alternatives: [
7113
+ { name: "flask", note: "Lighter weight, more flexible" },
7114
+ { name: "fastapi", note: "API-focused, async" }
7115
+ ],
7116
+ documentation: "https://docs.djangoproject.com/",
7117
+ popularity: "very-high",
7118
+ maintenance: "active"
7119
+ },
7120
+ // Node.js packages
7121
+ "react": {
7122
+ summary: "React - UI component library",
7123
+ description: "JavaScript library for building user interfaces. Uses a component-based architecture and virtual DOM for efficient updates.",
7124
+ category: "Frontend",
7125
+ useCases: [
7126
+ "Single-page applications",
7127
+ "Component libraries",
7128
+ "Mobile apps (React Native)",
7129
+ "Server-side rendering"
7130
+ ],
7131
+ commonlyUsedWith: ["react-dom", "react-router-dom", "redux", "next"],
7132
+ alternatives: [
7133
+ { name: "vue", note: "Easier learning curve" },
7134
+ { name: "svelte", note: "Compile-time framework, smaller bundles" }
7135
+ ],
7136
+ documentation: "https://react.dev/",
7137
+ popularity: "very-high",
7138
+ maintenance: "active"
7139
+ },
7140
+ "next": {
7141
+ summary: "Next.js - React framework",
7142
+ description: "Production-ready React framework with server-side rendering, static site generation, API routes, and more.",
7143
+ category: "Frontend Framework",
7144
+ useCases: [
7145
+ "Server-side rendered apps",
7146
+ "Static websites",
7147
+ "Full-stack applications",
7148
+ "E-commerce sites"
7149
+ ],
7150
+ commonlyUsedWith: ["react", "tailwindcss", "prisma", "vercel"],
7151
+ alternatives: [
7152
+ { name: "remix", note: "Web standards focused" },
7153
+ { name: "gatsby", note: "Static site generator" }
7154
+ ],
7155
+ documentation: "https://nextjs.org/docs",
7156
+ popularity: "very-high",
7157
+ maintenance: "active"
7158
+ },
7159
+ "express": {
7160
+ summary: "Express - Node.js web framework",
7161
+ description: "Minimal and flexible Node.js web application framework providing robust features for web and mobile applications.",
7162
+ category: "Backend Framework",
7163
+ useCases: [
7164
+ "REST APIs",
7165
+ "Web servers",
7166
+ "Middleware-based apps",
7167
+ "Backend services"
7168
+ ],
7169
+ commonlyUsedWith: ["cors", "helmet", "mongoose", "passport"],
7170
+ alternatives: [
7171
+ { name: "fastify", note: "Faster, lower overhead" },
7172
+ { name: "koa", note: "Modern, lighter weight" }
7173
+ ],
7174
+ documentation: "https://expressjs.com/",
7175
+ popularity: "very-high",
7176
+ maintenance: "stable"
7177
+ },
7178
+ // Utilities
7179
+ "requests": {
7180
+ summary: "Requests - HTTP for humans",
7181
+ description: "Elegant and simple HTTP library for Python. Makes HTTP requests intuitive with a clean API.",
7182
+ category: "Networking",
7183
+ useCases: [
7184
+ "API consumption",
7185
+ "Web scraping",
7186
+ "File downloads",
7187
+ "REST client"
7188
+ ],
7189
+ commonlyUsedWith: ["beautifulsoup4", "lxml", "urllib3"],
7190
+ alternatives: [
7191
+ { name: "httpx", note: "Async support, HTTP/2" },
7192
+ { name: "aiohttp", note: "Async HTTP client/server" }
7193
+ ],
7194
+ documentation: "https://requests.readthedocs.io/",
7195
+ popularity: "very-high",
7196
+ maintenance: "active"
7197
+ },
7198
+ "axios": {
7199
+ summary: "Axios - HTTP client",
7200
+ description: "Promise-based HTTP client for the browser and Node.js. Supports request/response interceptors and automatic JSON parsing.",
7201
+ category: "Networking",
7202
+ useCases: [
7203
+ "API requests",
7204
+ "Form submissions",
7205
+ "File uploads",
7206
+ "REST client"
7207
+ ],
7208
+ commonlyUsedWith: ["react", "vue", "typescript"],
7209
+ alternatives: [
7210
+ { name: "fetch", note: "Native browser API" },
7211
+ { name: "got", note: "Node.js focused" }
7212
+ ],
7213
+ documentation: "https://axios-http.com/docs/intro",
7214
+ popularity: "very-high",
7215
+ maintenance: "active"
7216
+ },
7217
+ // Deprecated/Legacy
7218
+ "moment": {
7219
+ summary: "Moment.js - Date library (Legacy)",
7220
+ description: "Date manipulation library. Now in maintenance mode - the team recommends using alternatives for new projects.",
7221
+ category: "Utilities",
7222
+ useCases: [
7223
+ "Date parsing",
7224
+ "Date formatting",
7225
+ "Date manipulation"
7226
+ ],
7227
+ commonlyUsedWith: ["moment-timezone"],
7228
+ alternatives: [
7229
+ { name: "dayjs", note: "2KB, same API - recommended" },
7230
+ { name: "date-fns", note: "Modular, tree-shakeable" },
7231
+ { name: "luxon", note: "By Moment team, modern" }
7232
+ ],
7233
+ warnings: [
7234
+ "Moment.js is in maintenance mode",
7235
+ "Large bundle size (300KB)",
7236
+ "Not tree-shakeable"
7237
+ ],
7238
+ documentation: "https://momentjs.com/docs/",
7239
+ popularity: "high",
7240
+ maintenance: "minimal"
7241
+ },
7242
+ "request": {
7243
+ summary: "Request - HTTP client (Deprecated)",
7244
+ description: "Simplified HTTP request client. DEPRECATED - no longer maintained.",
7245
+ category: "Networking",
7246
+ useCases: ["Legacy HTTP requests"],
7247
+ commonlyUsedWith: [],
7248
+ alternatives: [
7249
+ { name: "axios", note: "Modern, Promise-based" },
7250
+ { name: "got", note: "Modern Node.js client" },
7251
+ { name: "node-fetch", note: "Fetch API for Node" }
7252
+ ],
7253
+ warnings: [
7254
+ "DEPRECATED - do not use for new projects",
7255
+ "No security updates"
7256
+ ],
7257
+ documentation: "https://github.com/request/request",
7258
+ popularity: "medium",
7259
+ maintenance: "deprecated"
7260
+ }
7261
+ };
7262
+ function explainPackage(name) {
7263
+ const normalizedName = name.toLowerCase();
7264
+ const info = PACKAGE_DATABASE[normalizedName];
7265
+ if (!info) {
7266
+ return null;
7267
+ }
7268
+ const runtime = ["torch", "tensorflow", "numpy", "pandas", "fastapi", "flask", "django", "requests"].includes(normalizedName) ? "python" : "node";
7269
+ return {
7270
+ name,
7271
+ ...info,
7272
+ installCommand: runtime === "python" ? `pip install ${name}` : `npm install ${name}`
7273
+ };
7274
+ }
7275
+ function formatExplanation(explanation, colorize = true) {
7276
+ const colors = {
7277
+ reset: colorize ? "\x1B[0m" : "",
7278
+ bold: colorize ? "\x1B[1m" : "",
7279
+ dim: colorize ? "\x1B[2m" : "",
7280
+ cyan: colorize ? "\x1B[36m" : "",
7281
+ green: colorize ? "\x1B[32m" : "",
7282
+ yellow: colorize ? "\x1B[33m" : "",
7283
+ red: colorize ? "\x1B[31m" : "",
7284
+ blue: colorize ? "\x1B[34m" : ""
7285
+ };
7286
+ const lines = [];
7287
+ lines.push(`${colors.bold}${explanation.name}${colors.reset} ${colors.dim}(${explanation.category})${colors.reset}`);
7288
+ lines.push(`${colors.cyan}${explanation.summary}${colors.reset}`);
7289
+ lines.push("");
7290
+ lines.push(explanation.description);
7291
+ lines.push("");
7292
+ const popColor = explanation.popularity === "very-high" ? colors.green : colors.yellow;
7293
+ const maintColor = explanation.maintenance === "active" ? colors.green : explanation.maintenance === "stable" ? colors.blue : explanation.maintenance === "deprecated" ? colors.red : colors.yellow;
7294
+ lines.push(`${colors.dim}Popularity:${colors.reset} ${popColor}${explanation.popularity}${colors.reset} | ${colors.dim}Maintenance:${colors.reset} ${maintColor}${explanation.maintenance}${colors.reset}`);
7295
+ lines.push("");
7296
+ if (explanation.warnings && explanation.warnings.length > 0) {
7297
+ lines.push(`${colors.red}${colors.bold}Warnings${colors.reset}`);
7298
+ for (const warning of explanation.warnings) {
7299
+ lines.push(` ${colors.red}\u26A0${colors.reset} ${warning}`);
7300
+ }
7301
+ lines.push("");
7302
+ }
7303
+ lines.push(`${colors.bold}Use Cases${colors.reset}`);
7304
+ for (const useCase of explanation.useCases) {
7305
+ lines.push(` ${colors.green}\u2022${colors.reset} ${useCase}`);
7306
+ }
7307
+ lines.push("");
7308
+ if (explanation.commonlyUsedWith.length > 0) {
7309
+ lines.push(`${colors.bold}Commonly Used With${colors.reset}`);
7310
+ lines.push(` ${explanation.commonlyUsedWith.join(", ")}`);
7311
+ lines.push("");
7312
+ }
7313
+ if (explanation.alternatives.length > 0) {
7314
+ lines.push(`${colors.bold}Alternatives${colors.reset}`);
7315
+ for (const alt of explanation.alternatives) {
7316
+ lines.push(` ${colors.cyan}${alt.name}${colors.reset} - ${colors.dim}${alt.note}${colors.reset}`);
7317
+ }
7318
+ lines.push("");
7319
+ }
7320
+ lines.push(`${colors.bold}Install${colors.reset}`);
7321
+ lines.push(` ${colors.cyan}$ ${explanation.installCommand}${colors.reset}`);
7322
+ lines.push("");
7323
+ lines.push(`${colors.dim}Documentation: ${explanation.documentation}${colors.reset}`);
7324
+ return lines.join("\n");
7325
+ }
7326
+ function parseGitHubUrl(url) {
7327
+ const patterns = [
7328
+ /github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?(?:\/tree\/([^\/]+))?$/,
7329
+ /github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/
7330
+ ];
7331
+ for (const pattern of patterns) {
7332
+ const match = url.match(pattern);
7333
+ if (match && match[1] && match[2]) {
7334
+ return {
7335
+ owner: match[1],
7336
+ repo: match[2].replace(/\.git$/, ""),
7337
+ branch: match[3]
7338
+ };
7339
+ }
7340
+ }
7341
+ return null;
7342
+ }
7343
+ function parseRequirementsTxt(content) {
7344
+ const packages = [];
7345
+ const lines = content.split("\n");
7346
+ for (const line of lines) {
7347
+ const trimmed = line.trim();
7348
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) {
7349
+ continue;
7350
+ }
7351
+ const match = trimmed.match(/^([a-zA-Z0-9_-]+)(?:\[.*\])?(?:([<>=!~]+)(.+))?$/);
7352
+ if (match && match[1]) {
7353
+ packages.push({
7354
+ name: match[1],
7355
+ version: match[3]?.trim()
7356
+ });
7357
+ }
7358
+ }
7359
+ return { packages, source: "requirements.txt" };
7360
+ }
7361
+ function parsePackageJson(content) {
7362
+ const packages = [];
7363
+ try {
7364
+ const pkg = JSON.parse(content);
7365
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
7366
+ for (const [name, version] of Object.entries(deps)) {
7367
+ packages.push({
7368
+ name,
7369
+ version: String(version).replace(/^[\^~]/, "")
7370
+ });
7371
+ }
7372
+ } catch {
7373
+ }
7374
+ return { packages, source: "package.json" };
7375
+ }
7376
+ function parsePyprojectToml(content) {
7377
+ const packages = [];
7378
+ const depsMatch = content.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
7379
+ if (depsMatch && depsMatch[1]) {
7380
+ const depsContent = depsMatch[1];
7381
+ const depLines = depsContent.match(/"([^"]+)"|'([^']+)'/g);
7382
+ if (depLines) {
7383
+ for (const dep of depLines) {
7384
+ const cleanDep = dep.replace(/["']/g, "").trim();
7385
+ const match = cleanDep.match(/^([a-zA-Z0-9_-]+)(?:\[.*\])?(?:([<>=!~]+)(.+))?$/);
7386
+ if (match && match[1]) {
7387
+ packages.push({
7388
+ name: match[1],
7389
+ version: match[3]?.trim()
7390
+ });
7391
+ }
7392
+ }
7393
+ }
7394
+ }
7395
+ return { packages, source: "pyproject.toml" };
7396
+ }
7397
+ function parseSetupPy(content) {
7398
+ const packages = [];
7399
+ const requiresMatch = content.match(/install_requires\s*=\s*\[([\s\S]*?)\]/);
7400
+ if (requiresMatch && requiresMatch[1]) {
7401
+ const requiresContent = requiresMatch[1];
7402
+ const depLines = requiresContent.match(/"([^"]+)"|'([^']+)'/g);
7403
+ if (depLines) {
7404
+ for (const dep of depLines) {
7405
+ const cleanDep = dep.replace(/["']/g, "").trim();
7406
+ const match = cleanDep.match(/^([a-zA-Z0-9_-]+)/);
7407
+ if (match && match[1]) {
7408
+ packages.push({ name: match[1] });
7409
+ }
7410
+ }
7411
+ }
7412
+ }
7413
+ return { packages, source: "setup.py" };
7414
+ }
7415
+ function analyzeRepoStructure(files) {
7416
+ const packages = [];
7417
+ const setupCommands = [];
7418
+ const envVars = [];
7419
+ const notes = [];
7420
+ let runtime = "unknown";
7421
+ let confidence = "low";
7422
+ const hasPython = "requirements.txt" in files || "pyproject.toml" in files || "setup.py" in files;
7423
+ const hasNode = "package.json" in files;
7424
+ if (hasPython && hasNode) {
7425
+ runtime = "mixed";
7426
+ } else if (hasPython) {
7427
+ runtime = "python";
7428
+ } else if (hasNode) {
7429
+ runtime = "node";
7430
+ }
7431
+ if (files["requirements.txt"]) {
7432
+ const parsed = parseRequirementsTxt(files["requirements.txt"]);
7433
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "requirements.txt" })));
7434
+ setupCommands.push("pip install -r requirements.txt");
7435
+ confidence = "high";
7436
+ }
7437
+ if (files["pyproject.toml"]) {
7438
+ const parsed = parsePyprojectToml(files["pyproject.toml"]);
7439
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "pyproject.toml" })));
7440
+ if (!setupCommands.some((c) => c.includes("pip"))) {
7441
+ setupCommands.push("pip install -e .");
7442
+ }
7443
+ confidence = "high";
7444
+ }
7445
+ if (files["setup.py"] && packages.length === 0) {
7446
+ const parsed = parseSetupPy(files["setup.py"]);
7447
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "setup.py" })));
7448
+ setupCommands.push("pip install -e .");
7449
+ confidence = "medium";
7450
+ }
7451
+ if (files["package.json"]) {
7452
+ const parsed = parsePackageJson(files["package.json"]);
7453
+ packages.push(...parsed.packages.map((p) => ({ ...p, source: "package.json" })));
7454
+ if (files["pnpm-lock.yaml"]) {
7455
+ setupCommands.push("pnpm install");
7456
+ } else if (files["yarn.lock"]) {
7457
+ setupCommands.push("yarn install");
7458
+ } else {
7459
+ setupCommands.push("npm install");
7460
+ }
7461
+ confidence = "high";
7462
+ }
7463
+ if (files[".env.example"] || files[".env.sample"]) {
7464
+ const envContent = files[".env.example"] || files[".env.sample"] || "";
7465
+ const envMatches = envContent.match(/^([A-Z_]+)=/gm);
7466
+ if (envMatches) {
7467
+ envVars.push(...envMatches.map((m) => m.replace("=", "")));
7468
+ }
7469
+ notes.push("This project uses environment variables - check .env.example");
7470
+ }
7471
+ if (files["Dockerfile"] || files["docker-compose.yml"]) {
7472
+ notes.push("Docker setup available - you can use `docker-compose up` instead");
7473
+ }
7474
+ if (files["README.md"]) {
7475
+ if (files["README.md"].includes("pip install")) {
7476
+ notes.push("Check README.md for additional setup instructions");
7477
+ }
7478
+ }
7479
+ const uniquePackages = /* @__PURE__ */ new Map();
7480
+ for (const pkg of packages) {
7481
+ if (!uniquePackages.has(pkg.name.toLowerCase())) {
7482
+ uniquePackages.set(pkg.name.toLowerCase(), pkg);
7483
+ }
7484
+ }
7485
+ return {
7486
+ url: "",
7487
+ name: "",
7488
+ runtime,
7489
+ packages: Array.from(uniquePackages.values()),
7490
+ setupCommands,
7491
+ envVars,
7492
+ notes,
7493
+ confidence
7494
+ };
7495
+ }
7496
+ function formatRepoAnalysis(analysis, colorize = true) {
7497
+ const colors = {
7498
+ reset: colorize ? "\x1B[0m" : "",
7499
+ bold: colorize ? "\x1B[1m" : "",
7500
+ dim: colorize ? "\x1B[2m" : "",
7501
+ cyan: colorize ? "\x1B[36m" : "",
7502
+ green: colorize ? "\x1B[32m" : "",
7503
+ yellow: colorize ? "\x1B[33m" : ""
7504
+ };
7505
+ const lines = [];
7506
+ lines.push(`${colors.bold}Repository Analysis: ${analysis.name}${colors.reset}`);
7507
+ lines.push(`${colors.dim}${analysis.url}${colors.reset}`);
7508
+ lines.push("");
7509
+ const runtimeLabel = {
7510
+ python: "Python",
7511
+ node: "Node.js",
7512
+ mixed: "Python + Node.js",
7513
+ unknown: "Unknown"
7514
+ }[analysis.runtime];
7515
+ lines.push(`${colors.bold}Runtime:${colors.reset} ${runtimeLabel}`);
7516
+ lines.push(`${colors.bold}Confidence:${colors.reset} ${analysis.confidence}`);
7517
+ lines.push("");
7518
+ if (analysis.packages.length > 0) {
7519
+ lines.push(`${colors.bold}Dependencies (${analysis.packages.length})${colors.reset}`);
7520
+ const bySource = /* @__PURE__ */ new Map();
7521
+ for (const pkg of analysis.packages) {
7522
+ if (!bySource.has(pkg.source)) {
7523
+ bySource.set(pkg.source, []);
7524
+ }
7525
+ bySource.get(pkg.source).push(pkg);
7526
+ }
7527
+ for (const [source, pkgs] of bySource) {
7528
+ lines.push(` ${colors.cyan}${source}:${colors.reset}`);
7529
+ const displayed = pkgs.slice(0, 10);
7530
+ for (const pkg of displayed) {
7531
+ const version = pkg.version ? `@${pkg.version}` : "";
7532
+ lines.push(` ${pkg.name}${colors.dim}${version}${colors.reset}`);
7533
+ }
7534
+ if (pkgs.length > 10) {
7535
+ lines.push(` ${colors.dim}...and ${pkgs.length - 10} more${colors.reset}`);
7536
+ }
7537
+ }
7538
+ lines.push("");
7539
+ }
7540
+ if (analysis.setupCommands.length > 0) {
7541
+ lines.push(`${colors.bold}Setup Commands${colors.reset}`);
7542
+ for (const cmd of analysis.setupCommands) {
7543
+ lines.push(` ${colors.cyan}$ ${cmd}${colors.reset}`);
7544
+ }
7545
+ lines.push("");
7546
+ }
7547
+ if (analysis.envVars.length > 0) {
7548
+ lines.push(`${colors.yellow}${colors.bold}Environment Variables Required${colors.reset}`);
7549
+ for (const envVar of analysis.envVars.slice(0, 5)) {
7550
+ lines.push(` ${colors.yellow}\u2022${colors.reset} ${envVar}`);
7551
+ }
7552
+ if (analysis.envVars.length > 5) {
7553
+ lines.push(` ${colors.dim}...and ${analysis.envVars.length - 5} more${colors.reset}`);
7554
+ }
7555
+ lines.push("");
7556
+ }
7557
+ if (analysis.notes.length > 0) {
7558
+ lines.push(`${colors.bold}Notes${colors.reset}`);
7559
+ for (const note of analysis.notes) {
7560
+ lines.push(` ${colors.green}\u2192${colors.reset} ${note}`);
7561
+ }
7562
+ }
7563
+ return lines.join("\n");
7564
+ }
7565
+ var PACKAGE_CATEGORIES = {
7566
+ "torch": "ML/AI",
7567
+ "tensorflow": "ML/AI",
7568
+ "transformers": "ML/AI",
7569
+ "scikit-learn": "ML/AI",
7570
+ "numpy": "Data Science",
7571
+ "pandas": "Data Science",
7572
+ "matplotlib": "Data Science",
7573
+ "seaborn": "Data Science",
7574
+ "scipy": "Data Science",
7575
+ "fastapi": "Web Backend",
7576
+ "flask": "Web Backend",
7577
+ "django": "Web Backend",
7578
+ "express": "Web Backend",
7579
+ "react": "Web Frontend",
7580
+ "vue": "Web Frontend",
7581
+ "next": "Web Frontend",
7582
+ "angular": "Web Frontend",
7583
+ "requests": "Networking",
7584
+ "httpx": "Networking",
7585
+ "axios": "Networking",
7586
+ "pytest": "Testing",
7587
+ "jest": "Testing",
7588
+ "black": "Dev Tools",
7589
+ "eslint": "Dev Tools",
7590
+ "prettier": "Dev Tools"
7591
+ };
7592
+ var PACKAGE_SIZES2 = {
7593
+ "torch": 85e4,
7594
+ "tensorflow": 55e4,
7595
+ "transformers": 45e3,
7596
+ "numpy": 15e3,
7597
+ "pandas": 12e3,
7598
+ "scipy": 35e3,
7599
+ "matplotlib": 8e3,
7600
+ "scikit-learn": 25e3,
7601
+ "opencv-python": 45e3,
7602
+ "pillow": 3e3,
7603
+ "requests": 500,
7604
+ "flask": 800,
7605
+ "django": 8e3,
7606
+ "fastapi": 400,
7607
+ "react": 150,
7608
+ "next": 5e3,
7609
+ "express": 200,
7610
+ "lodash": 1500,
7611
+ "axios": 100
7612
+ };
7613
+ function getPackageSize2(name) {
7614
+ return PACKAGE_SIZES2[name.toLowerCase()] || 500;
7615
+ }
7616
+ function getPackageCategory(name) {
7617
+ return PACKAGE_CATEGORIES[name.toLowerCase()] || "Other";
7618
+ }
7619
+ function analyzeEnvironment(packages, snapshots) {
7620
+ const stats = calculateStats(packages);
7621
+ const timeline = snapshots ? buildTimeline(snapshots) : void 0;
7622
+ const recommendations = [];
7623
+ const achievements = [];
7624
+ const warnings = [];
7625
+ if (stats.technicalDebtScore > 50) {
7626
+ recommendations.push("High technical debt detected - consider updating outdated packages");
7627
+ }
7628
+ if (stats.totalPackages > 100) {
7629
+ recommendations.push("Large number of packages - audit for unused dependencies");
7630
+ }
7631
+ const largestPkg = stats.largestPackages[0];
7632
+ if (stats.largestPackages.length > 0 && largestPkg && largestPkg.sizeKb > 5e5) {
7633
+ recommendations.push(`${largestPkg.name} is very large - ensure it's necessary`);
7634
+ }
7635
+ const mlPackages = packages.filter((p) => getPackageCategory(p.name) === "ML/AI");
7636
+ if (mlPackages.length > 2) {
7637
+ recommendations.push("Multiple ML frameworks detected - consider consolidating");
7638
+ }
7639
+ if (stats.technicalDebtScore < 20) {
7640
+ achievements.push("Low technical debt - great maintenance!");
7641
+ }
7642
+ if (stats.totalPackages < 20) {
7643
+ achievements.push("Minimal dependencies - clean project structure");
7644
+ }
7645
+ if (timeline && timeline.averageChangesPerWeek > 1) {
7646
+ achievements.push("Active maintenance - regular updates");
7647
+ }
7648
+ if (stats.technicalDebtScore > 70) {
7649
+ warnings.push("Critical technical debt - multiple packages need updates");
7650
+ }
7651
+ for (const reason of stats.technicalDebtReasons) {
7652
+ warnings.push(reason);
7653
+ }
7654
+ return {
7655
+ stats,
7656
+ timeline,
7657
+ recommendations,
7658
+ achievements,
7659
+ warnings
7660
+ };
7661
+ }
7662
+ function calculateStats(packages) {
7663
+ const totalPackages = packages.length;
7664
+ let totalSizeKb = 0;
7665
+ const runtimeBreakdown = { python: 0, node: 0 };
7666
+ const categoryBreakdown = {};
7667
+ const technicalDebtReasons = [];
7668
+ let debtScore = 0;
7669
+ const pythonIndicators = ["torch", "numpy", "pandas", "flask", "django", "fastapi", "requests"];
7670
+ const nodeIndicators = ["react", "vue", "next", "express", "lodash", "axios"];
7671
+ for (const pkg of packages) {
7672
+ const name = pkg.name.toLowerCase();
7673
+ const size = getPackageSize2(name);
7674
+ totalSizeKb += size;
7675
+ const category = getPackageCategory(name);
7676
+ categoryBreakdown[category] = (categoryBreakdown[category] || 0) + 1;
7677
+ if (pythonIndicators.includes(name)) {
7678
+ runtimeBreakdown["python"] = (runtimeBreakdown["python"] || 0) + 1;
7679
+ } else if (nodeIndicators.includes(name)) {
7680
+ runtimeBreakdown["node"] = (runtimeBreakdown["node"] || 0) + 1;
7681
+ }
7682
+ const majorVersion = parseInt(pkg.version.split(".")[0] || "0");
7683
+ if (majorVersion === 0) {
7684
+ debtScore += 2;
7685
+ }
7686
+ if (majorVersion < 2 && ["torch", "tensorflow", "react"].includes(name)) {
7687
+ debtScore += 10;
7688
+ technicalDebtReasons.push(`${pkg.name} v${pkg.version} is significantly outdated`);
7689
+ }
7690
+ }
7691
+ debtScore = Math.min(100, Math.max(0, debtScore));
7692
+ const sortedBySize = [...packages].map((p) => ({ name: p.name, sizeKb: getPackageSize2(p.name) })).sort((a, b) => b.sizeKb - a.sizeKb).slice(0, 5);
7693
+ const updateFrequency = totalPackages > 50 ? "frequent" : totalPackages > 20 ? "regular" : "rare";
7694
+ return {
7695
+ totalPackages,
7696
+ totalSizeKb,
7697
+ runtimeBreakdown,
7698
+ categoryBreakdown,
7699
+ largestPackages: sortedBySize,
7700
+ updateFrequency,
7701
+ technicalDebtScore: debtScore,
7702
+ technicalDebtReasons
7703
+ };
7704
+ }
7705
+ function buildTimeline(snapshots) {
7706
+ const sorted = [...snapshots].sort(
7707
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
7708
+ );
7709
+ const timelineEntries = sorted.map((s) => ({
7710
+ id: s.id,
7711
+ date: new Date(s.createdAt),
7712
+ packageCount: s.packages.length,
7713
+ description: s.description,
7714
+ changeType: determineChangeType(s.description)
7715
+ }));
7716
+ const now = /* @__PURE__ */ new Date();
7717
+ const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
7718
+ const recentChanges = timelineEntries.filter((t) => t.date > oneWeekAgo);
7719
+ const packageChanges = /* @__PURE__ */ new Map();
7720
+ for (let i = 0; i < sorted.length - 1; i++) {
7721
+ const currentSnapshot = sorted[i];
7722
+ const previousSnapshot = sorted[i + 1];
7723
+ if (!currentSnapshot || !previousSnapshot) continue;
7724
+ const current = new Set(currentSnapshot.packages.map((p) => p.name));
7725
+ const previous = new Set(previousSnapshot.packages.map((p) => p.name));
7726
+ for (const pkg of current) {
7727
+ if (!previous.has(pkg)) {
7728
+ packageChanges.set(pkg, (packageChanges.get(pkg) || 0) + 1);
7729
+ }
7730
+ }
7731
+ }
7732
+ const mostChanged = [...packageChanges.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name]) => name);
7733
+ return {
7734
+ snapshots: timelineEntries,
7735
+ averageChangesPerWeek: recentChanges.length,
7736
+ lastChange: timelineEntries[0]?.date,
7737
+ mostFrequentlyChanged: mostChanged
7738
+ };
7739
+ }
7740
+ function determineChangeType(description) {
7741
+ const lower = description.toLowerCase();
7742
+ if (lower.includes("rollback") || lower.includes("revert")) return "rollback";
7743
+ if (lower.includes("remove") || lower.includes("uninstall")) return "remove";
7744
+ if (lower.includes("update") || lower.includes("upgrade")) return "update";
7745
+ return "install";
7746
+ }
7747
+ function formatInsights(insights, colorize = true) {
7748
+ const colors = {
7749
+ reset: colorize ? "\x1B[0m" : "",
7750
+ bold: colorize ? "\x1B[1m" : "",
7751
+ dim: colorize ? "\x1B[2m" : "",
7752
+ cyan: colorize ? "\x1B[36m" : "",
7753
+ green: colorize ? "\x1B[32m" : "",
7754
+ yellow: colorize ? "\x1B[33m" : "",
7755
+ red: colorize ? "\x1B[31m" : "",
7756
+ blue: colorize ? "\x1B[34m" : ""
7757
+ };
7758
+ const lines = [];
7759
+ function formatSize(kb) {
7760
+ if (kb >= 1e6) return `${(kb / 1e6).toFixed(1)}GB`;
7761
+ if (kb >= 1e3) return `${(kb / 1e3).toFixed(1)}MB`;
7762
+ return `${kb}KB`;
7763
+ }
7764
+ lines.push(`${colors.bold}Environment Analytics${colors.reset}`);
7765
+ lines.push("");
7766
+ lines.push(`${colors.bold}Overview${colors.reset}`);
7767
+ lines.push(` Packages: ${colors.cyan}${insights.stats.totalPackages}${colors.reset}`);
7768
+ lines.push(` Total Size: ${colors.cyan}${formatSize(insights.stats.totalSizeKb)}${colors.reset}`);
7769
+ const debtColor = insights.stats.technicalDebtScore < 30 ? colors.green : insights.stats.technicalDebtScore < 60 ? colors.yellow : colors.red;
7770
+ lines.push(` Technical Debt: ${debtColor}${insights.stats.technicalDebtScore}/100${colors.reset}`);
7771
+ lines.push("");
7772
+ if (Object.keys(insights.stats.categoryBreakdown).length > 0) {
7773
+ lines.push(`${colors.bold}Categories${colors.reset}`);
7774
+ const sortedCategories = Object.entries(insights.stats.categoryBreakdown).sort((a, b) => b[1] - a[1]);
7775
+ for (const [category, count] of sortedCategories) {
7776
+ const bar = "\u2588".repeat(Math.min(20, Math.ceil(count / insights.stats.totalPackages * 40)));
7777
+ lines.push(` ${category.padEnd(15)} ${colors.cyan}${bar}${colors.reset} ${count}`);
7778
+ }
7779
+ lines.push("");
7780
+ }
7781
+ if (insights.stats.largestPackages.length > 0) {
7782
+ lines.push(`${colors.bold}Largest Packages${colors.reset}`);
7783
+ for (const pkg of insights.stats.largestPackages) {
7784
+ lines.push(` ${pkg.name.padEnd(20)} ${colors.dim}${formatSize(pkg.sizeKb)}${colors.reset}`);
7785
+ }
7786
+ lines.push("");
7787
+ }
7788
+ if (insights.timeline) {
7789
+ lines.push(`${colors.bold}Activity${colors.reset}`);
7790
+ lines.push(` Changes this week: ${insights.timeline.averageChangesPerWeek}`);
7791
+ if (insights.timeline.lastChange) {
7792
+ lines.push(` Last change: ${insights.timeline.lastChange.toLocaleDateString()}`);
7793
+ }
7794
+ if (insights.timeline.mostFrequentlyChanged.length > 0) {
7795
+ lines.push(` Most changed: ${insights.timeline.mostFrequentlyChanged.join(", ")}`);
7796
+ }
7797
+ lines.push("");
7798
+ }
7799
+ if (insights.achievements.length > 0) {
7800
+ lines.push(`${colors.green}${colors.bold}Achievements${colors.reset}`);
7801
+ for (const achievement of insights.achievements) {
7802
+ lines.push(` ${colors.green}\u2605${colors.reset} ${achievement}`);
7803
+ }
7804
+ lines.push("");
7805
+ }
7806
+ if (insights.warnings.length > 0) {
7807
+ lines.push(`${colors.yellow}${colors.bold}Warnings${colors.reset}`);
7808
+ for (const warning of insights.warnings) {
7809
+ lines.push(` ${colors.yellow}\u26A0${colors.reset} ${warning}`);
7810
+ }
7811
+ lines.push("");
7812
+ }
7813
+ if (insights.recommendations.length > 0) {
7814
+ lines.push(`${colors.bold}Recommendations${colors.reset}`);
7815
+ for (const rec of insights.recommendations) {
7816
+ lines.push(` ${colors.cyan}\u2192${colors.reset} ${rec}`);
7817
+ }
7818
+ }
7819
+ return lines.join("\n");
7820
+ }
5706
7821
 
5707
7822
  export {
5708
7823
  __require,
@@ -5713,5 +7828,26 @@ export {
5713
7828
  Komodo,
5714
7829
  getInstalledPackages,
5715
7830
  detectConflicts,
5716
- analyzeHealth
7831
+ analyzeHealth,
7832
+ runDoctor,
7833
+ formatDoctorReport,
7834
+ buildDependencyTree,
7835
+ visualizeTree,
7836
+ diffPackages,
7837
+ formatDiff,
7838
+ analyzeOptimizations,
7839
+ formatOptimizationReport,
7840
+ generateOptimizationScript,
7841
+ TEMPLATES,
7842
+ getTemplateById,
7843
+ searchTemplates,
7844
+ formatTemplateList,
7845
+ getInstallCommandsForTemplate,
7846
+ explainPackage,
7847
+ formatExplanation,
7848
+ parseGitHubUrl,
7849
+ analyzeRepoStructure,
7850
+ formatRepoAnalysis,
7851
+ analyzeEnvironment,
7852
+ formatInsights
5717
7853
  };