kodelyth-ecc 2.5.2 → 2.5.4
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 +34 -0
- package/VERSION +1 -1
- package/bin/kodelyth-ecc.js +71 -0
- package/package.json +1 -1
- package/scripts/memory/extract.js +38 -24
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Kodelyth ECC are documented here.
|
|
4
4
|
|
|
5
|
+
## v2.5.4 — Memory: direct capture CLI + aggressive auto-capture (July 2026)
|
|
6
|
+
|
|
7
|
+
Two memory improvements after an end-to-end verification pass confirmed the pipeline works but only captured when the user said "thanks".
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`kodelythecc memory` subcommand** — manage the local BM25 memory directly:
|
|
12
|
+
- `memory capture --problem "…" --approach "…" [--tags a,b] [--language ts] [--files a,b]` — store a fix on the spot, **no review queue**
|
|
13
|
+
- `memory recall "<query>" [--limit N]` — BM25 search
|
|
14
|
+
- `memory list` · `memory stats`
|
|
15
|
+
- Wired into `--help` (`kodelythecc memory --help`).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Auto-capture is now more aggressive.** Previously it only queued a memory when a user message contained a success phrase ("that worked", "perfect", "thanks"). It now **also** captures on a real verification signal — an `Edit`/`Write` followed by a passing test or successful build (`exit code 0`, `tests passed`, `build succeeded`, `PASS`, …) — even without a spoken acknowledgement. This catches fixes you verified but never verbally confirmed. Opt out with `KODELYTH_CAPTURE_AGGRESSIVE=0`.
|
|
20
|
+
|
|
21
|
+
### Verified
|
|
22
|
+
|
|
23
|
+
- End-to-end: capture → persist → recall across processes; `auto-recall` hook injects past fixes; `auto-capture` hook queues from a real `.message`-nested transcript; `doctor` memory checks pass.
|
|
24
|
+
- New regression tests: aggressive path captures without "thanks"; opt-out flag disables it. Full suite 0 failures.
|
|
25
|
+
|
|
26
|
+
## v2.5.3 — Devil Mode + Memory cards for "How it works" (July 2026)
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- `social/card-devil.svg` — Devil Mode feature card: "8 agents that attack your code first", the 8-agent adversarial grid + `/devil-mode --all`. Red accent.
|
|
31
|
+
- `social/card-memory.svg` — Compound Memory card: "It remembers what worked last time", the BM25 recall flow (prompt → recalled fix → injected into context). Violet accent.
|
|
32
|
+
- Both wired into the site's "How it works" showcase — now 8 features (routing, RTK, Terse, codebase, MCP, CLI, devil mode, memory), all in the taller card format with summaries.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- SVG version badges v2.5.2 → v2.5.3; 8K PNGs re-rendered across the set.
|
|
37
|
+
- Fresh GitHub social preview render (196 skills, v2.5.3) at 8K + upload-ready 2×.
|
|
38
|
+
|
|
5
39
|
## v2.5.2 — Broken README image fix + brand polish (July 2026)
|
|
6
40
|
|
|
7
41
|
### Fixed
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.5.
|
|
1
|
+
2.5.4
|
package/bin/kodelyth-ecc.js
CHANGED
|
@@ -118,6 +118,21 @@ if (args[1] === '--help' || args[1] === '-h') {
|
|
|
118
118
|
install target, hook registration, memory recall, MCP registration + boot,
|
|
119
119
|
prompt-injection guard, RTK, Terse, codebase graph. Run it if anything
|
|
120
120
|
feels off — it pinpoints exactly what's not wired and how to fix it.
|
|
121
|
+
`,
|
|
122
|
+
memory: `
|
|
123
|
+
kodelyth-ecc memory — local BM25 self-learning memory
|
|
124
|
+
|
|
125
|
+
Usage:
|
|
126
|
+
kodelythecc memory capture --problem "<what was hard>" --approach "<what worked>" [--tags a,b] [--language ts] [--files a,b]
|
|
127
|
+
kodelythecc memory recall "<query>" [--limit N]
|
|
128
|
+
kodelythecc memory list
|
|
129
|
+
kodelythecc memory stats
|
|
130
|
+
|
|
131
|
+
'capture' stores directly (no review queue) — use it to remember a fix on
|
|
132
|
+
the spot. Auto-recall/auto-capture also run via hooks in your AI tool.
|
|
133
|
+
Storage: ~/.kodelythecc/memory/ · 100% local, zero telemetry.
|
|
134
|
+
Auto-capture aggressiveness: it also captures on edit + passing test even
|
|
135
|
+
without a spoken "thanks"; set KODELYTH_CAPTURE_AGGRESSIVE=0 to opt out.
|
|
121
136
|
`,
|
|
122
137
|
};
|
|
123
138
|
const cmd = args[0];
|
|
@@ -353,6 +368,62 @@ if (args[0] === 'rtk') {
|
|
|
353
368
|
|
|
354
369
|
// ── Subcommand: doctor (live subsystem health check) ─────────────────────────
|
|
355
370
|
// Usage: kodelythecc doctor [--json]
|
|
371
|
+
// ── Subcommand: memory (direct capture / recall / stats, no review queue) ────
|
|
372
|
+
// Usage:
|
|
373
|
+
// kodelythecc memory capture --problem "..." --approach "..." [--tags a,b] [--language ts]
|
|
374
|
+
// kodelythecc memory recall "<query>" [--limit N]
|
|
375
|
+
// kodelythecc memory list
|
|
376
|
+
// kodelythecc memory stats
|
|
377
|
+
if (args[0] === 'memory') {
|
|
378
|
+
const store = require(path.join(ROOT, 'scripts', 'memory', 'store.js'));
|
|
379
|
+
const sub = args[1] || 'stats';
|
|
380
|
+
const rest = args.slice(2);
|
|
381
|
+
function flag(name, dflt) { const i = rest.indexOf('--' + name); return i >= 0 && rest[i + 1] && !rest[i + 1].startsWith('--') ? rest[i + 1] : dflt; }
|
|
382
|
+
const w = (m) => process.stdout.write(m + '\n');
|
|
383
|
+
try {
|
|
384
|
+
if (sub === 'capture' || sub === 'remember') {
|
|
385
|
+
const problem = flag('problem') || rest.find(a => !a.startsWith('--'));
|
|
386
|
+
const approach = flag('approach');
|
|
387
|
+
if (!problem || !approach) {
|
|
388
|
+
process.stderr.write('usage: kodelythecc memory capture --problem "<what was hard>" --approach "<what worked>" [--tags a,b] [--language ts] [--files a,b]\n');
|
|
389
|
+
process.exit(2);
|
|
390
|
+
}
|
|
391
|
+
const m = store.capture({
|
|
392
|
+
problem, approach,
|
|
393
|
+
tags: (flag('tags') || '').split(',').filter(Boolean),
|
|
394
|
+
language: flag('language') || null,
|
|
395
|
+
files: (flag('files') || '').split(',').filter(Boolean),
|
|
396
|
+
project: flag('project') || process.cwd(),
|
|
397
|
+
source: 'cli',
|
|
398
|
+
});
|
|
399
|
+
w(`✓ stored ${m.id} — "${m.problem.slice(0, 60)}"`);
|
|
400
|
+
process.exit(0);
|
|
401
|
+
}
|
|
402
|
+
if (sub === 'recall' || sub === 'search') {
|
|
403
|
+
const query = rest.find(a => !a.startsWith('--'));
|
|
404
|
+
if (!query) { process.stderr.write('usage: kodelythecc memory recall "<query>"\n'); process.exit(2); }
|
|
405
|
+
const results = store.recall(query, { limit: Number(flag('limit')) || 5, minScore: 0.1 });
|
|
406
|
+
if (!results.length) { w('No matching memories.'); process.exit(0); }
|
|
407
|
+
for (const m of results) w(`[${(m.score || 0).toFixed(2)}] ${m.problem}\n → ${(m.approach || '').split('\n')[0].slice(0, 80)} (${(m.tags || []).join(', ')})`);
|
|
408
|
+
process.exit(0);
|
|
409
|
+
}
|
|
410
|
+
if (sub === 'list') {
|
|
411
|
+
const all = store.listAll();
|
|
412
|
+
w(`${all.length} memories:`);
|
|
413
|
+
for (const m of all.slice(0, 50)) w(` ${m.id} ${(m.captured_at || '').slice(0, 10)} ${(m.problem || '').slice(0, 60)}`);
|
|
414
|
+
process.exit(0);
|
|
415
|
+
}
|
|
416
|
+
if (sub === 'stats') {
|
|
417
|
+
const s = store.stats();
|
|
418
|
+
w(`Memories: ${s.total} · projects: ${s.projects} · dir: ${s.storageDir}`);
|
|
419
|
+
w(`Top tags: ${s.topTags.slice(0, 8).map(t => t[0] + '(' + t[1] + ')').join(', ')}`);
|
|
420
|
+
process.exit(0);
|
|
421
|
+
}
|
|
422
|
+
process.stderr.write('unknown memory subcommand. try: capture | recall | list | stats\n');
|
|
423
|
+
process.exit(2);
|
|
424
|
+
} catch (e) { process.stderr.write(`[memory] ${e.message}\n`); process.exit(1); }
|
|
425
|
+
}
|
|
426
|
+
|
|
356
427
|
if (args[0] === 'doctor') {
|
|
357
428
|
const { run, PASS, WARN, FAIL } = require(path.join(ROOT, 'scripts', 'doctor-health.js'));
|
|
358
429
|
const report = run();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kodelyth-ecc",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.4",
|
|
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",
|
|
@@ -172,19 +172,11 @@ function extractCandidates(jsonlPath) {
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
if (!SUCCESS_PHRASES.some(rx => rx.test(text))) continue;
|
|
181
|
-
|
|
182
|
-
const score = scoreCandidate(events, i);
|
|
183
|
-
if (score < 3) continue;
|
|
184
|
-
|
|
185
|
-
// Look back to find the problem and approach
|
|
186
|
-
const window = events.slice(Math.max(0, i - 20), i);
|
|
187
|
-
const problemEvent = window.find(e => e.role === 'user');
|
|
175
|
+
// Build a candidate from the window of events before a "solved" moment at
|
|
176
|
+
// index `i`. Returns null if there's no clear problem + approach.
|
|
177
|
+
function buildAt(i, score) {
|
|
178
|
+
const window = events.slice(Math.max(0, i - 20), i + 1);
|
|
179
|
+
const problemEvent = window.find(e => e.role === 'user' && extractText(e).trim());
|
|
188
180
|
const problem = problemEvent ? extractText(problemEvent).split('\n')[0].slice(0, 280) : null;
|
|
189
181
|
|
|
190
182
|
const filesTouched = Array.from(new Set(
|
|
@@ -194,26 +186,48 @@ function extractCandidates(jsonlPath) {
|
|
|
194
186
|
.filter(Boolean)
|
|
195
187
|
)).slice(0, 5);
|
|
196
188
|
|
|
197
|
-
//
|
|
198
|
-
// tool_use/tool_result events which carry no explanation of the fix.
|
|
199
|
-
const reversed = window.slice().reverse();
|
|
189
|
+
// Last assistant message with actual explanatory TEXT.
|
|
200
190
|
let approach = null;
|
|
201
|
-
for (const e of
|
|
191
|
+
for (const e of window.slice().reverse()) {
|
|
202
192
|
if (e.role !== 'assistant') continue;
|
|
203
193
|
const t = extractText(e).trim();
|
|
204
194
|
if (t) { approach = t.slice(0, 600); break; }
|
|
205
195
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
candidates.push({
|
|
210
|
-
problem,
|
|
211
|
-
approach,
|
|
196
|
+
if (!problem || !approach) return null;
|
|
197
|
+
return {
|
|
198
|
+
problem, approach,
|
|
212
199
|
tags: extractTags(`${problem} ${approach}`),
|
|
213
200
|
files: filesTouched,
|
|
214
201
|
language: detectLanguage(filesTouched),
|
|
215
202
|
score,
|
|
216
|
-
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const PASS_SIGNAL = /\bexit code 0\b|\btests? passed\b|\ball (tests )?pass(ed)?\b|\b0 failing\b|\bbuild succeeded\b|✓|PASS\b/i;
|
|
207
|
+
|
|
208
|
+
for (let i = 0; i < events.length; i++) {
|
|
209
|
+
const ev = events[i];
|
|
210
|
+
|
|
211
|
+
// Path 1 — user says it worked ("that worked", "perfect", "thanks", ...).
|
|
212
|
+
if (ev.role === 'user' && SUCCESS_PHRASES.some(rx => rx.test(extractText(ev)))) {
|
|
213
|
+
const score = scoreCandidate(events, i);
|
|
214
|
+
if (score >= 3) { const c = buildAt(i, score); if (c) candidates.push(c); }
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Path 2 (aggressive, v2.5.4+) — a passing test / successful build that
|
|
219
|
+
// FOLLOWS at least one edit, even without an explicit user "thanks". This
|
|
220
|
+
// catches real fixes the user verified but never verbally acknowledged.
|
|
221
|
+
// Opt out with KODELYTH_CAPTURE_AGGRESSIVE=0.
|
|
222
|
+
if (process.env.KODELYTH_CAPTURE_AGGRESSIVE !== '0'
|
|
223
|
+
&& PASS_SIGNAL.test(extractText(ev))) {
|
|
224
|
+
const priorWindow = events.slice(Math.max(0, i - 12), i);
|
|
225
|
+
const hadEdit = priorWindow.some(e => e.tool_name === 'Edit' || e.tool_name === 'Write');
|
|
226
|
+
if (hadEdit) {
|
|
227
|
+
const score = scoreCandidate(events, i) + 2; // verification is a strong signal
|
|
228
|
+
if (score >= 3) { const c = buildAt(i, score); if (c) candidates.push(c); }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
217
231
|
}
|
|
218
232
|
|
|
219
233
|
// Dedupe by problem
|