kodelyth-ecc 1.8.5 → 1.9.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,49 @@
2
2
 
3
3
  All notable changes to Kodelyth ECC are documented here.
4
4
 
5
+ ## v1.9.0 — RTK integration + revived dashboard (July 2026)
6
+
7
+ ECC now auto-installs [RTK](https://github.com/rtk-ai/rtk) (Rust Token Killer) and wires its transparent command filter into whichever IDE ECC was installed for. Real token savings (60-90% on shell commands) show up in the dashboard, pulled straight from RTK's own ledger — no synthetic numbers.
8
+
9
+ ### Added
10
+
11
+ - `scripts/rtk/index.js` — RTK integration module: `install()`, `enableFor(target)`, `disableFor(target)`, `status()`, `savings()`
12
+ - `kodelyth-ecc rtk <install|enable|disable|status|gain>` — CLI subcommands to manage RTK from ECC
13
+ - Post-install auto-hook: after `npx kodelyth-ecc --target <ide>` succeeds, ECC auto-installs the RTK binary (Homebrew on macOS, curl script elsewhere) and runs `rtk init` for the target IDE. Opt out with `--no-rtk`
14
+ - Target map covers 10 install targets: `claude-code`, `cursor`, `cursor-project`, `windsurf-home`, `windsurf-project`, `antigravity`, `codex-home`, `opencode`, `cline`, `gemini-cli`
15
+ - `/api/rtk` + `/api/rtk/status` dashboard endpoints — surface RTK's live ledger
16
+ - New **RTK Savings** tab in the dashboard: total tokens saved, raw tokens seen, avg reduction %, active IDE integrations, 30-day daily bar chart
17
+
18
+ ### Changed
19
+
20
+ - Dashboard nav order: `Overview → RTK Savings → Memory → Evolve → Catalog → Sessions`
21
+
22
+ ### Notes
23
+
24
+ - Windows auto-install is skipped (RTK requires manual .zip download on native Windows); WSL follows the Linux path
25
+ - RTK setup is best-effort — if brew/curl aren't available, install fails gracefully with a hint to run `kodelyth-ecc rtk install` later
26
+ - Existing RTK installs are detected and reused; no double-install
27
+
28
+ ## v1.8.6 — Memory path rename + auto-migration (July 2026)
29
+
30
+ Renamed the on-disk memory root from `~/.kodelyth/` to `~/.kodelythecc/` across every runtime path. Existing installs auto-migrate on first CLI invocation or hook fire — idempotent, non-destructive, keeps a dated backup of the old directory.
31
+
32
+ ### Changed
33
+
34
+ - All 20 runtime JS files: memory, hooks, dashboard, evolve, MCP, router, tests now point at `~/.kodelythecc/` (42 replacements total)
35
+ - `scripts/memory/store.js` — default `MEMORY_DIR` now `~/.kodelythecc/memory/`; runs migration on `require`
36
+ - `bin/kodelyth-ecc.js` — runs migration on every CLI invocation (no-op after first run)
37
+ - `KODELYTH_MEMORY_DIR` and related env vars kept unchanged for backwards compat
38
+
39
+ ### Added
40
+
41
+ - `scripts/migrate-legacy.js` — one-shot migrator: merges memories, copies sibling files (index, patterns, projects/, evolve/, safety/, mcp-clients.json), renames old dir to `~/.kodelyth.backup-YYYY-MM-DD`, drops `.migrated-from-kodelyth` marker so it never runs twice
42
+
43
+ ### Behavior
44
+
45
+ - **New users**: get `~/.kodelythecc/` from the start
46
+ - **Existing users** (have `~/.kodelyth/`): data migrated on next CLI run or hook fire; original preserved as backup
47
+
5
48
  ## v1.8.0 — Visual system overhaul + SVG polish (May 2026)
6
49
 
7
50
  Comprehensive overhaul of all 31 social assets. GitHub social preview and OG image rebuilt with two-panel stat card layout. All text overflow issues fixed across the full SVG set. Test counts updated to 373. PNG exports regenerated at 4K only.
@@ -20,6 +20,11 @@ const fs = require('fs');
20
20
  const os = require('os');
21
21
 
22
22
  const ROOT = path.join(__dirname, '..');
23
+
24
+ // Auto-migrate legacy ~/.kodelyth/ → ~/.kodelythecc/ on first run for existing users.
25
+ // Idempotent; instant no-op if legacy dir is absent or migration marker exists.
26
+ try { require(path.join(ROOT, 'scripts', 'migrate-legacy.js')).main(); } catch { /* best-effort */ }
27
+
23
28
  // zsh (unlike bash) passes inline comments as literal args — strip them
24
29
  const args = process.argv.slice(2).filter((a, i, arr) => {
25
30
  if (a.startsWith('#')) return false; // drop # and everything after
@@ -172,6 +177,59 @@ if (args[0] && args[0].startsWith('mcp-')) {
172
177
  return;
173
178
  }
174
179
 
180
+ // ── Subcommand: rtk (Rust Token Killer integration) ──────────────────────────
181
+ // Usage:
182
+ // kodelyth-ecc rtk install install rtk binary (brew or curl)
183
+ // kodelyth-ecc rtk enable [--target X] wire rtk into an IDE (default: claude-code)
184
+ // kodelyth-ecc rtk disable [--target X] remove rtk hook from an IDE
185
+ // kodelyth-ecc rtk status show binary version + active integrations
186
+ // kodelyth-ecc rtk gain thin wrapper around `rtk gain`
187
+ if (args[0] === 'rtk') {
188
+ const rtk = require(path.join(ROOT, 'scripts', 'rtk', 'index.js'));
189
+ const sub = args[1] || 'status';
190
+ const rest = args.slice(2);
191
+ function flag(name, dflt) {
192
+ const i = rest.indexOf(name);
193
+ return i >= 0 && rest[i + 1] ? rest[i + 1] : dflt;
194
+ }
195
+ const log = (m) => process.stdout.write(m + '\n');
196
+ try {
197
+ if (sub === 'install') {
198
+ const r = rtk.install({ log });
199
+ log(JSON.stringify(r, null, 2));
200
+ process.exit(r.installed || r.skipped ? 0 : 1);
201
+ }
202
+ if (sub === 'enable') {
203
+ const target = flag('--target', 'claude-code');
204
+ const inst = rtk.install({ log });
205
+ if (!rtk.isInstalled()) { log(JSON.stringify(inst, null, 2)); process.exit(1); }
206
+ const r = rtk.enableFor(target, { log });
207
+ log(JSON.stringify(r, null, 2));
208
+ process.exit(r.enabled ? 0 : 1);
209
+ }
210
+ if (sub === 'disable') {
211
+ const target = flag('--target', 'claude-code');
212
+ const r = rtk.disableFor(target, { log });
213
+ log(JSON.stringify(r, null, 2));
214
+ process.exit(r.disabled ? 0 : 1);
215
+ }
216
+ if (sub === 'status') {
217
+ log(JSON.stringify(rtk.status(), null, 2));
218
+ process.exit(0);
219
+ }
220
+ if (sub === 'gain') {
221
+ const r = spawnSync('rtk', ['gain', ...rest], { stdio: 'inherit' });
222
+ process.exit(r.status ?? 1);
223
+ }
224
+ process.stderr.write('unknown rtk subcommand. try: install | enable | disable | status | gain\n');
225
+ process.exit(2);
226
+ } catch (e) {
227
+ process.stderr.write(`[rtk] ${e.message}\n`);
228
+ process.exit(1);
229
+ }
230
+ return;
231
+ }
232
+
175
233
  // ── Subcommand: route (cost-aware model tier recommendation) ──────────────────
176
234
  // Usage: npx kodelyth-ecc route "<task description>" [--files N] [--agent <name>] [--current <model-id>]
177
235
  if (args[0] === 'route') {
@@ -863,7 +921,7 @@ if (args.includes('--help') || args.includes('-h')) {
863
921
  mcp-call <name> <tool> [--json '{"arg":"value"}']
864
922
  Call a tool on a registered server. See docs/mcp-clients.md.
865
923
  route Recommend trivial/standard/hard model tier for a task. Reads
866
- .kodelyth/router.json and KODELYTH_ROUTER_* env vars. Disable with
924
+ .kodelythecc/router.json and KODELYTH_ROUTER_* env vars. Disable with
867
925
  KODELYTH_ROUTER=off. Use --json for machine-readable output.
868
926
  swarm Run N specialist agents in parallel inside isolated git worktrees +
869
927
  a tmux session. Auto-picks agents from --task signals or accepts
@@ -1010,6 +1068,7 @@ if (isWin) {
1010
1068
  }
1011
1069
  process.exit(result.status ?? 1);
1012
1070
  } else {
1071
+ try { require(path.join(ROOT, 'scripts', 'migrate-legacy.js')).main(); } catch {}
1013
1072
  const sh = path.join(ROOT, 'install.sh');
1014
1073
  if (!fs.existsSync(sh)) {
1015
1074
  console.error('Error: install.sh not found in package root:', ROOT);
@@ -1017,5 +1076,33 @@ if (isWin) {
1017
1076
  }
1018
1077
  fs.chmodSync(sh, 0o755);
1019
1078
  const result = spawnSync('bash', [sh, ...args], { stdio: 'inherit', shell: false });
1079
+
1080
+ // Post-install: auto-install + wire RTK for the target IDE (opt-out via --no-rtk).
1081
+ if (result.status === 0 && !args.includes('--no-rtk')) {
1082
+ try {
1083
+ const rtk = require(path.join(ROOT, 'scripts', 'rtk', 'index.js'));
1084
+ const targetIdx = args.indexOf('--target');
1085
+ const target = targetIdx >= 0 && args[targetIdx + 1] ? args[targetIdx + 1] : 'claude-code';
1086
+ if (rtk.TARGET_MAP[target]) {
1087
+ process.stdout.write('\n' + '─'.repeat(60) + '\n');
1088
+ process.stdout.write('[rtk] setting up token savings (60-90% on shell commands)\n');
1089
+ const inst = rtk.install({ log: (m) => process.stdout.write(m + '\n') });
1090
+ if (inst.installed || inst.reason === 'already installed') {
1091
+ const en = rtk.enableFor(target, { log: (m) => process.stdout.write(m + '\n') });
1092
+ if (en.enabled) {
1093
+ process.stdout.write(`[rtk] enabled for ${target} — restart your AI tool to activate\n`);
1094
+ } else {
1095
+ process.stdout.write(`[rtk] enable skipped: ${en.reason}\n`);
1096
+ }
1097
+ } else {
1098
+ process.stdout.write(`[rtk] install skipped: ${inst.reason}\n`);
1099
+ process.stdout.write('[rtk] you can retry later with: kodelyth-ecc rtk enable --target ' + target + '\n');
1100
+ }
1101
+ }
1102
+ } catch (e) {
1103
+ process.stderr.write(`[rtk] setup skipped: ${e.message}\n`);
1104
+ }
1105
+ }
1106
+
1020
1107
  process.exit(result.status ?? 1);
1021
1108
  }
@@ -11,7 +11,7 @@
11
11
  // - Skips on prompts that look like agent commands (`use foo`, `@bar`)
12
12
  // - Skips when no memory exists yet
13
13
  // - Suppresses repeats: never re-surfaces the same memory twice in a session
14
- // (state file: ~/.kodelyth/memory/session-surfaced-<sessionId>.json)
14
+ // (state file: ~/.kodelythecc/memory/session-surfaced-<sessionId>.json)
15
15
  // - Always exits 0 — never blocks the prompt because memory is unavailable
16
16
  // =============================================================================
17
17
 
@@ -120,7 +120,7 @@ function formatBlock(memories, userPrompt) {
120
120
 
121
121
  function surfacedStatePath(sessionId) {
122
122
  const dir = process.env.KODELYTH_MEMORY_DIR
123
- || path.join(os.homedir(), '.kodelyth', 'memory');
123
+ || path.join(os.homedir(), '.kodelythecc', 'memory');
124
124
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
125
125
  return path.join(dir, `session-surfaced-${sessionId}.json`);
126
126
  }
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // Runs at the end of a Claude Code session. Locates the session JSONL,
6
6
  // extracts memory candidates, writes them to a review queue at:
7
- // ~/.kodelyth/memory/pending-review.jsonl
7
+ // ~/.kodelythecc/memory/pending-review.jsonl
8
8
  //
9
9
  // Candidates are NEVER auto-stored. The user reviews via:
10
10
  // /memory review-pending
@@ -51,7 +51,7 @@ function main() {
51
51
  }
52
52
 
53
53
  const dir = process.env.KODELYTH_MEMORY_DIR
54
- || path.join(os.homedir(), '.kodelyth', 'memory');
54
+ || path.join(os.homedir(), '.kodelythecc', 'memory');
55
55
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
56
56
 
57
57
  const sessionId = data.session_id || path.basename(sessionJsonl, '.jsonl');
@@ -21,7 +21,7 @@
21
21
  // Blocks SessionStart once usage >= budget.
22
22
  //
23
23
  // Optional knobs:
24
- // KODELYTH_TOKEN_BUDGET_DIR=/custom/path default ~/.kodelyth/safety
24
+ // KODELYTH_TOKEN_BUDGET_DIR=/custom/path default ~/.kodelythecc/safety
25
25
  // KODELYTH_TOKEN_BUDGET_WARN=0.7 warn threshold (0-1, default 0.7)
26
26
  // KODELYTH_TOKEN_BUDGET_RESET=1 wipe usage and exit (admin op)
27
27
  //
@@ -39,7 +39,7 @@ const WARN_PCT = Math.max(0, Math.min(1, Number(process.env.KODELYTH_TOKEN_BUD
39
39
  const RESET = !!process.env.KODELYTH_TOKEN_BUDGET_RESET;
40
40
 
41
41
  const STATE_DIR = process.env.KODELYTH_TOKEN_BUDGET_DIR
42
- || path.join(os.homedir(), '.kodelyth', 'safety');
42
+ || path.join(os.homedir(), '.kodelythecc', 'safety');
43
43
 
44
44
  function safeExit(code) {
45
45
  try { process.stdout.write(''); } catch {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodelyth-ecc",
3
- "version": "1.8.5",
3
+ "version": "1.9.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",
@@ -4,8 +4,8 @@
4
4
  //
5
5
  // All data the dashboard renders comes from:
6
6
  // - The MCP catalog (filesystem reads of agents/, skills/, commands/, rules/, bundles/)
7
- // - The BM25 memory store (~/.kodelyth/memory/)
8
- // - The evolve signal streams (~/.kodelyth/evolve/)
7
+ // - The BM25 memory store (~/.kodelythecc/memory/)
8
+ // - The evolve signal streams (~/.kodelythecc/evolve/)
9
9
  // - The token-budget hook state (${KODELYTH_TOKEN_BUDGET_DIR})
10
10
  // - The orchestration session dirs (.orchestration/<session>/)
11
11
  // - Live IDE session activity (Claude Code, Windsurf, Antigravity)
@@ -35,13 +35,13 @@ const evolveProposals = safeRequire('scripts/evolve/proposals.js');
35
35
  // ── helpers ──────────────────────────────────────────────────────────────────
36
36
 
37
37
  function defaultMemoryDir() {
38
- return process.env.KODELYTH_MEMORY_DIR || path.join(os.homedir(), '.kodelyth', 'memory');
38
+ return process.env.KODELYTH_MEMORY_DIR || path.join(os.homedir(), '.kodelythecc', 'memory');
39
39
  }
40
40
  function defaultEvolveDir() {
41
- return process.env.KODELYTH_EVOLVE_DIR || path.join(os.homedir(), '.kodelyth', 'evolve');
41
+ return process.env.KODELYTH_EVOLVE_DIR || path.join(os.homedir(), '.kodelythecc', 'evolve');
42
42
  }
43
43
  function defaultBudgetDir() {
44
- return process.env.KODELYTH_TOKEN_BUDGET_DIR || path.join(os.homedir(), '.kodelyth', 'token-budget');
44
+ return process.env.KODELYTH_TOKEN_BUDGET_DIR || path.join(os.homedir(), '.kodelythecc', 'token-budget');
45
45
  }
46
46
  function defaultCoordRoot() {
47
47
  return process.env.KODELYTH_COORDINATION_ROOT || path.join(process.cwd(), '.orchestration');
@@ -37,11 +37,11 @@ const MAX_SSE_CLIENTS = 10;
37
37
  // IMPORTANT: include both `routing-misses.jsonl` (always present once evolve sees
38
38
  // any miss) AND `proposals.jsonl` (created later, after `evolve analyze` runs).
39
39
  const WATCH_PATHS = [
40
- path.join(os.homedir(), '.kodelyth', 'memory', 'memories.jsonl'),
41
- path.join(os.homedir(), '.kodelyth', 'evolve', 'reuse.json'),
42
- path.join(os.homedir(), '.kodelyth', 'evolve', 'routing-misses.jsonl'),
43
- path.join(os.homedir(), '.kodelyth', 'evolve', 'proposals.jsonl'),
44
- path.join(os.homedir(), '.kodelyth', 'token-budget'), // dir mtime — changes when budget files appear
40
+ path.join(os.homedir(), '.kodelythecc', 'memory', 'memories.jsonl'),
41
+ path.join(os.homedir(), '.kodelythecc', 'evolve', 'reuse.json'),
42
+ path.join(os.homedir(), '.kodelythecc', 'evolve', 'routing-misses.jsonl'),
43
+ path.join(os.homedir(), '.kodelythecc', 'evolve', 'proposals.jsonl'),
44
+ path.join(os.homedir(), '.kodelythecc', 'token-budget'), // dir mtime — changes when budget files appear
45
45
  ];
46
46
  const lastMtimes = new Map();
47
47
  let lastIdeMtime = 0; // max mtime across Claude Code / Windsurf / Antigravity sessions
@@ -279,6 +279,25 @@ function handleRequest(req, res) {
279
279
  return jsonResponse(res, 200, data.tokenBudgetSnapshot());
280
280
  }
281
281
 
282
+ if (p === '/api/rtk/status') {
283
+ const rtk = require('../rtk/index.js');
284
+ return jsonResponse(res, 200, rtk.status());
285
+ }
286
+
287
+ if (p === '/api/rtk') {
288
+ const rtk = require('../rtk/index.js');
289
+ const st = rtk.status();
290
+ if (!st.installed) {
291
+ return jsonResponse(res, 200, {
292
+ ok: false,
293
+ installed: false,
294
+ install_hint: 'Run: kodelyth-ecc rtk install',
295
+ });
296
+ }
297
+ const s = rtk.savings({ days: Number(q.get('days')) || 30 });
298
+ return jsonResponse(res, 200, { ok: true, installed: true, version: st.version, active: st.active, ...s });
299
+ }
300
+
282
301
  if (p.startsWith('/api/')) return notFound(res);
283
302
 
284
303
  // ── static fallback ───────────────────────────────────────────────────
@@ -220,6 +220,7 @@
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
224
  <button data-tab="memory">Memory</button>
224
225
  <button data-tab="evolve">Evolve</button>
225
226
  <button data-tab="catalog">Catalog</button>
@@ -245,6 +246,27 @@
245
246
  </div>
246
247
  </section>
247
248
 
249
+ <!-- ───────── RTK SAVINGS ───────── -->
250
+ <section data-panel="rtk" hidden>
251
+ <div class="grid" id="rtkCards"></div>
252
+ <hr class="sep">
253
+ <div class="row">
254
+ <div class="card">
255
+ <h2>Top saving commands</h2>
256
+ <div id="rtkTopCmds">loading…</div>
257
+ </div>
258
+ <div class="card">
259
+ <h2>Active integrations</h2>
260
+ <div id="rtkActive">loading…</div>
261
+ </div>
262
+ </div>
263
+ <hr class="sep">
264
+ <div class="card">
265
+ <h2>Daily savings (last 30 days)</h2>
266
+ <div id="rtkDaily">loading…</div>
267
+ </div>
268
+ </section>
269
+
248
270
  <!-- ───────── MEMORY ───────── -->
249
271
  <section data-panel="memory" hidden>
250
272
  <div class="grid" id="memoryCards"></div>
@@ -630,9 +652,76 @@
630
652
  }
631
653
  }
632
654
 
655
+ // ───── RTK loader ─────
656
+ async function loadRtk() {
657
+ try {
658
+ const r = await fetch('/api/rtk').then(r => r.json());
659
+ const cards = $('#rtkCards');
660
+ if (!r.installed) {
661
+ cards.innerHTML = `<div class="empty">RTK not installed. Install with:
662
+ <code>kodelyth-ecc rtk install</code>
663
+ <br><br>${escapeHtml(r.install_hint || '')}</div>`;
664
+ $('#rtkTopCmds').innerHTML = '';
665
+ $('#rtkActive').innerHTML = '';
666
+ $('#rtkDaily').innerHTML = '';
667
+ return;
668
+ }
669
+ if (!r.ok || !r.gain) {
670
+ cards.innerHTML = `<div class="empty">RTK installed (${escapeHtml(r.version || '')}) but no savings data yet. Use your AI tool with RTK enabled and refresh.</div>`;
671
+ $('#rtkTopCmds').innerHTML = '';
672
+ $('#rtkActive').innerHTML = (r.active || []).map(a => `<div class="muted">• ${escapeHtml(a)}</div>`).join('') || '<div class="empty">none</div>';
673
+ $('#rtkDaily').innerHTML = '';
674
+ return;
675
+ }
676
+ const g = r.gain || {};
677
+ const s = g.summary || {};
678
+ const totalSaved = s.total_saved ?? 0;
679
+ const totalInput = s.total_input ?? 0;
680
+ const percent = s.avg_savings_pct != null ? Math.round(s.avg_savings_pct) : 0;
681
+ const cmdCount = s.total_commands ?? 0;
682
+ cards.innerHTML = [
683
+ statCard(fmtN(totalSaved), 'Tokens saved (all-time)', `${percent}% avg reduction`),
684
+ statCard(fmtN(cmdCount), 'Commands filtered', 'through RTK'),
685
+ statCard(fmtN(totalInput), 'Raw tokens seen', 'before RTK compression'),
686
+ statCard(escapeHtml((r.version || '—').replace(/^rtk\s+/,'')), 'RTK version', 'active binary'),
687
+ ].join('');
688
+ // Per-command breakdown — RTK's `gain` JSON does not expose this today.
689
+ $('#rtkTopCmds').innerHTML = `<div class="muted" style="font-size:12.5px;">
690
+ Per-command breakdown lives in <code>rtk discover</code>. Run it in your terminal for missed savings opportunities.
691
+ </div>`;
692
+ // Active integrations
693
+ $('#rtkActive').innerHTML = (r.active && r.active.length)
694
+ ? r.active.map(a => `<div class="muted" style="font-family:monospace;font-size:12px;">${escapeHtml(a)}</div>`).join('')
695
+ : '<div class="empty">no active IDE integrations detected</div>';
696
+ // Daily graph — simple ascii-style bars, RTK schema: g.daily[].saved_tokens
697
+ const daily = g.daily || [];
698
+ if (Array.isArray(daily) && daily.length) {
699
+ const max = Math.max(...daily.map(d => d.saved_tokens || 0));
700
+ $('#rtkDaily').innerHTML = daily.slice(-30).map(d => {
701
+ const saved = d.saved_tokens || 0;
702
+ const pct = max ? (saved / max) * 100 : 0;
703
+ return `<div style="display:flex;gap:8px;align-items:center;margin:3px 0;font-size:12px;">
704
+ <span class="muted" style="width:90px;font-family:monospace;">${escapeHtml(d.date || '')}</span>
705
+ <div style="flex:1;background:#f1f5f9;border-radius:3px;height:14px;overflow:hidden;">
706
+ <div style="background:#34d399;height:100%;width:${pct}%;"></div>
707
+ </div>
708
+ <span style="width:110px;text-align:right;font-family:monospace;">${fmtN(saved)}</span>
709
+ </div>`;
710
+ }).join('');
711
+ } else {
712
+ $('#rtkDaily').innerHTML = '<div class="empty">no daily data yet</div>';
713
+ }
714
+ setLastUpdated();
715
+ } catch (e) {
716
+ $('#rtkCards').innerHTML = `<div class="empty">failed: ${escapeHtml(e.message)}</div>`;
717
+ }
718
+ }
719
+ function fmtN(n) { return Number(n || 0).toLocaleString(); }
720
+
633
721
  // ───── tab dispatcher ─────
634
722
  function onTabShown(tab) {
635
723
  if (tab === 'overview') loadOverview();
724
+ if (tab === 'rtk') loadRtk();
636
725
  if (tab === 'memory') loadMemory();
637
726
  if (tab === 'evolve') loadEvolve();
638
727
  if (tab === 'catalog') loadCatalog();
@@ -12,7 +12,7 @@
12
12
  // audit trail is preserved. Reads collapse to "latest state per id".
13
13
  //
14
14
  // Storage:
15
- // ${KODELYTH_EVOLVE_DIR:-~/.kodelyth/evolve}/proposals.jsonl
15
+ // ${KODELYTH_EVOLVE_DIR:-~/.kodelythecc/evolve}/proposals.jsonl
16
16
  //
17
17
  // Pure where possible. Public functions accept dir explicitly.
18
18
  'use strict';
@@ -22,7 +22,7 @@ const path = require('path');
22
22
  const os = require('os');
23
23
 
24
24
  const DEFAULT_DIR = process.env.KODELYTH_EVOLVE_DIR
25
- || path.join(os.homedir(), '.kodelyth', 'evolve');
25
+ || path.join(os.homedir(), '.kodelythecc', 'evolve');
26
26
 
27
27
  const PROPOSALS_FILE = 'proposals.jsonl';
28
28
 
@@ -14,7 +14,7 @@
14
14
  // by tokens to surface "we keep getting asked
15
15
  // about X but have no agent / skill / memory for it".
16
16
  //
17
- // Storage layout (default ${HOME}/.kodelyth/evolve/):
17
+ // Storage layout (default ${HOME}/.kodelythecc/evolve/):
18
18
  // reuse.json { byMemory: { id: { count, sessions[], lastSurfaced } } }
19
19
  // routing-misses.jsonl append-only — one prompt per line
20
20
  //
@@ -31,7 +31,7 @@ const path = require('path');
31
31
  const crypto = require('crypto');
32
32
 
33
33
  const DEFAULT_DIR = process.env.KODELYTH_EVOLVE_DIR
34
- || path.join(os.homedir(), '.kodelyth', 'evolve');
34
+ || path.join(os.homedir(), '.kodelythecc', 'evolve');
35
35
 
36
36
  const REUSE_FILE = 'reuse.json';
37
37
  const MISSES_FILE = 'routing-misses.jsonl';
@@ -6,7 +6,7 @@
6
6
  // make ECC the MCP HUB: serve to any framework, consume from any provider.
7
7
  //
8
8
  // Storage:
9
- // ~/.kodelyth/mcp-clients.json registry of named external servers
9
+ // ~/.kodelythecc/mcp-clients.json registry of named external servers
10
10
  //
11
11
  // Public API (all functions pure or local file-only):
12
12
  // loadRegistry(), saveRegistry(reg)
@@ -32,7 +32,7 @@ const os = require('os');
32
32
  const path = require('path');
33
33
 
34
34
  const REGISTRY_DIR = process.env.KODELYTH_MCP_CLIENT_DIR
35
- || path.join(os.homedir(), '.kodelyth');
35
+ || path.join(os.homedir(), '.kodelythecc');
36
36
  const REGISTRY_FILE = path.join(REGISTRY_DIR, 'mcp-clients.json');
37
37
 
38
38
  // ── Registry I/O ─────────────────────────────────────────────────────────────
@@ -2,7 +2,7 @@
2
2
  // Kodelyth ECC — Structured Instinct Schema (Improvement B)
3
3
  //
4
4
  // Stores learned instincts as typed JSON records in:
5
- // ~/.kodelyth/memory/instincts.jsonl
5
+ // ~/.kodelythecc/memory/instincts.jsonl
6
6
  //
7
7
  // Each instinct has: pattern, trigger, confidence, last_used, outcome, decay.
8
8
  // This replaces free-form markdown bullets for machine-readable learning.
@@ -33,7 +33,7 @@ const path = require('path');
33
33
  const crypto = require('crypto');
34
34
 
35
35
  const MEMORY_DIR = process.env.KODELYTH_MEMORY_DIR
36
- || path.join(os.homedir(), '.kodelyth', 'memory');
36
+ || path.join(os.homedir(), '.kodelythecc', 'memory');
37
37
 
38
38
  const INSTINCTS_FILE = path.join(MEMORY_DIR, 'instincts.jsonl');
39
39
  const STALE_DAYS = 30;
@@ -2,7 +2,7 @@
2
2
  // Kodelyth ECC — Memory Store
3
3
  // Local, zero-dependency, model-agnostic memory for AI coding sessions.
4
4
  //
5
- // Storage layout (all in ~/.kodelyth/memory/):
5
+ // Storage layout (all in ~/.kodelythecc/memory/):
6
6
  // memories.jsonl Append-only log of every captured memory
7
7
  // index.json Inverted index: token -> [memory ids]
8
8
  // patterns.json User-level patterns (preferences, conventions)
@@ -19,8 +19,11 @@ const os = require('os');
19
19
  const path = require('path');
20
20
  const crypto = require('crypto');
21
21
 
22
+ // Auto-migrate legacy ~/.kodelyth/ → ~/.kodelythecc/ before we touch any path.
23
+ try { require('../migrate-legacy').main(); } catch { /* best-effort */ }
24
+
22
25
  const MEMORY_DIR = process.env.KODELYTH_MEMORY_DIR
23
- || path.join(os.homedir(), '.kodelyth', 'memory');
26
+ || path.join(os.homedir(), '.kodelythecc', 'memory');
24
27
 
25
28
  const PATHS = {
26
29
  dir: MEMORY_DIR,
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ // One-shot migrator: ~/.kodelyth/ (old canonical) → ~/.kodelythecc/ (1.8.5+ canonical)
3
+ // Idempotent — leaves a marker so it never runs twice.
4
+ // Safe — never deletes source; renames old dir to .backup-<date>.
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ const LEGACY = path.join(os.homedir(), '.kodelyth');
13
+ const NEW = path.join(os.homedir(), '.kodelythecc');
14
+ const MARKER = path.join(NEW, '.migrated-from-kodelyth');
15
+
16
+ function main() {
17
+ if (!fs.existsSync(LEGACY)) return { migrated: false, reason: 'no legacy dir' };
18
+ if (fs.existsSync(MARKER)) return { migrated: false, reason: 'already migrated' };
19
+
20
+ fs.mkdirSync(path.join(NEW, 'memory'), { recursive: true });
21
+
22
+ const legacyMem = path.join(LEGACY, 'memory', 'memories.jsonl');
23
+ const newMem = path.join(NEW, 'memory', 'memories.jsonl');
24
+ let addedLines = 0;
25
+
26
+ if (fs.existsSync(legacyMem)) {
27
+ const existing = new Set(
28
+ fs.existsSync(newMem)
29
+ ? fs.readFileSync(newMem, 'utf8').split('\n').filter(Boolean)
30
+ : []
31
+ );
32
+ const legacyLines = fs.readFileSync(legacyMem, 'utf8').split('\n').filter(Boolean);
33
+ const toAppend = legacyLines.filter(l => !existing.has(l));
34
+ if (toAppend.length) {
35
+ fs.appendFileSync(newMem, (existing.size ? '\n' : '') + toAppend.join('\n') + '\n');
36
+ addedLines = toAppend.length;
37
+ }
38
+ }
39
+
40
+ // Copy any sibling files (index.json, session-surfaced-*.json) if not already present.
41
+ const legacyMemDir = path.join(LEGACY, 'memory');
42
+ let copiedFiles = 0;
43
+ if (fs.existsSync(legacyMemDir)) {
44
+ for (const f of fs.readdirSync(legacyMemDir)) {
45
+ if (f === 'memories.jsonl') continue;
46
+ const dst = path.join(NEW, 'memory', f);
47
+ if (!fs.existsSync(dst)) {
48
+ fs.copyFileSync(path.join(legacyMemDir, f), dst);
49
+ copiedFiles++;
50
+ }
51
+ }
52
+ }
53
+
54
+ // Copy other subtrees (evolve, safety, token-budget, mcp-clients.json) if missing.
55
+ const subtrees = ['evolve', 'safety', 'token-budget'];
56
+ for (const sub of subtrees) {
57
+ const src = path.join(LEGACY, sub);
58
+ const dst = path.join(NEW, sub);
59
+ if (fs.existsSync(src) && !fs.existsSync(dst)) {
60
+ copyDirSync(src, dst);
61
+ }
62
+ }
63
+ const legacyMcp = path.join(LEGACY, 'mcp-clients.json');
64
+ const newMcp = path.join(NEW, 'mcp-clients.json');
65
+ if (fs.existsSync(legacyMcp) && !fs.existsSync(newMcp)) {
66
+ fs.copyFileSync(legacyMcp, newMcp);
67
+ }
68
+
69
+ // Rename legacy dir → backup (never delete).
70
+ const stamp = new Date().toISOString().slice(0, 10);
71
+ const backup = path.join(os.homedir(), `.kodelyth.backup-${stamp}`);
72
+ try {
73
+ if (!fs.existsSync(backup)) fs.renameSync(LEGACY, backup);
74
+ } catch { /* best-effort */ }
75
+
76
+ fs.writeFileSync(MARKER, JSON.stringify({
77
+ at: new Date().toISOString(),
78
+ addedLines,
79
+ copiedFiles,
80
+ backupPath: backup,
81
+ }, null, 2));
82
+
83
+ return { migrated: true, addedLines, copiedFiles, backupPath: backup };
84
+ }
85
+
86
+ function copyDirSync(src, dst) {
87
+ fs.mkdirSync(dst, { recursive: true });
88
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
89
+ const s = path.join(src, entry.name);
90
+ const d = path.join(dst, entry.name);
91
+ if (entry.isDirectory()) copyDirSync(s, d);
92
+ else fs.copyFileSync(s, d);
93
+ }
94
+ }
95
+
96
+ if (require.main === module) {
97
+ try {
98
+ const r = main();
99
+ if (r.migrated) {
100
+ process.stdout.write(
101
+ `[kodelyth] migrated legacy ~/.kodelyth → ~/.kodelythecc ` +
102
+ `(+${r.addedLines} memories, ${r.copiedFiles} files copied, backup: ${r.backupPath})\n`
103
+ );
104
+ }
105
+ } catch (e) {
106
+ process.stderr.write(`[kodelyth] migration skipped: ${e.message}\n`);
107
+ }
108
+ }
109
+
110
+ module.exports = { main };
@@ -164,13 +164,13 @@ function classify(task, opts = {}) {
164
164
  return { tier: 'standard', reasons, score, explicit_tier: explicitTier || null };
165
165
  }
166
166
 
167
- // ── Config loader (env vars + .kodelyth/router.json) ─────────────────────────
167
+ // ── Config loader (env vars + .kodelythecc/router.json) ─────────────────────────
168
168
  function loadConfig({ projectRoot = process.cwd() } = {}) {
169
169
  const cfg = { ...DEFAULT_MODELS, notes: '' };
170
170
 
171
171
  // 1. Project-level override.
172
172
  try {
173
- const file = path.join(projectRoot, '.kodelyth', 'router.json');
173
+ const file = path.join(projectRoot, '.kodelythecc', 'router.json');
174
174
  if (fs.existsSync(file)) {
175
175
  const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
176
176
  for (const k of ['trivial', 'standard', 'hard', 'default', 'notes']) {
@@ -0,0 +1,160 @@
1
+ // scripts/rtk/index.js
2
+ // RTK (Rust Token Killer) integration — auto-installs and wires RTK into the IDE
3
+ // that ECC was installed for, so shell commands run through RTK's filter for
4
+ // 60-90% token savings.
5
+ //
6
+ // Public API:
7
+ // isInstalled() → boolean
8
+ // install({ log }) → { installed, method, version } | { skipped, reason }
9
+ // enableFor(target,{log})→ { enabled, target, agent, output } | { skipped, reason }
10
+ // status() → { installed, version, agents: [...] }
11
+ // savings({ days }) → parsed `rtk gain --format json` snapshot or null
12
+ 'use strict';
13
+
14
+ const { execFileSync, spawnSync } = require('child_process');
15
+ const os = require('os');
16
+ const path = require('path');
17
+ const fs = require('fs');
18
+
19
+ // ── ECC install target → RTK agent flag ──────────────────────────────────────
20
+ // Reference: `rtk init --help` and RTK README "Supported AI Tools" table.
21
+ const TARGET_MAP = {
22
+ 'claude-code': ['init', '-g'],
23
+ 'cursor': ['init', '-g', '--agent', 'cursor'],
24
+ 'cursor-project': ['init', '--agent', 'cursor'],
25
+ 'windsurf-home': ['init', '-g', '--agent', 'windsurf'],
26
+ 'windsurf-project': ['init', '--agent', 'windsurf'],
27
+ 'antigravity': ['init', '--agent', 'antigravity'],
28
+ 'codex-home': ['init', '-g', '--codex'],
29
+ 'opencode': ['init', '-g', '--opencode'],
30
+ 'cline': ['init', '--agent', 'cline'],
31
+ 'gemini-cli': ['init', '-g', '--gemini'],
32
+ };
33
+
34
+ function isInstalled() {
35
+ try {
36
+ execFileSync('rtk', ['--version'], { stdio: 'ignore' });
37
+ return true;
38
+ } catch { return false; }
39
+ }
40
+
41
+ function getVersion() {
42
+ try {
43
+ return execFileSync('rtk', ['--version'], { encoding: 'utf8' }).trim();
44
+ } catch { return null; }
45
+ }
46
+
47
+ // ── Install RTK binary ───────────────────────────────────────────────────────
48
+ // Mac: prefer `brew install rtk` if brew is on PATH (fastest, cached).
49
+ // Otherwise: pipe the official install script through sh (installs to ~/.local/bin).
50
+ // Windows: skipped (needs manual .zip download from releases).
51
+ function install({ log = () => {} } = {}) {
52
+ if (isInstalled()) {
53
+ return { installed: false, skipped: true, reason: 'already installed', version: getVersion() };
54
+ }
55
+ if (os.platform() === 'win32') {
56
+ return { installed: false, skipped: true, reason: 'windows requires manual install: https://github.com/rtk-ai/rtk/releases' };
57
+ }
58
+
59
+ // Try Homebrew first on macOS.
60
+ if (os.platform() === 'darwin') {
61
+ try {
62
+ execFileSync('brew', ['--version'], { stdio: 'ignore' });
63
+ log('[rtk] installing via Homebrew…');
64
+ const r = spawnSync('brew', ['install', 'rtk'], { stdio: 'inherit' });
65
+ if (r.status === 0 && isInstalled()) {
66
+ return { installed: true, method: 'brew', version: getVersion() };
67
+ }
68
+ log('[rtk] brew install did not complete; falling back to curl script');
69
+ } catch { /* brew not present */ }
70
+ }
71
+
72
+ // Fall back to the official install script.
73
+ log('[rtk] installing via curl script (installs to ~/.local/bin)…');
74
+ const script = 'curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh';
75
+ const r = spawnSync('sh', ['-c', script], { stdio: 'inherit' });
76
+ if (r.status !== 0) {
77
+ return { installed: false, skipped: true, reason: 'install script failed — install rtk manually: https://github.com/rtk-ai/rtk#installation' };
78
+ }
79
+
80
+ // Make sure ~/.local/bin is on PATH for this process so isInstalled() succeeds.
81
+ const localBin = path.join(os.homedir(), '.local', 'bin');
82
+ if (fs.existsSync(path.join(localBin, 'rtk')) && !process.env.PATH.split(':').includes(localBin)) {
83
+ process.env.PATH = `${localBin}:${process.env.PATH}`;
84
+ }
85
+
86
+ if (!isInstalled()) {
87
+ 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' };
88
+ }
89
+ return { installed: true, method: 'curl', version: getVersion() };
90
+ }
91
+
92
+ // ── Enable RTK for a specific IDE ────────────────────────────────────────────
93
+ function enableFor(target, { log = () => {} } = {}) {
94
+ if (!isInstalled()) {
95
+ return { enabled: false, skipped: true, reason: 'rtk binary not on PATH' };
96
+ }
97
+ const rtkArgs = TARGET_MAP[target];
98
+ if (!rtkArgs) {
99
+ return { enabled: false, skipped: true, reason: `no RTK mapping for target "${target}"` };
100
+ }
101
+
102
+ log(`[rtk] wiring RTK into ${target} …`);
103
+ const r = spawnSync('rtk', [...rtkArgs, '--auto-patch'], { encoding: 'utf8' });
104
+ const output = (r.stdout || '') + (r.stderr || '');
105
+ if (r.status !== 0) {
106
+ return { enabled: false, skipped: true, reason: 'rtk init failed', output };
107
+ }
108
+ return { enabled: true, target, agent: rtkArgs.join(' '), output: output.trim() };
109
+ }
110
+
111
+ // ── Disable RTK for a specific IDE (removes hook + RTK.md) ───────────────────
112
+ function disableFor(target, { log = () => {} } = {}) {
113
+ if (!isInstalled()) return { disabled: false, skipped: true, reason: 'rtk not installed' };
114
+ const rtkArgs = TARGET_MAP[target];
115
+ if (!rtkArgs) return { disabled: false, skipped: true, reason: `no RTK mapping for target "${target}"` };
116
+ log(`[rtk] removing RTK from ${target} …`);
117
+ const r = spawnSync('rtk', [...rtkArgs, '--uninstall'], { encoding: 'utf8' });
118
+ return { disabled: r.status === 0, output: ((r.stdout || '') + (r.stderr || '')).trim() };
119
+ }
120
+
121
+ // ── Status: what's installed, what's active ──────────────────────────────────
122
+ function status() {
123
+ const installed = isInstalled();
124
+ if (!installed) return { installed: false, version: null, active: [] };
125
+ const version = getVersion();
126
+ const active = [];
127
+ try {
128
+ const r = execFileSync('rtk', ['init', '--show'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
129
+ // Only surface the '[ok]' status rows — skip the usage help RTK prints below.
130
+ for (const line of r.split('\n')) {
131
+ const t = line.trim();
132
+ if (t.startsWith('[ok]') || t.startsWith('[--]')) active.push(t);
133
+ }
134
+ } catch { /* older rtk versions may not have --show */ }
135
+ return { installed: true, version, active };
136
+ }
137
+
138
+ // ── Savings: read `rtk gain --format json` for dashboard ─────────────────────
139
+ function savings({ days = 30 } = {}) {
140
+ if (!isInstalled()) return null;
141
+ try {
142
+ const r = execFileSync('rtk', ['gain', '--all', '--format', 'json'], {
143
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 16 * 1024 * 1024,
144
+ });
145
+ return { ok: true, days, gain: JSON.parse(r) };
146
+ } catch (e) {
147
+ return { ok: false, error: e.message };
148
+ }
149
+ }
150
+
151
+ module.exports = {
152
+ TARGET_MAP,
153
+ isInstalled,
154
+ getVersion,
155
+ install,
156
+ enableFor,
157
+ disableFor,
158
+ status,
159
+ savings,
160
+ };
@@ -0,0 +1,11 @@
1
+ # Claude Lessons
2
+
3
+ Project: **kodelyth-ecc**
4
+
5
+ Auto-generated by Kodelyth ECC. Each entry is a rule Claude learned from a correction.
6
+ Edit freely — add, remove, reword. These are YOUR rules.
7
+
8
+ ---
9
+ ## 2026-07-03
10
+
11
+ - We need to update our kodelyth ecc ( elite code crew) we build from this - https://github.com/affaan-m/ecc but they are now more advance. So now problem is is our ECC less powerfull then them. We have bm25 memories but not works. We need grab maximum feature from them and attach into ours ( don't te