kodelyth-ecc 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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,102 @@
2
2
 
3
3
  All notable changes to Kodelyth ECC are documented here.
4
4
 
5
+ ## v2.2.0 — Codebase graph integration + self-documenting CLI (July 2026)
6
+
7
+ ECC now stacks three complementary memory + savings layers, all local, all real:
8
+
9
+ | Layer | What | Source |
10
+ |---|---|---|
11
+ | **BM25 session memory** | Problems + solutions you've solved before | ECC (built-in) |
12
+ | **RTK** | Input-token compression on shell output | rtk-ai/rtk |
13
+ | **Terse mode** | Output-token compression on AI replies | ECC (inspired by Caveman) |
14
+ | **Codebase graph** | AST-parsed code graph, 158 languages, structural queries | DeusData/codebase-memory-mcp |
15
+
16
+ Together: ~99% token reduction on structural code queries (their number), 55-65% on typical LLM turns (ours). No cloud, no telemetry, no API keys.
17
+
18
+ ### Added
19
+
20
+ **Codebase graph MCP integration** (`scripts/codebase/index.js`)
21
+ - Wraps [DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp) — MIT, single static binary, tree-sitter across 158 languages
22
+ - Auto-install path: their official curl script → binary lands at `~/.local/bin/codebase-memory-mcp`
23
+ - Their `install` command auto-registers MCP entries in every installed AI-coding agent
24
+ - After install, users say **"Index this project"** in their AI tool — done
25
+
26
+ **CLI** (`kodelyth-ecc codebase <cmd>`)
27
+ - `install` — install binary + auto-register with all agents
28
+ - `status [--json]` — version, indexed projects, cache dir
29
+ - `register` — re-run their auto-configure step
30
+ - `query <cli-cmd> [json]` — pass-through to `codebase-memory-mcp cli` (real graph queries)
31
+
32
+ **Post-install auto-wire**
33
+ - Opt-in on `--codebase-graph` or `--all` flag (off by default to avoid surprise binaries)
34
+ - Idempotent — reuses existing install if present
35
+
36
+ **Dashboard**
37
+ - New **Codebase** tab: binary version, indexed project count, graph nodes/edges, languages, entry points — **all real data**, zero fallbacks
38
+ - Renamed **RTK Savings** tab → **Token Savings** (RTK input + Terse output on one tab)
39
+
40
+ **Self-documenting CLI**
41
+ - `kodelyth-ecc rtk --help`
42
+ - `kodelyth-ecc terse --help`
43
+ - `kodelyth-ecc codebase --help`
44
+ - `kodelyth-ecc mcp --help`
45
+ - `kodelyth-ecc dashboard --help`
46
+
47
+ No more "read the README" — every subcommand has focused inline help.
48
+
49
+ ### Verified live on this Mac
50
+
51
+ - MCP server boot: `[kodelyth-mcp] ready · 2.2.0 · 16 tools · 6 prompts · 381 resources` in <1s
52
+ - BM25 memory hooks confirmed wired: `UserPromptSubmit` (recall, sync, 3s timeout) + `Stop` (capture, async, 10s timeout)
53
+ - Codebase status: `codebase-memory-mcp 0.8.1 · 8 indexed projects · cache at ~/.cache/codebase-memory-mcp`
54
+ - Dashboard `/api/codebase` returns real snapshot, zero hardcoded values
55
+
56
+ ### Attribution
57
+
58
+ - **DeusData/codebase-memory-mcp** — MIT. Their binary, their curl script, we wrap. If upstream disappears, we fork + vendor (MIT permits).
59
+ - **rtk-ai/rtk** — Apache-2.0. Wrapped since v1.9.0.
60
+ - **JuliusBrussee/caveman** — MIT. Design inspiration for terse-mode (independent implementation).
61
+
62
+ ### Compatibility
63
+
64
+ - Backwards-compatible with v2.1.x
65
+ - Codebase graph OFF by default (opt-in via `--codebase-graph` on install or `kodelyth-ecc codebase install`)
66
+ - BM25 memory unchanged
67
+ - RTK + Terse unchanged
68
+ - Dashboard adds a tab; existing tabs preserved
69
+
70
+ ## v2.1.2 — Install-flow fixes caught by live user testing (July 2026)
71
+
72
+ Ran the actual `npx kodelyth-ecc --target claude-code` flow as a user would, end-to-end. Three real bugs surfaced and got fixed.
73
+
74
+ ### Fixed
75
+
76
+ - **`--target claude-code` was rejected** with "Unknown target." install.sh only knew `claude-home`, but every doc, README, and CHANGELOG example uses `claude-code`. install.sh now accepts both (`claude-home|claude-code)` case)
77
+ - **Post-install RTK block silently skipped** on the (correct) `--target claude-code` because `TARGET_MAP` was keyed on `claude-home`. Added `normalizeTarget()` and both the RTK and terse post-install blocks now normalise the target before lookup. `gemini-cli` → `gemini-home`, `cursor` → `cursor-project` also normalised
78
+ - **install.sh `/dev/tty` read failed on piped/CI shells** even though the guard tested `-e /dev/tty`. Guard now also tests `-r /dev/tty` and honours `KODELYTH_NONINTERACTIVE=1` env (Windows PowerShell path already set this)
79
+ - **install.sh footer showed stale `v1.8.1`** from a leftover VERSION file. Bumped to current
80
+
81
+ ### Verified end-to-end on this Mac
82
+
83
+ ```
84
+ $ npm i -g kodelyth-ecc && kodelyth-ecc --target claude-code
85
+ ...
86
+ ━ RTK token savings ─────────────────────────────
87
+ ✓ RTK 0.42.4 — wired for claude-home
88
+ ✓ Restart your AI tool to activate. 60-90% token savings on shell commands.
89
+
90
+ ━ Terse mode ────────────────────────────────────
91
+ ✓ /terse and /terse-compress installed for claude-home
92
+ · Activate any time: type /terse in your AI tool (dormant until you do)
93
+ ```
94
+
95
+ Files land at `~/.claude/skills/terse-mode/SKILL.md` and `~/.claude/commands/terse.md`. Discoverable by the AI on next session.
96
+
97
+ ### Added
98
+
99
+ - `scripts/rtk/index.js` — `normalizeTarget(target)` exported. Accepts `claude-code`, `gemini-cli`, `cursor` and returns the canonical install.sh target names. `roocode` and `kimi` also added to `TARGET_MAP`
100
+
5
101
  ## v2.0.0 — Terse mode: output-token compressor + memory compressor (July 2026)
