claude-recall 0.20.12 → 0.20.14

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.
@@ -8,16 +8,23 @@ source: claude-recall
8
8
 
9
9
  # Corrections
10
10
 
11
- Auto-generated from 7 memories. Last updated: 2026-04-06.
11
+ Auto-generated from 14 memories. Last updated: 2026-04-09.
12
12
 
13
13
  ## Rules
14
14
 
15
15
  - CORRECTION: Memory with complex metadata
16
- - CORRECTION: Never delete claude-recall memories without asking the user first. Show what you plan to delete and get explicit approval before running delete_memory.
17
16
  - CORRECTION: Memory with complex metadata
18
17
  - CORRECTION: Memory with complex metadata
18
+ - CORRECTION: Memory with complex metadata
19
+ - CORRECTION: Memory with complex metadata
20
+ - CORRECTION: Memory with complex metadata
21
+ - CORRECTION: Never delete claude-recall memories without asking the user first. Show what you plan to delete and get explicit approval before running delete_memory.
22
+ - CORRECTION: Memory with complex metadata
23
+ - CORRECTION: wait until next system performnce review
24
+ - CORRECTION: [PreCompact] i just want you tellme what you observe claude recall has done lately
19
25
  - CORRECTION: i just want you tellme what you observe claude recall has done lately
20
26
  - CORRECTION: Use true agentic design where agents decide when to run, not cron jobs
27
+ - CORRECTION: [PreCompact] delete without asking first
21
28
  - CORRECTION: delete without asking first
22
29
 
23
30
  ---
@@ -1,15 +1,22 @@
1
1
  {
2
2
  "topicId": "corrections",
3
- "sourceHash": "c3bf72dbbe482ded411952a50d3c0d6bb04f7c0a8d8ee76c01992ed8f27c8fcd",
4
- "memoryCount": 7,
5
- "generatedAt": "2026-04-06T21:44:03.092Z",
3
+ "sourceHash": "c32cc0fe5f8e375075a893c5c677945df13798f9aeda6763e5ee0a4b8de91a47",
4
+ "memoryCount": 14,
5
+ "generatedAt": "2026-04-09T20:39:45.990Z",
6
6
  "memoryKeys": [
7
+ "memory_1775767185973_5h07cll2t",
8
+ "memory_1775766122931_keod4qo79",
9
+ "memory_1775766107107_jh925r7qo",
10
+ "memory_1775766089878_07ouaa5vy",
11
+ "memory_1775766072071_snw3h6dke",
7
12
  "memory_1775511843079_fzzjymikj",
8
13
  "memory_1775511566528_2zqlbeiyy",
9
14
  "memory_1775500740471_dp5hmdxaf",
10
- "memory_1775499328353_2ade6746o",
15
+ "hook_correction_1775554504739_geen26iio",
16
+ "hook_correction_1775543706749_u29qqfmdd",
11
17
  "hook_correction_1775511596163_oj20xa10q",
12
18
  "hook_correction_1775498361284_9amjuw4le",
19
+ "hook_correction_1775543706706_eqk8p0lm1",
13
20
  "hook_correction_1775511572913_yq1y9tvlo"
14
21
  ]
15
22
  }
@@ -8,18 +8,28 @@ source: claude-recall
8
8
 
9
9
  # Failure Lessons
10
10
 
11
- Auto-generated from 87 memories. Last updated: 2026-04-06.
11
+ Auto-generated from 117 memories. Last updated: 2026-04-09.
12
12
 
13
13
  ## Rules
14
14
 
15
15
  - Check command syntax, file paths, and prerequisites before running
16
16
  - Check inputs and prerequisites before retrying
17
+ - Multiple ssh tunnels + python processes writing same output file causes interleaved corruption and duplicate events. Root cause: nohup orphans from previous session not cleaned before restart.
17
18
  - OpenClaw gateway websocket connection fails from root user context in sandbox; this is a known pairing regression. CLI cannot communicate with gateway from inside the container.
18
19
  - SQLite query syntax error: LIKE clause requires single quotes around string literal, not double quotes
19
20
  - Fault injection into mock-eiap/world-state.json on host will not be visible to sandbox agents until collect.sh runs and pushes updated signals.json into the sandbox. Verify data propagation by checking signals.json inside sandbox, not just host files.
20
21
  - Session extraction prompt design flaw: fix pairing relies on string similarity between commands, but problem fixes often look structurally different from the failing command (e.g. bash script vs echo piped to bash); causes failure→fix patterns to not be recognized
22
+ - nemoclaw's SSRF guard rejects all private/loopback IPs — cannot use ericai-on-host locally without infrastructure patching at three layers; prefer public HTTPS API endpoints instead.
23
+ - Migrating a 'messy and complicated application' as-is risks baking in workarounds, race conditions (e.g., two-writer reload-on-tick), and stale cache patterns (e.g., 5s cache) that no longer make sense in the new architecture — pause to identify and remove technical debt before migration
24
+ - remediate-cell.js still uses legacy direct file mutation (path.join to mock-eiap/world-state.json). When run in sandbox, this fails — must refactor to call worldState.save() + strategic logging at caller boundaries, same pattern as remediate-backhaul.js.
21
25
  - Missing shared tool files in sandbox caused SENTINEL's full chain to fail silently — always verify all tool dependencies (check-delta.js, post-comms.js, self-assess.js, update-run-state.js) are deployed before running agent cycles
22
26
  - When collect.sh fails with 'EIAP endpoint unreachable' inside sandbox, check if EIAP_URL env var is set. If empty, the script should auto-detect sandbox environment and default to http://host.openshell.internal:8090 instead of localhost.
