claude-code-workflow 6.1.4 → 6.2.2

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 (437) hide show
  1. package/.claude/CLAUDE.md +10 -0
  2. package/.claude/agents/action-planning-agent.md +857 -778
  3. package/.claude/agents/cli-execution-agent.md +266 -269
  4. package/.claude/agents/cli-explore-agent.md +2 -2
  5. package/.claude/agents/cli-lite-planning-agent.md +142 -92
  6. package/.claude/agents/cli-planning-agent.md +4 -4
  7. package/.claude/agents/code-developer.md +7 -6
  8. package/.claude/agents/conceptual-planning-agent.md +2 -2
  9. package/.claude/agents/context-search-agent.md +31 -32
  10. package/.claude/agents/doc-generator.md +4 -4
  11. package/.claude/agents/memory-bridge.md +93 -93
  12. package/.claude/agents/test-context-search-agent.md +8 -7
  13. package/.claude/agents/test-fix-agent.md +7 -6
  14. package/.claude/commands/clean.md +516 -0
  15. package/.claude/commands/memory/compact.md +383 -0
  16. package/.claude/commands/memory/docs-full-cli.md +471 -471
  17. package/.claude/commands/memory/docs-related-cli.md +386 -386
  18. package/.claude/commands/memory/docs.md +615 -615
  19. package/.claude/commands/memory/load.md +5 -5
  20. package/.claude/commands/memory/tech-research-rules.md +310 -0
  21. package/.claude/commands/memory/update-full.md +332 -332
  22. package/.claude/commands/memory/workflow-skill-memory.md +4 -4
  23. package/.claude/commands/task/create.md +151 -151
  24. package/.claude/commands/version.md +254 -254
  25. package/.claude/commands/workflow/brainstorm/api-designer.md +587 -585
  26. package/.claude/commands/workflow/brainstorm/artifacts.md +1 -0
  27. package/.claude/commands/workflow/brainstorm/auto-parallel.md +443 -443
  28. package/.claude/commands/workflow/brainstorm/data-architect.md +220 -220
  29. package/.claude/commands/workflow/brainstorm/product-manager.md +200 -200
  30. package/.claude/commands/workflow/brainstorm/product-owner.md +200 -200
  31. package/.claude/commands/workflow/brainstorm/scrum-master.md +200 -200
  32. package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
  33. package/.claude/commands/workflow/brainstorm/system-architect.md +389 -387
  34. package/.claude/commands/workflow/brainstorm/ui-designer.md +221 -221
  35. package/.claude/commands/workflow/brainstorm/ux-expert.md +221 -221
  36. package/.claude/commands/workflow/debug.md +321 -0
  37. package/.claude/commands/workflow/execute.md +13 -0
  38. package/.claude/commands/workflow/init.md +165 -164
  39. package/.claude/commands/workflow/lite-execute.md +119 -13
  40. package/.claude/commands/workflow/lite-fix.md +623 -621
  41. package/.claude/commands/workflow/lite-plan.md +610 -592
  42. package/.claude/commands/workflow/plan.md +5 -5
  43. package/.claude/commands/workflow/review-module-cycle.md +2 -0
  44. package/.claude/commands/workflow/review-session-cycle.md +2 -0
  45. package/.claude/commands/workflow/review.md +297 -291
  46. package/.claude/commands/workflow/session/complete.md +153 -500
  47. package/.claude/commands/workflow/session/list.md +95 -95
  48. package/.claude/commands/workflow/session/resume.md +60 -60
  49. package/.claude/commands/workflow/session/start.md +199 -199
  50. package/.claude/commands/workflow/tdd-plan.md +3 -3
  51. package/.claude/commands/workflow/tdd-verify.md +23 -9
  52. package/.claude/commands/workflow/test-cycle-execute.md +2 -0
  53. package/.claude/commands/workflow/test-fix-gen.md +699 -699
  54. package/.claude/commands/workflow/tools/conflict-resolution.md +104 -18
  55. package/.claude/commands/workflow/tools/context-gather.md +436 -434
  56. package/.claude/commands/workflow/tools/task-generate-agent.md +490 -291
  57. package/.claude/commands/workflow/tools/task-generate-tdd.md +18 -10
  58. package/.claude/commands/workflow/tools/test-concept-enhanced.md +2 -1
  59. package/.claude/commands/workflow/tools/test-context-gather.md +1 -0
  60. package/.claude/commands/workflow/tools/test-task-generate.md +1 -0
  61. package/.claude/commands/workflow/ui-design/import-from-code.md +9 -6
  62. package/.claude/skills/command-guide/SKILL.md +5 -5
  63. package/.claude/skills/command-guide/index/all-commands.json +1 -1
  64. package/.claude/skills/command-guide/index/by-category.json +1 -1
  65. package/.claude/skills/command-guide/index/by-use-case.json +1 -1
  66. package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +857 -778
  67. package/.claude/skills/command-guide/reference/agents/cli-execution-agent.md +266 -269
  68. package/.claude/skills/command-guide/reference/agents/cli-explore-agent.md +2 -2
  69. package/.claude/skills/command-guide/reference/agents/cli-lite-planning-agent.md +142 -92
  70. package/.claude/skills/command-guide/reference/agents/cli-planning-agent.md +4 -4
  71. package/.claude/skills/command-guide/reference/agents/code-developer.md +7 -6
  72. package/.claude/skills/command-guide/reference/agents/conceptual-planning-agent.md +2 -2
  73. package/.claude/skills/command-guide/reference/agents/context-search-agent.md +31 -32
  74. package/.claude/skills/command-guide/reference/agents/doc-generator.md +4 -4
  75. package/.claude/skills/command-guide/reference/agents/memory-bridge.md +93 -93
  76. package/.claude/skills/command-guide/reference/agents/test-context-search-agent.md +8 -7
  77. package/.claude/skills/command-guide/reference/agents/test-fix-agent.md +7 -6
  78. package/.claude/skills/command-guide/reference/commands/memory/docs-full-cli.md +471 -471
  79. package/.claude/skills/command-guide/reference/commands/memory/docs-related-cli.md +386 -386
  80. package/.claude/skills/command-guide/reference/commands/memory/docs.md +17 -16
  81. package/.claude/skills/command-guide/reference/commands/memory/load.md +5 -5
  82. package/.claude/skills/command-guide/reference/commands/memory/tech-research.md +194 -357
  83. package/.claude/skills/command-guide/reference/commands/memory/update-full.md +332 -332
  84. package/.claude/skills/command-guide/reference/commands/memory/workflow-skill-memory.md +4 -4
  85. package/.claude/skills/command-guide/reference/commands/task/create.md +151 -151
  86. package/.claude/skills/command-guide/reference/commands/version.md +254 -254
  87. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/api-designer.md +585 -585
  88. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/auto-parallel.md +443 -443
  89. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/data-architect.md +220 -220
  90. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-manager.md +200 -200
  91. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-owner.md +200 -200
  92. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/scrum-master.md +200 -200
  93. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
  94. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/system-architect.md +387 -387
  95. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ui-designer.md +221 -221
  96. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ux-expert.md +221 -221
  97. package/.claude/skills/command-guide/reference/commands/workflow/execute.md +25 -20
  98. package/.claude/skills/command-guide/reference/commands/workflow/init.md +164 -164
  99. package/.claude/skills/command-guide/reference/commands/workflow/lite-execute.md +748 -686
  100. package/.claude/skills/command-guide/reference/commands/workflow/lite-fix.md +664 -621
  101. package/.claude/skills/command-guide/reference/commands/workflow/lite-plan.md +645 -592
  102. package/.claude/skills/command-guide/reference/commands/workflow/plan.md +5 -5
  103. package/.claude/skills/command-guide/reference/commands/workflow/review.md +25 -18
  104. package/.claude/skills/command-guide/reference/commands/workflow/session/complete.md +547 -500
  105. package/.claude/skills/command-guide/reference/commands/workflow/session/list.md +45 -27
  106. package/.claude/skills/command-guide/reference/commands/workflow/session/resume.md +35 -19
  107. package/.claude/skills/command-guide/reference/commands/workflow/session/start.md +90 -33
  108. package/.claude/skills/command-guide/reference/commands/workflow/tdd-plan.md +3 -3
  109. package/.claude/skills/command-guide/reference/commands/workflow/tdd-verify.md +23 -9
  110. package/.claude/skills/command-guide/reference/commands/workflow/test-fix-gen.md +699 -699
  111. package/.claude/skills/command-guide/reference/commands/workflow/tools/conflict-resolution.md +103 -17
  112. package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +434 -434
  113. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-agent.md +487 -291
  114. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +17 -10
  115. package/.claude/skills/command-guide/reference/commands/workflow/tools/test-concept-enhanced.md +1 -1
  116. package/.claude/skills/command-guide/reference/commands/workflow/ui-design/import-from-code.md +6 -6
  117. package/.claude/workflows/chinese-response.md +38 -0
  118. package/.claude/workflows/cli-templates/prompts/rules/rule-api.txt +122 -0
  119. package/.claude/workflows/cli-templates/prompts/rules/rule-components.txt +122 -0
  120. package/.claude/workflows/cli-templates/prompts/rules/rule-config.txt +89 -0
  121. package/.claude/workflows/cli-templates/prompts/rules/rule-core.txt +60 -0
  122. package/.claude/workflows/cli-templates/prompts/rules/rule-patterns.txt +70 -0
  123. package/.claude/workflows/cli-templates/prompts/rules/rule-testing.txt +81 -0
  124. package/.claude/workflows/cli-templates/prompts/rules/tech-rules-agent-prompt.txt +89 -0
  125. package/.claude/workflows/cli-templates/prompts/workflow/gemini-solution-design.txt +131 -131
  126. package/.claude/workflows/cli-templates/prompts/workflow/skill-conflict-patterns.txt +5 -9
  127. package/.claude/workflows/cli-templates/prompts/workflow/skill-lessons-learned.txt +5 -9
  128. package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +112 -0
  129. package/.claude/workflows/cli-templates/protocols/write-protocol.md +201 -0
  130. package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +137 -0
  131. package/.claude/workflows/cli-templates/schemas/debug-log-json-schema.json +127 -0
  132. package/.claude/workflows/cli-templates/schemas/fix-plan-json-schema.json +25 -0
  133. package/.claude/workflows/cli-templates/schemas/plan-json-schema.json +25 -0
  134. package/.claude/workflows/cli-tools-usage.md +526 -0
  135. package/{CLAUDE.md → .claude/workflows/coding-philosophy.md} +24 -45
  136. package/.claude/workflows/context-tools.md +84 -0
  137. package/.claude/workflows/file-modification.md +64 -0
  138. package/.claude/workflows/tool-strategy.md +216 -79
  139. package/.claude/workflows/windows-platform.md +16 -0
  140. package/.claude/workflows/workflow-architecture.md +942 -942
  141. package/.codex/AGENTS.md +63 -330
  142. package/.codex/prompts/debug.md +318 -0
  143. package/.codex/prompts/execute.md +273 -0
  144. package/.codex/prompts/lite-execute.md +164 -0
  145. package/.codex/prompts/lite-plan.md +469 -0
  146. package/.codex/prompts.zip +0 -0
  147. package/.gemini/GEMINI.md +25 -164
  148. package/.qwen/QWEN.md +0 -139
  149. package/README.md +29 -9
  150. package/ccw/README.md +30 -6
  151. package/ccw/bin/ccw-mcp.js +7 -0
  152. package/ccw/bin/ccw.js +9 -9
  153. package/ccw/package.json +65 -47
  154. package/ccw/src/.workflow/.cli-history/history.db +0 -0
  155. package/ccw/src/.workflow/.cli-history/history.db-shm +0 -0
  156. package/ccw/src/.workflow/.cli-history/history.db-wal +0 -0
  157. package/ccw/src/cli.ts +244 -0
  158. package/ccw/src/commands/cli.ts +740 -0
  159. package/ccw/src/commands/core-memory.ts +770 -0
  160. package/ccw/src/commands/hook.ts +315 -0
  161. package/ccw/src/commands/install.ts +519 -0
  162. package/ccw/src/commands/{list.js → list.ts} +1 -1
  163. package/ccw/src/commands/memory.ts +1090 -0
  164. package/ccw/src/commands/{serve.js → serve.ts} +14 -5
  165. package/ccw/src/commands/session-path-resolver.ts +372 -0
  166. package/ccw/src/commands/session.ts +1141 -0
  167. package/ccw/src/commands/{stop.js → stop.ts} +16 -6
  168. package/ccw/src/commands/tool.ts +201 -0
  169. package/ccw/src/commands/{uninstall.js → uninstall.ts} +89 -40
  170. package/ccw/src/commands/{upgrade.js → upgrade.ts} +68 -23
  171. package/ccw/src/commands/{view.js → view.ts} +22 -8
  172. package/ccw/src/config/storage-paths.ts +670 -0
  173. package/ccw/src/core/cache-manager.ts +294 -0
  174. package/ccw/src/core/claude-freshness.ts +319 -0
  175. package/ccw/src/core/core-memory-store.ts +1528 -0
  176. package/ccw/src/core/{dashboard-generator-patch.js → dashboard-generator-patch.ts} +18 -0
  177. package/ccw/src/core/{dashboard-generator.js → dashboard-generator.ts} +69 -12
  178. package/ccw/src/core/data-aggregator.ts +584 -0
  179. package/ccw/src/core/history-importer.ts +625 -0
  180. package/ccw/src/core/{lite-scanner.js → lite-scanner-complete.ts} +162 -66
  181. package/ccw/src/core/lite-scanner.ts +469 -0
  182. package/ccw/src/core/{manifest.js → manifest.ts} +104 -34
  183. package/ccw/src/core/memory-embedder-bridge.ts +262 -0
  184. package/ccw/src/core/memory-store.ts +978 -0
  185. package/ccw/src/core/routes/ccw-routes.ts +96 -0
  186. package/ccw/src/core/routes/claude-routes.ts +1183 -0
  187. package/ccw/src/core/routes/cli-routes.ts +561 -0
  188. package/ccw/src/core/routes/codexlens-routes.ts +806 -0
  189. package/ccw/src/core/routes/core-memory-routes.ts +605 -0
  190. package/ccw/src/core/routes/files-routes.ts +428 -0
  191. package/ccw/src/core/routes/graph-routes.md +164 -0
  192. package/ccw/src/core/routes/graph-routes.ts +626 -0
  193. package/ccw/src/core/routes/help-routes.ts +308 -0
  194. package/ccw/src/core/routes/hooks-routes.ts +405 -0
  195. package/ccw/src/core/routes/mcp-routes.ts +1271 -0
  196. package/ccw/src/core/routes/mcp-routes.ts.backup +550 -0
  197. package/ccw/src/core/routes/mcp-templates-db.ts +268 -0
  198. package/ccw/src/core/routes/memory-routes.ts +1206 -0
  199. package/ccw/src/core/routes/rules-routes.ts +526 -0
  200. package/ccw/src/core/routes/session-routes.ts +467 -0
  201. package/ccw/src/core/routes/skills-routes.ts +599 -0
  202. package/ccw/src/core/routes/status-routes.ts +57 -0
  203. package/ccw/src/core/routes/system-routes.ts +427 -0
  204. package/ccw/src/core/server.ts +431 -0
  205. package/ccw/src/core/session-clustering-service.ts +1258 -0
  206. package/ccw/src/core/session-scanner.ts +283 -0
  207. package/ccw/src/core/websocket.ts +190 -0
  208. package/ccw/src/{index.js → index.ts} +1 -0
  209. package/ccw/src/mcp-server/index.ts +186 -0
  210. package/ccw/src/templates/assets/css/github-dark.min.css +10 -0
  211. package/ccw/src/templates/assets/css/github.min.css +10 -0
  212. package/ccw/src/templates/assets/js/cytoscape.min.js +32 -0
  213. package/ccw/src/templates/assets/js/d3.min.js +2 -0
  214. package/ccw/src/templates/assets/js/highlight.min.js +1244 -0
  215. package/ccw/src/templates/assets/js/lucide.min.js +12 -0
  216. package/ccw/src/templates/assets/js/marked.min.js +69 -0
  217. package/ccw/src/templates/assets/js/tailwind.js +83 -0
  218. package/ccw/src/templates/dashboard-css/01-base.css +11 -0
  219. package/ccw/src/templates/dashboard-css/02-session.css +22 -0
  220. package/ccw/src/templates/dashboard-css/04-lite-tasks.css +10 -0
  221. package/ccw/src/templates/dashboard-css/06-cards.css +10 -4
  222. package/ccw/src/templates/dashboard-css/07-managers.css +1178 -7
  223. package/ccw/src/templates/dashboard-css/09-explorer.css +23 -12
  224. package/ccw/src/templates/dashboard-css/10-cli-status.css +337 -0
  225. package/ccw/src/templates/dashboard-css/11-cli-history.css +271 -0
  226. package/ccw/src/templates/dashboard-css/12-cli-legacy.css +796 -0
  227. package/ccw/src/templates/dashboard-css/13-cli-ccw.css +199 -0
  228. package/ccw/src/templates/dashboard-css/14-cli-modals.css +258 -0
  229. package/ccw/src/templates/dashboard-css/15-cli-endpoints.css +305 -0
  230. package/ccw/src/templates/dashboard-css/16-cli-session.css +241 -0
  231. package/ccw/src/templates/dashboard-css/17-cli-conversation.css +283 -0
  232. package/ccw/src/templates/dashboard-css/18-cli-settings.css +160 -0
  233. package/ccw/src/templates/dashboard-css/19-cli-native-session.css +496 -0
  234. package/ccw/src/templates/dashboard-css/20-cli-taskqueue.css +188 -0
  235. package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +310 -0
  236. package/ccw/src/templates/dashboard-css/22-cli-semantic.css +240 -0
  237. package/ccw/src/templates/dashboard-css/23-memory.css +2390 -0
  238. package/ccw/src/templates/dashboard-css/24-prompt-history.css +1089 -0
  239. package/ccw/src/templates/dashboard-css/25-skills-rules.css +326 -0
  240. package/ccw/src/templates/dashboard-css/26-claude-manager.css +908 -0
  241. package/ccw/src/templates/dashboard-css/27-graph-explorer.css +1678 -0
  242. package/ccw/src/templates/dashboard-css/28-mcp-manager.css +748 -0
  243. package/ccw/src/templates/dashboard-css/29-help.css +264 -0
  244. package/ccw/src/templates/dashboard-css/30-core-memory.css +1700 -0
  245. package/ccw/src/templates/dashboard-js/api.js +162 -142
  246. package/ccw/src/templates/dashboard-js/components/carousel.js +4 -4
  247. package/ccw/src/templates/dashboard-js/components/cli-history.js +876 -0
  248. package/ccw/src/templates/dashboard-js/components/cli-status.js +978 -0
  249. package/ccw/src/templates/dashboard-js/components/global-notifications.js +508 -219
  250. package/ccw/src/templates/dashboard-js/components/hook-manager.js +1277 -282
  251. package/ccw/src/templates/dashboard-js/components/index-manager.js +302 -0
  252. package/ccw/src/templates/dashboard-js/components/mcp-manager.js +718 -27
  253. package/ccw/src/templates/dashboard-js/components/modals.js +66 -0
  254. package/ccw/src/templates/dashboard-js/components/navigation.js +80 -12
  255. package/ccw/src/templates/dashboard-js/components/notifications.js +758 -194
  256. package/ccw/src/templates/dashboard-js/components/storage-manager.js +478 -0
  257. package/ccw/src/templates/dashboard-js/components/tabs-other.js +157 -6
  258. package/ccw/src/templates/dashboard-js/components/task-queue-sidebar.js +716 -0
  259. package/ccw/src/templates/dashboard-js/help-i18n.js +272 -0
  260. package/ccw/src/templates/dashboard-js/i18n.js +2807 -0
  261. package/ccw/src/templates/dashboard-js/main.js +15 -0
  262. package/ccw/src/templates/dashboard-js/state.js +243 -42
  263. package/ccw/src/templates/dashboard-js/utils.js +47 -1
  264. package/ccw/src/templates/dashboard-js/views/claude-manager.js +912 -0
  265. package/ccw/src/templates/dashboard-js/views/cli-manager.js +2272 -0
  266. package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +964 -0
  267. package/ccw/src/templates/dashboard-js/views/core-memory-clusters.js +503 -0
  268. package/ccw/src/templates/dashboard-js/views/core-memory.js +782 -0
  269. package/ccw/src/templates/dashboard-js/views/explorer.js +888 -852
  270. package/ccw/src/templates/dashboard-js/views/graph-explorer.js +1157 -0
  271. package/ccw/src/templates/dashboard-js/views/help.js +856 -0
  272. package/ccw/src/templates/dashboard-js/views/history.js +337 -0
  273. package/ccw/src/templates/dashboard-js/views/home.js +61 -15
  274. package/ccw/src/templates/dashboard-js/views/hook-manager.js +311 -43
  275. package/ccw/src/templates/dashboard-js/views/lite-tasks.js +204 -28
  276. package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2187 -411
  277. package/ccw/src/templates/dashboard-js/views/mcp-manager.js.backup +1729 -0
  278. package/ccw/src/templates/dashboard-js/views/mcp-manager.js.new +928 -0
  279. package/ccw/src/templates/dashboard-js/views/memory.js +1221 -0
  280. package/ccw/src/templates/dashboard-js/views/prompt-history.js +713 -0
  281. package/ccw/src/templates/dashboard-js/views/rules-manager.js +828 -0
  282. package/ccw/src/templates/dashboard-js/views/session-detail.js +54 -53
  283. package/ccw/src/templates/dashboard-js/views/skills-manager.js +819 -0
  284. package/ccw/src/templates/dashboard.html +185 -85
  285. package/ccw/src/templates/hooks-config-example.json +60 -0
  286. package/ccw/src/tools/classify-folders.ts +245 -0
  287. package/ccw/src/tools/cli-config-manager.ts +268 -0
  288. package/ccw/src/tools/cli-executor.ts +2014 -0
  289. package/ccw/src/tools/cli-history-store.ts +1195 -0
  290. package/ccw/src/tools/codex-lens.ts +1141 -0
  291. package/ccw/src/tools/{convert-tokens-to-css.js → convert-tokens-to-css.ts} +73 -23
  292. package/ccw/src/tools/core-memory.ts +444 -0
  293. package/ccw/src/tools/detect-changed-modules.ts +325 -0
  294. package/ccw/src/tools/{discover-design-files.js → discover-design-files.ts} +74 -24
  295. package/ccw/src/tools/edit-file.ts +568 -0
  296. package/ccw/src/tools/{generate-module-docs.js → generate-module-docs.ts} +207 -185
  297. package/ccw/src/tools/{get-modules-by-depth.js → get-modules-by-depth.ts} +120 -79
  298. package/ccw/src/tools/index.ts +370 -0
  299. package/ccw/src/tools/native-session-discovery.ts +795 -0
  300. package/ccw/src/tools/notifier.ts +129 -0
  301. package/ccw/src/tools/read-file.ts +410 -0
  302. package/ccw/src/tools/resume-strategy.ts +345 -0
  303. package/ccw/src/tools/session-content-parser.ts +619 -0
  304. package/ccw/src/tools/session-manager.ts +1026 -0
  305. package/ccw/src/tools/smart-context.ts +228 -0
  306. package/ccw/src/tools/smart-search.ts +2065 -0
  307. package/ccw/src/tools/smart-search.ts.backup +1233 -0
  308. package/ccw/src/tools/storage-manager.ts +455 -0
  309. package/ccw/src/tools/write-file.ts +222 -0
  310. package/ccw/src/types/config.ts +11 -0
  311. package/ccw/src/types/index.ts +3 -0
  312. package/ccw/src/types/session.ts +25 -0
  313. package/ccw/src/types/tool.ts +41 -0
  314. package/ccw/src/utils/{browser-launcher.js → browser-launcher.ts} +10 -8
  315. package/ccw/src/utils/file-utils.ts +48 -0
  316. package/ccw/src/utils/{path-resolver.js → path-resolver.ts} +114 -78
  317. package/ccw/src/utils/path-validator.ts +153 -0
  318. package/ccw/src/utils/{ui.js → ui.ts} +32 -25
  319. package/codex-lens/pyproject.toml +48 -0
  320. package/codex-lens/src/codexlens/.workflow/.cli-history/history.db +0 -0
  321. package/codex-lens/src/codexlens/__init__.py +28 -0
  322. package/codex-lens/src/codexlens/__main__.py +14 -0
  323. package/codex-lens/src/codexlens/__pycache__/__init__.cpython-313.pyc +0 -0
  324. package/codex-lens/src/codexlens/__pycache__/__main__.cpython-313.pyc +0 -0
  325. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  326. package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
  327. package/codex-lens/src/codexlens/__pycache__/errors.cpython-313.pyc +0 -0
  328. package/codex-lens/src/codexlens/cli/__init__.py +27 -0
  329. package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-313.pyc +0 -0
  330. package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
  331. package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
  332. package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
  333. package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
  334. package/codex-lens/src/codexlens/cli/commands.py +1931 -0
  335. package/codex-lens/src/codexlens/cli/embedding_manager.py +620 -0
  336. package/codex-lens/src/codexlens/cli/model_manager.py +289 -0
  337. package/codex-lens/src/codexlens/cli/output.py +124 -0
  338. package/codex-lens/src/codexlens/config.py +201 -0
  339. package/codex-lens/src/codexlens/entities.py +121 -0
  340. package/codex-lens/src/codexlens/errors.py +55 -0
  341. package/codex-lens/src/codexlens/indexing/README.md +77 -0
  342. package/codex-lens/src/codexlens/indexing/__init__.py +4 -0
  343. package/codex-lens/src/codexlens/indexing/__pycache__/__init__.cpython-313.pyc +0 -0
  344. package/codex-lens/src/codexlens/indexing/__pycache__/symbol_extractor.cpython-313.pyc +0 -0
  345. package/codex-lens/src/codexlens/indexing/symbol_extractor.py +243 -0
  346. package/codex-lens/src/codexlens/parsers/__init__.py +8 -0
  347. package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-313.pyc +0 -0
  348. package/codex-lens/src/codexlens/parsers/__pycache__/encoding.cpython-313.pyc +0 -0
  349. package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-313.pyc +0 -0
  350. package/codex-lens/src/codexlens/parsers/__pycache__/tokenizer.cpython-313.pyc +0 -0
  351. package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
  352. package/codex-lens/src/codexlens/parsers/encoding.py +202 -0
  353. package/codex-lens/src/codexlens/parsers/factory.py +256 -0
  354. package/codex-lens/src/codexlens/parsers/tokenizer.py +98 -0
  355. package/codex-lens/src/codexlens/parsers/treesitter_parser.py +335 -0
  356. package/codex-lens/src/codexlens/search/__init__.py +15 -0
  357. package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-313.pyc +0 -0
  358. package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
  359. package/codex-lens/src/codexlens/search/__pycache__/enrichment.cpython-313.pyc +0 -0
  360. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  361. package/codex-lens/src/codexlens/search/__pycache__/query_parser.cpython-313.pyc +0 -0
  362. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  363. package/codex-lens/src/codexlens/search/chain_search.py +647 -0
  364. package/codex-lens/src/codexlens/search/enrichment.py +150 -0
  365. package/codex-lens/src/codexlens/search/hybrid_search.py +313 -0
  366. package/codex-lens/src/codexlens/search/query_parser.py +242 -0
  367. package/codex-lens/src/codexlens/search/ranking.py +274 -0
  368. package/codex-lens/src/codexlens/semantic/__init__.py +39 -0
  369. package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
  370. package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-313.pyc +0 -0
  371. package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
  372. package/codex-lens/src/codexlens/semantic/__pycache__/code_extractor.cpython-313.pyc +0 -0
  373. package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
  374. package/codex-lens/src/codexlens/semantic/__pycache__/graph_analyzer.cpython-313.pyc +0 -0
  375. package/codex-lens/src/codexlens/semantic/__pycache__/llm_enhancer.cpython-313.pyc +0 -0
  376. package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
  377. package/codex-lens/src/codexlens/semantic/ann_index.py +414 -0
  378. package/codex-lens/src/codexlens/semantic/chunker.py +448 -0
  379. package/codex-lens/src/codexlens/semantic/code_extractor.py +274 -0
  380. package/codex-lens/src/codexlens/semantic/embedder.py +185 -0
  381. package/codex-lens/src/codexlens/semantic/vector_store.py +955 -0
  382. package/codex-lens/src/codexlens/storage/__init__.py +29 -0
  383. package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-313.pyc +0 -0
  384. package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
  385. package/codex-lens/src/codexlens/storage/__pycache__/file_cache.cpython-313.pyc +0 -0
  386. package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
  387. package/codex-lens/src/codexlens/storage/__pycache__/migration_manager.cpython-313.pyc +0 -0
  388. package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
  389. package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-313.pyc +0 -0
  390. package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-313.pyc +0 -0
  391. package/codex-lens/src/codexlens/storage/__pycache__/sqlite_utils.cpython-313.pyc +0 -0
  392. package/codex-lens/src/codexlens/storage/dir_index.py +1850 -0
  393. package/codex-lens/src/codexlens/storage/file_cache.py +32 -0
  394. package/codex-lens/src/codexlens/storage/index_tree.py +776 -0
  395. package/codex-lens/src/codexlens/storage/migration_manager.py +154 -0
  396. package/codex-lens/src/codexlens/storage/migrations/__init__.py +1 -0
  397. package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
  398. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_001_normalize_keywords.cpython-313.pyc +0 -0
  399. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_002_add_token_metadata.cpython-313.pyc +0 -0
  400. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_003_code_relationships.cpython-313.pyc +0 -0
  401. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
  402. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_005_cleanup_unused_fields.cpython-313.pyc +0 -0
  403. package/codex-lens/src/codexlens/storage/migrations/migration_001_normalize_keywords.py +123 -0
  404. package/codex-lens/src/codexlens/storage/migrations/migration_002_add_token_metadata.py +48 -0
  405. package/codex-lens/src/codexlens/storage/migrations/migration_004_dual_fts.py +232 -0
  406. package/codex-lens/src/codexlens/storage/migrations/migration_005_cleanup_unused_fields.py +196 -0
  407. package/codex-lens/src/codexlens/storage/path_mapper.py +274 -0
  408. package/codex-lens/src/codexlens/storage/registry.py +670 -0
  409. package/codex-lens/src/codexlens/storage/sqlite_store.py +576 -0
  410. package/codex-lens/src/codexlens/storage/sqlite_utils.py +64 -0
  411. package/package.json +4 -1
  412. package/.claude/commands/memory/tech-research.md +0 -477
  413. package/.claude/scripts/classify-folders.sh +0 -39
  414. package/.claude/scripts/convert_tokens_to_css.sh +0 -229
  415. package/.claude/scripts/detect_changed_modules.sh +0 -161
  416. package/.claude/scripts/discover-design-files.sh +0 -87
  417. package/.claude/scripts/extract-animations.js +0 -243
  418. package/.claude/scripts/extract-computed-styles.js +0 -118
  419. package/.claude/scripts/extract-layout-structure.js +0 -411
  420. package/.claude/scripts/generate_module_docs.sh +0 -717
  421. package/.claude/scripts/get_modules_by_depth.sh +0 -170
  422. package/.claude/scripts/ui-generate-preview.sh +0 -395
  423. package/.claude/scripts/ui-instantiate-prototypes.sh +0 -815
  424. package/.claude/scripts/update_module_claude.sh +0 -337
  425. package/.claude/workflows/context-search-strategy.md +0 -77
  426. package/.claude/workflows/intelligent-tools-strategy.md +0 -662
  427. package/ccw/src/cli.js +0 -119
  428. package/ccw/src/commands/install.js +0 -324
  429. package/ccw/src/commands/tool.js +0 -138
  430. package/ccw/src/core/data-aggregator.js +0 -409
  431. package/ccw/src/core/server.js +0 -2063
  432. package/ccw/src/core/session-scanner.js +0 -235
  433. package/ccw/src/tools/classify-folders.js +0 -204
  434. package/ccw/src/tools/detect-changed-modules.js +0 -288
  435. package/ccw/src/tools/edit-file.js +0 -266
  436. package/ccw/src/tools/index.js +0 -176
  437. package/ccw/src/utils/file-utils.js +0 -48
