kodelyth-ecc 2.3.0 → 2.4.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,46 @@
2
2
 
3
3
  All notable changes to Kodelyth ECC are documented here.
4
4
 
5
+ ## v2.4.0 — MCP auto-register + full uninstall (July 2026)
6
+
7
+ Real bugs surfaced by live user testing on a fresh Mac install.
8
+
9
+ ### Fixed
10
+
11
+ - **ECC's own MCP server was never registered anywhere** on install. Result: Claude Desktop showed "1 setup issue: MCP", the Codebase / MCP status views showed nothing, and no tool got to talk to our own MCP. `scripts/mcp/register-self.js` now writes `kodelyth-ecc` under `mcpServers` in both `~/.claude.json` (Claude Code) and `~/Library/Application Support/Claude/claude_desktop_config.json` (Claude Desktop). Idempotent — no duplicates on re-run.
12
+ - **Codebase Architecture snapshot was empty** on the dashboard when no active session graph existed. Dashboard now falls back to a per-project list (project name + nodes + edges) via `list_projects`, so users can see the 8 projects codebase-memory-mcp has indexed even before opening one in-session.
13
+ - **`mcp-register` subcommand was swallowed** by the `mcp-*` catch-all (like `mcp-status` was earlier). Excluded properly.
14
+
15
+ ### Added
16
+
17
+ **MCP self-registration**
18
+ - `kodelythecc mcp-register` — write ECC's MCP entry into Claude Code + Desktop configs
19
+ - `kodelythecc mcp-register --status` — show which configs already have it
20
+ - `kodelythecc mcp-register --unregister` — remove ECC's entry
21
+ - **Auto-runs in post-install** — after `--target claude-code` succeeds, ECC registers itself in both configs and prints a summary. Opt out with `--no-mcp-register`
22
+
23
+ **Full uninstall** (`scripts/cli/uninstall.js`)
24
+ - New menu row: **Uninstall ECC completely**
25
+ - CLI: `kodelythecc uninstall --dry-run | --yes | --yes --keep-memory`
26
+ - Removes only files that were shipped by ECC (checks against the package's own `agents/skills/commands/hooks/rules/scripts/` — safe for user-authored siblings)
27
+ - Unwires RTK (`rtk init -g --uninstall`)
28
+ - Removes codebase-memory-mcp agent configs (`codebase-memory-mcp uninstall`)
29
+ - Removes ECC's MCP entry from Claude Code + Desktop
30
+ - Removes `~/.kodelythecc/` (memory + ledgers + cache)
31
+ - Removes legacy `.kodelyth.backup-*` dirs
32
+ - Dry-run mode identifies **759 ECC files** across 6 subdirs on a fresh install
33
+
34
+ ### Verified live end-to-end on this Mac
35
+
36
+ - `mcp-register --status` (before): both configs empty
37
+ - `mcp-register` writes both:
38
+ ```json
39
+ "kodelyth-ecc": { "command": "kodelythecc", "args": ["mcp"], "env": {} }
40
+ ```
41
+ - `~/.claude.json` now has `mcpServers.kodelyth-ecc` + existing `mcpServers.codebase-memory-mcp` (co-existing, no clobber)
42
+ - `uninstall --dry-run` correctly counts 70 agents + 301 skills + 99 commands + 12 hooks + 104 rules + 173 scripts = 759 files, plus 1 backup dir
43
+ - Nothing removed on dry-run — verified
44
+
5
45
  ## v2.3.0 — Interactive arrow-key CLI menu (July 2026)
6
46
 
7
47
  Run `kodelythecc` (or `kodelyth-ecc`) with no args in a real terminal and an arrow-key menu opens.
package/README.md CHANGED
@@ -19,11 +19,22 @@
19
19
  ![Auto Recall](https://img.shields.io/badge/Auto%20Chat%20Recall-on-blueviolet)
20
20
  ![Parallel Agents](https://img.shields.io/badge/Parallel%20Agents-new-red)
21
21
  ![Image Generation](https://img.shields.io/badge/AI%20Image%20Gen-new-yellow)
22
+ ![RTK](https://img.shields.io/badge/RTK-input%20savings-orange)
23
+ ![Terse Mode](https://img.shields.io/badge/Terse%20Mode-output%20savings-orange)
24
+ ![Codebase Graph](https://img.shields.io/badge/Codebase%20Graph-158%20languages-green)
25
+ ![Interactive CLI](https://img.shields.io/badge/CLI-interactive%20menu-blue)
22
26
 
23
27
  </div>
24
28
 
25
29
  **Kodelyth ECC** is a production-grade AI coding toolkit — **70 specialist agents (incl. an 8-agent devil-mode adversarial crew), 194 skills, 97 commands**, a god-tier **semantic intent-routing system**, local self-learning memory, MCP server, swarm orchestrator, and an observability dashboard — all local, zero telemetry.
26
30
 
31
+ Now bundled with:
32
+
33
+ - **RTK** (Rust Token Killer, Apache-2.0) — auto-compresses shell command output for **60-90% input token savings**
34
+ - **Terse mode** (ECC-native, inspired by Caveman) — 4-level output-token compression dial for **40-70% output savings**
35
+ - **Codebase graph** (via [codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp), MIT) — AST-parsed knowledge graph across 158 languages; structural queries at **99% fewer tokens** than file-by-file grep
36
+ - **Interactive CLI** — type `kodelythecc` alone in a terminal for an arrow-key menu with live update check, dashboard, background daemon
37
+
27
38
  Works with **Claude Code**, **Windsurf**, **Cursor**, **Codex CLI**, **Google Antigravity**, **OpenCode**, **Cline**, **Roo Code**, **Aider**, **Kimi**, and **Gemini CLI**.
28
39
 
29
40
  > No telemetry. No cloud. Just rules, agents, skills, MCP server, and your own private memory store — all on your disk. The local dashboard gives you full visibility without sending anything anywhere.
@@ -88,6 +99,25 @@ You never typed `use debug-detective`. You didn't have to. The toolkit read the
88
99
  <img src="social/section-install.svg" alt="Install — One command, 11 platforms, any OS" width="900"/>
89
100
  </div>
90
101
 
102
+ ### The lazy install (one command, everything wired up)
103
+
104
+ ```bash
105
+ npm i -g kodelyth-ecc
106
+ kodelythecc --target claude-code --codebase-graph
107
+ # then restart your AI tool
108
+ ```
109
+
110
+ That's it. This single flow:
111
+
112
+ 1. Installs both binaries (`kodelyth-ecc` and short-form `kodelythecc`) to your PATH
113
+ 2. Copies 70 agents + 194 skills + 97 commands + 22 hooks + 14 rules into your AI tool's config dir
114
+ 3. Auto-installs **RTK** binary and wires its PreToolUse hook (input compression starts on next AI restart)
115
+ 4. Installs **Terse mode** skill + `/terse` and `/terse-compress` slash commands (dormant — user types `/terse` to activate)
116
+ 5. Auto-installs **codebase-memory-mcp** and registers its MCP entries in your AI tool (with `--codebase-graph`)
117
+ 6. Runs legacy-memory migration if you had `~/.kodelyth/` from an older install → `~/.kodelythecc/`
118
+
119
+ After it finishes, run `kodelythecc` alone to open the interactive menu.
120
+
91
121
  ### Option 1 — npx from npm (recommended, any platform)
92
122
 
93
123
  ```bash
@@ -398,6 +428,169 @@ Full reference: [`docs/dashboard.md`](docs/dashboard.md).
398
428
 
399
429
  ---
400
430
 
431
+ ## RTK — Input Token Savings (60-90%)
432
+
433
+ [RTK (Rust Token Killer)](https://github.com/rtk-ai/rtk) — Apache-2.0, single Rust binary — intercepts shell commands before they run and filters the output. Common commands like `git status`, `ls`, `cargo test`, `docker ps` return ~80% less text with zero information loss for the LLM.
434
+
435
+ ECC bundles RTK end-to-end:
436
+
437
+ - Auto-installs the binary via Homebrew (macOS) or curl script (Linux / WSL)
438
+ - Auto-configures the PreToolUse hook in your AI tool's settings
439
+ - Live ledger surfaced in the dashboard's **Token Savings** tab
440
+
441
+ ### Manage from the CLI
442
+
443
+ ```bash
444
+ kodelythecc rtk install # install rtk binary
445
+ kodelythecc rtk enable --target X # wire into one IDE
446
+ kodelythecc rtk enable --all # wire into every ECC-installed IDE at once
447
+ kodelythecc rtk status # version + active integrations
448
+ kodelythecc rtk gain --all # raw rtk savings output
449
+ kodelythecc rtk --help # focused help
450
+ ```
451
+
452
+ ### Live proof (from this repo maintainer's Mac)
453
+
454
+ ```
455
+ total_commands: 1,285
456
+ total_input: 7,964,612 (raw)
457
+ total_output: 2,858,501 (after RTK filter)
458
+ total_saved: 5,107,394 ← 64.1% average reduction
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Terse Mode — Output Token Savings (40-70%)
464
+
465
+ RTK compresses input. **Terse mode** compresses output. Together they stack — savings on both sides of every turn.
466
+
467
+ ECC's own implementation, inspired by [Caveman](https://github.com/JuliusBrussee/caveman) (MIT), independently written. Ships as a skill + two slash commands + a deterministic zero-dep memory-file compressor.
468
+
469
+ ### Four levels
470
+
471
+ Type `/terse` in your AI tool. Level sticks until you switch or session ends.
472
+
473
+ | Level | Style |
474
+ |---|---|
475
+ | `/terse off` | Normal AI voice |
476
+ | `/terse lite` | Light trim, drop filler ("basically", "essentially") |
477
+ | `/terse full` | Telegram-style fragments (default) |
478
+ | `/terse ultra` | Maximum compression, symbols over words |
479
+
480
+ **Byte-preserved always**: fenced code blocks, inline code, shell commands, error text, URLs, file paths, identifiers, numbers, versions.
481
+
482
+ ### Compress memory files permanently
483
+
484
+ `/terse-compress` (or `kodelythecc terse compress <file>`) rewrites `CLAUDE.md`-style memory files into terse form so they cost fewer tokens **every session forever**. ~30% average byte reduction on real prose, 100% code/URL/path integrity.
485
+
486
+ ### CLI
487
+
488
+ ```bash
489
+ kodelythecc terse status # skill install state
490
+ kodelythecc terse stats # tokens saved from ledger
491
+ kodelythecc terse compress <file> # rewrite a file, byte-preserves code
492
+ kodelythecc terse enable --all # install skill + commands into every ECC IDE
493
+ kodelythecc terse --help
494
+ ```
495
+
496
+ ---
497
+
498
+ ## Codebase Graph — 158 Languages, Structural Queries
499
+
500
+ Powered by [DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp) (MIT). AST-parsed knowledge graph via tree-sitter across **158 languages**, Hybrid LSP semantic type resolution for 11 major languages, cross-service HTTP/gRPC/GraphQL linking, 14 MCP tools for structural queries.
501
+
502
+ **Why this matters**: "Who calls `ProcessOrder`?" via file-by-file grep = ~412k tokens. Same question via the graph = ~3.4k tokens. That's a **99% reduction** on structural questions.
503
+
504
+ ### Auto-install path
505
+
506
+ Pass `--codebase-graph` on ECC install and it auto-runs their official curl script + auto-configures the MCP server entry in every installed AI-coding agent.
507
+
508
+ ```bash
509
+ kodelythecc --target claude-code --codebase-graph
510
+ # ↑ installs ECC + RTK + Terse + codebase-memory-mcp, all wired up
511
+ ```
512
+
513
+ Then in your AI tool, say **"Index this project"**. Done.
514
+
515
+ ### CLI
516
+
517
+ ```bash
518
+ kodelythecc codebase install # install + auto-register
519
+ kodelythecc codebase status # version + indexed projects
520
+ kodelythecc codebase query search_graph '{"name_pattern": ".*Handler.*"}'
521
+ kodelythecc codebase query trace_path '{"function_name": "main"}'
522
+ kodelythecc codebase query get_architecture '{}'
523
+ kodelythecc codebase --help
524
+ ```
525
+
526
+ ---
527
+
528
+ ## Interactive CLI Menu
529
+
530
+ Type `kodelythecc` alone in a real terminal → arrow-key menu opens.
531
+
532
+ ```
533
+ ⚙ Kodelyth ECC v2.3.0 · Elite Code Crew up to date
534
+
535
+ ▸ Open Dashboard localhost — RTK, Terse, Codebase, Memory
536
+ Install ECC for another IDE 13-target picker
537
+ RTK status Version + wired IDEs + savings
538
+ Terse status Skill install state, ledger totals
539
+ Codebase graph status 158 languages, structural queries
540
+ Memory stats BM25 recall — captures, projects, tags
541
+ Run in background Detached dashboard daemon
542
+ Exit
543
+
544
+ ↑/↓ navigate · ⏎ select · q / esc / Ctrl+C to quit
545
+ ```
546
+
547
+ ### Behaviour rules
548
+
549
+ | Situation | Menu opens? |
550
+ |---|---|
551
+ | `kodelythecc` in Terminal / iTerm | **Yes** |
552
+ | `kodelythecc rtk status` (any subcommand) | No — runs subcommand |
553
+ | `echo hi \| kodelythecc` (piped) | No — runs installer |
554
+ | CI environment (`$CI` set) | No |
555
+ | `KODELYTH_NO_MENU=1 kodelythecc` | No |
556
+
557
+ ### Uninstall
558
+
559
+ The menu's **Uninstall ECC completely** row runs an interactive full cleanup: removes the 759 ECC-installed files from `~/.claude/`, unwires RTK from your AI tool, removes codebase-memory-mcp agent configs, removes ECC's MCP entry from Claude Code + Claude Desktop, and deletes `~/.kodelythecc/` (memory + ledgers). Prompts confirm before anything is deleted; a dry-run mode previews what would be removed without touching anything.
560
+
561
+ You can also run it non-interactively:
562
+
563
+ ```bash
564
+ kodelythecc uninstall --dry-run # preview what would be removed
565
+ kodelythecc uninstall --yes # full cleanup
566
+ kodelythecc uninstall --yes --keep-memory # remove files but keep ~/.kodelythecc/
567
+ npm uninstall -g kodelyth-ecc # finally remove the npm package itself
568
+ ```
569
+
570
+ ### Update check
571
+
572
+ The menu polls `https://registry.npmjs.org/kodelyth-ecc/latest` on open. Cached 24h in `~/.kodelythecc/update-check.json`. When a newer version exists, an extra menu row appears at the top:
573
+
574
+ ```
575
+ ▸ Update to v2.3.1 [NEW] npm i -g kodelyth-ecc
576
+ ```
577
+
578
+ ### Background daemon
579
+
580
+ Selecting "Run in background" forks the dashboard as a detached process:
581
+
582
+ ```
583
+ ✓ Dashboard daemon started (pid 12345)
584
+ URL: http://127.0.0.1:5747
585
+ Log: /Users/you/.kodelythecc/dashboard-daemon.log
586
+ Pid: /Users/you/.kodelythecc/dashboard-daemon.pid
587
+ Stop: kill $(cat ~/.kodelythecc/dashboard-daemon.pid)
588
+ ```
589
+
590
+ Survives shell exit. Real daemon.
591
+
592
+ ---
593
+
401
594
  ## Kodelyth Memory — Local Self-Learning
402
595
 
403
596
  <div align="center">
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.3.0
1
+ 2.4.0
@@ -135,7 +135,7 @@ if (args[0] === 'mcp') {
135
135
  // kodelyth-ecc mcp-remove <name>
136
136
  // kodelyth-ecc mcp-tools <name>
137
137
  // kodelyth-ecc mcp-call <name> <tool> [--json '{"arg":"value"}']
138
- if (args[0] && args[0].startsWith('mcp-')) {
138
+ if (args[0] && args[0].startsWith('mcp-') && !['mcp-register'].includes(args[0])) {
139
139
  (async () => {
140
140
  const sub = args[0].slice(4);
141
141
  const client = require(path.join(ROOT, 'scripts', 'mcp', 'client.js'));
@@ -339,6 +339,40 @@ if (args[0] === 'rtk') {
339
339
  return;
340
340
  }
341
341
 
342
+ // ── Subcommand: uninstall (full ECC removal) ────────────────────────────────
343
+ if (args[0] === 'uninstall') {
344
+ const un = require(path.join(ROOT, 'scripts', 'cli', 'uninstall.js'));
345
+ const dryRun = args.includes('--dry-run');
346
+ const keepMemory = args.includes('--keep-memory');
347
+ const yes = args.includes('--yes') || args.includes('-y');
348
+ if (!yes && !dryRun) {
349
+ process.stderr.write('This removes ECC files, RTK hooks, codebase-memory-mcp configs, and (unless --keep-memory) ~/.kodelythecc/. Add --yes to confirm or --dry-run to preview.\n');
350
+ process.exit(1);
351
+ }
352
+ const r = un.run({ log: (m) => process.stdout.write(m + '\n'), dryRun, keepMemory });
353
+ process.stdout.write(`\nRemoved ${r.removed_files} files across ${r.subsystems_uninstalled.length} subsystems.\n`);
354
+ if (r.errors.length) process.stdout.write(`Errors: ${r.errors.length}\n${r.errors.map(e => ' ' + e).join('\n')}\n`);
355
+ process.stdout.write(dryRun ? '(dry-run — no changes made)\n' : 'ECC removed. Uninstall the npm package: npm uninstall -g kodelyth-ecc\n');
356
+ process.exit(0);
357
+ }
358
+
359
+ // ── Subcommand: mcp-register (self-register ECC MCP in Claude Code + Desktop)
360
+ if (args[0] === 'mcp-register') {
361
+ const reg = require(path.join(ROOT, 'scripts', 'mcp', 'register-self.js'));
362
+ if (args.includes('--status')) {
363
+ const s = reg.statusAll();
364
+ for (const row of s) process.stdout.write(`${row.present ? '✓' : '·'} ${row.file}${row.exists ? '' : ' (file missing)'}\n`);
365
+ process.exit(0);
366
+ }
367
+ if (args.includes('--unregister')) {
368
+ reg.unregisterAll({ log: (m) => process.stdout.write(m + '\n') });
369
+ process.exit(0);
370
+ }
371
+ reg.registerAll({ log: (m) => process.stdout.write(m + '\n') });
372
+ process.stdout.write('\nRestart Claude Code / Claude Desktop to pick up the new MCP server.\n');
373
+ process.exit(0);
374
+ }
375
+
342
376
  // ── Subcommand: codebase (DeusData code-graph MCP integration) ───────────────
343
377
  // Usage:
344
378
  // kodelyth-ecc codebase install install the codebase-memory-mcp binary
@@ -1451,6 +1485,22 @@ if (isWin) {
1451
1485
  fs.chmodSync(sh, 0o755);
1452
1486
  const result = spawnSync('bash', [sh, ...args], { stdio: 'inherit', shell: false });
1453
1487
 
1488
+ // Post-install: auto-register ECC's own MCP server in Claude Code + Desktop.
1489
+ if (result.status === 0 && !args.includes('--no-mcp-register')) {
1490
+ try {
1491
+ const reg = require(path.join(ROOT, 'scripts', 'mcp', 'register-self.js'));
1492
+ process.stdout.write('\n━ ECC MCP server registration ' + '─'.repeat(31) + '\n');
1493
+ const rows = reg.registerAll({ log: () => {} });
1494
+ for (const r of rows) {
1495
+ const marker = r.action === 'added' ? '✓ added' : r.action === 'updated' ? '✓ updated' : r.action === 'unchanged' ? '· unchanged' : `· ${r.action}`;
1496
+ process.stdout.write(` ${marker}: ${r.file}\n`);
1497
+ }
1498
+ process.stdout.write(` · Restart Claude Code / Claude Desktop to pick up the server\n`);
1499
+ } catch (e) {
1500
+ process.stderr.write(`[mcp] self-register skipped: ${e.message}\n`);
1501
+ }
1502
+ }
1503
+
1454
1504
  // Post-install: auto-install + wire RTK for the target IDE (opt-out via --no-rtk).
1455
1505
  if (result.status === 0 && !args.includes('--no-rtk')) {
1456
1506
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodelyth-ecc",
3
- "version": "2.3.0",
3
+ "version": "2.4.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",
@@ -89,6 +89,11 @@ async function buildOptions({ ROOT }) {
89
89
  hint: 'Detach dashboard as a daemon so /api routes stay reachable',
90
90
  run: () => runBackground(),
91
91
  });
92
+ opts.push({
93
+ label: 'Uninstall ECC completely',
94
+ hint: 'Remove agents/skills/commands/hooks, RTK hook, codebase-mcp, and ~/.kodelythecc/',
95
+ run: () => runUninstall(ROOT),
96
+ });
92
97
  opts.push({
93
98
  label: 'Exit',
94
99
  hint: '',
@@ -149,6 +154,42 @@ async function pickIdeAndInstall(ROOT) {
149
154
  waitForEnter().then(() => main().catch(() => process.exit(0)));
150
155
  }
151
156
 
157
+ async function runUninstall(ROOT) {
158
+ cleanup();
159
+ console.log(`${C.red}${C.bold}⚠ Uninstall Kodelyth ECC${C.reset}`);
160
+ console.log('');
161
+ console.log('This will remove:');
162
+ console.log(` ${C.gray}·${C.reset} All ECC agents/skills/commands/hooks/rules installed under ~/.claude/`);
163
+ console.log(` ${C.gray}·${C.reset} RTK hook (rtk binary stays — remove with brew uninstall rtk)`);
164
+ console.log(` ${C.gray}·${C.reset} codebase-memory-mcp agent configs (binary stays)`);
165
+ console.log(` ${C.gray}·${C.reset} ECC MCP server entry from Claude Code + Claude Desktop`);
166
+ console.log(` ${C.gray}·${C.reset} ~/.kodelythecc/ (memory, ledgers, cache) — unless you keep it`);
167
+ console.log('');
168
+ const choice = await pick('Confirm', [
169
+ 'Uninstall — remove EVERYTHING including memory',
170
+ 'Uninstall — keep ~/.kodelythecc/ memory + ledgers',
171
+ 'Dry run — show what would be removed, change nothing',
172
+ 'Cancel',
173
+ ]);
174
+ if (choice == null || choice === 3) return main();
175
+ const dryRun = choice === 2;
176
+ const keepMemory = choice === 1;
177
+ cleanup();
178
+ console.log('');
179
+ const un = require(path.join(ROOT, 'scripts', 'cli', 'uninstall.js'));
180
+ const r = un.run({ log: (m) => console.log(m), dryRun, keepMemory });
181
+ console.log('');
182
+ console.log(`${C.green}✓${C.reset} Removed ${C.bold}${r.removed_files}${C.reset} files across ${r.subsystems_uninstalled.length} subsystems.`);
183
+ if (r.errors.length) console.log(`${C.yellow}Errors: ${r.errors.length}${C.reset}`);
184
+ if (dryRun) {
185
+ console.log(`${C.dim}(dry-run — nothing was changed)${C.reset}`);
186
+ } else {
187
+ console.log('');
188
+ console.log(`Finish by removing the npm package: ${C.cyan}npm uninstall -g kodelyth-ecc${C.reset}`);
189
+ }
190
+ process.exit(0);
191
+ }
192
+
152
193
  function runBackground() {
153
194
  cleanup();
154
195
  const logDir = path.join(os.homedir(), '.kodelythecc');
@@ -0,0 +1,148 @@
1
+ // scripts/cli/uninstall.js
2
+ // Full ECC cleanup — reverses everything the installer + post-install did.
3
+ // Safe: only removes files installed BY ECC (checks for known markers).
4
+ // Never touches user's own custom agents/skills/commands they added themselves.
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('fs');
9
+ const os = require('os');
10
+ const path = require('path');
11
+ const { spawnSync } = require('child_process');
12
+
13
+ // ── What ECC ships — used to identify which files WE installed ─────────────
14
+ function listShippedFiles(ROOT, subdir) {
15
+ const src = path.join(ROOT, subdir);
16
+ if (!fs.existsSync(src)) return [];
17
+ const out = [];
18
+ const walk = (dir, prefix = '') => {
19
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
20
+ const rel = path.join(prefix, entry.name);
21
+ const abs = path.join(dir, entry.name);
22
+ if (entry.isDirectory()) walk(abs, rel);
23
+ else out.push(rel);
24
+ }
25
+ };
26
+ walk(src);
27
+ return out;
28
+ }
29
+
30
+ // Only remove files that also exist in the ECC package.
31
+ // Preserves user-authored files in the same directory.
32
+ function removeInstalledFiles(destDir, shipped) {
33
+ if (!fs.existsSync(destDir)) return { removed: 0, skipped: 0 };
34
+ let removed = 0, skipped = 0;
35
+ for (const rel of shipped) {
36
+ const abs = path.join(destDir, rel);
37
+ if (fs.existsSync(abs)) {
38
+ try { fs.unlinkSync(abs); removed++; } catch { skipped++; }
39
+ }
40
+ }
41
+ // Clean up now-empty ECC-owned directories.
42
+ const dirs = new Set(shipped.map(f => path.dirname(f)).filter(d => d !== '.'));
43
+ for (const rel of [...dirs].sort((a, b) => b.length - a.length)) {
44
+ const abs = path.join(destDir, rel);
45
+ try {
46
+ if (fs.existsSync(abs) && fs.readdirSync(abs).length === 0) fs.rmdirSync(abs);
47
+ } catch { /* keep going */ }
48
+ }
49
+ return { removed, skipped };
50
+ }
51
+
52
+ function rmrf(dir) {
53
+ try { fs.rmSync(dir, { recursive: true, force: true }); return true; }
54
+ catch { return false; }
55
+ }
56
+
57
+ // ── The uninstall plan (dry-run friendly) ──────────────────────────────────
58
+ function plan(ROOT) {
59
+ const home = os.homedir();
60
+ const claudeDir = path.join(home, '.claude');
61
+ const shipped = {
62
+ agents: listShippedFiles(ROOT, 'agents'),
63
+ skills: listShippedFiles(ROOT, 'skills'),
64
+ commands: listShippedFiles(ROOT, 'commands'),
65
+ hooks: listShippedFiles(ROOT, 'hooks'),
66
+ rules: listShippedFiles(ROOT, 'rules'),
67
+ scripts: listShippedFiles(ROOT, 'scripts'),
68
+ };
69
+ const dests = {
70
+ agents: path.join(claudeDir, 'agents'),
71
+ skills: path.join(claudeDir, 'skills'),
72
+ commands: path.join(claudeDir, 'commands'),
73
+ hooks: path.join(claudeDir, 'hooks'),
74
+ rules: path.join(claudeDir, 'rules'),
75
+ scripts: path.join(claudeDir, 'scripts'),
76
+ };
77
+ return { home, claudeDir, shipped, dests };
78
+ }
79
+
80
+ // ── Run the uninstall ──────────────────────────────────────────────────────
81
+ function run({ log = () => {}, keepMemory = false, dryRun = false } = {}) {
82
+ const ROOT = path.join(__dirname, '..', '..');
83
+ const p = plan(ROOT);
84
+ const results = { removed_files: 0, dirs_removed: [], subsystems_uninstalled: [], errors: [] };
85
+
86
+ log('Removing ECC-installed files from ~/.claude/ …');
87
+ for (const kind of Object.keys(p.shipped)) {
88
+ if (dryRun) {
89
+ log(` [dry-run] would remove ${p.shipped[kind].length} ${kind} files from ${p.dests[kind]}`);
90
+ continue;
91
+ }
92
+ const r = removeInstalledFiles(p.dests[kind], p.shipped[kind]);
93
+ results.removed_files += r.removed;
94
+ log(` ${kind}: removed ${r.removed} files (${r.skipped} skipped)`);
95
+ }
96
+
97
+ // Remove ECC's own MCP entries from Claude Code / Claude Desktop configs.
98
+ try {
99
+ const reg = require('../mcp/register-self.js');
100
+ if (!dryRun) reg.unregisterAll({ log: (m) => log(' ' + m) });
101
+ else log(' [dry-run] would unregister kodelyth-ecc MCP server from Claude Code + Desktop');
102
+ results.subsystems_uninstalled.push('mcp-registration');
103
+ } catch (e) { results.errors.push('mcp-unregister: ' + e.message); }
104
+
105
+ // Remove RTK integration (uses their own uninstall flag).
106
+ try {
107
+ log('Unwiring RTK integrations …');
108
+ if (!dryRun) {
109
+ const r = spawnSync('rtk', ['init', '-g', '--uninstall'], { encoding: 'utf8' });
110
+ log(r.status === 0 ? ' ✓ rtk uninstalled from claude-code' : ' · rtk uninstall skipped (may not be present)');
111
+ }
112
+ results.subsystems_uninstalled.push('rtk');
113
+ } catch (e) { results.errors.push('rtk: ' + e.message); }
114
+
115
+ // Remove codebase-memory-mcp registration (their own uninstall).
116
+ try {
117
+ log('Uninstalling codebase-memory-mcp agent configs …');
118
+ if (!dryRun) {
119
+ const r = spawnSync('codebase-memory-mcp', ['uninstall'], { encoding: 'utf8' });
120
+ log(r.status === 0 ? ' ✓ codebase-memory-mcp configs removed' : ' · codebase-memory-mcp uninstall skipped (binary not present)');
121
+ }
122
+ results.subsystems_uninstalled.push('codebase-memory-mcp');
123
+ } catch (e) { results.errors.push('codebase-memory-mcp: ' + e.message); }
124
+
125
+ // Remove memory + ledgers.
126
+ if (!keepMemory) {
127
+ log(`${dryRun ? '[dry-run] would remove' : 'Removing'} ~/.kodelythecc/ (memory, ledgers, cache) …`);
128
+ const target = path.join(p.home, '.kodelythecc');
129
+ if (!dryRun && rmrf(target)) results.dirs_removed.push(target);
130
+ } else {
131
+ log('Keeping ~/.kodelythecc/ (memory preserved as requested)');
132
+ }
133
+
134
+ // Remove legacy migration backup if present.
135
+ try {
136
+ for (const name of fs.readdirSync(p.home)) {
137
+ if (name.startsWith('.kodelyth.backup-')) {
138
+ const abs = path.join(p.home, name);
139
+ if (dryRun) log(` [dry-run] would remove backup ${abs}`);
140
+ else { rmrf(abs); results.dirs_removed.push(abs); }
141
+ }
142
+ }
143
+ } catch { /* home dir listing failed — skip */ }
144
+
145
+ return results;
146
+ }
147
+
148
+ module.exports = { plan, run };
@@ -86,6 +86,21 @@ function dashboardSnapshot() {
86
86
  const st = status();
87
87
  if (!st.installed) return { ok: false, installed: false, install_hint: 'Run: kodelyth-ecc codebase install' };
88
88
  const out = { ok: true, installed: true, version: st.version, indexed_projects: st.indexed_projects, cache_dir: st.cache_dir };
89
+ // Try to list projects with names — richer than just a count.
90
+ try {
91
+ const list = query('list_projects', '{}');
92
+ if (Array.isArray(list)) {
93
+ out.projects = list.slice(0, 20).map(p => ({
94
+ name: p.name || p.path || p.project || 'unnamed',
95
+ path: p.path || null,
96
+ nodes: p.node_count ?? p.nodes ?? null,
97
+ edges: p.edge_count ?? p.edges ?? null,
98
+ }));
99
+ } else if (list && Array.isArray(list.projects)) {
100
+ out.projects = list.projects.slice(0, 20);
101
+ }
102
+ } catch { /* older binaries may not support list_projects */ }
103
+ // Try architecture snapshot for whichever project is 'active'.
89
104
  try {
90
105
  const arch = query('get_architecture', '{}');
91
106
  if (arch && typeof arch === 'object') {
@@ -96,7 +111,7 @@ function dashboardSnapshot() {
96
111
  entry_points: (arch.entry_points || []).slice(0, 5),
97
112
  };
98
113
  }
99
- } catch { /* no active project yet */ }
114
+ } catch { /* no active project yet — dashboard shows CTA */ }
100
115
  return out;
101
116
  }
102
117
 
@@ -813,6 +813,16 @@ kodelyth-ecc codebase query detect_changes '{}'</pre>
813
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
814
  </tbody>
815
815
  </table>`;
816
+ } else if ((r.projects || []).length) {
817
+ $('#codebaseArch').innerHTML = `<div class="muted" style="font-size:13px;">
818
+ ${(r.projects || []).length} project(s) indexed. Architecture snapshot is per-active-session — open a project in your AI tool and it appears here.
819
+ </div><table style="margin-top:10px;">
820
+ <thead><tr><th>Project</th><th style="text-align:right;">Nodes</th><th style="text-align:right;">Edges</th></tr></thead>
821
+ <tbody>${r.projects.map(p => `<tr>
822
+ <td><code style="font-size:11.5px;">${escapeHtml(p.name)}</code></td>
823
+ <td style="text-align:right;">${fmtN(p.nodes || 0)}</td>
824
+ <td style="text-align:right;">${fmtN(p.edges || 0)}</td>
825
+ </tr>`).join('')}</tbody></table>`;
816
826
  } else {
817
827
  $('#codebaseArch').innerHTML = '<div class="empty">no active project graph yet — open a repo in your AI tool and say "Index this project"</div>';
818
828
  }
@@ -0,0 +1,116 @@
1
+ // scripts/mcp/register-self.js
2
+ // Auto-register ECC's own MCP server in Claude Code (~/.claude.json) and
3
+ // Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json).
4
+ //
5
+ // Idempotent — updates in place, never duplicates. Handles missing files.
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+ const os = require('os');
11
+ const path = require('path');
12
+
13
+ const SERVER_NAME = 'kodelyth-ecc';
14
+
15
+ function claudeCodeConfigPath() {
16
+ // Claude Code reads user-level MCP from ~/.claude.json
17
+ return path.join(os.homedir(), '.claude.json');
18
+ }
19
+
20
+ function claudeDesktopConfigPath() {
21
+ const home = os.homedir();
22
+ if (os.platform() === 'darwin') return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
23
+ if (os.platform() === 'win32') return path.join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
24
+ return path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
25
+ }
26
+
27
+ function readJson(file) {
28
+ try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch { return null; }
29
+ }
30
+
31
+ function writeJson(file, obj) {
32
+ fs.mkdirSync(path.dirname(file), { recursive: true });
33
+ fs.writeFileSync(file, JSON.stringify(obj, null, 2));
34
+ }
35
+
36
+ function serverEntry() {
37
+ return {
38
+ command: 'kodelythecc',
39
+ args: ['mcp'],
40
+ env: {},
41
+ };
42
+ }
43
+
44
+ function registerInFile(file, opts = {}) {
45
+ const existing = readJson(file) || {};
46
+ const servers = existing.mcpServers || {};
47
+ const already = servers[SERVER_NAME];
48
+ const desired = serverEntry();
49
+ if (already && JSON.stringify(already) === JSON.stringify(desired)) {
50
+ return { file, action: 'unchanged' };
51
+ }
52
+ servers[SERVER_NAME] = desired;
53
+ const next = { ...existing, mcpServers: servers };
54
+ if (!opts.dryRun) writeJson(file, next);
55
+ return { file, action: already ? 'updated' : 'added' };
56
+ }
57
+
58
+ function unregisterInFile(file) {
59
+ const existing = readJson(file);
60
+ if (!existing || !existing.mcpServers || !existing.mcpServers[SERVER_NAME]) {
61
+ return { file, action: 'not-present' };
62
+ }
63
+ delete existing.mcpServers[SERVER_NAME];
64
+ writeJson(file, existing);
65
+ return { file, action: 'removed' };
66
+ }
67
+
68
+ function registerAll({ log = () => {} } = {}) {
69
+ const results = [];
70
+ for (const p of [claudeCodeConfigPath(), claudeDesktopConfigPath()]) {
71
+ try {
72
+ const r = registerInFile(p);
73
+ log(` ${r.action}: ${r.file}`);
74
+ results.push(r);
75
+ } catch (e) {
76
+ log(` skipped ${p}: ${e.message}`);
77
+ results.push({ file: p, action: 'error', error: e.message });
78
+ }
79
+ }
80
+ return results;
81
+ }
82
+
83
+ function unregisterAll({ log = () => {} } = {}) {
84
+ const results = [];
85
+ for (const p of [claudeCodeConfigPath(), claudeDesktopConfigPath()]) {
86
+ try {
87
+ const r = unregisterInFile(p);
88
+ log(` ${r.action}: ${r.file}`);
89
+ results.push(r);
90
+ } catch (e) {
91
+ log(` skipped ${p}: ${e.message}`);
92
+ results.push({ file: p, action: 'error', error: e.message });
93
+ }
94
+ }
95
+ return results;
96
+ }
97
+
98
+ function statusAll() {
99
+ const rows = [];
100
+ for (const p of [claudeCodeConfigPath(), claudeDesktopConfigPath()]) {
101
+ const cfg = readJson(p);
102
+ const present = !!(cfg && cfg.mcpServers && cfg.mcpServers[SERVER_NAME]);
103
+ rows.push({ file: p, present, exists: !!cfg });
104
+ }
105
+ return rows;
106
+ }
107
+
108
+ module.exports = {
109
+ SERVER_NAME,
110
+ serverEntry,
111
+ claudeCodeConfigPath,
112
+ claudeDesktopConfigPath,
113
+ registerAll,
114
+ unregisterAll,
115
+ statusAll,
116
+ };