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.
Files changed (377) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +282 -0
  3. package/dashboard/components.json +22 -0
  4. package/dashboard/eslint.config.mjs +42 -0
  5. package/dashboard/next.config.ts +7 -0
  6. package/dashboard/package-lock.json +8053 -0
  7. package/dashboard/package.json +44 -0
  8. package/dashboard/postcss.config.mjs +7 -0
  9. package/dashboard/public/file.svg +1 -0
  10. package/dashboard/public/globe.svg +1 -0
  11. package/dashboard/public/next.svg +1 -0
  12. package/dashboard/public/vercel.svg +1 -0
  13. package/dashboard/public/window.svg +1 -0
  14. package/dashboard/scripts/ensure-api.mjs +76 -0
  15. package/dashboard/src/app/error.tsx +49 -0
  16. package/dashboard/src/app/favicon.ico +0 -0
  17. package/dashboard/src/app/globals.css +130 -0
  18. package/dashboard/src/app/layout.tsx +35 -0
  19. package/dashboard/src/app/page.tsx +364 -0
  20. package/dashboard/src/components/Providers.tsx +27 -0
  21. package/dashboard/src/components/brain/ActivityPulseSystem.tsx +229 -0
  22. package/dashboard/src/components/brain/BrainMesh.tsx +133 -0
  23. package/dashboard/src/components/brain/BrainRegions.tsx +254 -0
  24. package/dashboard/src/components/brain/BrainScene.tsx +255 -0
  25. package/dashboard/src/components/brain/CategoryLabels.tsx +103 -0
  26. package/dashboard/src/components/brain/CoreSphere.tsx +215 -0
  27. package/dashboard/src/components/brain/DataFlowParticles.tsx +123 -0
  28. package/dashboard/src/components/brain/DataStreamRings.tsx +161 -0
  29. package/dashboard/src/components/brain/ElectronFlow.tsx +323 -0
  30. package/dashboard/src/components/brain/HolographicGrid.tsx +235 -0
  31. package/dashboard/src/components/brain/MemoryLinks.tsx +271 -0
  32. package/dashboard/src/components/brain/MemoryNode.tsx +245 -0
  33. package/dashboard/src/components/brain/NeuralPathways.tsx +441 -0
  34. package/dashboard/src/components/brain/SynapseNodes.tsx +312 -0
  35. package/dashboard/src/components/brain/TimelineControls.tsx +205 -0
  36. package/dashboard/src/components/chip/ChipScene.tsx +497 -0
  37. package/dashboard/src/components/chip/ChipSubstrate.tsx +238 -0
  38. package/dashboard/src/components/chip/CortexCore.tsx +210 -0
  39. package/dashboard/src/components/chip/DataBus.tsx +416 -0
  40. package/dashboard/src/components/chip/MemoryCell.tsx +225 -0
  41. package/dashboard/src/components/chip/MemoryGrid.tsx +328 -0
  42. package/dashboard/src/components/chip/QuantumCell.tsx +316 -0
  43. package/dashboard/src/components/chip/SectionLabel.tsx +113 -0
  44. package/dashboard/src/components/chip/index.ts +14 -0
  45. package/dashboard/src/components/controls/ControlPanel.tsx +106 -0
  46. package/dashboard/src/components/controls/VersionPanel.tsx +185 -0
  47. package/dashboard/src/components/dashboard/StatsPanel.tsx +164 -0
  48. package/dashboard/src/components/debug/ActivityLog.tsx +250 -0
  49. package/dashboard/src/components/debug/DebugPanel.tsx +101 -0
  50. package/dashboard/src/components/debug/QueryTester.tsx +192 -0
  51. package/dashboard/src/components/debug/RelationshipGraph.tsx +403 -0
  52. package/dashboard/src/components/debug/SqlConsole.tsx +319 -0
  53. package/dashboard/src/components/graph/KnowledgeGraph.tsx +230 -0
  54. package/dashboard/src/components/graph/OntologyGraph.tsx +631 -0
  55. package/dashboard/src/components/insights/ActivityHeatmap.tsx +131 -0
  56. package/dashboard/src/components/insights/InsightsView.tsx +46 -0
  57. package/dashboard/src/components/insights/KnowledgeMapPanel.tsx +80 -0
  58. package/dashboard/src/components/insights/QualityPanel.tsx +116 -0
  59. package/dashboard/src/components/memories/MemoriesView.tsx +150 -0
  60. package/dashboard/src/components/memories/MemoryCard.tsx +103 -0
  61. package/dashboard/src/components/memory/MemoryDetail.tsx +325 -0
  62. package/dashboard/src/components/nav/NavRail.tsx +54 -0
  63. package/dashboard/src/components/ui/button.tsx +62 -0
  64. package/dashboard/src/components/ui/card.tsx +92 -0
  65. package/dashboard/src/components/ui/input.tsx +21 -0
  66. package/dashboard/src/hooks/useDebouncedValue.ts +24 -0
  67. package/dashboard/src/hooks/useMemories.ts +458 -0
  68. package/dashboard/src/hooks/useSuggestions.ts +46 -0
  69. package/dashboard/src/lib/category-colors.ts +84 -0
  70. package/dashboard/src/lib/position-algorithm.ts +177 -0
  71. package/dashboard/src/lib/simplex-noise.ts +217 -0
  72. package/dashboard/src/lib/store.ts +88 -0
  73. package/dashboard/src/lib/utils.ts +6 -0
  74. package/dashboard/src/lib/websocket.ts +249 -0
  75. package/dashboard/src/types/memory.ts +73 -0
  76. package/dashboard/tsconfig.json +34 -0
  77. package/dist/__tests__/consolidation-merge.test.d.ts +9 -0
  78. package/dist/__tests__/consolidation-merge.test.d.ts.map +1 -0
  79. package/dist/__tests__/consolidation-merge.test.js +137 -0
  80. package/dist/__tests__/consolidation-merge.test.js.map +1 -0
  81. package/dist/__tests__/contradictions.test.d.ts +8 -0
  82. package/dist/__tests__/contradictions.test.d.ts.map +1 -0
  83. package/dist/__tests__/contradictions.test.js +78 -0
  84. package/dist/__tests__/contradictions.test.js.map +1 -0
  85. package/dist/__tests__/salience-evolution.test.d.ts +7 -0
  86. package/dist/__tests__/salience-evolution.test.d.ts.map +1 -0
  87. package/dist/__tests__/salience-evolution.test.js +151 -0
  88. package/dist/__tests__/salience-evolution.test.js.map +1 -0
  89. package/dist/__tests__/store.test.d.ts +7 -0
  90. package/dist/__tests__/store.test.d.ts.map +1 -0
  91. package/dist/__tests__/store.test.js +582 -0
  92. package/dist/__tests__/store.test.js.map +1 -0
  93. package/dist/api/control.d.ts +27 -0
  94. package/dist/api/control.d.ts.map +1 -0
  95. package/dist/api/control.js +60 -0
  96. package/dist/api/control.js.map +1 -0
  97. package/dist/api/events.d.ts +159 -0
  98. package/dist/api/events.d.ts.map +1 -0
  99. package/dist/api/events.js +155 -0
  100. package/dist/api/events.js.map +1 -0
  101. package/dist/api/version.d.ts +36 -0
  102. package/dist/api/version.d.ts.map +1 -0
  103. package/dist/api/version.js +146 -0
  104. package/dist/api/version.js.map +1 -0
  105. package/dist/api/visualization-server.d.ts +11 -0
  106. package/dist/api/visualization-server.d.ts.map +1 -0
  107. package/dist/api/visualization-server.js +1186 -0
  108. package/dist/api/visualization-server.js.map +1 -0
  109. package/dist/context/project-context.d.ts +57 -0
  110. package/dist/context/project-context.d.ts.map +1 -0
  111. package/dist/context/project-context.js +135 -0
  112. package/dist/context/project-context.js.map +1 -0
  113. package/dist/database/init.d.ts +49 -0
  114. package/dist/database/init.d.ts.map +1 -0
  115. package/dist/database/init.js +567 -0
  116. package/dist/database/init.js.map +1 -0
  117. package/dist/defence/__tests__/firewall.test.d.ts +8 -0
  118. package/dist/defence/__tests__/firewall.test.d.ts.map +1 -0
  119. package/dist/defence/__tests__/firewall.test.js +123 -0
  120. package/dist/defence/__tests__/firewall.test.js.map +1 -0
  121. package/dist/defence/__tests__/fragmentation.test.d.ts +7 -0
  122. package/dist/defence/__tests__/fragmentation.test.d.ts.map +1 -0
  123. package/dist/defence/__tests__/fragmentation.test.js +51 -0
  124. package/dist/defence/__tests__/fragmentation.test.js.map +1 -0
  125. package/dist/defence/__tests__/pipeline.test.d.ts +8 -0
  126. package/dist/defence/__tests__/pipeline.test.d.ts.map +1 -0
  127. package/dist/defence/__tests__/pipeline.test.js +61 -0
  128. package/dist/defence/__tests__/pipeline.test.js.map +1 -0
  129. package/dist/defence/__tests__/sensitivity.test.d.ts +7 -0
  130. package/dist/defence/__tests__/sensitivity.test.d.ts.map +1 -0
  131. package/dist/defence/__tests__/sensitivity.test.js +61 -0
  132. package/dist/defence/__tests__/sensitivity.test.js.map +1 -0
  133. package/dist/defence/__tests__/trust.test.d.ts +7 -0
  134. package/dist/defence/__tests__/trust.test.d.ts.map +1 -0
  135. package/dist/defence/__tests__/trust.test.js +49 -0
  136. package/dist/defence/__tests__/trust.test.js.map +1 -0
  137. package/dist/defence/audit/index.d.ts +4 -0
  138. package/dist/defence/audit/index.d.ts.map +1 -0
  139. package/dist/defence/audit/index.js +3 -0
  140. package/dist/defence/audit/index.js.map +1 -0
  141. package/dist/defence/audit/logger.d.ts +14 -0
  142. package/dist/defence/audit/logger.d.ts.map +1 -0
  143. package/dist/defence/audit/logger.js +54 -0
  144. package/dist/defence/audit/logger.js.map +1 -0
  145. package/dist/defence/audit/queries.d.ts +33 -0
  146. package/dist/defence/audit/queries.d.ts.map +1 -0
  147. package/dist/defence/audit/queries.js +103 -0
  148. package/dist/defence/audit/queries.js.map +1 -0
  149. package/dist/defence/firewall/anomaly-scorer.d.ts +8 -0
  150. package/dist/defence/firewall/anomaly-scorer.d.ts.map +1 -0
  151. package/dist/defence/firewall/anomaly-scorer.js +58 -0
  152. package/dist/defence/firewall/anomaly-scorer.js.map +1 -0
  153. package/dist/defence/firewall/encoding-detector.d.ts +13 -0
  154. package/dist/defence/firewall/encoding-detector.d.ts.map +1 -0
  155. package/dist/defence/firewall/encoding-detector.js +120 -0
  156. package/dist/defence/firewall/encoding-detector.js.map +1 -0
  157. package/dist/defence/firewall/index.d.ts +21 -0
  158. package/dist/defence/firewall/index.d.ts.map +1 -0
  159. package/dist/defence/firewall/index.js +133 -0
  160. package/dist/defence/firewall/index.js.map +1 -0
  161. package/dist/defence/firewall/instruction-detector.d.ts +12 -0
  162. package/dist/defence/firewall/instruction-detector.d.ts.map +1 -0
  163. package/dist/defence/firewall/instruction-detector.js +99 -0
  164. package/dist/defence/firewall/instruction-detector.js.map +1 -0
  165. package/dist/defence/firewall/privilege-detector.d.ts +13 -0
  166. package/dist/defence/firewall/privilege-detector.d.ts.map +1 -0
  167. package/dist/defence/firewall/privilege-detector.js +89 -0
  168. package/dist/defence/firewall/privilege-detector.js.map +1 -0
  169. package/dist/defence/fragmentation/assembly-detector.d.ts +18 -0
  170. package/dist/defence/fragmentation/assembly-detector.d.ts.map +1 -0
  171. package/dist/defence/fragmentation/assembly-detector.js +72 -0
  172. package/dist/defence/fragmentation/assembly-detector.js.map +1 -0
  173. package/dist/defence/fragmentation/entity-extractor.d.ts +19 -0
  174. package/dist/defence/fragmentation/entity-extractor.d.ts.map +1 -0
  175. package/dist/defence/fragmentation/entity-extractor.js +86 -0
  176. package/dist/defence/fragmentation/entity-extractor.js.map +1 -0
  177. package/dist/defence/fragmentation/index.d.ts +23 -0
  178. package/dist/defence/fragmentation/index.d.ts.map +1 -0
  179. package/dist/defence/fragmentation/index.js +49 -0
  180. package/dist/defence/fragmentation/index.js.map +1 -0
  181. package/dist/defence/fragmentation/temporal-analyzer.d.ts +28 -0
  182. package/dist/defence/fragmentation/temporal-analyzer.d.ts.map +1 -0
  183. package/dist/defence/fragmentation/temporal-analyzer.js +41 -0
  184. package/dist/defence/fragmentation/temporal-analyzer.js.map +1 -0
  185. package/dist/defence/index.d.ts +12 -0
  186. package/dist/defence/index.d.ts.map +1 -0
  187. package/dist/defence/index.js +18 -0
  188. package/dist/defence/index.js.map +1 -0
  189. package/dist/defence/pipeline.d.ts +9 -0
  190. package/dist/defence/pipeline.d.ts.map +1 -0
  191. package/dist/defence/pipeline.js +115 -0
  192. package/dist/defence/pipeline.js.map +1 -0
  193. package/dist/defence/scanner/index.d.ts +5 -0
  194. package/dist/defence/scanner/index.d.ts.map +1 -0
  195. package/dist/defence/scanner/index.js +5 -0
  196. package/dist/defence/scanner/index.js.map +1 -0
  197. package/dist/defence/scanner/scan-existing.d.ts +34 -0
  198. package/dist/defence/scanner/scan-existing.d.ts.map +1 -0
  199. package/dist/defence/scanner/scan-existing.js +136 -0
  200. package/dist/defence/scanner/scan-existing.js.map +1 -0
  201. package/dist/defence/sensitivity/classifier.d.ts +6 -0
  202. package/dist/defence/sensitivity/classifier.d.ts.map +1 -0
  203. package/dist/defence/sensitivity/classifier.js +50 -0
  204. package/dist/defence/sensitivity/classifier.js.map +1 -0
  205. package/dist/defence/sensitivity/index.d.ts +11 -0
  206. package/dist/defence/sensitivity/index.d.ts.map +1 -0
  207. package/dist/defence/sensitivity/index.js +13 -0
  208. package/dist/defence/sensitivity/index.js.map +1 -0
  209. package/dist/defence/sensitivity/patterns.d.ts +14 -0
  210. package/dist/defence/sensitivity/patterns.d.ts.map +1 -0
  211. package/dist/defence/sensitivity/patterns.js +67 -0
  212. package/dist/defence/sensitivity/patterns.js.map +1 -0
  213. package/dist/defence/sensitivity/redaction.d.ts +17 -0
  214. package/dist/defence/sensitivity/redaction.d.ts.map +1 -0
  215. package/dist/defence/sensitivity/redaction.js +47 -0
  216. package/dist/defence/sensitivity/redaction.js.map +1 -0
  217. package/dist/defence/trust/index.d.ts +3 -0
  218. package/dist/defence/trust/index.d.ts.map +1 -0
  219. package/dist/defence/trust/index.js +3 -0
  220. package/dist/defence/trust/index.js.map +1 -0
  221. package/dist/defence/trust/recall-filter.d.ts +10 -0
  222. package/dist/defence/trust/recall-filter.d.ts.map +1 -0
  223. package/dist/defence/trust/recall-filter.js +38 -0
  224. package/dist/defence/trust/recall-filter.js.map +1 -0
  225. package/dist/defence/trust/source-scorer.d.ts +6 -0
  226. package/dist/defence/trust/source-scorer.d.ts.map +1 -0
  227. package/dist/defence/trust/source-scorer.js +34 -0
  228. package/dist/defence/trust/source-scorer.js.map +1 -0
  229. package/dist/defence/types.d.ts +88 -0
  230. package/dist/defence/types.d.ts.map +1 -0
  231. package/dist/defence/types.js +15 -0
  232. package/dist/defence/types.js.map +1 -0
  233. package/dist/embeddings/generator.d.ts +20 -0
  234. package/dist/embeddings/generator.d.ts.map +1 -0
  235. package/dist/embeddings/generator.js +83 -0
  236. package/dist/embeddings/generator.js.map +1 -0
  237. package/dist/embeddings/index.d.ts +2 -0
  238. package/dist/embeddings/index.d.ts.map +1 -0
  239. package/dist/embeddings/index.js +2 -0
  240. package/dist/embeddings/index.js.map +1 -0
  241. package/dist/errors.d.ts +74 -0
  242. package/dist/errors.d.ts.map +1 -0
  243. package/dist/errors.js +131 -0
  244. package/dist/errors.js.map +1 -0
  245. package/dist/graph/backfill.d.ts +6 -0
  246. package/dist/graph/backfill.d.ts.map +1 -0
  247. package/dist/graph/backfill.js +33 -0
  248. package/dist/graph/backfill.js.map +1 -0
  249. package/dist/graph/extract.d.ts +21 -0
  250. package/dist/graph/extract.d.ts.map +1 -0
  251. package/dist/graph/extract.js +231 -0
  252. package/dist/graph/extract.js.map +1 -0
  253. package/dist/graph/resolve.d.ts +6 -0
  254. package/dist/graph/resolve.d.ts.map +1 -0
  255. package/dist/graph/resolve.js +126 -0
  256. package/dist/graph/resolve.js.map +1 -0
  257. package/dist/index.d.ts +31 -0
  258. package/dist/index.d.ts.map +1 -0
  259. package/dist/index.js +248 -0
  260. package/dist/index.js.map +1 -0
  261. package/dist/memory/activation.d.ts +69 -0
  262. package/dist/memory/activation.d.ts.map +1 -0
  263. package/dist/memory/activation.js +168 -0
  264. package/dist/memory/activation.js.map +1 -0
  265. package/dist/memory/consolidate.d.ts +98 -0
  266. package/dist/memory/consolidate.d.ts.map +1 -0
  267. package/dist/memory/consolidate.js +511 -0
  268. package/dist/memory/consolidate.js.map +1 -0
  269. package/dist/memory/contradiction.d.ts +69 -0
  270. package/dist/memory/contradiction.d.ts.map +1 -0
  271. package/dist/memory/contradiction.js +286 -0
  272. package/dist/memory/contradiction.js.map +1 -0
  273. package/dist/memory/decay.d.ts +62 -0
  274. package/dist/memory/decay.d.ts.map +1 -0
  275. package/dist/memory/decay.js +184 -0
  276. package/dist/memory/decay.js.map +1 -0
  277. package/dist/memory/salience.d.ts +36 -0
  278. package/dist/memory/salience.d.ts.map +1 -0
  279. package/dist/memory/salience.js +216 -0
  280. package/dist/memory/salience.js.map +1 -0
  281. package/dist/memory/similarity.d.ts +57 -0
  282. package/dist/memory/similarity.d.ts.map +1 -0
  283. package/dist/memory/similarity.js +114 -0
  284. package/dist/memory/similarity.js.map +1 -0
  285. package/dist/memory/store.d.ts +179 -0
  286. package/dist/memory/store.d.ts.map +1 -0
  287. package/dist/memory/store.js +1184 -0
  288. package/dist/memory/store.js.map +1 -0
  289. package/dist/memory/types.d.ts +97 -0
  290. package/dist/memory/types.d.ts.map +1 -0
  291. package/dist/memory/types.js +30 -0
  292. package/dist/memory/types.js.map +1 -0
  293. package/dist/server.d.ts +12 -0
  294. package/dist/server.d.ts.map +1 -0
  295. package/dist/server.js +568 -0
  296. package/dist/server.js.map +1 -0
  297. package/dist/service/install.d.ts +15 -0
  298. package/dist/service/install.d.ts.map +1 -0
  299. package/dist/service/install.js +178 -0
  300. package/dist/service/install.js.map +1 -0
  301. package/dist/service/templates.d.ts +13 -0
  302. package/dist/service/templates.d.ts.map +1 -0
  303. package/dist/service/templates.js +58 -0
  304. package/dist/service/templates.js.map +1 -0
  305. package/dist/setup/claude-md.d.ts +12 -0
  306. package/dist/setup/claude-md.d.ts.map +1 -0
  307. package/dist/setup/claude-md.js +68 -0
  308. package/dist/setup/claude-md.js.map +1 -0
  309. package/dist/setup/clawdbot.d.ts +15 -0
  310. package/dist/setup/clawdbot.d.ts.map +1 -0
  311. package/dist/setup/clawdbot.js +118 -0
  312. package/dist/setup/clawdbot.js.map +1 -0
  313. package/dist/setup/doctor.d.ts +5 -0
  314. package/dist/setup/doctor.d.ts.map +1 -0
  315. package/dist/setup/doctor.js +141 -0
  316. package/dist/setup/doctor.js.map +1 -0
  317. package/dist/setup/hooks.d.ts +6 -0
  318. package/dist/setup/hooks.d.ts.map +1 -0
  319. package/dist/setup/hooks.js +36 -0
  320. package/dist/setup/hooks.js.map +1 -0
  321. package/dist/setup/migrate.d.ts +16 -0
  322. package/dist/setup/migrate.d.ts.map +1 -0
  323. package/dist/setup/migrate.js +164 -0
  324. package/dist/setup/migrate.js.map +1 -0
  325. package/dist/setup/settings-hooks.d.ts +7 -0
  326. package/dist/setup/settings-hooks.d.ts.map +1 -0
  327. package/dist/setup/settings-hooks.js +83 -0
  328. package/dist/setup/settings-hooks.js.map +1 -0
  329. package/dist/setup/uninstall.d.ts +12 -0
  330. package/dist/setup/uninstall.d.ts.map +1 -0
  331. package/dist/setup/uninstall.js +125 -0
  332. package/dist/setup/uninstall.js.map +1 -0
  333. package/dist/tools/context.d.ts +135 -0
  334. package/dist/tools/context.d.ts.map +1 -0
  335. package/dist/tools/context.js +273 -0
  336. package/dist/tools/context.js.map +1 -0
  337. package/dist/tools/forget.d.ts +53 -0
  338. package/dist/tools/forget.d.ts.map +1 -0
  339. package/dist/tools/forget.js +179 -0
  340. package/dist/tools/forget.js.map +1 -0
  341. package/dist/tools/graph.d.ts +46 -0
  342. package/dist/tools/graph.d.ts.map +1 -0
  343. package/dist/tools/graph.js +206 -0
  344. package/dist/tools/graph.js.map +1 -0
  345. package/dist/tools/recall.d.ts +79 -0
  346. package/dist/tools/recall.d.ts.map +1 -0
  347. package/dist/tools/recall.js +156 -0
  348. package/dist/tools/recall.js.map +1 -0
  349. package/dist/tools/remember.d.ts +83 -0
  350. package/dist/tools/remember.d.ts.map +1 -0
  351. package/dist/tools/remember.js +151 -0
  352. package/dist/tools/remember.js.map +1 -0
  353. package/dist/worker/brain-worker.d.ts +100 -0
  354. package/dist/worker/brain-worker.d.ts.map +1 -0
  355. package/dist/worker/brain-worker.js +283 -0
  356. package/dist/worker/brain-worker.js.map +1 -0
  357. package/dist/worker/link-discovery.d.ts +47 -0
  358. package/dist/worker/link-discovery.d.ts.map +1 -0
  359. package/dist/worker/link-discovery.js +103 -0
  360. package/dist/worker/link-discovery.js.map +1 -0
  361. package/dist/worker/predictive-consolidation.d.ts +46 -0
  362. package/dist/worker/predictive-consolidation.d.ts.map +1 -0
  363. package/dist/worker/predictive-consolidation.js +110 -0
  364. package/dist/worker/predictive-consolidation.js.map +1 -0
  365. package/dist/worker/types.d.ts +91 -0
  366. package/dist/worker/types.d.ts.map +1 -0
  367. package/dist/worker/types.js +22 -0
  368. package/dist/worker/types.js.map +1 -0
  369. package/hooks/clawdbot/cortex-memory/HOOK.md +71 -0
  370. package/hooks/clawdbot/cortex-memory/handler.js +279 -0
  371. package/package.json +73 -0
  372. package/scripts/pre-compact-hook.mjs +716 -0
  373. package/scripts/session-end-hook.mjs +548 -0
  374. package/scripts/session-start-hook.mjs +221 -0
  375. package/scripts/start-dashboard.sh +41 -0
  376. package/scripts/stop-dashboard.sh +21 -0
  377. package/scripts/stop-hook.mjs +163 -0
