claude-code-workflow 6.2.2 → 6.2.3
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/ccw/dist/cli.d.ts +2 -0
- package/ccw/dist/cli.d.ts.map +1 -0
- package/ccw/dist/cli.js +219 -0
- package/ccw/dist/cli.js.map +1 -0
- package/ccw/dist/commands/cli.d.ts +32 -0
- package/ccw/dist/commands/cli.d.ts.map +1 -0
- package/ccw/dist/commands/cli.js +619 -0
- package/ccw/dist/commands/cli.js.map +1 -0
- package/ccw/dist/commands/core-memory.d.ts +32 -0
- package/ccw/dist/commands/core-memory.d.ts.map +1 -0
- package/ccw/dist/commands/core-memory.js +640 -0
- package/ccw/dist/commands/core-memory.js.map +1 -0
- package/ccw/dist/commands/hook.d.ts +16 -0
- package/ccw/dist/commands/hook.d.ts.map +1 -0
- package/ccw/dist/commands/hook.js +276 -0
- package/ccw/dist/commands/hook.js.map +1 -0
- package/ccw/dist/commands/install.d.ts +12 -0
- package/ccw/dist/commands/install.d.ts.map +1 -0
- package/ccw/dist/commands/install.js +443 -0
- package/ccw/dist/commands/install.js.map +1 -0
- package/ccw/dist/commands/list.d.ts +5 -0
- package/ccw/dist/commands/list.d.ts.map +1 -0
- package/ccw/dist/commands/list.js +32 -0
- package/ccw/dist/commands/list.js.map +1 -0
- package/ccw/dist/commands/memory.d.ts +57 -0
- package/ccw/dist/commands/memory.d.ts.map +1 -0
- package/ccw/dist/commands/memory.js +890 -0
- package/ccw/dist/commands/memory.js.map +1 -0
- package/ccw/dist/commands/serve.d.ts +12 -0
- package/ccw/dist/commands/serve.d.ts.map +1 -0
- package/ccw/dist/commands/serve.js +63 -0
- package/ccw/dist/commands/serve.js.map +1 -0
- package/ccw/dist/commands/session-path-resolver.d.ts +45 -0
- package/ccw/dist/commands/session-path-resolver.d.ts.map +1 -0
- package/ccw/dist/commands/session-path-resolver.js +302 -0
- package/ccw/dist/commands/session-path-resolver.js.map +1 -0
- package/ccw/dist/commands/session.d.ts +12 -0
- package/ccw/dist/commands/session.d.ts.map +1 -0
- package/ccw/dist/commands/session.js +954 -0
- package/ccw/dist/commands/session.js.map +1 -0
- package/ccw/dist/commands/stop.d.ts +11 -0
- package/ccw/dist/commands/stop.d.ts.map +1 -0
- package/ccw/dist/commands/stop.js +96 -0
- package/ccw/dist/commands/stop.js.map +1 -0
- package/ccw/dist/commands/tool.d.ts +29 -0
- package/ccw/dist/commands/tool.d.ts.map +1 -0
- package/ccw/dist/commands/tool.js +173 -0
- package/ccw/dist/commands/tool.js.map +1 -0
- package/ccw/dist/commands/uninstall.d.ts +9 -0
- package/ccw/dist/commands/uninstall.d.ts.map +1 -0
- package/ccw/dist/commands/uninstall.js +239 -0
- package/ccw/dist/commands/uninstall.js.map +1 -0
- package/ccw/dist/commands/upgrade.d.ts +10 -0
- package/ccw/dist/commands/upgrade.d.ts.map +1 -0
- package/ccw/dist/commands/upgrade.js +288 -0
- package/ccw/dist/commands/upgrade.js.map +1 -0
- package/ccw/dist/commands/view.d.ts +14 -0
- package/ccw/dist/commands/view.d.ts.map +1 -0
- package/ccw/dist/commands/view.js +100 -0
- package/ccw/dist/commands/view.js.map +1 -0
- package/ccw/dist/config/storage-paths.d.ts +184 -0
- package/ccw/dist/config/storage-paths.d.ts.map +1 -0
- package/ccw/dist/config/storage-paths.js +536 -0
- package/ccw/dist/config/storage-paths.js.map +1 -0
- package/ccw/dist/core/cache-manager.d.ts +80 -0
- package/ccw/dist/core/cache-manager.d.ts.map +1 -0
- package/ccw/dist/core/cache-manager.js +260 -0
- package/ccw/dist/core/cache-manager.js.map +1 -0
- package/ccw/dist/core/claude-freshness.d.ts +53 -0
- package/ccw/dist/core/claude-freshness.d.ts.map +1 -0
- package/ccw/dist/core/claude-freshness.js +232 -0
- package/ccw/dist/core/claude-freshness.js.map +1 -0
- package/ccw/dist/core/core-memory-store.d.ts +320 -0
- package/ccw/dist/core/core-memory-store.d.ts.map +1 -0
- package/ccw/dist/core/core-memory-store.js +1177 -0
- package/ccw/dist/core/core-memory-store.js.map +1 -0
- package/ccw/dist/core/dashboard-generator-patch.d.ts +2 -0
- package/ccw/dist/core/dashboard-generator-patch.d.ts.map +1 -0
- package/ccw/dist/core/dashboard-generator-patch.js +48 -0
- package/ccw/dist/core/dashboard-generator-patch.js.map +1 -0
- package/ccw/dist/core/dashboard-generator.d.ts +8 -0
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -0
- package/ccw/dist/core/dashboard-generator.js +695 -0
- package/ccw/dist/core/dashboard-generator.js.map +1 -0
- package/ccw/dist/core/data-aggregator.d.ts +145 -0
- package/ccw/dist/core/data-aggregator.d.ts.map +1 -0
- package/ccw/dist/core/data-aggregator.js +416 -0
- package/ccw/dist/core/data-aggregator.js.map +1 -0
- package/ccw/dist/core/history-importer.d.ts +102 -0
- package/ccw/dist/core/history-importer.d.ts.map +1 -0
- package/ccw/dist/core/history-importer.js +493 -0
- package/ccw/dist/core/history-importer.js.map +1 -0
- package/ccw/dist/core/lite-scanner-complete.d.ts +81 -0
- package/ccw/dist/core/lite-scanner-complete.d.ts.map +1 -0
- package/ccw/dist/core/lite-scanner-complete.js +368 -0
- package/ccw/dist/core/lite-scanner-complete.js.map +1 -0
- package/ccw/dist/core/lite-scanner.d.ts +81 -0
- package/ccw/dist/core/lite-scanner.d.ts.map +1 -0
- package/ccw/dist/core/lite-scanner.js +368 -0
- package/ccw/dist/core/lite-scanner.js.map +1 -0
- package/ccw/dist/core/manifest.d.ts +88 -0
- package/ccw/dist/core/manifest.d.ts.map +1 -0
- package/ccw/dist/core/manifest.js +214 -0
- package/ccw/dist/core/manifest.js.map +1 -0
- package/ccw/dist/core/memory-embedder-bridge.d.ts +83 -0
- package/ccw/dist/core/memory-embedder-bridge.d.ts.map +1 -0
- package/ccw/dist/core/memory-embedder-bridge.js +181 -0
- package/ccw/dist/core/memory-embedder-bridge.js.map +1 -0
- package/ccw/dist/core/memory-store.d.ts +249 -0
- package/ccw/dist/core/memory-store.d.ts.map +1 -0
- package/ccw/dist/core/memory-store.js +781 -0
- package/ccw/dist/core/memory-store.js.map +1 -0
- package/ccw/dist/core/routes/ccw-routes.d.ts +20 -0
- package/ccw/dist/core/routes/ccw-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/ccw-routes.js +70 -0
- package/ccw/dist/core/routes/ccw-routes.js.map +1 -0
- package/ccw/dist/core/routes/claude-routes.d.ts +19 -0
- package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/claude-routes.js +1017 -0
- package/ccw/dist/core/routes/claude-routes.js.map +1 -0
- package/ccw/dist/core/routes/cli-routes.d.ts +20 -0
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/cli-routes.js +468 -0
- package/ccw/dist/core/routes/cli-routes.js.map +1 -0
- package/ccw/dist/core/routes/codexlens-routes.d.ts +20 -0
- package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/codexlens-routes.js +754 -0
- package/ccw/dist/core/routes/codexlens-routes.js.map +1 -0
- package/ccw/dist/core/routes/core-memory-routes.d.ts +21 -0
- package/ccw/dist/core/routes/core-memory-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/core-memory-routes.js +520 -0
- package/ccw/dist/core/routes/core-memory-routes.js.map +1 -0
- package/ccw/dist/core/routes/files-routes.d.ts +20 -0
- package/ccw/dist/core/routes/files-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/files-routes.js +374 -0
- package/ccw/dist/core/routes/files-routes.js.map +1 -0
- package/ccw/dist/core/routes/graph-routes.d.ts +20 -0
- package/ccw/dist/core/routes/graph-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/graph-routes.js +517 -0
- package/ccw/dist/core/routes/graph-routes.js.map +1 -0
- package/ccw/dist/core/routes/help-routes.d.ts +20 -0
- package/ccw/dist/core/routes/help-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/help-routes.js +250 -0
- package/ccw/dist/core/routes/help-routes.js.map +1 -0
- package/ccw/dist/core/routes/hooks-routes.d.ts +21 -0
- package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/hooks-routes.js +346 -0
- package/ccw/dist/core/routes/hooks-routes.js.map +1 -0
- package/ccw/dist/core/routes/mcp-routes.d.ts +20 -0
- package/ccw/dist/core/routes/mcp-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/mcp-routes.js +1129 -0
- package/ccw/dist/core/routes/mcp-routes.js.map +1 -0
- package/ccw/dist/core/routes/mcp-templates-db.d.ts +54 -0
- package/ccw/dist/core/routes/mcp-templates-db.d.ts.map +1 -0
- package/ccw/dist/core/routes/mcp-templates-db.js +226 -0
- package/ccw/dist/core/routes/mcp-templates-db.js.map +1 -0
- package/ccw/dist/core/routes/memory-routes.d.ts +21 -0
- package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/memory-routes.js +1095 -0
- package/ccw/dist/core/routes/memory-routes.js.map +1 -0
- package/ccw/dist/core/routes/rules-routes.d.ts +20 -0
- package/ccw/dist/core/routes/rules-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/rules-routes.js +442 -0
- package/ccw/dist/core/routes/rules-routes.js.map +1 -0
- package/ccw/dist/core/routes/session-routes.d.ts +20 -0
- package/ccw/dist/core/routes/session-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/session-routes.js +423 -0
- package/ccw/dist/core/routes/session-routes.js.map +1 -0
- package/ccw/dist/core/routes/skills-routes.d.ts +20 -0
- package/ccw/dist/core/routes/skills-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/skills-routes.js +533 -0
- package/ccw/dist/core/routes/skills-routes.js.map +1 -0
- package/ccw/dist/core/routes/status-routes.d.ts +20 -0
- package/ccw/dist/core/routes/status-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/status-routes.js +38 -0
- package/ccw/dist/core/routes/status-routes.js.map +1 -0
- package/ccw/dist/core/routes/system-routes.d.ts +22 -0
- package/ccw/dist/core/routes/system-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/system-routes.js +354 -0
- package/ccw/dist/core/routes/system-routes.js.map +1 -0
- package/ccw/dist/core/server.d.ts +17 -0
- package/ccw/dist/core/server.d.ts.map +1 -0
- package/ccw/dist/core/server.js +386 -0
- package/ccw/dist/core/server.js.map +1 -0
- package/ccw/dist/core/session-clustering-service.d.ts +153 -0
- package/ccw/dist/core/session-clustering-service.d.ts.map +1 -0
- package/ccw/dist/core/session-clustering-service.js +1065 -0
- package/ccw/dist/core/session-clustering-service.js.map +1 -0
- package/ccw/dist/core/session-scanner.d.ts +32 -0
- package/ccw/dist/core/session-scanner.d.ts.map +1 -0
- package/ccw/dist/core/session-scanner.js +253 -0
- package/ccw/dist/core/session-scanner.js.map +1 -0
- package/ccw/dist/core/websocket.d.ts +23 -0
- package/ccw/dist/core/websocket.d.ts.map +1 -0
- package/ccw/dist/core/websocket.js +168 -0
- package/ccw/dist/core/websocket.js.map +1 -0
- package/ccw/dist/index.d.ts +10 -0
- package/ccw/dist/index.d.ts.map +1 -0
- package/ccw/dist/index.js +10 -0
- package/ccw/dist/index.js.map +1 -0
- package/ccw/dist/mcp-server/index.d.ts +7 -0
- package/ccw/dist/mcp-server/index.d.ts.map +1 -0
- package/ccw/dist/mcp-server/index.js +157 -0
- package/ccw/dist/mcp-server/index.js.map +1 -0
- package/ccw/dist/tools/classify-folders.d.ts +26 -0
- package/ccw/dist/tools/classify-folders.d.ts.map +1 -0
- package/ccw/dist/tools/classify-folders.js +201 -0
- package/ccw/dist/tools/classify-folders.js.map +1 -0
- package/ccw/dist/tools/cli-config-manager.d.ts +62 -0
- package/ccw/dist/tools/cli-config-manager.d.ts.map +1 -0
- package/ccw/dist/tools/cli-config-manager.js +221 -0
- package/ccw/dist/tools/cli-config-manager.js.map +1 -0
- package/ccw/dist/tools/cli-executor.d.ts +373 -0
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -0
- package/ccw/dist/tools/cli-executor.js +1625 -0
- package/ccw/dist/tools/cli-executor.js.map +1 -0
- package/ccw/dist/tools/cli-history-store.d.ts +330 -0
- package/ccw/dist/tools/cli-history-store.d.ts.map +1 -0
- package/ccw/dist/tools/cli-history-store.js +916 -0
- package/ccw/dist/tools/cli-history-store.js.map +1 -0
- package/ccw/dist/tools/codex-lens.d.ts +118 -0
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -0
- package/ccw/dist/tools/codex-lens.js +962 -0
- package/ccw/dist/tools/codex-lens.js.map +1 -0
- package/ccw/dist/tools/convert-tokens-to-css.d.ts +14 -0
- package/ccw/dist/tools/convert-tokens-to-css.d.ts.map +1 -0
- package/ccw/dist/tools/convert-tokens-to-css.js +244 -0
- package/ccw/dist/tools/convert-tokens-to-css.js.map +1 -0
- package/ccw/dist/tools/core-memory.d.ts +66 -0
- package/ccw/dist/tools/core-memory.d.ts.map +1 -0
- package/ccw/dist/tools/core-memory.js +324 -0
- package/ccw/dist/tools/core-memory.js.map +1 -0
- package/ccw/dist/tools/detect-changed-modules.d.ts +24 -0
- package/ccw/dist/tools/detect-changed-modules.d.ts.map +1 -0
- package/ccw/dist/tools/detect-changed-modules.js +277 -0
- package/ccw/dist/tools/detect-changed-modules.js.map +1 -0
- package/ccw/dist/tools/discover-design-files.d.ts +36 -0
- package/ccw/dist/tools/discover-design-files.d.ts.map +1 -0
- package/ccw/dist/tools/discover-design-files.js +147 -0
- package/ccw/dist/tools/discover-design-files.js.map +1 -0
- package/ccw/dist/tools/edit-file.d.ts +28 -0
- package/ccw/dist/tools/edit-file.d.ts.map +1 -0
- package/ccw/dist/tools/edit-file.js +479 -0
- package/ccw/dist/tools/edit-file.js.map +1 -0
- package/ccw/dist/tools/generate-module-docs.d.ts +22 -0
- package/ccw/dist/tools/generate-module-docs.d.ts.map +1 -0
- package/ccw/dist/tools/generate-module-docs.js +379 -0
- package/ccw/dist/tools/generate-module-docs.js.map +1 -0
- package/ccw/dist/tools/get-modules-by-depth.d.ts +15 -0
- package/ccw/dist/tools/get-modules-by-depth.d.ts.map +1 -0
- package/ccw/dist/tools/get-modules-by-depth.js +296 -0
- package/ccw/dist/tools/get-modules-by-depth.js.map +1 -0
- package/ccw/dist/tools/index.d.ts +55 -0
- package/ccw/dist/tools/index.d.ts.map +1 -0
- package/ccw/dist/tools/index.js +304 -0
- package/ccw/dist/tools/index.js.map +1 -0
- package/ccw/dist/tools/native-session-discovery.d.ts +97 -0
- package/ccw/dist/tools/native-session-discovery.d.ts.map +1 -0
- package/ccw/dist/tools/native-session-discovery.js +700 -0
- package/ccw/dist/tools/native-session-discovery.js.map +1 -0
- package/ccw/dist/tools/notifier.d.ts +50 -0
- package/ccw/dist/tools/notifier.d.ts.map +1 -0
- package/ccw/dist/tools/notifier.js +90 -0
- package/ccw/dist/tools/notifier.js.map +1 -0
- package/ccw/dist/tools/read-file.d.ts +32 -0
- package/ccw/dist/tools/read-file.d.ts.map +1 -0
- package/ccw/dist/tools/read-file.js +329 -0
- package/ccw/dist/tools/read-file.js.map +1 -0
- package/ccw/dist/tools/resume-strategy.d.ts +48 -0
- package/ccw/dist/tools/resume-strategy.d.ts.map +1 -0
- package/ccw/dist/tools/resume-strategy.js +248 -0
- package/ccw/dist/tools/resume-strategy.js.map +1 -0
- package/ccw/dist/tools/session-content-parser.d.ts +58 -0
- package/ccw/dist/tools/session-content-parser.d.ts.map +1 -0
- package/ccw/dist/tools/session-content-parser.js +420 -0
- package/ccw/dist/tools/session-content-parser.js.map +1 -0
- package/ccw/dist/tools/session-manager.d.ts +9 -0
- package/ccw/dist/tools/session-manager.d.ts.map +1 -0
- package/ccw/dist/tools/session-manager.js +834 -0
- package/ccw/dist/tools/session-manager.js.map +1 -0
- package/ccw/dist/tools/smart-context.d.ts +35 -0
- package/ccw/dist/tools/smart-context.d.ts.map +1 -0
- package/ccw/dist/tools/smart-context.js +182 -0
- package/ccw/dist/tools/smart-context.js.map +1 -0
- package/ccw/dist/tools/smart-search.d.ts +105 -0
- package/ccw/dist/tools/smart-search.d.ts.map +1 -0
- package/ccw/dist/tools/smart-search.js +1753 -0
- package/ccw/dist/tools/smart-search.js.map +1 -0
- package/ccw/dist/tools/storage-manager.d.ts +114 -0
- package/ccw/dist/tools/storage-manager.d.ts.map +1 -0
- package/ccw/dist/tools/storage-manager.js +392 -0
- package/ccw/dist/tools/storage-manager.js.map +1 -0
- package/ccw/dist/tools/ui-generate-preview.d.ts +39 -0
- package/ccw/dist/tools/ui-generate-preview.d.ts.map +1 -0
- package/ccw/dist/tools/ui-generate-preview.js +300 -0
- package/ccw/dist/tools/ui-generate-preview.js.map +1 -0
- package/ccw/dist/tools/ui-instantiate-prototypes.d.ts +75 -0
- package/ccw/dist/tools/ui-instantiate-prototypes.d.ts.map +1 -0
- package/ccw/dist/tools/ui-instantiate-prototypes.js +256 -0
- package/ccw/dist/tools/ui-instantiate-prototypes.js.map +1 -0
- package/ccw/dist/tools/update-module-claude.d.ts +80 -0
- package/ccw/dist/tools/update-module-claude.d.ts.map +1 -0
- package/ccw/dist/tools/update-module-claude.js +351 -0
- package/ccw/dist/tools/update-module-claude.js.map +1 -0
- package/ccw/dist/tools/write-file.d.ts +19 -0
- package/ccw/dist/tools/write-file.d.ts.map +1 -0
- package/ccw/dist/tools/write-file.js +193 -0
- package/ccw/dist/tools/write-file.js.map +1 -0
- package/ccw/dist/types/config.d.ts +11 -0
- package/ccw/dist/types/config.d.ts.map +1 -0
- package/ccw/dist/types/config.js +2 -0
- package/ccw/dist/types/config.js.map +1 -0
- package/ccw/dist/types/index.d.ts +4 -0
- package/ccw/dist/types/index.d.ts.map +1 -0
- package/ccw/dist/types/index.js +4 -0
- package/ccw/dist/types/index.js.map +1 -0
- package/ccw/dist/types/session.d.ts +20 -0
- package/ccw/dist/types/session.d.ts.map +1 -0
- package/ccw/dist/types/session.js +2 -0
- package/ccw/dist/types/session.js.map +1 -0
- package/ccw/dist/types/tool.d.ts +36 -0
- package/ccw/dist/types/tool.d.ts.map +1 -0
- package/ccw/dist/types/tool.js +11 -0
- package/ccw/dist/types/tool.js.map +1 -0
- package/ccw/dist/utils/browser-launcher.d.ts +13 -0
- package/ccw/dist/utils/browser-launcher.d.ts.map +1 -0
- package/ccw/dist/utils/browser-launcher.js +60 -0
- package/ccw/dist/utils/browser-launcher.js.map +1 -0
- package/ccw/dist/utils/file-utils.d.ts +25 -0
- package/ccw/dist/utils/file-utils.d.ts.map +1 -0
- package/ccw/dist/utils/file-utils.js +48 -0
- package/ccw/dist/utils/file-utils.js.map +1 -0
- package/ccw/dist/utils/path-resolver.d.ts +80 -0
- package/ccw/dist/utils/path-resolver.d.ts.map +1 -0
- package/ccw/dist/utils/path-resolver.js +260 -0
- package/ccw/dist/utils/path-resolver.js.map +1 -0
- package/ccw/dist/utils/path-validator.d.ts +49 -0
- package/ccw/dist/utils/path-validator.d.ts.map +1 -0
- package/ccw/dist/utils/path-validator.js +123 -0
- package/ccw/dist/utils/path-validator.js.map +1 -0
- package/ccw/dist/utils/ui.d.ts +62 -0
- package/ccw/dist/utils/ui.d.ts.map +1 -0
- package/ccw/dist/utils/ui.js +129 -0
- package/ccw/dist/utils/ui.js.map +1 -0
- package/ccw/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1095 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, unlinkSync } from 'fs';
|
|
2
|
+
import { join, isAbsolute, extname } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { getMemoryStore } from '../memory-store.js';
|
|
5
|
+
import { executeCliTool } from '../../tools/cli-executor.js';
|
|
6
|
+
/**
|
|
7
|
+
* Derive prompt intent from text
|
|
8
|
+
*/
|
|
9
|
+
function derivePromptIntent(text) {
|
|
10
|
+
const lower = text.toLowerCase();
|
|
11
|
+
// Implementation/coding patterns
|
|
12
|
+
if (/实现|implement|create|add|build|write|develop|make/.test(lower))
|
|
13
|
+
return 'implement';
|
|
14
|
+
if (/修复|fix|bug|error|issue|problem|解决/.test(lower))
|
|
15
|
+
return 'fix';
|
|
16
|
+
if (/重构|refactor|optimize|improve|clean/.test(lower))
|
|
17
|
+
return 'refactor';
|
|
18
|
+
if (/测试|test|spec|coverage/.test(lower))
|
|
19
|
+
return 'test';
|
|
20
|
+
// Analysis patterns
|
|
21
|
+
if (/分析|analyze|review|check|examine|audit/.test(lower))
|
|
22
|
+
return 'analyze';
|
|
23
|
+
if (/解释|explain|what|how|why|understand/.test(lower))
|
|
24
|
+
return 'explain';
|
|
25
|
+
if (/搜索|search|find|look|where|locate/.test(lower))
|
|
26
|
+
return 'search';
|
|
27
|
+
// Documentation patterns
|
|
28
|
+
if (/文档|document|readme|comment|注释/.test(lower))
|
|
29
|
+
return 'document';
|
|
30
|
+
// Planning patterns
|
|
31
|
+
if (/计划|plan|design|architect|strategy/.test(lower))
|
|
32
|
+
return 'plan';
|
|
33
|
+
// Configuration patterns
|
|
34
|
+
if (/配置|config|setup|install|设置/.test(lower))
|
|
35
|
+
return 'configure';
|
|
36
|
+
// Default
|
|
37
|
+
return 'general';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Calculate prompt quality score (0-100)
|
|
41
|
+
*/
|
|
42
|
+
function calculateQualityScore(text) {
|
|
43
|
+
let score = 50; // Base score
|
|
44
|
+
// Length factors
|
|
45
|
+
const length = text.length;
|
|
46
|
+
if (length > 50 && length < 500)
|
|
47
|
+
score += 15;
|
|
48
|
+
else if (length >= 500 && length < 1000)
|
|
49
|
+
score += 10;
|
|
50
|
+
else if (length < 20)
|
|
51
|
+
score -= 20;
|
|
52
|
+
// Specificity indicators
|
|
53
|
+
if (/file|path|function|class|method|variable/i.test(text))
|
|
54
|
+
score += 10;
|
|
55
|
+
if (/src\/|\.ts|\.js|\.py|\.go/i.test(text))
|
|
56
|
+
score += 10;
|
|
57
|
+
// Context indicators
|
|
58
|
+
if (/when|after|before|because|since/i.test(text))
|
|
59
|
+
score += 5;
|
|
60
|
+
// Action clarity
|
|
61
|
+
if (/please|要|请|帮|help/i.test(text))
|
|
62
|
+
score += 5;
|
|
63
|
+
// Structure indicators
|
|
64
|
+
if (/\d+\.|•|-\s/.test(text))
|
|
65
|
+
score += 10; // Lists
|
|
66
|
+
// Cap at 100
|
|
67
|
+
return Math.min(100, Math.max(0, score));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Handle Memory API routes
|
|
71
|
+
* @returns true if route was handled, false otherwise
|
|
72
|
+
*/
|
|
73
|
+
export async function handleMemoryRoutes(ctx) {
|
|
74
|
+
const { pathname, url, req, res, initialPath, handlePostRequest, broadcastToClients } = ctx;
|
|
75
|
+
// API: Memory Module - Track entity access
|
|
76
|
+
if (pathname === '/api/memory/track' && req.method === 'POST') {
|
|
77
|
+
handlePostRequest(req, res, async (body) => {
|
|
78
|
+
const { type, action, value, sessionId, metadata, path: projectPath } = body;
|
|
79
|
+
if (!type || !action || !value) {
|
|
80
|
+
return { error: 'type, action, and value are required', status: 400 };
|
|
81
|
+
}
|
|
82
|
+
const basePath = projectPath || initialPath;
|
|
83
|
+
try {
|
|
84
|
+
const memoryStore = getMemoryStore(basePath);
|
|
85
|
+
const now = new Date().toISOString();
|
|
86
|
+
// Normalize the value
|
|
87
|
+
const normalizedValue = value.toLowerCase().trim();
|
|
88
|
+
// Upsert entity
|
|
89
|
+
const entityId = memoryStore.upsertEntity({
|
|
90
|
+
type,
|
|
91
|
+
value,
|
|
92
|
+
normalized_value: normalizedValue,
|
|
93
|
+
first_seen_at: now,
|
|
94
|
+
last_seen_at: now,
|
|
95
|
+
metadata: metadata ? JSON.stringify(metadata) : undefined
|
|
96
|
+
});
|
|
97
|
+
// Log access
|
|
98
|
+
memoryStore.logAccess({
|
|
99
|
+
entity_id: entityId,
|
|
100
|
+
action,
|
|
101
|
+
session_id: sessionId,
|
|
102
|
+
timestamp: now,
|
|
103
|
+
context_summary: metadata?.context
|
|
104
|
+
});
|
|
105
|
+
// Update stats
|
|
106
|
+
memoryStore.updateStats(entityId, action);
|
|
107
|
+
// Calculate new heat score
|
|
108
|
+
const heatScore = memoryStore.calculateHeatScore(entityId);
|
|
109
|
+
const stats = memoryStore.getStats(entityId);
|
|
110
|
+
// Broadcast MEMORY_UPDATED event via WebSocket
|
|
111
|
+
broadcastToClients({
|
|
112
|
+
type: 'MEMORY_UPDATED',
|
|
113
|
+
payload: {
|
|
114
|
+
entity: { id: entityId, type, value },
|
|
115
|
+
stats: {
|
|
116
|
+
read_count: stats?.read_count || 0,
|
|
117
|
+
write_count: stats?.write_count || 0,
|
|
118
|
+
mention_count: stats?.mention_count || 0,
|
|
119
|
+
heat_score: heatScore
|
|
120
|
+
},
|
|
121
|
+
timestamp: now
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
success: true,
|
|
126
|
+
entity_id: entityId,
|
|
127
|
+
heat_score: heatScore
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return { error: error.message, status: 500 };
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// API: Memory Module - Get native Claude history from ~/.claude/history.jsonl
|
|
137
|
+
if (pathname === '/api/memory/native-history') {
|
|
138
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
139
|
+
const limit = parseInt(url.searchParams.get('limit') || '100', 10);
|
|
140
|
+
const historyFile = join(homedir(), '.claude', 'history.jsonl');
|
|
141
|
+
try {
|
|
142
|
+
if (!existsSync(historyFile)) {
|
|
143
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
144
|
+
res.end(JSON.stringify({ prompts: [], total: 0, message: 'No history file found' }));
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
const content = readFileSync(historyFile, 'utf8');
|
|
148
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
149
|
+
const allPrompts = [];
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
try {
|
|
152
|
+
const entry = JSON.parse(line);
|
|
153
|
+
// Filter by project if specified
|
|
154
|
+
if (projectPath && entry.project) {
|
|
155
|
+
const normalizedProject = entry.project.replace(/\\/g, '/').toLowerCase();
|
|
156
|
+
const normalizedPath = projectPath.replace(/\\/g, '/').toLowerCase();
|
|
157
|
+
if (!normalizedProject.includes(normalizedPath) && !normalizedPath.includes(normalizedProject)) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
allPrompts.push({
|
|
162
|
+
id: `${entry.sessionId}-${entry.timestamp}`,
|
|
163
|
+
text: entry.display || '',
|
|
164
|
+
timestamp: new Date(entry.timestamp).toISOString(),
|
|
165
|
+
project: entry.project || '',
|
|
166
|
+
session_id: entry.sessionId || '',
|
|
167
|
+
pasted_contents: entry.pastedContents || {},
|
|
168
|
+
// Derive intent from content keywords
|
|
169
|
+
intent: derivePromptIntent(entry.display || ''),
|
|
170
|
+
quality_score: calculateQualityScore(entry.display || '')
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (parseError) {
|
|
174
|
+
// Skip malformed lines
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Sort by timestamp descending
|
|
178
|
+
allPrompts.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
179
|
+
// Apply limit
|
|
180
|
+
const prompts = allPrompts.slice(0, limit);
|
|
181
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
182
|
+
res.end(JSON.stringify({ prompts, total: allPrompts.length }));
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
186
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
// API: Memory Module - Get prompt history
|
|
191
|
+
if (pathname === '/api/memory/prompts') {
|
|
192
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
193
|
+
const limit = parseInt(url.searchParams.get('limit') || '50', 10);
|
|
194
|
+
const search = url.searchParams.get('search') || null;
|
|
195
|
+
const recursive = url.searchParams.get('recursive') !== 'false';
|
|
196
|
+
try {
|
|
197
|
+
let prompts;
|
|
198
|
+
// Recursive mode: aggregate prompts from parent and child projects
|
|
199
|
+
if (recursive && !search) {
|
|
200
|
+
const { getAggregatedPrompts } = await import('../memory-store.js');
|
|
201
|
+
prompts = await getAggregatedPrompts(projectPath, limit);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// Non-recursive mode or search mode: query only current project
|
|
205
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
206
|
+
if (search) {
|
|
207
|
+
prompts = memoryStore.searchPrompts(search, limit);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
// Get all recent prompts (we'll need to add this method to MemoryStore)
|
|
211
|
+
const stmt = memoryStore['db'].prepare(`
|
|
212
|
+
SELECT * FROM prompt_history
|
|
213
|
+
ORDER BY timestamp DESC
|
|
214
|
+
LIMIT ?
|
|
215
|
+
`);
|
|
216
|
+
prompts = stmt.all(limit);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
220
|
+
res.end(JSON.stringify({ prompts }));
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
224
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
// API: Memory Module - Get insights (from prompt_patterns)
|
|
229
|
+
if (pathname === '/api/memory/insights' && req.method === 'GET') {
|
|
230
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
231
|
+
const limitParam = url.searchParams.get('limit');
|
|
232
|
+
const tool = url.searchParams.get('tool') || undefined;
|
|
233
|
+
// Check if this is a request for insights history (has limit or tool param)
|
|
234
|
+
if (limitParam || tool) {
|
|
235
|
+
const limit = parseInt(limitParam || '20', 10);
|
|
236
|
+
try {
|
|
237
|
+
const storeModule = await import('../../tools/cli-history-store.js');
|
|
238
|
+
const store = storeModule.getHistoryStore(projectPath);
|
|
239
|
+
const insights = store.getInsights({ limit, tool });
|
|
240
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
241
|
+
res.end(JSON.stringify({ success: true, insights }));
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
245
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
// Default: Get prompt pattern insights
|
|
250
|
+
try {
|
|
251
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
252
|
+
// Get total prompt count
|
|
253
|
+
const countStmt = memoryStore['db'].prepare(`SELECT COUNT(*) as count FROM prompt_history`);
|
|
254
|
+
const { count: totalPrompts } = countStmt.get();
|
|
255
|
+
// Get top intent
|
|
256
|
+
const topIntentStmt = memoryStore['db'].prepare(`
|
|
257
|
+
SELECT intent_label, COUNT(*) as count
|
|
258
|
+
FROM prompt_history
|
|
259
|
+
WHERE intent_label IS NOT NULL
|
|
260
|
+
GROUP BY intent_label
|
|
261
|
+
ORDER BY count DESC
|
|
262
|
+
LIMIT 1
|
|
263
|
+
`);
|
|
264
|
+
const topIntentRow = topIntentStmt.get();
|
|
265
|
+
// Get average prompt length
|
|
266
|
+
const avgLengthStmt = memoryStore['db'].prepare(`
|
|
267
|
+
SELECT AVG(LENGTH(prompt_text)) as avg_length
|
|
268
|
+
FROM prompt_history
|
|
269
|
+
WHERE prompt_text IS NOT NULL
|
|
270
|
+
`);
|
|
271
|
+
const { avg_length: avgLength } = avgLengthStmt.get();
|
|
272
|
+
// Get prompt patterns
|
|
273
|
+
const patternsStmt = memoryStore['db'].prepare(`
|
|
274
|
+
SELECT * FROM prompt_patterns
|
|
275
|
+
ORDER BY frequency DESC
|
|
276
|
+
LIMIT 10
|
|
277
|
+
`);
|
|
278
|
+
const patterns = patternsStmt.all();
|
|
279
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
280
|
+
res.end(JSON.stringify({
|
|
281
|
+
stats: {
|
|
282
|
+
totalPrompts,
|
|
283
|
+
topIntent: topIntentRow?.intent_label || 'unknown',
|
|
284
|
+
avgLength: Math.round(avgLength || 0)
|
|
285
|
+
},
|
|
286
|
+
patterns: patterns.map((p) => ({
|
|
287
|
+
type: p.pattern_type,
|
|
288
|
+
description: `Pattern detected in prompts`,
|
|
289
|
+
occurrences: p.frequency,
|
|
290
|
+
suggestion: `Consider using more specific prompts for ${p.pattern_type}`
|
|
291
|
+
}))
|
|
292
|
+
}));
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
296
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
297
|
+
}
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
// API: Memory Module - Trigger async CLI-based insights analysis
|
|
301
|
+
if (pathname === '/api/memory/insights/analyze' && req.method === 'POST') {
|
|
302
|
+
handlePostRequest(req, res, async (body) => {
|
|
303
|
+
const projectPath = body.path || initialPath;
|
|
304
|
+
const tool = body.tool || 'gemini'; // gemini, qwen, codex, claude
|
|
305
|
+
const prompts = body.prompts || [];
|
|
306
|
+
const lang = body.lang || 'en'; // Language preference
|
|
307
|
+
if (prompts.length === 0) {
|
|
308
|
+
return { error: 'No prompts provided for analysis', status: 400 };
|
|
309
|
+
}
|
|
310
|
+
// Prepare prompt summary for CLI analysis
|
|
311
|
+
const promptSummary = prompts.slice(0, 20).map((p, i) => {
|
|
312
|
+
return `${i + 1}. [${p.intent || 'unknown'}] ${(p.text || '').substring(0, 100)}...`;
|
|
313
|
+
}).join('\n');
|
|
314
|
+
const langInstruction = lang === 'zh'
|
|
315
|
+
? '请用中文回复。所有 description、suggestion、title 字段必须使用中文。'
|
|
316
|
+
: 'Respond in English. All description, suggestion, title fields must be in English.';
|
|
317
|
+
const analysisPrompt = `
|
|
318
|
+
PURPOSE: Analyze prompt patterns and provide optimization suggestions
|
|
319
|
+
TASK:
|
|
320
|
+
• Review the following prompt history summary
|
|
321
|
+
• Identify common patterns (vague requests, repetitive queries, incomplete context)
|
|
322
|
+
• Suggest specific improvements for prompt quality
|
|
323
|
+
• Detect areas where prompts could be more effective
|
|
324
|
+
MODE: analysis
|
|
325
|
+
CONTEXT: ${prompts.length} prompts from project: ${projectPath}
|
|
326
|
+
EXPECTED: JSON with patterns array and suggestions array
|
|
327
|
+
LANGUAGE: ${langInstruction}
|
|
328
|
+
|
|
329
|
+
PROMPT HISTORY:
|
|
330
|
+
${promptSummary}
|
|
331
|
+
|
|
332
|
+
Return ONLY valid JSON in this exact format (no markdown, no code blocks, just pure JSON):
|
|
333
|
+
{
|
|
334
|
+
"patterns": [
|
|
335
|
+
{"type": "pattern_type", "description": "description", "occurrences": count, "severity": "low|medium|high", "suggestion": "how to improve"}
|
|
336
|
+
],
|
|
337
|
+
"suggestions": [
|
|
338
|
+
{"title": "title", "description": "description", "example": "example prompt"}
|
|
339
|
+
]
|
|
340
|
+
}`;
|
|
341
|
+
try {
|
|
342
|
+
// Queue CLI execution
|
|
343
|
+
const result = await executeCliTool({
|
|
344
|
+
tool,
|
|
345
|
+
prompt: analysisPrompt,
|
|
346
|
+
mode: 'analysis',
|
|
347
|
+
timeout: 120000,
|
|
348
|
+
cd: projectPath,
|
|
349
|
+
category: 'insight'
|
|
350
|
+
});
|
|
351
|
+
// Try to parse JSON from response
|
|
352
|
+
let insights = { patterns: [], suggestions: [] };
|
|
353
|
+
if (result.stdout) {
|
|
354
|
+
let outputText = result.stdout;
|
|
355
|
+
// Strip markdown code blocks if present
|
|
356
|
+
const codeBlockMatch = outputText.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
357
|
+
if (codeBlockMatch) {
|
|
358
|
+
outputText = codeBlockMatch[1].trim();
|
|
359
|
+
}
|
|
360
|
+
// Find JSON object in the response
|
|
361
|
+
const jsonMatch = outputText.match(/\{[\s\S]*\}/);
|
|
362
|
+
if (jsonMatch) {
|
|
363
|
+
try {
|
|
364
|
+
insights = JSON.parse(jsonMatch[0]);
|
|
365
|
+
// Ensure arrays exist
|
|
366
|
+
if (!Array.isArray(insights.patterns))
|
|
367
|
+
insights.patterns = [];
|
|
368
|
+
if (!Array.isArray(insights.suggestions))
|
|
369
|
+
insights.suggestions = [];
|
|
370
|
+
}
|
|
371
|
+
catch (e) {
|
|
372
|
+
console.error('[insights/analyze] JSON parse error:', e);
|
|
373
|
+
// Return raw output if JSON parse fails
|
|
374
|
+
insights = {
|
|
375
|
+
patterns: [{ type: 'raw_analysis', description: result.stdout.substring(0, 500), occurrences: 1, severity: 'low', suggestion: '' }],
|
|
376
|
+
suggestions: []
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
// No JSON found, wrap raw output
|
|
382
|
+
insights = {
|
|
383
|
+
patterns: [{ type: 'raw_analysis', description: result.stdout.substring(0, 500), occurrences: 1, severity: 'low', suggestion: '' }],
|
|
384
|
+
suggestions: []
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Save insight to database
|
|
389
|
+
try {
|
|
390
|
+
const storeModule = await import('../../tools/cli-history-store.js');
|
|
391
|
+
const store = storeModule.getHistoryStore(projectPath);
|
|
392
|
+
const insightId = `insight-${Date.now()}`;
|
|
393
|
+
store.saveInsight({
|
|
394
|
+
id: insightId,
|
|
395
|
+
tool,
|
|
396
|
+
promptCount: prompts.length,
|
|
397
|
+
patterns: insights.patterns,
|
|
398
|
+
suggestions: insights.suggestions,
|
|
399
|
+
rawOutput: result.stdout || '',
|
|
400
|
+
executionId: result.execution?.id,
|
|
401
|
+
lang
|
|
402
|
+
});
|
|
403
|
+
console.log('[Insights] Saved insight:', insightId);
|
|
404
|
+
}
|
|
405
|
+
catch (saveErr) {
|
|
406
|
+
console.warn('[Insights] Failed to save insight:', saveErr.message);
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
success: true,
|
|
410
|
+
insights,
|
|
411
|
+
tool,
|
|
412
|
+
executionId: result.execution.id
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
return { error: error.message, status: 500 };
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
// API: Get single insight detail
|
|
422
|
+
if (pathname.startsWith('/api/memory/insights/') && req.method === 'GET') {
|
|
423
|
+
const insightId = pathname.replace('/api/memory/insights/', '');
|
|
424
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
425
|
+
if (!insightId || insightId === 'analyze') {
|
|
426
|
+
// Skip - handled by other routes
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
const storeModule = await import('../../tools/cli-history-store.js');
|
|
431
|
+
const store = storeModule.getHistoryStore(projectPath);
|
|
432
|
+
const insight = store.getInsight(insightId);
|
|
433
|
+
if (insight) {
|
|
434
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
435
|
+
res.end(JSON.stringify({ success: true, insight }));
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
439
|
+
res.end(JSON.stringify({ error: 'Insight not found' }));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
444
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
445
|
+
}
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
// API: Delete insight
|
|
449
|
+
if (pathname.startsWith('/api/memory/insights/') && req.method === 'DELETE') {
|
|
450
|
+
const insightId = pathname.replace('/api/memory/insights/', '');
|
|
451
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
452
|
+
try {
|
|
453
|
+
const storeModule = await import('../../tools/cli-history-store.js');
|
|
454
|
+
const store = storeModule.getHistoryStore(projectPath);
|
|
455
|
+
const deleted = store.deleteInsight(insightId);
|
|
456
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
457
|
+
res.end(JSON.stringify({ success: deleted }));
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
461
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
462
|
+
}
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
// API: Memory Module - Get hotspot statistics
|
|
466
|
+
if (pathname === '/api/memory/stats') {
|
|
467
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
468
|
+
const filter = url.searchParams.get('filter') || 'all'; // today, week, all
|
|
469
|
+
const limit = parseInt(url.searchParams.get('limit') || '10', 10);
|
|
470
|
+
const recursive = url.searchParams.get('recursive') !== 'false';
|
|
471
|
+
try {
|
|
472
|
+
// If requesting aggregated stats, use the aggregated function
|
|
473
|
+
if (url.searchParams.has('aggregated') || recursive) {
|
|
474
|
+
const { getAggregatedStats } = await import('../memory-store.js');
|
|
475
|
+
const aggregatedStats = await getAggregatedStats(projectPath);
|
|
476
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
477
|
+
res.end(JSON.stringify({
|
|
478
|
+
stats: aggregatedStats,
|
|
479
|
+
aggregated: true
|
|
480
|
+
}));
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
// Original hotspot statistics (non-recursive)
|
|
484
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
485
|
+
const hotEntities = memoryStore.getHotEntities(limit * 4);
|
|
486
|
+
// Filter by time if needed
|
|
487
|
+
let filtered = hotEntities;
|
|
488
|
+
if (filter === 'today') {
|
|
489
|
+
const today = new Date();
|
|
490
|
+
today.setHours(0, 0, 0, 0);
|
|
491
|
+
filtered = hotEntities.filter((e) => new Date(e.last_seen_at) >= today);
|
|
492
|
+
}
|
|
493
|
+
else if (filter === 'week') {
|
|
494
|
+
const weekAgo = new Date();
|
|
495
|
+
weekAgo.setDate(weekAgo.getDate() - 7);
|
|
496
|
+
filtered = hotEntities.filter((e) => new Date(e.last_seen_at) >= weekAgo);
|
|
497
|
+
}
|
|
498
|
+
// Separate into mostRead, mostEdited, and mostMentioned
|
|
499
|
+
const fileEntities = filtered.filter((e) => e.type === 'file');
|
|
500
|
+
const topicEntities = filtered.filter((e) => e.type === 'topic');
|
|
501
|
+
const mostRead = fileEntities
|
|
502
|
+
.filter((e) => e.stats.read_count > 0)
|
|
503
|
+
.sort((a, b) => b.stats.read_count - a.stats.read_count)
|
|
504
|
+
.slice(0, limit)
|
|
505
|
+
.map((e) => ({
|
|
506
|
+
path: e.value,
|
|
507
|
+
file: e.value.split(/[/\\]/).pop(),
|
|
508
|
+
heat: e.stats.read_count,
|
|
509
|
+
count: e.stats.read_count,
|
|
510
|
+
lastSeen: e.last_seen_at
|
|
511
|
+
}));
|
|
512
|
+
const mostEdited = fileEntities
|
|
513
|
+
.filter((e) => e.stats.write_count > 0)
|
|
514
|
+
.sort((a, b) => b.stats.write_count - a.stats.write_count)
|
|
515
|
+
.slice(0, limit)
|
|
516
|
+
.map((e) => ({
|
|
517
|
+
path: e.value,
|
|
518
|
+
file: e.value.split(/[/\\]/).pop(),
|
|
519
|
+
heat: e.stats.write_count,
|
|
520
|
+
count: e.stats.write_count,
|
|
521
|
+
lastSeen: e.last_seen_at
|
|
522
|
+
}));
|
|
523
|
+
const mostMentioned = topicEntities
|
|
524
|
+
.filter((e) => e.stats.mention_count > 0)
|
|
525
|
+
.sort((a, b) => b.stats.mention_count - a.stats.mention_count)
|
|
526
|
+
.slice(0, limit)
|
|
527
|
+
.map((e) => ({
|
|
528
|
+
topic: e.value,
|
|
529
|
+
preview: e.value.substring(0, 100) + (e.value.length > 100 ? '...' : ''),
|
|
530
|
+
heat: e.stats.mention_count,
|
|
531
|
+
count: e.stats.mention_count,
|
|
532
|
+
lastSeen: e.last_seen_at
|
|
533
|
+
}));
|
|
534
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
535
|
+
res.end(JSON.stringify({ stats: { mostRead, mostEdited, mostMentioned } }));
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
539
|
+
res.end(JSON.stringify({ stats: { mostRead: [], mostEdited: [], mostMentioned: [] } }));
|
|
540
|
+
}
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
// API: Memory Module - Get memory graph (file associations with modules and components)
|
|
544
|
+
if (pathname === '/api/memory/graph') {
|
|
545
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
546
|
+
try {
|
|
547
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
548
|
+
const hotEntities = memoryStore.getHotEntities(100);
|
|
549
|
+
// Build file nodes from entities
|
|
550
|
+
const fileEntities = hotEntities.filter((e) => e.type === 'file');
|
|
551
|
+
const fileNodes = fileEntities.map((e) => {
|
|
552
|
+
const fileName = e.value.split(/[/\\]/).pop() || '';
|
|
553
|
+
// Detect component type based on file name patterns
|
|
554
|
+
const isComponent = /\.(tsx|jsx|vue|svelte)$/.test(fileName) ||
|
|
555
|
+
/^[A-Z][a-zA-Z]+\.(ts|js)$/.test(fileName) ||
|
|
556
|
+
fileName.includes('.component.') ||
|
|
557
|
+
fileName.includes('.controller.');
|
|
558
|
+
return {
|
|
559
|
+
id: e.value,
|
|
560
|
+
name: fileName,
|
|
561
|
+
path: e.value,
|
|
562
|
+
type: isComponent ? 'component' : 'file',
|
|
563
|
+
heat: Math.min(25, 8 + e.stats.heat_score / 10)
|
|
564
|
+
};
|
|
565
|
+
});
|
|
566
|
+
// Extract unique modules (directories) from file paths
|
|
567
|
+
const moduleMap = new Map();
|
|
568
|
+
for (const file of fileEntities) {
|
|
569
|
+
const parts = file.value.split(/[/\\]/);
|
|
570
|
+
// Get parent directory as module (skip if root level)
|
|
571
|
+
if (parts.length > 1) {
|
|
572
|
+
const modulePath = parts.slice(0, -1).join('/');
|
|
573
|
+
const moduleName = parts[parts.length - 2] || modulePath;
|
|
574
|
+
// Skip common non-module directories
|
|
575
|
+
if (['node_modules', '.git', 'dist', 'build', '.next', '.nuxt'].includes(moduleName))
|
|
576
|
+
continue;
|
|
577
|
+
if (!moduleMap.has(modulePath)) {
|
|
578
|
+
moduleMap.set(modulePath, { heat: 0, files: [] });
|
|
579
|
+
}
|
|
580
|
+
const mod = moduleMap.get(modulePath);
|
|
581
|
+
mod.heat += file.stats.heat_score / 20;
|
|
582
|
+
mod.files.push(file.value);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// Create module nodes (limit to top modules by heat)
|
|
586
|
+
const moduleNodes = Array.from(moduleMap.entries())
|
|
587
|
+
.sort((a, b) => b[1].heat - a[1].heat)
|
|
588
|
+
.slice(0, 15)
|
|
589
|
+
.map(([modulePath, data]) => ({
|
|
590
|
+
id: modulePath,
|
|
591
|
+
name: modulePath.split(/[/\\]/).pop() || modulePath,
|
|
592
|
+
path: modulePath,
|
|
593
|
+
type: 'module',
|
|
594
|
+
heat: Math.min(20, 12 + data.heat / 5),
|
|
595
|
+
fileCount: data.files.length
|
|
596
|
+
}));
|
|
597
|
+
// Combine all nodes
|
|
598
|
+
const nodes = [...fileNodes, ...moduleNodes];
|
|
599
|
+
const nodeIds = new Set(nodes.map(n => n.id));
|
|
600
|
+
// Build edges from associations
|
|
601
|
+
const edges = [];
|
|
602
|
+
const edgeSet = new Set(); // Prevent duplicate edges
|
|
603
|
+
// Add file-to-file associations
|
|
604
|
+
for (const entity of hotEntities) {
|
|
605
|
+
if (!entity.id || entity.type !== 'file')
|
|
606
|
+
continue;
|
|
607
|
+
const associations = memoryStore.getAssociations(entity.id, 10);
|
|
608
|
+
for (const assoc of associations) {
|
|
609
|
+
if (assoc.target && nodeIds.has(assoc.target.value)) {
|
|
610
|
+
const edgeKey = [entity.value, assoc.target.value].sort().join('|');
|
|
611
|
+
if (!edgeSet.has(edgeKey)) {
|
|
612
|
+
edgeSet.add(edgeKey);
|
|
613
|
+
edges.push({
|
|
614
|
+
source: entity.value,
|
|
615
|
+
target: assoc.target.value,
|
|
616
|
+
weight: assoc.weight
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// Add file-to-module edges (files belong to their parent modules)
|
|
623
|
+
for (const [modulePath, data] of moduleMap.entries()) {
|
|
624
|
+
if (!nodeIds.has(modulePath))
|
|
625
|
+
continue;
|
|
626
|
+
for (const filePath of data.files) {
|
|
627
|
+
if (nodeIds.has(filePath)) {
|
|
628
|
+
const edgeKey = [modulePath, filePath].sort().join('|');
|
|
629
|
+
if (!edgeSet.has(edgeKey)) {
|
|
630
|
+
edgeSet.add(edgeKey);
|
|
631
|
+
edges.push({
|
|
632
|
+
source: modulePath,
|
|
633
|
+
target: filePath,
|
|
634
|
+
weight: 2 // Lower weight for structural relationships
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
641
|
+
res.end(JSON.stringify({ graph: { nodes, edges } }));
|
|
642
|
+
}
|
|
643
|
+
catch (error) {
|
|
644
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
645
|
+
res.end(JSON.stringify({ graph: { nodes: [], edges: [] } }));
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
// API: Memory Module - Get recent context activities
|
|
650
|
+
if (pathname === '/api/memory/recent') {
|
|
651
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
652
|
+
const limit = parseInt(url.searchParams.get('limit') || '20', 10);
|
|
653
|
+
try {
|
|
654
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
655
|
+
// Get recent access logs with entity info - filter to file type only
|
|
656
|
+
const db = memoryStore.db;
|
|
657
|
+
const recentLogs = db.prepare(`
|
|
658
|
+
SELECT a.*, e.type, e.value
|
|
659
|
+
FROM access_logs a
|
|
660
|
+
JOIN entities e ON a.entity_id = e.id
|
|
661
|
+
WHERE e.type = 'file'
|
|
662
|
+
ORDER BY a.timestamp DESC
|
|
663
|
+
LIMIT ?
|
|
664
|
+
`).all(limit * 2); // Fetch more to account for filtering
|
|
665
|
+
// Filter out invalid entries (JSON strings, error messages, etc.)
|
|
666
|
+
const validLogs = recentLogs.filter((log) => {
|
|
667
|
+
const value = log.value || '';
|
|
668
|
+
// Skip if value looks like JSON or contains error-like patterns
|
|
669
|
+
if (value.includes('"status"') || value.includes('"content"') ||
|
|
670
|
+
value.includes('"activeForm"') || value.startsWith('{') ||
|
|
671
|
+
value.startsWith('[') || value.includes('graph 400')) {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
// Must have a file extension or look like a valid path
|
|
675
|
+
const hasExtension = /\.[a-zA-Z0-9]{1,10}$/.test(value);
|
|
676
|
+
const looksLikePath = value.includes('/') || value.includes('\\');
|
|
677
|
+
return hasExtension || looksLikePath;
|
|
678
|
+
}).slice(0, limit);
|
|
679
|
+
const recent = validLogs.map((log) => ({
|
|
680
|
+
type: log.action, // read, write, mention
|
|
681
|
+
timestamp: log.timestamp,
|
|
682
|
+
prompt: log.context_summary || '',
|
|
683
|
+
files: [log.value],
|
|
684
|
+
description: `${log.action}: ${log.value.split(/[/\\]/).pop()}`
|
|
685
|
+
}));
|
|
686
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
687
|
+
res.end(JSON.stringify({ recent }));
|
|
688
|
+
}
|
|
689
|
+
catch (error) {
|
|
690
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
691
|
+
res.end(JSON.stringify({ recent: [] }));
|
|
692
|
+
}
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
// API: Active Memory - Get status
|
|
696
|
+
if (pathname === '/api/memory/active/status') {
|
|
697
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
698
|
+
if (!projectPath) {
|
|
699
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
700
|
+
res.end(JSON.stringify({ enabled: false, status: null, config: { interval: 'manual', tool: 'gemini' } }));
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
try {
|
|
704
|
+
const rulesDir = join(projectPath, '.claude', 'rules');
|
|
705
|
+
const configPath = join(rulesDir, 'active_memory.md');
|
|
706
|
+
const configJsonPath = join(projectPath, '.claude', 'active_memory_config.json');
|
|
707
|
+
const enabled = existsSync(configPath);
|
|
708
|
+
let lastSync = null;
|
|
709
|
+
let fileCount = 0;
|
|
710
|
+
let config = { interval: 'manual', tool: 'gemini' };
|
|
711
|
+
if (enabled) {
|
|
712
|
+
const stats = statSync(configPath);
|
|
713
|
+
lastSync = stats.mtime.toISOString();
|
|
714
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
715
|
+
// Count file sections
|
|
716
|
+
fileCount = (content.match(/^## /gm) || []).length;
|
|
717
|
+
}
|
|
718
|
+
// Load config if exists
|
|
719
|
+
if (existsSync(configJsonPath)) {
|
|
720
|
+
try {
|
|
721
|
+
config = JSON.parse(readFileSync(configJsonPath, 'utf-8'));
|
|
722
|
+
}
|
|
723
|
+
catch (e) { /* ignore parse errors */ }
|
|
724
|
+
}
|
|
725
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
726
|
+
res.end(JSON.stringify({
|
|
727
|
+
enabled,
|
|
728
|
+
status: enabled ? { lastSync, fileCount } : null,
|
|
729
|
+
config
|
|
730
|
+
}));
|
|
731
|
+
}
|
|
732
|
+
catch (error) {
|
|
733
|
+
console.error('Active Memory status error:', error);
|
|
734
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
735
|
+
res.end(JSON.stringify({ enabled: false, status: null, config: { interval: 'manual', tool: 'gemini' } }));
|
|
736
|
+
}
|
|
737
|
+
return true;
|
|
738
|
+
}
|
|
739
|
+
// API: Active Memory - Toggle
|
|
740
|
+
if (pathname === '/api/memory/active/toggle' && req.method === 'POST') {
|
|
741
|
+
let body = '';
|
|
742
|
+
req.on('data', (chunk) => { body += chunk.toString(); });
|
|
743
|
+
req.on('end', async () => {
|
|
744
|
+
try {
|
|
745
|
+
const { enabled, config } = JSON.parse(body || '{}');
|
|
746
|
+
const projectPath = initialPath;
|
|
747
|
+
if (!projectPath) {
|
|
748
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
749
|
+
res.end(JSON.stringify({ error: 'No project path configured' }));
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
const claudeDir = join(projectPath, '.claude');
|
|
753
|
+
const rulesDir = join(claudeDir, 'rules');
|
|
754
|
+
const configPath = join(rulesDir, 'active_memory.md');
|
|
755
|
+
const configJsonPath = join(claudeDir, 'active_memory_config.json');
|
|
756
|
+
if (enabled) {
|
|
757
|
+
// Enable: Create directories and initial file
|
|
758
|
+
if (!existsSync(claudeDir)) {
|
|
759
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
760
|
+
}
|
|
761
|
+
if (!existsSync(rulesDir)) {
|
|
762
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
763
|
+
}
|
|
764
|
+
// Save config
|
|
765
|
+
if (config) {
|
|
766
|
+
writeFileSync(configJsonPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
767
|
+
}
|
|
768
|
+
// Create initial active_memory.md with header
|
|
769
|
+
const initialContent = `# Active Memory - Project Context
|
|
770
|
+
|
|
771
|
+
> Auto-generated understanding of frequently accessed files.
|
|
772
|
+
> Last updated: ${new Date().toISOString()}
|
|
773
|
+
|
|
774
|
+
---
|
|
775
|
+
|
|
776
|
+
*No files analyzed yet. Click "Sync Now" to analyze hot files.*
|
|
777
|
+
`;
|
|
778
|
+
writeFileSync(configPath, initialContent, 'utf-8');
|
|
779
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
780
|
+
res.end(JSON.stringify({ enabled: true, message: 'Active Memory enabled' }));
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
// Disable: Remove the files
|
|
784
|
+
if (existsSync(configPath)) {
|
|
785
|
+
unlinkSync(configPath);
|
|
786
|
+
}
|
|
787
|
+
if (existsSync(configJsonPath)) {
|
|
788
|
+
unlinkSync(configJsonPath);
|
|
789
|
+
}
|
|
790
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
791
|
+
res.end(JSON.stringify({ enabled: false, message: 'Active Memory disabled' }));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
catch (error) {
|
|
795
|
+
console.error('Active Memory toggle error:', error);
|
|
796
|
+
if (!res.headersSent) {
|
|
797
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
798
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
return true;
|
|
803
|
+
}
|
|
804
|
+
// API: Active Memory - Update Config
|
|
805
|
+
if (pathname === '/api/memory/active/config' && req.method === 'POST') {
|
|
806
|
+
let body = '';
|
|
807
|
+
req.on('data', (chunk) => { body += chunk.toString(); });
|
|
808
|
+
req.on('end', async () => {
|
|
809
|
+
try {
|
|
810
|
+
const { config } = JSON.parse(body || '{}');
|
|
811
|
+
const projectPath = initialPath;
|
|
812
|
+
const claudeDir = join(projectPath, '.claude');
|
|
813
|
+
const configJsonPath = join(claudeDir, 'active_memory_config.json');
|
|
814
|
+
if (!existsSync(claudeDir)) {
|
|
815
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
816
|
+
}
|
|
817
|
+
writeFileSync(configJsonPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
818
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
819
|
+
res.end(JSON.stringify({ success: true, config }));
|
|
820
|
+
}
|
|
821
|
+
catch (error) {
|
|
822
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
823
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
// API: Active Memory - Sync (analyze hot files using CLI and update active_memory.md)
|
|
829
|
+
if (pathname === '/api/memory/active/sync' && req.method === 'POST') {
|
|
830
|
+
let body = '';
|
|
831
|
+
req.on('data', (chunk) => { body += chunk.toString(); });
|
|
832
|
+
req.on('end', async () => {
|
|
833
|
+
try {
|
|
834
|
+
const { tool = 'gemini' } = JSON.parse(body || '{}');
|
|
835
|
+
const projectPath = initialPath;
|
|
836
|
+
if (!projectPath) {
|
|
837
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
838
|
+
res.end(JSON.stringify({ error: 'No project path configured' }));
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
const claudeDir = join(projectPath, '.claude');
|
|
842
|
+
const rulesDir = join(claudeDir, 'rules');
|
|
843
|
+
const configPath = join(rulesDir, 'active_memory.md');
|
|
844
|
+
// Get hot files from memory store - with fallback
|
|
845
|
+
let hotFiles = [];
|
|
846
|
+
try {
|
|
847
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
848
|
+
const hotEntities = memoryStore.getHotEntities(20);
|
|
849
|
+
hotFiles = hotEntities
|
|
850
|
+
.filter((e) => e.type === 'file')
|
|
851
|
+
.slice(0, 10);
|
|
852
|
+
}
|
|
853
|
+
catch (memErr) {
|
|
854
|
+
console.warn('[Active Memory] Memory store error, using empty list:', memErr.message);
|
|
855
|
+
}
|
|
856
|
+
// Build file list for CLI analysis
|
|
857
|
+
const filePaths = hotFiles.map((f) => {
|
|
858
|
+
const filePath = f.value;
|
|
859
|
+
return isAbsolute(filePath) ? filePath : join(projectPath, filePath);
|
|
860
|
+
}).filter((p) => existsSync(p));
|
|
861
|
+
// Build the active_memory.md content header
|
|
862
|
+
let content = `# Active Memory - Project Context
|
|
863
|
+
|
|
864
|
+
> Auto-generated understanding of frequently accessed files using ${tool.toUpperCase()}.
|
|
865
|
+
> Last updated: ${new Date().toISOString()}
|
|
866
|
+
> Files analyzed: ${hotFiles.length}
|
|
867
|
+
> CLI Tool: ${tool}
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
`;
|
|
872
|
+
// Use CCW CLI tool to analyze files
|
|
873
|
+
let cliOutput = '';
|
|
874
|
+
// Build CLI prompt
|
|
875
|
+
const cliPrompt = `PURPOSE: Analyze the following hot files and provide a concise understanding of each.
|
|
876
|
+
TASK: For each file, describe its purpose, key exports, dependencies, and how it relates to other files.
|
|
877
|
+
MODE: analysis
|
|
878
|
+
CONTEXT: ${filePaths.map((p) => '@' + p).join(' ')}
|
|
879
|
+
EXPECTED: Markdown format with ## headings for each file, bullet points for key information.
|
|
880
|
+
RULES: Be concise. Focus on practical understanding. Include function signatures for key exports.`;
|
|
881
|
+
// Try to execute CLI using CCW's built-in executor
|
|
882
|
+
try {
|
|
883
|
+
const syncId = `active-memory-${Date.now()}`;
|
|
884
|
+
const result = await executeCliTool({
|
|
885
|
+
tool: tool === 'qwen' ? 'qwen' : 'gemini',
|
|
886
|
+
prompt: cliPrompt,
|
|
887
|
+
mode: 'analysis',
|
|
888
|
+
format: 'plain',
|
|
889
|
+
cd: projectPath,
|
|
890
|
+
timeout: 120000,
|
|
891
|
+
stream: false,
|
|
892
|
+
category: 'internal',
|
|
893
|
+
id: syncId
|
|
894
|
+
});
|
|
895
|
+
if (result.success && result.execution?.output) {
|
|
896
|
+
// Extract stdout from output object with proper serialization
|
|
897
|
+
const output = result.execution.output;
|
|
898
|
+
if (typeof output === 'string') {
|
|
899
|
+
cliOutput = output;
|
|
900
|
+
}
|
|
901
|
+
else if (output && typeof output === 'object') {
|
|
902
|
+
// Handle object output - extract stdout or serialize the object
|
|
903
|
+
if (output.stdout && typeof output.stdout === 'string') {
|
|
904
|
+
cliOutput = output.stdout;
|
|
905
|
+
}
|
|
906
|
+
else if (output.stderr && typeof output.stderr === 'string') {
|
|
907
|
+
cliOutput = output.stderr;
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
// Last resort: serialize the entire object as JSON
|
|
911
|
+
cliOutput = JSON.stringify(output, null, 2);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
cliOutput = '';
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
// Add CLI output to content (only if not empty)
|
|
919
|
+
if (cliOutput && cliOutput.trim()) {
|
|
920
|
+
content += cliOutput + '\n\n---\n\n';
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
catch (cliErr) {
|
|
924
|
+
// Fallback to basic analysis if CLI fails
|
|
925
|
+
console.warn('[Active Memory] CLI analysis failed, using basic analysis:', cliErr.message);
|
|
926
|
+
// Basic analysis fallback
|
|
927
|
+
for (const file of hotFiles) {
|
|
928
|
+
const fileName = file.value.split(/[/\\]/).pop() || file.value;
|
|
929
|
+
const filePath = file.value;
|
|
930
|
+
const heat = file.stats?.heat_score || 0;
|
|
931
|
+
const readCount = file.stats?.read_count || 0;
|
|
932
|
+
const writeCount = file.stats?.write_count || 0;
|
|
933
|
+
content += `## ${fileName}
|
|
934
|
+
|
|
935
|
+
- **Path**: \`${filePath}\`
|
|
936
|
+
- **Heat Score**: ${heat}
|
|
937
|
+
- **Access**: ${readCount} reads, ${writeCount} writes
|
|
938
|
+
- **Last Seen**: ${file.last_seen_at || 'Unknown'}
|
|
939
|
+
|
|
940
|
+
`;
|
|
941
|
+
// Try to read file and generate summary
|
|
942
|
+
try {
|
|
943
|
+
const fullPath = isAbsolute(filePath) ? filePath : join(projectPath, filePath);
|
|
944
|
+
if (existsSync(fullPath)) {
|
|
945
|
+
const stat = statSync(fullPath);
|
|
946
|
+
const ext = extname(fullPath).toLowerCase();
|
|
947
|
+
content += `- **Size**: ${(stat.size / 1024).toFixed(1)} KB\n`;
|
|
948
|
+
content += `- **Type**: ${ext || 'unknown'}\n`;
|
|
949
|
+
const textExts = ['.ts', '.js', '.tsx', '.jsx', '.md', '.json', '.css', '.html', '.vue', '.svelte', '.py', '.go', '.rs'];
|
|
950
|
+
if (textExts.includes(ext) && stat.size < 100000) {
|
|
951
|
+
const fileContent = readFileSync(fullPath, 'utf-8');
|
|
952
|
+
const lines = fileContent.split('\n').slice(0, 30);
|
|
953
|
+
const exports = lines.filter(l => l.includes('export ') || l.includes('function ') ||
|
|
954
|
+
l.includes('class ') || l.includes('interface ')).slice(0, 8);
|
|
955
|
+
if (exports.length > 0) {
|
|
956
|
+
content += `\n**Key Exports**:\n\`\`\`\n${exports.join('\n')}\n\`\`\`\n`;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
catch (fileErr) {
|
|
962
|
+
// Skip file analysis errors
|
|
963
|
+
}
|
|
964
|
+
content += '\n---\n\n';
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
// Ensure directories exist
|
|
968
|
+
if (!existsSync(claudeDir)) {
|
|
969
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
970
|
+
}
|
|
971
|
+
if (!existsSync(rulesDir)) {
|
|
972
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
973
|
+
}
|
|
974
|
+
// Write the file
|
|
975
|
+
writeFileSync(configPath, content, 'utf-8');
|
|
976
|
+
// Broadcast Active Memory sync completion event
|
|
977
|
+
broadcastToClients({
|
|
978
|
+
type: 'ACTIVE_MEMORY_SYNCED',
|
|
979
|
+
payload: {
|
|
980
|
+
filesAnalyzed: hotFiles.length,
|
|
981
|
+
path: configPath,
|
|
982
|
+
tool,
|
|
983
|
+
usedCli: cliOutput.length > 0,
|
|
984
|
+
timestamp: new Date().toISOString()
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
988
|
+
res.end(JSON.stringify({
|
|
989
|
+
success: true,
|
|
990
|
+
filesAnalyzed: hotFiles.length,
|
|
991
|
+
path: configPath,
|
|
992
|
+
usedCli: cliOutput.length > 0
|
|
993
|
+
}));
|
|
994
|
+
}
|
|
995
|
+
catch (error) {
|
|
996
|
+
console.error('[Active Memory] Sync error:', error);
|
|
997
|
+
if (!res.headersSent) {
|
|
998
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
999
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
return true;
|
|
1004
|
+
}
|
|
1005
|
+
// API: Memory Module - Get conversations index
|
|
1006
|
+
if (pathname === '/api/memory/conversations') {
|
|
1007
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
1008
|
+
const project = url.searchParams.get('project') || null;
|
|
1009
|
+
const limit = parseInt(url.searchParams.get('limit') || '20', 10);
|
|
1010
|
+
try {
|
|
1011
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
1012
|
+
let conversations;
|
|
1013
|
+
if (project) {
|
|
1014
|
+
const stmt = memoryStore['db'].prepare(`
|
|
1015
|
+
SELECT * FROM conversations
|
|
1016
|
+
WHERE project_name = ?
|
|
1017
|
+
ORDER BY updated_at DESC
|
|
1018
|
+
LIMIT ?
|
|
1019
|
+
`);
|
|
1020
|
+
conversations = stmt.all(project, limit);
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
conversations = memoryStore.getConversations(limit);
|
|
1024
|
+
}
|
|
1025
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1026
|
+
res.end(JSON.stringify({ conversations }));
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1030
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1031
|
+
}
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
// API: Memory Module - Replay conversation
|
|
1035
|
+
if (pathname.startsWith('/api/memory/replay/')) {
|
|
1036
|
+
const conversationId = pathname.replace('/api/memory/replay/', '');
|
|
1037
|
+
const projectPath = url.searchParams.get('path') || initialPath;
|
|
1038
|
+
if (!conversationId) {
|
|
1039
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1040
|
+
res.end(JSON.stringify({ error: 'Conversation ID is required' }));
|
|
1041
|
+
return true;
|
|
1042
|
+
}
|
|
1043
|
+
try {
|
|
1044
|
+
const memoryStore = getMemoryStore(projectPath);
|
|
1045
|
+
const conversation = memoryStore.getConversation(conversationId);
|
|
1046
|
+
if (!conversation) {
|
|
1047
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1048
|
+
res.end(JSON.stringify({ error: 'Conversation not found' }));
|
|
1049
|
+
return true;
|
|
1050
|
+
}
|
|
1051
|
+
const messages = memoryStore.getMessages(conversationId);
|
|
1052
|
+
// Enhance messages with tool calls
|
|
1053
|
+
const messagesWithTools = [];
|
|
1054
|
+
for (const message of messages) {
|
|
1055
|
+
const toolCalls = message.id ? memoryStore.getToolCalls(message.id) : [];
|
|
1056
|
+
messagesWithTools.push({
|
|
1057
|
+
...message,
|
|
1058
|
+
tool_calls: toolCalls
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1062
|
+
res.end(JSON.stringify({
|
|
1063
|
+
conversation,
|
|
1064
|
+
messages: messagesWithTools
|
|
1065
|
+
}));
|
|
1066
|
+
}
|
|
1067
|
+
catch (error) {
|
|
1068
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1069
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1070
|
+
}
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
// API: Memory Module - Import history (async task)
|
|
1074
|
+
if (pathname === '/api/memory/import' && req.method === 'POST') {
|
|
1075
|
+
handlePostRequest(req, res, async (body) => {
|
|
1076
|
+
const { source = 'all', project, path: projectPath } = body;
|
|
1077
|
+
const basePath = projectPath || initialPath;
|
|
1078
|
+
// Generate task ID for async operation
|
|
1079
|
+
const taskId = `import-${Date.now()}`;
|
|
1080
|
+
// TODO: Implement actual history import using HistoryImporter
|
|
1081
|
+
// For now, return a placeholder response
|
|
1082
|
+
console.log(`[Memory] Import task ${taskId} started: source=${source}, project=${project}`);
|
|
1083
|
+
return {
|
|
1084
|
+
success: true,
|
|
1085
|
+
taskId,
|
|
1086
|
+
message: 'Import task started (not yet implemented)',
|
|
1087
|
+
source,
|
|
1088
|
+
project
|
|
1089
|
+
};
|
|
1090
|
+
});
|
|
1091
|
+
return true;
|
|
1092
|
+
}
|
|
1093
|
+
return false;
|
|
1094
|
+
}
|
|
1095
|
+
//# sourceMappingURL=memory-routes.js.map
|