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 +43 -0
- package/bin/kodelyth-ecc.js +88 -1
- package/hooks/memory/auto-recall.js +2 -2
- package/hooks/memory/capture-stop.js +2 -2
- package/hooks/safety/token-budget.js +2 -2
- package/package.json +1 -1
- package/scripts/dashboard/data.js +5 -5
- package/scripts/dashboard/server.js +24 -5
- package/scripts/dashboard/static/index.html +89 -0
- package/scripts/evolve/proposals.js +2 -2
- package/scripts/evolve/stats.js +2 -2
- package/scripts/mcp/client.js +2 -2
- package/scripts/memory/instincts.js +2 -2
- package/scripts/memory/store.js +5 -2
- package/scripts/migrate-legacy.js +110 -0
- package/scripts/router/classify.js +2 -2
- package/scripts/rtk/index.js +160 -0
- package/tasks/lessons.md +11 -0
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.
|
package/bin/kodelyth-ecc.js
CHANGED
|
@@ -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
|
-
.
|
|
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: ~/.
|
|
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(), '.
|
|
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
|
-
// ~/.
|
|
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(), '.
|
|
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 ~/.
|
|
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(), '.
|
|
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.
|
|
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 (~/.
|
|
8
|
-
// - The evolve signal streams (~/.
|
|
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(), '.
|
|
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(), '.
|
|
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(), '.
|
|
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(), '.
|
|
41
|
-
path.join(os.homedir(), '.
|
|
42
|
-
path.join(os.homedir(), '.
|
|
43
|
-
path.join(os.homedir(), '.
|
|
44
|
-
path.join(os.homedir(), '.
|
|
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:-~/.
|
|
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(), '.
|
|
25
|
+
|| path.join(os.homedir(), '.kodelythecc', 'evolve');
|
|
26
26
|
|
|
27
27
|
const PROPOSALS_FILE = 'proposals.jsonl';
|
|
28
28
|
|
package/scripts/evolve/stats.js
CHANGED
|
@@ -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}/.
|
|
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(), '.
|
|
34
|
+
|| path.join(os.homedir(), '.kodelythecc', 'evolve');
|
|
35
35
|
|
|
36
36
|
const REUSE_FILE = 'reuse.json';
|
|
37
37
|
const MISSES_FILE = 'routing-misses.jsonl';
|
package/scripts/mcp/client.js
CHANGED
|
@@ -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
|
-
// ~/.
|
|
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(), '.
|
|
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
|
-
// ~/.
|
|
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(), '.
|
|
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;
|
package/scripts/memory/store.js
CHANGED
|
@@ -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 ~/.
|
|
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(), '.
|
|
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 + .
|
|
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, '.
|
|
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
|
+
};
|
package/tasks/lessons.md
ADDED
|
@@ -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
|