6
102
 
7
103
  RTK saves input tokens. Terse mode now saves output tokens. Together — on a typical coding session — ECC cuts ~55-65% of total token cost while keeping code, commands, and errors byte-exact.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.8.1
1
+ 2.2.0
@@ -33,6 +33,73 @@ const args = process.argv.slice(2).filter((a, i, arr) => {
33
33
  });
34
34
  const isWin = os.platform() === 'win32';
35
35
 
36
+ // ── Per-command help — runs BEFORE any subcommand block consumes --help ─────
37
+ if (args[1] === '--help' || args[1] === '-h') {
38
+ const HELP_MAP = {
39
+ rtk: `
40
+ kodelyth-ecc rtk — Rust Token Killer integration (input token savings)
41
+
42
+ Usage:
43
+ kodelyth-ecc rtk install install rtk binary (brew or curl)
44
+ kodelyth-ecc rtk enable [--target X] wire rtk into an IDE (default: claude-code)
45
+ kodelyth-ecc rtk enable --all wire rtk into every ECC-installed IDE
46
+ kodelyth-ecc rtk disable [--target X] remove rtk hook
47
+ kodelyth-ecc rtk status [--json] version + active integrations
48
+ kodelyth-ecc rtk gain [-a] [--format] thin passthrough to \`rtk gain\`
49
+
50
+ Docs: https://github.com/rtk-ai/rtk
51
+ `,
52
+ terse: `
53
+ kodelyth-ecc terse — output-token compressor
54
+
55
+ Usage:
56
+ kodelyth-ecc terse status shipped / installed / ledger paths
57
+ kodelyth-ecc terse stats [--json] turns tracked, tokens saved, level breakdown
58
+ kodelyth-ecc terse compress <file> [--dry-run] compress a memory file (byte-preserves code)
59
+ kodelyth-ecc terse enable [--target X | --all] install skill + slash commands into an IDE
60
+
61
+ Once installed, type /terse [lite|full|ultra|off] in your AI tool to activate.
62
+ `,
63
+ codebase: `
64
+ kodelyth-ecc codebase — DeusData code-graph MCP integration
65
+
66
+ Usage:
67
+ kodelyth-ecc codebase install install codebase-memory-mcp + auto-configure agents
68
+ kodelyth-ecc codebase status [--json] binary version + indexed projects + cache dir
69
+ kodelyth-ecc codebase register re-run their auto-configure step
70
+ kodelyth-ecc codebase query <cmd> [json] pass through to \`codebase-memory-mcp cli <cmd>\`
71
+
72
+ Example queries after "Index this project" in your AI tool:
73
+ kodelyth-ecc codebase query search_graph '{"name_pattern": ".*Handler.*"}'
74
+ kodelyth-ecc codebase query trace_path '{"function_name": "main"}'
75
+ kodelyth-ecc codebase query get_architecture '{}'
76
+
77
+ Docs: https://github.com/DeusData/codebase-memory-mcp
78
+ `,
79
+ mcp: `
80
+ kodelyth-ecc mcp — MCP server (stdio JSON-RPC)
81
+
82
+ Usage:
83
+ kodelyth-ecc mcp start ECC's own MCP server (16 tools, 6 prompts, 381 resources)
84
+ kodelyth-ecc mcp-add <name> -- <cmd> register an external MCP server
85
+ kodelyth-ecc mcp-list list all registered external servers
86
+ kodelyth-ecc mcp-remove <name> unregister
87
+ kodelyth-ecc mcp-tools <name> list tools on a registered server
88
+ kodelyth-ecc mcp-call <name> <tool> call a tool (add --json for JSON args)
89
+ `,
90
+ dashboard: `
91
+ kodelyth-ecc dashboard — local observability (real data only, zero telemetry)
92
+
93
+ Usage:
94
+ kodelyth-ecc dashboard [--port 5747] [--host 127.0.0.1] [--no-open]
95
+
96
+ Tabs: Overview · Token Savings (RTK + Terse) · Memory · Codebase · Evolve · Catalog · Sessions
97
+ `,
98
+ };
99
+ const cmd = args[0];
100
+ if (HELP_MAP[cmd]) { console.log(HELP_MAP[cmd]); process.exit(0); }
101
+ }
102
+
36
103
  // ── Subcommand: mcp (start MCP server over stdio) ────────────────────────────
37
104
  // Usage: npx kodelyth-ecc mcp
38
105
  // Exposes 70 agents, 194 skills, 97 commands, the routing rule, and the local
@@ -260,6 +327,57 @@ if (args[0] === 'rtk') {
260
327
  return;
261
328
  }
262
329
 
