composto-ai 0.8.0 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.js +53 -8
- package/dist/mcp/server.js +11 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**Token-efficient code context for AI agents. Your file's full structure in a fraction of the tokens, with its causal history baked in.**
|
|
4
4
|
|
|
5
|
+
> Send your agent the structure, not the noise.
|
|
6
|
+
|
|
5
7
|
Composto compresses any source file into a Health-Aware IR that keeps exactly what your agent needs — signatures, types, control flow, dependencies — at 60-95% fewer tokens than raw code. On top of that, it surfaces the file's causal history (what historically changed and broke alongside the code you're touching) as advisory context. Local-first, MIT. Works with Claude Code, Cursor, and Gemini CLI.
|
|
6
8
|
|
|
7
9
|
```
|
package/dist/index.js
CHANGED
|
@@ -1010,6 +1010,17 @@ function emitTier3(node) {
|
|
|
1010
1010
|
if (expr.type === "await_expression") {
|
|
1011
1011
|
return null;
|
|
1012
1012
|
}
|
|
1013
|
+
if (expr.type === "assignment_expression") {
|
|
1014
|
+
if (node.parent?.type === "statement_block") return null;
|
|
1015
|
+
const left = expr.childForFieldName("left");
|
|
1016
|
+
const right = expr.childForFieldName("right");
|
|
1017
|
+
if (left?.type === "member_expression" && right && (right.type === "function_expression" || right.type === "arrow_function")) {
|
|
1018
|
+
const asyncPrefix = isAsync(right) ? "ASYNC " : "";
|
|
1019
|
+
const params = right.childForFieldName("parameters")?.text ?? "()";
|
|
1020
|
+
return `${asyncPrefix}FN:${left.text}${collapseText(params, 60)}`;
|
|
1021
|
+
}
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1013
1024
|
if (expr.type === "call_expression") {
|
|
1014
1025
|
const callee = expr.child(0)?.text ?? "";
|
|
1015
1026
|
if (callee === "ObjectSetPrototypeOf" || callee === "Object.setPrototypeOf") {
|
|
@@ -1515,6 +1526,17 @@ function summarize(results) {
|
|
|
1515
1526
|
return { fileCount: results.length, totalRaw, totalIRL0, totalIRL1, totalSavedPercent, astCount, fpCount };
|
|
1516
1527
|
}
|
|
1517
1528
|
|
|
1529
|
+
// src/version.ts
|
|
1530
|
+
import { createRequire } from "module";
|
|
1531
|
+
var VERSION = (() => {
|
|
1532
|
+
try {
|
|
1533
|
+
const req = createRequire(import.meta.url);
|
|
1534
|
+
return req("../package.json").version;
|
|
1535
|
+
} catch {
|
|
1536
|
+
return "0.0.0";
|
|
1537
|
+
}
|
|
1538
|
+
})();
|
|
1539
|
+
|
|
1518
1540
|
// src/benchmark/quality.ts
|
|
1519
1541
|
var BENCHMARK_PROMPTS = [
|
|
1520
1542
|
{
|
|
@@ -2681,7 +2703,8 @@ function statFileSize(path) {
|
|
|
2681
2703
|
function runScan(projectPath) {
|
|
2682
2704
|
const adapter = new CLIAdapter();
|
|
2683
2705
|
const config = loadConfig(projectPath);
|
|
2684
|
-
console.log(
|
|
2706
|
+
console.log(`composto v${VERSION} \u2014 scanning...
|
|
2707
|
+
`);
|
|
2685
2708
|
const files = collectFiles(projectPath, [".ts", ".tsx", ".js", ".jsx"]);
|
|
2686
2709
|
console.log(` Found ${files.length} files
|
|
2687
2710
|
`);
|
|
@@ -2708,7 +2731,8 @@ function runScan(projectPath) {
|
|
|
2708
2731
|
function runTrends(projectPath) {
|
|
2709
2732
|
const adapter = new CLIAdapter();
|
|
2710
2733
|
const config = loadConfig(projectPath);
|
|
2711
|
-
console.log(
|
|
2734
|
+
console.log(`composto v${VERSION} \u2014 trend analysis...
|
|
2735
|
+
`);
|
|
2712
2736
|
const entries = getGitLog(projectPath, 100);
|
|
2713
2737
|
if (entries.length === 0) {
|
|
2714
2738
|
console.log(" No git history found.\n");
|
|
@@ -2750,7 +2774,8 @@ async function runIR(projectPath, filePath, layer) {
|
|
|
2750
2774
|
}
|
|
2751
2775
|
var ALL_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs"];
|
|
2752
2776
|
async function runBenchmark(projectPath) {
|
|
2753
|
-
console.log(
|
|
2777
|
+
console.log(`composto v${VERSION} \u2014 benchmark
|
|
2778
|
+
`);
|
|
2754
2779
|
const files = collectFiles(projectPath, ALL_EXTENSIONS);
|
|
2755
2780
|
console.log(` ${files.length} files
|
|
2756
2781
|
`);
|
|
@@ -2788,6 +2813,25 @@ async function runBenchmark(projectPath) {
|
|
|
2788
2813
|
console.log(` L1 (full IR): ${summary.totalRaw} \u2192 ${summary.totalIRL1} tokens (${summary.totalSavedPercent.toFixed(1)}% reduction)`);
|
|
2789
2814
|
console.log(` Files analyzed: ${summary.fileCount}`);
|
|
2790
2815
|
console.log(` Engine: ${summary.astCount} AST, ${summary.fpCount} FP`);
|
|
2816
|
+
printSavingsCard(summary);
|
|
2817
|
+
}
|
|
2818
|
+
function printSavingsCard(summary) {
|
|
2819
|
+
if (summary.totalRaw <= 0) return;
|
|
2820
|
+
const saved = summary.totalRaw - summary.totalIRL1;
|
|
2821
|
+
const pct2 = summary.totalSavedPercent.toFixed(1);
|
|
2822
|
+
const SONNET_PER_MTOK = 3;
|
|
2823
|
+
const LOADS_PER_DAY = 50;
|
|
2824
|
+
const DAYS = 30;
|
|
2825
|
+
const rawCost = summary.totalRaw / 1e6 * SONNET_PER_MTOK;
|
|
2826
|
+
const irCost = summary.totalIRL1 / 1e6 * SONNET_PER_MTOK;
|
|
2827
|
+
const monthly = (rawCost - irCost) * LOADS_PER_DAY * DAYS;
|
|
2828
|
+
console.log("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2829
|
+
console.log(` \u{1F4B8} Every full-context load of this project: $${rawCost.toFixed(2)} raw \u2192 $${irCost.toFixed(2)} with Composto`);
|
|
2830
|
+
console.log(` (Claude Sonnet input, $3/Mtok). At ${LOADS_PER_DAY} loads/day that is ~$${monthly.toFixed(0)}/month saved.`);
|
|
2831
|
+
console.log("\n \u{1F4CB} Share your result:");
|
|
2832
|
+
console.log(` Composto compressed my ${summary.fileCount}-file project ${pct2}% (${summary.totalRaw.toLocaleString()} \u2192 ${summary.totalIRL1.toLocaleString()} tokens).`);
|
|
2833
|
+
console.log(" Try yours: npm i -g composto-ai && composto benchmark .");
|
|
2834
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2791
2835
|
}
|
|
2792
2836
|
async function runBenchmarkQuality(projectPath, filePath) {
|
|
2793
2837
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
@@ -2797,7 +2841,8 @@ async function runBenchmarkQuality(projectPath, filePath) {
|
|
|
2797
2841
|
}
|
|
2798
2842
|
const code = readFileSync4(filePath, "utf-8");
|
|
2799
2843
|
const relPath = relative2(projectPath, filePath);
|
|
2800
|
-
console.log(
|
|
2844
|
+
console.log(`composto v${VERSION} \u2014 quality benchmark
|
|
2845
|
+
`);
|
|
2801
2846
|
console.log(` File: ${relPath}
|
|
2802
2847
|
`);
|
|
2803
2848
|
console.log(" Sending to Claude Haiku...\n");
|
|
@@ -2827,8 +2872,8 @@ ${result.ir.response}
|
|
|
2827
2872
|
}
|
|
2828
2873
|
}
|
|
2829
2874
|
async function runContext(projectPath, budget, target) {
|
|
2830
|
-
const header = target ? `composto
|
|
2831
|
-
` : `composto
|
|
2875
|
+
const header = target ? `composto v${VERSION} \u2014 context (target: ${target}, budget: ${budget} tokens)
|
|
2876
|
+
` : `composto v${VERSION} \u2014 context (budget: ${budget} tokens)
|
|
2832
2877
|
`;
|
|
2833
2878
|
console.log(header);
|
|
2834
2879
|
const files = collectFiles(projectPath, ALL_EXTENSIONS);
|
|
@@ -3649,11 +3694,11 @@ function renderSummary(s) {
|
|
|
3649
3694
|
}
|
|
3650
3695
|
|
|
3651
3696
|
// src/index.ts
|
|
3652
|
-
import { createRequire } from "module";
|
|
3697
|
+
import { createRequire as createRequire2 } from "module";
|
|
3653
3698
|
import { join as join13, resolve as resolve2 } from "path";
|
|
3654
3699
|
var PKG_VERSION = (() => {
|
|
3655
3700
|
try {
|
|
3656
|
-
const req =
|
|
3701
|
+
const req = createRequire2(import.meta.url);
|
|
3657
3702
|
return req("../package.json").version;
|
|
3658
3703
|
} catch {
|
|
3659
3704
|
return "0.0.0";
|
package/dist/mcp/server.js
CHANGED
|
@@ -818,6 +818,17 @@ function emitTier3(node) {
|
|
|
818
818
|
if (expr.type === "await_expression") {
|
|
819
819
|
return null;
|
|
820
820
|
}
|
|
821
|
+
if (expr.type === "assignment_expression") {
|
|
822
|
+
if (node.parent?.type === "statement_block") return null;
|
|
823
|
+
const left = expr.childForFieldName("left");
|
|
824
|
+
const right = expr.childForFieldName("right");
|
|
825
|
+
if (left?.type === "member_expression" && right && (right.type === "function_expression" || right.type === "arrow_function")) {
|
|
826
|
+
const asyncPrefix = isAsync(right) ? "ASYNC " : "";
|
|
827
|
+
const params = right.childForFieldName("parameters")?.text ?? "()";
|
|
828
|
+
return `${asyncPrefix}FN:${left.text}${collapseText(params, 60)}`;
|
|
829
|
+
}
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
821
832
|
if (expr.type === "call_expression") {
|
|
822
833
|
const callee = expr.child(0)?.text ?? "";
|
|
823
834
|
if (callee === "ObjectSetPrototypeOf" || callee === "Object.setPrototypeOf") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "composto-ai",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Proactive AI team companion — less tokens, more insight",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsup",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
21
22
|
"test": "vitest run",
|
|
22
23
|
"test:watch": "vitest",
|
|
23
24
|
"dev": "tsx src/index.ts"
|