@@ -0,0 +1,131 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+
5
+ export interface ActivityDay {
6
+ date: string;
7
+ count: number;
8
+ }
9
+
10
+ interface ActivityHeatmapProps {
11
+ activity: ActivityDay[];
12
+ }
13
+
14
+ const CELL_SIZE = 12;
15
+ const CELL_GAP = 2;
16
+ const CELL_RADIUS = 2;
17
+ const WEEKS = 52;
18
+ const DAYS = 7;
19
+ const LEFT_LABEL_WIDTH = 30;
20
+ const TOP_LABEL_HEIGHT = 16;
21
+
22
+ const DAY_LABELS = ['', 'Mon', '', 'Wed', '', 'Fri', ''];
23
+ const MONTH_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
24
+
25
+ function getColor(count: number, max: number): string {
26
+ if (count === 0) return '#0f172a'; // slate-900 (darker empty)
27
+ const ratio = count / Math.max(max, 1);
28
+ if (ratio < 0.25) return '#155e75'; // cyan-800
29
+ if (ratio < 0.50) return '#0891b2'; // cyan-600
30
+ if (ratio < 0.75) return '#06b6d4'; // cyan-500
31
+ return '#22d3ee'; // cyan-400
32
+ }
33
+
34
+ export function ActivityHeatmap({ activity }: ActivityHeatmapProps) {
35
+ const { grid, monthPositions, maxCount } = useMemo(() => {
36
+ // Build lookup map
37
+ const lookup = new Map<string, number>();
38
+ for (const d of activity) {
39
+ lookup.set(d.date, d.count);
40
+ }
41
+
42
+ // Today at start of day
43
+ const today = new Date();
44
+ today.setHours(0, 0, 0, 0);
45
+
46
+ // Find the Saturday of the current week (end of last column)
47
+ const todayDay = today.getDay(); // 0=Sun
48
+ const endDate = new Date(today);
49
+ endDate.setDate(today.getDate() + (6 - todayDay));
50
+
51
+ // Start date is 52 weeks before endDate's Sunday
52
+ const startDate = new Date(endDate);
53
+ startDate.setDate(endDate.getDate() - (WEEKS * 7 - 1));
54
+
55
+ const cells: Array<{ week: number; day: number; date: string; count: number }> = [];
56
+ const months: Array<{ label: string; week: number }> = [];
57
+ let lastMonth = -1;
58
+
59
+ const cursor = new Date(startDate);
60
+ for (let w = 0; w < WEEKS; w++) {
61
+ for (let d = 0; d < DAYS; d++) {
62
+ const dateStr = cursor.toISOString().slice(0, 10);
63
+ const count = lookup.get(dateStr) || 0;
64
+ cells.push({ week: w, day: d, date: dateStr, count });
65
+
66
+ const month = cursor.getMonth();
67
+ if (month !== lastMonth && d === 0) {
68
+ months.push({ label: MONTH_LABELS[month], week: w });
69
+ lastMonth = month;
70
+ }
71
+
72
+ cursor.setDate(cursor.getDate() + 1);
73
+ }
74
+ }
75
+
76
+ const max = Math.max(...cells.map(c => c.count), 1);
77
+ return { grid: cells, monthPositions: months, maxCount: max };
78
+ }, [activity]);
79
+
80
+ const svgWidth = LEFT_LABEL_WIDTH + WEEKS * (CELL_SIZE + CELL_GAP);
81
+ const svgHeight = TOP_LABEL_HEIGHT + DAYS * (CELL_SIZE + CELL_GAP);
82
+
83
+ return (
84
+ <div className="bg-slate-900/50 rounded-lg p-4 overflow-x-auto">
85
+ <svg width={svgWidth} height={svgHeight} className="text-slate-400">
86
+ {/* Month labels */}
87
+ {monthPositions.map((m, i) => (
88
+ <text
89
+ key={i}
90
+ x={LEFT_LABEL_WIDTH + m.week * (CELL_SIZE + CELL_GAP)}
91
+ y={12}
92
+ fontSize={10}
93
+ fill="currentColor"
94
+ >
95
+ {m.label}
96
+ </text>
97
+ ))}
98
+
99
+ {/* Day labels */}
100
+ {DAY_LABELS.map((label, i) =>
101
+ label ? (
102
+ <text
103
+ key={i}
104
+ x={0}
105
+ y={TOP_LABEL_HEIGHT + i * (CELL_SIZE + CELL_GAP) + CELL_SIZE - 1}
106
+ fontSize={10}
107
+ fill="currentColor"
108
+ >
109
+ {label}
110
+ </text>
111
+ ) : null
112
+ )}
113
+
114
+ {/* Cells */}
115
+ {grid.map((cell, i) => (
116
+ <rect
117
+ key={i}
118
+ x={LEFT_LABEL_WIDTH + cell.week * (CELL_SIZE + CELL_GAP)}
119
+ y={TOP_LABEL_HEIGHT + cell.day * (CELL_SIZE + CELL_GAP)}
120
+ width={CELL_SIZE}
121
+ height={CELL_SIZE}
122
+ rx={CELL_RADIUS}
123
+ fill={getColor(cell.count, maxCount)}
124
+ >
125
+ <title>{`${cell.date}: ${cell.count} memories`}</title>
126
+ </rect>
127
+ ))}
128
+ </svg>
129
+ </div>
130
+ );
131
+ }
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { useActivity } from '@/hooks/useMemories';
4
+ import { useDashboardStore } from '@/lib/store';
5
+ import { ActivityHeatmap } from './ActivityHeatmap';
6
+ import { KnowledgeMapPanel } from './KnowledgeMapPanel';
7
+ import { QualityPanel } from './QualityPanel';
8
+ import type { MemoryStats } from '@/types/memory';
9
+
10
+ interface InsightsViewProps {
11
+ selectedProject?: string;
12
+ stats?: MemoryStats;
13
+ }
14
+
15
+ export function InsightsView({ selectedProject, stats }: InsightsViewProps) {
16
+ const { data: activityData } = useActivity(selectedProject);
17
+ const { setViewMode, setCategoryFilter } = useDashboardStore();
18
+
19
+ const handleNavigate = (filter: { category?: string }) => {
20
+ if (filter.category) {
21
+ setCategoryFilter(filter.category);
22
+ }
23
+ setViewMode('memories');
24
+ };
25
+
26
+ return (
27
+ <div className="h-full overflow-y-auto p-6 space-y-8">
28
+ <section>
29
+ <h2 className="text-lg font-semibold text-white mb-4">Activity</h2>
30
+ <ActivityHeatmap activity={activityData?.activity ?? []} />
31
+ </section>
32
+
33
+ {stats && (
34
+ <section>
35
+ <h2 className="text-lg font-semibold text-white mb-4">Knowledge Coverage</h2>
36
+ <KnowledgeMapPanel stats={stats} onNavigate={handleNavigate} />
37
+ </section>
38
+ )}
39
+
40
+ <section>
41
+ <h2 className="text-lg font-semibold text-white mb-4">Memory Quality</h2>
42
+ <QualityPanel project={selectedProject} />
43
+ </section>
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+ import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts';
5
+ import { getCategoryColor } from '@/lib/category-colors';
6
+ import type { MemoryStats, MemoryCategory } from '@/types/memory';
7
+
8
+ interface KnowledgeMapPanelProps {
9
+ stats: MemoryStats;
10
+ onNavigate: (filter: { category?: string }) => void;
11
+ }
12
+
13
+ export function KnowledgeMapPanel({ stats, onNavigate }: KnowledgeMapPanelProps) {
14
+ const data = useMemo(() => {
15
+ return Object.entries(stats.byCategory)
16
+ .map(([category, count]) => ({
17
+ category,
18
+ count,
19
+ color: getCategoryColor(category as MemoryCategory),
20
+ thin: count < 3,
21
+ }))
22
+ .sort((a, b) => b.count - a.count);
23
+ }, [stats.byCategory]);
24
+
25
+ if (data.length === 0) {
26
+ return (
27
+ <div className="bg-slate-900/50 rounded-lg p-4 text-slate-400 text-sm">
28
+ No category data available.
29
+ </div>
30
+ );
31
+ }
32
+
33
+ return (
34
+ <div className="bg-slate-900/50 rounded-lg p-4">
35
+ <ResponsiveContainer width="100%" height={data.length * 36 + 20}>
36
+ <BarChart data={data} layout="vertical" margin={{ left: 10, right: 30 }}>
37
+ <XAxis type="number" hide />
38
+ <YAxis
39
+ type="category"
40
+ dataKey="category"
41
+ width={100}
42
+ tick={{ fill: '#94a3b8', fontSize: 12 }}
43
+ axisLine={false}
44
+ tickLine={false}
45
+ />
46
+ <Tooltip
47
+ contentStyle={{ background: '#1e293b', border: '1px solid #334155', borderRadius: 8 }}
48
+ labelStyle={{ color: '#e2e8f0' }}
49
+ itemStyle={{ color: '#e2e8f0' }}
50
+ />
51
+ <Bar
52
+ dataKey="count"
53
+ radius={[0, 4, 4, 0]}
54
+ cursor="pointer"
55
+ onClick={(_data, index) => {
56
+ if (index !== undefined && data[index]) {
57
+ onNavigate({ category: data[index].category });
58
+ }
59
+ }}
60
+ >
61
+ {data.map((entry, index) => (
62
+ <Cell key={index} fill={entry.color} />
63
+ ))}
64
+ </Bar>
65
+ </BarChart>
66
+ </ResponsiveContainer>
67
+
68
+ {/* Thin coverage warnings */}
69
+ {data.some(d => d.thin) && (
70
+ <div className="mt-2 flex flex-wrap gap-2">
71
+ {data.filter(d => d.thin).map(d => (
72
+ <span key={d.category} className="text-xs text-amber-400 bg-amber-400/10 px-2 py-0.5 rounded">
73
+ ⚠ {d.category} ({d.count})
74
+ </span>
75
+ ))}
76
+ </div>
77
+ )}
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Eye, Clock, Copy, AlertTriangle, ChevronDown, ChevronRight } from 'lucide-react';
5
+ import { useQuality, useContradictions } from '@/hooks/useMemories';
6
+
7
+ interface QualityPanelProps {
8
+ project?: string;
9
+ }
10
+
11
+ function Section({
12
+ icon: Icon,
13
+ title,
14
+ count,
15
+ children,
16
+ }: {
17
+ icon: React.ComponentType<{ className?: string; size?: number }>;
18
+ title: string;
19
+ count: number;
20
+ children: React.ReactNode;
21
+ }) {
22
+ const [expanded, setExpanded] = useState(false);
23
+
24
+ return (
25
+ <div className="border border-slate-800 rounded-lg overflow-hidden">
26
+ <button
27
+ onClick={() => setExpanded(!expanded)}
28
+ className="w-full flex items-center gap-2 px-3 py-2 hover:bg-slate-800/50 transition-colors"
29
+ >
30
+ {expanded ? <ChevronDown size={14} className="text-slate-400" /> : <ChevronRight size={14} className="text-slate-400" />}
31
+ <Icon size={14} className="text-slate-400" />
32
+ <span className="text-sm text-slate-200 flex-1 text-left">{title}</span>
33
+ <span className="text-xs px-1.5 py-0.5 rounded bg-slate-700 text-slate-300">{count}</span>
34
+ </button>
35
+ {expanded && <div className="px-3 pb-3 space-y-1">{children}</div>}
36
+ </div>
37
+ );
38
+ }
39
+
40
+ export function QualityPanel({ project }: QualityPanelProps) {
41
+ const { data: quality } = useQuality(project);
42
+ const { data: contradictionsData } = useContradictions(project);
43
+
44
+ const neverAccessed = quality?.neverAccessed;
45
+ const stale = quality?.stale;
46
+ const duplicates = quality?.duplicates;
47
+ const contradictions = contradictionsData?.contradictions ?? [];
48
+
49
+ return (
50
+ <div className="space-y-2">
51
+ <Section icon={Eye} title="Never Accessed" count={neverAccessed?.count ?? 0}>
52
+ {neverAccessed?.items?.length ? (
53
+ neverAccessed.items.map((item, i) => (
54
+ <div key={i} className="text-xs text-slate-400 py-1 border-b border-slate-800/50 last:border-0">
55
+ <span className="text-slate-300">{String(item.title || 'Untitled')}</span>
56
+ {item.created_at ? (
57
+ <span className="ml-2 text-slate-500">{String(item.created_at).slice(0, 10)}</span>
58
+ ) : null}
59
+ </div>
60
+ ))
61
+ ) : (
62
+ <div className="text-xs text-slate-500">None found</div>
63
+ )}
64
+ </Section>
65
+
66
+ <Section icon={Clock} title="Stale Memories" count={stale?.count ?? 0}>
67
+ {stale?.items?.length ? (
68
+ stale.items.map((item, i) => {
69
+ const score = Number(item.decayed_score ?? 0);
70
+ const color = score < 0.3 ? 'text-red-400' : score < 0.5 ? 'text-amber-400' : 'text-slate-400';
71
+ return (
72
+ <div key={i} className="text-xs text-slate-400 py-1 border-b border-slate-800/50 last:border-0 flex items-center gap-2">
73
+ <span className={`${color} font-mono`}>{score.toFixed(2)}</span>
74
+ <span className="text-slate-300">{String(item.title || 'Untitled')}</span>
75
+ </div>
76
+ );
77
+ })
78
+ ) : (
79
+ <div className="text-xs text-slate-500">None found</div>
80
+ )}
81
+ </Section>
82
+
83
+ <Section icon={Copy} title="Duplicates" count={duplicates?.count ?? 0}>
84
+ {duplicates?.items?.length ? (
85
+ duplicates.items.map((item, i) => (
86
+ <div key={i} className="text-xs text-slate-400 py-1 border-b border-slate-800/50 last:border-0">
87
+ <span className="text-slate-300">{String(item.title_a || 'Untitled')}</span>
88
+ <span className="mx-1 text-slate-600">&harr;</span>
89
+ <span className="text-slate-300">{String(item.title_b || 'Untitled')}</span>
90
+ </div>
91
+ ))
92
+ ) : (
93
+ <div className="text-xs text-slate-500">None found</div>
94
+ )}
95
+ </Section>
96
+
97
+ <Section icon={AlertTriangle} title="Contradictions" count={contradictions.length}>
98
+ {contradictions.length ? (
99
+ contradictions.map((c, i) => (
100
+ <div key={i} className="text-xs py-1 border-b border-slate-800/50 last:border-0">
101
+ <div className="flex items-center gap-2 text-slate-400">
102
+ <span className="font-mono text-amber-400">{c.score.toFixed(2)}</span>
103
+ <span className="text-slate-300">{c.memoryATitle}</span>
104
+ <span className="text-slate-600">&harr;</span>
105
+ <span className="text-slate-300">{c.memoryBTitle}</span>
106
+ </div>
107
+ {c.reason && <div className="text-slate-500 mt-0.5 ml-10">{c.reason}</div>}
108
+ </div>
109
+ ))
110
+ ) : (
111
+ <div className="text-xs text-slate-500">None found</div>
112
+ )}
113
+ </Section>
114
+ </div>
115
+ );
116
+ }
@@ -0,0 +1,150 @@
1
+ 'use client';
2
+
3
+ import { useState, useMemo, useCallback } from 'react';
4
+ import { useQueryClient } from '@tanstack/react-query';
5
+ import { Memory } from '@/types/memory';
6
+ import { MemoryCard } from './MemoryCard';
7
+
8
+ const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
9
+
10
+ type SortKey = 'salience' | 'createdAt' | 'lastAccessed' | 'decayedScore';
11
+ type ViewStyle = 'grid' | 'list';
12
+
13
+ interface MemoriesViewProps {
14
+ memories: Memory[];
15
+ selectedMemory: Memory | null;
16
+ onSelectMemory: (m: Memory | null) => void;
17
+ }
18
+
19
+ export function MemoriesView({ memories, selectedMemory, onSelectMemory }: MemoriesViewProps) {
20
+ const [sortKey, setSortKey] = useState<SortKey>('salience');
21
+ const [viewStyle, setViewStyle] = useState<ViewStyle>('grid');
22
+ const [bulkMode, setBulkMode] = useState(false);
23
+ const [checked, setChecked] = useState<Set<number>>(new Set());
24
+ const [deleting, setDeleting] = useState(false);
25
+ const queryClient = useQueryClient();
26
+
27
+ const sorted = useMemo(() => {
28
+ const arr = [...memories];
29
+ arr.sort((a, b) => {
30
+ switch (sortKey) {
31
+ case 'salience': return b.salience - a.salience;
32
+ case 'createdAt': return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
33
+ case 'lastAccessed': return new Date(b.lastAccessed).getTime() - new Date(a.lastAccessed).getTime();
34
+ case 'decayedScore': return (b.decayedScore ?? b.salience) - (a.decayedScore ?? a.salience);
35
+ default: return 0;
36
+ }
37
+ });
38
+ return arr;
39
+ }, [memories, sortKey]);
40
+
41
+ const handleCheck = useCallback((id: number, val: boolean) => {
42
+ setChecked((prev) => {
43
+ const next = new Set(prev);
44
+ if (val) next.add(id); else next.delete(id);
45
+ return next;
46
+ });
47
+ }, []);
48
+
49
+ const selectAll = () => setChecked(new Set(sorted.map((m) => m.id)));
50
+ const _deselectAll = () => setChecked(new Set());
51
+
52
+ const deleteSelected = async () => {
53
+ if (checked.size === 0) return;
54
+ setDeleting(true);
55
+ try {
56
+ await Promise.all(
57
+ Array.from(checked).map((id) =>
58
+ fetch(`${API_BASE}/api/memories/${id}`, { method: 'DELETE' })
59
+ )
60
+ );
61
+ setChecked(new Set());
62
+ queryClient.invalidateQueries({ queryKey: ['memories'] });
63
+ } finally {
64
+ setDeleting(false);
65
+ }
66
+ };
67
+
68
+ return (
69
+ <div className="flex flex-col h-full">
70
+ {/* Toolbar */}
71
+ <div className="flex items-center gap-3 px-4 py-2 border-b border-slate-800 shrink-0">
72
+ <span className="text-xs text-slate-400">{memories.length} memories</span>
73
+
74
+ <div className="w-px h-5 bg-slate-700" />
75
+
76
+ <select
77
+ value={sortKey}
78
+ onChange={(e) => setSortKey(e.target.value as SortKey)}
79
+ className="bg-slate-800 border border-slate-700 text-white text-xs rounded px-2 py-1"
80
+ >
81
+ <option value="salience">Salience</option>
82
+ <option value="createdAt">Created</option>
83
+ <option value="lastAccessed">Last Accessed</option>
84
+ <option value="decayedScore">Decay Score</option>
85
+ </select>
86
+
87
+ <div className="flex items-center border border-slate-700 rounded overflow-hidden">
88
+ <button
89
+ onClick={() => setViewStyle('grid')}
90
+ className={`px-2 py-1 text-xs ${viewStyle === 'grid' ? 'bg-slate-700 text-white' : 'text-slate-400 hover:text-white'}`}
91
+ >
92
+ Grid
93
+ </button>
94
+ <button
95
+ onClick={() => setViewStyle('list')}
96
+ className={`px-2 py-1 text-xs ${viewStyle === 'list' ? 'bg-slate-700 text-white' : 'text-slate-400 hover:text-white'}`}
97
+ >
98
+ List
99
+ </button>
100
+ </div>
101
+
102
+ <div className="w-px h-5 bg-slate-700" />
103
+
104
+ <button
105
+ onClick={() => { setBulkMode(!bulkMode); setChecked(new Set()); }}
106
+ className={`px-2 py-1 text-xs rounded ${bulkMode ? 'bg-blue-600 text-white' : 'bg-slate-800 text-slate-400 hover:text-white'}`}
107
+ >
108
+ Select
109
+ </button>
110
+
111
+ {bulkMode && (
112
+ <>
113
+ <button onClick={selectAll} className="px-2 py-1 text-xs text-slate-400 hover:text-white">
114
+ Select all
115
+ </button>
116
+ <button
117
+ onClick={deleteSelected}
118
+ disabled={checked.size === 0 || deleting}
119
+ className="px-2 py-1 text-xs rounded bg-red-600/20 text-red-400 hover:bg-red-600/40 disabled:opacity-40"
120
+ >
121
+ {deleting ? 'Deleting...' : `Delete (${checked.size})`}
122
+ </button>
123
+ </>
124
+ )}
125
+ </div>
126
+
127
+ {/* Card grid */}
128
+ <div className="flex-1 overflow-y-auto p-4">
129
+ <div
130
+ className={
131
+ viewStyle === 'grid'
132
+ ? 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3'
133
+ : 'flex flex-col gap-3 max-w-2xl'
134
+ }
135
+ >
136
+ {sorted.map((memory) => (
137
+ <MemoryCard
138
+ key={memory.id}
139
+ memory={memory}
140
+ isSelected={selectedMemory?.id === memory.id}
141
+ onSelect={onSelectMemory}
142
+ isChecked={bulkMode ? checked.has(memory.id) : undefined}
143
+ onCheck={bulkMode ? handleCheck : undefined}
144
+ />
145
+ ))}
146
+ </div>
147
+ </div>
148
+ </div>
149
+ );
150
+ }
@@ -0,0 +1,103 @@
1
+ 'use client';
2
+
3
+ import { Memory } from '@/types/memory';
4
+ import { getCategoryColor, getTypeColor } from '@/lib/category-colors';
5
+
6
+ function relativeTime(dateStr: string): string {
7
+ const diff = Date.now() - new Date(dateStr).getTime();
8
+ const mins = Math.floor(diff / 60000);
9
+ if (mins < 1) return 'now';
10
+ if (mins < 60) return `${mins}m ago`;
11
+ const hrs = Math.floor(mins / 60);
12
+ if (hrs < 24) return `${hrs}h ago`;
13
+ const days = Math.floor(hrs / 24);
14
+ if (days < 7) return `${days}d ago`;
15
+ const weeks = Math.floor(days / 7);
16
+ return `${weeks}w ago`;
17
+ }
18
+
19
+ interface MemoryCardProps {
20
+ memory: Memory;
21
+ isSelected: boolean;
22
+ onSelect: (m: Memory) => void;
23
+ isChecked?: boolean;
24
+ onCheck?: (id: number, checked: boolean) => void;
25
+ }
26
+
27
+ export function MemoryCard({ memory, isSelected, onSelect, isChecked, onCheck }: MemoryCardProps) {
28
+ const catColor = getCategoryColor(memory.category);
29
+ const typeColor = getTypeColor(memory.type);
30
+
31
+ return (
32
+ <div
33
+ onClick={() => onSelect(memory)}
34
+ className={`bg-slate-900 border rounded-lg p-3 hover:border-slate-600 cursor-pointer transition-colors relative ${
35
+ isSelected ? 'border-cyan-500' : 'border-slate-800'
36
+ }`}
37
+ >
38
+ {/* Salience bar */}
39
+ <div className="h-0.5 rounded-full bg-slate-800 mb-2 overflow-hidden">
40
+ <div
41
+ className="h-full rounded-full"
42
+ style={{ width: `${memory.salience * 100}%`, backgroundColor: catColor }}
43
+ />
44
+ </div>
45
+
46
+ {/* Checkbox */}
47
+ {onCheck && (
48
+ <input
49
+ type="checkbox"
50
+ checked={isChecked ?? false}
51
+ onChange={(e) => {
52
+ e.stopPropagation();
53
+ onCheck(memory.id, e.target.checked);
54
+ }}
55
+ onClick={(e) => e.stopPropagation()}
56
+ className="absolute top-3 right-3 w-4 h-4 accent-blue-500"
57
+ />
58
+ )}
59
+
60
+ {/* Title */}
61
+ <h3 className="text-sm font-semibold text-white truncate pr-6">{memory.title}</h3>
62
+
63
+ {/* Badges */}
64
+ <div className="flex items-center gap-1.5 mt-1">
65
+ <span
66
+ className="text-[10px] px-1.5 py-0.5 rounded-full font-medium"
67
+ style={{ backgroundColor: catColor + '22', color: catColor }}
68
+ >
69
+ {memory.category}
70
+ </span>
71
+ <span
72
+ className="text-[10px] px-1.5 py-0.5 rounded-full font-medium"
73
+ style={{ backgroundColor: typeColor + '22', color: typeColor }}
74
+ >
75
+ {memory.type.replace('_', '-')}
76
+ </span>
77
+ </div>
78
+
79
+ {/* Content preview */}
80
+ <p className="text-[13px] text-slate-400 mt-1.5 line-clamp-3 leading-snug">{memory.content}</p>
81
+
82
+ {/* Tags */}
83
+ {memory.tags.length > 0 && (
84
+ <div className="flex items-center gap-1 mt-2 flex-wrap">
85
+ {memory.tags.slice(0, 3).map((tag) => (
86
+ <span key={tag} className="text-[10px] px-1.5 py-0.5 rounded bg-slate-800 text-slate-400">
87
+ {tag}
88
+ </span>
89
+ ))}
90
+ {memory.tags.length > 3 && (
91
+ <span className="text-[10px] text-slate-500">+{memory.tags.length - 3} more</span>
92
+ )}
93
+ </div>
94
+ )}
95
+
96
+ {/* Footer */}
97
+ <div className="flex items-center justify-between mt-2 text-[11px] text-slate-500">
98
+ <span>Created {relativeTime(memory.createdAt)}</span>
99
+ <span>Accessed {relativeTime(memory.lastAccessed)}</span>
100
+ </div>
101
+ </div>
102
+ );
103
+ }