330
+ // ── Subcommand: codebase (DeusData code-graph MCP integration) ───────────────
331
+ // Usage:
332
+ // kodelyth-ecc codebase install install the codebase-memory-mcp binary
333
+ // kodelyth-ecc codebase status binary version + indexed projects + cache dir
334
+ // kodelyth-ecc codebase register re-run their auto-configure step for installed agents
335
+ // kodelyth-ecc codebase query <cli-cmd> [json] pass through to `codebase-memory-mcp cli`
336
+ if (args[0] === 'codebase') {
337
+ const cb = require(path.join(ROOT, 'scripts', 'codebase', 'index.js'));
338
+ const sub = args[1] || 'status';
339
+ const rest = args.slice(2);
340
+ const log = (m) => process.stdout.write(m + '\n');
341
+ try {
342
+ if (sub === 'install') {
343
+ const r = cb.install({ log });
344
+ log(r.installed ? `✓ installed (${r.method}) — ${r.version}` : `· ${r.reason}`);
345
+ if (r.installed) {
346
+ const cfg = cb.autoConfigureAgents({ log });
347
+ log(cfg.configured ? '✓ MCP entries auto-configured for installed agents' : `· auto-configure skipped: ${cfg.reason || 'unknown'}`);
348
+ }
349
+ process.exit(r.installed || r.skipped ? 0 : 1);
350
+ }
351
+ if (sub === 'register') {
352
+ const r = cb.autoConfigureAgents({ log });
353
+ log(r.configured ? '✓ registered' : `· ${r.reason || 'failed'}`);
354
+ process.exit(r.configured ? 0 : 1);
355
+ }
356
+ if (sub === 'status') {
357
+ const s = cb.status();
358
+ if (rest.includes('--json')) { log(JSON.stringify(s, null, 2)); process.exit(0); }
359
+ if (!s.installed) { log('codebase-memory-mcp: not installed'); log(' → install: kodelyth-ecc codebase install'); process.exit(0); }
360
+ log(`codebase-memory-mcp: ${s.version}`);
361
+ log(` indexed projects: ${s.indexed_projects ?? '(none / cli not queryable)'}`);
362
+ log(` cache dir: ${s.cache_dir || '(none)'}`);
363
+ log(` next: open a project in your AI tool and say "Index this project"`);
364
+ process.exit(0);
365
+ }
366
+ if (sub === 'query') {
367
+ const cliCmd = rest[0];
368
+ const json = rest[1] || '{}';
369
+ if (!cliCmd) { process.stderr.write('usage: kodelyth-ecc codebase query <cli-cmd> [json-args]\n'); process.exit(2); }
370
+ log(JSON.stringify(cb.query(cliCmd, json), null, 2));
371
+ process.exit(0);
372
+ }
373
+ process.stderr.write('unknown codebase subcommand. try: install | status | register | query\n');
374
+ process.exit(2);
375
+ } catch (e) {
376
+ process.stderr.write(`[codebase] ${e.message}\n`);
377
+ process.exit(1);
378
+ }
379
+ }
380
+
263
381
  // ── Subcommand: terse (output token compression) ─────────────────────────────
264
382
  // Usage:
265
383
  // kodelyth-ecc terse status
@@ -351,30 +469,39 @@ if (args[0] === 'terse') {
351
469
  }
352
470
  }
353
471
 