27
+ - Browser-SSO tokens are a fundamental impedance mismatch with headless workloads in K8s — do not attempt to make Kiro (browser-token-based auth) work in containerized environments; use API tokens or service accounts instead
28
+ - Avoid: Command failed: openshell --help 2>&1 | grep -iE "gateway|stop|start|down" | head -15; echo ---gateway-list---; o... → Instead: Check command syntax, file paths, and prerequisites before running
29
+ - Avoid: Command failed: echo ---kill-everything---; pkill -f "stream-sessions.sh" 2>/dev/null; pkill -f "stream-watcher.p... → Instead: Check command syntax, file paths, and prerequisites before running
30
+ - Avoid: Command failed: docker exec openshell-cluster-nemoclaw kubectl exec -n openshell nka-telco -- sh -c "ls -la /sand... → Instead: Check command syntax, file paths, and prerequisites before running
31
+ - Avoid: Command failed: cd /home/ebiarao/.claude/projects/-home-ebiarao-repos-wsl-personal-projects-openclaw-autonomous-t... → Instead: Check command syntax, file paths, and prerequisites before running
32
+ - Avoid: Command failed: B64=$(base64 -w0 < agents/nka/scripts/remediate-backhaul.js); docker exec openshell-cluster-nemoc... → Instead: Check command syntax, file paths, and prerequisites before running
23
33
  - Avoid: Command failed: git commit -m "$(cat <<'EOF'
24
34
  Event-driven agent cascade + direct EIAP polling — two breakthroughs... → Instead: Check command syntax, file paths, and prerequisites before running
25
35
  - Avoid: Command failed: docker exec openshell-cluster-nemoclaw kubectl exec -n openshell nka-telco -- ls -la /sandbox/.op... → Instead: Check command syntax, file paths, and prerequisites before running
@@ -50,6 +60,22 @@ Event-driven agent cascade + direct EIAP polling — two breakthroughs... → In
50
60
  - Avoid: Command failed: npx jest tests/unit/failure-detectors.test.ts 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
51
61
  - SQLite query error: LIKE clause needs proper string literal syntax with single quotes in better-sqlite3
52
62
  - Node.js syntax error: multiline strings in -e flag not properly escaped; newlines break the command parsing
