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 +40 -0
- package/README.md +193 -0
- package/VERSION +1 -1
- package/bin/kodelyth-ecc.js +51 -1
- package/package.json +1 -1
- package/scripts/cli/menu.js +41 -0
- package/scripts/cli/uninstall.js +148 -0
- package/scripts/codebase/index.js +16 -1
- package/scripts/dashboard/static/index.html +10 -0
- package/scripts/mcp/register-self.js +116 -0
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
|

|
|
20
20
|

|
|
21
21
|

|
|
22
|
+

|
|
23
|
+

|
|
24
|
+

|
|
25
|
+

|
|
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.
|
|
1
|
+
2.4.0
|
package/bin/kodelyth-ecc.js
CHANGED
|
@@ -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
|
+
"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",
|
package/scripts/cli/menu.js
CHANGED
|
@@ -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
|
+
};
|