composto-ai 0.6.1 → 0.7.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.
package/dist/index.js CHANGED
@@ -3062,40 +3062,45 @@ function writeCursorHooks(projectPath, result) {
3062
3062
  if (existed) result.merged.push(relPath);
3063
3063
  else result.written.push(relPath);
3064
3064
  }
3065
- function initCursor(projectPath, result) {
3066
- writeJsonMerged(
3067
- join7(projectPath, ".cursor", "mcp.json"),
3068
- {
3069
- mcpServers: {
3070
- composto: {
3071
- command: "composto-mcp",
3072
- env: { COMPOSTO_BLASTRADIUS: "1" }
3065
+ function initCursor(projectPath, result, options) {
3066
+ if (options.withMcp) {
3067
+ writeJsonMerged(
3068
+ join7(projectPath, ".cursor", "mcp.json"),
3069
+ {
3070
+ mcpServers: {
3071
+ composto: {
3072
+ command: "composto-mcp",
3073
+ env: { COMPOSTO_BLASTRADIUS: "1" }
3074
+ }
3073
3075
  }
3074
- }
3075
- },
3076
- result,
3077
- ".cursor/mcp.json"
3078
- );
3079
- writeFileSkipIfExists(
3080
- join7(projectPath, ".cursor", "rules", "composto.mdc"),
3081
- CURSOR_RULES_MDC,
3082
- result,
3083
- ".cursor/rules/composto.mdc"
3084
- );
3076
+ },
3077
+ result,
3078
+ ".cursor/mcp.json"
3079
+ );
3080
+ }
3081
+ if (options.withRules) {
3082
+ writeFileSkipIfExists(
3083
+ join7(projectPath, ".cursor", "rules", "composto.mdc"),
3084
+ CURSOR_RULES_MDC,
3085
+ result,
3086
+ ".cursor/rules/composto.mdc"
3087
+ );
3088
+ }
3085
3089
  writeCursorHooks(projectPath, result);
3086
3090
  }
3087
- function initClaudeCode(projectPath, result) {
3091
+ function initClaudeCode(projectPath, result, options) {
3088
3092
  const settingsPath = join7(projectPath, ".claude", "settings.json");
3089
3093
  const relPath = ".claude/settings.json";
3090
3094
  const existed = existsSync4(settingsPath);
3091
3095
  const existing = readJsonIfExists(settingsPath);
3092
- const mcpServers = {
3093
- ...existing.mcpServers ?? {},
3096
+ const baseExistingMcp = existing.mcpServers ?? {};
3097
+ const mcpServers = options.withMcp ? {
3098
+ ...baseExistingMcp,
3094
3099
  composto: {
3095
3100
  command: "composto-mcp",
3096
3101
  env: { COMPOSTO_BLASTRADIUS: "1" }
3097
3102
  }
3098
- };
3103
+ } : baseExistingMcp;
3099
3104
  const compostoHookEntry = {
3100
3105
  matcher: "Edit|Write|MultiEdit",
3101
3106
  hooks: [
@@ -3110,9 +3115,11 @@ function initClaudeCode(projectPath, result) {
3110
3115
  );
3111
3116
  const merged = {
3112
3117
  ...existing,
3113
- mcpServers,
3114
3118
  hooks: { ...existingHooks, PreToolUse: preToolUse }
3115
3119
  };
3120
+ if (Object.keys(mcpServers).length > 0) {
3121
+ merged.mcpServers = mcpServers;
3122
+ }
3116
3123
  ensureDir(settingsPath);
3117
3124
  writeFileSync2(settingsPath, JSON.stringify(merged, null, 2) + "\n");
3118
3125
  if (existed) result.merged.push(relPath);
@@ -3124,13 +3131,14 @@ function initGeminiCli(_projectPath, result, options) {
3124
3131
  try {
3125
3132
  const existed = existsSync4(settingsPath);
3126
3133
  const existing = readJsonIfExists(settingsPath);
3127
- const mcpServers = {
3128
- ...existing.mcpServers ?? {},
3134
+ const baseExistingMcp = existing.mcpServers ?? {};
3135
+ const mcpServers = options.withMcp ? {
3136
+ ...baseExistingMcp,
3129
3137
  composto: {
3130
3138
  command: "composto-mcp",
3131
3139
  env: { COMPOSTO_BLASTRADIUS: "1" }
3132
3140
  }
3133
- };
3141
+ } : baseExistingMcp;
3134
3142
  const compostoHookEntry = {
3135
3143
  matcher: "edit_file|write_file|replace",
3136
3144
  hooks: [
@@ -3145,9 +3153,11 @@ function initGeminiCli(_projectPath, result, options) {
3145
3153
  );
3146
3154
  const merged = {
3147
3155
  ...existing,
3148
- mcpServers,
3149
3156
  hooks: { ...existingHooks, BeforeTool: beforeTool }
3150
3157
  };
3158
+ if (Object.keys(mcpServers).length > 0) {
3159
+ merged.mcpServers = mcpServers;
3160
+ }
3151
3161
  ensureDir(settingsPath);
3152
3162
  writeFileSync2(settingsPath, JSON.stringify(merged, null, 2) + "\n");
3153
3163
  if (existed) result.merged.push(relPath);
@@ -3160,9 +3170,9 @@ function initGeminiCli(_projectPath, result, options) {
3160
3170
  function runInit(projectPath, options) {
3161
3171
  const client = options.client ?? "cursor";
3162
3172
  const result = { client, written: [], skipped: [], merged: [] };
3163
- if (client === "claude-code") initClaudeCode(projectPath, result);
3173
+ if (client === "claude-code") initClaudeCode(projectPath, result, options);
3164
3174
  else if (client === "gemini-cli") initGeminiCli(projectPath, result, options);
3165
- else initCursor(projectPath, result);
3175
+ else initCursor(projectPath, result, options);
3166
3176
  return result;
3167
3177
  }
3168
3178
 
@@ -3675,7 +3685,13 @@ switch (command) {
3675
3685
  console.error(`Unknown --client=${clientArg}. Valid: ${valid.join(", ")}`);
3676
3686
  process.exit(1);
3677
3687
  }
3678
- const result = runInit(resolve2("."), { client: clientArg });
3688
+ const withRules = args.includes("--with-rules");
3689
+ const withMcp = args.includes("--with-mcp");
3690
+ const result = runInit(resolve2("."), {
3691
+ client: clientArg,
3692
+ withRules,
3693
+ withMcp
3694
+ });
3679
3695
  console.log(`composto init \u2014 configured for ${result.client}
3680
3696
  `);
3681
3697
  for (const f of result.written) console.log(` wrote ${f}`);
@@ -3753,8 +3769,10 @@ switch (command) {
3753
3769
  console.log(" impact <file> Show historical blast radius for a file");
3754
3770
  console.log(" index [--since=YYYY-MM-DD] Build or refresh the memory index (--since bounds work for huge repos)");
3755
3771
  console.log(" index --status Show memory index diagnostics");
3756
- console.log(" init [--client=<name>] Configure Composto MCP + hooks for an AI client");
3757
- console.log(" (clients: cursor, claude-code, gemini-cli)");
3772
+ console.log(" init [--client=<name>] [--with-mcp] [--with-rules]");
3773
+ console.log(" Lean Hook init (clients: cursor, claude-code, gemini-cli)");
3774
+ console.log(" --with-mcp register the composto MCP server (5 tools)");
3775
+ console.log(" --with-rules write .cursor/rules/composto.mdc (cursor only)");
3758
3776
  console.log(" hook <platform> <event> Run BlastRadius hook (reads tool JSON from stdin)");
3759
3777
  console.log(" stats [--json] [--disable] Show hook telemetry (last 7d); --disable opts out");
3760
3778
  console.log(" version Show version");
@@ -2387,7 +2387,7 @@ var server = new McpServer({
2387
2387
  });
2388
2388
  server.tool(
2389
2389
  "composto_ir",
2390
- "Generate compressed IR (Intermediate Representation) for a source file. Uses AST parsing to keep function signatures, control flow, and dependencies while dropping 89% of noise tokens. Use this instead of reading raw files when you need to understand what a file does.",
2390
+ "Compressed AST-based IR for a file. ~89% fewer tokens than raw read.",
2391
2391
  {
2392
2392
  file: z.string().describe("Path to the source file"),
2393
2393
  layer: z.enum(["L0", "L1", "L2", "L3"]).default("L1").describe("L0=structure only, L1=full IR (default), L2=delta context, L3=raw source")
@@ -2423,7 +2423,7 @@ ${result}` }]
2423
2423
  );
2424
2424
  server.tool(
2425
2425
  "composto_benchmark",
2426
- "Benchmark how much Composto saves across a directory. Shows per-file token savings comparing raw code vs compressed IR.",
2426
+ "Per-file token-savings benchmark across a directory.",
2427
2427
  {
2428
2428
  path: z.string().default(".").describe("Directory to benchmark")
2429
2429
  },
@@ -2455,7 +2455,7 @@ server.tool(
2455
2455
  );
2456
2456
  server.tool(
2457
2457
  "composto_context",
2458
- "Pack maximum code context into a token budget. When target symbol is provided, its file is included as raw code (L3) while surrounding files get compressed IR. Perfect for 'fix this bug in X' or 'why does X return wrong value' \u2014 LLM sees exact code of target plus compressed context. Without target, hotspot files get L1, rest get L0.",
2458
+ "Pack code into a token budget; target raw, neighbors as IR.",
2459
2459
  {
2460
2460
  path: z.string().default(".").describe("Directory to pack"),
2461
2461
  budget: z.number().default(4e3).describe("Maximum tokens to use"),
@@ -2518,7 +2518,7 @@ Files: ${parts.join(", ")}`);
2518
2518
  );
2519
2519
  server.tool(
2520
2520
  "composto_scan",
2521
- "Scan codebase for security issues (hardcoded secrets, API keys) and debug artifacts (console.log). Zero token cost \u2014 pure local analysis.",
2521
+ "Scan for hardcoded secrets and debug artifacts. Local-only.",
2522
2522
  {
2523
2523
  path: z.string().default(".").describe("Directory to scan")
2524
2524
  },
@@ -2551,7 +2551,7 @@ server.tool(
2551
2551
  );
2552
2552
  server.tool(
2553
2553
  "composto_blastradius",
2554
- 'Predict the historical blast radius of a code change before applying it. Returns a risk verdict (low/medium/high/unknown), confidence, and the git-derived signals behind it (revert history, hotspots, fix ratio, coverage decline, ownership churn). Call BEFORE proposing significant edits to files with non-trivial history. Honest about uncertainty \u2014 returns "unknown" when confidence is low instead of guessing. Degraded modes (empty repo, shallow clone, indexing) are explicit in the `status` field.',
2554
+ "Risk verdict (low/medium/high/unknown) for editing a file, from git history.",
2555
2555
  {
2556
2556
  file: z.string().describe("Repo-relative path of the file the agent intends to modify."),
2557
2557
  intent: z.enum(["refactor", "bugfix", "feature", "test", "docs", "unknown"]).default("unknown").optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "composto-ai",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Proactive AI team companion — less tokens, more insight",
5
5
  "type": "module",
6
6
  "bin": {