63
+ - {"what_failed":"Bash command failed: sqlite3 ~/.claude-recall/claude-recall.db \"SELECT DISTINCT content FROM memories WHERE content LI...","why_failed":"Exit code 1","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 1","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
64
+ - {"what_failed":"Read failed","why_failed":"File content (17014 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","what_should_do":"Check inputs and prerequisites before retrying","context":"Error: File content (17014 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
65
+ - {"what_failed":"Grep failed","why_failed":"spawn /home/ebiarao/.nvm/versions/node/v20.19.3/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/x64-linux/rg ENOENT","what_should_do":"Check inputs and prerequisites before retrying","context":"Error: spawn /home/ebiarao/.nvm/versions/node/v20.19.3/lib/node_modules/@anthropic-ai/claude-code/vendor/ripgrep/x64-linux/rg ENOENT","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
66
+ - {"what_failed":"Read failed","why_failed":"File content (17014 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","what_should_do":"Check inputs and prerequisites before retrying","context":"Error: File content (17014 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
67
+ - {"what_failed":"Bash command failed: tail -30 ~/.claude-recall/hook-logs/event-processor.log ~/.claude-recall/hook-logs/memory-stop.lo...","why_failed":"Exit code 1\ntail: option used in invalid context -- 3","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 1\ntail: option used in invalid context -- 3","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
68
+ - {"what_failed":"Bash command failed: npx claude-recall outcomes 2>&1","why_failed":"Exit code 1\nerror: unknown command 'outcomes'","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 1\nerror: unknown command 'outcomes'","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
69
+ - Read/ToolSearch hook errors in claude-recall indicate search_enforcer.py rule-load failures — check python3 syntax and MCP tool availability (mcp__claude-recall__mcp__claude-recall__load_rules must be registered).
70
+ - Pre-tool-use hook errors (e.g., '[python3 search_enforcer.py]' load failures) indicate stale or misconfigured rule state. Call mcp__claude-recall__load_rules {} before retrying the tool to refresh rule context.
71
+ - {"what_failed":"Read failed","why_failed":"File content (11705 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","what_should_do":"Check inputs and prerequisites before retrying","context":"Error: File content (11705 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
72
+ - git mv may not fully track deletion of nested files — after moving directories, verify with git status that all expected deletes are staged; 'git mv mock-eiap/ sandbox/mock-eiap/' leaves orphaned nested files (data.js, world/build-ireland.js, world/config.js) that must be explicitly deleted
73
+ - {"what_failed":"Bash command failed: echo ---kill-smoke-eiap---; pkill -f \"mock-eiap/server.js\" 2>/dev/null; sleep 1; echo ---try-even...","why_failed":"Exit code 144\n---kill-smoke-eiap---","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 144\n---kill-smoke-eiap---","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
74
+ - {"what_failed":"Bash command failed: openshell --help 2>&1 | grep -iE \"gateway|stop|start|down\" | head -15; echo ---gateway-list---; o...","why_failed":"Exit code 2\nGATEWAY COMMANDS\n gateway: Manage the gateway lifecycle\n status: Show gateway status and information\n doctor: Diagnose gateway issues\n -g, --gateway <GATEWAY>\n ...","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 2\nGATEWAY COMMANDS\n gateway: Manage the gateway lifecycle\n status: Show gateway status and information\n doctor: Diagnose gateway issues\n -g, --gateway <GATEWAY>\n ...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
75
+ - {"what_failed":"Bash command failed: echo ---kill-everything---; pkill -f \"stream-sessions.sh\" 2>/dev/null; pkill -f \"stream-watcher.p...","why_failed":"Exit code 144\n---kill-everything---","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 144\n---kill-everything---","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
76
+ - {"what_failed":"Bash command failed: docker exec openshell-cluster-nemoclaw kubectl exec -n openshell nka-telco -- sh -c \"ls -la /sand...","why_failed":"Exit code 2\nls: cannot access '/sandbox/.openclaw-data/workspace/artifacts/rebuild-status.json': No such file or directory\ncat: /sandbox/.openclaw-data/workspace/artifacts/rebuild-status.json: No s...","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 2\nls: cannot access '/sandbox/.openclaw-data/workspace/artifacts/rebuild-status.json': No such file or directory\ncat: /sandbox/.openclaw-data/workspace/artifacts/rebuild-status.json: No s...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
77
+ - {"what_failed":"Bash command failed: cd /home/ebiarao/.claude/projects/-home-ebiarao-repos-wsl-personal-projects-openclaw-autonomous-t...","why_failed":"Exit code 2\nls: cannot access 'project_open_remediate_cell_rewrite.md': No such file or directory\nproject_openclaw_egress_filter.md","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 2\nls: cannot access 'project_open_remediate_cell_rewrite.md': No such file or directory\nproject_openclaw_egress_filter.md","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
78
+ - {"what_failed":"Bash command failed: B64=$(base64 -w0 < agents/nka/scripts/remediate-backhaul.js); docker exec openshell-cluster-nemoc...","why_failed":"Exit code 2\nsh: 1: cannot create /sandbox/agents/nka/scripts/remediate-backhaul.js: Directory nonexistent\ncommand terminated with exit code 2","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 2\nsh: 1: cannot create /sandbox/agents/nka/scripts/remediate-backhaul.js: Directory nonexistent\ncommand terminated with exit code 2","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
53
79
  - {"what_failed":"Bash command failed: git commit -m \"$(cat <<'EOF'\nEvent-driven agent cascade + direct EIAP polling — two breakthroughs...","why_failed":"Exit code 128\nerror: gpg failed to sign the data:\n[GNUPG:] KEY_CONSIDERED F3CE7255442A2E7886375262B810E846921AD84F 2\n[GNUPG:] BEGIN_SIGNING H10\n[GNUPG:] USERID_HINT B810E846921AD84F Raoul Biagioni ...","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 128\nerror: gpg failed to sign the data:\n[GNUPG:] KEY_CONSIDERED F3CE7255442A2E7886375262B810E846921AD84F 2\n[GNUPG:] BEGIN_SIGNING H10\n[GNUPG:] USERID_HINT B810E846921AD84F Raoul Biagioni ...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
54
80
  - {"what_failed":"Bash command failed: docker exec openshell-cluster-nemoclaw kubectl exec -n openshell nka-telco -- ls -la /sandbox/.op...","why_failed":"Exit code 1\nfile missing","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 1\nfile missing","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
55
81
  - {"what_failed":"Bash command failed: cat /tmp/claude-1000/-home-ebiarao-repos-wsl-personal-projects-openclaw-autonomous-telco-agents/5...","why_failed":"Exit code 7\n\u001b[1mNAME \u001b[0m \u001b[1mNAMESPACE\u001b[0m \u001b[1mCREATED \u001b[0m \u001b[1mPHASE\u001b[0m\nnka-telco openshell 2026-04-06 21:16:26 \u001b[32mReady\u001b[39m\n\u001b[1mNAME \u001b[0m \u001b[1mNAMESPACE\u001b[0m \u001b[1mCRE...","what_should_do":"Check inputs and prerequisites before retrying","context":"Bash error: Exit code 7\n\u001b[1mNAME \u001b[0m \u001b[1mNAMESPACE\u001b[0m \u001b[1mCREATED \u001b[0m \u001b[1mPHASE\u001b[0m\nnka-telco openshell 2026-04-06 21:16:26 \u001b[32mReady\u001b[39m\n\u001b[1mNAME \u001b[0m \u001b[1mNAMESPACE\u001b[0m \u001b[1mCRE...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
@@ -91,12 +117,16 @@ Event-driven agent cascade + direct EIAP polling — two breakthroughs... → In
91
117
  - {"what_failed":"Read failed","why_failed":"File content (11505 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","what_should_do":"Check inputs and prerequisites before retrying","context":"Error: File content (11505 tokens) exceeds maximum allowed tokens (10000). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whol...","preventative_checks":["Verify tool inputs are correct","Check preconditions"]}
92
118
  - Avoid: Test command reported failures: npx jest tests/unit/failure-detectors.test.ts 2>&1 → Instead: Read test output carefully — exit code 0 does not mean all tests passed
93
119
  - Node.js -e flag cannot parse multiline strings with unescaped newlines in single quotes
120
+ - 'npx claude-recall stats' can fail with 'PreToolUse:Bash hook error' — indicates a disconnect between hook registration and execution. When stats fails, verify search results separately as they may disagree; at least one is silently wrong.
94
121
  - Grep with escaped quotes (pattern="SESSION_EXTRACTION_PROMPT\"") returns no matches — use unescaped pattern="SESSION_EXTRACTION_PROMPT" instead
95
122
  - Gateway token mismatch error — requires verifying token in Control UI settings via dashboard URL
96
123
  - claude-recall reconnection failed after reinstall
97
124
  - claude-recall reconnection failed after reinstall attempt
98
125
  - Output is showing '>' instead of expected response
99
126
  - Code output returns '>' instead of expected response
127
+ - Bash command base64 encoding long file content failed silently — use base64 -w0 to disable line wrapping and pipe directly into kubectl exec without intermediate variables
128
+ - Read hook error on .gitignore — search_enforcer.py failed to load rules; retry Read with mcp__claude-recall__load_rules first to populate rules before file operations
129
+ - Avoid: Claude backtracked: "Going back to" → Instead: Consider the problem more carefully before starting. Check for similar past failures.
100
130
  - Background command 'Wait one more SENTINEL cycle' failed with exit code 1
101
131
  - Avoid: Claude backtracked: "Let me try another" → Instead: Consider the problem more carefully before starting. Check for similar past failures.
102
132
  - Avoid: Claude backtracked: "let me try a different approach" → Instead: Consider the problem more carefully before starting. Check for similar past failures.
@@ -1,17 +1,27 @@
1
1
  {
2
2
  "topicId": "failure-lessons",
3
- "sourceHash": "0a433bc57da8ed5c3137f8676cae1ec253e4a5037b115400d0e0c9e394789440",
4
- "memoryCount": 87,
5
- "generatedAt": "2026-04-06T21:44:03.038Z",
3
+ "sourceHash": "f5b6a2ae08e9e7fbfe9f9d678b1068fa5eaad0d81bf30ef2e3d065bfcf873626",
4
+ "memoryCount": 117,
5
+ "generatedAt": "2026-04-09T20:21:12.032Z",
6
6
  "memoryKeys": [
7
7
  "promoted_1775498319002_awwrsovw6",
8
8
  "promoted_1775499579486_bxvgjc80r",
9
+ "hook_failure_1775567919637_ado9z7i52",
9
10
  "hook_failure_1775502039213_k4yusuan1",
10
11
  "hook_failure_1772637584921_0tj4rrxnt",
11
12
  "hook_failure_1775503464385_vz5vdzxvs",
12
13
  "hook_failure_1775500959351_j74owpvko",
14
+ "hook_failure_1775637261359_prhnbz9gp",
15
+ "hook_failure_1775571333207_b0jxc7wg9",
16
+ "hook_failure_1775554467902_vefk0wqnx",
13
17
  "hook_failure_1775511109813_ly4cjtde6",
14
18
  "hook_failure_1775509498191_f79imbrs4",
19
+ "hook_failure_1775645996326_0mz9beu5u",
20
+ "hook_failure_non-zero-exit_1775568947522_v9v3s4rcq",
21
+ "hook_failure_non-zero-exit_1775567518525_sa9trys9z",
22
+ "hook_failure_non-zero-exit_1775566023853_x9yadg72p",
23
+ "hook_failure_non-zero-exit_1775564413767_rjg38weet",
24
+ "hook_failure_non-zero-exit_1775546352476_ifdye1dp6",
15
25
  "hook_failure_non-zero-exit_1775511575363_nen5eyl3b",
16
26
  "hook_failure_non-zero-exit_1775510785418_2xm52g5q8",
17
27
  "hook_failure_non-zero-exit_1775510309712_olv4egt1g",
@@ -41,6 +51,22 @@
41
51
  "hook_failure_non-zero-exit_1772640279977_g3gwlfoqi",
42
52
  "hook_failure_1772637570984_yxs8zmurp",
43
53
  "hook_failure_1772637485532_djep8eysa",
54
+ "hook_failure_1775764137796_nt2gy4gn4",
55
+ "hook_failure_1775764050725_qexdbw5km",
56
+ "hook_failure_1775764035582_dbcp1mjyw",
57
+ "hook_failure_1775764006133_rszt1zn54",
58
+ "hook_failure_1775763649047_jnx0je5mk",
59
+ "hook_failure_1775763633851_mga29nvnf",
60
+ "hook_failure_1775760197556_7kmkzjbph",
61
+ "hook_failure_1775759374123_6l7pw3gqs",
62
+ "hook_failure_1775759306394_fm3fhkc35",
63
+ "hook_failure_1775571478609_zua292iy0",
64
+ "hook_failure_1775570162971_5ii1j8sw3",
65
+ "hook_failure_1775568820718_1d7pfm9s1",
66
+ "hook_failure_1775566682561_jfbkzxzvt",
67
+ "hook_failure_1775565590990_g2pab28yu",
68
+ "hook_failure_1775563474928_wqww7caqd",
69
+ "hook_failure_1775544637722_0vkaiyddz",
44
70
  "hook_failure_1775511332509_3mxf5vtg3",
45
71
  "hook_failure_1775510710817_sn23160zp",
46
72
  "hook_failure_1775510222535_o2t7vm7qp",
@@ -82,12 +108,16 @@
82
108
  "hook_failure_1775498542480_wc23ux8gt",
83
109
  "hook_failure_silent-test-failure_1772640279996_m8f3ks8fw",
84
110
  "hook_failure_1772637495154_i21dho3dv",
111
+ "hook_failure_1775760542139_lm1nf043v",
85
112
  "hook_failure_1775511713477_psti3hr7y",
86
113
  "hook_failure_1775502085189_pnbd0kkg1",
87
114
  "hook_failure_1773410663873_oegccxk83",
88
115
  "hook_failure_1773410656672_kno1aw8wf",
89
116
  "hook_failure_1772637391602_cjj3esg33",
90
117
  "hook_failure_1772637376157_hvco6jbm4",
118
+ "hook_failure_1775546352399_1znn01j7o",
119
+ "hook_failure_1775759908170_k8yreza2o",
120
+ "hook_failure_backtracking_1775564413782_4qgh3envk",
91
121
  "hook_failure_1775509857057_847nm61ld",
92
122
  "hook_failure_backtracking_1775508193524_54wn8o688",
93
123
  "hook_failure_backtracking_1772640280006_snb0x5cgg"
@@ -8,33 +8,15 @@ source: claude-recall
8
8
 
9
9
  # Preferences
10
10
 
11
- Auto-generated from 23 memories. Last updated: 2026-04-06.
11
+ Auto-generated from 5 memories. Last updated: 2026-04-10.
12
12
 
13
13
  ## Rules
14
14
 
15
- - Session test preference 1775511843229
16
- - Test preference 1775511843109-2
17
- - Test preference 1775511843109-1
18
- - Test preference 1775511843109-0
15
+ - Session test preference 1775821892450
16
+ - Test preference 1775821892400-2
17
+ - Test preference 1775821892400-1
18
+ - Test preference 1775821892400-0
19
19
  - Test memory content
20
- - Session test preference 1775500740595
21
- - Test preference 1775500740495-2
22
- - Test preference 1775500740495-1
23
- - Test preference 1775500740495-0
24
- - Test memory content
25
- - Session test preference 1775499328559
26
- - Test preference 1775499328384-2
27
- - Test preference 1775499328384-1
28
- - Test preference 1775499328384-0
29
- - Test memory content
30
- - Session test preference 1775496241643
31
- - After each refactoring, document the changes made. Don't batch documentation to the end — write it as you go.
32
- - Any major refactoring requires exhaustive search to make sure nothing is missed. Always grep/search comprehensively before and after changes to verify no stale references, broken imports, or missed files remain.
33
- - Markdown documentation files should be named with the convention YYYYMMDD_<filename>.md (e.g., 20260402_nemoclaw_integration.md). Use underscores, not hyphens, in the date-prefixed filenames.
34
- - Always ask before deleting anything
35
- - When user questions the scope or complexity of a solution mid-session, pause and clarify the reusable abstractions extracted from the implementation. Identify generalizable patterns (e.g., trigger-watcher, signal-based coordination) that justify the engineering effort beyond the immediate use case.
36
- - When discovering architectural issues, create comprehensive documentation immediately (host/sandbox duality) and add redesign work to TODO list for later iteration.
37
- - For agentic design, agents should decide when to run rather than using cron jobs for scheduling
38
20
 
39
21
  ---
40
22
  *Auto-generated by Claude Recall. Regenerate: `npx claude-recall skills generate`*
@@ -1,31 +1,13 @@
1
1
  {
2
2
  "topicId": "preferences",
3
- "sourceHash": "819c9fa966f5ffabe7b065fa6290958f1601072fb16e47d162bd513228ec08d5",
4
- "memoryCount": 23,
5
- "generatedAt": "2026-04-06T21:44:03.259Z",
3
+ "sourceHash": "9b2ae714e4bdd82091b4fcc5347c2b1a8a2a10d3be57ca57877e943e380a5457",
4
+ "memoryCount": 5,
5
+ "generatedAt": "2026-04-10T11:51:32.469Z",
6
6
  "memoryKeys": [
7
- "memory_1775511843231_at7wjj6kf",
8
- "memory_1775511843181_7ruo2iof1",
9
- "memory_1775511843150_vdcha6qwp",
10
- "memory_1775511843112_mmaq6zmrn",
11
- "memory_1775511842984_cn67mk6m3",
12
- "memory_1775500740596_xok3iey9j",
13
- "memory_1775500740545_op5713e5i",
14
- "memory_1775500740519_ln1fd8ow4",
15
- "memory_1775500740497_f06v303gf",
16
- "memory_1775500740399_ud5aghy9a",
17
- "memory_1775499328561_9m7ao3h8s",
18
- "memory_1775499328480_e2zx9g5ly",
19
- "memory_1775499328425_plvaaxd69",
20
- "memory_1775499328386_dqcav3itu",
21
- "memory_1775499328258_cilhe1owj",
22
- "memory_1775496241645_xtz7cwan4",
23
- "memory_1775208934902_2kovciriy",
24
- "memory_1775208477621_fqa3w21j1",
25
- "memory_1775168901303_o8o0i4952",
26
- "hook_preference_1775511561137_rjm92h4o3",
27
- "hook_preference_1775508193449_aphyh28z9",
28
- "hook_preference_1775503672864_weqgjstda",
29
- "hook_preference_1775498374200_6d21qj14y"
7
+ "memory_1775821892451_dzjdxx6ef",
8
+ "memory_1775821892427_462vxb2gq",
9
+ "memory_1775821892416_8taocju9a",
10
+ "memory_1775821892402_3x8ksma5p",
11
+ "memory_1775821892372_4khhv0wc2"
30
12
  ]
31
13
  }
@@ -117,10 +117,9 @@ class ClaudeRecallCLI {
117
117
  * Get stats for a specific project (includes universal and unscoped memories)
118
118
  */
119
119
  getProjectStats(projectId) {
120
- const allMemories = this.memoryService.search('');
121
- const projectMemories = allMemories.filter(m => m.project_id === projectId ||
122
- m.scope === 'universal' ||
123
- m.project_id === null);
120
+ // Use getAllByProject for true enumeration. search() pre-ranks and caps
121
+ // at top-5, which silently masks most memories from stats.
122
+ const projectMemories = this.memoryService.getAllByProject(projectId);
124
123
  // Calculate byType breakdown
125
124
  const byType = {};
126
125
  for (const mem of projectMemories) {
@@ -135,7 +134,8 @@ class ClaudeRecallCLI {
135
134
  * Show skills evolution breakdown
136
135
  */
137
136
  showSkillsEvolution() {
138
- const allMemories = this.memoryService.search('');
137
+ const projectId = config_1.ConfigService.getInstance().getProjectId();
138
+ const allMemories = this.memoryService.getAllByProject(projectId);
139
139
  const devopsMemories = allMemories.filter(m => m.type === 'devops');
140
140
  if (devopsMemories.length === 0) {
141
141
  return;
@@ -182,7 +182,8 @@ class ClaudeRecallCLI {
182
182
  // Rough estimation:
183
183
  // - Each memory saves ~200 tokens (vs repeating to LLM)
184
184
  // - DevOps memories save ~1,500 tokens each (vs loading reference files)
185
- const allMemories = this.memoryService.search('');
185
+ const projectId = config_1.ConfigService.getInstance().getProjectId();
186
+ const allMemories = this.memoryService.getAllByProject(projectId);
186
187
  const devopsCount = allMemories.filter(m => m.type === 'devops').length;
187
188
  const otherCount = stats.total - devopsCount;
188
189
  const devopsSavings = devopsCount * 1500;
@@ -224,11 +225,9 @@ class ClaudeRecallCLI {
224
225
  * Show failure memories (v0.7.0)
225
226
  */
226
227
  showFailures(options) {
227
- const allMemories = this.memoryService.search('');
228
- let failures = allMemories.filter(m => m.type === 'failure');
229
- if (options.project) {
230
- failures = failures.filter(m => m.project_id === options.project);
231
- }
228
+ const projectId = options.project || config_1.ConfigService.getInstance().getProjectId();
229
+ const allMemories = this.memoryService.getAllByProject(projectId);
230
+ const failures = allMemories.filter(m => m.type === 'failure');
232
231
  // Sort by timestamp (newest first)
233
232
  failures.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
234
233
  // Limit results
@@ -354,8 +353,8 @@ class ClaudeRecallCLI {
354
353
  // Determine search scope
355
354
  let results;
356
355
  if (options.global) {
357
- // Global search: all memories
358
- results = this.memoryService.search(query);
356
+ // Global search: explicit cross-project opt-in
357
+ results = this.memoryService.search(query, { includeAllProjects: true });
359
358
  }
360
359
  else if (options.project) {
361
360
  // Project-specific search: project + universal
@@ -408,8 +407,10 @@ class ClaudeRecallCLI {
408
407
  async export(outputPath, options) {
409
408
  const format = options.format || 'json';
410
409
  try {
411
- // Get all memories via search with empty query
412
- const memories = this.memoryService.search('');
410
+ // Export all memories from the current project (+ universal). For a
411
+ // true cross-project dump, the user can run `--project` per project.
412
+ const projectId = config_1.ConfigService.getInstance().getProjectId();
413
+ const memories = this.memoryService.getAllByProject(projectId);
413
414
  if (format === 'json') {
414
415
  const exportData = {
415
416
  version: '0.2.0',
@@ -49,6 +49,7 @@ var __importStar = (this && this.__importStar) || (function () {
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.handleBashFailureWatcher = void 0;
51
51
  exports.handleToolOutcomeWatcher = handleToolOutcomeWatcher;
52
+ exports.shouldCaptureFailure = shouldCaptureFailure;
52
53
  exports.handleToolFailure = handleToolFailure;
53
54
  const fs = __importStar(require("fs"));
54
55
  const path = __importStar(require("path"));
@@ -195,6 +196,37 @@ async function handleToolOutcomeWatcher(input) {
195
196
  }
196
197
  // Backward compatibility alias
197
198
  exports.handleBashFailureWatcher = handleToolOutcomeWatcher;
199
+ /**
200
+ * Quality filter: should this bash failure be captured as a memory?
201
+ * Returns false for exploratory probes, command-not-found, and other low-signal failures.
202
+ */
203
+ function shouldCaptureFailure(command, exitCode, output) {
204
+ // Very short commands (ls, cd, etc.) — not worth capturing
205
+ if (command.trim().length < 5)
206
+ return false;
207
+ // Exploratory probes: commands ending in 2>/dev/null are intentionally suppressing errors
208
+ if (/2>\s*\/dev\/null\s*$/.test(command))
209
+ return false;
210
+ // Command-existence checks: which, type, command -v
211
+ const trimmed = command.trim();
212
+ if (/^(which|type)\s+/i.test(trimmed))
213
+ return false;
214
+ if (/^command\s+-v\s+/i.test(trimmed))
215
+ return false;
216
+ // Command not found (exit code 127 or output contains "command not found")
217
+ if (exitCode === '127')
218
+ return false;
219
+ if (/command not found/i.test(output))
220
+ return false;
221
+ // No useful output — nothing to learn from
222
+ const cleanOutput = output.replace(/\(no output\)/gi, '').trim();
223
+ if (cleanOutput.length < 10 && !/^(npm|npx|node|python|pip|cargo|go|make|docker|git)\s/.test(trimmed))
224
+ return false;
225
+ // EISDIR / "Is a directory" — file read probes
226
+ if (/is a directory/i.test(output) || /EISDIR/i.test(output))
227
+ return false;
228
+ return true;
229
+ }
198
230
  // --- Bash handler (original bash-failure-watcher logic) ---
199
231
  async function handleBashOutcome(input) {
200
232
  const command = input.tool_input?.command;
@@ -215,6 +247,11 @@ async function handleBashOutcome(input) {
215
247
  }
216
248
  }
217
249
  async function handleBashFailure(command, exitCode, output, sessionId) {
250
+ // Quality filter — skip low-signal failures
251
+ if (!shouldCaptureFailure(command, exitCode, output)) {
252
+ (0, shared_1.hookLog)(HOOK_NAME, `Skipped low-quality failure: ${truncate(command, 60)} (exit ${exitCode})`);
253
+ return;
254
+ }
218
255
  // Dedup check
219
256
  const existing = (0, shared_1.searchExisting)(command);
220
257
  if ((0, shared_1.isDuplicate)(command, existing, 0.7)) {
@@ -161,7 +161,7 @@ class PromptsHandler {
161
161
  * Injects all user preferences into context
162
162
  */
163
163
  async getPreferencesPrompt() {
164
- const preferences = this.memoryStorage.searchByContext({ type: 'preference' });
164
+ const preferences = this.memoryStorage.searchByContext({ type: 'preference', project_id: config_1.ConfigService.getInstance().getProjectId() });
165
165
  const preferencesText = this.formatPreferencesForPrompt(preferences);
166
166
  return {
167
167
  description: 'User coding preferences automatically injected',
@@ -182,7 +182,7 @@ Apply these preferences when generating code or making suggestions.`
182
182
  * Injects relevant project knowledge
183
183
  */
184
184
  async getProjectContextPrompt(topic) {
185
- let knowledge = this.memoryStorage.searchByContext({ type: 'project-knowledge' });
185
+ let knowledge = this.memoryStorage.searchByContext({ type: 'project-knowledge', project_id: config_1.ConfigService.getInstance().getProjectId() });
186
186
  // Filter by topic if provided
187
187
  if (topic) {
188
188
  const results = this.memoryService.search(topic);
@@ -211,7 +211,7 @@ Use this information when working with the project.`
211
211
  * Injects recent correction patterns
212
212
  */
213
213
  async getCorrectionsPrompt() {
214
- const corrections = this.memoryStorage.searchByContext({ type: 'correction' });
214
+ const corrections = this.memoryStorage.searchByContext({ type: 'correction', project_id: config_1.ConfigService.getInstance().getProjectId() });
215
215
  // Get most recent 10
216
216
  const recentCorrections = corrections
217
217
  .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ResourcesHandler = void 0;
4
4
  const memory_1 = require("../services/memory");
5
5
  const logging_1 = require("../services/logging");
6
+ const config_1 = require("../services/config");
6
7
  /**
7
8
  * Handles MCP Resources protocol
8
9
  * Resources are static/semi-static context that Claude Code can subscribe to
@@ -141,7 +142,7 @@ class ResourcesHandler {
141
142
  * Get preferences resource content
142
143
  */
143
144
  async getPreferencesResource(uri) {
144
- const memories = this.memoryStorage.searchByContext({ type: 'preference' });
145
+ const memories = this.memoryStorage.searchByContext({ type: 'preference', project_id: config_1.ConfigService.getInstance().getProjectId() });
145
146
  // Format preferences for easy consumption
146
147
  const preferences = memories.map(m => ({
147
148
  key: m.key,
@@ -163,7 +164,7 @@ class ResourcesHandler {
163
164
  * Get project knowledge resource content
164
165
  */
165
166
  async getProjectKnowledgeResource(uri) {
166
- const memories = this.memoryStorage.searchByContext({ type: 'project-knowledge' });
167
+ const memories = this.memoryStorage.searchByContext({ type: 'project-knowledge', project_id: config_1.ConfigService.getInstance().getProjectId() });
167
168
  const knowledge = memories.map(m => ({
168
169
  key: m.key,
169
170
  content: m.value,
@@ -182,7 +183,7 @@ class ResourcesHandler {
182
183
  * Get recent corrections resource content
183
184
  */
184
185
  async getCorrectionsResource(uri) {
185
- const memories = this.memoryStorage.searchByContext({ type: 'correction' });
186
+ const memories = this.memoryStorage.searchByContext({ type: 'correction', project_id: config_1.ConfigService.getInstance().getProjectId() });
186
187
  // Get most recent 10 corrections
187
188
  const recentCorrections = memories
188
189
  .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0))
@@ -206,7 +207,7 @@ class ResourcesHandler {
206
207
  async getActiveContextResource(uri) {
207
208
  // Get top 5 most accessed memories from last 24 hours
208
209
  const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);
209
- const allMemories = this.memoryStorage.searchByContext({});
210
+ const allMemories = this.memoryStorage.searchByContext({ project_id: config_1.ConfigService.getInstance().getProjectId() });
210
211
  const recentMemories = allMemories
211
212
  .filter(m => (m.timestamp || 0) > oneDayAgo)
212
213
  .sort((a, b) => (b.access_count || 0) - (a.access_count || 0))
@@ -36,7 +36,8 @@ class PatternStore {
36
36
  const memories = this.storage.searchByContext({
37
37
  type: 'correction-pattern',
38
38
  project_id: process.env.CLAUDE_PROJECT_DIR,
39
- file_path: undefined
39
+ file_path: undefined,
40
+ includeAllProjects: !process.env.CLAUDE_PROJECT_DIR,
40
41
  });
41
42
  for (const memory of memories) {
42
43
  const stored = memory.value;
@@ -55,7 +56,8 @@ class PatternStore {
55
56
  const patterns = this.storage.searchByContext({
56
57
  type: 'correction-pattern',
57
58
  project_id: process.env.CLAUDE_PROJECT_DIR,
58
- file_path: undefined
59
+ file_path: undefined,
60
+ includeAllProjects: !process.env.CLAUDE_PROJECT_DIR,
59
61
  });
60
62
  return patterns
61
63
  .map(m => m.value)
@@ -230,6 +230,77 @@ class MemoryStorage {
230
230
  });
231
231
  return crypto.createHash('sha256').update(canonical).digest('hex');
232
232
  }
233
+ /**
234
+ * Find a same-type memory whose text content is a near-duplicate (Jaccard >= 0.85).
235
+ * Returns the key of the matching memory, or null if none found.
236
+ */
237
+ findFuzzyDuplicate(memory) {
238
+ const newText = this.extractText(memory.value).toLowerCase();
239
+ if (newText.length < 40)
240
+ return null; // Too short to fuzzy-match reliably
241
+ // Scope fuzzy dedup to same type AND same project (or both null/universal)
242
+ const projectFilter = memory.project_id
243
+ ? 'AND project_id = ?'
244
+ : 'AND (project_id IS NULL OR project_id = \'\')';
245
+ const params = [memory.type, memory.key];
246
+ if (memory.project_id)
247
+ params.push(memory.project_id);
248
+ const candidates = this.db.prepare(`SELECT key, value FROM memories WHERE type = ? AND key != ? ${projectFilter}`).all(...params);
249
+ for (const candidate of candidates) {
250
+ let candidateText;
251
+ try {
252
+ const parsed = JSON.parse(candidate.value);
253
+ candidateText = this.extractText(parsed).toLowerCase();
254
+ }
255
+ catch {
256
+ candidateText = candidate.value.toLowerCase();
257
+ }
258
+ if (this.jaccardSimilarity(newText, candidateText) >= 0.65) {
259
+ return candidate.key;
260
+ }
261
+ }
262
+ return null;
263
+ }
264
+ extractText(value) {
265
+ if (typeof value === 'string')
266
+ return value;
267
+ if (value && typeof value === 'object') {
268
+ // Collect all leaf string values, ignoring JSON keys
269
+ const leaves = [];
270
+ const collect = (obj) => {
271
+ if (typeof obj === 'string') {
272
+ leaves.push(obj);
273
+ return;
274
+ }
275
+ if (obj && typeof obj === 'object') {
276
+ for (const v of Object.values(obj))
277
+ collect(v);
278
+ }
279
+ };
280
+ collect(value);
281
+ return leaves.join(' ');
282
+ }
283
+ return String(value);
284
+ }
285
+ jaccardSimilarity(a, b) {
286
+ const tokenize = (s) => {
287
+ const words = s.replace(/[^a-z0-9\s]/g, ' ').split(/\s+/).filter(Boolean);
288
+ return new Set(words.filter(w => !MemoryStorage.STOP_WORDS.has(w)));
289
+ };
290
+ const wordsA = tokenize(a);
291
+ const wordsB = tokenize(b);
292
+ if (wordsA.size === 0 && wordsB.size === 0)
293
+ return 1;
294
+ if (wordsA.size === 0 || wordsB.size === 0)
295
+ return 0;
296
+ let intersection = 0;
297
+ for (const w of wordsA) {
298
+ if (wordsB.has(w))
299
+ intersection++;
300
+ }
301
+ const union = wordsA.size + wordsB.size - intersection;
302
+ return union === 0 ? 0 : intersection / union;
303
+ }
233
304
  save(memory) {
234
305
  const contentHash = this.computeContentHash(memory.value, memory.type);
235
306
  // Write-time dedup: check if identical content already exists under a different key
@@ -240,6 +311,13 @@ class MemoryStorage {
240
311
  this.db.pragma('wal_checkpoint(TRUNCATE)');
241
312
  return;
242
313
  }
314
+ // Fuzzy dedup: check if a same-type memory with very similar content exists
315
+ const fuzzyMatch = this.findFuzzyDuplicate(memory);
316
+ if (fuzzyMatch) {
317
+ this.db.prepare('UPDATE memories SET timestamp = ?, access_count = access_count + 1 WHERE key = ?').run(Date.now(), fuzzyMatch);
318
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
319
+ return;
320
+ }
243
321
  const stmt = this.db.prepare(`
244
322
  INSERT OR REPLACE INTO memories
245
323
  (key, value, type, project_id, file_path, timestamp, relevance_score, access_count,
@@ -296,6 +374,13 @@ class MemoryStorage {
296
374
  };
297
375
  }
298
376
  searchByContext(context) {
377
+ // Fail loud when called without scope. This prevents silent cross-project
378
+ // leakage that the previous default-allow behaviour caused. Callers that
379
+ // genuinely want all projects must opt in with includeAllProjects: true.
380
+ if (!context.project_id && !context.includeAllProjects) {
381
+ throw new Error('searchByContext requires context.project_id or context.includeAllProjects=true. ' +
382
+ 'Calling without either would silently return memories from all projects.');
383
+ }
299
384
  let query = 'SELECT * FROM memories WHERE 1=1';
300
385
  const params = [];
301
386
  if (context.project_id) {
@@ -303,6 +388,7 @@ class MemoryStorage {
303
388
  query += ' AND (project_id = ? OR scope = ? OR project_id IS NULL)';
304
389
  params.push(context.project_id, 'universal');
305
390
  }
391
+ // includeAllProjects: no project filter — caller explicitly asked for everything.
306
392
  if (context.file_path) {
307
393
  query += ' AND file_path = ?';
308
394
  params.push(context.file_path);
@@ -533,3 +619,9 @@ class MemoryStorage {
533
619
  }
534
620
  }
535
621
  exports.MemoryStorage = MemoryStorage;
622
+ MemoryStorage.STOP_WORDS = new Set([
623
+ 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
624
+ 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as',
625
+ 'and', 'or', 'but', 'not', 'no', 'do', 'does', 'did', 'this', 'that',
626
+ 'it', 'its', 'via', 'can', 'should', 'will', 'would', 'may', 'might',
627
+ ]);
@@ -134,12 +134,16 @@ class ConfigService {
134
134
  return { ...this.config };
135
135
  }
136
136
  getDatabasePath() {
137
- // ALWAYS use ~/.claude-recall/claude-recall.db regardless of environment variables
138
- // This ensures consistency across CLI and MCP server
139
- const dbDir = path.join(os.homedir(), '.claude-recall');
140
- const dbPath = path.join(dbDir, 'claude-recall.db');
141
- // Ensure database directory exists
142
- if (!fs.existsSync(dbDir)) {
137
+ // Honor CLAUDE_RECALL_DB_PATH / CLAUDE_RECALL_DB_NAME via the loaded
138
+ // config (loadConfig already reads them with sensible defaults). The
139
+ // previous "always use ~/.claude-recall" behaviour broke test isolation:
140
+ // tests setting CLAUDE_RECALL_DB_PATH silently wrote to the production
141
+ // DB and polluted it with fixture rows.
142
+ const dbDir = this.config.database.path;
143
+ const dbPath = path.join(dbDir, this.config.database.name);
144
+ // Ensure database directory exists. Skip for in-memory DBs (`:memory:`)
145
+ // and any path that doesn't look like a real filesystem location.
146
+ if (dbDir && dbDir !== ':memory:' && !fs.existsSync(dbDir)) {
143
147
  fs.mkdirSync(dbDir, { recursive: true });
144
148
  }
145
149
  return dbPath;
@@ -109,19 +109,35 @@ class MemoryService {
109
109
  }
110
110
  }
111
111
  /**
112
- * Search memories by keyword
112
+ * Search memories by keyword.
113
+ *
114
+ * Scope rules (post-Fix-2):
115
+ * - Default: scoped to current project (projectId from ConfigService) +
116
+ * universal/unscoped memories.
117
+ * - Pass opts.projectId to scope to a specific project.
118
+ * - Pass opts.includeAllProjects=true to opt into a true global search
119
+ * (used by `claude-recall search --global`).
113
120
  */
114
- search(query, sortBy = 'relevance') {
121
+ search(query, optsOrSortBy = 'relevance') {
122
+ const opts = typeof optsOrSortBy === 'string' ? { sortBy: optsOrSortBy } : optsOrSortBy;
123
+ const sortBy = opts.sortBy || 'relevance';
115
124
  try {
116
- // Use findRelevant with query context for better semantic matching
117
125
  const context = {
118
126
  query: query,
119
- timestamp: Date.now()
127
+ timestamp: Date.now(),
120
128
  };
129
+ if (opts.includeAllProjects) {
130
+ context.includeAllProjects = true;
131
+ }
132
+ else {
133
+ context.project_id = opts.projectId || this.config.getProjectId();
134
+ }
121
135
  const results = this.retrieval.findRelevant(context, sortBy);
122
136
  this.logger.logRetrieval(query, results.length, {
123
137
  searchType: 'contextual',
124
- sortBy
138
+ sortBy,
139
+ projectId: context.project_id,
140
+ includeAllProjects: !!opts.includeAllProjects,
125
141
  });
126
142
  return results;
127
143
  }
@@ -130,6 +146,24 @@ class MemoryService {
130
146
  throw error;
131
147
  }
132
148
  }
149
+ /**
150
+ * Enumerate ALL memories scoped to a project (project + universal + unscoped),
151
+ * with NO ranking and NO result cap.
152
+ *
153
+ * Use this for stats, exports, and any other "give me everything in scope"
154
+ * operation. Do NOT use search() / findRelevant() for enumeration — those
155
+ * pre-rank by type priority and cap at top-5, which silently hides most
156
+ * memories from callers that wanted a complete list.
157
+ */
158
+ getAllByProject(projectId) {
159
+ try {
160
+ return this.storage.searchByContext({ project_id: projectId });
161
+ }
162
+ catch (error) {
163
+ this.logger.logServiceError('MemoryService', 'getAllByProject', error, { projectId });
164
+ throw error;
165
+ }
166
+ }
133
167
  /**
134
168
  * Get memory storage statistics
135
169
  */
@@ -288,6 +288,10 @@ class SkillGenerator {
288
288
  if (projectId) {
289
289
  searchContext.project_id = projectId;
290
290
  }
291
+ else {
292
+ // Skill generation runs across all projects for global skill discovery.
293
+ searchContext.includeAllProjects = true;
294
+ }
291
295
  let memories = this.storage.searchByContext(searchContext);
292
296
  // Filter by devops category if specified
293
297
  if (topic.devopsCategory) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.20.12",
3
+ "version": "0.20.14",
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": {