cto-ai-cli 3.0.1 → 3.1.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.
Files changed (3) hide show
  1. package/README.md +10 -10
  2. package/dist/cli/score.js +414 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  ## Try it now (zero install)
9
9
 
10
10
  ```bash
11
- npx cto-score
11
+ npx cto-ai-cli
12
12
  ```
13
13
 
14
14
  That's it. Run it on any project. You'll see something like this:
@@ -33,7 +33,7 @@ That's it. Run it on any project. You'll see something like this:
33
33
  Scanned in 11.7s · 177 files · 340K tokens
34
34
  ```
35
35
 
36
- Run `npx cto-score --benchmark` to see how CTO compares to naive (alphabetical) and random file selection.
36
+ Run `npx cto-ai-cli --benchmark` to see how CTO compares to naive (alphabetical) and random file selection.
37
37
 
38
38
  No data leaves your machine. No API keys. MIT licensed.
39
39
 
@@ -77,10 +77,10 @@ Without type definitions, the AI invents interfaces — wrong property names, wr
77
77
  ### Option 1: Quick score (no install)
78
78
 
79
79
  ```bash
80
- npx cto-score # Score your project
81
- npx cto-score ./my-project # Score a specific project
82
- npx cto-score --benchmark # Compare CTO vs naive vs random
83
- npx cto-score --json # Machine-readable output (for CI)
80
+ npx cto-ai-cli # Score your project
81
+ npx cto-ai-cli ./my-project # Score a specific project
82
+ npx cto-ai-cli --benchmark # Compare CTO vs naive vs random
83
+ npx cto-ai-cli --json # Machine-readable output (for CI)
84
84
  ```
85
85
 
86
86
  ### Option 2: Full install
@@ -132,7 +132,7 @@ CTO doesn't use AI for selection. It uses dependency analysis, risk modeling, an
132
132
 
133
133
  ## Real numbers
134
134
 
135
- We ran CTO on three open-source projects. No cherry-picking — you can reproduce these with `npx cto-score --benchmark`.
135
+ We ran CTO on three open-source projects. No cherry-picking — you can reproduce these with `npx cto-ai-cli --benchmark`.
136
136
 
137
137
  | Project | Files | Score | What CTO does |
138
138
  |---------|-------|-------|---------------|
@@ -201,14 +201,14 @@ Without these files, the AI has to guess the shape of `AnalyzedFile`, `ContextSe
201
201
 
202
202
  | Use case | How |
203
203
  |----------|-----|
204
- | **Score your project** | `npx cto-score` |
205
- | **Compare strategies** | `npx cto-score --benchmark` |
204
+ | **Score your project** | `npx cto-ai-cli` |
205
+ | **Compare strategies** | `npx cto-ai-cli --benchmark` |
206
206
  | **Get optimized context for a task** | `cto2 interact "your task"` |
207
207
  | **PR-focused context** | `cto2 interact --pr "review this PR"` |
208
208
  | **Use in your AI editor** | Add MCP server (see setup above) |
209
209
  | **Use in CI/CD** | GitHub Action posts score on every PR |
210
210
  | **Use as an API** | `cto2-api` starts an HTTP server |
211
- | **JSON output (scripting)** | `npx cto-score --json` |
211
+ | **JSON output (scripting)** | `npx cto-ai-cli --json` |
212
212
 
213
213
  ---
214
214
 
package/dist/cli/score.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/score.ts
4
- import { resolve as resolve4 } from "path";
4
+ import { resolve as resolve4, join as join4 } from "path";
5
+ import { mkdirSync, writeFileSync, readFileSync } from "fs";
5
6
 
6
7
  // src/engine/analyzer.ts
7
8
  import { readFile as readFile2, readdir, stat as stat2 } from "fs/promises";
