claude-recall 0.28.3 → 0.28.5

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 CHANGED
@@ -129,6 +129,18 @@ With Option B the agent has the memory tools (`load_rules`, `store_memory`, `sea
129
129
 
130
130
  **Not available under Kiro** with either option (Kiro's hooks expose no transcript): transcript-based failure detection and session-end auto-checkpoints.
131
131
 
132
+ > **Project scoping & `--resume`.** Memories scope to the **working directory Kiro reports for the session**. `kiro --resume` (with no conversation id) continues your *most-recent conversation* and restores **its** directory — which may be a different project than the one your shell is in. So a resumed session captures into the project of the conversation you're continuing, not wherever you launched Kiro. That's usually what you want (memories follow the conversation), but if you bounce between projects it can surprise you.
133
+ >
134
+ > To force a fixed project regardless of what `--resume` restores, pin it with `CLAUDE_RECALL_PROJECT_ID`. A per-project shell alias makes it seamless:
135
+ >
136
+ > ```bash
137
+ > alias kiro-epic='CLAUDE_RECALL_PROJECT_ID=epic-workflow-cicd kiro-cli chat --agent mcp-agent-env --resume'
138
+ > ```
139
+ >
140
+ > `claude-recall kiro doctor` always prints the resolved project (and whether it's pinned) so you can confirm where memories are landing before trusting it.
141
+ >
142
+ > **Capture works even when the MCP tools are blocked.** Under enterprise governance that restricts MCP to a trusted registry, Kiro drops the claude-recall MCP server — so the agent may say it "has no memory tools." Ignore that: the hooks capture and inject against the local DB regardless. The agent is told this at session start and will confirm it's remembering; only the on-demand tools (the agent calling `search_memory` itself) need an admin to allowlist claude-recall.
143
+
132
144
  ### Shared Database
133
145
 
134
146
  All runtimes (Claude Code, Pi, Kiro CLI) use the same database at `~/.claude-recall/claude-recall.db`, scoped per project by working directory. A correction learned in one agent is available in the others.
@@ -425,6 +437,7 @@ claude-recall store "content" -t <type> # Store with type (preference, correcti
425
437
  claude-recall export backup.json # Export current project's memories to JSON
426
438
  claude-recall export backup.json --global # Export all projects
427
439
  claude-recall import backup.json # Import memories from JSON
440
+ claude-recall delete <key> # Delete one memory by key (get keys from `search`)
428
441
  claude-recall clear --force # Clear current project (auto-backup written first)
429
442
  claude-recall clear --force --global # Clear all projects
430
443
  claude-recall failures # View failure memories
@@ -542,6 +555,7 @@ Runtime behavior can be tuned via environment variables. Defaults are chosen so
542
555
  | `CLAUDE_RECALL_ENFORCE_MODE` | `on` | Set to `off` to bypass the search-enforcer hook. |
543
556
  | `CLAUDE_RECALL_LLM_TIMEOUT_MS` | `5000` | Timeout for hook-context LLM calls (classification, hindsight hints). Hooks fall back to regex when it fires. |
544
557
  | `CLAUDE_RECALL_STOP_DEBOUNCE_MS` | `300000` | Debounce for the heavy Stop-hook pipeline (episodes, session extraction, promotion). Citations still scan every turn. `0` disables. |
558
+ | `CLAUDE_RECALL_PROJECT_ID` | *(cwd)* | Pin the project scope to a fixed id, overriding working-directory detection. Useful when `kiro --resume` restores a different project's directory than you intend. |
545
559
 
546
560
  ---
547
561
 
@@ -1961,6 +1961,26 @@ async function main() {
1961
1961
  await cli.import(input, options);
1962
1962
  process.exit(0);
1963
1963
  });
1964
+ // Delete a single memory by key
1965
+ program
1966
+ .command('delete <key>')
1967
+ .description('Delete a single memory by its key (find keys with `search`)')
1968
+ .action((key) => {
1969
+ try {
1970
+ const deleted = memory_1.MemoryService.getInstance().delete(key);
1971
+ if (deleted) {
1972
+ console.log(`✅ Deleted memory: ${key}`);
1973
+ }
1974
+ else {
1975
+ console.log(`⚠️ No memory found with key: ${key}`);
1976
+ }
1977
+ process.exit(deleted ? 0 : 1);
1978
+ }
1979
+ catch (error) {
1980
+ console.error(`❌ Delete failed: ${error.message}`);
1981
+ process.exit(1);
1982
+ }
1983
+ });
1964
1984
  // Clear command
1965
1985
  program
1966
1986
  .command('clear')
@@ -180,6 +180,24 @@ class HookCommands {
180
180
  (0, shared_1.hookLog)('hook-dispatcher', `stdin read failed (continuing with empty payload): ${(0, shared_1.safeErrorMessage)(err)}`);
181
181
  input = {};
182
182
  }
183
+ // Deterministic project scoping. Resolve the project from the cwd the
184
+ // RUNTIME declares in the hook payload — NOT the cwd this subprocess
185
+ // happened to inherit. Both Claude Code and Kiro include `cwd`; for CC
186
+ // it equals process.cwd() so this is a no-op, but for Kiro it pins
187
+ // scoping to the session's working directory. Without this a memory
188
+ // captured while working on project A could land in project B (e.g. a
189
+ // `kiro --resume`d session whose cwd differs from the shell's), and
190
+ // capture (userPromptSubmit) and injection (agentSpawn) could even
191
+ // disagree. Project memories must scope to ONE deterministic project.
192
+ if (input && typeof input.cwd === 'string' && input.cwd.trim()) {
193
+ try {
194
+ const { ConfigService } = await Promise.resolve().then(() => __importStar(require('../../services/config')));
195
+ ConfigService.getInstance().updateConfig({ project: { rootDir: input.cwd } });
196
+ }
197
+ catch (err) {
198
+ (0, shared_1.hookLog)('hook-dispatcher', `cwd scoping failed (using inherited cwd): ${(0, shared_1.safeErrorMessage)(err)}`);
199
+ }
200
+ }
183
201
  for (const name of names) {
184
202
  try {
185
203
  await HookCommands.runOne(name, input);
@@ -355,7 +355,10 @@ class KiroCommands {
355
355
  const ms = MemoryService.getInstance();
356
356
  const projectId = ConfigService.getInstance().getProjectId();
357
357
  const stats = ms.getStats();
358
- line('✓', `project: ${projectId}`);
358
+ const pin = process.env.CLAUDE_RECALL_PROJECT_ID || process.env.CLAUDE_PROJECT_ID;
359
+ line('✓', pin
360
+ ? `project: ${projectId} (PINNED via ${process.env.CLAUDE_RECALL_PROJECT_ID ? 'CLAUDE_RECALL_PROJECT_ID' : 'CLAUDE_PROJECT_ID'})`
361
+ : `project: ${projectId} (from working directory)`);
359
362
  line('•', `total memories (all projects): ${stats.total}`);
360
363
  const rules = ms.loadActiveRules(projectId);
361
364
  const ruleCount = rules.preferences.length + rules.corrections.length + rules.failures.length + rules.devops.length;
@@ -91,16 +91,26 @@ function formatRulesForContext() {
91
91
  return { body: sections.join('\n\n'), total };
92
92
  }
93
93
  /**
94
- * Standing instruction so the agent KNOWS it has persistent memory. Without
95
- * this, a session with no stored rules yet answers "I have no memory between
96
- * conversations" and never calls store_memory when the user says
97
- * "remember ..." the exact failure observed on first use.
94
+ * Standing instruction so the agent KNOWS it has persistent memory — and,
95
+ * critically, that capture happens via BACKGROUND HOOKS independent of the MCP
96
+ * tools. Under enterprise Kiro governance the claude-recall MCP server is
97
+ * dropped from the toolset, but the userPromptSubmit/preToolUse/postToolUse
98
+ * hooks still write to and read from the local DB. Without the "even without
99
+ * the tools" clause the agent answers "I can't store that, I have no memory
100
+ * tools" — technically true of the TOOL, but false of the system, since the
101
+ * hook already captured it. This is the exact confusion observed in a
102
+ * governance-locked session.
98
103
  */
99
- const KIRO_MEMORY_DIRECTIVE = 'You have PERSISTENT MEMORY across sessions via Claude Recall (MCP server "claude-recall": ' +
100
- 'load_rules, store_memory, search_memory, delete_memory, save_checkpoint, load_checkpoint). ' +
101
- 'When the user says "remember ...", "store this", "recall ...", states a preference, or corrects you, ' +
102
- 'call store_memory (tell them what you will store). Use search_memory before decisions when past ' +
103
- 'context might apply. Memories are scoped per project and shared with the user\'s other coding agents.';
104
+ const KIRO_MEMORY_DIRECTIVE = 'You have PERSISTENT MEMORY across sessions via Claude Recall. Your preferences, corrections, and the ' +
105
+ 'rules shown below are captured AUTOMATICALLY by background hooks and injected into your context at the ' +
106
+ 'start of every session this works even if the claude-recall MCP tools (store_memory, search_memory, …) ' +
107
+ 'are NOT in your current toolset (e.g. restricted by your organization\'s policy). ' +
108
+ 'So when the user says "remember …", "recall …", "store this", states a preference, or corrects you: ' +
109
+ 'if the store_memory tool is available, call it and say what you stored; if it is NOT available, simply ' +
110
+ 'confirm the point will be remembered — it is already being captured automatically by the hook. ' +
111
+ 'NEVER tell the user you have no memory between sessions or cannot persist anything. ' +
112
+ 'When the MCP tools ARE available, also use search_memory before decisions where past context may apply. ' +
113
+ 'Memories are scoped per project and shared with the user\'s other coding agents.';
104
114
  /**
105
115
  * agentSpawn — runs once when a Kiro agent activates. Whatever we print is
106
116
  * added to the agent's context, so this IS the load_rules call: rules are in
@@ -97,7 +97,13 @@ class ConfigService {
97
97
  project: {
98
98
  rootDir: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
99
99
  name: process.env.CLAUDE_PROJECT_NAME,
100
- id: process.env.CLAUDE_PROJECT_ID
100
+ // Explicit project pin. Overrides cwd-based scoping entirely — set it
101
+ // to force every memory into one project regardless of which directory
102
+ // the runtime reports (e.g. `kiro --resume` restores the resumed
103
+ // conversation's directory, which may not be the project you mean to
104
+ // work in). CLAUDE_RECALL_PROJECT_ID is the documented name;
105
+ // CLAUDE_PROJECT_ID is honored for backward compatibility.
106
+ id: process.env.CLAUDE_RECALL_PROJECT_ID || process.env.CLAUDE_PROJECT_ID
101
107
  },
102
108
  hooks: {
103
109
  timeout: parseInt(process.env.CLAUDE_RECALL_HOOK_TIMEOUT || '5000'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.28.3",
3
+ "version": "0.28.5",
4
4
  "description": "Persistent memory for Claude Code and Pi with native Skills integration, automatic capture, failure learning, and project scoping",
5
5
  "main": "dist/index.js",
6
6
  "bin": {