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.
- package/.claude/CLAUDE.md +10 -0
- package/.claude/agents/action-planning-agent.md +857 -778
- package/.claude/agents/cli-execution-agent.md +266 -269
- package/.claude/agents/cli-explore-agent.md +2 -2
- package/.claude/agents/cli-lite-planning-agent.md +142 -92
- package/.claude/agents/cli-planning-agent.md +4 -4
- package/.claude/agents/code-developer.md +7 -6
- package/.claude/agents/conceptual-planning-agent.md +2 -2
- package/.claude/agents/context-search-agent.md +31 -32
- package/.claude/agents/doc-generator.md +4 -4
- package/.claude/agents/memory-bridge.md +93 -93
- package/.claude/agents/test-context-search-agent.md +8 -7
- package/.claude/agents/test-fix-agent.md +7 -6
- package/.claude/commands/clean.md +516 -0
- package/.claude/commands/memory/compact.md +383 -0
- package/.claude/commands/memory/docs-full-cli.md +471 -471
- package/.claude/commands/memory/docs-related-cli.md +386 -386
- package/.claude/commands/memory/docs.md +615 -615
- package/.claude/commands/memory/load.md +5 -5
- package/.claude/commands/memory/tech-research-rules.md +310 -0
- package/.claude/commands/memory/update-full.md +332 -332
- package/.claude/commands/memory/workflow-skill-memory.md +4 -4
- package/.claude/commands/task/create.md +151 -151
- package/.claude/commands/version.md +254 -254
- package/.claude/commands/workflow/brainstorm/api-designer.md +587 -585
- package/.claude/commands/workflow/brainstorm/artifacts.md +1 -0
- package/.claude/commands/workflow/brainstorm/auto-parallel.md +443 -443
- package/.claude/commands/workflow/brainstorm/data-architect.md +220 -220
- package/.claude/commands/workflow/brainstorm/product-manager.md +200 -200
- package/.claude/commands/workflow/brainstorm/product-owner.md +200 -200
- package/.claude/commands/workflow/brainstorm/scrum-master.md +200 -200
- package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
- package/.claude/commands/workflow/brainstorm/system-architect.md +389 -387
- package/.claude/commands/workflow/brainstorm/ui-designer.md +221 -221
- package/.claude/commands/workflow/brainstorm/ux-expert.md +221 -221
- package/.claude/commands/workflow/debug.md +321 -0
- package/.claude/commands/workflow/execute.md +13 -0
- package/.claude/commands/workflow/init.md +165 -164
- package/.claude/commands/workflow/lite-execute.md +119 -13
- package/.claude/commands/workflow/lite-fix.md +623 -621
- package/.claude/commands/workflow/lite-plan.md +610 -592
- package/.claude/commands/workflow/plan.md +5 -5
- package/.claude/commands/workflow/review-module-cycle.md +2 -0
- package/.claude/commands/workflow/review-session-cycle.md +2 -0
- package/.claude/commands/workflow/review.md +297 -291
- package/.claude/commands/workflow/session/complete.md +153 -500
- package/.claude/commands/workflow/session/list.md +95 -95
- package/.claude/commands/workflow/session/resume.md +60 -60
- package/.claude/commands/workflow/session/start.md +199 -199
- package/.claude/commands/workflow/tdd-plan.md +3 -3
- package/.claude/commands/workflow/tdd-verify.md +23 -9
- package/.claude/commands/workflow/test-cycle-execute.md +2 -0
- package/.claude/commands/workflow/test-fix-gen.md +699 -699
- package/.claude/commands/workflow/tools/conflict-resolution.md +104 -18
- package/.claude/commands/workflow/tools/context-gather.md +436 -434
- package/.claude/commands/workflow/tools/task-generate-agent.md +490 -291
- package/.claude/commands/workflow/tools/task-generate-tdd.md +18 -10
- package/.claude/commands/workflow/tools/test-concept-enhanced.md +2 -1
- package/.claude/commands/workflow/tools/test-context-gather.md +1 -0
- package/.claude/commands/workflow/tools/test-task-generate.md +1 -0
- package/.claude/commands/workflow/ui-design/import-from-code.md +9 -6
- package/.claude/skills/command-guide/SKILL.md +5 -5
- package/.claude/skills/command-guide/index/all-commands.json +1 -1
- package/.claude/skills/command-guide/index/by-category.json +1 -1
- package/.claude/skills/command-guide/index/by-use-case.json +1 -1
- package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +857 -778
- package/.claude/skills/command-guide/reference/agents/cli-execution-agent.md +266 -269
- package/.claude/skills/command-guide/reference/agents/cli-explore-agent.md +2 -2
- package/.claude/skills/command-guide/reference/agents/cli-lite-planning-agent.md +142 -92
- package/.claude/skills/command-guide/reference/agents/cli-planning-agent.md +4 -4
- package/.claude/skills/command-guide/reference/agents/code-developer.md +7 -6
- package/.claude/skills/command-guide/reference/agents/conceptual-planning-agent.md +2 -2
- package/.claude/skills/command-guide/reference/agents/context-search-agent.md +31 -32
- package/.claude/skills/command-guide/reference/agents/doc-generator.md +4 -4
- package/.claude/skills/command-guide/reference/agents/memory-bridge.md +93 -93
- package/.claude/skills/command-guide/reference/agents/test-context-search-agent.md +8 -7
- package/.claude/skills/command-guide/reference/agents/test-fix-agent.md +7 -6
- package/.claude/skills/command-guide/reference/commands/memory/docs-full-cli.md +471 -471
- package/.claude/skills/command-guide/reference/commands/memory/docs-related-cli.md +386 -386
- package/.claude/skills/command-guide/reference/commands/memory/docs.md +17 -16
- package/.claude/skills/command-guide/reference/commands/memory/load.md +5 -5
- package/.claude/skills/command-guide/reference/commands/memory/tech-research.md +194 -357
- package/.claude/skills/command-guide/reference/commands/memory/update-full.md +332 -332
- package/.claude/skills/command-guide/reference/commands/memory/workflow-skill-memory.md +4 -4
- package/.claude/skills/command-guide/reference/commands/task/create.md +151 -151
- package/.claude/skills/command-guide/reference/commands/version.md +254 -254
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/api-designer.md +585 -585
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/auto-parallel.md +443 -443
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/data-architect.md +220 -220
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-manager.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-owner.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/scrum-master.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/system-architect.md +387 -387
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ui-designer.md +221 -221
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ux-expert.md +221 -221
- package/.claude/skills/command-guide/reference/commands/workflow/execute.md +25 -20
- package/.claude/skills/command-guide/reference/commands/workflow/init.md +164 -164
- package/.claude/skills/command-guide/reference/commands/workflow/lite-execute.md +748 -686
- package/.claude/skills/command-guide/reference/commands/workflow/lite-fix.md +664 -621
- package/.claude/skills/command-guide/reference/commands/workflow/lite-plan.md +645 -592
- package/.claude/skills/command-guide/reference/commands/workflow/plan.md +5 -5
- package/.claude/skills/command-guide/reference/commands/workflow/review.md +25 -18
- package/.claude/skills/command-guide/reference/commands/workflow/session/complete.md +547 -500
- package/.claude/skills/command-guide/reference/commands/workflow/session/list.md +45 -27
- package/.claude/skills/command-guide/reference/commands/workflow/session/resume.md +35 -19
- package/.claude/skills/command-guide/reference/commands/workflow/session/start.md +90 -33
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-plan.md +3 -3
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-verify.md +23 -9
- package/.claude/skills/command-guide/reference/commands/workflow/test-fix-gen.md +699 -699
- package/.claude/skills/command-guide/reference/commands/workflow/tools/conflict-resolution.md +103 -17
- package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +434 -434
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-agent.md +487 -291
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +17 -10
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-concept-enhanced.md +1 -1
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/import-from-code.md +6 -6
- package/.claude/workflows/chinese-response.md +38 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-api.txt +122 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-components.txt +122 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-config.txt +89 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-core.txt +60 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-patterns.txt +70 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-testing.txt +81 -0
- package/.claude/workflows/cli-templates/prompts/rules/tech-rules-agent-prompt.txt +89 -0
- package/.claude/workflows/cli-templates/prompts/workflow/gemini-solution-design.txt +131 -131
- package/.claude/workflows/cli-templates/prompts/workflow/skill-conflict-patterns.txt +5 -9
- package/.claude/workflows/cli-templates/prompts/workflow/skill-lessons-learned.txt +5 -9
- package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +112 -0
- package/.claude/workflows/cli-templates/protocols/write-protocol.md +201 -0
- package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +137 -0
- package/.claude/workflows/cli-templates/schemas/debug-log-json-schema.json +127 -0
- package/.claude/workflows/cli-templates/schemas/fix-plan-json-schema.json +25 -0
- package/.claude/workflows/cli-templates/schemas/plan-json-schema.json +25 -0
- package/.claude/workflows/cli-tools-usage.md +526 -0
- package/{CLAUDE.md → .claude/workflows/coding-philosophy.md} +24 -45
- package/.claude/workflows/context-tools.md +84 -0
- package/.claude/workflows/file-modification.md +64 -0
- package/.claude/workflows/tool-strategy.md +216 -79
- package/.claude/workflows/windows-platform.md +16 -0
- package/.claude/workflows/workflow-architecture.md +942 -942
- package/.codex/AGENTS.md +63 -330
- package/.codex/prompts/debug.md +318 -0
- package/.codex/prompts/execute.md +273 -0
- package/.codex/prompts/lite-execute.md +164 -0
- package/.codex/prompts/lite-plan.md +469 -0
- package/.codex/prompts.zip +0 -0
- package/.gemini/GEMINI.md +25 -164
- package/.qwen/QWEN.md +0 -139
- package/README.md +29 -9
- package/ccw/README.md +30 -6
- package/ccw/bin/ccw-mcp.js +7 -0
- package/ccw/bin/ccw.js +9 -9
- package/ccw/package.json +65 -47
- package/ccw/src/.workflow/.cli-history/history.db +0 -0
- package/ccw/src/.workflow/.cli-history/history.db-shm +0 -0
- package/ccw/src/.workflow/.cli-history/history.db-wal +0 -0
- package/ccw/src/cli.ts +244 -0
- package/ccw/src/commands/cli.ts +740 -0
- package/ccw/src/commands/core-memory.ts +770 -0
- package/ccw/src/commands/hook.ts +315 -0
- package/ccw/src/commands/install.ts +519 -0
- package/ccw/src/commands/{list.js → list.ts} +1 -1
- package/ccw/src/commands/memory.ts +1090 -0
- package/ccw/src/commands/{serve.js → serve.ts} +14 -5
- package/ccw/src/commands/session-path-resolver.ts +372 -0
- package/ccw/src/commands/session.ts +1141 -0
- package/ccw/src/commands/{stop.js → stop.ts} +16 -6
- package/ccw/src/commands/tool.ts +201 -0
- package/ccw/src/commands/{uninstall.js → uninstall.ts} +89 -40
- package/ccw/src/commands/{upgrade.js → upgrade.ts} +68 -23
- package/ccw/src/commands/{view.js → view.ts} +22 -8
- package/ccw/src/config/storage-paths.ts +670 -0
- package/ccw/src/core/cache-manager.ts +294 -0
- package/ccw/src/core/claude-freshness.ts +319 -0
- package/ccw/src/core/core-memory-store.ts +1528 -0
- package/ccw/src/core/{dashboard-generator-patch.js → dashboard-generator-patch.ts} +18 -0
- package/ccw/src/core/{dashboard-generator.js → dashboard-generator.ts} +69 -12
- package/ccw/src/core/data-aggregator.ts +584 -0
- package/ccw/src/core/history-importer.ts +625 -0
- package/ccw/src/core/{lite-scanner.js → lite-scanner-complete.ts} +162 -66
- package/ccw/src/core/lite-scanner.ts +469 -0
- package/ccw/src/core/{manifest.js → manifest.ts} +104 -34
- package/ccw/src/core/memory-embedder-bridge.ts +262 -0
- package/ccw/src/core/memory-store.ts +978 -0
- package/ccw/src/core/routes/ccw-routes.ts +96 -0
- package/ccw/src/core/routes/claude-routes.ts +1183 -0
- package/ccw/src/core/routes/cli-routes.ts +561 -0
- package/ccw/src/core/routes/codexlens-routes.ts +806 -0
- package/ccw/src/core/routes/core-memory-routes.ts +605 -0
- package/ccw/src/core/routes/files-routes.ts +428 -0
- package/ccw/src/core/routes/graph-routes.md +164 -0
- package/ccw/src/core/routes/graph-routes.ts +626 -0
- package/ccw/src/core/routes/help-routes.ts +308 -0
- package/ccw/src/core/routes/hooks-routes.ts +405 -0
- package/ccw/src/core/routes/mcp-routes.ts +1271 -0
- package/ccw/src/core/routes/mcp-routes.ts.backup +550 -0
- package/ccw/src/core/routes/mcp-templates-db.ts +268 -0
- package/ccw/src/core/routes/memory-routes.ts +1206 -0
- package/ccw/src/core/routes/rules-routes.ts +526 -0
- package/ccw/src/core/routes/session-routes.ts +467 -0
- package/ccw/src/core/routes/skills-routes.ts +599 -0
- package/ccw/src/core/routes/status-routes.ts +57 -0
- package/ccw/src/core/routes/system-routes.ts +427 -0
- package/ccw/src/core/server.ts +431 -0
- package/ccw/src/core/session-clustering-service.ts +1258 -0
- package/ccw/src/core/session-scanner.ts +283 -0
- package/ccw/src/core/websocket.ts +190 -0
- package/ccw/src/{index.js → index.ts} +1 -0
- package/ccw/src/mcp-server/index.ts +186 -0
- package/ccw/src/templates/assets/css/github-dark.min.css +10 -0
- package/ccw/src/templates/assets/css/github.min.css +10 -0
- package/ccw/src/templates/assets/js/cytoscape.min.js +32 -0
- package/ccw/src/templates/assets/js/d3.min.js +2 -0
- package/ccw/src/templates/assets/js/highlight.min.js +1244 -0
- package/ccw/src/templates/assets/js/lucide.min.js +12 -0
- package/ccw/src/templates/assets/js/marked.min.js +69 -0
- package/ccw/src/templates/assets/js/tailwind.js +83 -0
- package/ccw/src/templates/dashboard-css/01-base.css +11 -0
- package/ccw/src/templates/dashboard-css/02-session.css +22 -0
- package/ccw/src/templates/dashboard-css/04-lite-tasks.css +10 -0
- package/ccw/src/templates/dashboard-css/06-cards.css +10 -4
- package/ccw/src/templates/dashboard-css/07-managers.css +1178 -7
- package/ccw/src/templates/dashboard-css/09-explorer.css +23 -12
- package/ccw/src/templates/dashboard-css/10-cli-status.css +337 -0
- package/ccw/src/templates/dashboard-css/11-cli-history.css +271 -0
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +796 -0
- package/ccw/src/templates/dashboard-css/13-cli-ccw.css +199 -0
- package/ccw/src/templates/dashboard-css/14-cli-modals.css +258 -0
- package/ccw/src/templates/dashboard-css/15-cli-endpoints.css +305 -0
- package/ccw/src/templates/dashboard-css/16-cli-session.css +241 -0
- package/ccw/src/templates/dashboard-css/17-cli-conversation.css +283 -0
- package/ccw/src/templates/dashboard-css/18-cli-settings.css +160 -0
- package/ccw/src/templates/dashboard-css/19-cli-native-session.css +496 -0
- package/ccw/src/templates/dashboard-css/20-cli-taskqueue.css +188 -0
- package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +310 -0
- package/ccw/src/templates/dashboard-css/22-cli-semantic.css +240 -0
- package/ccw/src/templates/dashboard-css/23-memory.css +2390 -0
- package/ccw/src/templates/dashboard-css/24-prompt-history.css +1089 -0
- package/ccw/src/templates/dashboard-css/25-skills-rules.css +326 -0
- package/ccw/src/templates/dashboard-css/26-claude-manager.css +908 -0
- package/ccw/src/templates/dashboard-css/27-graph-explorer.css +1678 -0
- package/ccw/src/templates/dashboard-css/28-mcp-manager.css +748 -0
- package/ccw/src/templates/dashboard-css/29-help.css +264 -0
- package/ccw/src/templates/dashboard-css/30-core-memory.css +1700 -0
- package/ccw/src/templates/dashboard-js/api.js +162 -142
- package/ccw/src/templates/dashboard-js/components/carousel.js +4 -4
- package/ccw/src/templates/dashboard-js/components/cli-history.js +876 -0
- package/ccw/src/templates/dashboard-js/components/cli-status.js +978 -0
- package/ccw/src/templates/dashboard-js/components/global-notifications.js +508 -219
- package/ccw/src/templates/dashboard-js/components/hook-manager.js +1277 -282
- package/ccw/src/templates/dashboard-js/components/index-manager.js +302 -0
- package/ccw/src/templates/dashboard-js/components/mcp-manager.js +718 -27
- package/ccw/src/templates/dashboard-js/components/modals.js +66 -0
- package/ccw/src/templates/dashboard-js/components/navigation.js +80 -12
- package/ccw/src/templates/dashboard-js/components/notifications.js +758 -194
- package/ccw/src/templates/dashboard-js/components/storage-manager.js +478 -0
- package/ccw/src/templates/dashboard-js/components/tabs-other.js +157 -6
- package/ccw/src/templates/dashboard-js/components/task-queue-sidebar.js +716 -0
- package/ccw/src/templates/dashboard-js/help-i18n.js +272 -0
- package/ccw/src/templates/dashboard-js/i18n.js +2807 -0
- package/ccw/src/templates/dashboard-js/main.js +15 -0
- package/ccw/src/templates/dashboard-js/state.js +243 -42
- package/ccw/src/templates/dashboard-js/utils.js +47 -1
- package/ccw/src/templates/dashboard-js/views/claude-manager.js +912 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +2272 -0
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +964 -0
- package/ccw/src/templates/dashboard-js/views/core-memory-clusters.js +503 -0
- package/ccw/src/templates/dashboard-js/views/core-memory.js +782 -0
- package/ccw/src/templates/dashboard-js/views/explorer.js +888 -852
- package/ccw/src/templates/dashboard-js/views/graph-explorer.js +1157 -0
- package/ccw/src/templates/dashboard-js/views/help.js +856 -0
- package/ccw/src/templates/dashboard-js/views/history.js +337 -0
- package/ccw/src/templates/dashboard-js/views/home.js +61 -15
- package/ccw/src/templates/dashboard-js/views/hook-manager.js +311 -43
- package/ccw/src/templates/dashboard-js/views/lite-tasks.js +204 -28
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2187 -411
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js.backup +1729 -0
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js.new +928 -0
- package/ccw/src/templates/dashboard-js/views/memory.js +1221 -0
- package/ccw/src/templates/dashboard-js/views/prompt-history.js +713 -0
- package/ccw/src/templates/dashboard-js/views/rules-manager.js +828 -0
- package/ccw/src/templates/dashboard-js/views/session-detail.js +54 -53
- package/ccw/src/templates/dashboard-js/views/skills-manager.js +819 -0
- package/ccw/src/templates/dashboard.html +185 -85
- package/ccw/src/templates/hooks-config-example.json +60 -0
- package/ccw/src/tools/classify-folders.ts +245 -0
- package/ccw/src/tools/cli-config-manager.ts +268 -0
- package/ccw/src/tools/cli-executor.ts +2014 -0
- package/ccw/src/tools/cli-history-store.ts +1195 -0
- package/ccw/src/tools/codex-lens.ts +1141 -0
- package/ccw/src/tools/{convert-tokens-to-css.js → convert-tokens-to-css.ts} +73 -23
- package/ccw/src/tools/core-memory.ts +444 -0
- package/ccw/src/tools/detect-changed-modules.ts +325 -0
- package/ccw/src/tools/{discover-design-files.js → discover-design-files.ts} +74 -24
- package/ccw/src/tools/edit-file.ts +568 -0
- package/ccw/src/tools/{generate-module-docs.js → generate-module-docs.ts} +207 -185
- package/ccw/src/tools/{get-modules-by-depth.js → get-modules-by-depth.ts} +120 -79
- package/ccw/src/tools/index.ts +370 -0
- package/ccw/src/tools/native-session-discovery.ts +795 -0
- package/ccw/src/tools/notifier.ts +129 -0
- package/ccw/src/tools/read-file.ts +410 -0
- package/ccw/src/tools/resume-strategy.ts +345 -0
- package/ccw/src/tools/session-content-parser.ts +619 -0
- package/ccw/src/tools/session-manager.ts +1026 -0
- package/ccw/src/tools/smart-context.ts +228 -0
- package/ccw/src/tools/smart-search.ts +2065 -0
- package/ccw/src/tools/smart-search.ts.backup +1233 -0
- package/ccw/src/tools/storage-manager.ts +455 -0
- package/ccw/src/tools/write-file.ts +222 -0
- package/ccw/src/types/config.ts +11 -0
- package/ccw/src/types/index.ts +3 -0
- package/ccw/src/types/session.ts +25 -0
- package/ccw/src/types/tool.ts +41 -0
- package/ccw/src/utils/{browser-launcher.js → browser-launcher.ts} +10 -8
- package/ccw/src/utils/file-utils.ts +48 -0
- package/ccw/src/utils/{path-resolver.js → path-resolver.ts} +114 -78
- package/ccw/src/utils/path-validator.ts +153 -0
- package/ccw/src/utils/{ui.js → ui.ts} +32 -25
- package/codex-lens/pyproject.toml +48 -0
- package/codex-lens/src/codexlens/.workflow/.cli-history/history.db +0 -0
- package/codex-lens/src/codexlens/__init__.py +28 -0
- package/codex-lens/src/codexlens/__main__.py +14 -0
- package/codex-lens/src/codexlens/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/__main__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/errors.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__init__.py +27 -0
- package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +1931 -0
- package/codex-lens/src/codexlens/cli/embedding_manager.py +620 -0
- package/codex-lens/src/codexlens/cli/model_manager.py +289 -0
- package/codex-lens/src/codexlens/cli/output.py +124 -0
- package/codex-lens/src/codexlens/config.py +201 -0
- package/codex-lens/src/codexlens/entities.py +121 -0
- package/codex-lens/src/codexlens/errors.py +55 -0
- package/codex-lens/src/codexlens/indexing/README.md +77 -0
- package/codex-lens/src/codexlens/indexing/__init__.py +4 -0
- package/codex-lens/src/codexlens/indexing/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/indexing/__pycache__/symbol_extractor.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/indexing/symbol_extractor.py +243 -0
- package/codex-lens/src/codexlens/parsers/__init__.py +8 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/encoding.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/tokenizer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/encoding.py +202 -0
- package/codex-lens/src/codexlens/parsers/factory.py +256 -0
- package/codex-lens/src/codexlens/parsers/tokenizer.py +98 -0
- package/codex-lens/src/codexlens/parsers/treesitter_parser.py +335 -0
- package/codex-lens/src/codexlens/search/__init__.py +15 -0
- package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/enrichment.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/query_parser.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +647 -0
- package/codex-lens/src/codexlens/search/enrichment.py +150 -0
- package/codex-lens/src/codexlens/search/hybrid_search.py +313 -0
- package/codex-lens/src/codexlens/search/query_parser.py +242 -0
- package/codex-lens/src/codexlens/search/ranking.py +274 -0
- package/codex-lens/src/codexlens/semantic/__init__.py +39 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/code_extractor.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/graph_analyzer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/llm_enhancer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/ann_index.py +414 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +448 -0
- package/codex-lens/src/codexlens/semantic/code_extractor.py +274 -0
- package/codex-lens/src/codexlens/semantic/embedder.py +185 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +955 -0
- package/codex-lens/src/codexlens/storage/__init__.py +29 -0
- package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/file_cache.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_utils.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/dir_index.py +1850 -0
- package/codex-lens/src/codexlens/storage/file_cache.py +32 -0
- package/codex-lens/src/codexlens/storage/index_tree.py +776 -0
- package/codex-lens/src/codexlens/storage/migration_manager.py +154 -0
- package/codex-lens/src/codexlens/storage/migrations/__init__.py +1 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_001_normalize_keywords.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_002_add_token_metadata.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_003_code_relationships.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_005_cleanup_unused_fields.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_001_normalize_keywords.py +123 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_002_add_token_metadata.py +48 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_004_dual_fts.py +232 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_005_cleanup_unused_fields.py +196 -0
- package/codex-lens/src/codexlens/storage/path_mapper.py +274 -0
- package/codex-lens/src/codexlens/storage/registry.py +670 -0
- package/codex-lens/src/codexlens/storage/sqlite_store.py +576 -0
- package/codex-lens/src/codexlens/storage/sqlite_utils.py +64 -0
- package/package.json +4 -1
- package/.claude/commands/memory/tech-research.md +0 -477
- package/.claude/scripts/classify-folders.sh +0 -39
- package/.claude/scripts/convert_tokens_to_css.sh +0 -229
- package/.claude/scripts/detect_changed_modules.sh +0 -161
- package/.claude/scripts/discover-design-files.sh +0 -87
- package/.claude/scripts/extract-animations.js +0 -243
- package/.claude/scripts/extract-computed-styles.js +0 -118
- package/.claude/scripts/extract-layout-structure.js +0 -411
- package/.claude/scripts/generate_module_docs.sh +0 -717
- package/.claude/scripts/get_modules_by_depth.sh +0 -170
- package/.claude/scripts/ui-generate-preview.sh +0 -395
- package/.claude/scripts/ui-instantiate-prototypes.sh +0 -815
- package/.claude/scripts/update_module_claude.sh +0 -337
- package/.claude/workflows/context-search-strategy.md +0 -77
- package/.claude/workflows/intelligent-tools-strategy.md +0 -662
- package/ccw/src/cli.js +0 -119
- package/ccw/src/commands/install.js +0 -324
- package/ccw/src/commands/tool.js +0 -138
- package/ccw/src/core/data-aggregator.js +0 -409
- package/ccw/src/core/server.js +0 -2063
- package/ccw/src/core/session-scanner.js +0 -235
- package/ccw/src/tools/classify-folders.js +0 -204
- package/ccw/src/tools/detect-changed-modules.js +0 -288
- package/ccw/src/tools/edit-file.js +0 -266
- package/ccw/src/tools/index.js +0 -176
- 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, '"') + ')">' +
|
|
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
|
+
}
|