claude-recall 0.19.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/auto-corrections/SKILL.md +1 -25
- package/.claude/skills/auto-corrections/manifest.json +8 -32
- package/.claude/skills/auto-failure-lessons/SKILL.md +1 -9
- package/.claude/skills/auto-failure-lessons/manifest.json +3 -11
- package/.claude/skills/auto-preferences/SKILL.md +20 -64
- package/.claude/skills/auto-preferences/manifest.json +24 -68
- package/README.md +49 -56
- package/dist/pi/extension.js +296 -0
- package/dist/shared/event-processors.js +400 -0
- package/package.json +7 -2
- package/skills/memory-management.md +32 -0
|
@@ -8,40 +8,16 @@ source: claude-recall
|
|
|
8
8
|
|
|
9
9
|
# Corrections
|
|
10
10
|
|
|
11
|
-
Auto-generated from
|
|
11
|
+
Auto-generated from 6 memories. Last updated: 2026-04-02.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
15
|
-
- CORRECTION: Memory with complex metadata
|
|
16
|
-
- CORRECTION: Memory with complex metadata
|
|
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: Memory with complex metadata
|
|
22
|
-
- CORRECTION: Memory with complex metadata
|
|
23
|
-
- CORRECTION: Memory with complex metadata
|
|
24
|
-
- CORRECTION: Memory with complex metadata
|
|
25
|
-
- CORRECTION: Memory with complex metadata
|
|
26
|
-
- CORRECTION: Memory with complex metadata
|
|
27
|
-
- CORRECTION: Memory with complex metadata
|
|
28
|
-
- CORRECTION: Memory with complex metadata
|
|
29
|
-
- CORRECTION: Memory with complex metadata
|
|
30
|
-
- CORRECTION: Memory with complex metadata
|
|
31
|
-
- CORRECTION: Memory with complex metadata
|
|
32
15
|
- CORRECTION: Memory with complex metadata
|
|
33
16
|
- CORRECTION: Memory with complex metadata
|
|
34
17
|
- CORRECTION: Memory with complex metadata
|
|
35
18
|
- CORRECTION: Memory with complex metadata
|
|
36
19
|
- CORRECTION: License copyright should include user's name instead of 'Claude Recall Contributors'
|
|
37
|
-
- CORRECTION: Replace expired access token with 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
|
|
38
|
-
- CORRECTION: use spaces not tabs for indentation
|
|
39
20
|
- CORRECTION: License copyright should list your name instead of 'Claude Recall Contributors'
|
|
40
|
-
- CORRECTION: cited (loaded 5+ times): 19
|
|
41
|
-
- CORRECTION: cited (loaded 5+ times): 19
|
|
42
|
-
- CORRECTION: cited (loaded 5+ times): 19
|
|
43
|
-
- CORRECTION: cited (loaded 5+ times): 19
|
|
44
|
-
- CORRECTION: cited (loaded 5+ times): 19
|
|
45
21
|
|
|
46
22
|
---
|
|
47
23
|
*Auto-generated by Claude Recall. Regenerate: `npx claude-recall skills generate`*
|
|
@@ -1,38 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"topicId": "corrections",
|
|
3
|
-
"sourceHash": "
|
|
4
|
-
"memoryCount":
|
|
5
|
-
"generatedAt": "2026-04-
|
|
3
|
+
"sourceHash": "b1b937b59846b125f1209888415c3c3ba1eb668bee822d20b08fd6a70783510f",
|
|
4
|
+
"memoryCount": 6,
|
|
5
|
+
"generatedAt": "2026-04-02T20:13:01.816Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"memory_1775152517032_jvetpszkr",
|
|
12
|
-
"memory_1775152503075_3bncrxz1d",
|
|
13
|
-
"memory_1775152493186_ecq01e1gx",
|
|
14
|
-
"memory_1775152395533_qhhh4p5md",
|
|
15
|
-
"memory_1775152294113_jgwg4rw1q",
|
|
16
|
-
"memory_1775152031817_1vsksjun4",
|
|
17
|
-
"memory_1774195109722_gij039r4m",
|
|
18
|
-
"memory_1774191618092_vuxyvq3mw",
|
|
19
|
-
"memory_1774106331272_kg5w7ztfj",
|
|
20
|
-
"memory_1774104588311_65kg08e05",
|
|
21
|
-
"memory_1773140547102_2wmy0cfga",
|
|
22
|
-
"memory_1773065886725_rscm7v2qx",
|
|
23
|
-
"memory_1773063478046_x0ryr9bk4",
|
|
24
|
-
"memory_1773063445947_2r842dw2l",
|
|
25
|
-
"memory_1772641994141_ddvzwdkd9",
|
|
26
|
-
"memory_1772641570519_wmnb2b08w",
|
|
27
|
-
"memory_1772641026962_tqm8ow04r",
|
|
7
|
+
"memory_1775160781806_vnqf355e9",
|
|
8
|
+
"memory_1775159399542_awfryrxp3",
|
|
9
|
+
"memory_1775157082825_8uvrul28i",
|
|
10
|
+
"memory_1775156381094_dr1nihes8",
|
|
28
11
|
"hook_correction_1774180798682_9b5a2hadq",
|
|
29
|
-
"
|
|
30
|
-
"hook_correction_1771112125882_99ihypf8x",
|
|
31
|
-
"hook_correction_1774180805192_4ft4zhfsa",
|
|
32
|
-
"hook_correction_1772638229134_otn9za2il",
|
|
33
|
-
"hook_correction_1772636083381_th3eluwzv",
|
|
34
|
-
"hook_correction_1772635851763_y91ugnjgy",
|
|
35
|
-
"hook_correction_1772633504532_l6yo1jvun",
|
|
36
|
-
"hook_correction_1772633492812_0iqsrgnl2"
|
|
12
|
+
"hook_correction_1774180805192_4ft4zhfsa"
|
|
37
13
|
]
|
|
38
14
|
}
|
|
@@ -8,27 +8,19 @@ source: claude-recall
|
|
|
8
8
|
|
|
9
9
|
# Failure Lessons
|
|
10
10
|
|
|
11
|
-
Auto-generated from
|
|
11
|
+
Auto-generated from 16 memories. Last updated: 2026-04-02.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
15
15
|
- SQLite query syntax error: LIKE clause requires single quotes around string literal, not double quotes
|
|
16
16
|
- Avoid: Command failed: claude-recall outcomes 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
|
|
17
17
|
- Avoid: Command failed: npm whoami 2>&1 && npm config get registry 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
|
|
18
|
-
- npm install -g claude-recall@0.15.36 failed with notarget error
|
|
19
|
-
- claude-recall@0.15.36 does not exist on npm registry (ETARGET error)
|
|
20
|
-
- npm install failed: claude-recall@0.15.36 version not found in registry
|
|
21
|
-
- npm package claude-recall@0.15.36 does not exist - version not found in registry
|
|
22
|
-
- npm package claude-recall@0.15.36 not found - version does not exist in registry
|
|
23
18
|
- Claude-recall MCP Server failed to start with npx claude-recall@latest mcp start
|
|
24
19
|
- Claude-recall MCP Server failed to start with command: npx -y claude-recall@latest mcp start
|
|
25
20
|
- Avoid: Command failed: npm run build 2>&1 | tail -3 && npm test 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
|
|
26
21
|
- Avoid: Command failed: npx jest tests/unit/failure-detectors.test.ts 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
|
|
27
|
-
- Missing dependency: better-sqlite3 module not installed
|
|
28
|
-
- Missing dependency: better-sqlite3 module not installed
|
|
29
22
|
- SQLite query error: LIKE clause needs proper string literal syntax with single quotes in better-sqlite3
|
|
30
23
|
- Node.js syntax error: multiline strings in -e flag not properly escaped; newlines break the command parsing
|
|
31
|
-
- npm package claude-recall@0.15.14 not found in registry - 404 error during installation
|
|
32
24
|
- 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
|
|
33
25
|
- Node.js -e flag cannot parse multiline strings with unescaped newlines in single quotes
|
|
34
26
|
- claude-recall reconnection failed after reinstall
|
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"topicId": "failure-lessons",
|
|
3
|
-
"sourceHash": "
|
|
4
|
-
"memoryCount":
|
|
5
|
-
"generatedAt": "2026-
|
|
3
|
+
"sourceHash": "f3351c090380fb67413721e169ecee6882c991fff7f46e1bbd3b13b208e4c24b",
|
|
4
|
+
"memoryCount": 16,
|
|
5
|
+
"generatedAt": "2026-04-02T18:23:28.296Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
7
|
"hook_failure_1772637584921_0tj4rrxnt",
|
|
8
8
|
"hook_failure_non-zero-exit_1774020949485_6ubuoswae",
|
|
9
9
|
"hook_failure_non-zero-exit_1773409269877_ful451241",
|
|
10
|
-
"hook_failure_1773410916808_5k1r6zo4u",
|
|
11
|
-
"hook_failure_1773410916789_xtrb8j9nw",
|
|
12
|
-
"hook_failure_1773410910825_rd25wy0tf",
|
|
13
|
-
"hook_failure_1773410885720_u05tuf2dk",
|
|
14
|
-
"hook_failure_1773410874391_kbnfssg62",
|
|
15
10
|
"hook_failure_1773409616313_gmsfcbuzh",
|
|
16
11
|
"hook_failure_1773409607268_83ie5yunz",
|
|
17
12
|
"hook_failure_non-zero-exit_1773068793859_0z0ah0743",
|
|
18
13
|
"hook_failure_non-zero-exit_1772640279977_g3gwlfoqi",
|
|
19
|
-
"hook_failure_1772637646276_ow6m7pr34",
|
|
20
|
-
"hook_failure_1772637630961_6gsnac7cw",
|
|
21
14
|
"hook_failure_1772637570984_yxs8zmurp",
|
|
22
15
|
"hook_failure_1772637485532_djep8eysa",
|
|
23
|
-
"hook_failure_1771937269919_jx3wvpyxq",
|
|
24
16
|
"hook_failure_silent-test-failure_1772640279996_m8f3ks8fw",
|
|
25
17
|
"hook_failure_1772637495154_i21dho3dv",
|
|
26
18
|
"hook_failure_1773410663873_oegccxk83",
|
|
@@ -8,75 +8,31 @@ source: claude-recall
|
|
|
8
8
|
|
|
9
9
|
# Preferences
|
|
10
10
|
|
|
11
|
-
Auto-generated from
|
|
11
|
+
Auto-generated from 22 memories. Last updated: 2026-04-02.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
15
|
-
- Session test preference
|
|
16
|
-
- Test preference
|
|
17
|
-
- Test preference
|
|
18
|
-
- Test preference
|
|
19
|
-
- Test memory content
|
|
20
|
-
- Session test preference
|
|
21
|
-
- Test preference
|
|
22
|
-
- Test preference
|
|
23
|
-
- Test preference
|
|
24
|
-
- Test memory content
|
|
25
|
-
- Session test preference
|
|
26
|
-
- Test preference
|
|
27
|
-
- Test preference
|
|
28
|
-
- Test preference
|
|
29
|
-
- Test memory content
|
|
30
|
-
- Session test preference
|
|
31
|
-
- Test preference
|
|
32
|
-
- Test preference
|
|
33
|
-
- Test preference
|
|
34
|
-
- Test memory content
|
|
35
|
-
- Test preference 1775152517049-2
|
|
36
|
-
- Test preference 1775152517049-1
|
|
37
|
-
- Test preference 1775152517049-0
|
|
38
|
-
- Test memory content
|
|
39
|
-
- Test preference 1775152503089-2
|
|
40
|
-
- Test preference 1775152503089-1
|
|
41
|
-
- Test preference 1775152503089-0
|
|
42
|
-
- Test memory content
|
|
43
|
-
- Test preference 1775152493202-2
|
|
44
|
-
- Test preference 1775152493202-1
|
|
45
|
-
- Test preference 1775152493202-0
|
|
46
|
-
- Test memory content
|
|
47
|
-
- Test preference 1775152395578-2
|
|
48
|
-
- Test preference 1775152395578-1
|
|
49
|
-
- Test preference 1775152395578-0
|
|
50
|
-
- Test memory content
|
|
51
|
-
- Test preference 1775152294130-2
|
|
52
|
-
- Test preference 1775152294130-1
|
|
53
|
-
- Test preference 1775152294130-0
|
|
54
|
-
- Test memory content
|
|
55
|
-
- Test preference 1775152031832-2
|
|
56
|
-
- Test preference 1775152031832-1
|
|
57
|
-
- Test preference 1775152031832-0
|
|
15
|
+
- Session test preference 1775160781884
|
|
16
|
+
- Test preference 1775160781820-2
|
|
17
|
+
- Test preference 1775160781820-1
|
|
18
|
+
- Test preference 1775160781820-0
|
|
19
|
+
- Test memory content
|
|
20
|
+
- Session test preference 1775159399698
|
|
21
|
+
- Test preference 1775159399559-2
|
|
22
|
+
- Test preference 1775159399559-1
|
|
23
|
+
- Test preference 1775159399559-0
|
|
24
|
+
- Test memory content
|
|
25
|
+
- Session test preference 1775157082982
|
|
26
|
+
- Test preference 1775157082853-2
|
|
27
|
+
- Test preference 1775157082853-1
|
|
28
|
+
- Test preference 1775157082853-0
|
|
29
|
+
- Test memory content
|
|
30
|
+
- Session test preference 1775156381211
|
|
31
|
+
- Test preference 1775156381110-2
|
|
32
|
+
- Test preference 1775156381110-1
|
|
33
|
+
- Test preference 1775156381110-0
|
|
58
34
|
- Test memory content
|
|
59
35
|
- axios npm package was compromised in a supply chain attack (axios@1.14.1 pulled in malicious plain-crypto-js@4.2.1). Claude Recall is NOT affected — does not use axios. Verified 2026-04-01. If axios is ever added as a dependency, pin the version and audit lockfiles.
|
|
60
|
-
- Test preference 1774195109742-2
|
|
61
|
-
- Test preference 1774195109742-1
|
|
62
|
-
- Test preference 1774195109742-0
|
|
63
|
-
- Test memory content
|
|
64
|
-
- Test preference 1774191618111-2
|
|
65
|
-
- Test preference 1774191618111-1
|
|
66
|
-
- Test preference 1774191618111-0
|
|
67
|
-
- Test memory content
|
|
68
|
-
- a normal preference
|
|
69
|
-
- Session test preference 1774106331339
|
|
70
|
-
- Test preference 1774106331283-2
|
|
71
|
-
- Test preference 1774106331283-1
|
|
72
|
-
- Test preference 1774106331283-0
|
|
73
|
-
- Test memory content
|
|
74
|
-
- Session test preference 1774104588383
|
|
75
|
-
- Test preference 1774104588325-2
|
|
76
|
-
- Test preference 1774104588325-1
|
|
77
|
-
- Test preference 1774104588325-0
|
|
78
|
-
- Test memory content
|
|
79
|
-
- Session test preference 1774020789925
|
|
80
36
|
- Upgrade all projects whenever a new version is pushed
|
|
81
37
|
|
|
82
38
|
---
|
|
@@ -1,74 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"topicId": "preferences",
|
|
3
|
-
"sourceHash": "
|
|
4
|
-
"memoryCount":
|
|
5
|
-
"generatedAt": "2026-04-
|
|
3
|
+
"sourceHash": "d26df6886905013086073ba0031b5e052ec17ae91863e95938269e72d1d5a556",
|
|
4
|
+
"memoryCount": 22,
|
|
5
|
+
"generatedAt": "2026-04-02T20:13:01.897Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"memory_1775152517069_7iyt7gtd2",
|
|
29
|
-
"memory_1775152517050_jyndlwzm3",
|
|
30
|
-
"memory_1775152516970_9jsac5vef",
|
|
31
|
-
"memory_1775152503124_36fasrgv4",
|
|
32
|
-
"memory_1775152503106_rmjhhcaqy",
|
|
33
|
-
"memory_1775152503090_1vgaf4xvt",
|
|
34
|
-
"memory_1775152503030_czylzojcv",
|
|
35
|
-
"memory_1775152493236_kge25b20q",
|
|
36
|
-
"memory_1775152493220_1xpmtploo",
|
|
37
|
-
"memory_1775152493203_cck14tmil",
|
|
38
|
-
"memory_1775152493142_fdxvwri6p",
|
|
39
|
-
"memory_1775152395670_69if6leyw",
|
|
40
|
-
"memory_1775152395636_iatgy1qym",
|
|
41
|
-
"memory_1775152395579_00j66922k",
|
|
42
|
-
"memory_1775152395397_c28bq57a0",
|
|
43
|
-
"memory_1775152294164_6jc19cw4j",
|
|
44
|
-
"memory_1775152294147_0b50nxbf8",
|
|
45
|
-
"memory_1775152294131_isvbcko3z",
|
|
46
|
-
"memory_1775152294061_93dm7kgkv",
|
|
47
|
-
"memory_1775152031897_3owwqu9z2",
|
|
48
|
-
"memory_1775152031871_p5fdk67r0",
|
|
49
|
-
"memory_1775152031833_qtjc1ng2t",
|
|
50
|
-
"memory_1775152031740_5lnk9fdzg",
|
|
51
|
-
"memory_1775030829333_64gdk8kql",
|
|
52
|
-
"memory_1774195109780_8ffmflge1",
|
|
53
|
-
"memory_1774195109763_v6olh83ct",
|
|
54
|
-
"memory_1774195109743_i79j9a9rl",
|
|
55
|
-
"memory_1774195109624_nnh3wrwca",
|
|
56
|
-
"memory_1774191618164_wefq7s9x6",
|
|
57
|
-
"memory_1774191618139_k77izvxnq",
|
|
58
|
-
"memory_1774191618112_hzi64751y",
|
|
59
|
-
"memory_1774191617980_pzspevoct",
|
|
60
|
-
"valid1",
|
|
61
|
-
"memory_1774106331340_cwt88yhle",
|
|
62
|
-
"memory_1774106331311_491hg2a21",
|
|
63
|
-
"memory_1774106331297_0b6bvacd7",
|
|
64
|
-
"memory_1774106331284_ai62szr9y",
|
|
65
|
-
"memory_1774106331222_zjfz8zu59",
|
|
66
|
-
"memory_1774104588384_hy5yej2pk",
|
|
67
|
-
"memory_1774104588351_whsk5rvw3",
|
|
68
|
-
"memory_1774104588338_80cx2o3jv",
|
|
69
|
-
"memory_1774104588325_g4o5ksubl",
|
|
70
|
-
"memory_1774104588255_fy0wv845g",
|
|
71
|
-
"memory_1774020789927_g99mrubvu",
|
|
7
|
+
"memory_1775160781885_fkbnc0s85",
|
|
8
|
+
"memory_1775160781857_gj5y299dr",
|
|
9
|
+
"memory_1775160781842_0xmv146n5",
|
|
10
|
+
"memory_1775160781821_o9ipt8nsc",
|
|
11
|
+
"memory_1775160781747_70e5gbp7j",
|
|
12
|
+
"memory_1775159399702_yg6ynd05n",
|
|
13
|
+
"memory_1775159399631_fmy4s2vhr",
|
|
14
|
+
"memory_1775159399606_xymkvcjfv",
|
|
15
|
+
"memory_1775159399561_o6vpb3fto",
|
|
16
|
+
"memory_1775159399463_0pvhuixyr",
|
|
17
|
+
"memory_1775157082984_oiyt9u65w",
|
|
18
|
+
"memory_1775157082927_29d7mm59z",
|
|
19
|
+
"memory_1775157082892_o9i0jbw76",
|
|
20
|
+
"memory_1775157082855_zlbl2ib0j",
|
|
21
|
+
"memory_1775157082743_fu07oklto",
|
|
22
|
+
"memory_1775156381212_ptlo01224",
|
|
23
|
+
"memory_1775156381158_3p6goornd",
|
|
24
|
+
"memory_1775156381132_ikx95mk0n",
|
|
25
|
+
"memory_1775156381111_1x4fxfu9n",
|
|
26
|
+
"memory_1775156381012_3tw7we7cz",
|
|
27
|
+
"memory_1775154208266_arwo2fctx",
|
|
72
28
|
"hook_preference_1774106575282_45sk4ep52"
|
|
73
29
|
]
|
|
74
30
|
}
|
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# Claude Recall
|
|
2
2
|
|
|
3
|
-
### Persistent, local memory for
|
|
3
|
+
### Persistent, local memory for coding agents — learn from every session.
|
|
4
4
|
|
|
5
|
-
Claude Recall is a **local memory engine
|
|
5
|
+
Claude Recall is a **local memory engine** that gives coding agents something they're missing by default:
|
|
6
6
|
**the ability to learn from you over time.**
|
|
7
7
|
|
|
8
|
+
Works with **Claude Code** (via MCP server + hooks) and **[Pi](https://github.com/mariozechner/pi)** (via native extension). Both share the same local database — a preference learned in one agent is available in the other.
|
|
9
|
+
|
|
8
10
|
Your preferences, project structure, workflows, corrections, and coding style are captured automatically and applied in future sessions — **securely stored on your machine**.
|
|
9
11
|
|
|
10
12
|
---
|
|
@@ -12,8 +14,8 @@ Your preferences, project structure, workflows, corrections, and coding style ar
|
|
|
12
14
|
## Features
|
|
13
15
|
|
|
14
16
|
- **Smart Memory Capture** — LLM-powered classification (via Claude Haiku) detects preferences and corrections from natural language, with silent regex fallback
|
|
15
|
-
- **Project-Scoped Knowledge** — each project gets its own memory namespace; switch projects and
|
|
16
|
-
- **Failure Learning** — captures what failed, why, and what to do instead — so
|
|
17
|
+
- **Project-Scoped Knowledge** — each project gets its own memory namespace; switch projects and the agent switches context automatically
|
|
18
|
+
- **Failure Learning** — captures what failed, why, and what to do instead — so the agent doesn't repeat mistakes
|
|
17
19
|
- **Outcome-Aware Learning** — tracks action outcomes (all tool results, test cycles, user corrections), synthesizes candidate lessons, and promotes validated patterns into active rules automatically
|
|
18
20
|
- **Skill Crystallization** — auto-generates `.claude/skills/auto-*/` files from accumulated memories, using Anthropic's [Agent Skills](https://agentskills.io/) open standard
|
|
19
21
|
- **Local-Only** — SQLite on your machine, no telemetry, no cloud, works fully offline
|
|
@@ -29,60 +31,46 @@ Your preferences, project structure, workflows, corrections, and coding style ar
|
|
|
29
31
|
| Node.js | **20+** | required for better-sqlite3 |
|
|
30
32
|
| OS | macOS / Linux / Windows | WSL supported |
|
|
31
33
|
|
|
32
|
-
### Install
|
|
33
|
-
|
|
34
|
-
Run these from your project directory:
|
|
34
|
+
### Install for Claude Code
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
#
|
|
38
|
-
claude mcp remove claude-recall
|
|
39
|
-
|
|
40
|
-
# 2. Clear npm cache
|
|
41
|
-
npm cache clean --force
|
|
42
|
-
|
|
43
|
-
# 3. Uninstall global claude-recall
|
|
44
|
-
npm uninstall -g claude-recall
|
|
45
|
-
|
|
46
|
-
# 4. Install global claude-recall
|
|
37
|
+
# Install globally
|
|
47
38
|
npm install -g claude-recall
|
|
48
39
|
|
|
49
|
-
#
|
|
40
|
+
# Set up hooks and skills in your project
|
|
50
41
|
claude-recall setup --install
|
|
51
42
|
|
|
52
|
-
#
|
|
43
|
+
# Register MCP server
|
|
53
44
|
claude mcp add claude-recall -- claude-recall mcp start
|
|
54
45
|
```
|
|
55
46
|
|
|
56
|
-
Then restart your Claude Code session.
|
|
47
|
+
Then restart your Claude Code session. For additional projects, only the last two commands are needed.
|
|
57
48
|
|
|
58
|
-
|
|
49
|
+
**Verify:** Ask *"Load my rules"* — Claude should call `mcp__claude-recall__load_rules`.
|
|
59
50
|
|
|
60
|
-
|
|
51
|
+
### Install for Pi
|
|
61
52
|
|
|
62
53
|
```bash
|
|
63
|
-
claude-recall
|
|
64
|
-
claude mcp add claude-recall -- claude-recall mcp start
|
|
54
|
+
pi install npm:claude-recall
|
|
65
55
|
```
|
|
66
56
|
|
|
67
|
-
|
|
57
|
+
That's it. The extension registers tools and loads a skill automatically. No further configuration needed.
|
|
68
58
|
|
|
69
|
-
|
|
59
|
+
**Verify:** Start Pi and ask *"Load my rules"* — Pi should call `recall_load_rules`.
|
|
70
60
|
|
|
71
|
-
|
|
61
|
+
### Shared Database
|
|
72
62
|
|
|
73
|
-
|
|
63
|
+
Both agents use the same database (`~/.claude-recall/claude-recall.db`). Memories are scoped per project by working directory. A correction learned in Claude Code is available in Pi and vice versa.
|
|
74
64
|
|
|
75
65
|
### Upgrading
|
|
76
66
|
|
|
77
|
-
When a new version is published, update the global binary — no per-project reinstall needed:
|
|
78
|
-
|
|
79
67
|
```bash
|
|
80
|
-
|
|
81
|
-
npm uninstall -g claude-recall
|
|
68
|
+
# Claude Code
|
|
82
69
|
npm install -g claude-recall
|
|
83
|
-
```
|
|
84
70
|
|
|
85
|
-
|
|
71
|
+
# Pi
|
|
72
|
+
pi update claude-recall
|
|
73
|
+
```
|
|
86
74
|
|
|
87
75
|
---
|
|
88
76
|
|
|
@@ -90,21 +78,20 @@ Then restart Claude Code sessions in each project to pick up the new version.
|
|
|
90
78
|
|
|
91
79
|
Once installed, Claude Recall works automatically in the background:
|
|
92
80
|
|
|
93
|
-
1. **
|
|
94
|
-
2. **As you work** —
|
|
95
|
-
3. **
|
|
96
|
-
4. **
|
|
97
|
-
5. **Reask detection** —
|
|
98
|
-
6. **Before context compression** —
|
|
99
|
-
7. **Rules sync
|
|
81
|
+
1. **Session start** — active rules are loaded before the first action. In Claude Code, this happens via the `search_enforcer` hook; in Pi, rules are injected into the system prompt automatically
|
|
82
|
+
2. **As you work** — every prompt is classified for corrections and preferences. Natural statements like *"we use tabs here"* or *"no, put tests in `__tests__/`"* are detected and stored
|
|
83
|
+
3. **Tool outcomes** — results from all tools (Bash, Edit, Write, and more) are captured. Failures are stored as memories; Bash failures are paired with successful fixes
|
|
84
|
+
4. **End of session** — session episodes are created, candidate lessons extracted from failures, and a promotion cycle graduates validated patterns into active rules
|
|
85
|
+
5. **Reask detection** — frustration signals ("still broken", "that didn't work") are recorded as outcome events
|
|
86
|
+
6. **Before context compression** — aggressive memory sweep captures important context before the window shrinks
|
|
87
|
+
7. **Rules sync** (Claude Code only) — top 30 rules are exported as typed `.md` files to Claude Code's native memory directory
|
|
100
88
|
|
|
101
|
-
|
|
89
|
+
Classification uses Claude Haiku (via `ANTHROPIC_API_KEY`) with silent regex fallback. No configuration needed.
|
|
102
90
|
|
|
103
|
-
**Next session:** `load_rules` returns everything captured previously —
|
|
91
|
+
**Next session:** `load_rules` returns everything captured previously — the agent applies your preferences without being told twice.
|
|
104
92
|
|
|
105
93
|
```bash
|
|
106
94
|
# Verify it's working
|
|
107
|
-
cat ~/.claude-recall/hook-logs/correction-detector.log
|
|
108
95
|
claude-recall stats
|
|
109
96
|
claude-recall search "preference"
|
|
110
97
|
```
|
|
@@ -113,22 +100,28 @@ claude-recall search "preference"
|
|
|
113
100
|
|
|
114
101
|
## How It Works
|
|
115
102
|
|
|
116
|
-
Claude Recall
|
|
103
|
+
Claude Recall provides four memory tools backed by a local SQLite database with WAL mode, content-hash deduplication, and automatic compaction. The tools are exposed differently depending on the agent:
|
|
104
|
+
|
|
105
|
+
- **Claude Code** — MCP server with four tools and seven prompts, plus file-system hooks for automatic capture
|
|
106
|
+
- **Pi** — native extension with registered tools and event handlers, plus a skill file for behavioral guidance
|
|
107
|
+
|
|
108
|
+
| Tool | Claude Code | Pi |
|
|
109
|
+
| ---- | ----------- | --- |
|
|
110
|
+
| Load rules | `mcp__claude-recall__load_rules` | `recall_load_rules` |
|
|
111
|
+
| Store memory | `mcp__claude-recall__store_memory` | `recall_store_memory` |
|
|
112
|
+
| Search memory | `mcp__claude-recall__search_memory` | `recall_search_memory` |
|
|
113
|
+
| Delete memory | `mcp__claude-recall__delete_memory` | `recall_delete_memory` |
|
|
117
114
|
|
|
118
|
-
###
|
|
115
|
+
### Skills
|
|
119
116
|
|
|
120
|
-
Claude Recall uses
|
|
117
|
+
Claude Recall uses skill files to teach agents when and how to use memory tools:
|
|
121
118
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
| `load_rules` | Load all active rules (preferences, corrections, failures, devops) at the start of a task |
|
|
125
|
-
| `store_memory` | Save new knowledge — preferences, corrections, devops rules, failures |
|
|
126
|
-
| `search_memory` | Search memories by keyword, ranked by relevance |
|
|
127
|
-
| `delete_memory` | Delete a specific memory by ID |
|
|
119
|
+
- **Claude Code** — uses Anthropic's [Agent Skills](https://agentskills.io/) open standard. A core skill (`.claude/skills/memory-management/SKILL.md`) guides memory behavior with progressive disclosure. Auto-generated skills (`.claude/skills/auto-*/`) crystallize from accumulated memories. See Anthropic's [blog post](https://claude.com/blog/equipping-agents-for-the-real-world-with-agent-skills) for more.
|
|
120
|
+
- **Pi** — ships a `skills/memory-management.md` skill loaded via Pi's package manifest
|
|
128
121
|
|
|
129
|
-
### Outcome-Aware Learning
|
|
122
|
+
### Outcome-Aware Learning
|
|
130
123
|
|
|
131
|
-
Claude Recall tracks what happens *after*
|
|
124
|
+
Claude Recall tracks what happens *after* the agent acts — not just what was said. The outcome processing pipeline:
|
|
132
125
|
|
|
133
126
|
```
|
|
134
127
|
action → outcome event → episode → candidate lesson → promotion → active rule
|
|
@@ -216,7 +209,7 @@ claude-recall hook run memory-sync # Stop + PreCompact hook (syncs rul
|
|
|
216
209
|
|
|
217
210
|
## Project Scoping
|
|
218
211
|
|
|
219
|
-
Each project gets isolated memory based on its working directory. **Project ID** is derived from the `cwd`
|
|
212
|
+
Each project gets isolated memory based on its working directory. **Project ID** is derived from the `cwd` passed by the agent. Universal memories (no project scope) are available everywhere. Switching projects switches memory automatically.
|
|
220
213
|
|
|
221
214
|
Database location: `~/.claude-recall/claude-recall.db` (shared file, scoped by `project_id` column).
|
|
222
215
|
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude Recall — Pi Extension
|
|
4
|
+
*
|
|
5
|
+
* Provides persistent memory tools for the Pi coding agent.
|
|
6
|
+
* Shares the same SQLite database as the Claude Code integration.
|
|
7
|
+
*
|
|
8
|
+
* Install: pi install npm:claude-recall
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.default = default_1;
|
|
12
|
+
const memory_1 = require("../services/memory");
|
|
13
|
+
const config_1 = require("../services/config");
|
|
14
|
+
const outcome_storage_1 = require("../services/outcome-storage");
|
|
15
|
+
const logging_1 = require("../services/logging");
|
|
16
|
+
const event_processors_1 = require("../shared/event-processors");
|
|
17
|
+
const LOAD_RULES_DIRECTIVE = 'INSTRUCTION: Before your FIRST edit or bash action, you MUST output an\n' +
|
|
18
|
+
'"Applying memories:" section listing which rules below apply to the current task.\n' +
|
|
19
|
+
'If none apply, say so. Do NOT skip this step.\n' +
|
|
20
|
+
'As you work, cite each applied rule inline: (applied from memory: <rule summary>)\n' +
|
|
21
|
+
'If a rule conflicts with your plan, follow the rule — it reflects a user decision.';
|
|
22
|
+
/** Format a memory value for display. */
|
|
23
|
+
function extractVal(value) {
|
|
24
|
+
if (typeof value === 'string')
|
|
25
|
+
return value;
|
|
26
|
+
if (typeof value === 'object' && value !== null) {
|
|
27
|
+
return value.content || value.value || JSON.stringify(value);
|
|
28
|
+
}
|
|
29
|
+
return String(value ?? '');
|
|
30
|
+
}
|
|
31
|
+
/** Format active rules as markdown sections. */
|
|
32
|
+
function formatRules(rules) {
|
|
33
|
+
const sections = [];
|
|
34
|
+
if (rules.preferences.length > 0) {
|
|
35
|
+
sections.push('## Preferences\n' + rules.preferences.map(m => `- ${extractVal(m.value)}`).join('\n'));
|
|
36
|
+
}
|
|
37
|
+
if (rules.corrections.length > 0) {
|
|
38
|
+
sections.push('## Corrections\n' + rules.corrections.map(m => `- ${extractVal(m.value)}`).join('\n'));
|
|
39
|
+
}
|
|
40
|
+
if (rules.failures.length > 0) {
|
|
41
|
+
sections.push('## Failures\n' + rules.failures.map(m => `- ${extractVal(m.value)}`).join('\n'));
|
|
42
|
+
}
|
|
43
|
+
if (rules.devops.length > 0) {
|
|
44
|
+
sections.push('## DevOps Rules\n' + rules.devops.map(m => `- ${extractVal(m.value)}`).join('\n'));
|
|
45
|
+
}
|
|
46
|
+
return sections.join('\n\n');
|
|
47
|
+
}
|
|
48
|
+
function default_1(pi) {
|
|
49
|
+
let projectId = '';
|
|
50
|
+
let sessionId = `pi_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
|
51
|
+
let rulesLoaded = false;
|
|
52
|
+
const collectedUserTexts = [];
|
|
53
|
+
// Route logs through Pi's UI when available
|
|
54
|
+
(0, event_processors_1.setLogFunction)((source, msg) => {
|
|
55
|
+
try {
|
|
56
|
+
logging_1.LoggingService.getInstance().info(source, msg);
|
|
57
|
+
}
|
|
58
|
+
catch { /* silent */ }
|
|
59
|
+
});
|
|
60
|
+
// --- Session init: set project context from cwd ---
|
|
61
|
+
pi.on('session_start', (_event, ctx) => {
|
|
62
|
+
projectId = ctx.cwd.split('/').pop() || 'unknown';
|
|
63
|
+
rulesLoaded = false;
|
|
64
|
+
collectedUserTexts.length = 0;
|
|
65
|
+
(0, event_processors_1.resetPendingFailures)();
|
|
66
|
+
try {
|
|
67
|
+
config_1.ConfigService.getInstance().updateConfig({
|
|
68
|
+
project: { rootDir: ctx.cwd },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Non-critical
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// --- Event: inject rules before first agent turn ---
|
|
76
|
+
pi.on('before_agent_start', (_event, _ctx) => {
|
|
77
|
+
if (rulesLoaded)
|
|
78
|
+
return;
|
|
79
|
+
rulesLoaded = true;
|
|
80
|
+
try {
|
|
81
|
+
const ms = memory_1.MemoryService.getInstance();
|
|
82
|
+
const rules = ms.loadActiveRules(projectId || undefined);
|
|
83
|
+
const body = formatRules(rules);
|
|
84
|
+
if (body) {
|
|
85
|
+
return { systemPrompt: _event.systemPrompt + '\n\n' + LOAD_RULES_DIRECTIVE + '\n\n---\n\n' + body };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Non-critical — tools still available as fallback
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// --- Event: capture tool outcomes ---
|
|
93
|
+
pi.on('tool_result', (event, _ctx) => {
|
|
94
|
+
const output = event.content
|
|
95
|
+
.filter((c) => c.type === 'text')
|
|
96
|
+
.map(c => c.text)
|
|
97
|
+
.join('\n');
|
|
98
|
+
(0, event_processors_1.processToolOutcome)(event.toolName, event.input, output, event.isError, sessionId);
|
|
99
|
+
});
|
|
100
|
+
// --- Event: detect corrections from user input ---
|
|
101
|
+
pi.on('input', (event, _ctx) => {
|
|
102
|
+
collectedUserTexts.push(event.text);
|
|
103
|
+
(0, event_processors_1.processUserInput)(event.text, sessionId).catch(() => { });
|
|
104
|
+
return { action: 'continue' };
|
|
105
|
+
});
|
|
106
|
+
// --- Event: session end — episode + promotion ---
|
|
107
|
+
pi.on('session_shutdown', (_event, _ctx) => {
|
|
108
|
+
(0, event_processors_1.processSessionEnd)(collectedUserTexts, sessionId, projectId).catch(() => { });
|
|
109
|
+
});
|
|
110
|
+
// --- Event: pre-compaction — aggressive capture ---
|
|
111
|
+
pi.on('session_before_compact', (event, _ctx) => {
|
|
112
|
+
// Extract user texts from branch entries
|
|
113
|
+
const texts = [];
|
|
114
|
+
for (const entry of (event.branchEntries || [])) {
|
|
115
|
+
if (entry?.role === 'user' && typeof entry.content === 'string') {
|
|
116
|
+
texts.push(entry.content);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (texts.length > 0) {
|
|
120
|
+
(0, event_processors_1.processPreCompact)(texts, sessionId).catch(() => { });
|
|
121
|
+
}
|
|
122
|
+
// Re-inject rules after compaction
|
|
123
|
+
rulesLoaded = false;
|
|
124
|
+
});
|
|
125
|
+
// --- Tool: recall_load_rules ---
|
|
126
|
+
pi.registerTool({
|
|
127
|
+
name: 'recall_load_rules',
|
|
128
|
+
label: 'Load Rules',
|
|
129
|
+
description: 'Load all stored rules (preferences, corrections, failures, devops). Call at the start of every task.',
|
|
130
|
+
promptSnippet: 'Load stored rules and preferences from memory',
|
|
131
|
+
parameters: {},
|
|
132
|
+
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
133
|
+
try {
|
|
134
|
+
const ms = memory_1.MemoryService.getInstance();
|
|
135
|
+
const rules = ms.loadActiveRules(projectId || undefined);
|
|
136
|
+
const body = formatRules(rules);
|
|
137
|
+
const totalRules = rules.preferences.length + rules.corrections.length +
|
|
138
|
+
rules.failures.length + rules.devops.length;
|
|
139
|
+
// Track retrievals
|
|
140
|
+
try {
|
|
141
|
+
const os = outcome_storage_1.OutcomeStorage.getInstance();
|
|
142
|
+
const all = [...rules.preferences, ...rules.corrections, ...rules.failures, ...rules.devops];
|
|
143
|
+
for (const m of all)
|
|
144
|
+
os.recordRetrieval(m.key);
|
|
145
|
+
}
|
|
146
|
+
catch { /* non-critical */ }
|
|
147
|
+
const text = body
|
|
148
|
+
? `${LOAD_RULES_DIRECTIVE}\n\n---\n\n${body}`
|
|
149
|
+
: 'No active rules found. This may be a new project.';
|
|
150
|
+
return {
|
|
151
|
+
content: [{ type: 'text', text }],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
return {
|
|
156
|
+
content: [{ type: 'text', text: `Failed to load rules: ${err.message}` }],
|
|
157
|
+
isError: true,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
// --- Tool: recall_store_memory ---
|
|
163
|
+
pi.registerTool({
|
|
164
|
+
name: 'recall_store_memory',
|
|
165
|
+
label: 'Store Memory',
|
|
166
|
+
description: 'Store a rule or learning. Use for: corrections, preferences, devops rules, failures.',
|
|
167
|
+
promptSnippet: 'Store a rule, correction, or preference to memory',
|
|
168
|
+
parameters: {},
|
|
169
|
+
async execute(_id, params, _signal, _onUpdate, _ctx) {
|
|
170
|
+
try {
|
|
171
|
+
const content = params.content;
|
|
172
|
+
if (!content || typeof content !== 'string') {
|
|
173
|
+
return {
|
|
174
|
+
content: [{ type: 'text', text: 'Error: content is required and must be a string' }],
|
|
175
|
+
isError: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const validTypes = ['preference', 'correction', 'devops', 'failure', 'project-knowledge'];
|
|
179
|
+
const metadata = params.metadata || {};
|
|
180
|
+
const detectedType = (metadata.type && validTypes.includes(metadata.type))
|
|
181
|
+
? metadata.type : 'preference';
|
|
182
|
+
const key = `memory_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
183
|
+
const ms = memory_1.MemoryService.getInstance();
|
|
184
|
+
ms.store({
|
|
185
|
+
key,
|
|
186
|
+
value: { content, ...metadata, sessionId, timestamp: Date.now() },
|
|
187
|
+
type: detectedType,
|
|
188
|
+
context: {
|
|
189
|
+
sessionId,
|
|
190
|
+
projectId: params.scope === 'project' ? projectId : undefined,
|
|
191
|
+
timestamp: Date.now(),
|
|
192
|
+
scope: params.scope || null,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: 'text', text: JSON.stringify({
|
|
197
|
+
id: key,
|
|
198
|
+
success: true,
|
|
199
|
+
activeRule: `Stored as active rule:\n- ${content}`,
|
|
200
|
+
type: detectedType,
|
|
201
|
+
_directive: 'Apply this rule immediately.',
|
|
202
|
+
}) }],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
return {
|
|
207
|
+
content: [{ type: 'text', text: `Failed to store memory: ${err.message}` }],
|
|
208
|
+
isError: true,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
// --- Tool: recall_search_memory ---
|
|
214
|
+
pi.registerTool({
|
|
215
|
+
name: 'recall_search_memory',
|
|
216
|
+
label: 'Search Memory',
|
|
217
|
+
description: 'Search memories by keyword. Returns matched memories ranked by relevance.',
|
|
218
|
+
promptSnippet: 'Search stored memories by keyword',
|
|
219
|
+
parameters: {},
|
|
220
|
+
async execute(_id, params, _signal, _onUpdate, _ctx) {
|
|
221
|
+
try {
|
|
222
|
+
const query = params.query;
|
|
223
|
+
if (!query || typeof query !== 'string') {
|
|
224
|
+
return {
|
|
225
|
+
content: [{ type: 'text', text: 'Error: query is required and must be a string' }],
|
|
226
|
+
isError: true,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const limit = Math.min(Math.max(params.limit || 10, 1), 25);
|
|
230
|
+
const ms = memory_1.MemoryService.getInstance();
|
|
231
|
+
const searchCtx = { query, projectId, timestamp: Date.now() };
|
|
232
|
+
if (params.type)
|
|
233
|
+
searchCtx.type = params.type;
|
|
234
|
+
const results = ms.findRelevant(searchCtx);
|
|
235
|
+
const top = results.slice(0, limit);
|
|
236
|
+
// Track retrievals
|
|
237
|
+
try {
|
|
238
|
+
const os = outcome_storage_1.OutcomeStorage.getInstance();
|
|
239
|
+
for (const r of top)
|
|
240
|
+
os.recordRetrieval(r.key);
|
|
241
|
+
}
|
|
242
|
+
catch { /* non-critical */ }
|
|
243
|
+
const formatted = top.map(r => {
|
|
244
|
+
const val = extractVal(r.value);
|
|
245
|
+
return `- [${r.type}] (id: ${r.key}) ${val}`;
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
content: [{ type: 'text', text: JSON.stringify({
|
|
249
|
+
results: formatted.length > 0 ? formatted.join('\n') : `No memories found matching "${query}".`,
|
|
250
|
+
count: top.length,
|
|
251
|
+
query,
|
|
252
|
+
}) }],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
return {
|
|
257
|
+
content: [{ type: 'text', text: `Failed to search: ${err.message}` }],
|
|
258
|
+
isError: true,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
// --- Tool: recall_delete_memory ---
|
|
264
|
+
pi.registerTool({
|
|
265
|
+
name: 'recall_delete_memory',
|
|
266
|
+
label: 'Delete Memory',
|
|
267
|
+
description: 'Delete a specific memory by its ID. Use recall_search_memory first to find the ID.',
|
|
268
|
+
parameters: {},
|
|
269
|
+
async execute(_id, params, _signal, _onUpdate, _ctx) {
|
|
270
|
+
try {
|
|
271
|
+
const id = params.id;
|
|
272
|
+
if (!id || typeof id !== 'string') {
|
|
273
|
+
return {
|
|
274
|
+
content: [{ type: 'text', text: 'Error: id is required and must be a string' }],
|
|
275
|
+
isError: true,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const ms = memory_1.MemoryService.getInstance();
|
|
279
|
+
const deleted = ms.delete(id);
|
|
280
|
+
return {
|
|
281
|
+
content: [{ type: 'text', text: JSON.stringify({
|
|
282
|
+
success: deleted,
|
|
283
|
+
id,
|
|
284
|
+
message: deleted ? `Memory "${id}" deleted.` : `Memory "${id}" not found.`,
|
|
285
|
+
}) }],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
return {
|
|
290
|
+
content: [{ type: 'text', text: `Failed to delete: ${err.message}` }],
|
|
291
|
+
isError: true,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
}
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared event processors — agent-agnostic functions that both
|
|
4
|
+
* Claude Code hooks and the Pi extension call.
|
|
5
|
+
*
|
|
6
|
+
* These operate on in-memory data (strings, arrays) rather than
|
|
7
|
+
* file paths or stdin, so they work in any runtime environment.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.setLogFunction = setLogFunction;
|
|
44
|
+
exports.resetPendingFailures = resetPendingFailures;
|
|
45
|
+
exports.processToolOutcome = processToolOutcome;
|
|
46
|
+
exports.processUserInput = processUserInput;
|
|
47
|
+
exports.processSessionEnd = processSessionEnd;
|
|
48
|
+
exports.processPreCompact = processPreCompact;
|
|
49
|
+
const shared_1 = require("../hooks/shared");
|
|
50
|
+
const memory_1 = require("../services/memory");
|
|
51
|
+
const outcome_storage_1 = require("../services/outcome-storage");
|
|
52
|
+
let logFn = () => { }; // silent by default
|
|
53
|
+
/** Set the log function (hookLog for CC hooks, console for Pi, etc.) */
|
|
54
|
+
function setLogFunction(fn) {
|
|
55
|
+
logFn = fn;
|
|
56
|
+
}
|
|
57
|
+
const MAX_PENDING = 10;
|
|
58
|
+
const FIX_WINDOW_MS = 5 * 60 * 1000; // 5 minutes
|
|
59
|
+
const FIX_SIMILARITY_THRESHOLD = 0.3;
|
|
60
|
+
let pendingFailures = [];
|
|
61
|
+
/** Reset pending failures (e.g. on session start). */
|
|
62
|
+
function resetPendingFailures() {
|
|
63
|
+
pendingFailures = [];
|
|
64
|
+
}
|
|
65
|
+
/** Extract an identifier string from tool input for similarity comparison. */
|
|
66
|
+
function getToolIdentifier(toolName, toolInput) {
|
|
67
|
+
if ((toolName === 'Bash' || toolName === 'bash') && toolInput?.command) {
|
|
68
|
+
return toolInput.command;
|
|
69
|
+
}
|
|
70
|
+
if (toolInput?.file_path) {
|
|
71
|
+
return `${toolName}:${toolInput.file_path}`;
|
|
72
|
+
}
|
|
73
|
+
return toolName;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if a successful tool result matches a recent failure and pair the fix.
|
|
77
|
+
* Returns true if a fix was paired.
|
|
78
|
+
*/
|
|
79
|
+
function tryPairFix(toolName, toolInput, output) {
|
|
80
|
+
if (pendingFailures.length === 0)
|
|
81
|
+
return false;
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const identifier = getToolIdentifier(toolName, toolInput);
|
|
84
|
+
let matched = false;
|
|
85
|
+
const remaining = [];
|
|
86
|
+
for (const pf of pendingFailures) {
|
|
87
|
+
if (now - pf.timestamp > FIX_WINDOW_MS)
|
|
88
|
+
continue; // expired
|
|
89
|
+
if (!matched && pf.toolName === toolName &&
|
|
90
|
+
(0, shared_1.jaccardSimilarity)(pf.identifier, identifier) >= FIX_SIMILARITY_THRESHOLD) {
|
|
91
|
+
try {
|
|
92
|
+
memory_1.MemoryService.getInstance().update(pf.memoryKey, {
|
|
93
|
+
value: { what_should_do: `Fix: ${truncate(identifier, 200)}` },
|
|
94
|
+
});
|
|
95
|
+
logFn('event-processor', `Paired fix: "${truncate(identifier, 60)}" → ${pf.memoryKey}`);
|
|
96
|
+
matched = true;
|
|
97
|
+
// Don't add to remaining — consumed
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
remaining.push(pf); // keep if update fails
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
remaining.push(pf);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
pendingFailures = remaining;
|
|
108
|
+
return matched;
|
|
109
|
+
}
|
|
110
|
+
// --- Tool Outcome Processing ---
|
|
111
|
+
/** Error patterns for Edit/Write tools */
|
|
112
|
+
const WRITE_ERROR_PATTERNS = [
|
|
113
|
+
/permission denied/i,
|
|
114
|
+
/EACCES/i,
|
|
115
|
+
/ENOENT/i,
|
|
116
|
+
/file not found/i,
|
|
117
|
+
/no such file/i,
|
|
118
|
+
/read-?only file/i,
|
|
119
|
+
/conflict/i,
|
|
120
|
+
/old_string.*not found/i,
|
|
121
|
+
/not unique in the file/i,
|
|
122
|
+
];
|
|
123
|
+
/** Error patterns for MCP/custom tools */
|
|
124
|
+
const TOOL_ERROR_PATTERNS = [
|
|
125
|
+
/error/i,
|
|
126
|
+
/failed/i,
|
|
127
|
+
/exception/i,
|
|
128
|
+
/timeout/i,
|
|
129
|
+
];
|
|
130
|
+
function truncate(s, maxLen) {
|
|
131
|
+
return s.length <= maxLen ? s : s.substring(0, maxLen - 3) + '...';
|
|
132
|
+
}
|
|
133
|
+
function firstLine(s) {
|
|
134
|
+
const idx = s.indexOf('\n');
|
|
135
|
+
return idx === -1 ? s : s.substring(0, idx);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Process a tool outcome — capture failures as memories, record outcome events.
|
|
139
|
+
* When a tool succeeds after a similar recent failure, pairs the fix.
|
|
140
|
+
* Works for any tool type (Bash, Edit, Write, MCP, generic).
|
|
141
|
+
*/
|
|
142
|
+
function processToolOutcome(toolName, toolInput, toolOutput, isError, sessionId) {
|
|
143
|
+
try {
|
|
144
|
+
// Skip own tools
|
|
145
|
+
if (toolName.includes('claude-recall') || toolName.includes('claude_recall') ||
|
|
146
|
+
toolName.startsWith('recall_'))
|
|
147
|
+
return;
|
|
148
|
+
const isFail = isError || isToolFailureOutput(toolName, toolOutput);
|
|
149
|
+
if (isFail) {
|
|
150
|
+
storeToolFailure(toolName, toolInput, toolOutput, sessionId);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Success — check if this fixes a recent failure
|
|
154
|
+
tryPairFix(toolName, toolInput, toolOutput);
|
|
155
|
+
}
|
|
156
|
+
// Record outcome event for all tools
|
|
157
|
+
recordOutcomeEvent(toolName, toolInput, toolOutput);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
logFn('event-processor', `processToolOutcome error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function isToolFailureOutput(toolName, output) {
|
|
164
|
+
if (toolName === 'Bash' || toolName === 'bash') {
|
|
165
|
+
return /Exit code (\d+)/.test(output) && !/Exit code 0/.test(output);
|
|
166
|
+
}
|
|
167
|
+
if (toolName === 'Edit' || toolName === 'Write' || toolName === 'edit' || toolName === 'write') {
|
|
168
|
+
return WRITE_ERROR_PATTERNS.some(p => p.test(output));
|
|
169
|
+
}
|
|
170
|
+
// For other tools, only flag short error outputs (avoid false positives on long results)
|
|
171
|
+
if (output.length < 500) {
|
|
172
|
+
return TOOL_ERROR_PATTERNS.some(p => p.test(output));
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
function storeToolFailure(toolName, toolInput, output, sessionId) {
|
|
177
|
+
const filePath = toolInput?.file_path ?? '';
|
|
178
|
+
const command = toolInput?.command ?? '';
|
|
179
|
+
let whatFailed;
|
|
180
|
+
if ((toolName === 'Bash' || toolName === 'bash') && command) {
|
|
181
|
+
whatFailed = `Command failed: ${truncate(command, 100)}`;
|
|
182
|
+
}
|
|
183
|
+
else if (filePath) {
|
|
184
|
+
whatFailed = `${toolName} failed on ${truncate(filePath, 80)}`;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
whatFailed = `${toolName} failed`;
|
|
188
|
+
}
|
|
189
|
+
// Dedup
|
|
190
|
+
const existing = (0, shared_1.searchExisting)(whatFailed);
|
|
191
|
+
if ((0, shared_1.isDuplicate)(whatFailed, existing, 0.7))
|
|
192
|
+
return;
|
|
193
|
+
const failureContent = {
|
|
194
|
+
what_failed: whatFailed,
|
|
195
|
+
why_failed: truncate(firstLine(output), 200),
|
|
196
|
+
what_should_do: 'Check inputs and prerequisites before retrying',
|
|
197
|
+
context: `${toolName} error`,
|
|
198
|
+
preventative_checks: ['Verify inputs are correct', 'Check preconditions'],
|
|
199
|
+
};
|
|
200
|
+
const key = `hook_failure_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
201
|
+
memory_1.MemoryService.getInstance().store({
|
|
202
|
+
key,
|
|
203
|
+
value: failureContent,
|
|
204
|
+
type: 'failure',
|
|
205
|
+
context: { timestamp: Date.now() },
|
|
206
|
+
relevanceScore: 0.75,
|
|
207
|
+
});
|
|
208
|
+
// Track for fix pairing — when a similar tool succeeds, we update this memory
|
|
209
|
+
const identifier = getToolIdentifier(toolName, toolInput);
|
|
210
|
+
pendingFailures.push({ toolName, identifier, memoryKey: key, timestamp: Date.now() });
|
|
211
|
+
while (pendingFailures.length > MAX_PENDING)
|
|
212
|
+
pendingFailures.shift();
|
|
213
|
+
logFn('event-processor', `Stored failure: ${truncate(whatFailed, 60)}`);
|
|
214
|
+
}
|
|
215
|
+
function recordOutcomeEvent(toolName, toolInput, output) {
|
|
216
|
+
try {
|
|
217
|
+
const tags = [toolName.toLowerCase()];
|
|
218
|
+
if (toolInput?.file_path) {
|
|
219
|
+
const ext = toolInput.file_path.split('.').pop();
|
|
220
|
+
if (ext)
|
|
221
|
+
tags.push(ext);
|
|
222
|
+
}
|
|
223
|
+
outcome_storage_1.OutcomeStorage.getInstance().createOutcomeEvent({
|
|
224
|
+
event_type: 'tool_result',
|
|
225
|
+
actor: 'tool',
|
|
226
|
+
action_summary: `${toolName}: ${truncate(firstLine(output), 100)}`,
|
|
227
|
+
next_state_summary: truncate(firstLine(output), 200),
|
|
228
|
+
tags,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Non-critical
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// --- User Input Processing (Correction Detection) ---
|
|
236
|
+
const REASK_PATTERNS = [
|
|
237
|
+
/still broken/i,
|
|
238
|
+
/that'?s not what I (?:meant|asked|wanted)/i,
|
|
239
|
+
/wrong file/i,
|
|
240
|
+
/try again/i,
|
|
241
|
+
/that didn'?t (?:work|fix|help)/i,
|
|
242
|
+
/you (?:missed|forgot|ignored)/i,
|
|
243
|
+
];
|
|
244
|
+
/**
|
|
245
|
+
* Process user input text — detect corrections, preferences, and reask signals.
|
|
246
|
+
* Returns a summary message if something was captured, or null.
|
|
247
|
+
*/
|
|
248
|
+
async function processUserInput(text, sessionId) {
|
|
249
|
+
if (text.length < 20 || text.length > 2000)
|
|
250
|
+
return null;
|
|
251
|
+
if (text.startsWith('```') || text.startsWith('{'))
|
|
252
|
+
return null;
|
|
253
|
+
// Detect reask signals
|
|
254
|
+
try {
|
|
255
|
+
for (const pattern of REASK_PATTERNS) {
|
|
256
|
+
if (pattern.test(text)) {
|
|
257
|
+
outcome_storage_1.OutcomeStorage.getInstance().createOutcomeEvent({
|
|
258
|
+
event_type: 'reask_signal',
|
|
259
|
+
actor: 'user',
|
|
260
|
+
next_state_summary: `User reask detected: ${text.substring(0, 100)}`,
|
|
261
|
+
tags: ['reask'],
|
|
262
|
+
});
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// Non-critical
|
|
269
|
+
}
|
|
270
|
+
const result = await (0, shared_1.classifyContent)(text);
|
|
271
|
+
if (!result)
|
|
272
|
+
return null;
|
|
273
|
+
if (result.extract.length < 10 || result.extract.length > 200)
|
|
274
|
+
return null;
|
|
275
|
+
if ((result.type === 'correction' || result.type === 'preference') && result.confidence < 0.7)
|
|
276
|
+
return null;
|
|
277
|
+
if (result.confidence < 0.6)
|
|
278
|
+
return null;
|
|
279
|
+
// Dedup
|
|
280
|
+
const existing = (0, shared_1.searchExisting)(result.extract.substring(0, 100));
|
|
281
|
+
if ((0, shared_1.isDuplicate)(result.extract, existing))
|
|
282
|
+
return null;
|
|
283
|
+
(0, shared_1.storeMemory)(result.extract, result.type, undefined, result.confidence);
|
|
284
|
+
const summary = result.extract.length > 60 ? result.extract.substring(0, 60) + '...' : result.extract;
|
|
285
|
+
logFn('event-processor', `Captured ${result.type}: ${result.extract.substring(0, 80)}`);
|
|
286
|
+
return `Auto-captured ${result.type}: ${summary}`;
|
|
287
|
+
}
|
|
288
|
+
// --- Session End Processing ---
|
|
289
|
+
/**
|
|
290
|
+
* Process end-of-session: batch classify user texts, store memories,
|
|
291
|
+
* run promotion cycle.
|
|
292
|
+
*
|
|
293
|
+
* @param userTexts Array of user message texts from the session
|
|
294
|
+
* @param sessionId Current session ID
|
|
295
|
+
* @param projectId Current project ID
|
|
296
|
+
* @param maxStore Max memories to store (default 3)
|
|
297
|
+
*/
|
|
298
|
+
async function processSessionEnd(userTexts, sessionId, projectId, maxStore = 3) {
|
|
299
|
+
let stored = 0;
|
|
300
|
+
let promoted = 0;
|
|
301
|
+
try {
|
|
302
|
+
// Create episode
|
|
303
|
+
const outcomeStorage = outcome_storage_1.OutcomeStorage.getInstance();
|
|
304
|
+
const episodeId = outcomeStorage.createEpisode({
|
|
305
|
+
project_id: projectId,
|
|
306
|
+
session_id: sessionId,
|
|
307
|
+
source: 'session-end',
|
|
308
|
+
});
|
|
309
|
+
// Filter to classifiable texts
|
|
310
|
+
const classifiable = userTexts
|
|
311
|
+
.filter(t => t.length >= 10 && t.length <= 2000)
|
|
312
|
+
.slice(-6); // last 6 entries
|
|
313
|
+
if (classifiable.length > 0) {
|
|
314
|
+
const results = await (0, shared_1.classifyBatch)(classifiable);
|
|
315
|
+
for (const result of results) {
|
|
316
|
+
if (stored >= maxStore)
|
|
317
|
+
break;
|
|
318
|
+
if (!result)
|
|
319
|
+
continue;
|
|
320
|
+
if (result.extract.length < 10 || result.extract.length > 200)
|
|
321
|
+
continue;
|
|
322
|
+
if ((result.type === 'correction' || result.type === 'preference') && result.confidence < 0.7)
|
|
323
|
+
continue;
|
|
324
|
+
if (result.confidence < 0.6)
|
|
325
|
+
continue;
|
|
326
|
+
const existing = (0, shared_1.searchExisting)(result.extract.substring(0, 100));
|
|
327
|
+
if ((0, shared_1.isDuplicate)(result.extract, existing))
|
|
328
|
+
continue;
|
|
329
|
+
(0, shared_1.storeMemory)(result.extract, result.type, projectId, result.confidence);
|
|
330
|
+
stored++;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Update episode
|
|
334
|
+
outcomeStorage.updateEpisode(episodeId, {
|
|
335
|
+
outcome_type: stored > 0 ? 'success' : 'unclear',
|
|
336
|
+
severity: 'low',
|
|
337
|
+
outcome_summary: `Session end: ${stored} memories captured`,
|
|
338
|
+
});
|
|
339
|
+
// Run promotion cycle
|
|
340
|
+
try {
|
|
341
|
+
const { PromotionEngine } = await Promise.resolve().then(() => __importStar(require('../services/promotion-engine')));
|
|
342
|
+
const result = PromotionEngine.getInstance().runCycle(projectId);
|
|
343
|
+
promoted = result.promoted;
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// Non-critical
|
|
347
|
+
}
|
|
348
|
+
// Prune old data
|
|
349
|
+
try {
|
|
350
|
+
outcomeStorage.pruneOldData();
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
// Non-critical
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
logFn('event-processor', `processSessionEnd error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
358
|
+
}
|
|
359
|
+
return { stored, promoted };
|
|
360
|
+
}
|
|
361
|
+
// --- Pre-Compact Processing ---
|
|
362
|
+
/**
|
|
363
|
+
* Aggressive memory capture before context compression.
|
|
364
|
+
* Batch classifies user texts with lower confidence threshold.
|
|
365
|
+
*
|
|
366
|
+
* @param userTexts Array of user message texts about to be compacted
|
|
367
|
+
* @param sessionId Current session ID
|
|
368
|
+
* @param maxStore Max memories to store (default 5)
|
|
369
|
+
*/
|
|
370
|
+
async function processPreCompact(userTexts, sessionId, maxStore = 5) {
|
|
371
|
+
let stored = 0;
|
|
372
|
+
try {
|
|
373
|
+
const classifiable = userTexts
|
|
374
|
+
.filter(t => t.length >= 10 && t.length <= 2000);
|
|
375
|
+
if (classifiable.length === 0)
|
|
376
|
+
return 0;
|
|
377
|
+
const results = await (0, shared_1.classifyBatch)(classifiable);
|
|
378
|
+
for (const result of results) {
|
|
379
|
+
if (stored >= maxStore)
|
|
380
|
+
break;
|
|
381
|
+
if (!result)
|
|
382
|
+
continue;
|
|
383
|
+
if (result.extract.length < 10 || result.extract.length > 200)
|
|
384
|
+
continue;
|
|
385
|
+
if (result.confidence < 0.6)
|
|
386
|
+
continue;
|
|
387
|
+
const existing = (0, shared_1.searchExisting)(result.extract.substring(0, 100));
|
|
388
|
+
if ((0, shared_1.isDuplicate)(result.extract, existing))
|
|
389
|
+
continue;
|
|
390
|
+
const prefixed = `[PreCompact] ${result.extract}`;
|
|
391
|
+
(0, shared_1.storeMemory)(prefixed, result.type, undefined, result.confidence);
|
|
392
|
+
stored++;
|
|
393
|
+
logFn('event-processor', `PreCompact captured ${result.type}: ${result.extract.substring(0, 80)}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch (err) {
|
|
397
|
+
logFn('event-processor', `processPreCompact error: ${(0, shared_1.safeErrorMessage)(err)}`);
|
|
398
|
+
}
|
|
399
|
+
return stored;
|
|
400
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-recall",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Persistent memory for Claude Code with native Skills integration, automatic capture, failure learning, and project scoping
|
|
3
|
+
"version": "0.20.1",
|
|
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": {
|
|
7
7
|
"claude-recall": "dist/cli/claude-recall-cli.js"
|
|
@@ -9,12 +9,17 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/",
|
|
11
11
|
".claude/",
|
|
12
|
+
"skills/",
|
|
12
13
|
"scripts/postinstall.js",
|
|
13
14
|
"scripts/uninstall.js",
|
|
14
15
|
"README.md",
|
|
15
16
|
"LICENSE",
|
|
16
17
|
"docs/"
|
|
17
18
|
],
|
|
19
|
+
"pi": {
|
|
20
|
+
"extensions": ["./dist/pi/extension.js"],
|
|
21
|
+
"skills": ["./skills/*.md"]
|
|
22
|
+
},
|
|
18
23
|
"directories": {
|
|
19
24
|
"doc": "docs",
|
|
20
25
|
"test": "tests"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory-management
|
|
3
|
+
description: Persistent memory management — load rules at session start, store corrections and preferences automatically
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Management
|
|
7
|
+
|
|
8
|
+
You have access to persistent memory tools from Claude Recall. Use them to learn from the user over time.
|
|
9
|
+
|
|
10
|
+
## When starting a task
|
|
11
|
+
|
|
12
|
+
Call `recall_load_rules` to load stored preferences, corrections, failure lessons, and devops rules. Apply relevant rules to your work and cite them inline: `(applied from memory: <rule summary>)`.
|
|
13
|
+
|
|
14
|
+
## When the user corrects you
|
|
15
|
+
|
|
16
|
+
If the user says things like "no, always use X" or "don't do Y", call `recall_store_memory` with:
|
|
17
|
+
- `content`: the correction in clear, reusable language
|
|
18
|
+
- `metadata.type`: `"correction"`
|
|
19
|
+
|
|
20
|
+
## When you learn a preference
|
|
21
|
+
|
|
22
|
+
If the user states a preference ("I prefer tabs", "use functional style"), call `recall_store_memory` with:
|
|
23
|
+
- `content`: the preference
|
|
24
|
+
- `metadata.type`: `"preference"`
|
|
25
|
+
|
|
26
|
+
## When something fails
|
|
27
|
+
|
|
28
|
+
If a command fails or you need to backtrack, the failure is captured automatically. You don't need to store it manually.
|
|
29
|
+
|
|
30
|
+
## Before making decisions
|
|
31
|
+
|
|
32
|
+
Call `recall_search_memory` with relevant keywords to check for existing project knowledge before choosing approaches, tools, or conventions.
|