Reorganize cognitive-memory skill: consolidate scripts, systemd, dev subdirs

- Move session_memory.py, ensure-symlinks.sh into skills/cognitive-memory/scripts/
- Copy systemd units into skills/cognitive-memory/systemd/ with README
- Move PROJECT_PLAN.json, migrate.py into skills/cognitive-memory/dev/
- Add mtime-based embeddings cache to client.py (6x faster semantic recall)
- Default recall to semantic+keyword merge (was keyword-only)
- Update settings.json SessionEnd hook path, MCP allow entry
- Update SKILL.md, feature.json, mcp_server.py docs for new defaults

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-02-19 16:02:20 -06:00
parent 09429eec3a
commit f0f075461e
24 changed files with 2805 additions and 2518 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{ {
"numStartups": 661, "numStartups": 663,
"installMethod": "native", "installMethod": "native",
"autoUpdates": true, "autoUpdates": true,
"preferredNotifChannel": "iterm2_with_bell", "preferredNotifChannel": "iterm2_with_bell",
@ -11,7 +11,7 @@
"enter-to-steer-in-relatime": 651, "enter-to-steer-in-relatime": 651,
"todo-list": 660, "todo-list": 660,
"# for memory": 38, "# for memory": 38,
"install-github-app": 653, "install-github-app": 663,
"permissions": 654, "permissions": 654,
"drag-and-drop-images": 659, "drag-and-drop-images": 659,
"double-esc": 75, "double-esc": 75,
@ -21,7 +21,7 @@
"custom-commands": 655, "custom-commands": 655,
"shift-enter": 661, "shift-enter": 661,
"shift-tab": 655, "shift-tab": 655,
"custom-agents": 648, "custom-agents": 663,
"status-line": 536, "status-line": 536,
"git-worktrees": 661, "git-worktrees": 661,
"image-paste": 646, "image-paste": 646,
@ -44,7 +44,7 @@
"agent-flag": 652 "agent-flag": 652
}, },
"memoryUsageCount": 18, "memoryUsageCount": 18,
"promptQueueUseCount": 4477, "promptQueueUseCount": 4513,
"cachedStatsigGates": { "cachedStatsigGates": {
"tengu_disable_bypass_permissions_mode": false, "tengu_disable_bypass_permissions_mode": false,
"tengu_use_file_checkpoints": true, "tengu_use_file_checkpoints": true,
@ -144,7 +144,7 @@
"tengu_vinteuil_phrase": true, "tengu_vinteuil_phrase": true,
"tengu_oboe": true, "tengu_oboe": true,
"tengu_tst_names_in_messages": false, "tengu_tst_names_in_messages": false,
"tengu_chomp_inflection": true, "tengu_chomp_inflection": false,
"tengu_silver_lantern": false, "tengu_silver_lantern": false,
"tengu_copper_lantern": false, "tengu_copper_lantern": false,
"tengu_workout2": true, "tengu_workout2": true,
@ -615,17 +615,17 @@
"ssh-tdarr", "ssh-tdarr",
"notediscovery" "notediscovery"
], ],
"lastCost": 0.29647025, "lastCost": 40.23374199999994,
"lastAPIDuration": 4856, "lastAPIDuration": 5995420,
"lastToolDuration": 0, "lastToolDuration": 2896147,
"lastDuration": 247436, "lastDuration": 20374119,
"lastLinesAdded": 0, "lastLinesAdded": 2594,
"lastLinesRemoved": 0, "lastLinesRemoved": 143,
"lastTotalInputTokens": 297, "lastTotalInputTokens": 747870,
"lastTotalOutputTokens": 29, "lastTotalOutputTokens": 224482,
"lastTotalCacheCreationInputTokens": 47321, "lastTotalCacheCreationInputTokens": 1751354,
"lastTotalCacheReadInputTokens": 0, "lastTotalCacheReadInputTokens": 47009653,
"lastSessionId": "8d1ffe8f-1e99-4e60-b9dd-270e9a939153", "lastSessionId": "606a9f26-d08c-4654-8a9a-e118a2735aaa",
"reactVulnerabilityCache": { "reactVulnerabilityCache": {
"detected": false, "detected": false,
"package": null, "package": null,
@ -633,34 +633,44 @@
"version": null, "version": null,
"packageManager": null "packageManager": null
}, },
"lastAPIDurationWithoutRetries": 4855, "lastAPIDurationWithoutRetries": 4961513,
"lastModelUsage": { "lastModelUsage": {
"claude-haiku-4-5-20251001": {
"inputTokens": 294,
"outputTokens": 16,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 0,
"webSearchRequests": 0,
"costUSD": 0.000374
},
"claude-opus-4-6": { "claude-opus-4-6": {
"inputTokens": 3, "inputTokens": 9124,
"outputTokens": 13, "outputTokens": 164889,
"cacheReadInputTokens": 0, "cacheReadInputTokens": 44348975,
"cacheCreationInputTokens": 47321, "cacheCreationInputTokens": 1408982,
"webSearchRequests": 0, "webSearchRequests": 0,
"costUSD": 0.29609625 "costUSD": 35.148469999999996
},
"claude-haiku-4-5-20251001": {
"inputTokens": 732883,
"outputTokens": 15034,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 67247,
"webSearchRequests": 0,
"costUSD": 0.892111749999999
},
"claude-sonnet-4-6": {
"inputTokens": 5863,
"outputTokens": 44559,
"cacheReadInputTokens": 2660678,
"cacheCreationInputTokens": 275125,
"webSearchRequests": 0,
"costUSD": 4.19316025
} }
}, },
"lastSessionMetrics": { "lastSessionMetrics": {
"frame_duration_ms_count": 24, "frame_duration_ms_count": 126433,
"frame_duration_ms_min": 0.26554799999999545, "frame_duration_ms_min": 0.033378999680280685,
"frame_duration_ms_max": 3.014287000000081, "frame_duration_ms_max": 185.3289749994874,
"frame_duration_ms_avg": 1.1559620416666985, "frame_duration_ms_avg": 4.580825976891975,
"frame_duration_ms_p50": 0.9233974999999646, "frame_duration_ms_p50": 1.4972434998489916,
"frame_duration_ms_p95": 1.925081300000033, "frame_duration_ms_p95": 3.451200450630833,
"frame_duration_ms_p99": 2.771056710000067 "frame_duration_ms_p99": 90.20297368977218
} },
"lastFpsAverage": 6.12,
"lastFpsLow1Pct": 11.02
}, },
"/mnt/NV2/Development/major-domo": { "/mnt/NV2/Development/major-domo": {
"allowedTools": [], "allowedTools": [],
@ -2012,13 +2022,20 @@
} }
}, },
"hasOpusPlanDefault": false, "hasOpusPlanDefault": false,
"lastPlanModeUse": 1771437917321, "lastPlanModeUse": 1771532414321,
"feedbackSurveyState": { "feedbackSurveyState": {
"lastShownTime": 1771451253831 "lastShownTime": 1771451253831
}, },
"sonnet45MigrationComplete": true, "sonnet45MigrationComplete": true,
"claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z", "claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z",
"mcpServers": {}, "mcpServers": {
"cognitive-memory": {
"command": "python3",
"args": [
"/home/cal/.claude/skills/cognitive-memory/mcp_server.py"
]
}
},
"s1mNonSubscriberAccessCache": { "s1mNonSubscriberAccessCache": {
"fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": { "fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": {
"hasAccess": false, "hasAccess": false,
@ -2088,7 +2105,7 @@
"currency": "USD" "currency": "USD"
}, },
"remaining_passes": 3, "remaining_passes": 3,
"timestamp": 1771521872816 "timestamp": 1771535572741
} }
}, },
"opus45MigrationComplete": true, "opus45MigrationComplete": true,
@ -2171,8 +2188,8 @@
"lastUsedAt": 1770959377885 "lastUsedAt": 1770959377885
}, },
"sync-config": { "sync-config": {
"usageCount": 18, "usageCount": 19,
"lastUsedAt": 1771507439661 "lastUsedAt": 1771530471038
}, },
"claude-optimised": { "claude-optimised": {
"usageCount": 4, "usageCount": 4,
@ -2187,8 +2204,8 @@
"lastUsedAt": 1771528594878 "lastUsedAt": 1771528594878
}, },
"commit-push": { "commit-push": {
"usageCount": 16, "usageCount": 17,
"lastUsedAt": 1771507468440 "lastUsedAt": 1771537208260
}, },
"check-rarity": { "check-rarity": {
"usageCount": 1, "usageCount": 1,
@ -2213,6 +2230,10 @@
"claude-automation-recommender": { "claude-automation-recommender": {
"usageCount": 1, "usageCount": 1,
"lastUsedAt": 1771474474878 "lastUsedAt": 1771474474878
},
"bmad-qa": {
"usageCount": 1,
"lastUsedAt": 1771536951910
} }
}, },
"opusProMigrationComplete": true, "opusProMigrationComplete": true,
@ -2221,7 +2242,7 @@
"passesLastSeenRemaining": 3, "passesLastSeenRemaining": 3,
"clientDataCache": { "clientDataCache": {
"data": {}, "data": {},
"timestamp": 1771529175111 "timestamp": 1771537923325
}, },
"hasShownOpus46Notice": { "hasShownOpus46Notice": {
"57783733-6e1e-48d5-9cb7-fa588a77b795": true "57783733-6e1e-48d5-9cb7-fa588a77b795": true

View File

@ -1,5 +1,5 @@
{ {
"numStartups": 661, "numStartups": 663,
"installMethod": "native", "installMethod": "native",
"autoUpdates": true, "autoUpdates": true,
"preferredNotifChannel": "iterm2_with_bell", "preferredNotifChannel": "iterm2_with_bell",
@ -11,7 +11,7 @@
"enter-to-steer-in-relatime": 651, "enter-to-steer-in-relatime": 651,
"todo-list": 660, "todo-list": 660,
"# for memory": 38, "# for memory": 38,
"install-github-app": 653, "install-github-app": 663,
"permissions": 654, "permissions": 654,
"drag-and-drop-images": 659, "drag-and-drop-images": 659,
"double-esc": 75, "double-esc": 75,
@ -21,7 +21,7 @@
"custom-commands": 655, "custom-commands": 655,
"shift-enter": 661, "shift-enter": 661,
"shift-tab": 655, "shift-tab": 655,
"custom-agents": 648, "custom-agents": 663,
"status-line": 536, "status-line": 536,
"git-worktrees": 661, "git-worktrees": 661,
"image-paste": 646, "image-paste": 646,
@ -44,7 +44,7 @@
"agent-flag": 652 "agent-flag": 652
}, },
"memoryUsageCount": 18, "memoryUsageCount": 18,
"promptQueueUseCount": 4477, "promptQueueUseCount": 4513,
"cachedStatsigGates": { "cachedStatsigGates": {
"tengu_disable_bypass_permissions_mode": false, "tengu_disable_bypass_permissions_mode": false,
"tengu_use_file_checkpoints": true, "tengu_use_file_checkpoints": true,
@ -144,7 +144,7 @@
"tengu_vinteuil_phrase": true, "tengu_vinteuil_phrase": true,
"tengu_oboe": true, "tengu_oboe": true,
"tengu_tst_names_in_messages": false, "tengu_tst_names_in_messages": false,
"tengu_chomp_inflection": true, "tengu_chomp_inflection": false,
"tengu_silver_lantern": false, "tengu_silver_lantern": false,
"tengu_copper_lantern": false, "tengu_copper_lantern": false,
"tengu_workout2": true, "tengu_workout2": true,
@ -615,17 +615,17 @@
"ssh-tdarr", "ssh-tdarr",
"notediscovery" "notediscovery"
], ],
"lastCost": 0.29647025, "lastCost": 40.23374199999994,
"lastAPIDuration": 4856, "lastAPIDuration": 5995420,
"lastToolDuration": 0, "lastToolDuration": 2896147,
"lastDuration": 247436, "lastDuration": 20374119,
"lastLinesAdded": 0, "lastLinesAdded": 2594,
"lastLinesRemoved": 0, "lastLinesRemoved": 143,
"lastTotalInputTokens": 297, "lastTotalInputTokens": 747870,
"lastTotalOutputTokens": 29, "lastTotalOutputTokens": 224482,
"lastTotalCacheCreationInputTokens": 47321, "lastTotalCacheCreationInputTokens": 1751354,
"lastTotalCacheReadInputTokens": 0, "lastTotalCacheReadInputTokens": 47009653,
"lastSessionId": "8d1ffe8f-1e99-4e60-b9dd-270e9a939153", "lastSessionId": "606a9f26-d08c-4654-8a9a-e118a2735aaa",
"reactVulnerabilityCache": { "reactVulnerabilityCache": {
"detected": false, "detected": false,
"package": null, "package": null,
@ -633,34 +633,44 @@
"version": null, "version": null,
"packageManager": null "packageManager": null
}, },
"lastAPIDurationWithoutRetries": 4855, "lastAPIDurationWithoutRetries": 4961513,
"lastModelUsage": { "lastModelUsage": {
"claude-haiku-4-5-20251001": {
"inputTokens": 294,
"outputTokens": 16,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 0,
"webSearchRequests": 0,
"costUSD": 0.000374
},
"claude-opus-4-6": { "claude-opus-4-6": {
"inputTokens": 3, "inputTokens": 9124,
"outputTokens": 13, "outputTokens": 164889,
"cacheReadInputTokens": 0, "cacheReadInputTokens": 44348975,
"cacheCreationInputTokens": 47321, "cacheCreationInputTokens": 1408982,
"webSearchRequests": 0, "webSearchRequests": 0,
"costUSD": 0.29609625 "costUSD": 35.148469999999996
},
"claude-haiku-4-5-20251001": {
"inputTokens": 732883,
"outputTokens": 15034,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 67247,
"webSearchRequests": 0,
"costUSD": 0.892111749999999
},
"claude-sonnet-4-6": {
"inputTokens": 5863,
"outputTokens": 44559,
"cacheReadInputTokens": 2660678,
"cacheCreationInputTokens": 275125,
"webSearchRequests": 0,
"costUSD": 4.19316025
} }
}, },
"lastSessionMetrics": { "lastSessionMetrics": {
"frame_duration_ms_count": 24, "frame_duration_ms_count": 126433,
"frame_duration_ms_min": 0.26554799999999545, "frame_duration_ms_min": 0.033378999680280685,
"frame_duration_ms_max": 3.014287000000081, "frame_duration_ms_max": 185.3289749994874,
"frame_duration_ms_avg": 1.1559620416666985, "frame_duration_ms_avg": 4.580825976891975,
"frame_duration_ms_p50": 0.9233974999999646, "frame_duration_ms_p50": 1.4972434998489916,
"frame_duration_ms_p95": 1.925081300000033, "frame_duration_ms_p95": 3.451200450630833,
"frame_duration_ms_p99": 2.771056710000067 "frame_duration_ms_p99": 90.20297368977218
} },
"lastFpsAverage": 6.12,
"lastFpsLow1Pct": 11.02
}, },
"/mnt/NV2/Development/major-domo": { "/mnt/NV2/Development/major-domo": {
"allowedTools": [], "allowedTools": [],
@ -2012,13 +2022,20 @@
} }
}, },
"hasOpusPlanDefault": false, "hasOpusPlanDefault": false,
"lastPlanModeUse": 1771437917321, "lastPlanModeUse": 1771532414321,
"feedbackSurveyState": { "feedbackSurveyState": {
"lastShownTime": 1771451253831 "lastShownTime": 1771451253831
}, },
"sonnet45MigrationComplete": true, "sonnet45MigrationComplete": true,
"claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z", "claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z",
"mcpServers": {}, "mcpServers": {
"cognitive-memory": {
"command": "python3",
"args": [
"/home/cal/.claude/skills/cognitive-memory/mcp_server.py"
]
}
},
"s1mNonSubscriberAccessCache": { "s1mNonSubscriberAccessCache": {
"fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": { "fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": {
"hasAccess": false, "hasAccess": false,
@ -2088,7 +2105,7 @@
"currency": "USD" "currency": "USD"
}, },
"remaining_passes": 3, "remaining_passes": 3,
"timestamp": 1771521872816 "timestamp": 1771535572741
} }
}, },
"opus45MigrationComplete": true, "opus45MigrationComplete": true,
@ -2171,8 +2188,8 @@
"lastUsedAt": 1770959377885 "lastUsedAt": 1770959377885
}, },
"sync-config": { "sync-config": {
"usageCount": 18, "usageCount": 19,
"lastUsedAt": 1771507439661 "lastUsedAt": 1771530471038
}, },
"claude-optimised": { "claude-optimised": {
"usageCount": 4, "usageCount": 4,
@ -2187,8 +2204,8 @@
"lastUsedAt": 1771528594878 "lastUsedAt": 1771528594878
}, },
"commit-push": { "commit-push": {
"usageCount": 16, "usageCount": 17,
"lastUsedAt": 1771507468440 "lastUsedAt": 1771537208260
}, },
"check-rarity": { "check-rarity": {
"usageCount": 1, "usageCount": 1,
@ -2213,6 +2230,10 @@
"claude-automation-recommender": { "claude-automation-recommender": {
"usageCount": 1, "usageCount": 1,
"lastUsedAt": 1771474474878 "lastUsedAt": 1771474474878
},
"bmad-qa": {
"usageCount": 1,
"lastUsedAt": 1771536951910
} }
}, },
"opusProMigrationComplete": true, "opusProMigrationComplete": true,
@ -2221,7 +2242,7 @@
"passesLastSeenRemaining": 3, "passesLastSeenRemaining": 3,
"clientDataCache": { "clientDataCache": {
"data": {}, "data": {},
"timestamp": 1771529292422 "timestamp": 1771538023670
}, },
"hasShownOpus46Notice": { "hasShownOpus46Notice": {
"57783733-6e1e-48d5-9cb7-fa588a77b795": true "57783733-6e1e-48d5-9cb7-fa588a77b795": true

View File

@ -1,5 +1,5 @@
{ {
"numStartups": 661, "numStartups": 663,
"installMethod": "native", "installMethod": "native",
"autoUpdates": true, "autoUpdates": true,
"preferredNotifChannel": "iterm2_with_bell", "preferredNotifChannel": "iterm2_with_bell",
@ -11,7 +11,7 @@
"enter-to-steer-in-relatime": 651, "enter-to-steer-in-relatime": 651,
"todo-list": 660, "todo-list": 660,
"# for memory": 38, "# for memory": 38,
"install-github-app": 653, "install-github-app": 663,
"permissions": 654, "permissions": 654,
"drag-and-drop-images": 659, "drag-and-drop-images": 659,
"double-esc": 75, "double-esc": 75,
@ -21,7 +21,7 @@
"custom-commands": 655, "custom-commands": 655,
"shift-enter": 661, "shift-enter": 661,
"shift-tab": 655, "shift-tab": 655,
"custom-agents": 648, "custom-agents": 663,
"status-line": 536, "status-line": 536,
"git-worktrees": 661, "git-worktrees": 661,
"image-paste": 646, "image-paste": 646,
@ -44,7 +44,7 @@
"agent-flag": 652 "agent-flag": 652
}, },
"memoryUsageCount": 18, "memoryUsageCount": 18,
"promptQueueUseCount": 4478, "promptQueueUseCount": 4513,
"cachedStatsigGates": { "cachedStatsigGates": {
"tengu_disable_bypass_permissions_mode": false, "tengu_disable_bypass_permissions_mode": false,
"tengu_use_file_checkpoints": true, "tengu_use_file_checkpoints": true,
@ -144,7 +144,7 @@
"tengu_vinteuil_phrase": true, "tengu_vinteuil_phrase": true,
"tengu_oboe": true, "tengu_oboe": true,
"tengu_tst_names_in_messages": false, "tengu_tst_names_in_messages": false,
"tengu_chomp_inflection": true, "tengu_chomp_inflection": false,
"tengu_silver_lantern": false, "tengu_silver_lantern": false,
"tengu_copper_lantern": false, "tengu_copper_lantern": false,
"tengu_workout2": true, "tengu_workout2": true,
@ -615,17 +615,17 @@
"ssh-tdarr", "ssh-tdarr",
"notediscovery" "notediscovery"
], ],
"lastCost": 0.29647025, "lastCost": 40.23374199999994,
"lastAPIDuration": 4856, "lastAPIDuration": 5995420,
"lastToolDuration": 0, "lastToolDuration": 2896147,
"lastDuration": 247436, "lastDuration": 20374119,
"lastLinesAdded": 0, "lastLinesAdded": 2594,
"lastLinesRemoved": 0, "lastLinesRemoved": 143,
"lastTotalInputTokens": 297, "lastTotalInputTokens": 747870,
"lastTotalOutputTokens": 29, "lastTotalOutputTokens": 224482,
"lastTotalCacheCreationInputTokens": 47321, "lastTotalCacheCreationInputTokens": 1751354,
"lastTotalCacheReadInputTokens": 0, "lastTotalCacheReadInputTokens": 47009653,
"lastSessionId": "8d1ffe8f-1e99-4e60-b9dd-270e9a939153", "lastSessionId": "606a9f26-d08c-4654-8a9a-e118a2735aaa",
"reactVulnerabilityCache": { "reactVulnerabilityCache": {
"detected": false, "detected": false,
"package": null, "package": null,
@ -633,34 +633,44 @@
"version": null, "version": null,
"packageManager": null "packageManager": null
}, },
"lastAPIDurationWithoutRetries": 4855, "lastAPIDurationWithoutRetries": 4961513,
"lastModelUsage": { "lastModelUsage": {
"claude-haiku-4-5-20251001": {
"inputTokens": 294,
"outputTokens": 16,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 0,
"webSearchRequests": 0,
"costUSD": 0.000374
},
"claude-opus-4-6": { "claude-opus-4-6": {
"inputTokens": 3, "inputTokens": 9124,
"outputTokens": 13, "outputTokens": 164889,
"cacheReadInputTokens": 0, "cacheReadInputTokens": 44348975,
"cacheCreationInputTokens": 47321, "cacheCreationInputTokens": 1408982,
"webSearchRequests": 0, "webSearchRequests": 0,
"costUSD": 0.29609625 "costUSD": 35.148469999999996
},
"claude-haiku-4-5-20251001": {
"inputTokens": 732883,
"outputTokens": 15034,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 67247,
"webSearchRequests": 0,
"costUSD": 0.892111749999999
},
"claude-sonnet-4-6": {
"inputTokens": 5863,
"outputTokens": 44559,
"cacheReadInputTokens": 2660678,
"cacheCreationInputTokens": 275125,
"webSearchRequests": 0,
"costUSD": 4.19316025
} }
}, },
"lastSessionMetrics": { "lastSessionMetrics": {
"frame_duration_ms_count": 24, "frame_duration_ms_count": 126433,
"frame_duration_ms_min": 0.26554799999999545, "frame_duration_ms_min": 0.033378999680280685,
"frame_duration_ms_max": 3.014287000000081, "frame_duration_ms_max": 185.3289749994874,
"frame_duration_ms_avg": 1.1559620416666985, "frame_duration_ms_avg": 4.580825976891975,
"frame_duration_ms_p50": 0.9233974999999646, "frame_duration_ms_p50": 1.4972434998489916,
"frame_duration_ms_p95": 1.925081300000033, "frame_duration_ms_p95": 3.451200450630833,
"frame_duration_ms_p99": 2.771056710000067 "frame_duration_ms_p99": 90.20297368977218
} },
"lastFpsAverage": 6.12,
"lastFpsLow1Pct": 11.02
}, },
"/mnt/NV2/Development/major-domo": { "/mnt/NV2/Development/major-domo": {
"allowedTools": [], "allowedTools": [],
@ -2012,13 +2022,20 @@
} }
}, },
"hasOpusPlanDefault": false, "hasOpusPlanDefault": false,
"lastPlanModeUse": 1771437917321, "lastPlanModeUse": 1771532414321,
"feedbackSurveyState": { "feedbackSurveyState": {
"lastShownTime": 1771451253831 "lastShownTime": 1771451253831
}, },
"sonnet45MigrationComplete": true, "sonnet45MigrationComplete": true,
"claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z", "claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z",
"mcpServers": {}, "mcpServers": {
"cognitive-memory": {
"command": "python3",
"args": [
"/home/cal/.claude/skills/cognitive-memory/mcp_server.py"
]
}
},
"s1mNonSubscriberAccessCache": { "s1mNonSubscriberAccessCache": {
"fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": { "fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": {
"hasAccess": false, "hasAccess": false,
@ -2088,7 +2105,7 @@
"currency": "USD" "currency": "USD"
}, },
"remaining_passes": 3, "remaining_passes": 3,
"timestamp": 1771521872816 "timestamp": 1771535572741
} }
}, },
"opus45MigrationComplete": true, "opus45MigrationComplete": true,
@ -2171,8 +2188,8 @@
"lastUsedAt": 1770959377885 "lastUsedAt": 1770959377885
}, },
"sync-config": { "sync-config": {
"usageCount": 18, "usageCount": 19,
"lastUsedAt": 1771507439661 "lastUsedAt": 1771530471038
}, },
"claude-optimised": { "claude-optimised": {
"usageCount": 4, "usageCount": 4,
@ -2187,8 +2204,8 @@
"lastUsedAt": 1771528594878 "lastUsedAt": 1771528594878
}, },
"commit-push": { "commit-push": {
"usageCount": 16, "usageCount": 17,
"lastUsedAt": 1771507468440 "lastUsedAt": 1771537208260
}, },
"check-rarity": { "check-rarity": {
"usageCount": 1, "usageCount": 1,
@ -2213,6 +2230,10 @@
"claude-automation-recommender": { "claude-automation-recommender": {
"usageCount": 1, "usageCount": 1,
"lastUsedAt": 1771474474878 "lastUsedAt": 1771474474878
},
"bmad-qa": {
"usageCount": 1,
"lastUsedAt": 1771536951910
} }
}, },
"opusProMigrationComplete": true, "opusProMigrationComplete": true,
@ -2221,7 +2242,7 @@
"passesLastSeenRemaining": 3, "passesLastSeenRemaining": 3,
"clientDataCache": { "clientDataCache": {
"data": {}, "data": {},
"timestamp": 1771529403021 "timestamp": 1771538091026
}, },
"hasShownOpus46Notice": { "hasShownOpus46Notice": {
"57783733-6e1e-48d5-9cb7-fa588a77b795": true "57783733-6e1e-48d5-9cb7-fa588a77b795": true

View File

@ -1,5 +1,5 @@
{ {
"numStartups": 661, "numStartups": 663,
"installMethod": "native", "installMethod": "native",
"autoUpdates": true, "autoUpdates": true,
"preferredNotifChannel": "iterm2_with_bell", "preferredNotifChannel": "iterm2_with_bell",
@ -11,7 +11,7 @@
"enter-to-steer-in-relatime": 651, "enter-to-steer-in-relatime": 651,
"todo-list": 660, "todo-list": 660,
"# for memory": 38, "# for memory": 38,
"install-github-app": 653, "install-github-app": 663,
"permissions": 654, "permissions": 654,
"drag-and-drop-images": 659, "drag-and-drop-images": 659,
"double-esc": 75, "double-esc": 75,
@ -21,7 +21,7 @@
"custom-commands": 655, "custom-commands": 655,
"shift-enter": 661, "shift-enter": 661,
"shift-tab": 655, "shift-tab": 655,
"custom-agents": 648, "custom-agents": 663,
"status-line": 536, "status-line": 536,
"git-worktrees": 661, "git-worktrees": 661,
"image-paste": 646, "image-paste": 646,
@ -44,7 +44,7 @@
"agent-flag": 652 "agent-flag": 652
}, },
"memoryUsageCount": 18, "memoryUsageCount": 18,
"promptQueueUseCount": 4478, "promptQueueUseCount": 4513,
"cachedStatsigGates": { "cachedStatsigGates": {
"tengu_disable_bypass_permissions_mode": false, "tengu_disable_bypass_permissions_mode": false,
"tengu_use_file_checkpoints": true, "tengu_use_file_checkpoints": true,
@ -144,7 +144,7 @@
"tengu_vinteuil_phrase": true, "tengu_vinteuil_phrase": true,
"tengu_oboe": true, "tengu_oboe": true,
"tengu_tst_names_in_messages": false, "tengu_tst_names_in_messages": false,
"tengu_chomp_inflection": true, "tengu_chomp_inflection": false,
"tengu_silver_lantern": false, "tengu_silver_lantern": false,
"tengu_copper_lantern": false, "tengu_copper_lantern": false,
"tengu_workout2": true, "tengu_workout2": true,
@ -615,17 +615,17 @@
"ssh-tdarr", "ssh-tdarr",
"notediscovery" "notediscovery"
], ],
"lastCost": 0.29647025, "lastCost": 40.23374199999994,
"lastAPIDuration": 4856, "lastAPIDuration": 5995420,
"lastToolDuration": 0, "lastToolDuration": 2896147,
"lastDuration": 247436, "lastDuration": 20374119,
"lastLinesAdded": 0, "lastLinesAdded": 2594,
"lastLinesRemoved": 0, "lastLinesRemoved": 143,
"lastTotalInputTokens": 297, "lastTotalInputTokens": 747870,
"lastTotalOutputTokens": 29, "lastTotalOutputTokens": 224482,
"lastTotalCacheCreationInputTokens": 47321, "lastTotalCacheCreationInputTokens": 1751354,
"lastTotalCacheReadInputTokens": 0, "lastTotalCacheReadInputTokens": 47009653,
"lastSessionId": "8d1ffe8f-1e99-4e60-b9dd-270e9a939153", "lastSessionId": "606a9f26-d08c-4654-8a9a-e118a2735aaa",
"reactVulnerabilityCache": { "reactVulnerabilityCache": {
"detected": false, "detected": false,
"package": null, "package": null,
@ -633,34 +633,44 @@
"version": null, "version": null,
"packageManager": null "packageManager": null
}, },
"lastAPIDurationWithoutRetries": 4855, "lastAPIDurationWithoutRetries": 4961513,
"lastModelUsage": { "lastModelUsage": {
"claude-haiku-4-5-20251001": {
"inputTokens": 294,
"outputTokens": 16,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 0,
"webSearchRequests": 0,
"costUSD": 0.000374
},
"claude-opus-4-6": { "claude-opus-4-6": {
"inputTokens": 3, "inputTokens": 9124,
"outputTokens": 13, "outputTokens": 164889,
"cacheReadInputTokens": 0, "cacheReadInputTokens": 44348975,
"cacheCreationInputTokens": 47321, "cacheCreationInputTokens": 1408982,
"webSearchRequests": 0, "webSearchRequests": 0,
"costUSD": 0.29609625 "costUSD": 35.148469999999996
},
"claude-haiku-4-5-20251001": {
"inputTokens": 732883,
"outputTokens": 15034,
"cacheReadInputTokens": 0,
"cacheCreationInputTokens": 67247,
"webSearchRequests": 0,
"costUSD": 0.892111749999999
},
"claude-sonnet-4-6": {
"inputTokens": 5863,
"outputTokens": 44559,
"cacheReadInputTokens": 2660678,
"cacheCreationInputTokens": 275125,
"webSearchRequests": 0,
"costUSD": 4.19316025
} }
}, },
"lastSessionMetrics": { "lastSessionMetrics": {
"frame_duration_ms_count": 24, "frame_duration_ms_count": 126433,
"frame_duration_ms_min": 0.26554799999999545, "frame_duration_ms_min": 0.033378999680280685,
"frame_duration_ms_max": 3.014287000000081, "frame_duration_ms_max": 185.3289749994874,
"frame_duration_ms_avg": 1.1559620416666985, "frame_duration_ms_avg": 4.580825976891975,
"frame_duration_ms_p50": 0.9233974999999646, "frame_duration_ms_p50": 1.4972434998489916,
"frame_duration_ms_p95": 1.925081300000033, "frame_duration_ms_p95": 3.451200450630833,
"frame_duration_ms_p99": 2.771056710000067 "frame_duration_ms_p99": 90.20297368977218
} },
"lastFpsAverage": 6.12,
"lastFpsLow1Pct": 11.02
}, },
"/mnt/NV2/Development/major-domo": { "/mnt/NV2/Development/major-domo": {
"allowedTools": [], "allowedTools": [],
@ -2012,13 +2022,20 @@
} }
}, },
"hasOpusPlanDefault": false, "hasOpusPlanDefault": false,
"lastPlanModeUse": 1771437917321, "lastPlanModeUse": 1771532414321,
"feedbackSurveyState": { "feedbackSurveyState": {
"lastShownTime": 1771451253831 "lastShownTime": 1771451253831
}, },
"sonnet45MigrationComplete": true, "sonnet45MigrationComplete": true,
"claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z", "claudeCodeFirstTokenDate": "2025-07-09T18:28:23.685647Z",
"mcpServers": {}, "mcpServers": {
"cognitive-memory": {
"command": "python3",
"args": [
"/home/cal/.claude/skills/cognitive-memory/mcp_server.py"
]
}
},
"s1mNonSubscriberAccessCache": { "s1mNonSubscriberAccessCache": {
"fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": { "fda1c56e-6b2f-4c2d-94f2-636cf90ad0f2": {
"hasAccess": false, "hasAccess": false,
@ -2088,7 +2105,7 @@
"currency": "USD" "currency": "USD"
}, },
"remaining_passes": 3, "remaining_passes": 3,
"timestamp": 1771521872816 "timestamp": 1771535572741
} }
}, },
"opus45MigrationComplete": true, "opus45MigrationComplete": true,
@ -2171,8 +2188,8 @@
"lastUsedAt": 1770959377885 "lastUsedAt": 1770959377885
}, },
"sync-config": { "sync-config": {
"usageCount": 18, "usageCount": 19,
"lastUsedAt": 1771507439661 "lastUsedAt": 1771530471038
}, },
"claude-optimised": { "claude-optimised": {
"usageCount": 4, "usageCount": 4,
@ -2187,8 +2204,8 @@
"lastUsedAt": 1771528594878 "lastUsedAt": 1771528594878
}, },
"commit-push": { "commit-push": {
"usageCount": 16, "usageCount": 17,
"lastUsedAt": 1771507468440 "lastUsedAt": 1771537208260
}, },
"check-rarity": { "check-rarity": {
"usageCount": 1, "usageCount": 1,
@ -2213,6 +2230,10 @@
"claude-automation-recommender": { "claude-automation-recommender": {
"usageCount": 1, "usageCount": 1,
"lastUsedAt": 1771474474878 "lastUsedAt": 1771474474878
},
"bmad-qa": {
"usageCount": 1,
"lastUsedAt": 1771536951910
} }
}, },
"opusProMigrationComplete": true, "opusProMigrationComplete": true,
@ -2221,7 +2242,7 @@
"passesLastSeenRemaining": 3, "passesLastSeenRemaining": 3,
"clientDataCache": { "clientDataCache": {
"data": {}, "data": {},
"timestamp": 1771529672138 "timestamp": 1771538153521
}, },
"hasShownOpus46Notice": { "hasShownOpus46Notice": {
"57783733-6e1e-48d5-9cb7-fa588a77b795": true "57783733-6e1e-48d5-9cb7-fa588a77b795": true

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{ {
"fetchedAt": "2026-02-19T19:47:49.668Z", "fetchedAt": "2026-02-19T21:12:52.014Z",
"plugins": [ "plugins": [
{ {
"plugin": "code-review@claude-plugins-official", "plugin": "code-review@claude-plugins-official",

View File

@ -13,6 +13,6 @@
"repo": "anthropics/claude-code" "repo": "anthropics/claude-code"
}, },
"installLocation": "/home/cal/.claude/plugins/marketplaces/claude-code-plugins", "installLocation": "/home/cal/.claude/plugins/marketplaces/claude-code-plugins",
"lastUpdated": "2026-02-19T19:47:51.730Z" "lastUpdated": "2026-02-19T21:13:33.303Z"
} }
} }

View File

@ -54,7 +54,8 @@
"mcp__notediscovery__get_notes_by_tag", "mcp__notediscovery__get_notes_by_tag",
"mcp__memorygraph__*", "mcp__memorygraph__*",
"mcp__memorygraph__get_memory", "mcp__memorygraph__get_memory",
"Skill(notediscovery)" "Skill(notediscovery)",
"mcp__cognitive-memory__*"
], ],
"deny": [ "deny": [
"Bash(diskutil partitionDisk)", "Bash(diskutil partitionDisk)",
@ -115,7 +116,7 @@
"hooks": [ "hooks": [
{ {
"type": "command", "type": "command",
"command": "/usr/bin/python3 /home/cal/.claude/scripts/session-memory/session_memory.py", "command": "/usr/bin/python3 /home/cal/.claude/skills/cognitive-memory/scripts/session_memory.py",
"timeout": 15 "timeout": 15
} }
] ]

View File

@ -56,8 +56,10 @@ All commands support `--help` for full argument details. Key non-obvious feature
# --episode flag auto-logs a session entry when storing # --episode flag auto-logs a session entry when storing
claude-memory store --type solution --title "Fixed X" --content "..." --tags "t1,t2" --episode claude-memory store --type solution --title "Fixed X" --content "..." --tags "t1,t2" --episode
# --semantic merges keyword + Ollama embedding results (run embed first) # recall uses semantic+keyword merge by default (when embeddings exist)
claude-memory recall "timeout error" --semantic claude-memory recall "timeout error"
# use --no-semantic for keyword-only (faster, ~3ms vs ~200ms)
claude-memory recall "timeout error" --no-semantic
# procedure type takes structured steps/preconditions/postconditions # procedure type takes structured steps/preconditions/postconditions
claude-memory procedure --title "Deploy flow" --content "..." \ claude-memory procedure --title "Deploy flow" --content "..." \
@ -99,7 +101,29 @@ claude-memory tags suggest <memory_id>
| 0.5-0.7 | Standard - useful pattern or solution | | 0.5-0.7 | Standard - useful pattern or solution |
| 0.3-0.4 | Minor - nice-to-know, edge cases | | 0.3-0.4 | Minor - nice-to-know, edge cases |
## Directory Structure ## Skill Directory Structure
```
~/.claude/skills/cognitive-memory/
├── client.py # Core API + CLI entrypoint
├── mcp_server.py # MCP server for Claude Code tools
├── SKILL.md # This file
├── SCHEMA.md # Format reference for all file types
├── feature.json # Skill manifest
├── scripts/
│ ├── session_memory.py # SessionEnd hook — auto-stores session learnings
│ └── ensure-symlinks.sh # Refreshes MEMORY.md symlinks to CORE.md
├── systemd/
│ ├── README.md # Install instructions for timers
│ ├── cognitive-memory-daily.* # Daily: decay, core, symlinks
│ ├── cognitive-memory-embed.* # Hourly: refresh embeddings
│ └── cognitive-memory-weekly.* # Weekly: reflection cycle
└── dev/
├── PROJECT_PLAN.json # Development roadmap
└── migrate.py # One-time MemoryGraph migration
```
## Data Directory Structure
``` ```
~/.claude/memory/ ~/.claude/memory/
@ -185,8 +209,10 @@ claude-memory store --type decision \
# First-time setup: generate embeddings (requires Ollama + nomic-embed-text) # First-time setup: generate embeddings (requires Ollama + nomic-embed-text)
claude-memory embed claude-memory embed
# Recall with semantic search (merges keyword + embedding results) # Recall uses semantic+keyword merge by default
claude-memory recall "authentication timeout" --semantic claude-memory recall "authentication timeout"
# Use --no-semantic for keyword-only
claude-memory recall "authentication timeout" --no-semantic
``` ```
### 5. Store a Procedure ### 5. Store a Procedure
@ -256,7 +282,7 @@ Auto-generated summary of memory themes, cross-project patterns, and access stat
## Semantic Search ## Semantic Search
Requires Ollama running locally with the `nomic-embed-text` model. Generate embeddings with `claude-memory embed`, then use `--semantic` flag on recall to merge keyword and embedding-based results. Semantic search provides deeper matching beyond exact title/tag keywords - useful for finding conceptually related memories even when different terminology was used. Requires Ollama running locally with the `nomic-embed-text` model. Generate embeddings with `claude-memory embed`. Recall uses semantic+keyword merge by default when embeddings exist (~200ms with warm cache). Use `--no-semantic` for keyword-only (~3ms). Embeddings are cached in memory (mtime-based invalidation) so repeated recalls avoid re-parsing the 24MB embeddings file. Semantic search provides deeper matching beyond exact title/tag keywords - useful for finding conceptually related memories even when different terminology was used.
## MCP Server ## MCP Server
@ -315,7 +341,7 @@ This skill should be used proactively when:
4. **Pattern discovered** - Store for future recall 4. **Pattern discovered** - Store for future recall
5. **Configuration worked** - Store what worked and why 5. **Configuration worked** - Store what worked and why
6. **Troubleshooting complete** - Store what was tried, what worked 6. **Troubleshooting complete** - Store what was tried, what worked
7. **Session start** - CORE.md auto-loads via MEMORY.md symlinks; read REFLECTION.md for theme context, then recall relevant memories (use `--semantic` for deeper matching) 7. **Session start** - CORE.md auto-loads via MEMORY.md symlinks; read REFLECTION.md for theme context, then recall relevant memories (semantic is on by default)
8. **Multi-step workflow documented** - Use `procedure` type with structured steps/preconditions/postconditions 8. **Multi-step workflow documented** - Use `procedure` type with structured steps/preconditions/postconditions
9. **Periodically** - Run `reflect` to cluster memories and surface cross-cutting insights. Run `tags suggest` to find missing tag connections 9. **Periodically** - Run `reflect` to cluster memories and surface cross-cutting insights. Run `tags suggest` to find missing tag connections
10. **After adding memories** - Run `embed` to refresh semantic search index 10. **After adding memories** - Run `embed` to refresh semantic search index

View File

@ -553,6 +553,8 @@ class CognitiveMemoryClient:
self.memory_dir = memory_dir or MEMORY_DIR self.memory_dir = memory_dir or MEMORY_DIR
self.index_path = self.memory_dir / "_index.json" self.index_path = self.memory_dir / "_index.json"
self.state_path = self.memory_dir / "_state.json" self.state_path = self.memory_dir / "_state.json"
self._embeddings_cache: Optional[Dict] = None
self._embeddings_mtime: float = 0.0
self._ensure_dirs() self._ensure_dirs()
def _ensure_dirs(self): def _ensure_dirs(self):
@ -563,6 +565,32 @@ class CognitiveMemoryClient:
(self.memory_dir / "episodes").mkdir(parents=True, exist_ok=True) (self.memory_dir / "episodes").mkdir(parents=True, exist_ok=True)
(self.memory_dir / "vault").mkdir(parents=True, exist_ok=True) (self.memory_dir / "vault").mkdir(parents=True, exist_ok=True)
def _load_embeddings_cached(self) -> Optional[Dict]:
"""Load _embeddings.json with mtime-based caching.
Returns the parsed dict, or None if the file doesn't exist or fails to parse.
Only re-reads from disk when the file's mtime has changed.
"""
embeddings_path = self.memory_dir / "_embeddings.json"
if not embeddings_path.exists():
return None
try:
current_mtime = embeddings_path.stat().st_mtime
except OSError:
return None
if (
self._embeddings_cache is not None
and current_mtime == self._embeddings_mtime
):
return self._embeddings_cache
try:
data = json.loads(embeddings_path.read_text())
self._embeddings_cache = data
self._embeddings_mtime = current_mtime
return data
except (json.JSONDecodeError, OSError):
return None
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Index and State management # Index and State management
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@ -877,7 +905,7 @@ class CognitiveMemoryClient:
query: str, query: str,
memory_types: Optional[List[str]] = None, memory_types: Optional[List[str]] = None,
limit: int = 10, limit: int = 10,
semantic: bool = False, semantic: bool = True,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
"""Search memories by query, ranked by relevance and decay score. """Search memories by query, ranked by relevance and decay score.
@ -941,57 +969,55 @@ class CognitiveMemoryClient:
results.sort(key=lambda x: x.pop("_score", 0), reverse=True) results.sort(key=lambda x: x.pop("_score", 0), reverse=True)
keyword_results = results[:limit] keyword_results = results[:limit]
# Merge with semantic results if requested # Merge with semantic results (on by default)
# Weights: semantic 60%, keyword 40% (--semantic signals intent for # Weights: semantic 60%, keyword 40%
# conceptual matching; keyword acts as precision boost for exact terms) # Conceptual matching dominates; keyword acts as precision boost for exact terms
if semantic: if semantic:
embeddings_path = self.memory_dir / "_embeddings.json" sem_results = self.semantic_recall(query, limit=limit)
if embeddings_path.exists(): if sem_results:
sem_results = self.semantic_recall(query, limit=limit) score_map: Dict[str, float] = {}
if sem_results: result_map: Dict[str, Dict] = {}
score_map: Dict[str, float] = {}
result_map: Dict[str, Dict] = {}
# Keyword: normalize rank to 0-1 (rank 1 = 1.0, last = ~0.1) # Keyword: normalize rank to 0-1 (rank 1 = 1.0, last = ~0.1)
kw_weight = 0.4 kw_weight = 0.4
for i, r in enumerate(keyword_results): for i, r in enumerate(keyword_results):
mid = r["id"] mid = r["id"]
normalized = (limit - i) / limit normalized = (limit - i) / limit
score_map[mid] = normalized * kw_weight score_map[mid] = normalized * kw_weight
result_map[mid] = r result_map[mid] = r
# Semantic: similarity is already 0-1 # Semantic: similarity is already 0-1
sem_weight = 0.6 sem_weight = 0.6
for r in sem_results: for r in sem_results:
mid = r["id"] mid = r["id"]
sim = r.get("similarity", 0.0) sim = r.get("similarity", 0.0)
sem_score = sim * sem_weight sem_score = sim * sem_weight
if mid in score_map: if mid in score_map:
score_map[mid] += sem_score score_map[mid] += sem_score
result_map[mid]["similarity"] = sim result_map[mid]["similarity"] = sim
else: else:
score_map[mid] = sem_score score_map[mid] = sem_score
idx_entry = index.get("entries", {}).get(mid, {}) idx_entry = index.get("entries", {}).get(mid, {})
s = state.get("entries", {}).get(mid, {}) s = state.get("entries", {}).get(mid, {})
result_map[mid] = { result_map[mid] = {
"id": mid, "id": mid,
"type": r.get("type"), "type": r.get("type"),
"title": r.get("title"), "title": r.get("title"),
"tags": r.get("tags", []), "tags": r.get("tags", []),
"importance": idx_entry.get("importance"), "importance": idx_entry.get("importance"),
"decay_score": round(s.get("decay_score", 0.5), 3), "decay_score": round(s.get("decay_score", 0.5), 3),
"similarity": sim, "similarity": sim,
"path": r.get("path"), "path": r.get("path"),
"created": idx_entry.get("created"), "created": idx_entry.get("created"),
} }
# Sort by merged score # Sort by merged score
merged = sorted( merged = sorted(
result_map.values(), result_map.values(),
key=lambda x: score_map.get(x["id"], 0), key=lambda x: score_map.get(x["id"], 0),
reverse=True, reverse=True,
) )
return merged[:limit] return merged[:limit]
return keyword_results return keyword_results
@ -2181,13 +2207,8 @@ class CognitiveMemoryClient:
Uses the same provider that generated stored embeddings to embed the query. Uses the same provider that generated stored embeddings to embed the query.
Skips vectors with dimension mismatch as safety guard. Skips vectors with dimension mismatch as safety guard.
""" """
embeddings_path = self.memory_dir / "_embeddings.json" emb_data = self._load_embeddings_cached()
if not embeddings_path.exists(): if emb_data is None:
return []
try:
emb_data = json.loads(embeddings_path.read_text())
except (json.JSONDecodeError, OSError):
return [] return []
stored = emb_data.get("entries", {}) stored = emb_data.get("entries", {})
@ -2831,10 +2852,10 @@ def main():
sp.add_argument("--types", help="Comma-separated memory types") sp.add_argument("--types", help="Comma-separated memory types")
sp.add_argument("--limit", "-n", type=int, default=10, help="Max results") sp.add_argument("--limit", "-n", type=int, default=10, help="Max results")
sp.add_argument( sp.add_argument(
"--semantic", "--no-semantic",
action="store_true", action="store_true",
default=False, default=False,
help="Also use embedding similarity (requires embed first)", help="Disable semantic search (keyword-only, faster)",
) )
# get # get
@ -3041,7 +3062,10 @@ def main():
elif args.command == "recall": elif args.command == "recall":
types = [t.strip() for t in args.types.split(",")] if args.types else None types = [t.strip() for t in args.types.split(",")] if args.types else None
result = client.recall( result = client.recall(
args.query, memory_types=types, limit=args.limit, semantic=args.semantic args.query,
memory_types=types,
limit=args.limit,
semantic=not args.no_semantic,
) )
elif args.command == "get": elif args.command == "get":

View File

@ -7,9 +7,14 @@
"status": "active", "status": "active",
"files": { "files": {
"client.py": "CLI and Python API for all memory operations", "client.py": "CLI and Python API for all memory operations",
"migrate.py": "One-time migration from MemoryGraph SQLite", "mcp_server.py": "MCP server for Claude Code tool integration",
"SKILL.md": "Skill documentation and activation triggers", "SKILL.md": "Skill documentation and activation triggers",
"SCHEMA.md": "Format documentation for all file types" "SCHEMA.md": "Format documentation for all file types",
"scripts/session_memory.py": "SessionEnd hook — auto-stores session learnings",
"scripts/ensure-symlinks.sh": "Refreshes MEMORY.md symlinks to CORE.md",
"systemd/": "Reference copies of systemd user timers (see systemd/README.md)",
"dev/migrate.py": "One-time migration from MemoryGraph SQLite",
"dev/PROJECT_PLAN.json": "Development roadmap and task tracking"
}, },
"data_location": "~/.claude/memory/", "data_location": "~/.claude/memory/",
"dependencies": "stdlib-only (no external packages; Ollama optional for semantic search)", "dependencies": "stdlib-only (no external packages; Ollama optional for semantic search)",

View File

@ -64,7 +64,7 @@ def create_tools() -> list:
"name": "memory_recall", "name": "memory_recall",
"description": ( "description": (
"Search memories by a natural language query, ranked by relevance and decay score. " "Search memories by a natural language query, ranked by relevance and decay score. "
"Set semantic=true to merge keyword results with vector similarity when embeddings exist." "Semantic search is enabled by default when embeddings exist. Set semantic=false for keyword-only."
), ),
"inputSchema": { "inputSchema": {
"type": "object", "type": "object",
@ -75,7 +75,7 @@ def create_tools() -> list:
}, },
"semantic": { "semantic": {
"type": "boolean", "type": "boolean",
"description": "Merge with semantic/vector similarity search (requires embeddings, default false)", "description": "Merge with semantic/vector similarity search (requires embeddings, default true)",
}, },
"limit": { "limit": {
"type": "integer", "type": "integer",
@ -442,7 +442,7 @@ def handle_tool_call(
elif tool_name == "memory_recall": elif tool_name == "memory_recall":
results = client.recall( results = client.recall(
query=arguments["query"], query=arguments["query"],
semantic=arguments.get("semantic", False), semantic=arguments.get("semantic", True),
limit=arguments.get("limit", 10), limit=arguments.get("limit", 10),
) )
return ok(results) return ok(results)

View File

@ -0,0 +1,26 @@
#!/bin/bash
# Ensure all Claude Code project MEMORY.md files symlink to cognitive memory CORE.md
# This makes CORE.md auto-load into every session's system prompt.
# Run by cognitive-memory-daily.service or manually.
CORE="/home/cal/.claude/memory/CORE.md"
PROJECTS="/home/cal/.claude/projects"
if [ ! -f "$CORE" ]; then
echo "ERROR: CORE.md not found at $CORE"
exit 1
fi
for dir in "$PROJECTS"/*/; do
memdir="$dir/memory"
memfile="$memdir/MEMORY.md"
mkdir -p "$memdir"
# Only create/fix symlink if it doesn't already point to CORE.md
if [ -L "$memfile" ] && [ "$(readlink "$memfile")" = "$CORE" ]; then
continue
fi
# Remove existing file (regular file or broken symlink)
rm -f "$memfile"
ln -s "$CORE" "$memfile"
echo "Linked: $memfile"
done

View File

@ -109,15 +109,38 @@ def read_transcript(transcript_path: str) -> list[dict]:
return messages return messages
def _is_memory_tool_use(block: dict) -> str | None:
"""Check if a tool_use block is a memory operation.
Detects both CLI calls (Bash with 'claude-memory') and MCP tool calls
(mcp__cognitive-memory__memory_*). Returns a short description of the
match or None.
"""
name = block.get("name", "")
# MCP tool calls: mcp__cognitive-memory__memory_store, memory_recall, etc.
if name.startswith("mcp__cognitive-memory__memory_"):
return f"MCP:{name}"
# Legacy/CLI: Bash commands containing 'claude-memory'
if name == "Bash":
cmd = block.get("input", {}).get("command", "")
if "claude-memory" in cmd:
return f"CLI:{cmd[:100]}"
return None
def find_last_memory_command_index(messages: list[dict]) -> int: def find_last_memory_command_index(messages: list[dict]) -> int:
"""Find the index of the last message containing a claude-memory command. """Find the index of the last message containing a memory operation.
Scans for Bash tool_use blocks where the command contains 'claude-memory' Scans for both MCP tool calls (mcp__cognitive-memory__memory_*) and
(store, recall, episode, etc). Returns the index of that message so we can Bash tool_use blocks where the command contains 'claude-memory'.
slice the transcript to only process messages after the last memory operation, Returns the index of that message so we can slice the transcript to
avoiding duplicate storage. only process messages after the last memory operation, avoiding
duplicate storage.
Returns -1 if no claude-memory commands were found. Returns -1 if no memory operations were found.
""" """
last_index = -1 last_index = -1
found_commands = [] found_commands = []
@ -132,19 +155,17 @@ def find_last_memory_command_index(messages: list[dict]) -> int:
continue continue
if block.get("type") != "tool_use": if block.get("type") != "tool_use":
continue continue
if block.get("name") != "Bash": match = _is_memory_tool_use(block)
continue if match:
cmd = block.get("input", {}).get("command", "")
if "claude-memory" in cmd:
last_index = i last_index = i
found_commands.append(f"msg[{i}]: {cmd[:100]}") found_commands.append(f"msg[{i}]: {match}")
if found_commands: if found_commands:
log(f"[cutoff] Found {len(found_commands)} claude-memory commands:") log(f"[cutoff] Found {len(found_commands)} memory operations:")
for fc in found_commands: for fc in found_commands:
log(f"[cutoff] {fc}") log(f"[cutoff] {fc}")
log(f"[cutoff] Will slice after message index {last_index}") log(f"[cutoff] Will slice after message index {last_index}")
else: else:
log("[cutoff] No claude-memory commands found — processing full transcript") log("[cutoff] No memory operations found — processing full transcript")
return last_index return last_index

View File

@ -0,0 +1,34 @@
# Cognitive Memory Systemd Timers
Reference copies of the systemd user units that automate memory maintenance.
## Services
| Unit | Schedule | What it does |
|------|----------|-------------|
| `cognitive-memory-daily` | daily | Decay scores, regenerate CORE.md, refresh MEMORY.md symlinks |
| `cognitive-memory-embed` | hourly | Refresh embeddings (skips if unchanged) |
| `cognitive-memory-weekly` | weekly | Run reflection cycle |
## Install / Update
```bash
# Copy units into place
cp ~/.claude/skills/cognitive-memory/systemd/*.service \
~/.claude/skills/cognitive-memory/systemd/*.timer \
~/.config/systemd/user/
# Reload and enable
systemctl --user daemon-reload
systemctl --user enable --now cognitive-memory-daily.timer
systemctl --user enable --now cognitive-memory-embed.timer
systemctl --user enable --now cognitive-memory-weekly.timer
```
## Verify
```bash
systemctl --user list-timers 'cognitive-memory-*'
systemctl --user start cognitive-memory-daily.service # manual test run
journalctl --user -u cognitive-memory-daily.service --since today
```

View File

@ -0,0 +1,6 @@
[Unit]
Description=Cognitive Memory daily maintenance (decay, core, symlinks)
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'export PATH="/home/cal/.local/bin:$PATH" && /home/cal/.local/bin/claude-memory decay && /home/cal/.local/bin/claude-memory core && /home/cal/.local/bin/claude-memory-symlinks'

View File

@ -0,0 +1,9 @@
[Unit]
Description=Run cognitive memory daily maintenance
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target

View File

@ -0,0 +1,6 @@
[Unit]
Description=Cognitive Memory hourly embedding refresh (skips if unchanged)
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'export PATH="/home/cal/.local/bin:$PATH" && /home/cal/.local/bin/claude-memory embed --if-changed'

View File

@ -0,0 +1,9 @@
[Unit]
Description=Run cognitive memory embedding refresh hourly
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target

View File

@ -0,0 +1,6 @@
[Unit]
Description=Cognitive Memory weekly reflection
[Service]
Type=oneshot
ExecStart=/home/cal/.local/bin/claude-memory reflect

View File

@ -0,0 +1,9 @@
[Unit]
Description=Run cognitive memory weekly reflection
[Timer]
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.target