@@ -0,0 +1,1221 @@
1
+ // Memory Module View
2
+ // Three-column layout: Context Hotspots | Memory Graph | Recent Context
3
+
4
+ // ========== Memory State ==========
5
+ var memoryStats = null;
6
+ var memoryGraphData = null;
7
+ var recentContext = [];
8
+ var memoryTimeFilter = 'all'; // 'today', 'week', 'all'
9
+ var selectedNode = null;
10
+ var activeMemoryEnabled = false;
11
+ var activeMemoryStatus = null;
12
+ var activeMemoryConfig = {
13
+ interval: 'manual', // manual, 5, 15, 30, 60 (minutes)
14
+ tool: 'gemini' // gemini, qwen
15
+ };
16
+ var activeMemorySyncTimer = null; // Timer for automatic periodic sync
17
+ var insightsHistory = []; // Insights analysis history
18
+ var selectedInsight = null; // Currently selected insight for detail view
19
+
20
+ // ========== Main Render Function ==========
21
+ async function renderMemoryView() {
22
+ var container = document.getElementById('mainContent');
23
+ if (!container) return;
24
+
25
+ // Hide stats grid and search for memory view
26
+ var statsGrid = document.getElementById('statsGrid');
27
+ var searchInput = document.getElementById('searchInput');
28
+ if (statsGrid) statsGrid.style.display = 'none';
29
+ if (searchInput) searchInput.parentElement.style.display = 'none';
30
+
31
+ // Show loading state
32
+ container.innerHTML = '<div class="memory-view loading">' +
33
+ '<div class="loading-spinner"><i data-lucide="loader-2" class="w-8 h-8 animate-spin"></i></div>' +
34
+ '<p>' + t('common.loading') + '</p>' +
35
+ '</div>';
36
+
37
+ // Load data
38
+ await Promise.all([
39
+ loadMemoryStats(),
40
+ loadMemoryGraph(),
41
+ loadRecentContext(),
42
+ loadActiveMemoryStatus(),
43
+ loadInsightsHistory()
44
+ ]);
45
+
46
+ // Render layout with Active Memory header
47
+ container.innerHTML = '<div class="memory-view">' +
48
+ '<div class="memory-header">' +
49
+ '<div class="memory-header-left">' +
50
+ '<h2><i data-lucide="brain" class="w-5 h-5"></i> ' + t('memory.title') + '</h2>' +
51
+ '</div>' +
52
+ '<div class="memory-header-right">' +
53
+ renderActiveMemoryControls() +
54
+ '</div>' +
55
+ '</div>' +
56
+ '<div class="memory-columns">' +
57
+ '<div class="memory-column left" id="memory-hotspots"></div>' +
58
+ '<div class="memory-column center" id="memory-graph"></div>' +
59
+ '<div class="memory-column right" id="memory-context"></div>' +
60
+ '</div>' +
61
+ '</div>';
62
+
63
+ // Render each column
64
+ renderHotspotsColumn();
65
+ renderGraphColumn();
66
+ renderContextColumn();
67
+
68
+ // Initialize Lucide icons
69
+ if (window.lucide) lucide.createIcons();
70
+ }
71
+
72
+ function renderActiveMemoryControls() {
73
+ var html = '<div class="active-memory-controls">' +
74
+ '<div class="active-memory-toggle">' +
75
+ '<span class="toggle-label">' + t('memory.activeMemory') + '</span>' +
76
+ '<label class="toggle-switch">' +
77
+ '<input type="checkbox" id="activeMemorySwitch" ' + (activeMemoryEnabled ? 'checked' : '') + ' onchange="toggleActiveMemory(this.checked)">' +
78
+ '<span class="toggle-slider"></span>' +
79
+ '</label>' +
80
+ (activeMemoryEnabled ? '<span class="toggle-status active"><i data-lucide="zap" class="w-3 h-3"></i> ' + t('memory.active') + '</span>' : '<span class="toggle-status">' + t('memory.inactive') + '</span>') +
81
+ '</div>';
82
+
83
+ if (activeMemoryEnabled) {
84
+ var isAutoSync = activeMemoryConfig.interval !== 'manual';
85
+ html += '<div class="active-memory-config">' +
86
+ // Interval selector
87
+ '<div class="config-item">' +
88
+ '<label>' + t('memory.interval') + '</label>' +
89
+ '<select id="activeMemoryInterval" onchange="updateActiveMemoryConfig(\'interval\', this.value)">' +
90
+ '<option value="manual"' + (activeMemoryConfig.interval === 'manual' ? ' selected' : '') + '>' + t('memory.intervalManual') + '</option>' +
91
+ '<option value="5"' + (activeMemoryConfig.interval === '5' ? ' selected' : '') + '>5 ' + t('memory.minutes') + '</option>' +
92
+ '<option value="15"' + (activeMemoryConfig.interval === '15' ? ' selected' : '') + '>15 ' + t('memory.minutes') + '</option>' +
93
+ '<option value="30"' + (activeMemoryConfig.interval === '30' ? ' selected' : '') + '>30 ' + t('memory.minutes') + '</option>' +
94
+ '<option value="60"' + (activeMemoryConfig.interval === '60' ? ' selected' : '') + '>60 ' + t('memory.minutes') + '</option>' +
95
+ '</select>' +
96
+ '</div>' +
97
+ // CLI tool selector
98
+ '<div class="config-item">' +
99
+ '<label>' + t('memory.cliTool') + '</label>' +
100
+ '<select id="activeMemoryCli" onchange="updateActiveMemoryConfig(\'tool\', this.value)">' +
101
+ '<option value="gemini"' + (activeMemoryConfig.tool === 'gemini' ? ' selected' : '') + '>Gemini</option>' +
102
+ '<option value="qwen"' + (activeMemoryConfig.tool === 'qwen' ? ' selected' : '') + '>Qwen</option>' +
103
+ '</select>' +
104
+ '</div>' +
105
+ // Auto-sync indicator
106
+ (isAutoSync ? '<div class="auto-sync-indicator"><i data-lucide="timer" class="w-3 h-3"></i> ' + t('memory.autoSyncActive') + '</div>' : '') +
107
+ '</div>' +
108
+ // Sync button and status
109
+ '<div class="active-memory-actions">' +
110
+ '<button class="btn-icon btn-sync" onclick="syncActiveMemory()" title="' + t('memory.syncNow') + '">' +
111
+ '<i data-lucide="refresh-cw" class="w-4 h-4"></i>' +
112
+ '</button>' +
113
+ (activeMemoryStatus && activeMemoryStatus.lastSync ?
114
+ '<span class="last-sync">' + t('memory.lastSync') + ': ' + formatTimestamp(activeMemoryStatus.lastSync) + '</span>' : '') +
115
+ '</div>';
116
+ }
117
+
118
+ html += '</div>';
119
+ return html;
120
+ }
121
+
122
+ // ========== Data Loading ==========
123
+ async function loadMemoryStats() {
124
+ try {
125
+ var response = await fetch('/api/memory/stats?filter=' + memoryTimeFilter);
126
+ if (!response.ok) throw new Error('Failed to load memory stats');
127
+ var data = await response.json();
128
+ memoryStats = data.stats || { mostRead: [], mostEdited: [] };
129
+ return memoryStats;
130
+ } catch (err) {
131
+ console.error('Failed to load memory stats:', err);
132
+ memoryStats = { mostRead: [], mostEdited: [] };
133
+ return memoryStats;
134
+ }
135
+ }
136
+
137
+ async function loadMemoryGraph() {
138
+ try {
139
+ var response = await fetch('/api/memory/graph');
140
+ if (!response.ok) throw new Error('Failed to load memory graph');
141
+ var data = await response.json();
142
+ memoryGraphData = data.graph || { nodes: [], edges: [] };
143
+ return memoryGraphData;
144
+ } catch (err) {
145
+ console.error('Failed to load memory graph:', err);
146
+ memoryGraphData = { nodes: [], edges: [] };
147
+ return memoryGraphData;
148
+ }
149
+ }
150
+
151
+ async function loadRecentContext() {
152
+ try {
153
+ var response = await fetch('/api/memory/recent');
154
+ if (!response.ok) throw new Error('Failed to load recent context');
155
+ var data = await response.json();
156
+ recentContext = data.recent || [];
157
+ return recentContext;
158
+ } catch (err) {
159
+ console.error('Failed to load recent context:', err);
160
+ recentContext = [];
161
+ return [];
162
+ }
163
+ }
164
+
165
+ async function loadInsightsHistory() {
166
+ try {
167
+ var response = await fetch('/api/memory/insights?limit=10');
168
+ if (!response.ok) throw new Error('Failed to load insights history');
169
+ var data = await response.json();
170
+ insightsHistory = data.insights || [];
171
+ return insightsHistory;
172
+ } catch (err) {
173
+ console.error('Failed to load insights history:', err);
174
+ insightsHistory = [];
175
+ return [];
176
+ }
177
+ }
178
+
179
+ // ========== Active Memory Functions ==========
180
+ // Timer management for automatic sync
181
+ function startActiveMemorySyncTimer() {
182
+ // Clear any existing timer
183
+ stopActiveMemorySyncTimer();
184
+
185
+ // Only start timer if interval is not manual
186
+ if (activeMemoryConfig.interval === 'manual' || !activeMemoryEnabled) {
187
+ return;
188
+ }
189
+
190
+ var intervalMs = parseInt(activeMemoryConfig.interval, 10) * 60 * 1000; // Convert minutes to ms
191
+ console.log('[ActiveMemory] Starting auto-sync timer:', activeMemoryConfig.interval, 'minutes');
192
+
193
+ activeMemorySyncTimer = setInterval(function() {
194
+ console.log('[ActiveMemory] Auto-sync triggered');
195
+ syncActiveMemory();
196
+ }, intervalMs);
197
+ }
198
+
199
+ function stopActiveMemorySyncTimer() {
200
+ if (activeMemorySyncTimer) {
201
+ console.log('[ActiveMemory] Stopping auto-sync timer');
202
+ clearInterval(activeMemorySyncTimer);
203
+ activeMemorySyncTimer = null;
204
+ }
205
+ }
206
+
207
+ async function loadActiveMemoryStatus() {
208
+ try {
209
+ var response = await fetch('/api/memory/active/status');
210
+ if (!response.ok) throw new Error('Failed to load active memory status');
211
+ var data = await response.json();
212
+ activeMemoryEnabled = data.enabled || false;
213
+ activeMemoryStatus = data.status || null;
214
+ // Load config if available
215
+ if (data.config) {
216
+ activeMemoryConfig = Object.assign(activeMemoryConfig, data.config);
217
+ }
218
+
219
+ // Start timer if active memory is enabled and interval is not manual
220
+ if (activeMemoryEnabled && activeMemoryConfig.interval !== 'manual') {
221
+ startActiveMemorySyncTimer();
222
+ }
223
+
224
+ return data;
225
+ } catch (err) {
226
+ console.error('Failed to load active memory status:', err);
227
+ activeMemoryEnabled = false;
228
+ activeMemoryStatus = null;
229
+ return { enabled: false };
230
+ }
231
+ }
232
+
233
+ async function toggleActiveMemory(enabled) {
234
+ try {
235
+ var response = await fetch('/api/memory/active/toggle', {
236
+ method: 'POST',
237
+ headers: { 'Content-Type': 'application/json' },
238
+ body: JSON.stringify({
239
+ enabled: enabled,
240
+ config: activeMemoryConfig
241
+ })
242
+ });
243
+ if (!response.ok) throw new Error('Failed to toggle active memory');
244
+ var data = await response.json();
245
+ activeMemoryEnabled = data.enabled;
246
+
247
+ // Manage auto-sync timer based on enabled state
248
+ if (activeMemoryEnabled) {
249
+ startActiveMemorySyncTimer();
250
+ } else {
251
+ stopActiveMemorySyncTimer();
252
+ }
253
+
254
+ // Show notification
255
+ if (window.showToast) {
256
+ showToast(enabled ? t('memory.activeMemoryEnabled') : t('memory.activeMemoryDisabled'), 'success');
257
+ }
258
+
259
+ // Re-render the view to update UI
260
+ renderMemoryView();
261
+ } catch (err) {
262
+ console.error('Failed to toggle active memory:', err);
263
+ if (window.showToast) {
264
+ showToast(t('memory.activeMemoryError'), 'error');
265
+ }
266
+ // Revert checkbox state
267
+ var checkbox = document.getElementById('activeMemorySwitch');
268
+ if (checkbox) checkbox.checked = !enabled;
269
+ }
270
+ }
271
+
272
+ async function updateActiveMemoryConfig(key, value) {
273
+ activeMemoryConfig[key] = value;
274
+
275
+ try {
276
+ var response = await fetch('/api/memory/active/config', {
277
+ method: 'POST',
278
+ headers: { 'Content-Type': 'application/json' },
279
+ body: JSON.stringify({ config: activeMemoryConfig })
280
+ });
281
+ if (!response.ok) throw new Error('Failed to update config');
282
+
283
+ // Restart timer if interval changed and active memory is enabled
284
+ if (key === 'interval' && activeMemoryEnabled) {
285
+ startActiveMemorySyncTimer();
286
+ }
287
+
288
+ if (window.showToast) {
289
+ showToast(t('memory.configUpdated'), 'success');
290
+ }
291
+ } catch (err) {
292
+ console.error('Failed to update active memory config:', err);
293
+ if (window.showToast) {
294
+ showToast(t('memory.configError'), 'error');
295
+ }
296
+ }
297
+ }
298
+
299
+ async function syncActiveMemory() {
300
+ var syncBtn = document.querySelector('.btn-sync');
301
+ if (syncBtn) {
302
+ syncBtn.classList.add('syncing');
303
+ syncBtn.disabled = true;
304
+ }
305
+
306
+ try {
307
+ var response = await fetch('/api/memory/active/sync', {
308
+ method: 'POST',
309
+ headers: { 'Content-Type': 'application/json' },
310
+ body: JSON.stringify({
311
+ tool: activeMemoryConfig.tool
312
+ })
313
+ });
314
+ if (!response.ok) throw new Error('Failed to sync active memory');
315
+ var data = await response.json();
316
+
317
+ if (window.showToast) {
318
+ showToast(t('memory.syncComplete') + ' (' + (data.filesAnalyzed || 0) + ' ' + t('memory.filesAnalyzed') + ')', 'success');
319
+ }
320
+
321
+ // Refresh data and update last sync time
322
+ await loadActiveMemoryStatus();
323
+ // Update last sync display without full re-render
324
+ var lastSyncEl = document.querySelector('.last-sync');
325
+ if (lastSyncEl && activeMemoryStatus && activeMemoryStatus.lastSync) {
326
+ lastSyncEl.textContent = t('memory.lastSync') + ': ' + formatTimestamp(activeMemoryStatus.lastSync);
327
+ }
328
+ } catch (err) {
329
+ console.error('Failed to sync active memory:', err);
330
+ if (window.showToast) {
331
+ showToast(t('memory.syncError'), 'error');
332
+ }
333
+ } finally {
334
+ if (syncBtn) {
335
+ syncBtn.classList.remove('syncing');
336
+ syncBtn.disabled = false;
337
+ }
338
+ }
339
+ }
340
+
341
+ // ========== Left Column: Context Hotspots ==========
342
+ function renderHotspotsColumn() {
343
+ var container = document.getElementById('memory-hotspots');
344
+ if (!container) return;
345
+
346
+ var mostRead = memoryStats.mostRead || [];
347
+ var mostEdited = memoryStats.mostEdited || [];
348
+ var mostMentioned = memoryStats.mostMentioned || [];
349
+
350
+ container.innerHTML = '<div class="memory-section">' +
351
+ '<div class="section-header">' +
352
+ '<div class="section-header-left">' +
353
+ '<h3><i data-lucide="flame" class="w-4 h-4"></i> ' + t('memory.contextHotspots') + '</h3>' +
354
+ '</div>' +
355
+ '<div class="section-header-actions">' +
356
+ '<button class="btn-icon" onclick="refreshMemoryData()" title="' + t('common.refresh') + '">' +
357
+ '<i data-lucide="refresh-cw" class="w-4 h-4"></i>' +
358
+ '</button>' +
359
+ '</div>' +
360
+ '</div>' +
361
+ '<div class="memory-filters">' +
362
+ '<button class="filter-btn ' + (memoryTimeFilter === 'today' ? 'active' : '') + '" onclick="setMemoryTimeFilter(\'today\')">' + t('memory.today') + '</button>' +
363
+ '<button class="filter-btn ' + (memoryTimeFilter === 'week' ? 'active' : '') + '" onclick="setMemoryTimeFilter(\'week\')">' + t('memory.week') + '</button>' +
364
+ '<button class="filter-btn ' + (memoryTimeFilter === 'all' ? 'active' : '') + '" onclick="setMemoryTimeFilter(\'all\')">' + t('memory.allTime') + '</button>' +
365
+ '</div>' +
366
+ '<div class="hotspot-lists">' +
367
+ '<div class="hotspot-list-container">' +
368
+ '<h4 class="hotspot-list-title"><i data-lucide="eye" class="w-3.5 h-3.5"></i> ' + t('memory.mostRead') + '</h4>' +
369
+ renderHotspotList(mostRead, 'read') +
370
+ '</div>' +
371
+ '<div class="hotspot-list-container">' +
372
+ '<h4 class="hotspot-list-title"><i data-lucide="pencil" class="w-3.5 h-3.5"></i> ' + t('memory.mostEdited') + '</h4>' +
373
+ renderHotspotList(mostEdited, 'edit') +
374
+ '</div>' +
375
+ '<div class="hotspot-list-container">' +
376
+ '<h4 class="hotspot-list-title"><i data-lucide="message-circle" class="w-3.5 h-3.5"></i> ' + t('memory.mostMentioned') + '</h4>' +
377
+ renderTopicList(mostMentioned) +
378
+ '</div>' +
379
+ '</div>' +
380
+ '</div>';
381
+
382
+ if (window.lucide) lucide.createIcons();
383
+ }
384
+
385
+ function renderHotspotList(items, type) {
386
+ if (!items || items.length === 0) {
387
+ return '<div class="hotspot-empty">' +
388
+ '<i data-lucide="inbox" class="w-5 h-5"></i>' +
389
+ '<p>' + t('memory.noData') + '</p>' +
390
+ '</div>';
391
+ }
392
+
393
+ return '<div class="hotspot-list">' +
394
+ items.map(function(item, index) {
395
+ var heat = item.heat || item.count || 0;
396
+ var heatClass = heat > 50 ? 'high' : heat > 20 ? 'medium' : 'low';
397
+ var path = item.path || item.file || 'Unknown';
398
+ var fileName = path.split('/').pop().split('\\').pop();
399
+
400
+ return '<div class="hotspot-item" onclick="highlightNode(\'' + escapeHtml(path) + '\')">' +
401
+ '<div class="hotspot-rank">' + (index + 1) + '</div>' +
402
+ '<div class="hotspot-info">' +
403
+ '<div class="hotspot-name" title="' + escapeHtml(path) + '">' + escapeHtml(fileName) + '</div>' +
404
+ '<div class="hotspot-path">' + escapeHtml(path.substring(0, path.lastIndexOf(fileName))) + '</div>' +
405
+ '</div>' +
406
+ '<div class="hotspot-heat ' + heatClass + '">' +
407
+ '<span class="heat-badge">' + heat + '</span>' +
408
+ '<i data-lucide="' + (type === 'read' ? 'eye' : 'pencil') + '" class="w-3 h-3"></i>' +
409
+ '</div>' +
410
+ '</div>';
411
+ }).join('') +
412
+ '</div>';
413
+ }
414
+
415
+ function renderTopicList(items) {
416
+ if (!items || items.length === 0) {
417
+ return '<div class="hotspot-empty">' +
418
+ '<i data-lucide="inbox" class="w-5 h-5"></i>' +
419
+ '<p>' + t('memory.noData') + '</p>' +
420
+ '</div>';
421
+ }
422
+
423
+ return '<div class="hotspot-list topic-list">' +
424
+ items.map(function(item, index) {
425
+ var heat = item.heat || item.count || 0;
426
+ var heatClass = heat > 10 ? 'high' : heat > 5 ? 'medium' : 'low';
427
+ var preview = item.preview || item.topic || 'Unknown';
428
+
429
+ return '<div class="hotspot-item topic-item">' +
430
+ '<div class="hotspot-rank">' + (index + 1) + '</div>' +
431
+ '<div class="hotspot-info">' +
432
+ '<div class="hotspot-name topic-preview" title="' + escapeHtml(item.topic || '') + '">' + escapeHtml(preview) + '</div>' +
433
+ '</div>' +
434
+ '<div class="hotspot-heat ' + heatClass + '">' +
435
+ '<span class="heat-badge">' + heat + '</span>' +
436
+ '<i data-lucide="message-circle" class="w-3 h-3"></i>' +
437
+ '</div>' +
438
+ '</div>';
439
+ }).join('') +
440
+ '</div>';
441
+ }
442
+
443
+ // ========== Center Column: Memory Graph ==========
444
+ // Store graph state for zoom/pan
445
+ var graphZoom = null;
446
+ var graphSvg = null;
447
+ var graphGroup = null;
448
+ var graphSimulation = null;
449
+
450
+ function renderGraphColumn() {
451
+ var container = document.getElementById('memory-graph');
452
+ if (!container) return;
453
+
454
+ container.innerHTML = '<div class="memory-section">' +
455
+ '<div class="section-header">' +
456
+ '<div class="section-header-left">' +
457
+ '<h3><i data-lucide="network" class="w-4 h-4"></i> ' + t('memory.memoryGraph') + '</h3>' +
458
+ '<span class="section-count">' + (memoryGraphData.nodes || []).length + ' ' + t('memory.nodes') + '</span>' +
459
+ '</div>' +
460
+ '<div class="section-header-actions graph-controls">' +
461
+ '<button class="btn-icon" onclick="zoomGraphIn()" title="' + t('memory.zoomIn') + '">' +
462
+ '<i data-lucide="zoom-in" class="w-4 h-4"></i>' +
463
+ '</button>' +
464
+ '<button class="btn-icon" onclick="zoomGraphOut()" title="' + t('memory.zoomOut') + '">' +
465
+ '<i data-lucide="zoom-out" class="w-4 h-4"></i>' +
466
+ '</button>' +
467
+ '<button class="btn-icon" onclick="fitGraphToView()" title="' + t('memory.fitView') + '">' +
468
+ '<i data-lucide="maximize-2" class="w-4 h-4"></i>' +
469
+ '</button>' +
470
+ '<button class="btn-icon" onclick="resetGraphView()" title="' + t('memory.resetView') + '">' +
471
+ '<i data-lucide="refresh-cw" class="w-4 h-4"></i>' +
472
+ '</button>' +
473
+ '</div>' +
474
+ '</div>' +
475
+ '<div class="memory-graph-container" id="memoryGraphSvg"></div>' +
476
+ '<div class="memory-graph-legend">' +
477
+ '<div class="legend-item"><span class="legend-dot file"></span> ' + t('memory.file') + '</div>' +
478
+ '<div class="legend-item"><span class="legend-dot module"></span> ' + t('memory.module') + '</div>' +
479
+ '<div class="legend-item"><span class="legend-dot component"></span> ' + t('memory.component') + '</div>' +
480
+ '</div>' +
481
+ '</div>';
482
+
483
+ // Render D3 graph
484
+ renderMemoryGraph(memoryGraphData);
485
+
486
+ if (window.lucide) lucide.createIcons();
487
+ }
488
+
489
+ function renderMemoryGraph(graphData) {
490
+ if (!graphData || !graphData.nodes || graphData.nodes.length === 0) {
491
+ var container = document.getElementById('memoryGraphSvg');
492
+ if (container) {
493
+ container.innerHTML = '<div class="graph-empty-state">' +
494
+ '<i data-lucide="network" class="w-8 h-8"></i>' +
495
+ '<p>' + t('memory.noGraphData') + '</p>' +
496
+ '</div>';
497
+ if (window.lucide) lucide.createIcons();
498
+ }
499
+ return;
500
+ }
501
+
502
+ // Check if D3 is available
503
+ if (typeof d3 === 'undefined') {
504
+ var container = document.getElementById('memoryGraphSvg');
505
+ if (container) {
506
+ container.innerHTML = '<div class="graph-error">' +
507
+ '<i data-lucide="alert-triangle" class="w-6 h-6"></i>' +
508
+ '<p>' + t('memory.d3NotLoaded') + '</p>' +
509
+ '</div>';
510
+ if (window.lucide) lucide.createIcons();
511
+ }
512
+ return;
513
+ }
514
+
515
+ var container = document.getElementById('memoryGraphSvg');
516
+ if (!container) return;
517
+
518
+ var width = container.clientWidth || 600;
519
+ var height = container.clientHeight || 400;
520
+
521
+ // Clear existing
522
+ container.innerHTML = '';
523
+
524
+ // Filter and clean nodes - remove invalid names (like JSON data)
525
+ var cleanNodes = graphData.nodes.filter(function(node) {
526
+ var name = node.name || node.id || '';
527
+ // Filter out JSON-like data, error messages, and very long strings
528
+ if (name.length > 100) return false;
529
+ if (name.includes('"status"') || name.includes('"content"')) return false;
530
+ if (name.includes('"todos"') || name.includes('"activeForm"')) return false;
531
+ if (name.startsWith('{') || name.startsWith('[')) return false;
532
+ // Allow all valid node types: file, module, component
533
+ return true;
534
+ }).map(function(node) {
535
+ // Truncate long names for display
536
+ var displayName = node.name || node.id || 'Unknown';
537
+ if (displayName.length > 25) {
538
+ displayName = displayName.substring(0, 22) + '...';
539
+ }
540
+ return Object.assign({}, node, { displayName: displayName });
541
+ });
542
+
543
+ // Filter edges to only include valid nodes
544
+ var nodeIds = new Set(cleanNodes.map(function(n) { return n.id; }));
545
+ var cleanEdges = graphData.edges.filter(function(edge) {
546
+ var sourceId = typeof edge.source === 'object' ? edge.source.id : edge.source;
547
+ var targetId = typeof edge.target === 'object' ? edge.target.id : edge.target;
548
+ return nodeIds.has(sourceId) && nodeIds.has(targetId);
549
+ });
550
+
551
+ // Create SVG with zoom support
552
+ graphSvg = d3.select('#memoryGraphSvg')
553
+ .append('svg')
554
+ .attr('width', width)
555
+ .attr('height', height)
556
+ .attr('class', 'memory-graph-svg')
557
+ .attr('viewBox', [0, 0, width, height]);
558
+
559
+ // Create a group for zoom/pan transformations
560
+ graphGroup = graphSvg.append('g').attr('class', 'graph-content');
561
+
562
+ // Setup zoom behavior
563
+ graphZoom = d3.zoom()
564
+ .scaleExtent([0.1, 4])
565
+ .on('zoom', function(event) {
566
+ graphGroup.attr('transform', event.transform);
567
+ });
568
+
569
+ graphSvg.call(graphZoom);
570
+
571
+ // Create force simulation
572
+ graphSimulation = d3.forceSimulation(cleanNodes)
573
+ .force('link', d3.forceLink(cleanEdges).id(function(d) { return d.id; }).distance(80))
574
+ .force('charge', d3.forceManyBody().strength(-200))
575
+ .force('center', d3.forceCenter(width / 2, height / 2))
576
+ .force('collision', d3.forceCollide().radius(function(d) { return Math.max(15, (d.heat || 10) + 10); }))
577
+ .force('x', d3.forceX(width / 2).strength(0.05))
578
+ .force('y', d3.forceY(height / 2).strength(0.05));
579
+
580
+ // Draw edges
581
+ var link = graphGroup.append('g')
582
+ .attr('class', 'graph-links')
583
+ .selectAll('line')
584
+ .data(cleanEdges)
585
+ .enter()
586
+ .append('line')
587
+ .attr('class', 'graph-edge')
588
+ .attr('stroke-width', function(d) { return Math.sqrt(d.weight || 1); });
589
+
590
+ // Draw nodes
591
+ var node = graphGroup.append('g')
592
+ .attr('class', 'graph-nodes')
593
+ .selectAll('g')
594
+ .data(cleanNodes)
595
+ .enter()
596
+ .append('g')
597
+ .attr('class', function(d) { return 'graph-node-group ' + (d.type || 'file'); })
598
+ .call(d3.drag()
599
+ .on('start', dragstarted)
600
+ .on('drag', dragged)
601
+ .on('end', dragended))
602
+ .on('click', function(event, d) {
603
+ event.stopPropagation();
604
+ selectNode(d);
605
+ });
606
+
607
+ // Add circles to nodes
608
+ node.append('circle')
609
+ .attr('class', function(d) { return 'graph-node ' + (d.type || 'file'); })
610
+ .attr('r', function(d) { return Math.max(8, Math.min(20, (d.heat || 10))); })
611
+ .attr('data-id', function(d) { return d.id; });
612
+
613
+ // Add labels to nodes
614
+ node.append('text')
615
+ .attr('class', 'graph-label')
616
+ .text(function(d) {
617
+ // Show file count for modules
618
+ if (d.type === 'module' && d.fileCount) {
619
+ return d.displayName + ' (' + d.fileCount + ')';
620
+ }
621
+ return d.displayName;
622
+ })
623
+ .attr('x', function(d) { return Math.max(10, (d.heat || 10)) + 4; })
624
+ .attr('y', 4)
625
+ .attr('font-size', '11px');
626
+
627
+ // Update positions on simulation tick
628
+ graphSimulation.on('tick', function() {
629
+ link
630
+ .attr('x1', function(d) { return d.source.x; })
631
+ .attr('y1', function(d) { return d.source.y; })
632
+ .attr('x2', function(d) { return d.target.x; })
633
+ .attr('y2', function(d) { return d.target.y; });
634
+
635
+ node.attr('transform', function(d) {
636
+ return 'translate(' + d.x + ',' + d.y + ')';
637
+ });
638
+ });
639
+
640
+ // Auto-fit after simulation stabilizes
641
+ graphSimulation.on('end', function() {
642
+ fitGraphToView();
643
+ });
644
+
645
+ // Also fit after initial layout
646
+ setTimeout(function() {
647
+ fitGraphToView();
648
+ }, 1000);
649
+
650
+ // Drag functions
651
+ function dragstarted(event, d) {
652
+ if (!event.active) graphSimulation.alphaTarget(0.3).restart();
653
+ d.fx = d.x;
654
+ d.fy = d.y;
655
+ }
656
+
657
+ function dragged(event, d) {
658
+ d.fx = event.x;
659
+ d.fy = event.y;
660
+ }
661
+
662
+ function dragended(event, d) {
663
+ if (!event.active) graphSimulation.alphaTarget(0);
664
+ d.fx = null;
665
+ d.fy = null;
666
+ }
667
+ }
668
+
669
+ // ========== Graph Zoom Controls ==========
670
+ function zoomGraphIn() {
671
+ if (graphSvg && graphZoom) {
672
+ graphSvg.transition().duration(300).call(graphZoom.scaleBy, 1.3);
673
+ }
674
+ }
675
+
676
+ function zoomGraphOut() {
677
+ if (graphSvg && graphZoom) {
678
+ graphSvg.transition().duration(300).call(graphZoom.scaleBy, 0.7);
679
+ }
680
+ }
681
+
682
+ function fitGraphToView() {
683
+ if (!graphSvg || !graphGroup || !graphZoom) return;
684
+
685
+ var container = document.getElementById('memoryGraphSvg');
686
+ if (!container) return;
687
+
688
+ var width = container.clientWidth || 600;
689
+ var height = container.clientHeight || 400;
690
+
691
+ // Get the bounds of all nodes
692
+ var bounds = graphGroup.node().getBBox();
693
+ if (bounds.width === 0 || bounds.height === 0) return;
694
+
695
+ // Calculate scale to fit with padding
696
+ var padding = 40;
697
+ var scale = Math.min(
698
+ (width - padding * 2) / bounds.width,
699
+ (height - padding * 2) / bounds.height
700
+ );
701
+ scale = Math.min(Math.max(scale, 0.2), 2); // Clamp scale between 0.2 and 2
702
+
703
+ // Calculate translation to center
704
+ var tx = (width - bounds.width * scale) / 2 - bounds.x * scale;
705
+ var ty = (height - bounds.height * scale) / 2 - bounds.y * scale;
706
+
707
+ // Apply transform with animation
708
+ graphSvg.transition()
709
+ .duration(500)
710
+ .call(graphZoom.transform, d3.zoomIdentity.translate(tx, ty).scale(scale));
711
+ }
712
+
713
+ function centerGraphOnNode(nodeId) {
714
+ if (!graphSvg || !graphGroup || !graphZoom) return;
715
+
716
+ var container = document.getElementById('memoryGraphSvg');
717
+ if (!container) return;
718
+
719
+ var width = container.clientWidth || 600;
720
+ var height = container.clientHeight || 400;
721
+
722
+ // Find the node
723
+ var nodeData = null;
724
+ graphGroup.selectAll('.graph-node-group').each(function(d) {
725
+ if (d.id === nodeId) nodeData = d;
726
+ });
727
+
728
+ if (!nodeData || nodeData.x === undefined) return;
729
+
730
+ // Calculate translation to center on node
731
+ var scale = 1.2;
732
+ var tx = width / 2 - nodeData.x * scale;
733
+ var ty = height / 2 - nodeData.y * scale;
734
+
735
+ graphSvg.transition()
736
+ .duration(500)
737
+ .call(graphZoom.transform, d3.zoomIdentity.translate(tx, ty).scale(scale));
738
+ }
739
+
740
+ function selectNode(node) {
741
+ selectedNode = node;
742
+
743
+ // Highlight in graph
744
+ if (graphGroup) {
745
+ graphGroup.selectAll('.graph-node').classed('selected', false);
746
+ graphGroup.selectAll('.graph-node[data-id="' + node.id + '"]').classed('selected', true);
747
+ }
748
+
749
+ // Center graph on selected node
750
+ centerGraphOnNode(node.id);
751
+
752
+ // Show node details in context column
753
+ showNodeDetails(node);
754
+ }
755
+
756
+ function highlightNode(path) {
757
+ var node = memoryGraphData.nodes.find(function(n) { return n.path === path || n.id === path; });
758
+ if (node) {
759
+ selectNode(node);
760
+ }
761
+ }
762
+
763
+ function resetGraphView() {
764
+ selectedNode = null;
765
+ if (graphGroup) {
766
+ graphGroup.selectAll('.graph-node').classed('selected', false);
767
+ }
768
+ fitGraphToView();
769
+ renderContextColumn();
770
+ }
771
+
772
+ // ========== Right Column: Recent Context ==========
773
+ function renderContextColumn() {
774
+ var container = document.getElementById('memory-context');
775
+ if (!container) return;
776
+
777
+ if (selectedNode) {
778
+ showNodeDetails(selectedNode);
779
+ return;
780
+ }
781
+
782
+ container.innerHTML = '<div class="memory-section">' +
783
+ '<div class="section-header">' +
784
+ '<div class="section-header-left">' +
785
+ '<h3><i data-lucide="clock" class="w-4 h-4"></i> ' + t('memory.recentContext') + '</h3>' +
786
+ '<span class="section-count">' + recentContext.length + ' ' + t('memory.activities') + '</span>' +
787
+ '</div>' +
788
+ '</div>' +
789
+ '<div class="context-search">' +
790
+ '<input type="text" id="contextSearchInput" class="context-search-input" placeholder="' + t('memory.searchContext') + '" onkeyup="filterRecentContext(this.value)">' +
791
+ '<i data-lucide="search" class="w-4 h-4 search-icon"></i>' +
792
+ '</div>' +
793
+ renderContextTimeline(recentContext) +
794
+ renderContextStats() +
795
+ '</div>';
796
+
797
+ if (window.lucide) lucide.createIcons();
798
+ }
799
+
800
+ function renderContextTimeline(prompts) {
801
+ if (!prompts || prompts.length === 0) {
802
+ return '<div class="context-empty">' +
803
+ '<i data-lucide="inbox" class="w-6 h-6"></i>' +
804
+ '<p>' + t('memory.noRecentActivity') + '</p>' +
805
+ '</div>';
806
+ }
807
+
808
+ return '<div class="context-timeline">' +
809
+ prompts.map(function(item, index) {
810
+ var timestamp = item.timestamp ? formatTimestamp(item.timestamp) : 'Unknown time';
811
+ var type = item.type || 'unknown';
812
+ var typeIcon = type === 'read' ? 'eye' : type === 'write' ? 'pencil' : type === 'edit' ? 'pencil' : 'file-text';
813
+ var files = item.files || [];
814
+ var description = item.prompt || item.description || 'No description';
815
+
816
+ return '<div class="timeline-item" data-index="' + index + '" onclick="toggleTimelineItem(this)">' +
817
+ '<div class="timeline-icon ' + type + '">' +
818
+ '<i data-lucide="' + typeIcon + '" class="w-3.5 h-3.5"></i>' +
819
+ '</div>' +
820
+ '<div class="timeline-content">' +
821
+ '<div class="timeline-header">' +
822
+ '<span class="timeline-type">' + escapeHtml(type.charAt(0).toUpperCase() + type.slice(1)) + '</span>' +
823
+ '<span class="timeline-time">' + timestamp + '</span>' +
824
+ '</div>' +
825
+ '<div class="timeline-prompt">' + escapeHtml(description) + '</div>' +
826
+ (files.length > 0 ? '<div class="timeline-files">' +
827
+ files.map(function(f) {
828
+ return '<span class="file-tag" onclick="event.stopPropagation(); highlightNode(\'' + escapeHtml(f) + '\')">' +
829
+ '<i data-lucide="file" class="w-3 h-3"></i> ' + escapeHtml(f.split('/').pop().split('\\').pop()) +
830
+ '</span>';
831
+ }).join('') +
832
+ '</div>' : '') +
833
+ '</div>' +
834
+ '</div>';
835
+ }).join('') +
836
+ '</div>';
837
+ }
838
+
839
+ /**
840
+ * Toggle timeline item expansion
841
+ */
842
+ function toggleTimelineItem(element) {
843
+ element.classList.toggle('expanded');
844
+ }
845
+
846
+ function renderContextStats() {
847
+ var totalReads = recentContext.filter(function(c) { return c.type === 'read'; }).length;
848
+ var totalEdits = recentContext.filter(function(c) { return c.type === 'edit' || c.type === 'write'; }).length;
849
+ var totalMentions = recentContext.filter(function(c) { return c.type === 'mention'; }).length;
850
+
851
+ return '<div class="context-stats">' +
852
+ '<div class="context-stat-item">' +
853
+ '<i data-lucide="eye" class="w-4 h-4"></i>' +
854
+ '<span class="stat-label">' + t('memory.reads') + '</span>' +
855
+ '<span class="stat-value">' + totalReads + '</span>' +
856
+ '</div>' +
857
+ '<div class="context-stat-item">' +
858
+ '<i data-lucide="pencil" class="w-4 h-4"></i>' +
859
+ '<span class="stat-label">' + t('memory.edits') + '</span>' +
860
+ '<span class="stat-value">' + totalEdits + '</span>' +
861
+ '</div>' +
862
+ '<div class="context-stat-item">' +
863
+ '<i data-lucide="message-square" class="w-4 h-4"></i>' +
864
+ '<span class="stat-label">' + t('memory.mentions') + '</span>' +
865
+ '<span class="stat-value">' + totalMentions + '</span>' +
866
+ '</div>' +
867
+ '</div>';
868
+ }
869
+
870
+ function showNodeDetails(node) {
871
+ var container = document.getElementById('memory-context');
872
+ if (!container) return;
873
+
874
+ var associations = memoryGraphData.edges
875
+ .filter(function(e) { return e.source.id === node.id || e.target.id === node.id; })
876
+ .map(function(e) {
877
+ var other = e.source.id === node.id ? e.target : e.source;
878
+ return { node: other, weight: e.weight || 1 };
879
+ })
880
+ .sort(function(a, b) { return b.weight - a.weight; });
881
+
882
+ container.innerHTML = '<div class="memory-section">' +
883
+ '<div class="section-header">' +
884
+ '<div class="section-header-left">' +
885
+ '<h3><i data-lucide="info" class="w-4 h-4"></i> ' + t('memory.nodeDetails') + '</h3>' +
886
+ '</div>' +
887
+ '<div class="section-header-actions">' +
888
+ '<button class="btn-icon" onclick="resetGraphView()" title="' + t('common.close') + '">' +
889
+ '<i data-lucide="x" class="w-4 h-4"></i>' +
890
+ '</button>' +
891
+ '</div>' +
892
+ '</div>' +
893
+ '<div class="node-details">' +
894
+ '<div class="node-detail-header">' +
895
+ '<div class="node-detail-icon ' + (node.type || 'file') + '">' +
896
+ '<i data-lucide="' + (node.type === 'module' ? 'package' : node.type === 'component' ? 'box' : 'file') + '" class="w-5 h-5"></i>' +
897
+ '</div>' +
898
+ '<div class="node-detail-info">' +
899
+ '<div class="node-detail-name">' + escapeHtml(node.name || node.id) + '</div>' +
900
+ '<div class="node-detail-path">' + escapeHtml(node.path || node.id) + '</div>' +
901
+ '</div>' +
902
+ '</div>' +
903
+ '<div class="node-detail-stats">' +
904
+ '<div class="detail-stat">' +
905
+ '<span class="detail-stat-label">' + t('memory.heat') + '</span>' +
906
+ '<span class="detail-stat-value">' + (node.heat || 0) + '</span>' +
907
+ '</div>' +
908
+ '<div class="detail-stat">' +
909
+ '<span class="detail-stat-label">' + t('memory.associations') + '</span>' +
910
+ '<span class="detail-stat-value">' + associations.length + '</span>' +
911
+ '</div>' +
912
+ '<div class="detail-stat">' +
913
+ '<span class="detail-stat-label">' + t('memory.type') + '</span>' +
914
+ '<span class="detail-stat-value">' + (node.type || 'file') + '</span>' +
915
+ '</div>' +
916
+ '</div>' +
917
+ (associations.length > 0 ? '<div class="node-associations">' +
918
+ '<h4 class="associations-title">' + t('memory.relatedNodes') + '</h4>' +
919
+ '<div class="associations-list">' +
920
+ associations.slice(0, 10).map(function(a) {
921
+ return '<div class="association-item" onclick="selectNode(' + JSON.stringify(a.node).replace(/"/g, '&quot;') + ')">' +
922
+ '<div class="association-node">' +
923
+ '<i data-lucide="' + (a.node.type === 'module' ? 'package' : a.node.type === 'component' ? 'box' : 'file') + '" class="w-3.5 h-3.5"></i>' +
924
+ '<span>' + escapeHtml(a.node.name || a.node.id) + '</span>' +
925
+ '</div>' +
926
+ '<div class="association-weight">' + a.weight + '</div>' +
927
+ '</div>';
928
+ }).join('') +
929
+ (associations.length > 10 ? '<div class="associations-more">+' + (associations.length - 10) + ' more</div>' : '') +
930
+ '</div>' +
931
+ '</div>' : '<div class="node-no-associations">' + t('memory.noAssociations') + '</div>') +
932
+ '</div>' +
933
+ '</div>';
934
+
935
+ if (window.lucide) lucide.createIcons();
936
+ }
937
+
938
+ // ========== Insights Section ==========
939
+ function renderInsightsSection() {
940
+ var container = document.getElementById('memory-insights');
941
+ if (!container) return;
942
+
943
+ container.innerHTML = '<div class="insights-section">' +
944
+ '<div class="section-header">' +
945
+ '<div class="section-header-left">' +
946
+ '<h3><i data-lucide="lightbulb" class="w-4 h-4"></i> ' + t('memory.insightsHistory') + '</h3>' +
947
+ '<span class="section-count">' + insightsHistory.length + ' ' + t('memory.analyses') + '</span>' +
948
+ '</div>' +
949
+ '<div class="section-header-actions">' +
950
+ '<button class="btn-icon" onclick="refreshInsightsHistory()" title="' + t('common.refresh') + '">' +
951
+ '<i data-lucide="refresh-cw" class="w-4 h-4"></i>' +
952
+ '</button>' +
953
+ '</div>' +
954
+ '</div>' +
955
+ '<div class="insights-cards-container">' +
956
+ renderInsightsCards() +
957
+ '</div>' +
958
+ (selectedInsight ? '<div class="insight-detail-panel" id="insightDetailPanel">' + renderInsightDetail(selectedInsight) + '</div>' : '') +
959
+ '</div>';
960
+
961
+ if (window.lucide) lucide.createIcons();
962
+ }
963
+
964
+ function renderInsightsCards() {
965
+ if (!insightsHistory || insightsHistory.length === 0) {
966
+ return '<div class="insights-empty">' +
967
+ '<i data-lucide="lightbulb-off" class="w-8 h-8"></i>' +
968
+ '<p>' + t('memory.noInsightsYet') + '</p>' +
969
+ '<p class="insights-empty-hint">' + t('memory.triggerAnalysis') + '</p>' +
970
+ '</div>';
971
+ }
972
+
973
+ return '<div class="insights-cards">' +
974
+ insightsHistory.map(function(insight) {
975
+ var patternCount = (insight.patterns || []).length;
976
+ var suggestionCount = (insight.suggestions || []).length;
977
+ var severity = getInsightSeverity(insight.patterns);
978
+ var date = new Date(insight.created_at);
979
+ var timeAgo = formatTimestamp(insight.created_at);
980
+
981
+ return '<div class="insight-card ' + severity + '" onclick="showInsightDetail(\'' + insight.id + '\')">' +
982
+ '<div class="insight-card-header">' +
983
+ '<div class="insight-card-tool">' +
984
+ '<i data-lucide="' + getToolIcon(insight.tool) + '" class="w-4 h-4"></i>' +
985
+ '<span>' + insight.tool + '</span>' +
986
+ '</div>' +
987
+ '<div class="insight-card-time">' + timeAgo + '</div>' +
988
+ '</div>' +
989
+ '<div class="insight-card-stats">' +
990
+ '<div class="insight-stat">' +
991
+ '<span class="insight-stat-value">' + patternCount + '</span>' +
992
+ '<span class="insight-stat-label">' + t('memory.patterns') + '</span>' +
993
+ '</div>' +
994
+ '<div class="insight-stat">' +
995
+ '<span class="insight-stat-value">' + suggestionCount + '</span>' +
996
+ '<span class="insight-stat-label">' + t('memory.suggestions') + '</span>' +
997
+ '</div>' +
998
+ '<div class="insight-stat">' +
999
+ '<span class="insight-stat-value">' + insight.prompt_count + '</span>' +
1000
+ '<span class="insight-stat-label">' + t('memory.prompts') + '</span>' +
1001
+ '</div>' +
1002
+ '</div>' +
1003
+ '<div class="insight-card-preview">' +
1004
+ (insight.patterns && insight.patterns.length > 0 ?
1005
+ '<div class="pattern-preview ' + (insight.patterns[0].severity || 'low') + '">' +
1006
+ '<span class="pattern-type">' + escapeHtml(insight.patterns[0].type || 'pattern') + '</span>' +
1007
+ '<span class="pattern-desc">' + escapeHtml((insight.patterns[0].description || '').substring(0, 60)) + '...</span>' +
1008
+ '</div>' : '') +
1009
+ '</div>' +
1010
+ '</div>';
1011
+ }).join('') +
1012
+ '</div>';
1013
+ }
1014
+
1015
+ function getInsightSeverity(patterns) {
1016
+ if (!patterns || patterns.length === 0) return 'low';
1017
+ var hasHigh = patterns.some(function(p) { return p.severity === 'high'; });
1018
+ var hasMedium = patterns.some(function(p) { return p.severity === 'medium'; });
1019
+ return hasHigh ? 'high' : (hasMedium ? 'medium' : 'low');
1020
+ }
1021
+
1022
+ function getToolIcon(tool) {
1023
+ switch(tool) {
1024
+ case 'gemini': return 'sparkles';
1025
+ case 'qwen': return 'bot';
1026
+ case 'codex': return 'code-2';
1027
+ default: return 'cpu';
1028
+ }
1029
+ }
1030
+
1031
+ async function showInsightDetail(insightId) {
1032
+ try {
1033
+ var response = await fetch('/api/memory/insights/' + insightId);
1034
+ if (!response.ok) throw new Error('Failed to load insight detail');
1035
+ var data = await response.json();
1036
+ selectedInsight = data.insight;
1037
+ renderInsightsSection();
1038
+ } catch (err) {
1039
+ console.error('Failed to load insight detail:', err);
1040
+ if (window.showToast) {
1041
+ showToast(t('memory.loadInsightError'), 'error');
1042
+ }
1043
+ }
1044
+ }
1045
+
1046
+ function closeInsightDetail() {
1047
+ selectedInsight = null;
1048
+ renderInsightsSection();
1049
+ }
1050
+
1051
+ function renderInsightDetail(insight) {
1052
+ if (!insight) return '';
1053
+
1054
+ var html = '<div class="insight-detail">' +
1055
+ '<div class="insight-detail-header">' +
1056
+ '<h4><i data-lucide="lightbulb" class="w-4 h-4"></i> ' + t('memory.insightDetail') + '</h4>' +
1057
+ '<button class="btn-icon" onclick="closeInsightDetail()" title="' + t('common.close') + '">' +
1058
+ '<i data-lucide="x" class="w-4 h-4"></i>' +
1059
+ '</button>' +
1060
+ '</div>' +
1061
+ '<div class="insight-detail-meta">' +
1062
+ '<span><i data-lucide="' + getToolIcon(insight.tool) + '" class="w-3 h-3"></i> ' + insight.tool + '</span>' +
1063
+ '<span><i data-lucide="clock" class="w-3 h-3"></i> ' + formatTimestamp(insight.created_at) + '</span>' +
1064
+ '<span><i data-lucide="file-text" class="w-3 h-3"></i> ' + insight.prompt_count + ' ' + t('memory.promptsAnalyzed') + '</span>' +
1065
+ '</div>';
1066
+
1067
+ // Patterns
1068
+ if (insight.patterns && insight.patterns.length > 0) {
1069
+ html += '<div class="insight-patterns">' +
1070
+ '<h5><i data-lucide="alert-triangle" class="w-3.5 h-3.5"></i> ' + t('memory.patternsFound') + ' (' + insight.patterns.length + ')</h5>' +
1071
+ '<div class="patterns-list">' +
1072
+ insight.patterns.map(function(p) {
1073
+ return '<div class="pattern-item ' + (p.severity || 'low') + '">' +
1074
+ '<div class="pattern-header">' +
1075
+ '<span class="pattern-type-badge">' + escapeHtml(p.type || 'pattern') + '</span>' +
1076
+ '<span class="pattern-severity">' + (p.severity || 'low') + '</span>' +
1077
+ (p.occurrences ? '<span class="pattern-occurrences">' + p.occurrences + 'x</span>' : '') +
1078
+ '</div>' +
1079
+ '<div class="pattern-description">' + escapeHtml(p.description || '') + '</div>' +
1080
+ (p.suggestion ? '<div class="pattern-suggestion"><i data-lucide="arrow-right" class="w-3 h-3"></i> ' + escapeHtml(p.suggestion) + '</div>' : '') +
1081
+ '</div>';
1082
+ }).join('') +
1083
+ '</div>' +
1084
+ '</div>';
1085
+ }
1086
+
1087
+ // Suggestions
1088
+ if (insight.suggestions && insight.suggestions.length > 0) {
1089
+ html += '<div class="insight-suggestions">' +
1090
+ '<h5><i data-lucide="lightbulb" class="w-3.5 h-3.5"></i> ' + t('memory.suggestionsProvided') + ' (' + insight.suggestions.length + ')</h5>' +
1091
+ '<div class="suggestions-list">' +
1092
+ insight.suggestions.map(function(s) {
1093
+ return '<div class="suggestion-item">' +
1094
+ '<div class="suggestion-title">' + escapeHtml(s.title || '') + '</div>' +
1095
+ '<div class="suggestion-description">' + escapeHtml(s.description || '') + '</div>' +
1096
+ (s.example ? '<div class="suggestion-example"><code>' + escapeHtml(s.example) + '</code></div>' : '') +
1097
+ '</div>';
1098
+ }).join('') +
1099
+ '</div>' +
1100
+ '</div>';
1101
+ }
1102
+
1103
+ html += '<div class="insight-detail-actions">' +
1104
+ '<button class="btn btn-sm btn-danger" onclick="deleteInsight(\'' + insight.id + '\')">' +
1105
+ '<i data-lucide="trash-2" class="w-3.5 h-3.5"></i> ' + t('common.delete') +
1106
+ '</button>' +
1107
+ '</div>' +
1108
+ '</div>';
1109
+
1110
+ return html;
1111
+ }
1112
+
1113
+ async function deleteInsight(insightId) {
1114
+ if (!confirm(t('memory.confirmDeleteInsight'))) return;
1115
+
1116
+ try {
1117
+ var response = await fetch('/api/memory/insights/' + insightId, { method: 'DELETE' });
1118
+ if (!response.ok) throw new Error('Failed to delete insight');
1119
+
1120
+ selectedInsight = null;
1121
+ await loadInsightsHistory();
1122
+ renderInsightsSection();
1123
+
1124
+ if (window.showToast) {
1125
+ showToast(t('memory.insightDeleted'), 'success');
1126
+ }
1127
+ } catch (err) {
1128
+ console.error('Failed to delete insight:', err);
1129
+ if (window.showToast) {
1130
+ showToast(t('memory.deleteInsightError'), 'error');
1131
+ }
1132
+ }
1133
+ }
1134
+
1135
+ async function refreshInsightsHistory() {
1136
+ await loadInsightsHistory();
1137
+ renderInsightsSection();
1138
+ }
1139
+
1140
+ // ========== Actions ==========
1141
+ async function setMemoryTimeFilter(filter) {
1142
+ memoryTimeFilter = filter;
1143
+ await loadMemoryStats();
1144
+ renderHotspotsColumn();
1145
+ }
1146
+
1147
+ async function refreshMemoryData() {
1148
+ await Promise.all([
1149
+ loadMemoryStats(),
1150
+ loadMemoryGraph(),
1151
+ loadRecentContext()
1152
+ ]);
1153
+ renderHotspotsColumn();
1154
+ renderGraphColumn();
1155
+ renderContextColumn();
1156
+ }
1157
+
1158
+ function filterRecentContext(query) {
1159
+ var filtered = recentContext;
1160
+ if (query && query.trim()) {
1161
+ var q = query.toLowerCase();
1162
+ filtered = recentContext.filter(function(item) {
1163
+ var promptMatch = (item.prompt || '').toLowerCase().includes(q);
1164
+ var filesMatch = (item.files || []).some(function(f) { return f.toLowerCase().includes(q); });
1165
+ return promptMatch || filesMatch;
1166
+ });
1167
+ }
1168
+
1169
+ var container = document.getElementById('memory-context');
1170
+ if (!container) return;
1171
+
1172
+ var timeline = container.querySelector('.context-timeline');
1173
+ if (timeline) {
1174
+ timeline.outerHTML = renderContextTimeline(filtered);
1175
+ if (window.lucide) lucide.createIcons();
1176
+ }
1177
+ }
1178
+
1179
+ // ========== WebSocket Event Handlers ==========
1180
+ function handleMemoryUpdated(payload) {
1181
+ // Refresh graph and stats without full re-render
1182
+ if (payload.type === 'stats') {
1183
+ loadMemoryStats().then(function() { renderHotspotsColumn(); });
1184
+ } else if (payload.type === 'graph') {
1185
+ loadMemoryGraph().then(function() { renderGraphColumn(); });
1186
+ } else if (payload.type === 'context') {
1187
+ loadRecentContext().then(function() { renderContextColumn(); });
1188
+ } else {
1189
+ // Full refresh
1190
+ refreshMemoryData();
1191
+ }
1192
+
1193
+ // Highlight updated node if provided
1194
+ if (payload.nodeId) {
1195
+ highlightNode(payload.nodeId);
1196
+ }
1197
+ }
1198
+
1199
+ // ========== Utilities ==========
1200
+ function formatTimestamp(timestamp) {
1201
+ var date = new Date(timestamp);
1202
+ var now = new Date();
1203
+ var diff = now - date;
1204
+
1205
+ // Less than 1 minute
1206
+ if (diff < 60000) {
1207
+ return t('memory.justNow');
1208
+ }
1209
+ // Less than 1 hour
1210
+ if (diff < 3600000) {
1211
+ var minutes = Math.floor(diff / 60000);
1212
+ return minutes + ' ' + t('memory.minutesAgo');
1213
+ }
1214
+ // Less than 1 day
1215
+ if (diff < 86400000) {
1216
+ var hours = Math.floor(diff / 3600000);
1217
+ return hours + ' ' + t('memory.hoursAgo');
1218
+ }
1219
+ // Otherwise show date
1220
+ return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
1221
+ }