kodelyth-ecc 1.8.6 → 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,29 @@
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
+
5
28
  ## v1.8.6 — Memory path rename + auto-migration (July 2026)
6
29
 
7
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.
@@ -177,6 +177,59 @@ if (args[0] && args[0].startsWith('mcp-')) {
177
177
  return;
178
178
  }
179
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
+
180
233
  // ── Subcommand: route (cost-aware model tier recommendation) ──────────────────
181
234
  // Usage: npx kodelyth-ecc route "<task description>" [--files N] [--agent <name>] [--current <model-id>]
182
235
  if (args[0] === 'route') {
@@ -1023,5 +1076,33 @@ if (isWin) {
1023
1076
  }
1024
1077
  fs.chmodSync(sh, 0o755);
1025
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
+
1026
1107
  process.exit(result.status ?? 1);
1027
1108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodelyth-ecc",
3
- "version": "1.8.6",
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",
@@ -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();
@@ -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
+ };