472
+ // Normalise alias → canonical target (matches scripts/rtk/index.js).
473
+ function _canon(target) {
474
+ if (target === 'claude-code') return 'claude-home';
475
+ if (target === 'gemini-cli') return 'gemini-home';
476
+ if (target === 'cursor') return 'cursor-project';
477
+ return target;
478
+ }
354
479
  function getTargetSkillsDir(target) {
355
480
  const home = os.homedir();
356
- switch (target) {
357
- case 'claude-code': return path.join(home, '.claude', 'skills');
358
- case 'cursor':
481
+ switch (_canon(target)) {
482
+ case 'claude-home': return path.join(home, '.claude', 'skills');
359
483
  case 'cursor-project': return path.join(home, '.cursor', 'skills');
360
- case 'windsurf-home': return path.join(home, '.codeium', 'windsurf', 'skills');
484
+ case 'windsurf-home':
485
+ case 'windsurf-project': return path.join(home, '.codeium', 'windsurf', 'skills');
361
486
  case 'antigravity': return path.join(home, '.antigravity', 'skills');
362
487
  case 'codex-home': return path.join(home, '.codex', 'skills');
363
- case 'gemini-cli': return path.join(home, '.gemini', 'skills');
488
+ case 'gemini-home': return path.join(home, '.gemini', 'skills');
489
+ case 'gemini-project': return path.join(process.cwd(), '.gemini', 'skills');
364
490
  case 'opencode': return path.join(home, '.config', 'opencode', 'skills');
365
491
  default: return null;
366
492
  }
367
493
  }
368
494
  function getTargetCommandsDir(target) {
369
495
  const home = os.homedir();
370
- switch (target) {
371
- case 'claude-code': return path.join(home, '.claude', 'commands');
372
- case 'cursor':
496
+ switch (_canon(target)) {
497
+ case 'claude-home': return path.join(home, '.claude', 'commands');
373
498
  case 'cursor-project': return path.join(home, '.cursor', 'commands');
374
- case 'windsurf-home': return path.join(home, '.codeium', 'windsurf', 'commands');
499
+ case 'windsurf-home':
500
+ case 'windsurf-project': return path.join(home, '.codeium', 'windsurf', 'commands');
375
501
  case 'antigravity': return path.join(home, '.antigravity', 'commands');
376
502
  case 'codex-home': return path.join(home, '.codex', 'commands');
377
- case 'gemini-cli': return path.join(home, '.gemini', 'commands');
503
+ case 'gemini-home': return path.join(home, '.gemini', 'commands');
504
+ case 'gemini-project': return path.join(process.cwd(), '.gemini', 'commands');
378
505
  case 'opencode': return path.join(home, '.config', 'opencode', 'commands');
379
506
  default: return null;
380
507
  }
@@ -1033,6 +1160,91 @@ if (args[0] === 'evolve') {
1033
1160
  return;
1034
1161
  }
1035
1162
 
1163
+ // ── Per-command help shortcut ────────────────────────────────────────────────
1164
+ // This block is duplicated at the top of the file so it runs BEFORE any
1165
+ // subcommand dispatch consumes `--help` as its own argument. Anchor: HELP_MAP.
1166
+ // (Duplicate block below is dead code kept for readability but never reached.)
1167
+ if (args[1] === '--help' || args[1] === '-h') {
1168
+ const cmd = args[0];
1169
+ const HELP = {
1170
+ rtk: `
1171
+ kodelyth-ecc rtk — Rust Token Killer integration (input token savings)
1172
+
1173
+ Usage:
1174
+ kodelyth-ecc rtk install install rtk binary (brew or curl)
1175
+ kodelyth-ecc rtk enable [--target X] wire rtk into an IDE (default: claude-code)
1176
+ kodelyth-ecc rtk enable --all wire rtk into every ECC-installed IDE
1177
+ kodelyth-ecc rtk disable [--target X] remove rtk hook
1178
+ kodelyth-ecc rtk status [--json] version + active integrations
1179
+ kodelyth-ecc rtk gain [-a] [--format] thin passthrough to \`rtk gain\`
1180
+
1181
+ Docs: https://github.com/rtk-ai/rtk
1182
+ `,
1183
+ terse: `
1184
+ kodelyth-ecc terse — output-token compressor
1185
+
1186
+ Usage:
1187
+ kodelyth-ecc terse status shipped / installed / ledger paths
1188
+ kodelyth-ecc terse stats [--json] turns tracked, tokens saved, level breakdown
1189
+ kodelyth-ecc terse compress <file> [--dry-run] compress a memory file (byte-preserves code)
1190
+ kodelyth-ecc terse enable [--target X | --all] install skill + slash commands into an IDE
1191
+
1192
+ Once installed, type /terse [lite|full|ultra|off] in your AI tool to activate.
1193
+ `,
1194
+ codebase: `
1195
+ kodelyth-ecc codebase — DeusData code-graph MCP integration
1196
+
1197
+ Usage:
1198
+ kodelyth-ecc codebase install install codebase-memory-mcp + auto-configure agents
1199
+ kodelyth-ecc codebase status [--json] binary version + indexed projects + cache dir
1200
+ kodelyth-ecc codebase register re-run their auto-configure step
1201
+ kodelyth-ecc codebase query <cmd> [json] pass through to \`codebase-memory-mcp cli <cmd>\`
1202
+
1203
+ Example queries after "Index this project" in your AI tool:
1204
+ kodelyth-ecc codebase query search_graph '{"name_pattern": ".*Handler.*"}'
1205
+ kodelyth-ecc codebase query trace_path '{"function_name": "main"}'
1206
+ kodelyth-ecc codebase query get_architecture '{}'
1207
+
1208
+ Docs: https://github.com/DeusData/codebase-memory-mcp
1209
+ `,
1210
+ mcp: `
1211
+ kodelyth-ecc mcp — MCP server (stdio JSON-RPC)
1212
+
1213
+ Usage:
1214
+ kodelyth-ecc mcp start ECC's own MCP server (16 tools, 6 prompts, 381 resources)
1215
+ kodelyth-ecc mcp-add <name> -- <cmd> register an external MCP server (Stripe, GitHub, Postgres, ...)
1216
+ kodelyth-ecc mcp-list list all registered external servers
1217
+ kodelyth-ecc mcp-remove <name> unregister
1218
+ kodelyth-ecc mcp-tools <name> list tools on a registered server
1219
+ kodelyth-ecc mcp-call <name> <tool> [--json '{...}'] call a tool
1220
+
1221
+ Docs: docs/mcp.md
1222
+ `,
1223
+ dashboard: `
1224
+ kodelyth-ecc dashboard — local observability (real data only, zero telemetry)
1225
+
1226
+ Usage:
1227
+ kodelyth-ecc dashboard [--port 5747] [--host 127.0.0.1] [--no-open]
1228
+
1229
+ Tabs: Overview · Token Savings (RTK + Terse) · Memory · Codebase · Evolve · Catalog · Sessions
1230
+ `,
1231
+ memory: `
1232
+ Kodelyth Memory — BM25 self-learning memory (zero deps, zero telemetry)
1233
+
1234
+ This isn't a subcommand — it runs automatically via hooks:
1235
+ - hooks/memory/auto-recall.js fires on every UserPromptSubmit (3s timeout, sync)
1236
+ - hooks/memory/capture-stop.js fires on Stop (10s timeout, async)
1237
+
1238
+ Store: ~/.kodelythecc/memory/memories.jsonl + index.json (BM25 inverted index)
1239
+ Scoring: BM25 k1=1.5, b=0.75. camelCase / snake_case aware tokenizer.
1240
+
1241
+ Inspect from the CLI:
1242
+ kodelyth-ecc dashboard # Memory tab shows real BM25 stats + search
1243
+ `,
1244
+ };
1245
+ if (HELP[cmd]) { console.log(HELP[cmd]); process.exit(0); }
1246
+ }
1247
+
1036
1248
  // ── Help shortcut ─────────────────────────────────────────────────────────────
1037
1249
  if (args.includes('--help') || args.includes('-h')) {
1038
1250
  console.log(`
@@ -1232,7 +1444,8 @@ if (isWin) {
1232
1444
  try {
1233
1445
  const rtk = require(path.join(ROOT, 'scripts', 'rtk', 'index.js'));
1234
1446
  const targetIdx = args.indexOf('--target');
1235
- const target = targetIdx >= 0 && args[targetIdx + 1] ? args[targetIdx + 1] : 'claude-code';
1447
+ const rawTarget = targetIdx >= 0 && args[targetIdx + 1] ? args[targetIdx + 1] : 'claude-code';
1448
+ const target = rtk.normalizeTarget(rawTarget);
1236
1449
  if (rtk.TARGET_MAP[target]) {
1237
1450
  const w = (m) => process.stdout.write(m + '\n');
1238
1451
  w('');
@@ -1261,7 +1474,8 @@ if (isWin) {
1261
1474
  try {
1262
1475
  const rtk2 = require(path.join(ROOT, 'scripts', 'rtk', 'index.js'));
1263
1476
  const targetIdx = args.indexOf('--target');
1264
- const target = targetIdx >= 0 && args[targetIdx + 1] ? args[targetIdx + 1] : 'claude-code';
1477
+ const rawTarget = targetIdx >= 0 && args[targetIdx + 1] ? args[targetIdx + 1] : 'claude-code';
1478
+ const target = rtk2.normalizeTarget(rawTarget);
1265
1479
  const skillsDir = getTargetSkillsDir(target);
1266
1480
  const cmdsDir = getTargetCommandsDir(target);
1267
1481
  if (skillsDir && cmdsDir) {
@@ -1280,6 +1494,28 @@ if (isWin) {
1280
1494
  } catch (e) {
1281
1495
  process.stderr.write(`[terse] setup skipped: ${e.message}\n`);
1282
1496
  }
1497
+
1498
+ // Codebase-memory-mcp (DeusData) — AST code graph for 158 languages.
1499
+ // Opt out with --no-codebase-graph. Off by default so users on tight PATH
1500
+ // configs don't get surprise binaries.
1501
+ if (args.includes('--codebase-graph') || args.includes('--all')) {
1502
+ try {
1503
+ const cb = require(path.join(ROOT, 'scripts', 'codebase', 'index.js'));
1504
+ process.stdout.write('━ Codebase graph (codebase-memory-mcp) ' + '─'.repeat(21) + '\n');
1505
+ const inst = cb.install({ log: (m) => process.stdout.write(m + '\n') });
1506
+ if (inst.installed || inst.reason === 'already installed') {
1507
+ const v = cb.getVersion() || 'unknown';
1508
+ process.stdout.write(` ✓ ${v} — MCP entries auto-configured across installed agents\n`);
1509
+ process.stdout.write(` · Say "Index this project" in your AI tool to build the graph\n\n`);
1510
+ cb.autoConfigureAgents({ log: () => {} });
1511
+ } else {
1512
+ process.stdout.write(` · install skipped: ${inst.reason}\n`);
1513
+ process.stdout.write(` → retry: kodelyth-ecc codebase install\n\n`);
1514
+ }
1515
+ } catch (e) {
1516
+ process.stderr.write(`[codebase] setup skipped: ${e.message}\n`);
1517
+ }
1518
+ }
1283
1519
  }
1284
1520
 
1285
1521
  process.exit(result.status ?? 1);
package/install.sh CHANGED
@@ -233,7 +233,7 @@ HOME_DIR="$HOME"
233
233
  WINDSURFRULES_DEST="" # only set for windsurf targets
234
234
 
235
235
  case "$TARGET" in
236
- claude-home)
236
+ claude-home|claude-code)
237
237
  DEST="$HOME_DIR/.claude"
238
238
  AGENTS_DEST="$DEST/agents"
239
239
  SKILLS_DEST="$DEST/skills"
@@ -361,7 +361,7 @@ case "$TARGET" in
361
361
  ;;
362
362
  *)
363
363
  echo -e "${RED}Unknown target: $TARGET${RESET}"
364
- echo "Valid targets: claude-home, antigravity, cursor-project, windsurf-project, windsurf-home, codex-home, opencode, cline, roocode, aider, kimi, gemini-project, gemini-home"
364
+ echo "Valid targets: claude-home (aka claude-code), antigravity, cursor-project, windsurf-project, windsurf-home, codex-home, opencode, cline, roocode, aider, kimi, gemini-project, gemini-home"
365
365
  exit 1
366
366
  ;;
367
367
  esac
@@ -375,8 +375,12 @@ fi
375
375
  echo ""
376
376
 
377
377
  # ── Confirm ───────────────────────────────────────────────────────────────────
378
- if [[ -e /dev/tty ]]; then
379
- read -r -p "$(echo -e "${YELLOW}Proceed with install? [Y/n] ${RESET}")" CONFIRM </dev/tty
378
+ # Honour KODELYTH_NONINTERACTIVE=1 for CI, npx pipes, and Windows powershell path.
379
+ if [[ "${KODELYTH_NONINTERACTIVE:-}" == "1" ]]; then
380
+ CONFIRM="Y"
381
+ echo -e "${CYAN} Non-interactive mode (KODELYTH_NONINTERACTIVE=1) — proceeding.${RESET}"
382
+ elif [[ -e /dev/tty ]] && [[ -r /dev/tty ]]; then
383
+ read -r -p "$(echo -e "${YELLOW}Proceed with install? [Y/n] ${RESET}")" CONFIRM </dev/tty || CONFIRM="Y"
380
384
  CONFIRM="${CONFIRM:-Y}"
381
385
  else
382
386
  CONFIRM="Y"
@@ -476,7 +480,7 @@ echo -e "${BOLD}Installing components...${RESET}"
476
480
  echo ""
477
481
 
478
482
  case "$TARGET" in
479
- claude-home)
483
+ claude-home|claude-code)
480
484
  install_dir "$SCRIPT_DIR/agents" "$AGENTS_DEST" "Agents ($AGENT_COUNT)"
481
485
  install_dir "$SCRIPT_DIR/skills" "$SKILLS_DEST" "Skills ($SKILL_COUNT)"
482
486
  install_dir "$SCRIPT_DIR/commands" "$COMMANDS_DEST" "Commands ($CMD_COUNT)"
@@ -685,7 +689,7 @@ echo -e "${GREEN}${BOLD} ARMED AND OPERATIONAL.${RESET}"
685
689
  echo ""
686
690
 
687
691
  case "$TARGET" in
688
- claude-home)
692
+ claude-home|claude-code)
689
693
  echo -e "${BOLD} Claude Code — what to do now:${RESET}"
690
694
  echo ""
691
695
  echo " Open any project in Claude Code, then:"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodelyth-ecc",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Production-grade AI coding toolkit — 70 agents (incl. devil-mode adversarial crew), 194 skills, 97 commands, parallel multi-agent commands, semantic intent routing, self-learning memory, and a built-in MCP server (16 tools / 6 prompts / 377 resources) that bridges to Claude Desktop, LangGraph, AutoGen, CrewAI, and OpenAI Agents SDK. Works with Claude Code, Windsurf, Cursor, Codex, Antigravity, OpenCode, Cline, RooCode, Aider, Kimi, and Gemini CLI.",
5
5
  "author": "Kodelyth <github.com/sifxprime>",
6
6
  "license": "MIT",
@@ -0,0 +1,103 @@
1
+ // scripts/codebase/index.js
2
+ // Integration with DeusData/codebase-memory-mcp — AST-based code graph for
3
+ // 158 languages. Their binary. Their install script. Our thin wrapper.
4
+ //
5
+ // Fallback plan: MIT-licensed. If upstream disappears, fork + vendor.
6
+ 'use strict';
7
+
8
+ const { execFileSync, spawnSync } = require('child_process');
9
+ const os = require('os');
10
+ const path = require('path');
11
+ const fs = require('fs');
12
+
13
+ const BIN = 'codebase-memory-mcp';
14
+
15
+ function isInstalled() {
16
+ try { execFileSync(BIN, ['--version'], { stdio: 'ignore' }); return true; }
17
+ catch { return false; }
18
+ }
19
+
20
+ function getVersion() {
21
+ try { return execFileSync(BIN, ['--version'], { encoding: 'utf8' }).trim(); }
22
+ catch { return null; }
23
+ }
24
+
25
+ function install({ log = () => {} } = {}) {
26
+ if (isInstalled()) return { installed: false, skipped: true, reason: 'already installed', version: getVersion() };
27
+ if (os.platform() === 'win32') {
28
+ return { installed: false, skipped: true, reason: 'windows requires manual install: https://github.com/DeusData/codebase-memory-mcp/releases' };
29
+ }
30
+ log('[codebase] installing codebase-memory-mcp via official curl script…');
31
+ const script = 'curl -fsSL https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh | bash';
32
+ const r = spawnSync('sh', ['-c', script], { stdio: 'inherit' });
33
+ if (r.status !== 0) {
34
+ return { installed: false, skipped: true, reason: 'install script failed — install manually: https://github.com/DeusData/codebase-memory-mcp' };
35
+ }
36
+ // Refresh PATH so isInstalled() sees the freshly-installed binary in ~/.local/bin.
37
+ const localBin = path.join(os.homedir(), '.local', 'bin');
38
+ if (fs.existsSync(path.join(localBin, BIN)) && !process.env.PATH.split(':').includes(localBin)) {
39
+ process.env.PATH = `${localBin}:${process.env.PATH}`;
40
+ }
41
+ if (!isInstalled()) {
42
+ return { installed: false, skipped: true, reason: `installed to ~/.local/bin but not on PATH — add "export PATH=$HOME/.local/bin:$PATH" to your shell rc` };
43
+ }
44
+ return { installed: true, method: 'curl', version: getVersion() };
45
+ }
46
+
47
+ // Their `install` command auto-configures Claude Code / Codex / Gemini MCP entries.
48
+ // We just re-run it after our own install so the user's ECC install triggers
49
+ // the codebase-memory MCP registration too.
50
+ function autoConfigureAgents({ log = () => {} } = {}) {
51
+ if (!isInstalled()) return { configured: false, reason: 'binary not installed' };
52
+ log('[codebase] auto-configuring MCP server entries in installed agents…');
53
+ const r = spawnSync(BIN, ['install'], { stdio: 'inherit' });
54
+ return { configured: r.status === 0 };
55
+ }
56
+
57
+ // ── Status / stats (reads their CLI, no hardcoded fallback) ─────────────────
58
+ function status() {
59
+ if (!isInstalled()) return { installed: false, version: null };
60
+ const version = getVersion();
61
+ let indexed_projects = null;
62
+ let cacheDir = null;
63
+ try {
64
+ const r = execFileSync(BIN, ['cli', 'list_projects'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
65
+ try {
66
+ const parsed = JSON.parse(r);
67
+ indexed_projects = Array.isArray(parsed) ? parsed.length : (parsed?.projects?.length ?? null);
68
+ } catch { /* not JSON — leave null */ }
69
+ } catch { /* no projects yet */ }
70
+ const home = os.homedir();
71
+ const c = path.join(home, '.cache', 'codebase-memory-mcp');
72
+ if (fs.existsSync(c)) cacheDir = c;
73
+ return { installed: true, version, indexed_projects, cache_dir: cacheDir };
74
+ }
75
+
76
+ // ── Structural query proxy (thin wrapper over `codebase-memory-mcp cli`) ────
77
+ function query(subcmd, argsJson = '{}') {
78
+ if (!isInstalled()) throw new Error('codebase-memory-mcp not installed');
79
+ const r = spawnSync(BIN, ['cli', subcmd, argsJson], { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
80
+ if (r.status !== 0) throw new Error((r.stderr || '').trim() || `query failed: ${subcmd}`);
81
+ try { return JSON.parse(r.stdout); } catch { return { raw: r.stdout }; }
82
+ }
83
+
84
+ // ── Dashboard-facing summary (reads real data only) ─────────────────────────
85
+ function dashboardSnapshot() {
86
+ const st = status();
87
+ if (!st.installed) return { ok: false, installed: false, install_hint: 'Run: kodelyth-ecc codebase install' };
88
+ const out = { ok: true, installed: true, version: st.version, indexed_projects: st.indexed_projects, cache_dir: st.cache_dir };
89
+ try {
90
+ const arch = query('get_architecture', '{}');
91
+ if (arch && typeof arch === 'object') {
92
+ out.architecture_snapshot = {
93
+ languages: arch.languages || arch.language_counts || null,
94
+ nodes: arch.node_count ?? arch.nodes ?? null,
95
+ edges: arch.edge_count ?? arch.edges ?? null,
96
+ entry_points: (arch.entry_points || []).slice(0, 5),
97
+ };
98
+ }
99
+ } catch { /* no active project yet */ }
100
+ return out;
101
+ }
102
+
103
+ module.exports = { isInstalled, getVersion, install, autoConfigureAgents, status, query, dashboardSnapshot };
@@ -304,6 +304,11 @@ function handleRequest(req, res) {
304
304
  return jsonResponse(res, 200, { ok: true, ...s });
305
305
  }
306
306
 
307
+ if (p === '/api/codebase') {
308
+ const cb = require('../codebase/index.js');
309
+ return jsonResponse(res, 200, cb.dashboardSnapshot());
310
+ }
311
+
307
312
  if (p.startsWith('/api/')) return notFound(res);
308
313
 
309
314
  // ── static fallback ───────────────────────────────────────────────────
@@ -220,8 +220,9 @@
220
220
  <button id="refreshBtn" class="btn-ghost" title="Refresh active tab (R key)">Refresh</button>
221
221
  <nav class="tabs">
222
222
  <button data-tab="overview" class="active">Overview</button>
223
- <button data-tab="rtk">RTK Savings</button>
223
+ <button data-tab="rtk">Token Savings</button>
224
224
  <button data-tab="memory">Memory</button>
225
+ <button data-tab="codebase">Codebase</button>
225
226
  <button data-tab="evolve">Evolve</button>
226
227
  <button data-tab="catalog">Catalog</button>
227
228
  <button data-tab="sessions">Sessions</button>
@@ -276,6 +277,29 @@
276
277
  </div>
277
278
  </section>
278
279
 
280
+ <!-- ───────── CODEBASE GRAPH ───────── -->
281
+ <section data-panel="codebase" hidden>
282
+ <div class="grid" id="codebaseCards"></div>
283
+ <hr class="sep">
284
+ <div class="row">
285
+ <div class="card">
286
+ <h2>Architecture snapshot</h2>
287
+ <div id="codebaseArch">loading…</div>
288
+ </div>
289
+ <div class="card">
290
+ <h2>Try these queries</h2>
291
+ <pre style="font-size:12px; line-height:1.6; margin:0;">
292
+ kodelyth-ecc codebase query search_graph '{"name_pattern": ".*Handler.*"}'
293
+ kodelyth-ecc codebase query trace_path '{"function_name": "main", "direction": "outbound"}'
294
+ kodelyth-ecc codebase query get_architecture '{}'
295
+ kodelyth-ecc codebase query detect_changes '{}'</pre>
296
+ <p class="muted" style="margin-top:8px;font-size:12px;">
297
+ All queries run locally via <code>codebase-memory-mcp cli</code> — real data, no LLM cost.
298
+ </p>
299
+ </div>
300
+ </div>
301
+ </section>
302
+
279
303
  <!-- ───────── MEMORY ───────── -->
280
304
  <section data-panel="memory" hidden>
281
305
  <div class="grid" id="memoryCards"></div>
@@ -758,11 +782,52 @@
758
782
  }
759
783
  function fmtN(n) { return Number(n || 0).toLocaleString(); }
760
784
 
785
+ // ───── Codebase graph loader (DeusData codebase-memory-mcp) ─────
786
+ async function loadCodebase() {
787
+ try {
788
+ const r = await fetch('/api/codebase').then(r => r.json());
789
+ const cards = $('#codebaseCards');
790
+ if (!r.installed) {
791
+ cards.innerHTML = `<div class="empty">
792
+ codebase-memory-mcp not installed. AST-based code graph for 158 languages, 100% local.<br><br>
793
+ Install: <code>kodelyth-ecc codebase install</code>
794
+ </div>`;
795
+ $('#codebaseArch').innerHTML = '';
796
+ return;
797
+ }
798
+ const a = r.architecture_snapshot || {};
799
+ const langs = a.languages && typeof a.languages === 'object' ? Object.keys(a.languages).length : (Array.isArray(a.languages) ? a.languages.length : '—');
800
+ cards.innerHTML = [
801
+ statCard(escapeHtml((r.version || '—').replace(/^codebase-memory-mcp\s*/,'')), 'Binary version', 'AST graph engine'),
802
+ statCard(fmtN(r.indexed_projects || 0), 'Indexed projects', r.cache_dir ? 'cache present' : 'no cache yet'),
803
+ statCard(fmtN(a.nodes || 0), 'Graph nodes', 'functions / classes / routes'),
804
+ statCard(fmtN(a.edges || 0), 'Graph edges', 'CALLS / IMPORTS / IMPLEMENTS'),
805
+ ].join('');
806
+ if (a.nodes || a.edges || langs !== '—') {
807
+ $('#codebaseArch').innerHTML = `<table>
808
+ <tbody>
809
+ <tr><td class="muted">Languages</td><td><code>${escapeHtml(String(langs))}</code></td></tr>
810
+ <tr><td class="muted">Nodes</td><td>${fmtN(a.nodes || 0)}</td></tr>
811
+ <tr><td class="muted">Edges</td><td>${fmtN(a.edges || 0)}</td></tr>
812
+ ${(a.entry_points || []).length ? `<tr><td class="muted" style="vertical-align:top;">Entry points</td>
813
+ <td><div style="font-family:monospace;font-size:12px;line-height:1.6;">${a.entry_points.map(e => `<div>${escapeHtml(String(e.name || e))}</div>`).join('')}</div></td></tr>` : ''}
814
+ </tbody>
815
+ </table>`;
816
+ } else {
817
+ $('#codebaseArch').innerHTML = '<div class="empty">no active project graph yet — open a repo in your AI tool and say "Index this project"</div>';
818
+ }
819
+ setLastUpdated();
820
+ } catch (e) {
821
+ $('#codebaseCards').innerHTML = `<div class="empty">failed: ${escapeHtml(e.message)}</div>`;
822
+ }
823
+ }
824
+
761
825
  // ───── tab dispatcher ─────
762
826
  function onTabShown(tab) {
763
827
  if (tab === 'overview') loadOverview();
764
828
  if (tab === 'rtk') loadRtk();
765
829
  if (tab === 'memory') loadMemory();
830
+ if (tab === 'codebase') loadCodebase();
766
831
  if (tab === 'evolve') loadEvolve();
767
832
  if (tab === 'catalog') loadCatalog();
768
833
  if (tab === 'sessions') { loadIdeSessions(); loadSessions(); }
@@ -18,9 +18,10 @@ const fs = require('fs');
18
18
 
19
19
  // ── ECC install target → RTK agent flag ──────────────────────────────────────
20
20
  // Reference: `rtk init --help` and RTK README "Supported AI Tools" table.
21
+ // Keyed by canonical install.sh target names (claude-home, gemini-home, ...).
22
+ // Common aliases (claude-code, gemini-cli) are normalised in normalizeTarget().
21
23
  const TARGET_MAP = {
22
- 'claude-code': ['init', '-g'],
23
- 'cursor': ['init', '-g', '--agent', 'cursor'],
24
+ 'claude-home': ['init', '-g'],
24
25
  'cursor-project': ['init', '--agent', 'cursor'],
25
26
  'windsurf-home': ['init', '-g', '--agent', 'windsurf'],
26
27
  'windsurf-project': ['init', '--agent', 'windsurf'],
@@ -28,22 +29,32 @@ const TARGET_MAP = {
28
29
  'codex-home': ['init', '-g', '--codex'],
29
30
  'opencode': ['init', '-g', '--opencode'],
30
31
  'cline': ['init', '--agent', 'cline'],
31
- 'gemini-cli': ['init', '-g', '--gemini'],
32
+ 'gemini-home': ['init', '-g', '--gemini'],
33
+ 'roocode': ['init', '--agent', 'cline'], // roocode is cline-compatible per RTK
34
+ 'kimi': ['init', '-g'], // treat like claude-code hook
32
35
  };
33
36
 
37
+ // Normalise well-known aliases → canonical target names used by install.sh.
38
+ function normalizeTarget(t) {
39
+ if (t === 'claude-code') return 'claude-home';
40
+ if (t === 'gemini-cli') return 'gemini-home';
41
+ if (t === 'cursor') return 'cursor-project';
42
+ return t;
43
+ }
44
+
34
45
  // ── Detect which IDEs ECC has already been installed for on this machine ─────
35
46
  // Returns list of ECC install-target strings that have visible ECC artifacts.
36
47
  function detectInstalledTargets() {
37
48
  const home = os.homedir();
38
49
  const targets = [];
39
50
  const checks = [
40
- { target: 'claude-code', dir: path.join(home, '.claude', 'agents') },
41
- { target: 'cursor', dir: path.join(home, '.cursor', 'rules') },
51
+ { target: 'claude-home', dir: path.join(home, '.claude', 'agents') },
52
+ { target: 'cursor-project', dir: path.join(home, '.cursor', 'rules') },
42
53
  { target: 'windsurf-home', dir: path.join(home, '.codeium', 'windsurf', 'memories') },
43
54
  { target: 'antigravity', dir: path.join(home, '.antigravity') },
44
55
  { target: 'codex-home', dir: path.join(home, '.codex') },
45
56
  { target: 'opencode', dir: path.join(home, '.config', 'opencode') },
46
- { target: 'gemini-cli', dir: path.join(home, '.gemini') },
57
+ { target: 'gemini-home', dir: path.join(home, '.gemini') },
47
58
  ];
48
59
  for (const { target, dir } of checks) {
49
60
  try {
@@ -116,15 +127,16 @@ function enableFor(target, { log = () => {} } = {}) {
116
127
  if (!isInstalled()) {
117
128
  return { enabled: false, skipped: true, reason: 'rtk binary not on PATH' };
118
129
  }
119
- const rtkArgs = TARGET_MAP[target];
130
+ const canonical = normalizeTarget(target);
131
+ const rtkArgs = TARGET_MAP[canonical];
120
132
  if (!rtkArgs) {
121
133
  return { enabled: false, skipped: true, reason: `no RTK mapping for target "${target}"` };
122
134
  }
123
135
 
124
- log(`[rtk] wiring RTK into ${target} …`);
136
+ log(`[rtk] wiring RTK into ${canonical} …`);
125
137
  // --auto-patch is only accepted by the default Claude Code hook flow.
126
138
  // Other agent flags (--codex, --gemini, --opencode, --agent X) reject it.
127
- const finalArgs = target === 'claude-code' ? [...rtkArgs, '--auto-patch'] : rtkArgs;
139
+ const finalArgs = canonical === 'claude-home' ? [...rtkArgs, '--auto-patch'] : rtkArgs;
128
140
  const r = spawnSync('rtk', finalArgs, { encoding: 'utf8' });
129
141
  const output = (r.stdout || '') + (r.stderr || '');
130
142
  if (r.status !== 0) {
@@ -175,6 +187,7 @@ function savings({ days = 30 } = {}) {
175
187
 
176
188
  module.exports = {
177
189
  TARGET_MAP,
190
+ normalizeTarget,
178
191
  detectInstalledTargets,
179
192
  isInstalled,
180
193
  getVersion,