kodelyth-ecc 1.4.0 → 1.4.1
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/.github/workflows/ci.yml +7 -8
- package/CHANGELOG.md +32 -0
- package/README.md +11 -3
- package/VERSION +1 -1
- package/hooks/hooks.json +15 -0
- package/hooks/memory/auto-recall.js +141 -0
- package/package.json +3 -2
- package/tests/memory/auto-recall.test.js +132 -0
package/.github/workflows/ci.yml
CHANGED
|
@@ -34,13 +34,12 @@ jobs:
|
|
|
34
34
|
- name: Run test suite
|
|
35
35
|
run: node tests/run-all.js
|
|
36
36
|
|
|
37
|
-
- name: Verify
|
|
38
|
-
run: node --check dashboard/server.js
|
|
39
|
-
|
|
40
|
-
- name: Verify all readers syntax
|
|
37
|
+
- name: Verify memory module syntax
|
|
41
38
|
run: |
|
|
42
|
-
node --
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
node --check scripts/memory/store.js
|
|
40
|
+
node --check scripts/memory/inject.js
|
|
41
|
+
node --check scripts/memory/extract.js
|
|
42
|
+
node --check scripts/memory/cli.js
|
|
43
|
+
node --check hooks/memory/inject-start.js
|
|
44
|
+
node --check hooks/memory/capture-stop.js
|
|
46
45
|
shell: bash
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Kodelyth ECC are documented here.
|
|
4
4
|
|
|
5
|
+
## v1.4.1 — Auto Chat Detection for Memory (May 2026)
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **`UserPromptSubmit` hook: `hooks/memory/auto-recall.js`** — watches every prompt in real-time, runs BM25 search against your memory, and injects relevant matches into the AI's context **before it responds**
|
|
9
|
+
- **Per-session repeat suppression** — never surfaces the same memory twice in one session (state at `~/.kodelyth/memory/session-surfaced-<id>.json`)
|
|
10
|
+
- **Smart skip filters** — skips trivial prompts (`ok`, `yes`, `thanks`), agent invocations (`use foo`, `@bar`, `/help`), and prompts under 12 chars
|
|
11
|
+
- **9 new tests** for the auto-recall hook (47 total passing — 26 existing + 12 memory + 9 auto-recall)
|
|
12
|
+
|
|
13
|
+
### How it changes the experience
|
|
14
|
+
Before v1.4.1: memory only injected once at session start, then the AI had to consciously decide to query it.
|
|
15
|
+
|
|
16
|
+
After v1.4.1: every meaningful prompt the user types triggers an instant memory check. Relevant past solutions appear in the AI's context for that turn, automatically. No agent invocation needed. No `/memory recall` needed.
|
|
17
|
+
|
|
18
|
+
### Example
|
|
19
|
+
```
|
|
20
|
+
You: "I need to add Stripe webhooks for subscription renewals"
|
|
21
|
+
└─ Auto-recall fires → finds your March 2026 memory →
|
|
22
|
+
injects: "raw body parser, validate with constructEvent,
|
|
23
|
+
test with stripe-cli not curl"
|
|
24
|
+
AI: "I checked your memory — you solved this exact problem in March.
|
|
25
|
+
The approach that worked was raw body parser before
|
|
26
|
+
signature validation. Want me to apply the same pattern?"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You never typed `/memory recall`. The AI just knew.
|
|
30
|
+
|
|
31
|
+
### Honest limits
|
|
32
|
+
- The hook fires on Claude Code's `UserPromptSubmit` event. Other platforms (Cursor, Codex, Windsurf, Antigravity) don't currently expose an equivalent pre-prompt hook — auto-recall there is limited to SessionStart injection.
|
|
33
|
+
- Repeat suppression is per-session-id. If your platform doesn't pass a stable session_id, the same memory may surface twice across sessions (still better than none).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
5
37
|
## v1.4.0 — Local Self-Learning Memory (May 2026)
|
|
6
38
|
|
|
7
39
|
### Added — Kodelyth Memory
|
package/README.md
CHANGED
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|

|
|
9
9
|

|
|
10
10
|

|
|
11
|
-

|
|
12
12
|

|
|
13
13
|

|
|
14
14
|

|
|
15
|
+

|
|
15
16
|
|
|
16
17
|
</div>
|
|
17
18
|
|
|
@@ -180,7 +181,8 @@ Session 2 (August, new project):
|
|
|
180
181
|
| **Capture** | A Stop hook scans your session JSONL, extracts (problem, approach, gotchas, tags). Queues for your review — never auto-stores. |
|
|
181
182
|
| **Storage** | `~/.kodelyth/memory/memories.jsonl` — append-only log on your disk only. Override with `KODELYTH_MEMORY_DIR`. |
|
|
182
183
|
| **Retrieval** | BM25 keyword + tag search. Pure JS, sub-millisecond, no embeddings, no network. |
|
|
183
|
-
| **
|
|
184
|
+
| **Session-start injection** | A SessionStart hook builds a cache-friendly context block: stable prefix (your patterns + recent project memories) → variable suffix (relevant to current task). |
|
|
185
|
+
| **Auto chat detection** *(v1.4.1)* | A `UserPromptSubmit` hook watches every message you type, runs BM25 search on it, and injects relevant memories **before the AI responds**. Per-session repeat suppression. Skips trivial / agent-command prompts. |
|
|
184
186
|
| **Cost win** | The stable prefix sits in the prompt cache. Anthropic charges 10% on cached tokens (5-min TTL); OpenAI auto-caches prefixes ≥1024 tokens. Long sessions become dramatically cheaper. |
|
|
185
187
|
|
|
186
188
|
### Privacy
|
|
@@ -453,7 +455,13 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for templates, checklists, and the full K
|
|
|
453
455
|
|
|
454
456
|
## Changelog
|
|
455
457
|
|
|
456
|
-
See [CHANGELOG.md](CHANGELOG.md). v1.4.
|
|
458
|
+
See [CHANGELOG.md](CHANGELOG.md). v1.4.1 highlights:
|
|
459
|
+
|
|
460
|
+
- **Added:** Auto chat detection — `UserPromptSubmit` hook watches every prompt, auto-injects relevant memories
|
|
461
|
+
- **Added:** Per-session repeat suppression (no memory shown twice in same session)
|
|
462
|
+
- **Added:** 9 new tests (47 total passing)
|
|
463
|
+
|
|
464
|
+
v1.4.0 highlights:
|
|
457
465
|
|
|
458
466
|
- **Added:** `kodelyth-memory` — local self-learning memory (BM25, zero deps, cache-friendly)
|
|
459
467
|
- **Added:** SessionStart hook injects relevant past memories; Stop hook queues candidates for review
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.4.
|
|
1
|
+
1.4.1
|
package/hooks/hooks.json
CHANGED
|
@@ -139,6 +139,21 @@
|
|
|
139
139
|
"id": "pre:mcp-health-check"
|
|
140
140
|
}
|
|
141
141
|
],
|
|
142
|
+
"UserPromptSubmit": [
|
|
143
|
+
{
|
|
144
|
+
"matcher": "*",
|
|
145
|
+
"hooks": [
|
|
146
|
+
{
|
|
147
|
+
"type": "command",
|
|
148
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/memory/auto-recall.js\"",
|
|
149
|
+
"async": false,
|
|
150
|
+
"timeout": 3
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
"description": "Kodelyth Memory: auto-detect topic from each user prompt and inject relevant past memories before the AI responds (suppresses repeats per session)",
|
|
154
|
+
"id": "kodelyth:user-prompt:memory-auto-recall"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
142
157
|
"PreCompact": [
|
|
143
158
|
{
|
|
144
159
|
"matcher": "*",
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// Kodelyth ECC — Auto Chat Detection (Memory Auto-Recall)
|
|
4
|
+
//
|
|
5
|
+
// Triggered on UserPromptSubmit (every message the user sends).
|
|
6
|
+
// Reads the prompt, searches memory in real-time, and injects relevant
|
|
7
|
+
// matches as additional context BEFORE the AI sees the prompt.
|
|
8
|
+
//
|
|
9
|
+
// Behaviour:
|
|
10
|
+
// - Skips on prompts that are too short (< 12 chars) or trivial ("ok", "yes")
|
|
11
|
+
// - Skips on prompts that look like agent commands (`use foo`, `@bar`)
|
|
12
|
+
// - Skips when no memory exists yet
|
|
13
|
+
// - Suppresses repeats: never re-surfaces the same memory twice in a session
|
|
14
|
+
// (state file: ~/.kodelyth/memory/session-surfaced-<sessionId>.json)
|
|
15
|
+
// - Always exits 0 — never blocks the prompt because memory is unavailable
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
const MIN_PROMPT_CHARS = 12;
|
|
25
|
+
const MIN_TOKENS = 2;
|
|
26
|
+
const MAX_RECALLED = 3;
|
|
27
|
+
const MIN_SCORE = 1.0; // Higher than passive inject — we want strong signal
|
|
28
|
+
const TRIVIAL_PROMPTS = new Set(['ok', 'yes', 'no', 'thanks', 'thx', 'sure', 'go', 'cool', 'k']);
|
|
29
|
+
const SKIP_PATTERN = /^\s*(use\s+|@|\/|invoke\s+)/i;
|
|
30
|
+
|
|
31
|
+
let payload = '';
|
|
32
|
+
process.stdin.setEncoding('utf8');
|
|
33
|
+
process.stdin.on('data', chunk => { payload += chunk; });
|
|
34
|
+
process.stdin.on('end', main);
|
|
35
|
+
setTimeout(() => { if (!process.stdin.readableEnded) main(); }, 200);
|
|
36
|
+
|
|
37
|
+
function main() {
|
|
38
|
+
try {
|
|
39
|
+
const data = payload ? safeJson(payload) : {};
|
|
40
|
+
const userPrompt = (data.prompt || data.user_prompt || data.text || '').trim();
|
|
41
|
+
const sessionId = data.session_id || 'unknown';
|
|
42
|
+
const projectRoot = data.cwd || process.cwd();
|
|
43
|
+
|
|
44
|
+
if (!shouldRecall(userPrompt)) return done({});
|
|
45
|
+
|
|
46
|
+
// Lazy require so the hook doesn't crash if memory module breaks
|
|
47
|
+
const { recallForProject } = require(path.join(__dirname, '..', '..', 'scripts', 'memory', 'store'));
|
|
48
|
+
|
|
49
|
+
const matches = recallForProject(projectRoot, userPrompt, {
|
|
50
|
+
limit: MAX_RECALLED * 2, // grab extra so we can filter out repeats
|
|
51
|
+
minScore: MIN_SCORE,
|
|
52
|
+
});
|
|
53
|
+
if (matches.length === 0) return done({});
|
|
54
|
+
|
|
55
|
+
// Filter out memories already surfaced this session
|
|
56
|
+
const surfacedFile = surfacedStatePath(sessionId);
|
|
57
|
+
const surfaced = loadSurfaced(surfacedFile);
|
|
58
|
+
const fresh = matches.filter(m => !surfaced.has(m.id)).slice(0, MAX_RECALLED);
|
|
59
|
+
if (fresh.length === 0) return done({});
|
|
60
|
+
|
|
61
|
+
// Mark the freshly surfaced memories so they don't re-appear this session
|
|
62
|
+
for (const m of fresh) surfaced.add(m.id);
|
|
63
|
+
saveSurfaced(surfacedFile, surfaced);
|
|
64
|
+
|
|
65
|
+
const block = formatBlock(fresh, userPrompt);
|
|
66
|
+
done({
|
|
67
|
+
additionalContext: block,
|
|
68
|
+
meta: {
|
|
69
|
+
source: 'kodelyth-memory:auto-recall',
|
|
70
|
+
recalledCount: fresh.length,
|
|
71
|
+
surfacedTotal: surfaced.size,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
process.stderr.write(`kodelyth-memory auto-recall: ${err.message}\n`);
|
|
76
|
+
done({});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function shouldRecall(prompt) {
|
|
81
|
+
if (!prompt || prompt.length < MIN_PROMPT_CHARS) return false;
|
|
82
|
+
if (TRIVIAL_PROMPTS.has(prompt.toLowerCase().trim())) return false;
|
|
83
|
+
if (SKIP_PATTERN.test(prompt)) return false;
|
|
84
|
+
// Token gate — need at least N meaningful words
|
|
85
|
+
const meaningful = prompt
|
|
86
|
+
.toLowerCase()
|
|
87
|
+
.split(/\s+/)
|
|
88
|
+
.filter(w => w.length >= 4);
|
|
89
|
+
return meaningful.length >= MIN_TOKENS;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatBlock(memories, userPrompt) {
|
|
93
|
+
const lines = [];
|
|
94
|
+
lines.push('## Kodelyth Memory — relevant past solutions');
|
|
95
|
+
lines.push('');
|
|
96
|
+
lines.push(`Auto-detected from your message: "${userPrompt.slice(0, 100).replace(/\n/g, ' ')}${userPrompt.length > 100 ? '...' : ''}"`);
|
|
97
|
+
lines.push('');
|
|
98
|
+
for (const m of memories) {
|
|
99
|
+
lines.push(`- **${m.problem}**`);
|
|
100
|
+
if (m.approach) lines.push(` Approach: ${m.approach.split('\n')[0].slice(0, 240)}`);
|
|
101
|
+
if (m.gotchas?.length) lines.push(` Gotcha: ${m.gotchas[0].slice(0, 200)}`);
|
|
102
|
+
if (m.tags?.length) lines.push(` Tags: ${m.tags.slice(0, 5).join(', ')}`);
|
|
103
|
+
lines.push(` (memory id: ${m.id} · captured ${m.captured_at?.slice(0, 10)})`);
|
|
104
|
+
}
|
|
105
|
+
lines.push('');
|
|
106
|
+
lines.push('> Surface these to the user before answering only if they are genuinely relevant to the current task. If not, ignore silently — do not force-fit a memory.');
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function surfacedStatePath(sessionId) {
|
|
111
|
+
const dir = process.env.KODELYTH_MEMORY_DIR
|
|
112
|
+
|| path.join(os.homedir(), '.kodelyth', 'memory');
|
|
113
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
114
|
+
return path.join(dir, `session-surfaced-${sessionId}.json`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function loadSurfaced(file) {
|
|
118
|
+
try {
|
|
119
|
+
if (!fs.existsSync(file)) return new Set();
|
|
120
|
+
return new Set(JSON.parse(fs.readFileSync(file, 'utf8')));
|
|
121
|
+
} catch {
|
|
122
|
+
return new Set();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function saveSurfaced(file, set) {
|
|
127
|
+
try {
|
|
128
|
+
fs.writeFileSync(file, JSON.stringify(Array.from(set)));
|
|
129
|
+
} catch {
|
|
130
|
+
/* non-fatal */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function safeJson(s) {
|
|
135
|
+
try { return JSON.parse(s); } catch { return {}; }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function done(obj) {
|
|
139
|
+
if (Object.keys(obj).length > 0) process.stdout.write(JSON.stringify(obj));
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kodelyth-ecc",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "Production-grade AI coding toolkit — 59 agents, 188 skills,
|
|
3
|
+
"version": "1.4.1",
|
|
4
|
+
"description": "Production-grade AI coding toolkit — 59 agents, 188 skills, 80 commands, god-tier intent routing, local self-learning memory with auto chat detection. Works with Claude Code, Windsurf, Cursor, Codex, Antigravity, and OpenCode.",
|
|
5
5
|
"author": "Kodelyth <github.com/sifxprime>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"llm",
|
|
22
22
|
"memory",
|
|
23
23
|
"self-learning",
|
|
24
|
+
"auto-recall",
|
|
24
25
|
"kodelyth"
|
|
25
26
|
],
|
|
26
27
|
"bin": {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Tests for hooks/memory/auto-recall.js — UserPromptSubmit auto chat detection
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const test = require('node:test');
|
|
5
|
+
const assert = require('node:assert/strict');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { spawnSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
const TMP = fs.mkdtempSync(path.join(os.tmpdir(), 'kodelyth-recall-'));
|
|
12
|
+
process.env.KODELYTH_MEMORY_DIR = TMP;
|
|
13
|
+
|
|
14
|
+
const HOOK = path.join(__dirname, '..', '..', 'hooks', 'memory', 'auto-recall.js');
|
|
15
|
+
const store = require('../../scripts/memory/store');
|
|
16
|
+
|
|
17
|
+
function runHook(payload) {
|
|
18
|
+
const res = spawnSync(process.execPath, [HOOK], {
|
|
19
|
+
input: JSON.stringify(payload),
|
|
20
|
+
encoding: 'utf8',
|
|
21
|
+
env: { ...process.env, KODELYTH_MEMORY_DIR: TMP },
|
|
22
|
+
timeout: 5000,
|
|
23
|
+
});
|
|
24
|
+
return { stdout: res.stdout || '', stderr: res.stderr || '', status: res.status };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Seed memory once
|
|
28
|
+
store.capture({
|
|
29
|
+
problem: 'Stripe webhook signature failed in production',
|
|
30
|
+
approach: 'Switched body parser from json to raw, validated with constructEvent',
|
|
31
|
+
tags: ['payments', 'stripe', 'webhooks'],
|
|
32
|
+
project: '/test/payments',
|
|
33
|
+
language: 'typescript',
|
|
34
|
+
});
|
|
35
|
+
store.capture({
|
|
36
|
+
problem: 'Redis cluster split-brain after AZ failover',
|
|
37
|
+
approach: 'Configured min-slaves-to-write to 2 to prevent stale primary writes',
|
|
38
|
+
tags: ['redis', 'cluster', 'reliability'],
|
|
39
|
+
project: '/test/payments',
|
|
40
|
+
language: 'typescript',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('auto-recall: returns nothing for empty prompt', () => {
|
|
44
|
+
const r = runHook({ prompt: '', cwd: '/test/payments', session_id: 's1' });
|
|
45
|
+
assert.equal(r.status, 0);
|
|
46
|
+
assert.equal(r.stdout, '');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('auto-recall: skips trivial prompts', () => {
|
|
50
|
+
for (const trivial of ['ok', 'yes', 'thanks', 'cool']) {
|
|
51
|
+
const r = runHook({ prompt: trivial, cwd: '/test/payments', session_id: 's-trivial' });
|
|
52
|
+
assert.equal(r.stdout, '', `should skip "${trivial}"`);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('auto-recall: skips agent invocation prompts', () => {
|
|
57
|
+
for (const cmd of ['use debug-detective', '@code-reviewer', '/help', 'invoke security-reviewer']) {
|
|
58
|
+
const r = runHook({ prompt: cmd, cwd: '/test/payments', session_id: 's-cmd' });
|
|
59
|
+
assert.equal(r.stdout, '', `should skip "${cmd}"`);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('auto-recall: skips short prompts', () => {
|
|
64
|
+
const r = runHook({ prompt: 'fix bug', cwd: '/test/payments', session_id: 's-short' });
|
|
65
|
+
assert.equal(r.stdout, '');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('auto-recall: surfaces relevant memory for a real query', () => {
|
|
69
|
+
const r = runHook({
|
|
70
|
+
prompt: 'I need to add Stripe webhooks for subscription renewal events',
|
|
71
|
+
cwd: '/test/payments',
|
|
72
|
+
session_id: 's-fire-1',
|
|
73
|
+
});
|
|
74
|
+
assert.equal(r.status, 0);
|
|
75
|
+
assert.ok(r.stdout.length > 0, 'should produce output');
|
|
76
|
+
const out = JSON.parse(r.stdout);
|
|
77
|
+
assert.ok(out.additionalContext.includes('Stripe webhook'));
|
|
78
|
+
assert.equal(out.meta.source, 'kodelyth-memory:auto-recall');
|
|
79
|
+
assert.equal(out.meta.recalledCount, 1);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('auto-recall: suppresses repeat in same session', () => {
|
|
83
|
+
// First call surfaces the memory
|
|
84
|
+
runHook({
|
|
85
|
+
prompt: 'help with stripe webhook integration',
|
|
86
|
+
cwd: '/test/payments',
|
|
87
|
+
session_id: 's-repeat',
|
|
88
|
+
});
|
|
89
|
+
// Second call same topic — should NOT surface again
|
|
90
|
+
const r2 = runHook({
|
|
91
|
+
prompt: 'about that stripe webhook problem, what about retries',
|
|
92
|
+
cwd: '/test/payments',
|
|
93
|
+
session_id: 's-repeat',
|
|
94
|
+
});
|
|
95
|
+
assert.equal(r2.stdout, '', 'repeat should be suppressed');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('auto-recall: surfaces different memory for different topic in same session', () => {
|
|
99
|
+
runHook({
|
|
100
|
+
prompt: 'how do I handle stripe webhook errors',
|
|
101
|
+
cwd: '/test/payments',
|
|
102
|
+
session_id: 's-multi',
|
|
103
|
+
});
|
|
104
|
+
// Different topic — redis — should surface
|
|
105
|
+
const r2 = runHook({
|
|
106
|
+
prompt: 'redis cluster is acting weird after failover today',
|
|
107
|
+
cwd: '/test/payments',
|
|
108
|
+
session_id: 's-multi',
|
|
109
|
+
});
|
|
110
|
+
assert.ok(r2.stdout.length > 0, 'different topic should surface');
|
|
111
|
+
const out = JSON.parse(r2.stdout);
|
|
112
|
+
assert.ok(out.additionalContext.toLowerCase().includes('redis'));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('auto-recall: returns nothing when no memory matches', () => {
|
|
116
|
+
const r = runHook({
|
|
117
|
+
prompt: 'what is the meaning of life and quantum entanglement',
|
|
118
|
+
cwd: '/test/payments',
|
|
119
|
+
session_id: 's-nomatch',
|
|
120
|
+
});
|
|
121
|
+
assert.equal(r.stdout, '');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('auto-recall: never crashes on malformed payload', () => {
|
|
125
|
+
const res = spawnSync(process.execPath, [HOOK], {
|
|
126
|
+
input: 'not-json-at-all',
|
|
127
|
+
encoding: 'utf8',
|
|
128
|
+
env: { ...process.env, KODELYTH_MEMORY_DIR: TMP },
|
|
129
|
+
timeout: 3000,
|
|
130
|
+
});
|
|
131
|
+
assert.equal(res.status, 0, 'must exit 0 even on garbage input');
|
|
132
|
+
});
|