shieldcortex 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +282 -0
- package/dashboard/components.json +22 -0
- package/dashboard/eslint.config.mjs +42 -0
- package/dashboard/next.config.ts +7 -0
- package/dashboard/package-lock.json +8053 -0
- package/dashboard/package.json +44 -0
- package/dashboard/postcss.config.mjs +7 -0
- package/dashboard/public/file.svg +1 -0
- package/dashboard/public/globe.svg +1 -0
- package/dashboard/public/next.svg +1 -0
- package/dashboard/public/vercel.svg +1 -0
- package/dashboard/public/window.svg +1 -0
- package/dashboard/scripts/ensure-api.mjs +76 -0
- package/dashboard/src/app/error.tsx +49 -0
- package/dashboard/src/app/favicon.ico +0 -0
- package/dashboard/src/app/globals.css +130 -0
- package/dashboard/src/app/layout.tsx +35 -0
- package/dashboard/src/app/page.tsx +364 -0
- package/dashboard/src/components/Providers.tsx +27 -0
- package/dashboard/src/components/brain/ActivityPulseSystem.tsx +229 -0
- package/dashboard/src/components/brain/BrainMesh.tsx +133 -0
- package/dashboard/src/components/brain/BrainRegions.tsx +254 -0
- package/dashboard/src/components/brain/BrainScene.tsx +255 -0
- package/dashboard/src/components/brain/CategoryLabels.tsx +103 -0
- package/dashboard/src/components/brain/CoreSphere.tsx +215 -0
- package/dashboard/src/components/brain/DataFlowParticles.tsx +123 -0
- package/dashboard/src/components/brain/DataStreamRings.tsx +161 -0
- package/dashboard/src/components/brain/ElectronFlow.tsx +323 -0
- package/dashboard/src/components/brain/HolographicGrid.tsx +235 -0
- package/dashboard/src/components/brain/MemoryLinks.tsx +271 -0
- package/dashboard/src/components/brain/MemoryNode.tsx +245 -0
- package/dashboard/src/components/brain/NeuralPathways.tsx +441 -0
- package/dashboard/src/components/brain/SynapseNodes.tsx +312 -0
- package/dashboard/src/components/brain/TimelineControls.tsx +205 -0
- package/dashboard/src/components/chip/ChipScene.tsx +497 -0
- package/dashboard/src/components/chip/ChipSubstrate.tsx +238 -0
- package/dashboard/src/components/chip/CortexCore.tsx +210 -0
- package/dashboard/src/components/chip/DataBus.tsx +416 -0
- package/dashboard/src/components/chip/MemoryCell.tsx +225 -0
- package/dashboard/src/components/chip/MemoryGrid.tsx +328 -0
- package/dashboard/src/components/chip/QuantumCell.tsx +316 -0
- package/dashboard/src/components/chip/SectionLabel.tsx +113 -0
- package/dashboard/src/components/chip/index.ts +14 -0
- package/dashboard/src/components/controls/ControlPanel.tsx +106 -0
- package/dashboard/src/components/controls/VersionPanel.tsx +185 -0
- package/dashboard/src/components/dashboard/StatsPanel.tsx +164 -0
- package/dashboard/src/components/debug/ActivityLog.tsx +250 -0
- package/dashboard/src/components/debug/DebugPanel.tsx +101 -0
- package/dashboard/src/components/debug/QueryTester.tsx +192 -0
- package/dashboard/src/components/debug/RelationshipGraph.tsx +403 -0
- package/dashboard/src/components/debug/SqlConsole.tsx +319 -0
- package/dashboard/src/components/graph/KnowledgeGraph.tsx +230 -0
- package/dashboard/src/components/graph/OntologyGraph.tsx +631 -0
- package/dashboard/src/components/insights/ActivityHeatmap.tsx +131 -0
- package/dashboard/src/components/insights/InsightsView.tsx +46 -0
- package/dashboard/src/components/insights/KnowledgeMapPanel.tsx +80 -0
- package/dashboard/src/components/insights/QualityPanel.tsx +116 -0
- package/dashboard/src/components/memories/MemoriesView.tsx +150 -0
- package/dashboard/src/components/memories/MemoryCard.tsx +103 -0
- package/dashboard/src/components/memory/MemoryDetail.tsx +325 -0
- package/dashboard/src/components/nav/NavRail.tsx +54 -0
- package/dashboard/src/components/ui/button.tsx +62 -0
- package/dashboard/src/components/ui/card.tsx +92 -0
- package/dashboard/src/components/ui/input.tsx +21 -0
- package/dashboard/src/hooks/useDebouncedValue.ts +24 -0
- package/dashboard/src/hooks/useMemories.ts +458 -0
- package/dashboard/src/hooks/useSuggestions.ts +46 -0
- package/dashboard/src/lib/category-colors.ts +84 -0
- package/dashboard/src/lib/position-algorithm.ts +177 -0
- package/dashboard/src/lib/simplex-noise.ts +217 -0
- package/dashboard/src/lib/store.ts +88 -0
- package/dashboard/src/lib/utils.ts +6 -0
- package/dashboard/src/lib/websocket.ts +249 -0
- package/dashboard/src/types/memory.ts +73 -0
- package/dashboard/tsconfig.json +34 -0
- package/dist/__tests__/consolidation-merge.test.d.ts +9 -0
- package/dist/__tests__/consolidation-merge.test.d.ts.map +1 -0
- package/dist/__tests__/consolidation-merge.test.js +137 -0
- package/dist/__tests__/consolidation-merge.test.js.map +1 -0
- package/dist/__tests__/contradictions.test.d.ts +8 -0
- package/dist/__tests__/contradictions.test.d.ts.map +1 -0
- package/dist/__tests__/contradictions.test.js +78 -0
- package/dist/__tests__/contradictions.test.js.map +1 -0
- package/dist/__tests__/salience-evolution.test.d.ts +7 -0
- package/dist/__tests__/salience-evolution.test.d.ts.map +1 -0
- package/dist/__tests__/salience-evolution.test.js +151 -0
- package/dist/__tests__/salience-evolution.test.js.map +1 -0
- package/dist/__tests__/store.test.d.ts +7 -0
- package/dist/__tests__/store.test.d.ts.map +1 -0
- package/dist/__tests__/store.test.js +582 -0
- package/dist/__tests__/store.test.js.map +1 -0
- package/dist/api/control.d.ts +27 -0
- package/dist/api/control.d.ts.map +1 -0
- package/dist/api/control.js +60 -0
- package/dist/api/control.js.map +1 -0
- package/dist/api/events.d.ts +159 -0
- package/dist/api/events.d.ts.map +1 -0
- package/dist/api/events.js +155 -0
- package/dist/api/events.js.map +1 -0
- package/dist/api/version.d.ts +36 -0
- package/dist/api/version.d.ts.map +1 -0
- package/dist/api/version.js +146 -0
- package/dist/api/version.js.map +1 -0
- package/dist/api/visualization-server.d.ts +11 -0
- package/dist/api/visualization-server.d.ts.map +1 -0
- package/dist/api/visualization-server.js +1186 -0
- package/dist/api/visualization-server.js.map +1 -0
- package/dist/context/project-context.d.ts +57 -0
- package/dist/context/project-context.d.ts.map +1 -0
- package/dist/context/project-context.js +135 -0
- package/dist/context/project-context.js.map +1 -0
- package/dist/database/init.d.ts +49 -0
- package/dist/database/init.d.ts.map +1 -0
- package/dist/database/init.js +567 -0
- package/dist/database/init.js.map +1 -0
- package/dist/defence/__tests__/firewall.test.d.ts +8 -0
- package/dist/defence/__tests__/firewall.test.d.ts.map +1 -0
- package/dist/defence/__tests__/firewall.test.js +123 -0
- package/dist/defence/__tests__/firewall.test.js.map +1 -0
- package/dist/defence/__tests__/fragmentation.test.d.ts +7 -0
- package/dist/defence/__tests__/fragmentation.test.d.ts.map +1 -0
- package/dist/defence/__tests__/fragmentation.test.js +51 -0
- package/dist/defence/__tests__/fragmentation.test.js.map +1 -0
- package/dist/defence/__tests__/pipeline.test.d.ts +8 -0
- package/dist/defence/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/defence/__tests__/pipeline.test.js +61 -0
- package/dist/defence/__tests__/pipeline.test.js.map +1 -0
- package/dist/defence/__tests__/sensitivity.test.d.ts +7 -0
- package/dist/defence/__tests__/sensitivity.test.d.ts.map +1 -0
- package/dist/defence/__tests__/sensitivity.test.js +61 -0
- package/dist/defence/__tests__/sensitivity.test.js.map +1 -0
- package/dist/defence/__tests__/trust.test.d.ts +7 -0
- package/dist/defence/__tests__/trust.test.d.ts.map +1 -0
- package/dist/defence/__tests__/trust.test.js +49 -0
- package/dist/defence/__tests__/trust.test.js.map +1 -0
- package/dist/defence/audit/index.d.ts +4 -0
- package/dist/defence/audit/index.d.ts.map +1 -0
- package/dist/defence/audit/index.js +3 -0
- package/dist/defence/audit/index.js.map +1 -0
- package/dist/defence/audit/logger.d.ts +14 -0
- package/dist/defence/audit/logger.d.ts.map +1 -0
- package/dist/defence/audit/logger.js +54 -0
- package/dist/defence/audit/logger.js.map +1 -0
- package/dist/defence/audit/queries.d.ts +33 -0
- package/dist/defence/audit/queries.d.ts.map +1 -0
- package/dist/defence/audit/queries.js +103 -0
- package/dist/defence/audit/queries.js.map +1 -0
- package/dist/defence/firewall/anomaly-scorer.d.ts +8 -0
- package/dist/defence/firewall/anomaly-scorer.d.ts.map +1 -0
- package/dist/defence/firewall/anomaly-scorer.js +58 -0
- package/dist/defence/firewall/anomaly-scorer.js.map +1 -0
- package/dist/defence/firewall/encoding-detector.d.ts +13 -0
- package/dist/defence/firewall/encoding-detector.d.ts.map +1 -0
- package/dist/defence/firewall/encoding-detector.js +120 -0
- package/dist/defence/firewall/encoding-detector.js.map +1 -0
- package/dist/defence/firewall/index.d.ts +21 -0
- package/dist/defence/firewall/index.d.ts.map +1 -0
- package/dist/defence/firewall/index.js +133 -0
- package/dist/defence/firewall/index.js.map +1 -0
- package/dist/defence/firewall/instruction-detector.d.ts +12 -0
- package/dist/defence/firewall/instruction-detector.d.ts.map +1 -0
- package/dist/defence/firewall/instruction-detector.js +99 -0
- package/dist/defence/firewall/instruction-detector.js.map +1 -0
- package/dist/defence/firewall/privilege-detector.d.ts +13 -0
- package/dist/defence/firewall/privilege-detector.d.ts.map +1 -0
- package/dist/defence/firewall/privilege-detector.js +89 -0
- package/dist/defence/firewall/privilege-detector.js.map +1 -0
- package/dist/defence/fragmentation/assembly-detector.d.ts +18 -0
- package/dist/defence/fragmentation/assembly-detector.d.ts.map +1 -0
- package/dist/defence/fragmentation/assembly-detector.js +72 -0
- package/dist/defence/fragmentation/assembly-detector.js.map +1 -0
- package/dist/defence/fragmentation/entity-extractor.d.ts +19 -0
- package/dist/defence/fragmentation/entity-extractor.d.ts.map +1 -0
- package/dist/defence/fragmentation/entity-extractor.js +86 -0
- package/dist/defence/fragmentation/entity-extractor.js.map +1 -0
- package/dist/defence/fragmentation/index.d.ts +23 -0
- package/dist/defence/fragmentation/index.d.ts.map +1 -0
- package/dist/defence/fragmentation/index.js +49 -0
- package/dist/defence/fragmentation/index.js.map +1 -0
- package/dist/defence/fragmentation/temporal-analyzer.d.ts +28 -0
- package/dist/defence/fragmentation/temporal-analyzer.d.ts.map +1 -0
- package/dist/defence/fragmentation/temporal-analyzer.js +41 -0
- package/dist/defence/fragmentation/temporal-analyzer.js.map +1 -0
- package/dist/defence/index.d.ts +12 -0
- package/dist/defence/index.d.ts.map +1 -0
- package/dist/defence/index.js +18 -0
- package/dist/defence/index.js.map +1 -0
- package/dist/defence/pipeline.d.ts +9 -0
- package/dist/defence/pipeline.d.ts.map +1 -0
- package/dist/defence/pipeline.js +115 -0
- package/dist/defence/pipeline.js.map +1 -0
- package/dist/defence/scanner/index.d.ts +5 -0
- package/dist/defence/scanner/index.d.ts.map +1 -0
- package/dist/defence/scanner/index.js +5 -0
- package/dist/defence/scanner/index.js.map +1 -0
- package/dist/defence/scanner/scan-existing.d.ts +34 -0
- package/dist/defence/scanner/scan-existing.d.ts.map +1 -0
- package/dist/defence/scanner/scan-existing.js +136 -0
- package/dist/defence/scanner/scan-existing.js.map +1 -0
- package/dist/defence/sensitivity/classifier.d.ts +6 -0
- package/dist/defence/sensitivity/classifier.d.ts.map +1 -0
- package/dist/defence/sensitivity/classifier.js +50 -0
- package/dist/defence/sensitivity/classifier.js.map +1 -0
- package/dist/defence/sensitivity/index.d.ts +11 -0
- package/dist/defence/sensitivity/index.d.ts.map +1 -0
- package/dist/defence/sensitivity/index.js +13 -0
- package/dist/defence/sensitivity/index.js.map +1 -0
- package/dist/defence/sensitivity/patterns.d.ts +14 -0
- package/dist/defence/sensitivity/patterns.d.ts.map +1 -0
- package/dist/defence/sensitivity/patterns.js +67 -0
- package/dist/defence/sensitivity/patterns.js.map +1 -0
- package/dist/defence/sensitivity/redaction.d.ts +17 -0
- package/dist/defence/sensitivity/redaction.d.ts.map +1 -0
- package/dist/defence/sensitivity/redaction.js +47 -0
- package/dist/defence/sensitivity/redaction.js.map +1 -0
- package/dist/defence/trust/index.d.ts +3 -0
- package/dist/defence/trust/index.d.ts.map +1 -0
- package/dist/defence/trust/index.js +3 -0
- package/dist/defence/trust/index.js.map +1 -0
- package/dist/defence/trust/recall-filter.d.ts +10 -0
- package/dist/defence/trust/recall-filter.d.ts.map +1 -0
- package/dist/defence/trust/recall-filter.js +38 -0
- package/dist/defence/trust/recall-filter.js.map +1 -0
- package/dist/defence/trust/source-scorer.d.ts +6 -0
- package/dist/defence/trust/source-scorer.d.ts.map +1 -0
- package/dist/defence/trust/source-scorer.js +34 -0
- package/dist/defence/trust/source-scorer.js.map +1 -0
- package/dist/defence/types.d.ts +88 -0
- package/dist/defence/types.d.ts.map +1 -0
- package/dist/defence/types.js +15 -0
- package/dist/defence/types.js.map +1 -0
- package/dist/embeddings/generator.d.ts +20 -0
- package/dist/embeddings/generator.d.ts.map +1 -0
- package/dist/embeddings/generator.js +83 -0
- package/dist/embeddings/generator.js.map +1 -0
- package/dist/embeddings/index.d.ts +2 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +2 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/errors.d.ts +74 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +131 -0
- package/dist/errors.js.map +1 -0
- package/dist/graph/backfill.d.ts +6 -0
- package/dist/graph/backfill.d.ts.map +1 -0
- package/dist/graph/backfill.js +33 -0
- package/dist/graph/backfill.js.map +1 -0
- package/dist/graph/extract.d.ts +21 -0
- package/dist/graph/extract.d.ts.map +1 -0
- package/dist/graph/extract.js +231 -0
- package/dist/graph/extract.js.map +1 -0
- package/dist/graph/resolve.d.ts +6 -0
- package/dist/graph/resolve.d.ts.map +1 -0
- package/dist/graph/resolve.js +126 -0
- package/dist/graph/resolve.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +248 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/activation.d.ts +69 -0
- package/dist/memory/activation.d.ts.map +1 -0
- package/dist/memory/activation.js +168 -0
- package/dist/memory/activation.js.map +1 -0
- package/dist/memory/consolidate.d.ts +98 -0
- package/dist/memory/consolidate.d.ts.map +1 -0
- package/dist/memory/consolidate.js +511 -0
- package/dist/memory/consolidate.js.map +1 -0
- package/dist/memory/contradiction.d.ts +69 -0
- package/dist/memory/contradiction.d.ts.map +1 -0
- package/dist/memory/contradiction.js +286 -0
- package/dist/memory/contradiction.js.map +1 -0
- package/dist/memory/decay.d.ts +62 -0
- package/dist/memory/decay.d.ts.map +1 -0
- package/dist/memory/decay.js +184 -0
- package/dist/memory/decay.js.map +1 -0
- package/dist/memory/salience.d.ts +36 -0
- package/dist/memory/salience.d.ts.map +1 -0
- package/dist/memory/salience.js +216 -0
- package/dist/memory/salience.js.map +1 -0
- package/dist/memory/similarity.d.ts +57 -0
- package/dist/memory/similarity.d.ts.map +1 -0
- package/dist/memory/similarity.js +114 -0
- package/dist/memory/similarity.js.map +1 -0
- package/dist/memory/store.d.ts +179 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +1184 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/types.d.ts +97 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +30 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +568 -0
- package/dist/server.js.map +1 -0
- package/dist/service/install.d.ts +15 -0
- package/dist/service/install.d.ts.map +1 -0
- package/dist/service/install.js +178 -0
- package/dist/service/install.js.map +1 -0
- package/dist/service/templates.d.ts +13 -0
- package/dist/service/templates.d.ts.map +1 -0
- package/dist/service/templates.js +58 -0
- package/dist/service/templates.js.map +1 -0
- package/dist/setup/claude-md.d.ts +12 -0
- package/dist/setup/claude-md.d.ts.map +1 -0
- package/dist/setup/claude-md.js +68 -0
- package/dist/setup/claude-md.js.map +1 -0
- package/dist/setup/clawdbot.d.ts +15 -0
- package/dist/setup/clawdbot.d.ts.map +1 -0
- package/dist/setup/clawdbot.js +118 -0
- package/dist/setup/clawdbot.js.map +1 -0
- package/dist/setup/doctor.d.ts +5 -0
- package/dist/setup/doctor.d.ts.map +1 -0
- package/dist/setup/doctor.js +141 -0
- package/dist/setup/doctor.js.map +1 -0
- package/dist/setup/hooks.d.ts +6 -0
- package/dist/setup/hooks.d.ts.map +1 -0
- package/dist/setup/hooks.js +36 -0
- package/dist/setup/hooks.js.map +1 -0
- package/dist/setup/migrate.d.ts +16 -0
- package/dist/setup/migrate.d.ts.map +1 -0
- package/dist/setup/migrate.js +164 -0
- package/dist/setup/migrate.js.map +1 -0
- package/dist/setup/settings-hooks.d.ts +7 -0
- package/dist/setup/settings-hooks.d.ts.map +1 -0
- package/dist/setup/settings-hooks.js +83 -0
- package/dist/setup/settings-hooks.js.map +1 -0
- package/dist/setup/uninstall.d.ts +12 -0
- package/dist/setup/uninstall.d.ts.map +1 -0
- package/dist/setup/uninstall.js +125 -0
- package/dist/setup/uninstall.js.map +1 -0
- package/dist/tools/context.d.ts +135 -0
- package/dist/tools/context.d.ts.map +1 -0
- package/dist/tools/context.js +273 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/forget.d.ts +53 -0
- package/dist/tools/forget.d.ts.map +1 -0
- package/dist/tools/forget.js +179 -0
- package/dist/tools/forget.js.map +1 -0
- package/dist/tools/graph.d.ts +46 -0
- package/dist/tools/graph.d.ts.map +1 -0
- package/dist/tools/graph.js +206 -0
- package/dist/tools/graph.js.map +1 -0
- package/dist/tools/recall.d.ts +79 -0
- package/dist/tools/recall.d.ts.map +1 -0
- package/dist/tools/recall.js +156 -0
- package/dist/tools/recall.js.map +1 -0
- package/dist/tools/remember.d.ts +83 -0
- package/dist/tools/remember.d.ts.map +1 -0
- package/dist/tools/remember.js +151 -0
- package/dist/tools/remember.js.map +1 -0
- package/dist/worker/brain-worker.d.ts +100 -0
- package/dist/worker/brain-worker.d.ts.map +1 -0
- package/dist/worker/brain-worker.js +283 -0
- package/dist/worker/brain-worker.js.map +1 -0
- package/dist/worker/link-discovery.d.ts +47 -0
- package/dist/worker/link-discovery.d.ts.map +1 -0
- package/dist/worker/link-discovery.js +103 -0
- package/dist/worker/link-discovery.js.map +1 -0
- package/dist/worker/predictive-consolidation.d.ts +46 -0
- package/dist/worker/predictive-consolidation.d.ts.map +1 -0
- package/dist/worker/predictive-consolidation.js +110 -0
- package/dist/worker/predictive-consolidation.js.map +1 -0
- package/dist/worker/types.d.ts +91 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +22 -0
- package/dist/worker/types.js.map +1 -0
- package/hooks/clawdbot/cortex-memory/HOOK.md +71 -0
- package/hooks/clawdbot/cortex-memory/handler.js +279 -0
- package/package.json +73 -0
- package/scripts/pre-compact-hook.mjs +716 -0
- package/scripts/session-end-hook.mjs +548 -0
- package/scripts/session-start-hook.mjs +221 -0
- package/scripts/start-dashboard.sh +41 -0
- package/scripts/stop-dashboard.sh +21 -0
- package/scripts/stop-hook.mjs +163 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Session-end hook for ShieldCortex - Automatic Memory Extraction on Exit
|
|
4
|
+
*
|
|
5
|
+
* This script runs when a Claude Code session ends and:
|
|
6
|
+
* 1. Reads the session transcript from the JSONL file
|
|
7
|
+
* 2. Analyzes conversation content for important information
|
|
8
|
+
* 3. Auto-extracts high-salience items (decisions, patterns, errors, etc.)
|
|
9
|
+
* 4. Saves them to the memory database automatically
|
|
10
|
+
*
|
|
11
|
+
* NOTE: SessionEnd doesn't always fire reliably (e.g. terminal killed, SSH drops).
|
|
12
|
+
* PreCompact remains the primary safety net for context preservation.
|
|
13
|
+
*
|
|
14
|
+
* Input (stdin JSON):
|
|
15
|
+
* {
|
|
16
|
+
* "session_id": "abc123",
|
|
17
|
+
* "transcript_path": "~/.claude/projects/.../abc.jsonl",
|
|
18
|
+
* "cwd": "/path/to/project",
|
|
19
|
+
* "hook_event_name": "SessionEnd",
|
|
20
|
+
* "reason": "exit" | "clear" | "logout" | "prompt_input_exit" | "other"
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import Database from 'better-sqlite3';
|
|
25
|
+
import { existsSync, mkdirSync, readFileSync } from 'fs';
|
|
26
|
+
import { join } from 'path';
|
|
27
|
+
import { homedir } from 'os';
|
|
28
|
+
|
|
29
|
+
// Database paths (with legacy fallback)
|
|
30
|
+
const NEW_DB_DIR = join(homedir(), '.shieldcortex');
|
|
31
|
+
const LEGACY_DB_DIR = join(homedir(), '.claude-cortex');
|
|
32
|
+
|
|
33
|
+
function getDbPath() {
|
|
34
|
+
const newPath = join(NEW_DB_DIR, 'memories.db');
|
|
35
|
+
const legacyPath = join(LEGACY_DB_DIR, 'memories.db');
|
|
36
|
+
if (existsSync(newPath) || !existsSync(legacyPath)) {
|
|
37
|
+
return { dir: NEW_DB_DIR, path: newPath };
|
|
38
|
+
}
|
|
39
|
+
return { dir: LEGACY_DB_DIR, path: legacyPath };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { dir: DB_DIR, path: DB_PATH } = getDbPath();
|
|
43
|
+
|
|
44
|
+
// Memory limits
|
|
45
|
+
const MAX_SHORT_TERM_MEMORIES = 100;
|
|
46
|
+
const MAX_LONG_TERM_MEMORIES = 1000;
|
|
47
|
+
const BASE_THRESHOLD = 0.35;
|
|
48
|
+
const MAX_AUTO_MEMORIES = 5;
|
|
49
|
+
|
|
50
|
+
const CATEGORY_EXTRACTION_THRESHOLDS = {
|
|
51
|
+
architecture: 0.28,
|
|
52
|
+
error: 0.30,
|
|
53
|
+
context: 0.32,
|
|
54
|
+
learning: 0.32,
|
|
55
|
+
pattern: 0.35,
|
|
56
|
+
preference: 0.38,
|
|
57
|
+
note: 0.42,
|
|
58
|
+
todo: 0.40,
|
|
59
|
+
relationship: 0.35,
|
|
60
|
+
custom: 0.35,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ==================== PROJECT DETECTION ====================
|
|
64
|
+
|
|
65
|
+
const SKIP_DIRECTORIES = [
|
|
66
|
+
'src', 'lib', 'dist', 'build', 'out',
|
|
67
|
+
'node_modules', '.git', '.next', '.cache',
|
|
68
|
+
'test', 'tests', '__tests__', 'spec',
|
|
69
|
+
'bin', 'scripts', 'config', 'public', 'static',
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
function extractProjectFromPath(path) {
|
|
73
|
+
if (!path) return null;
|
|
74
|
+
const segments = path.split(/[/\\]/).filter(Boolean);
|
|
75
|
+
if (segments.length === 0) return null;
|
|
76
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
77
|
+
const segment = segments[i];
|
|
78
|
+
if (!SKIP_DIRECTORIES.includes(segment.toLowerCase())) {
|
|
79
|
+
if (segment.startsWith('.')) continue;
|
|
80
|
+
return segment;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ==================== DYNAMIC THRESHOLD ====================
|
|
87
|
+
|
|
88
|
+
function getMemoryStats(db) {
|
|
89
|
+
try {
|
|
90
|
+
const stats = db.prepare(`
|
|
91
|
+
SELECT
|
|
92
|
+
COUNT(*) as total,
|
|
93
|
+
SUM(CASE WHEN type = 'short_term' THEN 1 ELSE 0 END) as shortTerm,
|
|
94
|
+
SUM(CASE WHEN type = 'long_term' THEN 1 ELSE 0 END) as longTerm
|
|
95
|
+
FROM memories
|
|
96
|
+
`).get();
|
|
97
|
+
return stats || { total: 0, shortTerm: 0, longTerm: 0 };
|
|
98
|
+
} catch {
|
|
99
|
+
return { total: 0, shortTerm: 0, longTerm: 0 };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getDynamicThreshold(memoryCount, maxMemories) {
|
|
104
|
+
const fullness = memoryCount / maxMemories;
|
|
105
|
+
if (fullness > 0.8) return 0.50;
|
|
106
|
+
if (fullness > 0.6) return 0.42;
|
|
107
|
+
if (fullness > 0.4) return 0.35;
|
|
108
|
+
if (fullness > 0.2) return 0.30;
|
|
109
|
+
return 0.25;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getExtractionThreshold(category, dynamicThreshold) {
|
|
113
|
+
const categoryThreshold = CATEGORY_EXTRACTION_THRESHOLDS[category] || BASE_THRESHOLD;
|
|
114
|
+
return Math.min(categoryThreshold, dynamicThreshold);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ==================== SALIENCE DETECTION ====================
|
|
118
|
+
|
|
119
|
+
const ARCHITECTURE_KEYWORDS = [
|
|
120
|
+
'architecture', 'design', 'pattern', 'structure', 'system',
|
|
121
|
+
'database', 'api', 'schema', 'model', 'framework', 'stack',
|
|
122
|
+
'microservice', 'monolith', 'serverless', 'infrastructure'
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
const ERROR_KEYWORDS = [
|
|
126
|
+
'error', 'bug', 'fix', 'issue', 'problem', 'crash', 'fail',
|
|
127
|
+
'exception', 'debug', 'resolve', 'solution', 'workaround'
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const PREFERENCE_KEYWORDS = [
|
|
131
|
+
'prefer', 'always', 'never', 'style', 'convention', 'standard',
|
|
132
|
+
'like', 'want', 'should', 'must', 'require'
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
const PATTERN_KEYWORDS = [
|
|
136
|
+
'pattern', 'practice', 'approach', 'method', 'technique',
|
|
137
|
+
'implementation', 'strategy', 'algorithm', 'workflow'
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const DECISION_KEYWORDS = [
|
|
141
|
+
'decided', 'decision', 'chose', 'chosen', 'selected', 'going with',
|
|
142
|
+
'will use', 'opted for', 'settled on', 'agreed'
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const LEARNING_KEYWORDS = [
|
|
146
|
+
'learned', 'discovered', 'realized', 'found out', 'turns out',
|
|
147
|
+
'TIL', 'now know', 'understand now', 'figured out'
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
const EMOTIONAL_MARKERS = [
|
|
151
|
+
'important', 'critical', 'crucial', 'essential', 'key',
|
|
152
|
+
'finally', 'breakthrough', 'eureka', 'aha', 'got it',
|
|
153
|
+
'frustrating', 'annoying', 'tricky', 'remember'
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const CODE_REFERENCE_PATTERNS = [
|
|
157
|
+
/\b[A-Z][a-zA-Z]*\.[a-zA-Z]+\b/,
|
|
158
|
+
/\b[a-z_][a-zA-Z0-9_]*\.(ts|js|py|go|rs)\b/,
|
|
159
|
+
/`[^`]+`/,
|
|
160
|
+
/\b(function|class|interface|type|const|let|var)\s+\w+/i,
|
|
161
|
+
/\bline\s*\d+\b/i,
|
|
162
|
+
/\b(src|lib|app|components?)\/\S+/,
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
function detectKeywords(text, keywords) {
|
|
166
|
+
const lower = text.toLowerCase();
|
|
167
|
+
return keywords.some(keyword => lower.includes(keyword.toLowerCase()));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function detectCodeReferences(content) {
|
|
171
|
+
return CODE_REFERENCE_PATTERNS.some(pattern => pattern.test(content));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function detectExplicitRequest(text) {
|
|
175
|
+
const patterns = [
|
|
176
|
+
/\bremember\s+(this|that)\b/i,
|
|
177
|
+
/\bdon'?t\s+forget\b/i,
|
|
178
|
+
/\bkeep\s+(in\s+)?mind\b/i,
|
|
179
|
+
/\bnote\s+(this|that)\b/i,
|
|
180
|
+
/\bsave\s+(this|that)\b/i,
|
|
181
|
+
/\bimportant[:\s]/i,
|
|
182
|
+
/\bfor\s+future\s+reference\b/i,
|
|
183
|
+
];
|
|
184
|
+
return patterns.some(pattern => pattern.test(text));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function calculateSalience(text) {
|
|
188
|
+
let score = 0.25;
|
|
189
|
+
if (detectExplicitRequest(text)) score += 0.5;
|
|
190
|
+
if (detectKeywords(text, ARCHITECTURE_KEYWORDS)) score += 0.4;
|
|
191
|
+
if (detectKeywords(text, ERROR_KEYWORDS)) score += 0.35;
|
|
192
|
+
if (detectKeywords(text, DECISION_KEYWORDS)) score += 0.35;
|
|
193
|
+
if (detectKeywords(text, LEARNING_KEYWORDS)) score += 0.3;
|
|
194
|
+
if (detectKeywords(text, PATTERN_KEYWORDS)) score += 0.25;
|
|
195
|
+
if (detectKeywords(text, PREFERENCE_KEYWORDS)) score += 0.25;
|
|
196
|
+
if (detectCodeReferences(text)) score += 0.15;
|
|
197
|
+
if (detectKeywords(text, EMOTIONAL_MARKERS)) score += 0.2;
|
|
198
|
+
return Math.min(1.0, score);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function suggestCategory(text) {
|
|
202
|
+
const lower = text.toLowerCase();
|
|
203
|
+
if (detectKeywords(lower, ARCHITECTURE_KEYWORDS)) return 'architecture';
|
|
204
|
+
if (detectKeywords(lower, ERROR_KEYWORDS)) return 'error';
|
|
205
|
+
if (detectKeywords(lower, DECISION_KEYWORDS)) return 'context';
|
|
206
|
+
if (detectKeywords(lower, LEARNING_KEYWORDS)) return 'learning';
|
|
207
|
+
if (detectKeywords(lower, PREFERENCE_KEYWORDS)) return 'preference';
|
|
208
|
+
if (detectKeywords(lower, PATTERN_KEYWORDS)) return 'pattern';
|
|
209
|
+
if (/\b(todo|fixme|hack|xxx)\b/i.test(lower)) return 'todo';
|
|
210
|
+
return 'note';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function extractTags(text, extractorName = null) {
|
|
214
|
+
const tags = new Set();
|
|
215
|
+
const hashtagMatches = text.match(/#[a-zA-Z][a-zA-Z0-9_-]*/g);
|
|
216
|
+
if (hashtagMatches) {
|
|
217
|
+
hashtagMatches.forEach(tag => tags.add(tag.slice(1).toLowerCase()));
|
|
218
|
+
}
|
|
219
|
+
const techTerms = [
|
|
220
|
+
'react', 'vue', 'angular', 'node', 'python', 'typescript', 'javascript',
|
|
221
|
+
'api', 'database', 'sql', 'mongodb', 'postgresql', 'mysql',
|
|
222
|
+
'docker', 'kubernetes', 'aws', 'git', 'testing', 'auth', 'security'
|
|
223
|
+
];
|
|
224
|
+
const lowerText = text.toLowerCase();
|
|
225
|
+
techTerms.forEach(term => {
|
|
226
|
+
if (lowerText.includes(term)) tags.add(term);
|
|
227
|
+
});
|
|
228
|
+
tags.add('auto-extracted');
|
|
229
|
+
tags.add('session-end');
|
|
230
|
+
if (extractorName) {
|
|
231
|
+
tags.add(`source:${extractorName}`);
|
|
232
|
+
}
|
|
233
|
+
return Array.from(tags).slice(0, 12);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function calculateFrequencyBoost(segment, allSegments) {
|
|
237
|
+
const commonWords = new Set([
|
|
238
|
+
'about', 'after', 'before', 'being', 'between', 'could', 'during',
|
|
239
|
+
'every', 'found', 'through', 'would', 'should', 'which', 'where',
|
|
240
|
+
'there', 'these', 'their', 'other', 'using', 'because', 'without'
|
|
241
|
+
]);
|
|
242
|
+
const words = segment.content.toLowerCase().split(/\s+/);
|
|
243
|
+
const keyTerms = words.filter(w =>
|
|
244
|
+
w.length > 5 && !commonWords.has(w) && /^[a-z]+$/.test(w)
|
|
245
|
+
);
|
|
246
|
+
let boost = 0;
|
|
247
|
+
const seenTerms = new Set();
|
|
248
|
+
for (const term of keyTerms) {
|
|
249
|
+
if (seenTerms.has(term)) continue;
|
|
250
|
+
seenTerms.add(term);
|
|
251
|
+
const mentions = allSegments.filter(s =>
|
|
252
|
+
s !== segment && s.content.toLowerCase().includes(term)
|
|
253
|
+
).length;
|
|
254
|
+
if (mentions > 1) {
|
|
255
|
+
boost += 0.03 * Math.min(mentions, 5);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return Math.min(0.15, boost);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ==================== CONTENT EXTRACTION ====================
|
|
262
|
+
|
|
263
|
+
function extractMemorableSegments(conversationText) {
|
|
264
|
+
const segments = [];
|
|
265
|
+
const extractors = [
|
|
266
|
+
{
|
|
267
|
+
name: 'decision',
|
|
268
|
+
patterns: [
|
|
269
|
+
/(?:we\s+)?decided\s+(?:to\s+)?(.{15,200})/gi,
|
|
270
|
+
/(?:going|went)\s+with\s+(.{15,150})/gi,
|
|
271
|
+
/(?:chose|chosen|selected)\s+(.{15,150})/gi,
|
|
272
|
+
/the\s+(?:approach|solution|fix)\s+(?:is|was)\s+(.{15,200})/gi,
|
|
273
|
+
/(?:using|will\s+use)\s+(.{15,150})/gi,
|
|
274
|
+
/(?:opted\s+for|settled\s+on)\s+(.{15,150})/gi,
|
|
275
|
+
],
|
|
276
|
+
titlePrefix: 'Decision: ',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'error-fix',
|
|
280
|
+
patterns: [
|
|
281
|
+
/(?:fixed|solved|resolved)\s+(?:by\s+)?(.{15,200})/gi,
|
|
282
|
+
/the\s+(?:fix|solution|workaround)\s+(?:is|was)\s+(.{15,200})/gi,
|
|
283
|
+
/(?:root\s+cause|issue)\s+(?:is|was)\s+(.{15,200})/gi,
|
|
284
|
+
/(?:error|bug)\s+(?:was\s+)?caused\s+by\s+(.{15,200})/gi,
|
|
285
|
+
/(?:problem|issue)\s+was\s+(.{15,150})/gi,
|
|
286
|
+
/(?:the\s+)?bug\s+(?:is|was)\s+(.{15,150})/gi,
|
|
287
|
+
/(?:debugging|debugged)\s+(.{15,150})/gi,
|
|
288
|
+
],
|
|
289
|
+
titlePrefix: 'Fix: ',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'learning',
|
|
293
|
+
patterns: [
|
|
294
|
+
/(?:learned|discovered|realized|found\s+out)\s+(?:that\s+)?(.{15,200})/gi,
|
|
295
|
+
/turns\s+out\s+(?:that\s+)?(.{15,200})/gi,
|
|
296
|
+
/(?:TIL|today\s+I\s+learned)[:\s]+(.{15,200})/gi,
|
|
297
|
+
/(?:now\s+)?(?:understand|know)\s+(?:that\s+)?(.{15,150})/gi,
|
|
298
|
+
/(?:figured\s+out|worked\s+out)\s+(.{15,150})/gi,
|
|
299
|
+
],
|
|
300
|
+
titlePrefix: 'Learned: ',
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'architecture',
|
|
304
|
+
patterns: [
|
|
305
|
+
/the\s+architecture\s+(?:is|uses|consists\s+of)\s+(.{15,200})/gi,
|
|
306
|
+
/(?:design|pattern)\s+(?:is|uses)\s+(.{15,200})/gi,
|
|
307
|
+
/(?:system|api|database)\s+(?:structure|design)\s+(?:is|uses)\s+(.{15,200})/gi,
|
|
308
|
+
/(?:created|added|implemented|built)\s+(?:a\s+)?(.{15,200})/gi,
|
|
309
|
+
/(?:refactored|updated|changed)\s+(?:the\s+)?(.{15,150})/gi,
|
|
310
|
+
],
|
|
311
|
+
titlePrefix: 'Architecture: ',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: 'preference',
|
|
315
|
+
patterns: [
|
|
316
|
+
/(?:always|never)\s+(.{10,150})/gi,
|
|
317
|
+
/(?:prefer|want)\s+to\s+(.{10,150})/gi,
|
|
318
|
+
/(?:should|must)\s+(?:always\s+)?(.{10,150})/gi,
|
|
319
|
+
],
|
|
320
|
+
titlePrefix: 'Preference: ',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'important-note',
|
|
324
|
+
patterns: [
|
|
325
|
+
/important[:\s]+(.{15,200})/gi,
|
|
326
|
+
/(?:note|remember)[:\s]+(.{15,200})/gi,
|
|
327
|
+
/(?:key|critical)\s+(?:point|thing)[:\s]+(.{15,200})/gi,
|
|
328
|
+
/(?:this\s+is\s+)?(?:crucial|essential)[:\s]+(.{15,150})/gi,
|
|
329
|
+
/(?:don't\s+forget|keep\s+in\s+mind)[:\s]+(.{15,150})/gi,
|
|
330
|
+
],
|
|
331
|
+
titlePrefix: 'Note: ',
|
|
332
|
+
},
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
for (const extractor of extractors) {
|
|
336
|
+
for (const pattern of extractor.patterns) {
|
|
337
|
+
let match;
|
|
338
|
+
while ((match = pattern.exec(conversationText)) !== null) {
|
|
339
|
+
const content = match[1].trim();
|
|
340
|
+
if (content.length >= 20) {
|
|
341
|
+
const titleContent = content.slice(0, 50).replace(/\s+/g, ' ').trim();
|
|
342
|
+
const title = extractor.titlePrefix + (titleContent.length < 50 ? titleContent : titleContent + '...');
|
|
343
|
+
segments.push({
|
|
344
|
+
title,
|
|
345
|
+
content: content.slice(0, 500),
|
|
346
|
+
extractorType: extractor.name,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return segments;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function processSegments(segments, dynamicThreshold = BASE_THRESHOLD) {
|
|
357
|
+
const unique = [];
|
|
358
|
+
for (const seg of segments) {
|
|
359
|
+
const isDupe = unique.some(existing => {
|
|
360
|
+
const overlap = calculateOverlap(existing.content, seg.content);
|
|
361
|
+
return overlap > 0.8;
|
|
362
|
+
});
|
|
363
|
+
if (!isDupe) {
|
|
364
|
+
const text = seg.title + ' ' + seg.content;
|
|
365
|
+
const baseSalience = calculateSalience(text);
|
|
366
|
+
const category = suggestCategory(text);
|
|
367
|
+
unique.push({
|
|
368
|
+
...seg,
|
|
369
|
+
baseSalience,
|
|
370
|
+
category,
|
|
371
|
+
tags: extractTags(text, seg.extractorType),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
for (const seg of unique) {
|
|
377
|
+
const frequencyBoost = calculateFrequencyBoost(seg, unique);
|
|
378
|
+
seg.salience = Math.min(1.0, seg.baseSalience + frequencyBoost);
|
|
379
|
+
seg.frequencyBoost = frequencyBoost;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
unique.sort((a, b) => b.salience - a.salience);
|
|
383
|
+
|
|
384
|
+
const filtered = unique.filter(seg => {
|
|
385
|
+
const threshold = getExtractionThreshold(seg.category, dynamicThreshold);
|
|
386
|
+
return seg.salience >= threshold;
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
return filtered.slice(0, MAX_AUTO_MEMORIES);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function calculateOverlap(text1, text2) {
|
|
393
|
+
const words1 = new Set(text1.toLowerCase().split(/\s+/));
|
|
394
|
+
const words2 = new Set(text2.toLowerCase().split(/\s+/));
|
|
395
|
+
const intersection = new Set([...words1].filter(w => words2.has(w)));
|
|
396
|
+
const union = new Set([...words1, ...words2]);
|
|
397
|
+
return intersection.size / union.size;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ==================== DATABASE OPERATIONS ====================
|
|
401
|
+
|
|
402
|
+
function saveMemory(db, memory, project) {
|
|
403
|
+
const timestamp = new Date().toISOString();
|
|
404
|
+
const stmt = db.prepare(`
|
|
405
|
+
INSERT INTO memories (title, content, type, category, salience, tags, project, created_at, last_accessed)
|
|
406
|
+
VALUES (?, ?, 'short_term', ?, ?, ?, ?, ?, ?)
|
|
407
|
+
`);
|
|
408
|
+
stmt.run(
|
|
409
|
+
memory.title,
|
|
410
|
+
memory.content,
|
|
411
|
+
memory.category,
|
|
412
|
+
memory.salience,
|
|
413
|
+
JSON.stringify(memory.tags),
|
|
414
|
+
project || null,
|
|
415
|
+
timestamp,
|
|
416
|
+
timestamp
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ==================== TRANSCRIPT READING ====================
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Read conversation text from the session transcript JSONL file.
|
|
424
|
+
*/
|
|
425
|
+
function readTranscript(transcriptPath) {
|
|
426
|
+
if (!transcriptPath) return '';
|
|
427
|
+
|
|
428
|
+
// Expand ~ to homedir
|
|
429
|
+
const resolvedPath = transcriptPath.replace(/^~/, homedir());
|
|
430
|
+
|
|
431
|
+
if (!existsSync(resolvedPath)) {
|
|
432
|
+
console.error(`[session-end] Transcript not found: ${resolvedPath}`);
|
|
433
|
+
return '';
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
const content = readFileSync(resolvedPath, 'utf-8');
|
|
438
|
+
const lines = content.trim().split('\n');
|
|
439
|
+
|
|
440
|
+
// Read last 50 lines to get recent conversation
|
|
441
|
+
const recentLines = lines.slice(-50);
|
|
442
|
+
const messages = [];
|
|
443
|
+
|
|
444
|
+
for (const line of recentLines) {
|
|
445
|
+
try {
|
|
446
|
+
const entry = JSON.parse(line);
|
|
447
|
+
const role = entry.type || entry.message?.role;
|
|
448
|
+
const msgContent = entry.message?.content;
|
|
449
|
+
if ((role === 'user' || role === 'assistant') && msgContent) {
|
|
450
|
+
const text = Array.isArray(msgContent)
|
|
451
|
+
? msgContent.filter(c => c.type === 'text').map(c => c.text).join('\n')
|
|
452
|
+
: msgContent;
|
|
453
|
+
if (text && !text.startsWith('/')) {
|
|
454
|
+
messages.push(text);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
} catch {
|
|
458
|
+
// Skip invalid lines
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const result = messages.join('\n\n');
|
|
463
|
+
console.error(`[session-end] Read ${messages.length} messages from transcript (${result.length} chars)`);
|
|
464
|
+
return result;
|
|
465
|
+
} catch (err) {
|
|
466
|
+
console.error(`[session-end] Failed to read transcript: ${err.message}`);
|
|
467
|
+
return '';
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ==================== MAIN HOOK LOGIC ====================
|
|
472
|
+
|
|
473
|
+
let input = '';
|
|
474
|
+
process.stdin.setEncoding('utf8');
|
|
475
|
+
|
|
476
|
+
process.stdin.on('readable', () => {
|
|
477
|
+
let chunk;
|
|
478
|
+
while ((chunk = process.stdin.read()) !== null) {
|
|
479
|
+
input += chunk;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
process.stdin.on('end', () => {
|
|
484
|
+
try {
|
|
485
|
+
const hookData = JSON.parse(input || '{}');
|
|
486
|
+
|
|
487
|
+
const reason = hookData.reason || 'unknown';
|
|
488
|
+
const project = extractProjectFromPath(hookData.cwd);
|
|
489
|
+
|
|
490
|
+
// Skip extraction on /clear — session is being intentionally wiped
|
|
491
|
+
if (reason === 'clear') {
|
|
492
|
+
console.error('[session-end] Session cleared, skipping extraction');
|
|
493
|
+
process.exit(0);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Read conversation from transcript_path (provided by Claude Code)
|
|
497
|
+
const conversationText = readTranscript(hookData.transcript_path);
|
|
498
|
+
|
|
499
|
+
if (!conversationText || conversationText.length < 100) {
|
|
500
|
+
console.error('[session-end] Not enough conversation content to extract from');
|
|
501
|
+
process.exit(0);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Ensure database directory exists
|
|
505
|
+
if (!existsSync(DB_DIR)) {
|
|
506
|
+
mkdirSync(DB_DIR, { recursive: true });
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!existsSync(DB_PATH)) {
|
|
510
|
+
console.error('[session-end] Memory database not found, skipping extraction');
|
|
511
|
+
process.exit(0);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const db = new Database(DB_PATH, { timeout: 5000 });
|
|
515
|
+
|
|
516
|
+
const stats = getMemoryStats(db);
|
|
517
|
+
const totalMemories = stats.shortTerm + stats.longTerm;
|
|
518
|
+
const maxMemories = MAX_SHORT_TERM_MEMORIES + MAX_LONG_TERM_MEMORIES;
|
|
519
|
+
const dynamicThreshold = getDynamicThreshold(totalMemories, maxMemories);
|
|
520
|
+
|
|
521
|
+
console.error(`[session-end] Memory status: ${totalMemories}/${maxMemories} (${(totalMemories/maxMemories*100).toFixed(0)}% full)`);
|
|
522
|
+
console.error(`[session-end] Reason: ${reason}, Dynamic threshold: ${dynamicThreshold.toFixed(2)}`);
|
|
523
|
+
|
|
524
|
+
// Extract memorable segments
|
|
525
|
+
const segments = extractMemorableSegments(conversationText);
|
|
526
|
+
const processedSegments = processSegments(segments, dynamicThreshold);
|
|
527
|
+
|
|
528
|
+
let autoExtractedCount = 0;
|
|
529
|
+
for (const memory of processedSegments) {
|
|
530
|
+
try {
|
|
531
|
+
saveMemory(db, memory, project);
|
|
532
|
+
autoExtractedCount++;
|
|
533
|
+
const boostInfo = memory.frequencyBoost > 0 ? ` +${memory.frequencyBoost.toFixed(2)} boost` : '';
|
|
534
|
+
console.error(`[session-end] Saved: ${memory.title} (salience: ${memory.salience.toFixed(2)}${boostInfo}, category: ${memory.category})`);
|
|
535
|
+
} catch (err) {
|
|
536
|
+
console.error(`[session-end] Failed to save "${memory.title}": ${err.message}`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
console.error(`[session-end] Complete: ${autoExtractedCount} memories auto-extracted on session ${reason}`);
|
|
541
|
+
|
|
542
|
+
db.close();
|
|
543
|
+
process.exit(0);
|
|
544
|
+
} catch (error) {
|
|
545
|
+
console.error(`[session-end] Error: ${error.message}`);
|
|
546
|
+
process.exit(0); // Don't block session exit on errors
|
|
547
|
+
}
|
|
548
|
+
});
|