composto-ai 0.8.0 → 0.8.2

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 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") {
@@ -2788,6 +2799,25 @@ async function runBenchmark(projectPath) {
2788
2799
  console.log(` L1 (full IR): ${summary.totalRaw} \u2192 ${summary.totalIRL1} tokens (${summary.totalSavedPercent.toFixed(1)}% reduction)`);
2789
2800
  console.log(` Files analyzed: ${summary.fileCount}`);
2790
2801
  console.log(` Engine: ${summary.astCount} AST, ${summary.fpCount} FP`);
2802
+ printSavingsCard(summary);
2803
+ }
2804
+ function printSavingsCard(summary) {
2805
+ if (summary.totalRaw <= 0) return;
2806
+ const saved = summary.totalRaw - summary.totalIRL1;
2807
+ const pct2 = summary.totalSavedPercent.toFixed(1);
2808
+ const SONNET_PER_MTOK = 3;
2809
+ const LOADS_PER_DAY = 50;
2810
+ const DAYS = 30;
2811
+ const rawCost = summary.totalRaw / 1e6 * SONNET_PER_MTOK;
2812
+ const irCost = summary.totalIRL1 / 1e6 * SONNET_PER_MTOK;
2813
+ const monthly = (rawCost - irCost) * LOADS_PER_DAY * DAYS;
2814
+ 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");
2815
+ console.log(` \u{1F4B8} Every full-context load of this project: $${rawCost.toFixed(2)} raw \u2192 $${irCost.toFixed(2)} with Composto`);
2816
+ console.log(` (Claude Sonnet input, $3/Mtok). At ${LOADS_PER_DAY} loads/day that is ~$${monthly.toFixed(0)}/month saved.`);
2817
+ console.log("\n \u{1F4CB} Share your result:");
2818
+ console.log(` Composto compressed my ${summary.fileCount}-file project ${pct2}% (${summary.totalRaw.toLocaleString()} \u2192 ${summary.totalIRL1.toLocaleString()} tokens).`);
2819
+ console.log(" Try yours: npm i -g composto-ai && composto benchmark .");
2820
+ 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
2821
  }
2792
2822
  async function runBenchmarkQuality(projectPath, filePath) {
2793
2823
  const apiKey = process.env.ANTHROPIC_API_KEY;
@@ -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.0",
3
+ "version": "0.8.2",
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"