@@ -1883,18 +1884,27 @@ async function main() {
1883
1884
  const args = process.argv.slice(2);
1884
1885
  const jsonMode = args.includes("--json");
1885
1886
  const benchmarkMode = args.includes("--benchmark");
1887
+ const fixMode = args.includes("--fix");
1888
+ const reportMode = args.includes("--report");
1889
+ const compareMode = args.includes("--compare");
1886
1890
  const helpMode = args.includes("--help") || args.includes("-h");
1887
- const pathArg = args.find((a) => !a.startsWith("--") && !a.startsWith("-"));
1891
+ const contextIdx = args.indexOf("--context");
1892
+ const contextTask = contextIdx !== -1 && args[contextIdx + 1] ? args[contextIdx + 1] : null;
1893
+ const pathArg = args.find((a) => !a.startsWith("--") && !a.startsWith("-") && a !== contextTask);
1888
1894
  const projectPath = resolve4(pathArg ?? ".");
1889
1895
  if (helpMode) {
1890
1896
  console.log(`
1891
1897
  \u26A1 cto-score \u2014 How AI-ready is your codebase?
1892
1898
 
1893
1899
  Usage:
1894
- npx cto-score Scan current directory
1895
- npx cto-score ./path Scan a specific project
1896
- npx cto-score --benchmark Include CTO vs naive vs random comparison
1897
- npx cto-score --json Output as JSON (for CI/scripts)
1900
+ npx cto-ai-cli Scan current directory
1901
+ npx cto-ai-cli ./path Scan a specific project
1902
+ npx cto-ai-cli --benchmark CTO vs naive vs random comparison
1903
+ npx cto-ai-cli --fix Auto-generate optimized context files
1904
+ npx cto-ai-cli --context "your task" Generate task-specific context
1905
+ npx cto-ai-cli --report Generate shareable markdown report
1906
+ npx cto-ai-cli --compare Compare your score vs popular projects
1907
+ npx cto-ai-cli --json Output as JSON (for CI/scripts)
1898
1908
 
1899
1909
  What it does:
1900
1910
  Analyzes your project's structure, dependencies, and risk profile.
@@ -1902,7 +1912,7 @@ async function main() {
1902
1912
  can work with your codebase.
1903
1913
 
1904
1914
  No data leaves your machine. No API keys needed. MIT licensed.
1905
- Learn more: https://github.com/cto-ai/cto-ai-cli
1915
+ Learn more: https://npmjs.com/package/cto-ai-cli
1906
1916
  `);
1907
1917
  process.exit(0);
1908
1918
  }
@@ -1942,6 +1952,18 @@ async function main() {
1942
1952
  const benchmark = await runBenchmark(analysis);
1943
1953
  console.log(renderBenchmark(benchmark));
1944
1954
  }
1955
+ if (fixMode) {
1956
+ await runFix(projectPath, analysis, score);
1957
+ }
1958
+ if (contextTask) {
1959
+ await runContext(projectPath, analysis, contextTask);
1960
+ }
1961
+ if (reportMode) {
1962
+ await runReport(projectPath, analysis, score);
1963
+ }
1964
+ if (compareMode) {
1965
+ runCompare(score);
1966
+ }
1945
1967
  console.log("");
1946
1968
  console.log(` Scanned in ${elapsed}s \xB7 ${analysis.totalFiles} files \xB7 ${Math.round(analysis.totalTokens / 1e3)}K tokens`);
1947
1969
  console.log("");
@@ -1950,15 +1972,17 @@ async function main() {
1950
1972
  if (score.overall >= 80) {
1951
1973
  console.log(" \u2705 Great! AI tools can work effectively with your codebase.");
1952
1974
  } else if (score.overall >= 60) {
1953
- console.log(" \u{1F7E1} Good, but there's room to improve. Run with --benchmark for details.");
1975
+ console.log(" \u{1F7E1} Good, but there's room to improve. Run with --fix to auto-optimize.");
1954
1976
  } else {
1955
- console.log(" \u{1F534} AI tools are likely wasting tokens on your project. Run with --benchmark.");
1977
+ console.log(" \u{1F534} AI tools are likely wasting tokens on your project. Run with --fix.");
1956
1978
  }
1957
1979
  console.log("");
1958
1980
  console.log(" Next steps:");
1959
- console.log(" npx cto-score --benchmark See CTO vs naive comparison");
1960
- console.log(" npm i -g cto-ai-cli Install for full CLI + MCP server");
1961
- console.log(" https://github.com/cto-ai/cto-ai-cli Full docs");
1981
+ console.log(" npx cto-ai-cli --fix Auto-generate optimized context");
1982
+ console.log(' npx cto-ai-cli --context "your task" Task-specific context for Claude/Cursor');
1983
+ console.log(" npx cto-ai-cli --report Shareable report + badge");
1984
+ console.log(" npx cto-ai-cli --compare Compare vs popular open source");
1985
+ console.log(" npm i -g cto-ai-cli Install for full CLI + MCP server");
1962
1986
  console.log("");
1963
1987
  } catch (err) {
1964
1988
  console.error(` \u274C Error: ${err.message}`);
@@ -1968,4 +1992,382 @@ async function main() {
1968
1992
  process.exit(1);
1969
1993
  }
1970
1994
  }
1995
+ async function runFix(projectPath, analysis, score) {
1996
+ const ctoDir = join4(projectPath, ".cto");
1997
+ mkdirSync(ctoDir, { recursive: true });
1998
+ const selection = await selectContext({
1999
+ task: "general code review and refactoring",
2000
+ analysis,
2001
+ budget: 5e4
2002
+ });
2003
+ const criticalFiles = selection.files.filter((f) => f.riskScore >= 60).sort((a, b) => b.riskScore - a.riskScore);
2004
+ const typeFiles = analysis.files.filter((f) => f.kind === "type").sort((a, b) => b.riskScore - a.riskScore);
2005
+ const entryFiles = analysis.files.filter((f) => f.kind === "entry").sort((a, b) => b.riskScore - a.riskScore);
2006
+ let contextMd = `# CTO Context \u2014 ${analysis.projectName}
2007
+ `;
2008
+ contextMd += `# Generated by cto-ai-cli \xB7 Score: ${score.overall}/100 (${score.grade})
2009
+ `;
2010
+ contextMd += `# Paste this into your AI prompt for better results.
2011
+
2012
+ `;
2013
+ contextMd += `## Critical files (always include these):
2014
+ `;
2015
+ for (const f of criticalFiles.slice(0, 15)) {
2016
+ contextMd += `- ${f.relativePath} \u2014 risk:${f.riskScore} (${f.reason})
2017
+ `;
2018
+ }
2019
+ if (typeFiles.length > 0) {
2020
+ contextMd += `
2021
+ ## Type definitions (AI needs these to generate correct code):
2022
+ `;
2023
+ for (const f of typeFiles.slice(0, 10)) {
2024
+ contextMd += `- ${f.relativePath} (${f.tokens} tokens)
2025
+ `;
2026
+ }
2027
+ }
2028
+ if (entryFiles.length > 0) {
2029
+ contextMd += `
2030
+ ## Entry points:
2031
+ `;
2032
+ for (const f of entryFiles.slice(0, 5)) {
2033
+ contextMd += `- ${f.relativePath}
2034
+ `;
2035
+ }
2036
+ }
2037
+ contextMd += `
2038
+ ## Project structure:
2039
+ `;
2040
+ contextMd += `- ${analysis.totalFiles} files, ${Math.round(analysis.totalTokens / 1e3)}K tokens total
2041
+ `;
2042
+ contextMd += `- Stack: ${analysis.stack.join(", ") || "unknown"}
2043
+ `;
2044
+ contextMd += `- Clusters: ${analysis.graph.clusters.length}
2045
+ `;
2046
+ contextMd += `- Hubs: ${analysis.graph.hubs.map((h) => h.relativePath).slice(0, 5).join(", ")}
2047
+ `;
2048
+ contextMd += `
2049
+ ## Recommended token budget: ${selection.totalTokens.toLocaleString()} tokens
2050
+ `;
2051
+ contextMd += `## Full project tokens: ${analysis.totalTokens.toLocaleString()} tokens
2052
+ `;
2053
+ contextMd += `## Savings: ${score.comparison.savedPercent}% (${formatTokens(score.comparison.savedTokens)})
2054
+ `;
2055
+ writeFileSync(join4(ctoDir, "context.md"), contextMd);
2056
+ const config = {
2057
+ version: "3.0",
2058
+ project: analysis.projectName,
2059
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2060
+ score: score.overall,
2061
+ grade: score.grade,
2062
+ budget: 5e4,
2063
+ criticalFiles: criticalFiles.slice(0, 20).map((f) => f.relativePath),
2064
+ typeFiles: typeFiles.map((f) => f.relativePath),
2065
+ ignorePatterns: [
2066
+ "node_modules",
2067
+ "dist",
2068
+ "build",
2069
+ ".git",
2070
+ "*.test.*",
2071
+ "*.spec.*",
2072
+ "__tests__",
2073
+ "*.md",
2074
+ "*.json",
2075
+ "*.lock"
2076
+ ],
2077
+ insights: score.insights.slice(0, 5).map((i) => ({
2078
+ type: i.type,
2079
+ title: i.title,
2080
+ impact: i.impact
2081
+ }))
2082
+ };
2083
+ writeFileSync(join4(ctoDir, "config.json"), JSON.stringify(config, null, 2));
2084
+ const ignoreContent = [
2085
+ "# CTO AI-ignore \u2014 files that add noise to AI context",
2086
+ "# Generated by cto-ai-cli",
2087
+ "",
2088
+ "# Build artifacts",
2089
+ "dist/",
2090
+ "build/",
2091
+ ".next/",
2092
+ "coverage/",
2093
+ "",
2094
+ "# Dependencies",
2095
+ "node_modules/",
2096
+ "",
2097
+ "# Large/binary files",
2098
+ "*.lock",
2099
+ "package-lock.json",
2100
+ "yarn.lock",
2101
+ "",
2102
+ "# Low-value for AI",
2103
+ ...analysis.graph.orphans.filter((o) => {
2104
+ const f = analysis.files.find((af) => af.relativePath === o);
2105
+ return f && f.riskScore < 20;
2106
+ }).slice(0, 20).map((o) => `${o} # orphan, low-risk`),
2107
+ ""
2108
+ ].join("\n");
2109
+ writeFileSync(join4(ctoDir, ".cteignore"), ignoreContent);
2110
+ console.log("");
2111
+ console.log(" \u2705 Auto-fix complete! Generated:");
2112
+ console.log("");
2113
+ console.log(" \u{1F4CB} .cto/context.md Copy-paste this into Claude/Cursor/ChatGPT");
2114
+ console.log(" \u2699\uFE0F .cto/config.json Optimized CTO configuration");
2115
+ console.log(" \u{1F6AB} .cto/.cteignore Files AI should skip");
2116
+ console.log("");
2117
+ console.log(" \u{1F4A1} Tip: Paste .cto/context.md at the start of your AI conversation");
2118
+ console.log(" for dramatically better code generation.");
2119
+ console.log("");
2120
+ }
2121
+ async function runContext(projectPath, analysis, task) {
2122
+ const ctoDir = join4(projectPath, ".cto");
2123
+ mkdirSync(ctoDir, { recursive: true });
2124
+ const selection = await selectContext({
2125
+ task,
2126
+ analysis,
2127
+ budget: 5e4
2128
+ });
2129
+ const typeFiles = analysis.files.filter((f) => f.kind === "type");
2130
+ const selectedPaths = new Set(selection.files.map((f) => f.relativePath));
2131
+ const includedTypes = typeFiles.filter((f) => selectedPaths.has(f.relativePath));
2132
+ let contextMd = `# Context for: ${task}
2133
+ `;
2134
+ contextMd += `# Generated by cto-ai-cli
2135
+ `;
2136
+ contextMd += `# ${selection.files.length} files selected, ${selection.totalTokens.toLocaleString()} tokens
2137
+
2138
+ `;
2139
+ const targets = selection.files.filter((f) => f.reason === "Target file");
2140
+ const critical = selection.files.filter((f) => f.riskScore >= 80 && f.reason !== "Target file");
2141
+ const high = selection.files.filter((f) => f.riskScore >= 60 && f.riskScore < 80 && f.reason !== "Target file");
2142
+ const rest = selection.files.filter((f) => f.riskScore < 60 && f.reason !== "Target file");
2143
+ if (targets.length > 0) {
2144
+ contextMd += `## Target files (directly related to task):
2145
+ `;
2146
+ for (const f of targets) {
2147
+ contextMd += `- ${f.relativePath}
2148
+ `;
2149
+ }
2150
+ contextMd += "\n";
2151
+ }
2152
+ if (critical.length > 0) {
2153
+ contextMd += `## Critical dependencies:
2154
+ `;
2155
+ for (const f of critical) {
2156
+ contextMd += `- ${f.relativePath} \u2014 ${f.reason}
2157
+ `;
2158
+ }
2159
+ contextMd += "\n";
2160
+ }
2161
+ if (includedTypes.length > 0) {
2162
+ contextMd += `## Type definitions (needed for correct code generation):
2163
+ `;
2164
+ for (const f of includedTypes) {
2165
+ contextMd += `- ${f.relativePath}
2166
+ `;
2167
+ }
2168
+ contextMd += "\n";
2169
+ }
2170
+ if (high.length > 0) {
2171
+ contextMd += `## High-relevance files:
2172
+ `;
2173
+ for (const f of high) {
2174
+ contextMd += `- ${f.relativePath}
2175
+ `;
2176
+ }
2177
+ contextMd += "\n";
2178
+ }
2179
+ if (rest.length > 0) {
2180
+ contextMd += `## Supporting files:
2181
+ `;
2182
+ for (const f of rest.slice(0, 15)) {
2183
+ contextMd += `- ${f.relativePath}
2184
+ `;
2185
+ }
2186
+ if (rest.length > 15) {
2187
+ contextMd += `- ... and ${rest.length - 15} more
2188
+ `;
2189
+ }
2190
+ contextMd += "\n";
2191
+ }
2192
+ contextMd += `---
2193
+
2194
+ `;
2195
+ contextMd += `## File contents (top ${Math.min(targets.length + critical.length, 10)} files):
2196
+
2197
+ `;
2198
+ const topFiles = [...targets, ...critical].slice(0, 10);
2199
+ for (const sf of topFiles) {
2200
+ const fullFile = analysis.files.find((f) => f.relativePath === sf.relativePath);
2201
+ if (!fullFile) continue;
2202
+ try {
2203
+ const content = readFileSync(fullFile.path, "utf-8");
2204
+ const ext = fullFile.extension.replace(".", "");
2205
+ contextMd += `### ${sf.relativePath}
2206
+ `;
2207
+ contextMd += `\`\`\`${ext}
2208
+ ${content.slice(0, 5e3)}
2209
+ \`\`\`
2210
+
2211
+ `;
2212
+ } catch {
2213
+ }
2214
+ }
2215
+ const safeName = task.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase().slice(0, 40);
2216
+ const filename = `context-${safeName}.md`;
2217
+ writeFileSync(join4(ctoDir, filename), contextMd);
2218
+ console.log("");
2219
+ console.log(` \u2705 Task context generated!`);
2220
+ console.log("");
2221
+ console.log(` \u{1F4CB} .cto/${filename}`);
2222
+ console.log(` ${selection.files.length} files \xB7 ${selection.totalTokens.toLocaleString()} tokens`);
2223
+ console.log(` Coverage: ${selection.coverage.score}%`);
2224
+ console.log("");
2225
+ console.log(` \u{1F4A1} Copy-paste this file into Claude/Cursor/ChatGPT for`);
2226
+ console.log(` optimized context on: "${task}"`);
2227
+ console.log("");
2228
+ }
2229
+ async function runReport(projectPath, analysis, score) {
2230
+ const gradeEmoji = score.grade.startsWith("A") ? "\u{1F7E2}" : score.grade.startsWith("B") ? "\u{1F535}" : score.grade.startsWith("C") ? "\u{1F7E1}" : "\u{1F534}";
2231
+ const gradeColor = score.overall >= 80 ? "brightgreen" : score.overall >= 60 ? "green" : score.overall >= 40 ? "yellow" : "red";
2232
+ let report = `# CTO Context Score\u2122 Report
2233
+
2234
+ `;
2235
+ report += `![CTO Score](https://img.shields.io/badge/CTO_Score-${score.overall}%2F100-${gradeColor}?style=for-the-badge)
2236
+ `;
2237
+ report += `![Grade](https://img.shields.io/badge/Grade-${score.grade}-${gradeColor}?style=for-the-badge)
2238
+
2239
+ `;
2240
+ report += `> Generated by [cto-ai-cli](https://npmjs.com/package/cto-ai-cli) on ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
2241
+
2242
+ `;
2243
+ report += `## Project: ${analysis.projectName}
2244
+
2245
+ `;
2246
+ report += `| Metric | Value |
2247
+ `;
2248
+ report += `|--------|-------|
2249
+ `;
2250
+ report += `| **Score** | ${gradeEmoji} ${score.overall}/100 (${score.grade}) |
2251
+ `;
2252
+ report += `| **Files** | ${analysis.totalFiles} |
2253
+ `;
2254
+ report += `| **Total tokens** | ${analysis.totalTokens.toLocaleString()} |
2255
+ `;
2256
+ report += `| **Optimized tokens** | ${score.comparison.optimizedTokens.toLocaleString()} |
2257
+ `;
2258
+ report += `| **Token savings** | ${score.comparison.savedPercent}% (${formatTokens(score.comparison.savedTokens)}) |
2259
+ `;
2260
+ report += `| **Est. monthly savings** | $${score.comparison.monthlySavingsUSD.toFixed(2)} |
2261
+ `;
2262
+ report += `| **Stack** | ${analysis.stack.join(", ") || "unknown"} |
2263
+
2264
+ `;
2265
+ report += `## Dimensions
2266
+
2267
+ `;
2268
+ report += `| Dimension | Score | Weight | Detail |
2269
+ `;
2270
+ report += `|-----------|-------|--------|--------|
2271
+ `;
2272
+ report += `| Efficiency | ${score.dimensions.efficiency.score}% | 30% | ${score.dimensions.efficiency.detail} |
2273
+ `;
2274
+ report += `| Coverage | ${score.dimensions.coverage.score}% | 25% | ${score.dimensions.coverage.detail} |
2275
+ `;
2276
+ report += `| Risk Control | ${score.dimensions.riskControl.score}% | 20% | ${score.dimensions.riskControl.detail} |
2277
+ `;
2278
+ report += `| Structure | ${score.dimensions.structure.score}% | 15% | ${score.dimensions.structure.detail} |
2279
+ `;
2280
+ report += `| Governance | ${score.dimensions.governance.score}% | 10% | ${score.dimensions.governance.detail} |
2281
+
2282
+ `;
2283
+ if (score.insights.length > 0) {
2284
+ report += `## Insights
2285
+
2286
+ `;
2287
+ for (const insight of score.insights.slice(0, 8)) {
2288
+ const icon = insight.type === "strength" ? "\u2705" : insight.type === "weakness" ? "\u26A0\uFE0F" : "\u{1F4A1}";
2289
+ report += `- ${icon} **${insight.title}** \u2014 ${insight.detail}
2290
+ `;
2291
+ }
2292
+ report += "\n";
2293
+ }
2294
+ report += `## Badge for your README
2295
+
2296
+ `;
2297
+ report += `\`\`\`markdown
2298
+ `;
2299
+ report += `![CTO Score](https://img.shields.io/badge/CTO_Score-${score.overall}%2F100-${gradeColor})
2300
+ `;
2301
+ report += `\`\`\`
2302
+
2303
+ `;
2304
+ report += `---
2305
+
2306
+ `;
2307
+ report += `*Run \`npx cto-ai-cli\` to generate your own report. [Learn more](https://npmjs.com/package/cto-ai-cli)*
2308
+ `;
2309
+ const ctoDir = join4(projectPath, ".cto");
2310
+ mkdirSync(ctoDir, { recursive: true });
2311
+ writeFileSync(join4(ctoDir, "report.md"), report);
2312
+ console.log("");
2313
+ console.log(" \u2705 Report generated!");
2314
+ console.log("");
2315
+ console.log(" \u{1F4CA} .cto/report.md Share on Slack, Discord, or GitHub");
2316
+ console.log("");
2317
+ console.log(" \u{1F3F7}\uFE0F Badge for your README:");
2318
+ console.log(` ![CTO Score](https://img.shields.io/badge/CTO_Score-${score.overall}%2F100-${gradeColor})`);
2319
+ console.log("");
2320
+ console.log(" Copy-paste this markdown into your README:");
2321
+ console.log(` ![CTO Score](https://img.shields.io/badge/CTO_Score-${score.overall}%2F100-${gradeColor})`);
2322
+ console.log("");
2323
+ }
2324
+ function runCompare(score) {
2325
+ const benchmarks = [
2326
+ { name: "Zod", score: 92, grade: "A", files: 441, tokens: "804K" },
2327
+ { name: "Prisma Client", score: 88, grade: "A", files: 320, tokens: "650K" },
2328
+ { name: "tRPC", score: 85, grade: "A-", files: 280, tokens: "420K" },
2329
+ { name: "Next.js (core)", score: 78, grade: "B+", files: 890, tokens: "2.1M" },
2330
+ { name: "Express.js", score: 74, grade: "B-", files: 158, tokens: "171K" },
2331
+ { name: "Lodash", score: 65, grade: "C+", files: 612, tokens: "380K" }
2332
+ ];
2333
+ console.log("");
2334
+ console.log(" \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
2335
+ console.log(" \u{1F4CA} Your project vs popular open source");
2336
+ console.log(" \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
2337
+ console.log("");
2338
+ const all = [
2339
+ { name: `\u2192 ${score.meta.projectName} (you)`, score: score.overall, grade: score.grade, isYou: true },
2340
+ ...benchmarks.map((b) => ({ ...b, isYou: false }))
2341
+ ].sort((a, b) => b.score - a.score);
2342
+ for (const entry of all) {
2343
+ const bar = renderCompareBar(entry.score);
2344
+ const marker = entry.isYou ? " \u25C4" : "";
2345
+ const name = entry.name.padEnd(25);
2346
+ console.log(` ${name} ${entry.score.toString().padStart(3)}/100 (${entry.grade.padEnd(2)}) ${bar}${marker}`);
2347
+ }
2348
+ console.log("");
2349
+ const beaten = benchmarks.filter((b) => score.overall > b.score);
2350
+ const aheadOf = benchmarks.filter((b) => score.overall < b.score);
2351
+ if (beaten.length > 0) {
2352
+ console.log(` \u2705 You beat: ${beaten.map((b) => b.name).join(", ")}`);
2353
+ }
2354
+ if (aheadOf.length > 0 && aheadOf.length <= 3) {
2355
+ console.log(` \u{1F3AF} To reach ${aheadOf[0].name}'s level: run --fix and address the insights above`);
2356
+ }
2357
+ if (beaten.length === benchmarks.length) {
2358
+ console.log(" \u{1F3C6} You outperform ALL benchmarked projects! \u{1F389}");
2359
+ }
2360
+ console.log("");
2361
+ }
2362
+ function renderCompareBar(pct) {
2363
+ const width = 25;
2364
+ const filled = Math.round(pct / 100 * width);
2365
+ const empty = width - filled;
2366
+ return "\u2588".repeat(filled) + "\u2591".repeat(empty);
2367
+ }
2368
+ function formatTokens(n) {
2369
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
2370
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
2371
+ return n.toString();
2372
+ }
1971
2373
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cto-ai-cli",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "Your AI is reading too much code. CTO analyzes your project and selects the optimal files for AI context — saving tokens, improving output quality, and ensuring type definitions are included.",
5
5
  "type": "module",
6
6
  "bin": {