claude-recall 0.32.0 → 0.34.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/README.md +12 -8
- package/dist/cli/commands/kiro-commands.js +43 -23
- package/dist/hooks/kiro-hooks.js +138 -22
- package/dist/hooks/llm-classifier.js +11 -10
- package/dist/hooks/shared.js +24 -30
- package/docs/2025-08-07-launch-article-linkedin.md +83 -0
- package/docs/2026-07-follow-up-article-draft.md +68 -0
- package/docs/2026-07-follow-up-article-linkedin-tight.md +46 -0
- package/docs/cc-llm-capture.md +147 -0
- package/docs/kiro-llm-capture.md +15 -11
- package/docs/kiro.md +9 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ That's the whole workflow. A background hook classified your prompt with an LLM
|
|
|
28
28
|
>
|
|
29
29
|
> **Agent:** Installing vitest with **pnpm** *(applied from memory: "Use pnpm, not npm")* …
|
|
30
30
|
|
|
31
|
-
Your rules are injected at session start and
|
|
31
|
+
Your rules are injected at session start and kept alive mid-session — just-in-time before each relevant tool call (Claude Code, Pi), or as a periodic refresh (Kiro) — at the moment of decision, not 50,000 tokens upstream. This works across agents too: correct Claude Code on Tuesday, and Kiro applies it on Friday.
|
|
32
32
|
|
|
33
33
|
**And you can audit what it knows at any time:**
|
|
34
34
|
|
|
@@ -45,7 +45,7 @@ $ claude-recall search "pnpm"
|
|
|
45
45
|
## Features
|
|
46
46
|
|
|
47
47
|
- **Automatic capture** — an LLM classifier detects preferences, corrections, and project facts in your normal prompts, running on **the agent's own LLM** (Claude subscription / Kiro credits — never a separate API key), with regex fallback when no LLM is available
|
|
48
|
-
- **Applied where it counts** — rules load at session start
|
|
48
|
+
- **Applied where it counts** — rules load at session start and are re-surfaced mid-session: just-in-time before each tool call (Claude Code, Pi) or as a periodic refresh every N prompts (Kiro)
|
|
49
49
|
- **Project-scoped** — each project gets its own memory namespace; switch directories and the agent switches context
|
|
50
50
|
- **Learns from failures** — records what broke, why, and what fixed it, so mistakes aren't repeated
|
|
51
51
|
- **Outcome-aware** — tracks whether rules actually help (tool results, test cycles, re-asks) and promotes validated lessons into active rules
|
|
@@ -92,6 +92,8 @@ tail -5 ~/.claude-recall/hook-logs/cc-classifier.log # which model ran, what w
|
|
|
92
92
|
claude-recall search "something you said"
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
Design details (the `claude -p` key-precedence gotcha, recursion guards, which features run on the subscription): [docs/cc-llm-capture.md](docs/cc-llm-capture.md).
|
|
96
|
+
|
|
95
97
|
### Pi
|
|
96
98
|
|
|
97
99
|
```bash
|
|
@@ -160,7 +162,8 @@ Once installed, Claude Recall works in the background (CC = Claude Code):
|
|
|
160
162
|
|---|---|:-:|:-:|:-:|
|
|
161
163
|
| **Session start** | Active rules are injected into the agent's context | ✓ | ✓ | ✓ |
|
|
162
164
|
| **As you type** | Prompts are classified; durable preferences/corrections are stored | ✓ | ✓ | ✓ |
|
|
163
|
-
| **Before each tool call** | Relevant rules are re-surfaced next to the action (just-in-time injection) | ✓ | ✓ |
|
|
165
|
+
| **Before each tool call** | Relevant rules are re-surfaced next to the action (just-in-time injection) | ✓ | ✓ | |
|
|
166
|
+
| **Every 15 prompts** | Active rules are re-injected so long sessions can't silently lose them (interval configurable) | | | ✓ |
|
|
164
167
|
| **Tool outcomes** | Failures are recorded; Bash failures are paired with their eventual fix | ✓ | ✓ | ✓ |
|
|
165
168
|
| **Re-ask detection** | Frustration signals (*"still broken"*) are recorded as outcome events | ✓ | ✓ | ✓ |
|
|
166
169
|
| **Before context compression** | Important context is captured before the window shrinks | ✓ | ✓ | |
|
|
@@ -169,7 +172,7 @@ Once installed, Claude Recall works in the background (CC = Claude Code):
|
|
|
169
172
|
| **Session exit** | An auto-checkpoint (`{completed, remaining, blockers}`) is saved for next time | ✓ | ✓ | |
|
|
170
173
|
| **End of session** | Failure patterns become candidate lessons; validated ones are promoted to rules | ✓ | ✓ | |
|
|
171
174
|
|
|
172
|
-
Classification runs on each runtime's **own** LLM — Claude Code via headless `claude -p` on your subscription; Kiro via `kiro-cli chat --no-interactive` on Kiro credits — with `ANTHROPIC_API_KEY`
|
|
175
|
+
Classification runs on each runtime's **own** LLM — Claude Code via headless `claude -p` on your subscription; Kiro via `kiro-cli chat --no-interactive` on Kiro credits — with regex as the fallback. **An exported `ANTHROPIC_API_KEY` is never touched** unless you explicitly opt in with `CLAUDE_RECALL_PREFER_API_KEY=1`. No API key is ever required; no configuration needed.
|
|
173
176
|
|
|
174
177
|
```bash
|
|
175
178
|
# Verify it's working
|
|
@@ -206,7 +209,7 @@ claude-recall checkpoint save --completed "API layer" --remaining "wire the UI"
|
|
|
206
209
|
claude-recall checkpoint load
|
|
207
210
|
```
|
|
208
211
|
|
|
209
|
-
Auto-checkpoints are also saved on session exit in Claude Code and Pi (Pi has no `--resume`, so this is its main recovery path). Extraction runs on your **Claude subscription** (headless `claude -p`) — like capture, no API key needed
|
|
212
|
+
Auto-checkpoints are also saved on session exit in Claude Code and Pi (Pi has no `--resume`, so this is its main recovery path). Extraction runs on your **Claude subscription** (headless `claude -p`) — like capture, no API key needed and no key touched. The same applies to the other background LLM features (failure hindsight hints, end-of-session lesson extraction). Pi-only machines without the `claude` binary can opt in to an `ANTHROPIC_API_KEY` with `CLAUDE_RECALL_PREFER_API_KEY=1`. A quality gate refuses to overwrite a manual checkpoint with a fabricated one when the task was already complete.
|
|
210
213
|
|
|
211
214
|
### Troubleshooting
|
|
212
215
|
|
|
@@ -323,7 +326,7 @@ action → outcome event → episode → candidate lesson → promotion → acti
|
|
|
323
326
|
outcome resolved per injected rule
|
|
324
327
|
```
|
|
325
328
|
|
|
326
|
-
Failures become candidate lessons (deduplicated by similarity); lessons seen 2+ times (or once, if severe) are promoted to active rules; every just-in-time injection is recorded and resolved against the tool's outcome, building per-rule effectiveness data over time.
|
|
329
|
+
Failures become candidate lessons (deduplicated by similarity); lessons seen 2+ times (or once, if severe) are promoted to active rules; every just-in-time injection (Claude Code, Pi) is recorded and resolved against the tool's outcome, building per-rule effectiveness data over time.
|
|
327
330
|
|
|
328
331
|
---
|
|
329
332
|
|
|
@@ -387,12 +390,13 @@ Defaults work out of the box; tune via environment variables as needed.
|
|
|
387
390
|
| Variable | Default | Effect |
|
|
388
391
|
| ---------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
|
|
389
392
|
| `CLAUDE_RECALL_DB_PATH` | `~/.claude-recall/` | Database directory. |
|
|
390
|
-
| `ANTHROPIC_API_KEY` | _(unset)_ | Optional personal API key for Haiku-based LLM features. **Never required
|
|
393
|
+
| `ANTHROPIC_API_KEY` | _(unset)_ | Optional personal API key for Haiku-based LLM features. **Never required, never provided by Claude Code, and never touched unless you opt in** with `CLAUDE_RECALL_PREFER_API_KEY=1` — capture, checkpoint extraction, hindsight hints, and session lessons all run on each runtime's own LLM (Claude subscription via `claude -p`; Kiro credits via `kiro-cli`), with regex as the fallback. |
|
|
391
394
|
| `CLAUDE_RECALL_CC_MODEL` | `haiku` | Dedicated model for capture classification under Claude Code (passed to `claude -p --model`) — independent of your interactive session model. |
|
|
392
395
|
| `CLAUDE_RECALL_CC_LLM_TIMEOUT_MS` | `30000` | Hard cap on the headless `claude -p` classify call before the capture worker gives up and falls through. |
|
|
393
396
|
| `CLAUDE_RECALL_KIRO_MODEL` | `claude-haiku-4.5` | Dedicated model for Kiro-LLM capture classification — **independent of your interactive Kiro chat model**. Raise to `claude-sonnet-4.6` for steadier judgement at more credits. See [docs/kiro-llm-capture.md](docs/kiro-llm-capture.md). |
|
|
394
|
-
| `CLAUDE_RECALL_PREFER_API_KEY` | _(unset)_ |
|
|
397
|
+
| `CLAUDE_RECALL_PREFER_API_KEY` | _(unset)_ | **The only switch that enables the `ANTHROPIC_API_KEY` backend** (and prefers it first). For Pi-only machines without the `claude` binary, or a stronger model you deliberately pay for. Applies to all LLM features, under Claude Code, Kiro, and Pi. |
|
|
395
398
|
| `CLAUDE_RECALL_KIRO_LLM_TIMEOUT_MS` | `30000` | Hard cap on the headless `kiro-cli` classify call before the capture worker gives up and falls back to regex. |
|
|
399
|
+
| `CLAUDE_RECALL_REFRESH_INTERVAL` | `15` | **Kiro only.** Re-inject the active rules into context every N prompts, so marathon sessions can't silently lose them to context rollover (Kiro has no post-compaction event). `0` disables. |
|
|
396
400
|
| `CLAUDE_RECALL_LOAD_BUDGET_TOKENS` | `2000` | Token budget for the `load_rules` payload. Rules are emitted in priority order (corrections → preferences by citation → devops by citation → failures) and dropped rules surface via `search_memory`. |
|
|
397
401
|
| `CLAUDE_RECALL_AUTO_DEMOTE` | `false` | When `true`, auto-demote rules on MCP boot where `load_count >= CLAUDE_RECALL_DEMOTE_MIN_LOADS`, `cite_count = 0`, and age `> CLAUDE_RECALL_DEMOTE_MIN_AGE_DAYS`. Still reversible via `rules promote <id>`. |
|
|
398
402
|
| `CLAUDE_RECALL_DEMOTE_MIN_LOADS` | `20` | Minimum load count before a rule qualifies for auto-demotion. |
|
|
@@ -44,7 +44,7 @@ const repair_1 = require("./repair");
|
|
|
44
44
|
* `claude-recall kiro setup` writes a Kiro custom agent config
|
|
45
45
|
* (.kiro/agents/recall.json) wiring the same shared memory database into
|
|
46
46
|
* Kiro CLI: MCP tools, rules auto-loaded into context at agentSpawn,
|
|
47
|
-
*
|
|
47
|
+
* user-prompt capture with a periodic mid-session rule refresh, and tool
|
|
48
48
|
* outcome tracking. See src/hooks/kiro-hooks.ts for the adapter details and
|
|
49
49
|
* kiro.dev/docs/cli/custom-agents for the config format.
|
|
50
50
|
*/
|
|
@@ -81,23 +81,31 @@ class KiroCommands {
|
|
|
81
81
|
fs.writeFileSync(p, JSON.stringify(KiroCommands.buildClassifierAgentConfig(), null, 2) + '\n');
|
|
82
82
|
return p;
|
|
83
83
|
}
|
|
84
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* The lifecycle hook entries (shared by fresh config and merge).
|
|
86
|
+
*
|
|
87
|
+
* No preToolUse entry: Kiro ignores preToolUse stdout (exit codes gate the
|
|
88
|
+
* tool; only exit-2 stderr reaches the LLM — kiro.dev/docs/cli/hooks), so
|
|
89
|
+
* the kiro-rule-injector wired there by versions ≤0.33 never reached the
|
|
90
|
+
* model. Mid-session injection rides userPromptSubmit instead (the periodic
|
|
91
|
+
* rule refresh inside kiro-capture); stale kiro-rule-injector entries are
|
|
92
|
+
* stripped on merge.
|
|
93
|
+
*/
|
|
85
94
|
static buildHookEntries(hookCmd) {
|
|
86
95
|
return {
|
|
87
96
|
// stdout of agentSpawn is added to context → rules present from turn one
|
|
88
97
|
agentSpawn: [
|
|
89
98
|
{ command: `${hookCmd} kiro-agent-spawn`, timeout_ms: 10000 },
|
|
90
99
|
],
|
|
91
|
-
// Capture. kiro-capture spawns a detached worker
|
|
92
|
-
// prompt via Kiro's own headless LLM (no
|
|
93
|
-
// stores in the background — see
|
|
94
|
-
// itself returns in milliseconds,
|
|
100
|
+
// Capture + periodic rule refresh. kiro-capture spawns a detached worker
|
|
101
|
+
// that classifies the prompt via Kiro's own headless LLM (no
|
|
102
|
+
// ANTHROPIC_API_KEY needed) and stores in the background — see
|
|
103
|
+
// src/hooks/kiro-classifier.ts. The hook itself returns in milliseconds,
|
|
104
|
+
// so the short timeout is ample. Its stdout is added to context, which
|
|
105
|
+
// also carries the every-N-prompts rule re-injection for long sessions.
|
|
95
106
|
userPromptSubmit: [
|
|
96
107
|
{ command: `${hookCmd} kiro-capture`, timeout_ms: 8000 },
|
|
97
108
|
],
|
|
98
|
-
preToolUse: [
|
|
99
|
-
{ matcher: '*', command: `${hookCmd} kiro-rule-injector`, timeout_ms: 5000 },
|
|
100
|
-
],
|
|
101
109
|
postToolUse: [
|
|
102
110
|
{ matcher: '*', command: `${hookCmd} kiro-tool-outcome`, timeout_ms: 5000 },
|
|
103
111
|
],
|
|
@@ -106,7 +114,7 @@ class KiroCommands {
|
|
|
106
114
|
static buildAgentConfig(hookCmd, mcpCommand, mcpArgs) {
|
|
107
115
|
return {
|
|
108
116
|
name: 'recall',
|
|
109
|
-
description: 'Kiro agent with Claude Recall persistent memory: rules auto-loaded at start,
|
|
117
|
+
description: 'Kiro agent with Claude Recall persistent memory: rules auto-loaded at start, periodic mid-session rule refresh, automatic capture of corrections and outcomes.',
|
|
110
118
|
// Also load any servers the user configured in .kiro/settings/mcp.json
|
|
111
119
|
includeMcpJson: true,
|
|
112
120
|
mcpServers: {
|
|
@@ -162,7 +170,6 @@ class KiroCommands {
|
|
|
162
170
|
const handlerMarkers = {
|
|
163
171
|
agentSpawn: 'kiro-agent-spawn',
|
|
164
172
|
userPromptSubmit: 'kiro-capture',
|
|
165
|
-
preToolUse: 'kiro-rule-injector',
|
|
166
173
|
postToolUse: 'kiro-tool-outcome',
|
|
167
174
|
};
|
|
168
175
|
// Superseded claude-recall hooks to strip from an event before wiring the
|
|
@@ -170,28 +177,41 @@ class KiroCommands {
|
|
|
170
177
|
// across versions. Pre-0.29 wired userPromptSubmit to `correction-detector`
|
|
171
178
|
// directly; 0.29 replaced it with `kiro-capture`. Leaving the old entry
|
|
172
179
|
// makes BOTH fire, double-capturing (often as near-duplicate memories).
|
|
180
|
+
// Versions ≤0.33 wired preToolUse to `kiro-rule-injector`, whose stdout
|
|
181
|
+
// Kiro never adds to context — a per-tool-call no-op, removed entirely.
|
|
173
182
|
// Only our own commands (containing 'claude-recall' / 'hook run') are
|
|
174
183
|
// touched — a user's unrelated hook on the same event is preserved.
|
|
175
184
|
const supersededMarkers = {
|
|
176
185
|
userPromptSubmit: ['correction-detector'],
|
|
186
|
+
preToolUse: ['kiro-rule-injector'],
|
|
177
187
|
};
|
|
178
188
|
const isOurCommand = (cmd) => cmd.includes('claude-recall') || cmd.includes('hook run');
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
const entriesByEvent = KiroCommands.buildHookEntries(hookCmd);
|
|
190
|
+
// Visit superseded-only events too (e.g. preToolUse, which we no longer
|
|
191
|
+
// wire but must still clean up), without inventing empty hook arrays in
|
|
192
|
+
// configs that never had them.
|
|
193
|
+
const allEvents = new Set([...Object.keys(entriesByEvent), ...Object.keys(supersededMarkers)]);
|
|
194
|
+
for (const event of allEvents) {
|
|
195
|
+
const entries = entriesByEvent[event] ?? [];
|
|
183
196
|
const marker = handlerMarkers[event];
|
|
184
197
|
// Strip superseded entries first (but never the current marker).
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
198
|
+
if (Array.isArray(config.hooks[event])) {
|
|
199
|
+
for (const stale of supersededMarkers[event] ?? []) {
|
|
200
|
+
if (stale === marker)
|
|
201
|
+
continue;
|
|
202
|
+
const before = config.hooks[event].length;
|
|
203
|
+
config.hooks[event] = config.hooks[event].filter((h) => !(typeof h?.command === 'string' && h.command.includes(stale) && isOurCommand(h.command)));
|
|
204
|
+
const removed = before - config.hooks[event].length;
|
|
205
|
+
if (removed > 0) {
|
|
206
|
+
changes.push(`hooks.${event}: removed ${removed} superseded ${stale} entr${removed === 1 ? 'y' : 'ies'}`);
|
|
207
|
+
}
|
|
193
208
|
}
|
|
194
209
|
}
|
|
210
|
+
if (entries.length === 0)
|
|
211
|
+
continue;
|
|
212
|
+
if (!Array.isArray(config.hooks[event])) {
|
|
213
|
+
config.hooks[event] = [];
|
|
214
|
+
}
|
|
195
215
|
const alreadyWired = config.hooks[event].some((h) => typeof h?.command === 'string' && h.command.includes(marker));
|
|
196
216
|
if (alreadyWired) {
|
|
197
217
|
changes.push(`hooks.${event}: ${marker} already wired — skipped`);
|
package/dist/hooks/kiro-hooks.js
CHANGED
|
@@ -12,11 +12,15 @@
|
|
|
12
12
|
* understand. fs_write inputs use `path`, mapped to `file_path`.
|
|
13
13
|
* 2. PostToolUse carries `tool_response` (an object), not a `tool_output`
|
|
14
14
|
* string.
|
|
15
|
-
* 3. Hook stdout
|
|
16
|
-
* hookSpecificOutput JSON envelope
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* 3. Hook stdout reaches the model's context ONLY on agentSpawn and
|
|
16
|
+
* userPromptSubmit (no hookSpecificOutput JSON envelope). preToolUse
|
|
17
|
+
* stdout is IGNORED — exit codes gate the tool call, and only exit-2
|
|
18
|
+
* stderr is shown to the LLM; postToolUse stdout is ignored too
|
|
19
|
+
* (kiro.dev/docs/cli/hooks, verified empirically 2026-07-13). So all
|
|
20
|
+
* context injection rides agentSpawn (rules up front at session start)
|
|
21
|
+
* and userPromptSubmit (periodic mid-session refresh, see
|
|
22
|
+
* emitRuleRefresh). Claude Code needs the search-enforcer dance for
|
|
23
|
+
* this; Kiro gets it for free.
|
|
20
24
|
*
|
|
21
25
|
* userPromptSubmit needs no adapter: Kiro sends { prompt, session_id, cwd },
|
|
22
26
|
* exactly what correction-detector expects — wire it directly.
|
|
@@ -26,6 +30,39 @@
|
|
|
26
30
|
* only assistant_response (no transcript file), so transcript-based failure
|
|
27
31
|
* detectors and session extraction don't run under Kiro.
|
|
28
32
|
*/
|
|
33
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
36
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
37
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
38
|
+
}
|
|
39
|
+
Object.defineProperty(o, k2, desc);
|
|
40
|
+
}) : (function(o, m, k, k2) {
|
|
41
|
+
if (k2 === undefined) k2 = k;
|
|
42
|
+
o[k2] = m[k];
|
|
43
|
+
}));
|
|
44
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
45
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
46
|
+
}) : function(o, v) {
|
|
47
|
+
o["default"] = v;
|
|
48
|
+
});
|
|
49
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
50
|
+
var ownKeys = function(o) {
|
|
51
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
52
|
+
var ar = [];
|
|
53
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
54
|
+
return ar;
|
|
55
|
+
};
|
|
56
|
+
return ownKeys(o);
|
|
57
|
+
};
|
|
58
|
+
return function (mod) {
|
|
59
|
+
if (mod && mod.__esModule) return mod;
|
|
60
|
+
var result = {};
|
|
61
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
62
|
+
__setModuleDefault(result, mod);
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
})();
|
|
29
66
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
67
|
exports.normalizeKiroInput = normalizeKiroInput;
|
|
31
68
|
exports.handleKiroAgentSpawn = handleKiroAgentSpawn;
|
|
@@ -34,11 +71,12 @@ exports.handleKiroToolOutcome = handleKiroToolOutcome;
|
|
|
34
71
|
exports.handleKiroCapture = handleKiroCapture;
|
|
35
72
|
exports.handleKiroCaptureWorker = handleKiroCaptureWorker;
|
|
36
73
|
const child_process_1 = require("child_process");
|
|
74
|
+
const fs = __importStar(require("fs"));
|
|
75
|
+
const path = __importStar(require("path"));
|
|
37
76
|
const shared_1 = require("./shared");
|
|
38
77
|
const directives_1 = require("../shared/directives");
|
|
39
78
|
const memory_1 = require("../services/memory");
|
|
40
79
|
const config_1 = require("../services/config");
|
|
41
|
-
const rule_injector_1 = require("./rule-injector");
|
|
42
80
|
const tool_outcome_watcher_1 = require("./tool-outcome-watcher");
|
|
43
81
|
const correction_detector_1 = require("./correction-detector");
|
|
44
82
|
const memory_tools_1 = require("../mcp/tools/memory-tools");
|
|
@@ -98,7 +136,7 @@ function formatRulesForContext() {
|
|
|
98
136
|
* Standing instruction so the agent KNOWS it has persistent memory — and,
|
|
99
137
|
* critically, that capture happens via BACKGROUND HOOKS independent of the MCP
|
|
100
138
|
* tools. Under enterprise Kiro governance the claude-recall MCP server is
|
|
101
|
-
* dropped from the toolset, but the userPromptSubmit/
|
|
139
|
+
* dropped from the toolset, but the agentSpawn/userPromptSubmit/postToolUse
|
|
102
140
|
* hooks still write to and read from the local DB. Without the "even without
|
|
103
141
|
* the tools" clause the agent answers "I can't store that, I have no memory
|
|
104
142
|
* tools" — technically true of the TOOL, but false of the system, since the
|
|
@@ -155,22 +193,18 @@ async function handleKiroAgentSpawn(_input) {
|
|
|
155
193
|
}
|
|
156
194
|
}
|
|
157
195
|
/**
|
|
158
|
-
* preToolUse — just-in-time rule
|
|
159
|
-
*
|
|
160
|
-
*
|
|
196
|
+
* preToolUse — DEPRECATED no-op. This used to emit just-in-time rule
|
|
197
|
+
* injections, on the assumption that Kiro adds preToolUse stdout to context.
|
|
198
|
+
* It does not: preToolUse stdout is ignored — exit codes gate the tool call
|
|
199
|
+
* and only exit-2 stderr reaches the LLM (kiro.dev/docs/cli/hooks, verified
|
|
200
|
+
* empirically 2026-07-13) — so nothing this handler printed ever reached the
|
|
201
|
+
* model, while every emission was falsely recorded as an injection. Mid-session
|
|
202
|
+
* injection now rides userPromptSubmit (emitRuleRefresh). The handler stays
|
|
203
|
+
* registered so agent configs wired by older versions keep exiting 0;
|
|
204
|
+
* `kiro setup` no longer wires it and strips stale entries on re-run.
|
|
161
205
|
*/
|
|
162
|
-
async function handleKiroRuleInjector(
|
|
163
|
-
|
|
164
|
-
const normalized = normalizeKiroInput(input);
|
|
165
|
-
const context = await (0, rule_injector_1.computeInjection)(normalized?.tool_name ?? '', normalized?.tool_input ?? {}, '');
|
|
166
|
-
if (context) {
|
|
167
|
-
process.stdout.write(context + '\n');
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
catch (err) {
|
|
171
|
-
// Best-effort — never block the tool call
|
|
172
|
-
(0, shared_1.hookLog)(HOOK_NAME, `rule-injector error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
173
|
-
}
|
|
206
|
+
async function handleKiroRuleInjector(_input) {
|
|
207
|
+
(0, shared_1.hookLog)(HOOK_NAME, 'kiro-rule-injector is deprecated (Kiro ignores preToolUse stdout) — no-op; re-run `claude-recall kiro setup` to unwire it');
|
|
174
208
|
}
|
|
175
209
|
/**
|
|
176
210
|
* postToolUse — outcome capture and Bash fix-pairing, delegated to the shared
|
|
@@ -184,6 +218,84 @@ async function handleKiroToolOutcome(input) {
|
|
|
184
218
|
(0, shared_1.hookLog)(HOOK_NAME, `tool-outcome error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
185
219
|
}
|
|
186
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Mid-session rule refresh — the Kiro answer to long-session context loss.
|
|
223
|
+
* Rules enter context once at agentSpawn; when Kiro later compacts or rolls
|
|
224
|
+
* the conversation, they silently vanish and nothing re-injects them (Kiro has
|
|
225
|
+
* no post-compaction event, and preToolUse stdout is ignored — see
|
|
226
|
+
* handleKiroRuleInjector). userPromptSubmit stdout IS added to context
|
|
227
|
+
* same-turn, so every CLAUDE_RECALL_REFRESH_INTERVAL prompts (default 15,
|
|
228
|
+
* 0 disables) we re-emit the active rules from here.
|
|
229
|
+
*
|
|
230
|
+
* The prompt counter is per-session (keyed by session_id) in hook-state/;
|
|
231
|
+
* stale counter files are pruned on each session's first prompt.
|
|
232
|
+
*/
|
|
233
|
+
const DEFAULT_REFRESH_INTERVAL = 15;
|
|
234
|
+
const REFRESH_STATE_PREFIX = 'kiro-refresh-';
|
|
235
|
+
const REFRESH_STATE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
236
|
+
function refreshInterval() {
|
|
237
|
+
const raw = process.env.CLAUDE_RECALL_REFRESH_INTERVAL;
|
|
238
|
+
if (raw === undefined || raw.trim() === '')
|
|
239
|
+
return DEFAULT_REFRESH_INTERVAL;
|
|
240
|
+
const n = parseInt(raw, 10);
|
|
241
|
+
if (!Number.isFinite(n))
|
|
242
|
+
return DEFAULT_REFRESH_INTERVAL;
|
|
243
|
+
return n > 0 ? n : 0;
|
|
244
|
+
}
|
|
245
|
+
/** Increment the per-session prompt counter and return the new count. */
|
|
246
|
+
function bumpPromptCount(sessionId) {
|
|
247
|
+
const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_') || 'default';
|
|
248
|
+
const file = path.join((0, shared_1.hookStateDir)(), `${REFRESH_STATE_PREFIX}${safeId}.json`);
|
|
249
|
+
let count = 0;
|
|
250
|
+
try {
|
|
251
|
+
count = JSON.parse(fs.readFileSync(file, 'utf8')).count ?? 0;
|
|
252
|
+
}
|
|
253
|
+
catch { /* first prompt of the session, or unreadable — start fresh */ }
|
|
254
|
+
count++;
|
|
255
|
+
fs.writeFileSync(file, JSON.stringify({ count, updated: Date.now() }));
|
|
256
|
+
if (count === 1)
|
|
257
|
+
pruneStaleRefreshState();
|
|
258
|
+
return count;
|
|
259
|
+
}
|
|
260
|
+
/** Best-effort removal of counter files from long-dead sessions. */
|
|
261
|
+
function pruneStaleRefreshState() {
|
|
262
|
+
try {
|
|
263
|
+
const dir = (0, shared_1.hookStateDir)();
|
|
264
|
+
const cutoff = Date.now() - REFRESH_STATE_MAX_AGE_MS;
|
|
265
|
+
for (const name of fs.readdirSync(dir)) {
|
|
266
|
+
if (!name.startsWith(REFRESH_STATE_PREFIX))
|
|
267
|
+
continue;
|
|
268
|
+
const p = path.join(dir, name);
|
|
269
|
+
try {
|
|
270
|
+
if (fs.statSync(p).mtimeMs < cutoff)
|
|
271
|
+
fs.unlinkSync(p);
|
|
272
|
+
}
|
|
273
|
+
catch { /* another process won the race — fine */ }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch { /* pruning is housekeeping, never let it interfere */ }
|
|
277
|
+
}
|
|
278
|
+
function emitRuleRefresh(input) {
|
|
279
|
+
try {
|
|
280
|
+
const interval = refreshInterval();
|
|
281
|
+
if (interval === 0)
|
|
282
|
+
return;
|
|
283
|
+
const count = bumpPromptCount(String(input?.session_id ?? 'default'));
|
|
284
|
+
if (count % interval !== 0)
|
|
285
|
+
return;
|
|
286
|
+
const rules = formatRulesForContext();
|
|
287
|
+
if (!rules.body)
|
|
288
|
+
return;
|
|
289
|
+
process.stdout.write(`🔄 Recall: periodic rule refresh (prompt ${count} this session). ` +
|
|
290
|
+
'Earlier rule injections may have been compacted out of your context — continue applying these:\n\n' +
|
|
291
|
+
rules.body + '\n');
|
|
292
|
+
(0, shared_1.hookLog)(HOOK_NAME, `refresh: re-injected ${rules.total} rule(s) at prompt ${count} (interval ${interval})`);
|
|
293
|
+
}
|
|
294
|
+
catch (err) {
|
|
295
|
+
// The refresh is an enhancement — never let it break capture
|
|
296
|
+
(0, shared_1.hookLog)(HOOK_NAME, `refresh error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
187
299
|
/**
|
|
188
300
|
* userPromptSubmit — the synchronous gate Kiro waits on. Under Kiro there is no
|
|
189
301
|
* ANTHROPIC_API_KEY, so classification uses Kiro's own headless LLM
|
|
@@ -195,8 +307,12 @@ async function handleKiroToolOutcome(input) {
|
|
|
195
307
|
* it over stdin, and returns in milliseconds. The worker performs the slow Kiro
|
|
196
308
|
* classify call and stores the memory in the background. Same pattern as
|
|
197
309
|
* session-end-checkpoint. See docs/kiro-llm-capture.md.
|
|
310
|
+
*
|
|
311
|
+
* It also owns the mid-session rule refresh (emitRuleRefresh above): the
|
|
312
|
+
* refresh must count EVERY prompt, so it runs before the capture pre-checks.
|
|
198
313
|
*/
|
|
199
314
|
async function handleKiroCapture(input) {
|
|
315
|
+
emitRuleRefresh(input);
|
|
200
316
|
const prompt = input?.prompt ?? '';
|
|
201
317
|
// Cheap pre-checks mirror correction-detector so we don't spawn a worker (and
|
|
202
318
|
// spend Kiro credits) on input that could never be stored.
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* Requires ANTHROPIC_API_KEY in the environment — a personal pay-as-you-go
|
|
6
6
|
* API key the user exported themselves. Claude Code does NOT mint one from
|
|
7
7
|
* the user's subscription (hooks just inherit the user's environment), which
|
|
8
|
-
* is why this is
|
|
9
|
-
* runtime's included LLM (claude -p / kiro-cli —
|
|
10
|
-
* kiro-classifier.ts) and
|
|
11
|
-
* gracefully (returns null) when unavailable.
|
|
8
|
+
* is why this backend is STRICTLY OPT-IN (CLAUDE_RECALL_PREFER_API_KEY): the
|
|
9
|
+
* default chains use each runtime's included LLM only (claude -p / kiro-cli —
|
|
10
|
+
* see cc-classifier.ts and kiro-classifier.ts), and an exported key is never
|
|
11
|
+
* spent silently. Falls back gracefully (returns null) when unavailable.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.classifyWithLLM = classifyWithLLM;
|
|
@@ -146,11 +146,12 @@ function parseJSON(text) {
|
|
|
146
146
|
* extraction, checkpoint extraction, batch classification) and return raw
|
|
147
147
|
* text, or null when no backend is available.
|
|
148
148
|
*
|
|
149
|
-
* Backend
|
|
150
|
-
* `claude -p`)
|
|
151
|
-
*
|
|
152
|
-
* never
|
|
153
|
-
*
|
|
149
|
+
* Backend policy matches capture: the user's Claude SUBSCRIPTION (headless
|
|
150
|
+
* `claude -p`) is the only default backend — a personally-exported
|
|
151
|
+
* ANTHROPIC_API_KEY is STRICTLY OPT-IN via CLAUDE_RECALL_PREFER_API_KEY and
|
|
152
|
+
* never consulted otherwise, not even as a fallback. The opt-in exists for
|
|
153
|
+
* Pi-only users (no `claude` binary; their key is how they run Pi itself)
|
|
154
|
+
* and anyone deliberately paying for a stronger model.
|
|
154
155
|
*
|
|
155
156
|
* The CLI backend is skipped inside a nested headless session
|
|
156
157
|
* (CLAUDE_RECALL_NESTED): if `claude -p` fires user-scope hooks of its own,
|
|
@@ -182,7 +183,7 @@ async function completeText(systemPrompt, userContent, maxTokens) {
|
|
|
182
183
|
: (0, cc_classifier_1.completeWithClaudeCli)(`${systemPrompt}\n\n${userContent}`, { timeoutMs: SECONDARY_CLI_TIMEOUT_MS });
|
|
183
184
|
const backends = process.env.CLAUDE_RECALL_PREFER_API_KEY
|
|
184
185
|
? [viaApi, viaCli]
|
|
185
|
-
: [viaCli
|
|
186
|
+
: [viaCli];
|
|
186
187
|
for (const backend of backends) {
|
|
187
188
|
const text = await backend();
|
|
188
189
|
if (text !== null && text.trim().length > 0)
|
package/dist/hooks/shared.js
CHANGED
|
@@ -166,18 +166,17 @@ function classifyContentRegex(text) {
|
|
|
166
166
|
* workers, which set the *_CLASSIFIER env vars — a ~4s CLI call can't run
|
|
167
167
|
* inline on a turn):
|
|
168
168
|
* - Under Claude Code (CLAUDE_RECALL_CC_CLASSIFIER set by cc-capture-worker):
|
|
169
|
-
* headless `claude -p` on the user's Claude SUBSCRIPTION →
|
|
170
|
-
* if present → regex.
|
|
169
|
+
* headless `claude -p` on the user's Claude SUBSCRIPTION → regex.
|
|
171
170
|
* - Under Kiro (CLAUDE_RECALL_KIRO_CLASSIFIER set by kiro-capture-worker):
|
|
172
171
|
* Kiro's own headless LLM (`kiro-cli chat --no-interactive`, Kiro credits)
|
|
173
|
-
* →
|
|
174
|
-
* - Inline callers
|
|
172
|
+
* → regex.
|
|
173
|
+
* - Inline callers: straight to regex.
|
|
175
174
|
*
|
|
176
|
-
* The
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* exported — Claude Code does NOT provide one from the subscription.
|
|
180
|
-
* See docs/kiro-llm-capture.md.
|
|
175
|
+
* The ANTHROPIC_API_KEY path is STRICTLY OPT-IN via
|
|
176
|
+
* CLAUDE_RECALL_PREFER_API_KEY — an exported key is never consulted
|
|
177
|
+
* otherwise, not even as a fallback. (It is always a personal key the user
|
|
178
|
+
* exported — Claude Code does NOT provide one from the subscription.)
|
|
179
|
+
* See docs/cc-llm-capture.md and docs/kiro-llm-capture.md.
|
|
181
180
|
*/
|
|
182
181
|
async function classifyContent(text) {
|
|
183
182
|
// Guard the LLM paths the same way the regex path is guarded: a question or
|
|
@@ -202,30 +201,25 @@ async function classifyContentInner(text) {
|
|
|
202
201
|
// module graph for hook invocations that don't run in a capture worker.
|
|
203
202
|
const tryKiro = async () => (await Promise.resolve().then(() => __importStar(require('./kiro-classifier')))).classifyWithKiro(text);
|
|
204
203
|
const tryCc = async () => (await Promise.resolve().then(() => __importStar(require('./cc-classifier')))).classifyWithClaudeCli(text);
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
204
|
+
// Pick the LLM backends. The ANTHROPIC_API_KEY path is STRICTLY OPT-IN
|
|
205
|
+
// (CLAUDE_RECALL_PREFER_API_KEY): claude-recall was built for runtimes that
|
|
206
|
+
// bring their own LLM — Kiro via `kiro-cli chat --no-interactive` (Kiro
|
|
207
|
+
// credits), Claude Code via `claude -p` (the user's Claude subscription) —
|
|
208
|
+
// and a key exported for other tools must NEVER be spent silently, not even
|
|
209
|
+
// as a fallback. Without the opt-in, the chain is included LLM → regex.
|
|
210
|
+
// The opt-in exists for Pi-only users (no `claude` binary; their key is how
|
|
211
|
+
// they run Pi itself) and anyone deliberately paying for a stronger model.
|
|
212
|
+
// The CLI backends only run inside their detached capture workers (which
|
|
213
|
+
// set the *_CLASSIFIER env vars) — inline hook paths go straight to regex,
|
|
214
|
+
// since a ~4s CLI call can't block a turn.
|
|
214
215
|
const backends = [];
|
|
215
|
-
if (
|
|
216
|
-
backends.push(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
backends.push(tryApiKey, tryKiro);
|
|
220
|
-
}
|
|
221
|
-
else if (underCc && !preferApiKey) {
|
|
222
|
-
backends.push(tryCc, tryApiKey);
|
|
216
|
+
if (preferApiKey)
|
|
217
|
+
backends.push(tryApiKey);
|
|
218
|
+
if (underKiro) {
|
|
219
|
+
backends.push(tryKiro);
|
|
223
220
|
}
|
|
224
221
|
else if (underCc) {
|
|
225
|
-
backends.push(
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
backends.push(tryApiKey);
|
|
222
|
+
backends.push(tryCc);
|
|
229
223
|
}
|
|
230
224
|
for (const backend of backends) {
|
|
231
225
|
const result = await backend();
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Archived: the original Claude Recall launch article, published on LinkedIn by
|
|
3
|
+
Raoul Biagioni on August 7, 2025. Preserved verbatim as a historical record of
|
|
4
|
+
the project's origin. NOTE: it predates most of what Claude Recall is today —
|
|
5
|
+
Pi and Kiro support, the outcome-aware learning loop, LLM-based capture, the
|
|
6
|
+
no-API-key/subscription architecture — so treat it as origin story, not docs.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# Why I Spent My Weekend Teaching a Goldfish-Brained AI to Remember
|
|
10
|
+
|
|
11
|
+
**Raoul Biagioni** — ♾️ AI Development Specialist | Agentic Engineer | Enabling AI-driven Business Innovation
|
|
12
|
+
|
|
13
|
+
*Published on LinkedIn, August 7, 2025*
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
Claude Code forgets what you just told it. Not just between sessions. Even 10 messages into a conversation.
|
|
18
|
+
|
|
19
|
+
But what if it didn't?
|
|
20
|
+
|
|
21
|
+
## The Problem That Sparked a Solution
|
|
22
|
+
|
|
23
|
+
Over the past six months, I watched myself explain the same codebase preferences to Claude over and over again.
|
|
24
|
+
|
|
25
|
+
PostgreSQL for data. TypeScript strict mode. Tests directory for saving tests.
|
|
26
|
+
|
|
27
|
+
Not just in new sessions. Even 20 conversation turns later, as the context window fills up, Claude starts forgetting what we discussed at the beginning.
|
|
28
|
+
|
|
29
|
+
That's when I decided: Claude Code needs a memory.
|
|
30
|
+
|
|
31
|
+
## Building Claude Recall: A Memory That Actually Works
|
|
32
|
+
|
|
33
|
+
Sure, there are memory solutions out there. xmem requires an external vector database for storing memory. Mem0 (OpenMemory) needs Chrome extensions and API keys to connect across tools.
|
|
34
|
+
|
|
35
|
+
But I wanted something different: truly local, minimal dependencies, and built directly into Claude Code using MCP. No browser extensions. No vector DB setup. No API keys. Just `npm install claude-recall` and you're done.
|
|
36
|
+
|
|
37
|
+
Here's what I built in 72 hours of coding:
|
|
38
|
+
|
|
39
|
+
### 🧠 Smart Capture
|
|
40
|
+
|
|
41
|
+
- Hooks that watch every tool Claude uses (118-line robust implementation)
|
|
42
|
+
- Patterns detected automatically from your workflow
|
|
43
|
+
- Zero configuration needed
|
|
44
|
+
|
|
45
|
+
### ⚡ Smart Retrieval
|
|
46
|
+
|
|
47
|
+
- MCP server provides memory tools directly to Claude
|
|
48
|
+
- Pattern-based intent detection from natural language
|
|
49
|
+
- Context-aware memory filtering and relevance scoring
|
|
50
|
+
|
|
51
|
+
### 🔧 The Tech Stack
|
|
52
|
+
|
|
53
|
+
Claude Recall consists of three main components: A JavaScript hook that intercepts Claude's tool usage (file reads, bash commands, etc.), a Node.js MCP server to expose memory search and retrieval tools to Claude, and a SQLite database that stores captured patterns and preferences locally.
|
|
54
|
+
|
|
55
|
+
When Claude performs an action, the hook captures it and sends it to the pattern detector. The detector uses regex-based pattern matching to identify things like file locations, tool preferences, and coding styles with confidence scores. Patterns scoring above 0.8 are stored in SQLite. During Claude sessions, the MCP server provides tools that Claude can call to search and retrieve these stored memories based on context and relevance scoring.
|
|
56
|
+
|
|
57
|
+
## Real Results From Real Conversations
|
|
58
|
+
|
|
59
|
+
**Before Claude Recall (message #25 in same chat):**
|
|
60
|
+
|
|
61
|
+
- "What database do we use?" → Claude searches everywhere
|
|
62
|
+
- "Where's the auth logic?" → Claude searches everywhere
|
|
63
|
+
- "Write a test" → Claude saves the test in the project root
|
|
64
|
+
|
|
65
|
+
**After Claude Recall (message #100, still remembers):**
|
|
66
|
+
|
|
67
|
+
- "What database do we use?" → "PostgreSQL, as always"
|
|
68
|
+
- "Where's the auth logic?" → "src/services/auth.ts:42"
|
|
69
|
+
- "Write a test" → Claude saves the test in tests/
|
|
70
|
+
|
|
71
|
+
## The Reality Check
|
|
72
|
+
|
|
73
|
+
I'm conscious that in the fast-evolving GenAI ecosystem, many projects are quickly destined to oblivion. I have no doubt that the problem I described and solved for myself will be solved better by someone else soon. But here's the thing: I had an absolute blast building Claude Recall with the help of Claude Code by Anthropic, and the amazing AI Orchestration Platform Claude-Flow by Reuven Cohen from Agentics Foundation.
|
|
74
|
+
|
|
75
|
+
Sometimes the best tools are the ones you build for yourself, not because they'll change the world, but because they solve your specific problem right now.
|
|
76
|
+
|
|
77
|
+
> 💡 **Key Takeaway:** We're living in the stone age of AI assistants. They're powerful but goldfish-brained — forgetting context even within the same conversation. That changes now.
|
|
78
|
+
|
|
79
|
+
🔗 Link: in the comments.
|
|
80
|
+
|
|
81
|
+
💬 Question for you: What's the ONE thing you wish your AI assistant would remember?
|
|
82
|
+
|
|
83
|
+
#AITools #DeveloperProductivity #OpenSource #ClaudeAI
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
DRAFT v2 — reader-first rewrite of the one-year follow-up (July 2026).
|
|
3
|
+
v1 (deleted) was structured around the author's broken-promise confession;
|
|
4
|
+
this version is structured around what the READER gets: three things they can
|
|
5
|
+
check/fix about their own agent's memory today, whether or not they install
|
|
6
|
+
anything. The tool enters late, the "when NOT to bother" section is explicit,
|
|
7
|
+
and the CTA offers a fair trade (2 commands, 1 week, reversible) for honest
|
|
8
|
+
answers to: does it work / am I over-promising / is it worth it at all.
|
|
9
|
+
Facts verified against v0.33.0 on 2026-07-10: write-time dedup (live-tested),
|
|
10
|
+
prune-on-boot, demotion (manual/opt-in — hence "can demote"), Kiro→CC shared
|
|
11
|
+
DB (live-tested), claude -p key-over-subscription precedence (live-tested).
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Your Coding Agent Finally Has Memory. Here's Where It Still Fails You.
|
|
15
|
+
|
|
16
|
+
**Raoul Biagioni** — *Draft v2, July 2026*
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
You've told your coding agent "use pnpm, not npm" at least five times. This year that was finally supposed to stop: Claude Code now ships auto-memory, on by default. It watches your corrections and writes itself notes.
|
|
21
|
+
|
|
22
|
+
Genuinely good. I build in this space and I'll say it plainly: for a lot of people, the built-in memory is enough.
|
|
23
|
+
|
|
24
|
+
But if you live in a coding agent daily, there are three failure modes it leaves wide open — and you'll hit all three without ever getting an error message. Whether or not you try my tool, you'll want to know they exist.
|
|
25
|
+
|
|
26
|
+
## 1. Agent memory rots — silently
|
|
27
|
+
|
|
28
|
+
Built-in memory is great for the first twenty sessions. Then the notes pile up: contradictory entries, "yesterday" written three weeks ago, fixes referencing files you've deleted. Past a point, your agent isn't remembering — it's confidently misremembering, and stale notes are worse than no notes, because you don't think to double-check something the agent states as known fact.
|
|
29
|
+
|
|
30
|
+
**Check it yourself today:** open your project's auto-memory file and read what your agent believes about your codebase. If you've been at it more than a month, I'd bet money something in there is wrong.
|
|
31
|
+
|
|
32
|
+
## 2. Your corrections don't travel
|
|
33
|
+
|
|
34
|
+
Correct Claude Code on Tuesday; Kiro makes the same mistake on Friday. Every agent keeps its own notebook, so a team (or a person) using more than one agent re-teaches every preference once per tool. The more agents you run, the more your corrections fragment.
|
|
35
|
+
|
|
36
|
+
## 3. Your exported API key may be quietly paying for things
|
|
37
|
+
|
|
38
|
+
This one surprised me, and it generalizes beyond memory tools: **if you have `ANTHROPIC_API_KEY` exported for some other tool, `claude -p` — and anything built on it — will silently bill that key instead of your Claude subscription.** Two wallets, one of them draining without you deciding anything. Audit your environment; you may be paying twice for AI you already subscribe to.
|
|
39
|
+
|
|
40
|
+
## What I built against these
|
|
41
|
+
|
|
42
|
+
A year ago I wrote here about giving my "goldfish-brained" agent a memory. That tool, Claude Recall, has since become specifically an answer to the three problems above:
|
|
43
|
+
|
|
44
|
+
- **Anti-rot:** it de-duplicates on write, prunes on every boot, and can demote rules that stop earning their keep — designed for session 200, not session 5.
|
|
45
|
+
- **One brain, every agent:** a single local SQLite file shared by Claude Code, Pi, and Kiro. Correct one agent; the others apply it.
|
|
46
|
+
- **Never your API key:** it runs on the login your agent already has (Claude subscription, Kiro credits). An exported key is untouched unless you explicitly opt in.
|
|
47
|
+
|
|
48
|
+
Local, no cloud, no telemetry, and you can read every stored memory with one command — or wipe them.
|
|
49
|
+
|
|
50
|
+
Setup is two commands: `npm install -g claude-recall`, then one setup command per project. Uninstalling is one.
|
|
51
|
+
|
|
52
|
+
## When you should NOT bother
|
|
53
|
+
|
|
54
|
+
Fair is fair: if you use a single agent, a couple of sessions a week, on one or two projects — the built-in memory is probably enough for you. Don't install another tool for a problem you don't feel.
|
|
55
|
+
|
|
56
|
+
## What I'm asking — and what you get
|
|
57
|
+
|
|
58
|
+
Here's the trade. I built this for myself, which means I genuinely don't know if it holds up in your workflow. So if the three failure modes above sound familiar, run it for a week next to the built-in memory and tell me the truth:
|
|
59
|
+
|
|
60
|
+
1. **Does it work as expected** — right things captured, surfaced at the right moment?
|
|
61
|
+
2. **Am I over-promising** — where does this article outrun your actual experience?
|
|
62
|
+
3. **Is it worth it at all** — or is the built-in memory quietly good enough?
|
|
63
|
+
|
|
64
|
+
What you get in return: a memory that survives, travels, and never touches your key — if I'm right. And if I'm wrong, you've spent two commands finding out, and your feedback directly decides what gets built next. Either answer is useful to me; only one of us is guessing right now.
|
|
65
|
+
|
|
66
|
+
Link and quick-start in the comments. Tell me which of the three failure modes you've hit.
|
|
67
|
+
|
|
68
|
+
#AITools #DeveloperProductivity #OpenSource #ClaudeAI #AIMemory
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Tight LinkedIn version (~430 words) of docs/2026-07-follow-up-article-draft.md
|
|
3
|
+
(reader-first v2). Same three failure modes, same honest opt-out, same CTA —
|
|
4
|
+
compressed for feed reading. Not yet published.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Your Coding Agent Finally Has Memory. Here's Where It Still Fails You.
|
|
8
|
+
|
|
9
|
+
Claude Code ships auto-memory, on by default. Your agent watches your corrections, writes itself notes, and remembers them next session.
|
|
10
|
+
|
|
11
|
+
Genuinely good. For many people it's enough.
|
|
12
|
+
|
|
13
|
+
But if you live in a coding agent daily, it leaves three failure modes wide open — and you'll hit all three without ever seeing an error.
|
|
14
|
+
|
|
15
|
+
**1. Agent memory rots — silently.**
|
|
16
|
+
Great for twenty sessions. Then the notes pile up: contradictions, "yesterday" written three weeks ago, fixes referencing deleted files — and only the first 200 lines even load at startup. Your agent stops remembering and starts confidently *mis*remembering. Anthropic knows: they shipped a periodic cleanup pass ("Auto Dream") for exactly this — a tidy-up of the same self-written notes, so a wrong note that reads plausibly stays believed. Check it yourself: open your auto-memory file and read what your agent believes about your codebase. To be clear, *no* memory system captures perfectly — mine included. The real question is what happens to the mistakes afterwards: dedup, pruning, retiring rules that stop earning their keep — or nothing.
|
|
17
|
+
|
|
18
|
+
**2. Your corrections don't travel.**
|
|
19
|
+
Correct Claude Code on Tuesday; Kiro repeats the mistake on Friday. Every agent keeps its own notebook, so every preference gets re-taught once per tool.
|
|
20
|
+
|
|
21
|
+
**3. Your exported API key may be quietly paying for things.**
|
|
22
|
+
If `ANTHROPIC_API_KEY` is exported for some other tool, headless Claude (`claude -p` — and every script or hook built on it) bills that key instead of your Claude subscription. Not a bug: Anthropic's docs say the env key *"takes precedence over subscription authentication"* — the interactive app asks you which to use; automation doesn't ask. Two wallets, one draining without you deciding anything. Audit your env.
|
|
23
|
+
|
|
24
|
+
**Claude Recall** — my open-source memory engine for coding agents — is built specifically against these three.
|
|
25
|
+
|
|
26
|
+
Some of you saw me launch it here last summer as a weekend hack: Claude-Code-only, regex-based capture, held together with enthusiasm. Today's version (v0.33) is a different animal — same local-first idea, now grown into what the weekend hack couldn't be:
|
|
27
|
+
|
|
28
|
+
▸ **Anti-rot:** de-duplicates on write, prunes on every boot, can demote rules that stop earning their keep. Built for session 200, not session 5.
|
|
29
|
+
▸ **One brain, every agent:** a single local SQLite file shared by Claude Code, Pi, and Kiro. Correct one; the others apply it.
|
|
30
|
+
▸ **Never your API key:** runs on the login your agent already has. Your key is untouched unless you explicitly opt in.
|
|
31
|
+
|
|
32
|
+
Local. No cloud. No telemetry. Two commands to install; one to remove.
|
|
33
|
+
|
|
34
|
+
**When NOT to bother:** one agent, a few sessions a week, one or two projects → the built-in memory is probably enough. Don't install a tool for a problem you don't feel.
|
|
35
|
+
|
|
36
|
+
**The ask.** I built this for myself — I genuinely don't know if it holds up in *your* workflow. If the three failure modes sound familiar: run it for a week next to the built-in memory and tell me the truth.
|
|
37
|
+
|
|
38
|
+
1. Does it work as expected?
|
|
39
|
+
2. Am I over-promising?
|
|
40
|
+
3. Is it worth it at all — or is built-in quietly enough?
|
|
41
|
+
|
|
42
|
+
Either answer is useful. Only one of us is guessing right now.
|
|
43
|
+
|
|
44
|
+
Link in the comments. Which of the three have you hit?
|
|
45
|
+
|
|
46
|
+
#AITools #DeveloperProductivity #OpenSource #ClaudeAI #AIMemory
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Using your Claude subscription for memory capture (no API key)
|
|
2
|
+
|
|
3
|
+
The Claude Code counterpart to [kiro-llm-capture.md](kiro-llm-capture.md): how
|
|
4
|
+
claude-recall's LLM features run on the user's **Claude subscription** — the
|
|
5
|
+
same login that powers their interactive session — instead of a personal
|
|
6
|
+
`ANTHROPIC_API_KEY`.
|
|
7
|
+
|
|
8
|
+
## The problem
|
|
9
|
+
|
|
10
|
+
Claude Recall's LLM classifier originally called Claude Haiku through the
|
|
11
|
+
Anthropic SDK, which needs `ANTHROPIC_API_KEY` — a **personal pay-as-you-go
|
|
12
|
+
key**. For a long time the docs claimed Claude Code "provides the key to its
|
|
13
|
+
hooks." That was false: Claude Code does not mint a key from the user's
|
|
14
|
+
subscription; hooks merely inherit the user's environment. So the key path
|
|
15
|
+
only ever worked for users who had exported their own key, and it silently
|
|
16
|
+
spent **their API credits** — a separate wallet from the Claude subscription
|
|
17
|
+
they were already paying for.
|
|
18
|
+
|
|
19
|
+
The failure mode that exposed all of this: a user's exported key ran out of
|
|
20
|
+
credits. Every SDK call failed silently, capture degraded to the regex
|
|
21
|
+
fallback, and the regex promptly stored four junk memories in one session —
|
|
22
|
+
while the user reasonably believed "the LLM" was doing the classifying.
|
|
23
|
+
|
|
24
|
+
## The finding
|
|
25
|
+
|
|
26
|
+
**`claude -p "<prompt>"` is a headless one-shot completion that runs on the
|
|
27
|
+
user's Claude Code login** — the direct analogue of the earlier
|
|
28
|
+
`kiro-cli chat --no-interactive` discovery. A background worker can shell out
|
|
29
|
+
to it to classify text with no API key and no extra cost beyond the
|
|
30
|
+
subscription usage the user already has.
|
|
31
|
+
|
|
32
|
+
One gotcha made this non-trivial, and it's the most important line in the
|
|
33
|
+
implementation:
|
|
34
|
+
|
|
35
|
+
> **`claude -p` prefers `ANTHROPIC_API_KEY` over subscription auth when the
|
|
36
|
+
> key is present in the environment.**
|
|
37
|
+
|
|
38
|
+
So a stray exported key — possibly dead, possibly billing a wallet the user
|
|
39
|
+
forgot about — would silently hijack every headless call. The fix:
|
|
40
|
+
`completeWithClaudeCli()` **strips `ANTHROPIC_API_KEY` from the child env**,
|
|
41
|
+
forcing subscription auth unconditionally. (Verified live: with a
|
|
42
|
+
credits-exhausted key in the env, `claude -p` returned
|
|
43
|
+
`Credit balance is too low`; with the key stripped, the same call succeeded on
|
|
44
|
+
the login.)
|
|
45
|
+
|
|
46
|
+
## The design
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Claude Code UserPromptSubmit
|
|
50
|
+
│
|
|
51
|
+
▼
|
|
52
|
+
hook run correction-detector (name unchanged in settings.json;
|
|
53
|
+
│ returns in <100 ms)
|
|
54
|
+
│ spawn detached, pipe payload via stdin
|
|
55
|
+
▼
|
|
56
|
+
hook run cc-capture-worker (background, survives parent exit)
|
|
57
|
+
│ sets CLAUDE_RECALL_CC_CLASSIFIER=1
|
|
58
|
+
▼
|
|
59
|
+
handleCorrectionDetector → classifyContent()
|
|
60
|
+
│
|
|
61
|
+
├─ classifyWithClaudeCli() → claude -p --model haiku "<prompt>"
|
|
62
|
+
│ (subscription auth, key stripped)
|
|
63
|
+
└─ regex fallback
|
|
64
|
+
|
|
65
|
+
(classifyWithLLM() / ANTHROPIC_API_KEY exists but is OPT-IN only —
|
|
66
|
+
see below; it is never in the default chain)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Precedence mirrors Kiro: **the runtime's included LLM → regex.** An exported
|
|
70
|
+
`ANTHROPIC_API_KEY` is **never consulted by default — not even as a
|
|
71
|
+
fallback**; `CLAUDE_RECALL_PREFER_API_KEY=1` is the one switch that enables
|
|
72
|
+
(and prefers) it. The registered hook name (`hook run correction-detector`)
|
|
73
|
+
was deliberately kept, so existing `settings.json` files work with no
|
|
74
|
+
re-setup — only the dispatch behind it changed.
|
|
75
|
+
|
|
76
|
+
**Trade-off:** the synchronous `📌 Recall: auto-captured …` echo is gone.
|
|
77
|
+
A cold `claude -p` takes ~4 s, far too slow to block the user's prompt, so
|
|
78
|
+
capture is silent and lands a few seconds later — the same contract Kiro has
|
|
79
|
+
always had. Verify via `~/.claude-recall/hook-logs/cc-classifier.log`
|
|
80
|
+
(`classified via claude -p (model=…, Claude subscription, no API key)`) or
|
|
81
|
+
`claude-recall search`.
|
|
82
|
+
|
|
83
|
+
### Recursion guard
|
|
84
|
+
|
|
85
|
+
The nested headless session is itself a Claude Code session. If the user has
|
|
86
|
+
claude-recall hooks registered at **user scope** (`~/.claude/settings.json`),
|
|
87
|
+
the classify call would fire those hooks, which would spawn another worker,
|
|
88
|
+
which would run another `claude -p` — forever. Two layers prevent this:
|
|
89
|
+
|
|
90
|
+
1. `claude -p` children run with `cwd = os.tmpdir()`, so **project**
|
|
91
|
+
settings/hooks never load.
|
|
92
|
+
2. Children are marked with `CLAUDE_RECALL_NESTED=1` (plus the legacy
|
|
93
|
+
`CLAUDE_RECALL_CC_CLASSIFIER=1`). The capture hook refuses to spawn a
|
|
94
|
+
worker, and the secondary-feature backend refuses to invoke the CLI, when
|
|
95
|
+
either marker is present.
|
|
96
|
+
|
|
97
|
+
### Beyond capture: the secondary features (0.32.0)
|
|
98
|
+
|
|
99
|
+
`completeWithClaudeCli()` is a generic completion primitive, and the four
|
|
100
|
+
features that previously knew only the key path now route through it with the
|
|
101
|
+
same precedence:
|
|
102
|
+
|
|
103
|
+
| Feature | Runs in | Without any key, now |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| Auto-checkpoint extraction | Session-exit worker (CC + **Pi** — Pi's only recovery path, it has no `--resume`) | Works via `claude -p` |
|
|
106
|
+
| Failure hindsight hints | Stop hook, inline | Works via `claude -p` |
|
|
107
|
+
| End-of-session lesson extraction | Stop hook, inline | Works via `claude -p` |
|
|
108
|
+
| Batch classification | Stop hook, inline | Works via `claude -p` |
|
|
109
|
+
|
|
110
|
+
Two budget rules keep the inline ones inside the Stop hook's ~40 s window
|
|
111
|
+
(capture doesn't need them — it runs in a detached worker):
|
|
112
|
+
|
|
113
|
+
- each secondary CLI call is capped at **10 s** (vs the worker's 30 s), and
|
|
114
|
+
- hindsight-hint generation is capped at **5 LLM calls per run** (the loop
|
|
115
|
+
over detected failures is otherwise unbounded; failures past the cap keep
|
|
116
|
+
the grounded generic lesson text).
|
|
117
|
+
|
|
118
|
+
Under **Pi** these functions run in-process, so Pi benefits automatically on
|
|
119
|
+
any machine with the `claude` binary installed alongside. Under **Kiro** the
|
|
120
|
+
Stop-hook features mostly don't run (no transcript in its hooks), and capture
|
|
121
|
+
uses the Kiro backend.
|
|
122
|
+
|
|
123
|
+
### Configuration
|
|
124
|
+
|
|
125
|
+
| Variable | Default | Effect |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| `CLAUDE_RECALL_CC_MODEL` | `haiku` | Model passed to `claude -p --model` — a dedicated classifier model, independent of the user's interactive session model. |
|
|
128
|
+
| `CLAUDE_RECALL_CC_LLM_TIMEOUT_MS` | `30000` | Hard cap on the capture worker's `claude -p` call. (Secondary features use a fixed 10 s per call.) |
|
|
129
|
+
| `CLAUDE_RECALL_PREFER_API_KEY` | *(unset)* | **The only switch that enables the `ANTHROPIC_API_KEY` backend** (and prefers it first) — without it an exported key is never touched, not even as a fallback. For Pi-only machines without the `claude` binary, or a stronger model you deliberately pay for. Applies to capture and the secondary features, on every runtime. |
|
|
130
|
+
| `CLAUDE_RECALL_NESTED` | *(set on `claude -p` children)* | Recursion marker — never set it by hand. |
|
|
131
|
+
|
|
132
|
+
## Caveats
|
|
133
|
+
|
|
134
|
+
- **Requires the `claude` binary on `PATH`.** Absent (e.g. a bare CI box, or a
|
|
135
|
+
Pi-only machine), the CLI backend errors out and the chain falls through to
|
|
136
|
+
regex — or to an `ANTHROPIC_API_KEY` if you opted in with
|
|
137
|
+
`CLAUDE_RECALL_PREFER_API_KEY=1`. No crash, no exception.
|
|
138
|
+
- **Subscription usage.** Each classified prompt is one small Haiku call
|
|
139
|
+
against the user's Claude plan limits. That is the deliberate trade — the
|
|
140
|
+
user's existing plan instead of a second wallet.
|
|
141
|
+
- **Latency.** ~4 s cold per call. Fine in a detached worker; the reason the
|
|
142
|
+
inline features carry the 10 s cap and hint budget.
|
|
143
|
+
- **Consistency.** Same probabilistic caveat as the Kiro backend: the
|
|
144
|
+
classifier is an LLM judgement call. The classify prompt carries a
|
|
145
|
+
"standalone rule" test with negative examples precisely because early
|
|
146
|
+
versions stored one-off task instructions ("first fix the sentence") as
|
|
147
|
+
corrections.
|
package/docs/kiro-llm-capture.md
CHANGED
|
@@ -9,7 +9,7 @@ Claude Haiku through `ANTHROPIC_API_KEY` — a personal API key the user had
|
|
|
9
9
|
exported themselves. (Claude Code does **not** mint a key from the user's
|
|
10
10
|
subscription; hooks merely inherit the environment. The headless-CLI approach
|
|
11
11
|
documented here was later applied back to Claude Code via `claude -p` on
|
|
12
|
-
subscription auth — see
|
|
12
|
+
subscription auth — see [cc-llm-capture.md](cc-llm-capture.md).)
|
|
13
13
|
|
|
14
14
|
Kiro CLI does **not** set `ANTHROPIC_API_KEY`. So under Kiro the classifier fell
|
|
15
15
|
back to a conservative regex that only fires on explicit phrasings
|
|
@@ -89,20 +89,24 @@ Kiro userPromptSubmit
|
|
|
89
89
|
▼
|
|
90
90
|
handleCorrectionDetector → classifyContent()
|
|
91
91
|
│
|
|
92
|
-
├─ classifyWithLLM() → ANTHROPIC_API_KEY (unset under Kiro) → null
|
|
93
92
|
├─ classifyWithKiro() → kiro-cli chat --no-interactive --agent
|
|
94
93
|
│ claude-recall-classifier --model claude-haiku-4.5
|
|
95
94
|
│ → {"type","confidence","extract"}
|
|
96
|
-
└─ regex fallback (only if
|
|
95
|
+
└─ regex fallback (only if the above yields nothing)
|
|
96
|
+
|
|
97
|
+
(classifyWithLLM() / ANTHROPIC_API_KEY exists but is OPT-IN only —
|
|
98
|
+
never in the default chain)
|
|
97
99
|
```
|
|
98
100
|
|
|
99
|
-
Capture precedence under Kiro: **Kiro's included LLM →
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Anthropic credits
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
Capture precedence under Kiro: **Kiro's included LLM → regex as a last
|
|
102
|
+
resort.** An exported `ANTHROPIC_API_KEY` is **never consulted by default —
|
|
103
|
+
not even as a fallback** — so a key exported for other tools never silently
|
|
104
|
+
spends the user's Anthropic credits; Kiro already ships an LLM.
|
|
105
|
+
`CLAUDE_RECALL_PREFER_API_KEY=1` is the one explicit switch that enables (and
|
|
106
|
+
prefers) the key, for anyone who deliberately wants to pay for a stronger
|
|
107
|
+
model. (Under Claude Code the same pattern applies with `claude -p` on the
|
|
108
|
+
user's subscription as the included LLM — see
|
|
109
|
+
[cc-llm-capture.md](cc-llm-capture.md).)
|
|
106
110
|
|
|
107
111
|
### Output parsing
|
|
108
112
|
|
|
@@ -119,7 +123,7 @@ returns `null`, degrading to regex — a hook must never throw.
|
|
|
119
123
|
| `CLAUDE_RECALL_KIRO_CLASSIFIER` | *(set by the worker)* | Enables the Kiro-LLM path in `classifyContent`. Set automatically by `kiro-capture-worker`; never needed by hand. |
|
|
120
124
|
| `CLAUDE_RECALL_KIRO_MODEL` | `claude-haiku-4.5` | Model for the classify call, passed explicitly as `--model`. This is a **dedicated classifier model, independent of the user's interactive Kiro chat model** (e.g. `auto`) — the headless call always uses this value. Raise to `claude-sonnet-4.6` for steadier judgement at ~3× the credits. |
|
|
121
125
|
| `CLAUDE_RECALL_KIRO_LLM_TIMEOUT_MS` | `30000` | Hard cap on the headless call before the worker gives up and falls back to regex. |
|
|
122
|
-
| `CLAUDE_RECALL_PREFER_API_KEY` | *(unset)* |
|
|
126
|
+
| `CLAUDE_RECALL_PREFER_API_KEY` | *(unset)* | **The only switch that enables the `ANTHROPIC_API_KEY` backend** — without it an exported key is never touched, not even as a fallback. |
|
|
123
127
|
|
|
124
128
|
## Caveats
|
|
125
129
|
|
package/docs/kiro.md
CHANGED
|
@@ -30,7 +30,7 @@ and inside the Kiro chat, switch to the agent:
|
|
|
30
30
|
/agent swap recall
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
You get: active rules injected into context automatically at agent start (no tool call needed),
|
|
33
|
+
You get: active rules injected into context automatically at agent start (no tool call needed), a periodic mid-session rule refresh (see below), automatic capture of corrections/preferences from your prompts, tool-outcome tracking with Bash fix-pairing, and the full MCP tool surface (read-only tools pre-approved).
|
|
34
34
|
|
|
35
35
|
### Merging into an agent you already use
|
|
36
36
|
|
|
@@ -40,7 +40,13 @@ Don't want to swap agents? Merge Claude Recall into your own:
|
|
|
40
40
|
claude-recall kiro setup --merge-into <agent-name>
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
This finds the agent config (workspace `.kiro/agents/` first, then `~/.kiro/agents/`; `--global` to target the global one directly), writes a timestamped backup, and appends the claude-recall pieces — MCP server, pre-approved read-only tools, and the
|
|
43
|
+
This finds the agent config (workspace `.kiro/agents/` first, then `~/.kiro/agents/`; `--global` to target the global one directly), writes a timestamped backup, and appends the claude-recall pieces — MCP server, pre-approved read-only tools, and the three hooks (`agentSpawn`, `userPromptSubmit`, `postToolUse`) — while leaving your own config untouched. It only rewrites claude-recall's own entries: superseded ones from an older version are swapped for the current wiring (any unrelated hook you have on the same event is preserved). Idempotent: on an already-current agent, re-running changes nothing. If the agent restricts tools with an explicit list, `@claude-recall` is added to it.
|
|
44
|
+
|
|
45
|
+
### Mid-session rule refresh (long sessions)
|
|
46
|
+
|
|
47
|
+
Rules enter context once, at agent start. In a long session Kiro eventually compacts or rolls the conversation and the rules silently vanish — Kiro exposes no post-compaction event to re-inject them (Claude Code has one, and claude-recall uses it there). So under Kiro, claude-recall re-injects the active rules **every 15 prompts** via the `userPromptSubmit` hook, whose stdout Kiro adds directly to context. Tune or disable with `CLAUDE_RECALL_REFRESH_INTERVAL` (`0` = off).
|
|
48
|
+
|
|
49
|
+
> **Note for configs wired by ≤0.33:** older versions also added a `preToolUse` rule injector. Kiro ignores `preToolUse` stdout — exit codes gate the tool call, and only exit-2 stderr reaches the model ([kiro.dev/docs/cli/hooks](https://kiro.dev/docs/cli/hooks)) — so that hook never actually injected anything. It is now a harmless no-op; re-run `claude-recall kiro setup --merge-into <agent>` once and the stale entry is stripped automatically.
|
|
44
50
|
|
|
45
51
|
---
|
|
46
52
|
|
|
@@ -74,7 +80,7 @@ To decide what's worth remembering, the capture hook classifies each prompt via
|
|
|
74
80
|
|
|
75
81
|
**The classifier uses a dedicated, fixed model — not your chat model.** It always runs the model in `CLAUDE_RECALL_KIRO_MODEL` (default `claude-haiku-4.5`, chosen because classification is cheap and high-volume), **independent of your interactive Kiro chat model** (e.g. `auto`). This keeps classification cost predictable no matter what your chat is set to. Set `CLAUDE_RECALL_KIRO_MODEL` to any model from `kiro-cli chat --list-models` to change it. Every successful classification is logged to `~/.claude-recall/hook-logs/kiro-classifier.log` as `classified via kiro-cli (model=…, Kiro credits, no API key)`, so you can always see which model ran.
|
|
76
82
|
|
|
77
|
-
**
|
|
83
|
+
**An exported `ANTHROPIC_API_KEY` is never touched** — a key exported for other tools won't quietly spend your Anthropic credits, not even as a fallback. Order: Kiro's LLM → regex. Setting `CLAUDE_RECALL_PREFER_API_KEY=1` is the one explicit switch that enables (and prefers) your key, e.g. for a stronger model you pay for.
|
|
78
84
|
|
|
79
85
|
Design details, latency measurements, and output-parsing internals: [kiro-llm-capture.md](kiro-llm-capture.md).
|
|
80
86
|
|
package/package.json
CHANGED