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,1017 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
2
|
+
import { join, relative } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
/**
|
|
5
|
+
* Parse frontmatter from markdown file
|
|
6
|
+
* Reuses logic from rules-routes.ts
|
|
7
|
+
*/
|
|
8
|
+
function parseClaudeFrontmatter(content) {
|
|
9
|
+
const result = {
|
|
10
|
+
paths: [],
|
|
11
|
+
content: content
|
|
12
|
+
};
|
|
13
|
+
if (content.startsWith('---')) {
|
|
14
|
+
const endIndex = content.indexOf('---', 3);
|
|
15
|
+
if (endIndex > 0) {
|
|
16
|
+
const frontmatter = content.substring(3, endIndex).trim();
|
|
17
|
+
result.content = content.substring(endIndex + 3).trim();
|
|
18
|
+
const lines = frontmatter.split('\n');
|
|
19
|
+
for (const line of lines) {
|
|
20
|
+
const colonIndex = line.indexOf(':');
|
|
21
|
+
if (colonIndex > 0) {
|
|
22
|
+
const key = line.substring(0, colonIndex).trim().toLowerCase();
|
|
23
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
24
|
+
if (key === 'paths') {
|
|
25
|
+
result.paths = value.replace(/^\[|\]$/g, '').split(',').map(t => t.trim()).filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Calculate file statistics
|
|
35
|
+
*/
|
|
36
|
+
function calculateFileStats(content) {
|
|
37
|
+
const lines = content.split('\n').length;
|
|
38
|
+
const words = content.split(/\s+/).filter(w => w.length > 0).length;
|
|
39
|
+
const characters = content.length;
|
|
40
|
+
return { lines, words, characters };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create ClaudeFile object from file path
|
|
44
|
+
*/
|
|
45
|
+
function createClaudeFile(filePath, level, basePath, isMainFile, depth) {
|
|
46
|
+
try {
|
|
47
|
+
if (!existsSync(filePath))
|
|
48
|
+
return null;
|
|
49
|
+
const stat = statSync(filePath);
|
|
50
|
+
const content = readFileSync(filePath, 'utf8');
|
|
51
|
+
const parsed = parseClaudeFrontmatter(content);
|
|
52
|
+
const relativePath = relative(basePath, filePath).replace(/\\/g, '/');
|
|
53
|
+
const fileName = filePath.split(/[\\/]/).pop() || 'CLAUDE.md';
|
|
54
|
+
// Parent directory for module-level files
|
|
55
|
+
const parentDir = level === 'module'
|
|
56
|
+
? filePath.split(/[\\/]/).slice(-2, -1)[0]
|
|
57
|
+
: undefined;
|
|
58
|
+
return {
|
|
59
|
+
id: `${level}-${relativePath}`,
|
|
60
|
+
level,
|
|
61
|
+
path: filePath,
|
|
62
|
+
relativePath,
|
|
63
|
+
name: fileName,
|
|
64
|
+
content: parsed.content,
|
|
65
|
+
size: stat.size,
|
|
66
|
+
lastModified: stat.mtime.toISOString(),
|
|
67
|
+
frontmatter: { paths: parsed.paths },
|
|
68
|
+
stats: calculateFileStats(content),
|
|
69
|
+
isMainFile,
|
|
70
|
+
parentDirectory: parentDir,
|
|
71
|
+
depth
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.error(`Error creating ClaudeFile for ${filePath}:`, e);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Scan rules directory (recursive)
|
|
81
|
+
* Adapted from rules-routes.ts::scanRulesDirectory
|
|
82
|
+
*/
|
|
83
|
+
function scanClaudeRulesDirectory(dirPath, level, basePath) {
|
|
84
|
+
const files = [];
|
|
85
|
+
try {
|
|
86
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const fullPath = join(dirPath, entry.name);
|
|
89
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
90
|
+
const file = createClaudeFile(fullPath, level, basePath, false);
|
|
91
|
+
if (file)
|
|
92
|
+
files.push(file);
|
|
93
|
+
}
|
|
94
|
+
else if (entry.isDirectory()) {
|
|
95
|
+
const subFiles = scanClaudeRulesDirectory(fullPath, level, basePath);
|
|
96
|
+
files.push(...subFiles);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
// Ignore errors
|
|
102
|
+
}
|
|
103
|
+
return files;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Scan modules for CLAUDE.md files
|
|
107
|
+
* Uses get-modules-by-depth logic
|
|
108
|
+
*/
|
|
109
|
+
function scanModules(projectPath) {
|
|
110
|
+
const modules = [];
|
|
111
|
+
const visited = new Set();
|
|
112
|
+
// Directories to exclude (from get-modules-by-depth.ts)
|
|
113
|
+
const SYSTEM_EXCLUDES = [
|
|
114
|
+
'.git', '.svn', '.hg', '__pycache__', 'node_modules', '.npm', '.yarn',
|
|
115
|
+
'dist', 'build', 'out', '.cache', '.venv', 'venv', 'env', 'coverage'
|
|
116
|
+
];
|
|
117
|
+
function scanDirectory(dirPath, depth) {
|
|
118
|
+
try {
|
|
119
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
120
|
+
// Check for CLAUDE.md in current directory
|
|
121
|
+
const claudePath = join(dirPath, 'CLAUDE.md');
|
|
122
|
+
if (existsSync(claudePath) && !visited.has(claudePath)) {
|
|
123
|
+
visited.add(claudePath);
|
|
124
|
+
const file = createClaudeFile(claudePath, 'module', projectPath, true, depth);
|
|
125
|
+
if (file)
|
|
126
|
+
modules.push(file);
|
|
127
|
+
}
|
|
128
|
+
// Recurse into subdirectories
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
if (!entry.isDirectory())
|
|
131
|
+
continue;
|
|
132
|
+
if (SYSTEM_EXCLUDES.includes(entry.name))
|
|
133
|
+
continue;
|
|
134
|
+
const fullPath = join(dirPath, entry.name);
|
|
135
|
+
scanDirectory(fullPath, depth + 1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
// Ignore permission errors
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
scanDirectory(projectPath, 0);
|
|
143
|
+
return modules.sort((a, b) => (b.depth || 0) - (a.depth || 0)); // Deepest first
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Scan all CLAUDE.md files
|
|
147
|
+
*/
|
|
148
|
+
function scanAllClaudeFiles(projectPath) {
|
|
149
|
+
const result = {
|
|
150
|
+
user: { main: null },
|
|
151
|
+
project: { main: null },
|
|
152
|
+
modules: [],
|
|
153
|
+
summary: { totalFiles: 0, totalSize: 0 }
|
|
154
|
+
};
|
|
155
|
+
// User-level files (only main CLAUDE.md, no rules)
|
|
156
|
+
const userHome = homedir();
|
|
157
|
+
const userClaudeDir = join(userHome, '.claude');
|
|
158
|
+
const userClaudePath = join(userClaudeDir, 'CLAUDE.md');
|
|
159
|
+
if (existsSync(userClaudePath)) {
|
|
160
|
+
result.user.main = createClaudeFile(userClaudePath, 'user', userHome, true);
|
|
161
|
+
}
|
|
162
|
+
// Project-level files (only main CLAUDE.md, no rules)
|
|
163
|
+
const projectClaudeDir = join(projectPath, '.claude');
|
|
164
|
+
const projectClaudePath = join(projectClaudeDir, 'CLAUDE.md');
|
|
165
|
+
if (existsSync(projectClaudePath)) {
|
|
166
|
+
result.project.main = createClaudeFile(projectClaudePath, 'project', projectPath, true);
|
|
167
|
+
}
|
|
168
|
+
// Module-level files
|
|
169
|
+
result.modules = scanModules(projectPath);
|
|
170
|
+
// Calculate summary (only main CLAUDE.md files, no rules)
|
|
171
|
+
const allFiles = [
|
|
172
|
+
result.user.main,
|
|
173
|
+
result.project.main,
|
|
174
|
+
...result.modules
|
|
175
|
+
].filter(f => f !== null);
|
|
176
|
+
result.summary = {
|
|
177
|
+
totalFiles: allFiles.length,
|
|
178
|
+
totalSize: allFiles.reduce((sum, f) => sum + f.size, 0),
|
|
179
|
+
lastSync: new Date().toISOString()
|
|
180
|
+
};
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get single file content
|
|
185
|
+
*/
|
|
186
|
+
function getClaudeFile(filePath) {
|
|
187
|
+
try {
|
|
188
|
+
if (!existsSync(filePath)) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const stat = statSync(filePath);
|
|
192
|
+
const content = readFileSync(filePath, 'utf8');
|
|
193
|
+
const parsed = parseClaudeFrontmatter(content);
|
|
194
|
+
// Determine level based on path
|
|
195
|
+
let level = 'module';
|
|
196
|
+
if (filePath.includes(join(homedir(), '.claude'))) {
|
|
197
|
+
level = 'user';
|
|
198
|
+
}
|
|
199
|
+
else if (filePath.includes('.claude')) {
|
|
200
|
+
level = 'project';
|
|
201
|
+
}
|
|
202
|
+
const isMainFile = filePath.endsWith('CLAUDE.md') && !filePath.includes('rules');
|
|
203
|
+
return {
|
|
204
|
+
id: `${level}-${filePath}`,
|
|
205
|
+
level,
|
|
206
|
+
path: filePath,
|
|
207
|
+
relativePath: filePath,
|
|
208
|
+
name: filePath.split(/[\\/]/).pop() || 'CLAUDE.md',
|
|
209
|
+
content: parsed.content,
|
|
210
|
+
size: stat.size,
|
|
211
|
+
lastModified: stat.mtime.toISOString(),
|
|
212
|
+
frontmatter: { paths: parsed.paths },
|
|
213
|
+
stats: calculateFileStats(content),
|
|
214
|
+
isMainFile
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
console.error('Error reading CLAUDE.md file:', error);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Save file content
|
|
224
|
+
*/
|
|
225
|
+
function saveClaudeFile(filePath, content, createBackup = false) {
|
|
226
|
+
try {
|
|
227
|
+
if (!existsSync(filePath)) {
|
|
228
|
+
return { success: false, error: 'File not found' };
|
|
229
|
+
}
|
|
230
|
+
// Create backup if requested
|
|
231
|
+
if (createBackup) {
|
|
232
|
+
const backupPath = `${filePath}.backup-${Date.now()}`;
|
|
233
|
+
const originalContent = readFileSync(filePath, 'utf8');
|
|
234
|
+
writeFileSync(backupPath, originalContent, 'utf8');
|
|
235
|
+
}
|
|
236
|
+
// Write new content
|
|
237
|
+
writeFileSync(filePath, content, 'utf8');
|
|
238
|
+
return { success: true };
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
return { success: false, error: error.message };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Generate CLI prompt for syncing CLAUDE.md files
|
|
246
|
+
*/
|
|
247
|
+
function generateSyncPrompt(level, modulePath) {
|
|
248
|
+
if (level === 'module' && modulePath) {
|
|
249
|
+
// Module-level prompt
|
|
250
|
+
return `PURPOSE: Generate module-level CLAUDE.md for ${modulePath}
|
|
251
|
+
TASK: • Analyze module's purpose and responsibilities • Document public APIs and interfaces • Identify dependencies and integration points • Note testing patterns and conventions
|
|
252
|
+
MODE: analysis
|
|
253
|
+
CONTEXT: @${modulePath}/**/* | Memory: Project conventions from .claude/CLAUDE.md
|
|
254
|
+
EXPECTED: Module-level CLAUDE.md with: - Module purpose (1-2 sentences) - Key files and their roles - Public API documentation - Integration points - Testing approach
|
|
255
|
+
RULES: $(cat ~/.claude/workflows/cli-templates/prompts/planning/02-design-component-spec.txt) | Module-level perspective only | Concrete examples | analysis=READ-ONLY`;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// User/Project level prompt
|
|
259
|
+
const contextPath = level === 'user' ? '~/.claude' : '.claude';
|
|
260
|
+
return `PURPOSE: Update CLAUDE.md with current ${level} understanding
|
|
261
|
+
TASK: • Analyze ${level} configuration and conventions • Identify common patterns and anti-patterns • Generate concise, actionable rules • Maintain existing structure and formatting
|
|
262
|
+
MODE: analysis
|
|
263
|
+
CONTEXT: @${contextPath}/**/*
|
|
264
|
+
EXPECTED: Updated CLAUDE.md content with: - Preserved existing sections - New insights appended to relevant sections - Timestamp header - Focus on ${level}-level concerns
|
|
265
|
+
RULES: $(cat ~/.claude/workflows/cli-templates/prompts/analysis/02-analyze-code-patterns.txt) | Maintain existing CLAUDE.md structure | Focus on actionable rules | analysis=READ-ONLY`;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Smart merge CLAUDE.md content (update mode)
|
|
270
|
+
*/
|
|
271
|
+
function smartMergeContent(existingContent, cliOutput) {
|
|
272
|
+
// For now, use simple append strategy
|
|
273
|
+
// TODO: Implement intelligent section-based merging
|
|
274
|
+
const timestamp = new Date().toISOString();
|
|
275
|
+
const separator = '\n\n---\n\n';
|
|
276
|
+
const header = `## Updated: ${timestamp}\n\n`;
|
|
277
|
+
return existingContent + separator + header + cliOutput;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Scan all files in project directory
|
|
281
|
+
*/
|
|
282
|
+
function scanAllProjectFiles(projectPath) {
|
|
283
|
+
const SYSTEM_EXCLUDES = [
|
|
284
|
+
'.git', '.svn', '.hg', '__pycache__', 'node_modules', '.npm', '.yarn',
|
|
285
|
+
'dist', 'build', 'out', '.cache', '.venv', 'venv', 'env', 'coverage',
|
|
286
|
+
'.next', '.nuxt', '.output', '.turbo', '.parcel-cache', 'logs', 'tmp', 'temp'
|
|
287
|
+
];
|
|
288
|
+
const results = {
|
|
289
|
+
files: [],
|
|
290
|
+
summary: { totalFiles: 0, totalDirectories: 0, totalSize: 0 }
|
|
291
|
+
};
|
|
292
|
+
function scanDir(dirPath, depth = 0) {
|
|
293
|
+
if (depth > 10)
|
|
294
|
+
return []; // Max depth limit
|
|
295
|
+
const files = [];
|
|
296
|
+
try {
|
|
297
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
298
|
+
for (const entry of entries) {
|
|
299
|
+
// Skip system excludes and hidden files (except .claude and .workflow)
|
|
300
|
+
if (SYSTEM_EXCLUDES.includes(entry.name))
|
|
301
|
+
continue;
|
|
302
|
+
if (entry.name.startsWith('.') && entry.name !== '.claude' && entry.name !== '.workflow')
|
|
303
|
+
continue;
|
|
304
|
+
const fullPath = join(dirPath, entry.name);
|
|
305
|
+
const relativePath = relative(projectPath, fullPath).replace(/\\/g, '/');
|
|
306
|
+
if (entry.isDirectory()) {
|
|
307
|
+
results.summary.totalDirectories++;
|
|
308
|
+
const dirNode = {
|
|
309
|
+
path: fullPath,
|
|
310
|
+
name: entry.name,
|
|
311
|
+
type: 'directory',
|
|
312
|
+
depth,
|
|
313
|
+
children: scanDir(fullPath, depth + 1)
|
|
314
|
+
};
|
|
315
|
+
files.push(dirNode);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
const stat = statSync(fullPath);
|
|
319
|
+
results.summary.totalFiles++;
|
|
320
|
+
results.summary.totalSize += stat.size;
|
|
321
|
+
files.push({
|
|
322
|
+
path: fullPath,
|
|
323
|
+
name: entry.name,
|
|
324
|
+
type: 'file',
|
|
325
|
+
size: stat.size,
|
|
326
|
+
lastModified: stat.mtime.toISOString(),
|
|
327
|
+
depth
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
// Ignore permission errors
|
|
334
|
+
}
|
|
335
|
+
return files;
|
|
336
|
+
}
|
|
337
|
+
results.files = scanDir(projectPath);
|
|
338
|
+
return results;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Read single file content
|
|
342
|
+
*/
|
|
343
|
+
function readSingleFile(filePath) {
|
|
344
|
+
try {
|
|
345
|
+
if (!existsSync(filePath))
|
|
346
|
+
return null;
|
|
347
|
+
const stat = statSync(filePath);
|
|
348
|
+
const content = readFileSync(filePath, 'utf8');
|
|
349
|
+
return {
|
|
350
|
+
content,
|
|
351
|
+
size: stat.size,
|
|
352
|
+
lastModified: stat.mtime.toISOString()
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
catch (e) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Delete CLAUDE.md file
|
|
361
|
+
*/
|
|
362
|
+
function deleteClaudeFile(filePath) {
|
|
363
|
+
try {
|
|
364
|
+
if (!existsSync(filePath)) {
|
|
365
|
+
return { success: false, error: 'File not found' };
|
|
366
|
+
}
|
|
367
|
+
// Create backup before deletion
|
|
368
|
+
const backupPath = `${filePath}.deleted-${Date.now()}`;
|
|
369
|
+
const content = readFileSync(filePath, 'utf8');
|
|
370
|
+
writeFileSync(backupPath, content, 'utf8');
|
|
371
|
+
// Delete original file
|
|
372
|
+
const fs = require('fs');
|
|
373
|
+
fs.unlinkSync(filePath);
|
|
374
|
+
return { success: true };
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
return { success: false, error: error.message };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Create new CLAUDE.md file with template
|
|
382
|
+
*/
|
|
383
|
+
function createNewClaudeFile(level, template, pathParam) {
|
|
384
|
+
try {
|
|
385
|
+
let filePath;
|
|
386
|
+
let content;
|
|
387
|
+
// Determine file path
|
|
388
|
+
if (level === 'user') {
|
|
389
|
+
filePath = join(homedir(), '.claude', 'CLAUDE.md');
|
|
390
|
+
}
|
|
391
|
+
else if (level === 'project' && pathParam) {
|
|
392
|
+
filePath = join(pathParam, '.claude', 'CLAUDE.md');
|
|
393
|
+
}
|
|
394
|
+
else if (level === 'module' && pathParam) {
|
|
395
|
+
filePath = join(pathParam, 'CLAUDE.md');
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
return { success: false, error: 'Invalid parameters' };
|
|
399
|
+
}
|
|
400
|
+
// Check if file already exists
|
|
401
|
+
if (existsSync(filePath)) {
|
|
402
|
+
return { success: false, error: 'File already exists' };
|
|
403
|
+
}
|
|
404
|
+
// Generate content based on template
|
|
405
|
+
const timestamp = new Date().toISOString();
|
|
406
|
+
if (template === 'minimal') {
|
|
407
|
+
content = `# CLAUDE.md (${level.toUpperCase()} Level)\n\n> Created: ${timestamp}\n\n## Purpose\n\n[Describe the purpose of this ${level}-level context]\n\n## Guidelines\n\n- [Add guideline 1]\n- [Add guideline 2]\n`;
|
|
408
|
+
}
|
|
409
|
+
else if (template === 'comprehensive') {
|
|
410
|
+
content = `# CLAUDE.md (${level.toUpperCase()} Level)\n\n> Created: ${timestamp}\n\n## Purpose\n\n[Describe the purpose and scope]\n\n## Architecture\n\n[Describe key architectural decisions]\n\n## Coding Conventions\n\n### Naming\n\n- [Convention 1]\n- [Convention 2]\n\n### Patterns\n\n- [Pattern 1]\n- [Pattern 2]\n\n## Testing Guidelines\n\n[Testing approach and conventions]\n\n## Dependencies\n\n[Key dependencies and integration points]\n\n## Common Tasks\n\n### Task 1\n\n[Steps for task 1]\n\n### Task 2\n\n[Steps for task 2]\n`;
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
// default template
|
|
414
|
+
content = `# CLAUDE.md (${level.toUpperCase()} Level)\n\n> Created: ${timestamp}\n\n## Overview\n\n[Brief description of this ${level}-level context]\n\n## Key Conventions\n\n- [Convention 1]\n- [Convention 2]\n- [Convention 3]\n\n## Guidelines\n\n### Code Style\n\n[Style guidelines]\n\n### Best Practices\n\n[Best practices]\n`;
|
|
415
|
+
}
|
|
416
|
+
// Ensure directory exists
|
|
417
|
+
const dir = filePath.substring(0, filePath.lastIndexOf('/') || filePath.lastIndexOf('\\'));
|
|
418
|
+
const fs = require('fs');
|
|
419
|
+
if (!existsSync(dir)) {
|
|
420
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
421
|
+
}
|
|
422
|
+
// Write file
|
|
423
|
+
writeFileSync(filePath, content, 'utf8');
|
|
424
|
+
return { success: true, path: filePath };
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
return { success: false, error: error.message };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Handle CLAUDE.md routes
|
|
432
|
+
*/
|
|
433
|
+
export async function handleClaudeRoutes(ctx) {
|
|
434
|
+
const { pathname, url, req, res, initialPath, handlePostRequest, broadcastToClients } = ctx;
|
|
435
|
+
// API: Scan all CLAUDE.md files
|
|
436
|
+
if (pathname === '/api/memory/claude/scan') {
|
|
437
|
+
const projectPathParam = url.searchParams.get('path') || initialPath;
|
|
438
|
+
const filesData = scanAllClaudeFiles(projectPathParam);
|
|
439
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
440
|
+
res.end(JSON.stringify(filesData));
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
// API: Scan all project files (not just CLAUDE.md)
|
|
444
|
+
if (pathname === '/api/memory/claude/scan-all') {
|
|
445
|
+
const projectPathParam = url.searchParams.get('path') || initialPath;
|
|
446
|
+
const filesData = scanAllProjectFiles(projectPathParam);
|
|
447
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
448
|
+
res.end(JSON.stringify(filesData));
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
// API: Read single file
|
|
452
|
+
if (pathname === '/api/memory/claude/read-file' && req.method === 'GET') {
|
|
453
|
+
const filePath = url.searchParams.get('path');
|
|
454
|
+
if (!filePath) {
|
|
455
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
456
|
+
res.end(JSON.stringify({ error: 'Missing path parameter' }));
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
const fileData = readSingleFile(filePath);
|
|
460
|
+
if (!fileData) {
|
|
461
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
462
|
+
res.end(JSON.stringify({ error: 'File not found' }));
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
466
|
+
res.end(JSON.stringify(fileData));
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
// API: CLI Sync (analyze and update CLAUDE.md using CLI tools)
|
|
470
|
+
if (pathname === '/api/memory/claude/sync' && req.method === 'POST') {
|
|
471
|
+
handlePostRequest(req, res, async (body) => {
|
|
472
|
+
const { level, path: modulePath, tool = 'gemini', mode = 'update', targets } = body;
|
|
473
|
+
if (!level) {
|
|
474
|
+
return { error: 'Missing level parameter', status: 400 };
|
|
475
|
+
}
|
|
476
|
+
try {
|
|
477
|
+
// Import CLI executor
|
|
478
|
+
const { executeCliTool } = await import('../../tools/cli-executor.js');
|
|
479
|
+
// Determine file path based on level
|
|
480
|
+
let filePath;
|
|
481
|
+
let workingDir;
|
|
482
|
+
if (level === 'user') {
|
|
483
|
+
filePath = join(homedir(), '.claude', 'CLAUDE.md');
|
|
484
|
+
workingDir = join(homedir(), '.claude');
|
|
485
|
+
}
|
|
486
|
+
else if (level === 'project') {
|
|
487
|
+
filePath = join(initialPath, '.claude', 'CLAUDE.md');
|
|
488
|
+
workingDir = join(initialPath, '.claude');
|
|
489
|
+
}
|
|
490
|
+
else if (level === 'module' && modulePath) {
|
|
491
|
+
filePath = join(modulePath, 'CLAUDE.md');
|
|
492
|
+
workingDir = modulePath;
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
return { error: 'Invalid level or missing path for module level', status: 400 };
|
|
496
|
+
}
|
|
497
|
+
// Check if file exists (for update/append modes)
|
|
498
|
+
const fileExists = existsSync(filePath);
|
|
499
|
+
if (!fileExists && mode !== 'generate') {
|
|
500
|
+
return { error: 'File does not exist. Use generate mode to create it.', status: 404 };
|
|
501
|
+
}
|
|
502
|
+
// Read existing content
|
|
503
|
+
const existingContent = fileExists ? readFileSync(filePath, 'utf8') : '';
|
|
504
|
+
// Generate CLI prompt
|
|
505
|
+
const cliPrompt = generateSyncPrompt(level, modulePath);
|
|
506
|
+
// Execute CLI tool
|
|
507
|
+
const syncId = `claude-sync-${level}-${Date.now()}`;
|
|
508
|
+
const result = await executeCliTool({
|
|
509
|
+
tool: tool === 'qwen' ? 'qwen' : 'gemini',
|
|
510
|
+
prompt: cliPrompt,
|
|
511
|
+
mode: 'analysis',
|
|
512
|
+
format: 'plain',
|
|
513
|
+
cd: workingDir,
|
|
514
|
+
timeout: 600000, // 10 minutes
|
|
515
|
+
stream: false,
|
|
516
|
+
category: 'internal',
|
|
517
|
+
id: syncId
|
|
518
|
+
});
|
|
519
|
+
if (!result.success || !result.execution?.output) {
|
|
520
|
+
return {
|
|
521
|
+
error: 'CLI execution failed',
|
|
522
|
+
details: result.execution?.error || 'No output received',
|
|
523
|
+
status: 500
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
// Extract CLI output
|
|
527
|
+
const cliOutput = typeof result.execution.output === 'string'
|
|
528
|
+
? result.execution.output
|
|
529
|
+
: result.execution.output.stdout || '';
|
|
530
|
+
if (!cliOutput || cliOutput.trim().length === 0) {
|
|
531
|
+
return { error: 'CLI returned empty output', status: 500 };
|
|
532
|
+
}
|
|
533
|
+
// Process content based on mode
|
|
534
|
+
let finalContent;
|
|
535
|
+
if (mode === 'generate') {
|
|
536
|
+
// Full replace
|
|
537
|
+
const timestamp = new Date().toISOString();
|
|
538
|
+
finalContent = `# CLAUDE.md (${level.toUpperCase()} Level)\n\n> Auto-generated using ${tool.toUpperCase()}\n> Last updated: ${timestamp}\n\n---\n\n${cliOutput}`;
|
|
539
|
+
}
|
|
540
|
+
else if (mode === 'append') {
|
|
541
|
+
// Simple append
|
|
542
|
+
const timestamp = new Date().toISOString();
|
|
543
|
+
finalContent = existingContent + `\n\n---\n\n## Updated: ${timestamp}\n\n${cliOutput}`;
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
// Smart merge (update mode)
|
|
547
|
+
finalContent = smartMergeContent(existingContent, cliOutput);
|
|
548
|
+
}
|
|
549
|
+
// Write updated content
|
|
550
|
+
writeFileSync(filePath, finalContent, 'utf8');
|
|
551
|
+
// Mark file as updated for freshness tracking
|
|
552
|
+
try {
|
|
553
|
+
const { markFileAsUpdated } = await import('../claude-freshness.js');
|
|
554
|
+
markFileAsUpdated(filePath, level, 'cli_sync', initialPath, { tool, mode });
|
|
555
|
+
}
|
|
556
|
+
catch (e) {
|
|
557
|
+
console.error('Failed to mark file as updated:', e);
|
|
558
|
+
}
|
|
559
|
+
// Broadcast WebSocket event
|
|
560
|
+
broadcastToClients({
|
|
561
|
+
type: 'CLAUDE_FILE_SYNCED',
|
|
562
|
+
payload: {
|
|
563
|
+
path: filePath,
|
|
564
|
+
level,
|
|
565
|
+
tool,
|
|
566
|
+
mode,
|
|
567
|
+
executionId: syncId,
|
|
568
|
+
timestamp: new Date().toISOString()
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
return {
|
|
572
|
+
success: true,
|
|
573
|
+
path: filePath,
|
|
574
|
+
executionId: syncId,
|
|
575
|
+
mode,
|
|
576
|
+
tool
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
console.error('Error syncing CLAUDE.md file:', error);
|
|
581
|
+
return {
|
|
582
|
+
error: 'Sync failed',
|
|
583
|
+
details: error.message,
|
|
584
|
+
status: 500
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
// API: Get single file
|
|
591
|
+
if (pathname === '/api/memory/claude/file' && req.method === 'GET') {
|
|
592
|
+
const filePath = url.searchParams.get('path');
|
|
593
|
+
if (!filePath) {
|
|
594
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
595
|
+
res.end(JSON.stringify({ error: 'Missing path parameter' }));
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
const file = getClaudeFile(filePath);
|
|
599
|
+
if (!file) {
|
|
600
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
601
|
+
res.end(JSON.stringify({ error: 'File not found' }));
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
605
|
+
res.end(JSON.stringify(file));
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
// API: Save file
|
|
609
|
+
if (pathname === '/api/memory/claude/file' && req.method === 'POST') {
|
|
610
|
+
handlePostRequest(req, res, async (body) => {
|
|
611
|
+
const { path: filePath, content, createBackup } = body;
|
|
612
|
+
if (!filePath || content === undefined) {
|
|
613
|
+
return { error: 'Missing path or content parameter', status: 400 };
|
|
614
|
+
}
|
|
615
|
+
const result = saveClaudeFile(filePath, content, createBackup);
|
|
616
|
+
if (result.success) {
|
|
617
|
+
// Broadcast update to all clients
|
|
618
|
+
ctx.broadcastToClients({
|
|
619
|
+
type: 'CLAUDE_FILE_UPDATED',
|
|
620
|
+
data: { path: filePath }
|
|
621
|
+
});
|
|
622
|
+
return { success: true, path: filePath };
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
return { error: result.error, status: 500 };
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
// API: Delete file
|
|
631
|
+
if (pathname === '/api/memory/claude/file' && req.method === 'DELETE') {
|
|
632
|
+
const filePath = url.searchParams.get('path');
|
|
633
|
+
const confirm = url.searchParams.get('confirm');
|
|
634
|
+
if (!filePath) {
|
|
635
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
636
|
+
res.end(JSON.stringify({ error: 'Missing path parameter' }));
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
if (confirm !== 'true') {
|
|
640
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
641
|
+
res.end(JSON.stringify({ error: 'Confirmation required' }));
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
const result = deleteClaudeFile(filePath);
|
|
645
|
+
if (result.success) {
|
|
646
|
+
broadcastToClients({
|
|
647
|
+
type: 'CLAUDE_FILE_DELETED',
|
|
648
|
+
data: { path: filePath }
|
|
649
|
+
});
|
|
650
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
651
|
+
res.end(JSON.stringify({ success: true }));
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
655
|
+
res.end(JSON.stringify({ error: result.error }));
|
|
656
|
+
}
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
// API: Create file
|
|
660
|
+
if (pathname === '/api/memory/claude/create' && req.method === 'POST') {
|
|
661
|
+
handlePostRequest(req, res, async (body) => {
|
|
662
|
+
const { level, path, template = 'default' } = body;
|
|
663
|
+
if (!level) {
|
|
664
|
+
return { error: 'Missing level parameter', status: 400 };
|
|
665
|
+
}
|
|
666
|
+
let result;
|
|
667
|
+
if (level === 'project') {
|
|
668
|
+
// For project level, use initialPath
|
|
669
|
+
const filePath = join(initialPath, '.claude', 'CLAUDE.md');
|
|
670
|
+
result = createNewClaudeFile(level, template, initialPath);
|
|
671
|
+
}
|
|
672
|
+
else if (level === 'module') {
|
|
673
|
+
if (!path) {
|
|
674
|
+
return { error: 'Module path required', status: 400 };
|
|
675
|
+
}
|
|
676
|
+
result = createNewClaudeFile(level, template, path);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
result = createNewClaudeFile(level, template);
|
|
680
|
+
}
|
|
681
|
+
if (result.success) {
|
|
682
|
+
broadcastToClients({
|
|
683
|
+
type: 'CLAUDE_FILE_CREATED',
|
|
684
|
+
data: { path: result.path, level }
|
|
685
|
+
});
|
|
686
|
+
return { success: true, path: result.path };
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
return { error: result.error, status: 500 };
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
694
|
+
// API: Get Chinese response setting status
|
|
695
|
+
if (pathname === '/api/language/chinese-response' && req.method === 'GET') {
|
|
696
|
+
try {
|
|
697
|
+
const userClaudePath = join(homedir(), '.claude', 'CLAUDE.md');
|
|
698
|
+
const chineseRefPattern = /@.*chinese-response\.md/i;
|
|
699
|
+
let enabled = false;
|
|
700
|
+
let guidelinesPath = '';
|
|
701
|
+
// Check if user CLAUDE.md exists and contains Chinese response reference
|
|
702
|
+
if (existsSync(userClaudePath)) {
|
|
703
|
+
const content = readFileSync(userClaudePath, 'utf8');
|
|
704
|
+
enabled = chineseRefPattern.test(content);
|
|
705
|
+
}
|
|
706
|
+
// Find guidelines file path - always use user-level path
|
|
707
|
+
const userGuidelinesPath = join(homedir(), '.claude', 'workflows', 'chinese-response.md');
|
|
708
|
+
if (existsSync(userGuidelinesPath)) {
|
|
709
|
+
guidelinesPath = userGuidelinesPath;
|
|
710
|
+
}
|
|
711
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
712
|
+
res.end(JSON.stringify({
|
|
713
|
+
enabled,
|
|
714
|
+
guidelinesPath,
|
|
715
|
+
guidelinesExists: !!guidelinesPath,
|
|
716
|
+
userClaudeMdExists: existsSync(userClaudePath)
|
|
717
|
+
}));
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
catch (error) {
|
|
721
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
722
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
// API: Toggle Chinese response setting
|
|
727
|
+
if (pathname === '/api/language/chinese-response' && req.method === 'POST') {
|
|
728
|
+
handlePostRequest(req, res, async (body) => {
|
|
729
|
+
const { enabled } = body;
|
|
730
|
+
if (typeof enabled !== 'boolean') {
|
|
731
|
+
return { error: 'Missing or invalid enabled parameter', status: 400 };
|
|
732
|
+
}
|
|
733
|
+
try {
|
|
734
|
+
const userClaudePath = join(homedir(), '.claude', 'CLAUDE.md');
|
|
735
|
+
const userClaudeDir = join(homedir(), '.claude');
|
|
736
|
+
// Find guidelines file path - always use user-level path with ~ shorthand
|
|
737
|
+
const userGuidelinesPath = join(homedir(), '.claude', 'workflows', 'chinese-response.md');
|
|
738
|
+
if (!existsSync(userGuidelinesPath)) {
|
|
739
|
+
return { error: 'Chinese response guidelines file not found at ~/.claude/workflows/chinese-response.md', status: 404 };
|
|
740
|
+
}
|
|
741
|
+
const guidelinesRef = '~/.claude/workflows/chinese-response.md';
|
|
742
|
+
const chineseRefLine = `- **中文回复准则**: @${guidelinesRef}`;
|
|
743
|
+
const chineseRefPattern = /^- \*\*中文回复准则\*\*:.*chinese-response\.md.*$/gm;
|
|
744
|
+
// Ensure user .claude directory exists
|
|
745
|
+
if (!existsSync(userClaudeDir)) {
|
|
746
|
+
const fs = require('fs');
|
|
747
|
+
fs.mkdirSync(userClaudeDir, { recursive: true });
|
|
748
|
+
}
|
|
749
|
+
let content = '';
|
|
750
|
+
if (existsSync(userClaudePath)) {
|
|
751
|
+
content = readFileSync(userClaudePath, 'utf8');
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
// Create new CLAUDE.md with header
|
|
755
|
+
content = '# Claude Instructions\n\n';
|
|
756
|
+
}
|
|
757
|
+
if (enabled) {
|
|
758
|
+
// Check if reference already exists
|
|
759
|
+
if (chineseRefPattern.test(content)) {
|
|
760
|
+
return { success: true, message: 'Already enabled' };
|
|
761
|
+
}
|
|
762
|
+
// Add reference after the header line or at the beginning
|
|
763
|
+
const headerMatch = content.match(/^# Claude Instructions\n\n?/);
|
|
764
|
+
if (headerMatch) {
|
|
765
|
+
const insertPosition = headerMatch[0].length;
|
|
766
|
+
content = content.slice(0, insertPosition) + chineseRefLine + '\n' + content.slice(insertPosition);
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
// Add header and reference
|
|
770
|
+
content = '# Claude Instructions\n\n' + chineseRefLine + '\n' + content;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
// Remove reference
|
|
775
|
+
content = content.replace(chineseRefPattern, '').replace(/\n{3,}/g, '\n\n').trim();
|
|
776
|
+
if (content)
|
|
777
|
+
content += '\n';
|
|
778
|
+
}
|
|
779
|
+
writeFileSync(userClaudePath, content, 'utf8');
|
|
780
|
+
// Broadcast update
|
|
781
|
+
broadcastToClients({
|
|
782
|
+
type: 'LANGUAGE_SETTING_CHANGED',
|
|
783
|
+
data: { chineseResponse: enabled }
|
|
784
|
+
});
|
|
785
|
+
return { success: true, enabled };
|
|
786
|
+
}
|
|
787
|
+
catch (error) {
|
|
788
|
+
return { error: error.message, status: 500 };
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
return true;
|
|
792
|
+
}
|
|
793
|
+
// API: Get Windows platform setting status
|
|
794
|
+
if (pathname === '/api/language/windows-platform' && req.method === 'GET') {
|
|
795
|
+
try {
|
|
796
|
+
const userClaudePath = join(homedir(), '.claude', 'CLAUDE.md');
|
|
797
|
+
const windowsRefPattern = /@.*windows-platform\.md/i;
|
|
798
|
+
let enabled = false;
|
|
799
|
+
let guidelinesPath = '';
|
|
800
|
+
// Check if user CLAUDE.md exists and contains Windows platform reference
|
|
801
|
+
if (existsSync(userClaudePath)) {
|
|
802
|
+
const content = readFileSync(userClaudePath, 'utf8');
|
|
803
|
+
enabled = windowsRefPattern.test(content);
|
|
804
|
+
}
|
|
805
|
+
// Find guidelines file path - always use user-level path
|
|
806
|
+
const userGuidelinesPath = join(homedir(), '.claude', 'workflows', 'windows-platform.md');
|
|
807
|
+
if (existsSync(userGuidelinesPath)) {
|
|
808
|
+
guidelinesPath = userGuidelinesPath;
|
|
809
|
+
}
|
|
810
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
811
|
+
res.end(JSON.stringify({
|
|
812
|
+
enabled,
|
|
813
|
+
guidelinesPath,
|
|
814
|
+
guidelinesExists: !!guidelinesPath,
|
|
815
|
+
userClaudeMdExists: existsSync(userClaudePath)
|
|
816
|
+
}));
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
catch (error) {
|
|
820
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
821
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
// API: Toggle Windows platform setting
|
|
826
|
+
if (pathname === '/api/language/windows-platform' && req.method === 'POST') {
|
|
827
|
+
handlePostRequest(req, res, async (body) => {
|
|
828
|
+
const { enabled } = body;
|
|
829
|
+
if (typeof enabled !== 'boolean') {
|
|
830
|
+
return { error: 'Missing or invalid enabled parameter', status: 400 };
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
const userClaudePath = join(homedir(), '.claude', 'CLAUDE.md');
|
|
834
|
+
const userClaudeDir = join(homedir(), '.claude');
|
|
835
|
+
// Find guidelines file path - always use user-level path with ~ shorthand
|
|
836
|
+
const userGuidelinesPath = join(homedir(), '.claude', 'workflows', 'windows-platform.md');
|
|
837
|
+
if (!existsSync(userGuidelinesPath)) {
|
|
838
|
+
return { error: 'Windows platform guidelines file not found at ~/.claude/workflows/windows-platform.md', status: 404 };
|
|
839
|
+
}
|
|
840
|
+
const guidelinesRef = '~/.claude/workflows/windows-platform.md';
|
|
841
|
+
const windowsRefLine = `- **Windows Platform**: @${guidelinesRef}`;
|
|
842
|
+
const windowsRefPattern = /^- \*\*Windows Platform\*\*:.*windows-platform\.md.*$/gm;
|
|
843
|
+
// Ensure user .claude directory exists
|
|
844
|
+
if (!existsSync(userClaudeDir)) {
|
|
845
|
+
const fs = require('fs');
|
|
846
|
+
fs.mkdirSync(userClaudeDir, { recursive: true });
|
|
847
|
+
}
|
|
848
|
+
let content = '';
|
|
849
|
+
if (existsSync(userClaudePath)) {
|
|
850
|
+
content = readFileSync(userClaudePath, 'utf8');
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
// Create new CLAUDE.md with header
|
|
854
|
+
content = '# Claude Instructions\n\n';
|
|
855
|
+
}
|
|
856
|
+
if (enabled) {
|
|
857
|
+
// Check if reference already exists
|
|
858
|
+
if (windowsRefPattern.test(content)) {
|
|
859
|
+
return { success: true, message: 'Already enabled' };
|
|
860
|
+
}
|
|
861
|
+
// Add reference after the header line or at the beginning
|
|
862
|
+
const headerMatch = content.match(/^# Claude Instructions\n\n?/);
|
|
863
|
+
if (headerMatch) {
|
|
864
|
+
const insertPosition = headerMatch[0].length;
|
|
865
|
+
content = content.slice(0, insertPosition) + windowsRefLine + '\n' + content.slice(insertPosition);
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
// Add header and reference
|
|
869
|
+
content = '# Claude Instructions\n\n' + windowsRefLine + '\n' + content;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
// Remove reference
|
|
874
|
+
content = content.replace(windowsRefPattern, '').replace(/\n{3,}/g, '\n\n').trim();
|
|
875
|
+
if (content)
|
|
876
|
+
content += '\n';
|
|
877
|
+
}
|
|
878
|
+
writeFileSync(userClaudePath, content, 'utf8');
|
|
879
|
+
// Broadcast update
|
|
880
|
+
broadcastToClients({
|
|
881
|
+
type: 'LANGUAGE_SETTING_CHANGED',
|
|
882
|
+
data: { windowsPlatform: enabled }
|
|
883
|
+
});
|
|
884
|
+
return { success: true, enabled };
|
|
885
|
+
}
|
|
886
|
+
catch (error) {
|
|
887
|
+
return { error: error.message, status: 500 };
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
return true;
|
|
891
|
+
}
|
|
892
|
+
// API: Get freshness scores for all CLAUDE.md files
|
|
893
|
+
if (pathname === '/api/memory/claude/freshness' && req.method === 'GET') {
|
|
894
|
+
try {
|
|
895
|
+
const { calculateAllFreshness } = await import('../claude-freshness.js');
|
|
896
|
+
const projectPathParam = url.searchParams.get('path') || initialPath;
|
|
897
|
+
const threshold = parseInt(url.searchParams.get('threshold') || '20', 10);
|
|
898
|
+
// Get all CLAUDE.md files
|
|
899
|
+
const filesData = scanAllClaudeFiles(projectPathParam);
|
|
900
|
+
// Prepare file list for freshness calculation
|
|
901
|
+
const claudeFiles = [];
|
|
902
|
+
if (filesData.user.main) {
|
|
903
|
+
claudeFiles.push({
|
|
904
|
+
path: filesData.user.main.path,
|
|
905
|
+
level: 'user',
|
|
906
|
+
lastModified: filesData.user.main.lastModified
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
if (filesData.project.main) {
|
|
910
|
+
claudeFiles.push({
|
|
911
|
+
path: filesData.project.main.path,
|
|
912
|
+
level: 'project',
|
|
913
|
+
lastModified: filesData.project.main.lastModified
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
for (const module of filesData.modules) {
|
|
917
|
+
claudeFiles.push({
|
|
918
|
+
path: module.path,
|
|
919
|
+
level: 'module',
|
|
920
|
+
lastModified: module.lastModified
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
// Calculate freshness
|
|
924
|
+
const freshnessData = calculateAllFreshness(claudeFiles, projectPathParam, threshold);
|
|
925
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
926
|
+
res.end(JSON.stringify(freshnessData));
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
catch (error) {
|
|
930
|
+
console.error('Error calculating freshness:', error);
|
|
931
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
932
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
// API: Mark a CLAUDE.md file as updated
|
|
937
|
+
if (pathname === '/api/memory/claude/mark-updated' && req.method === 'POST') {
|
|
938
|
+
handlePostRequest(req, res, async (body) => {
|
|
939
|
+
const { path: filePath, source, metadata } = body;
|
|
940
|
+
if (!filePath) {
|
|
941
|
+
return { error: 'Missing path parameter', status: 400 };
|
|
942
|
+
}
|
|
943
|
+
if (!source || !['manual', 'cli_sync', 'dashboard', 'api'].includes(source)) {
|
|
944
|
+
return { error: 'Invalid or missing source parameter', status: 400 };
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const { markFileAsUpdated } = await import('../claude-freshness.js');
|
|
948
|
+
// Determine file level
|
|
949
|
+
let level = 'module';
|
|
950
|
+
if (filePath.includes(join(homedir(), '.claude'))) {
|
|
951
|
+
level = 'user';
|
|
952
|
+
}
|
|
953
|
+
else if (filePath.includes('.claude')) {
|
|
954
|
+
level = 'project';
|
|
955
|
+
}
|
|
956
|
+
const record = markFileAsUpdated(filePath, level, source, initialPath, metadata);
|
|
957
|
+
// Broadcast update
|
|
958
|
+
broadcastToClients({
|
|
959
|
+
type: 'CLAUDE_FRESHNESS_UPDATED',
|
|
960
|
+
data: {
|
|
961
|
+
path: filePath,
|
|
962
|
+
level,
|
|
963
|
+
updatedAt: record.updated_at,
|
|
964
|
+
source
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
return {
|
|
968
|
+
success: true,
|
|
969
|
+
record: {
|
|
970
|
+
id: record.id,
|
|
971
|
+
updated_at: record.updated_at,
|
|
972
|
+
filesChangedBeforeUpdate: record.files_changed_before_update
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
catch (error) {
|
|
977
|
+
console.error('Error marking file as updated:', error);
|
|
978
|
+
return { error: error.message, status: 500 };
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
return true;
|
|
982
|
+
}
|
|
983
|
+
// API: Get update history for a CLAUDE.md file
|
|
984
|
+
if (pathname === '/api/memory/claude/history' && req.method === 'GET') {
|
|
985
|
+
const filePath = url.searchParams.get('path');
|
|
986
|
+
const limit = parseInt(url.searchParams.get('limit') || '50', 10);
|
|
987
|
+
if (!filePath) {
|
|
988
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
989
|
+
res.end(JSON.stringify({ error: 'Missing path parameter' }));
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
try {
|
|
993
|
+
const { getUpdateHistory } = await import('../claude-freshness.js');
|
|
994
|
+
const records = getUpdateHistory(filePath, initialPath, limit);
|
|
995
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
996
|
+
res.end(JSON.stringify({
|
|
997
|
+
records: records.map(r => ({
|
|
998
|
+
id: r.id,
|
|
999
|
+
updated_at: r.updated_at,
|
|
1000
|
+
update_source: r.update_source,
|
|
1001
|
+
git_commit_hash: r.git_commit_hash,
|
|
1002
|
+
files_changed_before_update: r.files_changed_before_update,
|
|
1003
|
+
metadata: r.metadata ? JSON.parse(r.metadata) : undefined
|
|
1004
|
+
}))
|
|
1005
|
+
}));
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
console.error('Error getting update history:', error);
|
|
1010
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1011
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return false;
|
|
1016
|
+
}
|
|
1017
|
+
//# sourceMappingURL=claude-routes.js.map
|