claude-code-workflow 6.1.4 → 6.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +10 -0
- package/.claude/agents/action-planning-agent.md +857 -778
- package/.claude/agents/cli-execution-agent.md +266 -269
- package/.claude/agents/cli-explore-agent.md +2 -2
- package/.claude/agents/cli-lite-planning-agent.md +142 -92
- package/.claude/agents/cli-planning-agent.md +4 -4
- package/.claude/agents/code-developer.md +7 -6
- package/.claude/agents/conceptual-planning-agent.md +2 -2
- package/.claude/agents/context-search-agent.md +31 -32
- package/.claude/agents/doc-generator.md +4 -4
- package/.claude/agents/memory-bridge.md +93 -93
- package/.claude/agents/test-context-search-agent.md +8 -7
- package/.claude/agents/test-fix-agent.md +7 -6
- package/.claude/commands/clean.md +516 -0
- package/.claude/commands/memory/compact.md +383 -0
- package/.claude/commands/memory/docs-full-cli.md +471 -471
- package/.claude/commands/memory/docs-related-cli.md +386 -386
- package/.claude/commands/memory/docs.md +615 -615
- package/.claude/commands/memory/load.md +5 -5
- package/.claude/commands/memory/tech-research-rules.md +310 -0
- package/.claude/commands/memory/update-full.md +332 -332
- package/.claude/commands/memory/workflow-skill-memory.md +4 -4
- package/.claude/commands/task/create.md +151 -151
- package/.claude/commands/version.md +254 -254
- package/.claude/commands/workflow/brainstorm/api-designer.md +587 -585
- package/.claude/commands/workflow/brainstorm/artifacts.md +1 -0
- package/.claude/commands/workflow/brainstorm/auto-parallel.md +443 -443
- package/.claude/commands/workflow/brainstorm/data-architect.md +220 -220
- package/.claude/commands/workflow/brainstorm/product-manager.md +200 -200
- package/.claude/commands/workflow/brainstorm/product-owner.md +200 -200
- package/.claude/commands/workflow/brainstorm/scrum-master.md +200 -200
- package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
- package/.claude/commands/workflow/brainstorm/system-architect.md +389 -387
- package/.claude/commands/workflow/brainstorm/ui-designer.md +221 -221
- package/.claude/commands/workflow/brainstorm/ux-expert.md +221 -221
- package/.claude/commands/workflow/debug.md +321 -0
- package/.claude/commands/workflow/execute.md +13 -0
- package/.claude/commands/workflow/init.md +165 -164
- package/.claude/commands/workflow/lite-execute.md +119 -13
- package/.claude/commands/workflow/lite-fix.md +623 -621
- package/.claude/commands/workflow/lite-plan.md +610 -592
- package/.claude/commands/workflow/plan.md +5 -5
- package/.claude/commands/workflow/review-module-cycle.md +2 -0
- package/.claude/commands/workflow/review-session-cycle.md +2 -0
- package/.claude/commands/workflow/review.md +297 -291
- package/.claude/commands/workflow/session/complete.md +153 -500
- package/.claude/commands/workflow/session/list.md +95 -95
- package/.claude/commands/workflow/session/resume.md +60 -60
- package/.claude/commands/workflow/session/start.md +199 -199
- package/.claude/commands/workflow/tdd-plan.md +3 -3
- package/.claude/commands/workflow/tdd-verify.md +23 -9
- package/.claude/commands/workflow/test-cycle-execute.md +2 -0
- package/.claude/commands/workflow/test-fix-gen.md +699 -699
- package/.claude/commands/workflow/tools/conflict-resolution.md +104 -18
- package/.claude/commands/workflow/tools/context-gather.md +436 -434
- package/.claude/commands/workflow/tools/task-generate-agent.md +490 -291
- package/.claude/commands/workflow/tools/task-generate-tdd.md +18 -10
- package/.claude/commands/workflow/tools/test-concept-enhanced.md +2 -1
- package/.claude/commands/workflow/tools/test-context-gather.md +1 -0
- package/.claude/commands/workflow/tools/test-task-generate.md +1 -0
- package/.claude/commands/workflow/ui-design/import-from-code.md +9 -6
- package/.claude/skills/command-guide/SKILL.md +5 -5
- package/.claude/skills/command-guide/index/all-commands.json +1 -1
- package/.claude/skills/command-guide/index/by-category.json +1 -1
- package/.claude/skills/command-guide/index/by-use-case.json +1 -1
- package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +857 -778
- package/.claude/skills/command-guide/reference/agents/cli-execution-agent.md +266 -269
- package/.claude/skills/command-guide/reference/agents/cli-explore-agent.md +2 -2
- package/.claude/skills/command-guide/reference/agents/cli-lite-planning-agent.md +142 -92
- package/.claude/skills/command-guide/reference/agents/cli-planning-agent.md +4 -4
- package/.claude/skills/command-guide/reference/agents/code-developer.md +7 -6
- package/.claude/skills/command-guide/reference/agents/conceptual-planning-agent.md +2 -2
- package/.claude/skills/command-guide/reference/agents/context-search-agent.md +31 -32
- package/.claude/skills/command-guide/reference/agents/doc-generator.md +4 -4
- package/.claude/skills/command-guide/reference/agents/memory-bridge.md +93 -93
- package/.claude/skills/command-guide/reference/agents/test-context-search-agent.md +8 -7
- package/.claude/skills/command-guide/reference/agents/test-fix-agent.md +7 -6
- package/.claude/skills/command-guide/reference/commands/memory/docs-full-cli.md +471 -471
- package/.claude/skills/command-guide/reference/commands/memory/docs-related-cli.md +386 -386
- package/.claude/skills/command-guide/reference/commands/memory/docs.md +17 -16
- package/.claude/skills/command-guide/reference/commands/memory/load.md +5 -5
- package/.claude/skills/command-guide/reference/commands/memory/tech-research.md +194 -357
- package/.claude/skills/command-guide/reference/commands/memory/update-full.md +332 -332
- package/.claude/skills/command-guide/reference/commands/memory/workflow-skill-memory.md +4 -4
- package/.claude/skills/command-guide/reference/commands/task/create.md +151 -151
- package/.claude/skills/command-guide/reference/commands/version.md +254 -254
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/api-designer.md +585 -585
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/auto-parallel.md +443 -443
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/data-architect.md +220 -220
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-manager.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-owner.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/scrum-master.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/system-architect.md +387 -387
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ui-designer.md +221 -221
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ux-expert.md +221 -221
- package/.claude/skills/command-guide/reference/commands/workflow/execute.md +25 -20
- package/.claude/skills/command-guide/reference/commands/workflow/init.md +164 -164
- package/.claude/skills/command-guide/reference/commands/workflow/lite-execute.md +748 -686
- package/.claude/skills/command-guide/reference/commands/workflow/lite-fix.md +664 -621
- package/.claude/skills/command-guide/reference/commands/workflow/lite-plan.md +645 -592
- package/.claude/skills/command-guide/reference/commands/workflow/plan.md +5 -5
- package/.claude/skills/command-guide/reference/commands/workflow/review.md +25 -18
- package/.claude/skills/command-guide/reference/commands/workflow/session/complete.md +547 -500
- package/.claude/skills/command-guide/reference/commands/workflow/session/list.md +45 -27
- package/.claude/skills/command-guide/reference/commands/workflow/session/resume.md +35 -19
- package/.claude/skills/command-guide/reference/commands/workflow/session/start.md +90 -33
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-plan.md +3 -3
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-verify.md +23 -9
- package/.claude/skills/command-guide/reference/commands/workflow/test-fix-gen.md +699 -699
- package/.claude/skills/command-guide/reference/commands/workflow/tools/conflict-resolution.md +103 -17
- package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +434 -434
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-agent.md +487 -291
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +17 -10
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-concept-enhanced.md +1 -1
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/import-from-code.md +6 -6
- package/.claude/workflows/chinese-response.md +38 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-api.txt +122 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-components.txt +122 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-config.txt +89 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-core.txt +60 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-patterns.txt +70 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-testing.txt +81 -0
- package/.claude/workflows/cli-templates/prompts/rules/tech-rules-agent-prompt.txt +89 -0
- package/.claude/workflows/cli-templates/prompts/workflow/gemini-solution-design.txt +131 -131
- package/.claude/workflows/cli-templates/prompts/workflow/skill-conflict-patterns.txt +5 -9
- package/.claude/workflows/cli-templates/prompts/workflow/skill-lessons-learned.txt +5 -9
- package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +112 -0
- package/.claude/workflows/cli-templates/protocols/write-protocol.md +201 -0
- package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +137 -0
- package/.claude/workflows/cli-templates/schemas/debug-log-json-schema.json +127 -0
- package/.claude/workflows/cli-templates/schemas/fix-plan-json-schema.json +25 -0
- package/.claude/workflows/cli-templates/schemas/plan-json-schema.json +25 -0
- package/.claude/workflows/cli-tools-usage.md +526 -0
- package/{CLAUDE.md → .claude/workflows/coding-philosophy.md} +24 -45
- package/.claude/workflows/context-tools.md +84 -0
- package/.claude/workflows/file-modification.md +64 -0
- package/.claude/workflows/tool-strategy.md +216 -79
- package/.claude/workflows/windows-platform.md +16 -0
- package/.claude/workflows/workflow-architecture.md +942 -942
- package/.codex/AGENTS.md +63 -330
- package/.codex/prompts/debug.md +318 -0
- package/.codex/prompts/execute.md +273 -0
- package/.codex/prompts/lite-execute.md +164 -0
- package/.codex/prompts/lite-plan.md +469 -0
- package/.codex/prompts.zip +0 -0
- package/.gemini/GEMINI.md +25 -164
- package/.qwen/QWEN.md +0 -139
- package/README.md +29 -9
- package/ccw/README.md +30 -6
- package/ccw/bin/ccw-mcp.js +7 -0
- package/ccw/bin/ccw.js +9 -9
- package/ccw/package.json +65 -47
- package/ccw/src/.workflow/.cli-history/history.db +0 -0
- package/ccw/src/.workflow/.cli-history/history.db-shm +0 -0
- package/ccw/src/.workflow/.cli-history/history.db-wal +0 -0
- package/ccw/src/cli.ts +244 -0
- package/ccw/src/commands/cli.ts +740 -0
- package/ccw/src/commands/core-memory.ts +770 -0
- package/ccw/src/commands/hook.ts +315 -0
- package/ccw/src/commands/install.ts +519 -0
- package/ccw/src/commands/{list.js → list.ts} +1 -1
- package/ccw/src/commands/memory.ts +1090 -0
- package/ccw/src/commands/{serve.js → serve.ts} +14 -5
- package/ccw/src/commands/session-path-resolver.ts +372 -0
- package/ccw/src/commands/session.ts +1141 -0
- package/ccw/src/commands/{stop.js → stop.ts} +16 -6
- package/ccw/src/commands/tool.ts +201 -0
- package/ccw/src/commands/{uninstall.js → uninstall.ts} +89 -40
- package/ccw/src/commands/{upgrade.js → upgrade.ts} +68 -23
- package/ccw/src/commands/{view.js → view.ts} +22 -8
- package/ccw/src/config/storage-paths.ts +670 -0
- package/ccw/src/core/cache-manager.ts +294 -0
- package/ccw/src/core/claude-freshness.ts +319 -0
- package/ccw/src/core/core-memory-store.ts +1528 -0
- package/ccw/src/core/{dashboard-generator-patch.js → dashboard-generator-patch.ts} +18 -0
- package/ccw/src/core/{dashboard-generator.js → dashboard-generator.ts} +69 -12
- package/ccw/src/core/data-aggregator.ts +584 -0
- package/ccw/src/core/history-importer.ts +625 -0
- package/ccw/src/core/{lite-scanner.js → lite-scanner-complete.ts} +162 -66
- package/ccw/src/core/lite-scanner.ts +469 -0
- package/ccw/src/core/{manifest.js → manifest.ts} +104 -34
- package/ccw/src/core/memory-embedder-bridge.ts +262 -0
- package/ccw/src/core/memory-store.ts +978 -0
- package/ccw/src/core/routes/ccw-routes.ts +96 -0
- package/ccw/src/core/routes/claude-routes.ts +1183 -0
- package/ccw/src/core/routes/cli-routes.ts +561 -0
- package/ccw/src/core/routes/codexlens-routes.ts +806 -0
- package/ccw/src/core/routes/core-memory-routes.ts +605 -0
- package/ccw/src/core/routes/files-routes.ts +428 -0
- package/ccw/src/core/routes/graph-routes.md +164 -0
- package/ccw/src/core/routes/graph-routes.ts +626 -0
- package/ccw/src/core/routes/help-routes.ts +308 -0
- package/ccw/src/core/routes/hooks-routes.ts +405 -0
- package/ccw/src/core/routes/mcp-routes.ts +1271 -0
- package/ccw/src/core/routes/mcp-routes.ts.backup +550 -0
- package/ccw/src/core/routes/mcp-templates-db.ts +268 -0
- package/ccw/src/core/routes/memory-routes.ts +1206 -0
- package/ccw/src/core/routes/rules-routes.ts +526 -0
- package/ccw/src/core/routes/session-routes.ts +467 -0
- package/ccw/src/core/routes/skills-routes.ts +599 -0
- package/ccw/src/core/routes/status-routes.ts +57 -0
- package/ccw/src/core/routes/system-routes.ts +427 -0
- package/ccw/src/core/server.ts +431 -0
- package/ccw/src/core/session-clustering-service.ts +1258 -0
- package/ccw/src/core/session-scanner.ts +283 -0
- package/ccw/src/core/websocket.ts +190 -0
- package/ccw/src/{index.js → index.ts} +1 -0
- package/ccw/src/mcp-server/index.ts +186 -0
- package/ccw/src/templates/assets/css/github-dark.min.css +10 -0
- package/ccw/src/templates/assets/css/github.min.css +10 -0
- package/ccw/src/templates/assets/js/cytoscape.min.js +32 -0
- package/ccw/src/templates/assets/js/d3.min.js +2 -0
- package/ccw/src/templates/assets/js/highlight.min.js +1244 -0
- package/ccw/src/templates/assets/js/lucide.min.js +12 -0
- package/ccw/src/templates/assets/js/marked.min.js +69 -0
- package/ccw/src/templates/assets/js/tailwind.js +83 -0
- package/ccw/src/templates/dashboard-css/01-base.css +11 -0
- package/ccw/src/templates/dashboard-css/02-session.css +22 -0
- package/ccw/src/templates/dashboard-css/04-lite-tasks.css +10 -0
- package/ccw/src/templates/dashboard-css/06-cards.css +10 -4
- package/ccw/src/templates/dashboard-css/07-managers.css +1178 -7
- package/ccw/src/templates/dashboard-css/09-explorer.css +23 -12
- package/ccw/src/templates/dashboard-css/10-cli-status.css +337 -0
- package/ccw/src/templates/dashboard-css/11-cli-history.css +271 -0
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +796 -0
- package/ccw/src/templates/dashboard-css/13-cli-ccw.css +199 -0
- package/ccw/src/templates/dashboard-css/14-cli-modals.css +258 -0
- package/ccw/src/templates/dashboard-css/15-cli-endpoints.css +305 -0
- package/ccw/src/templates/dashboard-css/16-cli-session.css +241 -0
- package/ccw/src/templates/dashboard-css/17-cli-conversation.css +283 -0
- package/ccw/src/templates/dashboard-css/18-cli-settings.css +160 -0
- package/ccw/src/templates/dashboard-css/19-cli-native-session.css +496 -0
- package/ccw/src/templates/dashboard-css/20-cli-taskqueue.css +188 -0
- package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +310 -0
- package/ccw/src/templates/dashboard-css/22-cli-semantic.css +240 -0
- package/ccw/src/templates/dashboard-css/23-memory.css +2390 -0
- package/ccw/src/templates/dashboard-css/24-prompt-history.css +1089 -0
- package/ccw/src/templates/dashboard-css/25-skills-rules.css +326 -0
- package/ccw/src/templates/dashboard-css/26-claude-manager.css +908 -0
- package/ccw/src/templates/dashboard-css/27-graph-explorer.css +1678 -0
- package/ccw/src/templates/dashboard-css/28-mcp-manager.css +748 -0
- package/ccw/src/templates/dashboard-css/29-help.css +264 -0
- package/ccw/src/templates/dashboard-css/30-core-memory.css +1700 -0
- package/ccw/src/templates/dashboard-js/api.js +162 -142
- package/ccw/src/templates/dashboard-js/components/carousel.js +4 -4
- package/ccw/src/templates/dashboard-js/components/cli-history.js +876 -0
- package/ccw/src/templates/dashboard-js/components/cli-status.js +978 -0
- package/ccw/src/templates/dashboard-js/components/global-notifications.js +508 -219
- package/ccw/src/templates/dashboard-js/components/hook-manager.js +1277 -282
- package/ccw/src/templates/dashboard-js/components/index-manager.js +302 -0
- package/ccw/src/templates/dashboard-js/components/mcp-manager.js +718 -27
- package/ccw/src/templates/dashboard-js/components/modals.js +66 -0
- package/ccw/src/templates/dashboard-js/components/navigation.js +80 -12
- package/ccw/src/templates/dashboard-js/components/notifications.js +758 -194
- package/ccw/src/templates/dashboard-js/components/storage-manager.js +478 -0
- package/ccw/src/templates/dashboard-js/components/tabs-other.js +157 -6
- package/ccw/src/templates/dashboard-js/components/task-queue-sidebar.js +716 -0
- package/ccw/src/templates/dashboard-js/help-i18n.js +272 -0
- package/ccw/src/templates/dashboard-js/i18n.js +2807 -0
- package/ccw/src/templates/dashboard-js/main.js +15 -0
- package/ccw/src/templates/dashboard-js/state.js +243 -42
- package/ccw/src/templates/dashboard-js/utils.js +47 -1
- package/ccw/src/templates/dashboard-js/views/claude-manager.js +912 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +2272 -0
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +964 -0
- package/ccw/src/templates/dashboard-js/views/core-memory-clusters.js +503 -0
- package/ccw/src/templates/dashboard-js/views/core-memory.js +782 -0
- package/ccw/src/templates/dashboard-js/views/explorer.js +888 -852
- package/ccw/src/templates/dashboard-js/views/graph-explorer.js +1157 -0
- package/ccw/src/templates/dashboard-js/views/help.js +856 -0
- package/ccw/src/templates/dashboard-js/views/history.js +337 -0
- package/ccw/src/templates/dashboard-js/views/home.js +61 -15
- package/ccw/src/templates/dashboard-js/views/hook-manager.js +311 -43
- package/ccw/src/templates/dashboard-js/views/lite-tasks.js +204 -28
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2187 -411
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js.backup +1729 -0
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js.new +928 -0
- package/ccw/src/templates/dashboard-js/views/memory.js +1221 -0
- package/ccw/src/templates/dashboard-js/views/prompt-history.js +713 -0
- package/ccw/src/templates/dashboard-js/views/rules-manager.js +828 -0
- package/ccw/src/templates/dashboard-js/views/session-detail.js +54 -53
- package/ccw/src/templates/dashboard-js/views/skills-manager.js +819 -0
- package/ccw/src/templates/dashboard.html +185 -85
- package/ccw/src/templates/hooks-config-example.json +60 -0
- package/ccw/src/tools/classify-folders.ts +245 -0
- package/ccw/src/tools/cli-config-manager.ts +268 -0
- package/ccw/src/tools/cli-executor.ts +2014 -0
- package/ccw/src/tools/cli-history-store.ts +1195 -0
- package/ccw/src/tools/codex-lens.ts +1141 -0
- package/ccw/src/tools/{convert-tokens-to-css.js → convert-tokens-to-css.ts} +73 -23
- package/ccw/src/tools/core-memory.ts +444 -0
- package/ccw/src/tools/detect-changed-modules.ts +325 -0
- package/ccw/src/tools/{discover-design-files.js → discover-design-files.ts} +74 -24
- package/ccw/src/tools/edit-file.ts +568 -0
- package/ccw/src/tools/{generate-module-docs.js → generate-module-docs.ts} +207 -185
- package/ccw/src/tools/{get-modules-by-depth.js → get-modules-by-depth.ts} +120 -79
- package/ccw/src/tools/index.ts +370 -0
- package/ccw/src/tools/native-session-discovery.ts +795 -0
- package/ccw/src/tools/notifier.ts +129 -0
- package/ccw/src/tools/read-file.ts +410 -0
- package/ccw/src/tools/resume-strategy.ts +345 -0
- package/ccw/src/tools/session-content-parser.ts +619 -0
- package/ccw/src/tools/session-manager.ts +1026 -0
- package/ccw/src/tools/smart-context.ts +228 -0
- package/ccw/src/tools/smart-search.ts +2065 -0
- package/ccw/src/tools/smart-search.ts.backup +1233 -0
- package/ccw/src/tools/storage-manager.ts +455 -0
- package/ccw/src/tools/write-file.ts +222 -0
- package/ccw/src/types/config.ts +11 -0
- package/ccw/src/types/index.ts +3 -0
- package/ccw/src/types/session.ts +25 -0
- package/ccw/src/types/tool.ts +41 -0
- package/ccw/src/utils/{browser-launcher.js → browser-launcher.ts} +10 -8
- package/ccw/src/utils/file-utils.ts +48 -0
- package/ccw/src/utils/{path-resolver.js → path-resolver.ts} +114 -78
- package/ccw/src/utils/path-validator.ts +153 -0
- package/ccw/src/utils/{ui.js → ui.ts} +32 -25
- package/codex-lens/pyproject.toml +48 -0
- package/codex-lens/src/codexlens/.workflow/.cli-history/history.db +0 -0
- package/codex-lens/src/codexlens/__init__.py +28 -0
- package/codex-lens/src/codexlens/__main__.py +14 -0
- package/codex-lens/src/codexlens/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/__main__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/errors.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__init__.py +27 -0
- package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +1931 -0
- package/codex-lens/src/codexlens/cli/embedding_manager.py +620 -0
- package/codex-lens/src/codexlens/cli/model_manager.py +289 -0
- package/codex-lens/src/codexlens/cli/output.py +124 -0
- package/codex-lens/src/codexlens/config.py +201 -0
- package/codex-lens/src/codexlens/entities.py +121 -0
- package/codex-lens/src/codexlens/errors.py +55 -0
- package/codex-lens/src/codexlens/indexing/README.md +77 -0
- package/codex-lens/src/codexlens/indexing/__init__.py +4 -0
- package/codex-lens/src/codexlens/indexing/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/indexing/__pycache__/symbol_extractor.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/indexing/symbol_extractor.py +243 -0
- package/codex-lens/src/codexlens/parsers/__init__.py +8 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/encoding.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/tokenizer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/encoding.py +202 -0
- package/codex-lens/src/codexlens/parsers/factory.py +256 -0
- package/codex-lens/src/codexlens/parsers/tokenizer.py +98 -0
- package/codex-lens/src/codexlens/parsers/treesitter_parser.py +335 -0
- package/codex-lens/src/codexlens/search/__init__.py +15 -0
- package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/enrichment.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/query_parser.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +647 -0
- package/codex-lens/src/codexlens/search/enrichment.py +150 -0
- package/codex-lens/src/codexlens/search/hybrid_search.py +313 -0
- package/codex-lens/src/codexlens/search/query_parser.py +242 -0
- package/codex-lens/src/codexlens/search/ranking.py +274 -0
- package/codex-lens/src/codexlens/semantic/__init__.py +39 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/code_extractor.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/graph_analyzer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/llm_enhancer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/ann_index.py +414 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +448 -0
- package/codex-lens/src/codexlens/semantic/code_extractor.py +274 -0
- package/codex-lens/src/codexlens/semantic/embedder.py +185 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +955 -0
- package/codex-lens/src/codexlens/storage/__init__.py +29 -0
- package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/file_cache.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_utils.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/dir_index.py +1850 -0
- package/codex-lens/src/codexlens/storage/file_cache.py +32 -0
- package/codex-lens/src/codexlens/storage/index_tree.py +776 -0
- package/codex-lens/src/codexlens/storage/migration_manager.py +154 -0
- package/codex-lens/src/codexlens/storage/migrations/__init__.py +1 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_001_normalize_keywords.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_002_add_token_metadata.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_003_code_relationships.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_005_cleanup_unused_fields.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_001_normalize_keywords.py +123 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_002_add_token_metadata.py +48 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_004_dual_fts.py +232 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_005_cleanup_unused_fields.py +196 -0
- package/codex-lens/src/codexlens/storage/path_mapper.py +274 -0
- package/codex-lens/src/codexlens/storage/registry.py +670 -0
- package/codex-lens/src/codexlens/storage/sqlite_store.py +576 -0
- package/codex-lens/src/codexlens/storage/sqlite_utils.py +64 -0
- package/package.json +4 -1
- package/.claude/commands/memory/tech-research.md +0 -477
- package/.claude/scripts/classify-folders.sh +0 -39
- package/.claude/scripts/convert_tokens_to_css.sh +0 -229
- package/.claude/scripts/detect_changed_modules.sh +0 -161
- package/.claude/scripts/discover-design-files.sh +0 -87
- package/.claude/scripts/extract-animations.js +0 -243
- package/.claude/scripts/extract-computed-styles.js +0 -118
- package/.claude/scripts/extract-layout-structure.js +0 -411
- package/.claude/scripts/generate_module_docs.sh +0 -717
- package/.claude/scripts/get_modules_by_depth.sh +0 -170
- package/.claude/scripts/ui-generate-preview.sh +0 -395
- package/.claude/scripts/ui-instantiate-prototypes.sh +0 -815
- package/.claude/scripts/update_module_claude.sh +0 -337
- package/.claude/workflows/context-search-strategy.md +0 -77
- package/.claude/workflows/intelligent-tools-strategy.md +0 -662
- package/ccw/src/cli.js +0 -119
- package/ccw/src/commands/install.js +0 -324
- package/ccw/src/commands/tool.js +0 -138
- package/ccw/src/core/data-aggregator.js +0 -409
- package/ccw/src/core/server.js +0 -2063
- package/ccw/src/core/session-scanner.js +0 -235
- package/ccw/src/tools/classify-folders.js +0 -204
- package/ccw/src/tools/detect-changed-modules.js +0 -288
- package/ccw/src/tools/edit-file.js +0 -266
- package/ccw/src/tools/index.js +0 -176
- package/ccw/src/utils/file-utils.js +0 -48
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
"""Global project registry for CodexLens - SQLite storage."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sqlite3
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from codexlens.errors import StorageError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ProjectInfo:
|
|
17
|
+
"""Registered project information."""
|
|
18
|
+
|
|
19
|
+
id: int
|
|
20
|
+
source_root: Path
|
|
21
|
+
index_root: Path
|
|
22
|
+
created_at: float
|
|
23
|
+
last_indexed: float
|
|
24
|
+
total_files: int
|
|
25
|
+
total_dirs: int
|
|
26
|
+
status: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class DirMapping:
|
|
31
|
+
"""Directory to index path mapping."""
|
|
32
|
+
|
|
33
|
+
id: int
|
|
34
|
+
project_id: int
|
|
35
|
+
source_path: Path
|
|
36
|
+
index_path: Path
|
|
37
|
+
depth: int
|
|
38
|
+
files_count: int
|
|
39
|
+
last_updated: float
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RegistryStore:
|
|
43
|
+
"""Global project registry - SQLite storage.
|
|
44
|
+
|
|
45
|
+
Manages indexed projects and directory-to-index path mappings.
|
|
46
|
+
Thread-safe with connection pooling.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
DEFAULT_DB_PATH = Path.home() / ".codexlens" / "registry.db"
|
|
50
|
+
|
|
51
|
+
def __init__(self, db_path: Path | None = None) -> None:
|
|
52
|
+
self.db_path = (db_path or self.DEFAULT_DB_PATH).resolve()
|
|
53
|
+
self._lock = threading.RLock()
|
|
54
|
+
self._local = threading.local()
|
|
55
|
+
self._pool_lock = threading.Lock()
|
|
56
|
+
self._pool: Dict[int, sqlite3.Connection] = {}
|
|
57
|
+
self._pool_generation = 0
|
|
58
|
+
|
|
59
|
+
def _get_connection(self) -> sqlite3.Connection:
|
|
60
|
+
"""Get or create a thread-local database connection."""
|
|
61
|
+
thread_id = threading.get_ident()
|
|
62
|
+
if getattr(self._local, "generation", None) == self._pool_generation:
|
|
63
|
+
conn = getattr(self._local, "conn", None)
|
|
64
|
+
if conn is not None:
|
|
65
|
+
return conn
|
|
66
|
+
|
|
67
|
+
with self._pool_lock:
|
|
68
|
+
conn = self._pool.get(thread_id)
|
|
69
|
+
if conn is None:
|
|
70
|
+
conn = sqlite3.connect(self.db_path, check_same_thread=False)
|
|
71
|
+
conn.row_factory = sqlite3.Row
|
|
72
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
73
|
+
conn.execute("PRAGMA synchronous=NORMAL")
|
|
74
|
+
conn.execute("PRAGMA foreign_keys=ON")
|
|
75
|
+
self._pool[thread_id] = conn
|
|
76
|
+
|
|
77
|
+
self._local.conn = conn
|
|
78
|
+
self._local.generation = self._pool_generation
|
|
79
|
+
return conn
|
|
80
|
+
|
|
81
|
+
def close(self) -> None:
|
|
82
|
+
"""Close all pooled connections."""
|
|
83
|
+
with self._lock:
|
|
84
|
+
with self._pool_lock:
|
|
85
|
+
for conn in self._pool.values():
|
|
86
|
+
conn.close()
|
|
87
|
+
self._pool.clear()
|
|
88
|
+
self._pool_generation += 1
|
|
89
|
+
|
|
90
|
+
if hasattr(self._local, "conn"):
|
|
91
|
+
self._local.conn = None
|
|
92
|
+
if hasattr(self._local, "generation"):
|
|
93
|
+
self._local.generation = self._pool_generation
|
|
94
|
+
|
|
95
|
+
def __enter__(self) -> RegistryStore:
|
|
96
|
+
self.initialize()
|
|
97
|
+
return self
|
|
98
|
+
|
|
99
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
|
|
100
|
+
self.close()
|
|
101
|
+
|
|
102
|
+
def initialize(self) -> None:
|
|
103
|
+
"""Create database and schema."""
|
|
104
|
+
with self._lock:
|
|
105
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
106
|
+
conn = self._get_connection()
|
|
107
|
+
self._create_schema(conn)
|
|
108
|
+
|
|
109
|
+
def _create_schema(self, conn: sqlite3.Connection) -> None:
|
|
110
|
+
"""Create database schema."""
|
|
111
|
+
try:
|
|
112
|
+
conn.execute(
|
|
113
|
+
"""
|
|
114
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
115
|
+
id INTEGER PRIMARY KEY,
|
|
116
|
+
source_root TEXT UNIQUE NOT NULL,
|
|
117
|
+
index_root TEXT NOT NULL,
|
|
118
|
+
created_at REAL,
|
|
119
|
+
last_indexed REAL,
|
|
120
|
+
total_files INTEGER DEFAULT 0,
|
|
121
|
+
total_dirs INTEGER DEFAULT 0,
|
|
122
|
+
status TEXT DEFAULT 'active'
|
|
123
|
+
)
|
|
124
|
+
"""
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
conn.execute(
|
|
128
|
+
"""
|
|
129
|
+
CREATE TABLE IF NOT EXISTS dir_mapping (
|
|
130
|
+
id INTEGER PRIMARY KEY,
|
|
131
|
+
project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
|
|
132
|
+
source_path TEXT NOT NULL,
|
|
133
|
+
index_path TEXT NOT NULL,
|
|
134
|
+
depth INTEGER,
|
|
135
|
+
files_count INTEGER DEFAULT 0,
|
|
136
|
+
last_updated REAL,
|
|
137
|
+
UNIQUE(source_path)
|
|
138
|
+
)
|
|
139
|
+
"""
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
conn.execute(
|
|
143
|
+
"CREATE INDEX IF NOT EXISTS idx_dir_source ON dir_mapping(source_path)"
|
|
144
|
+
)
|
|
145
|
+
conn.execute(
|
|
146
|
+
"CREATE INDEX IF NOT EXISTS idx_dir_project ON dir_mapping(project_id)"
|
|
147
|
+
)
|
|
148
|
+
conn.execute(
|
|
149
|
+
"CREATE INDEX IF NOT EXISTS idx_project_source ON projects(source_root)"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
conn.commit()
|
|
153
|
+
except sqlite3.DatabaseError as exc:
|
|
154
|
+
raise StorageError(f"Failed to initialize registry schema: {exc}") from exc
|
|
155
|
+
|
|
156
|
+
# === Project Operations ===
|
|
157
|
+
|
|
158
|
+
def register_project(self, source_root: Path, index_root: Path) -> ProjectInfo:
|
|
159
|
+
"""Register a new project or update existing one.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
source_root: Source code root directory
|
|
163
|
+
index_root: Index storage root directory
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
ProjectInfo for the registered project
|
|
167
|
+
"""
|
|
168
|
+
with self._lock:
|
|
169
|
+
conn = self._get_connection()
|
|
170
|
+
source_root_str = str(source_root.resolve())
|
|
171
|
+
index_root_str = str(index_root.resolve())
|
|
172
|
+
now = time.time()
|
|
173
|
+
|
|
174
|
+
conn.execute(
|
|
175
|
+
"""
|
|
176
|
+
INSERT INTO projects(source_root, index_root, created_at, last_indexed)
|
|
177
|
+
VALUES(?, ?, ?, ?)
|
|
178
|
+
ON CONFLICT(source_root) DO UPDATE SET
|
|
179
|
+
index_root=excluded.index_root,
|
|
180
|
+
last_indexed=excluded.last_indexed,
|
|
181
|
+
status='active'
|
|
182
|
+
""",
|
|
183
|
+
(source_root_str, index_root_str, now, now),
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
row = conn.execute(
|
|
187
|
+
"SELECT * FROM projects WHERE source_root=?", (source_root_str,)
|
|
188
|
+
).fetchone()
|
|
189
|
+
|
|
190
|
+
conn.commit()
|
|
191
|
+
|
|
192
|
+
if not row:
|
|
193
|
+
raise StorageError(f"Failed to register project: {source_root}")
|
|
194
|
+
|
|
195
|
+
return self._row_to_project_info(row)
|
|
196
|
+
|
|
197
|
+
def unregister_project(self, source_root: Path) -> bool:
|
|
198
|
+
"""Remove a project registration (cascades to directory mappings).
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
source_root: Source code root directory
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
True if project was removed, False if not found
|
|
205
|
+
"""
|
|
206
|
+
with self._lock:
|
|
207
|
+
conn = self._get_connection()
|
|
208
|
+
source_root_str = str(source_root.resolve())
|
|
209
|
+
|
|
210
|
+
row = conn.execute(
|
|
211
|
+
"SELECT id FROM projects WHERE source_root=?", (source_root_str,)
|
|
212
|
+
).fetchone()
|
|
213
|
+
|
|
214
|
+
if not row:
|
|
215
|
+
return False
|
|
216
|
+
|
|
217
|
+
conn.execute("DELETE FROM projects WHERE source_root=?", (source_root_str,))
|
|
218
|
+
conn.commit()
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
def get_project(self, source_root: Path) -> Optional[ProjectInfo]:
|
|
222
|
+
"""Get project information by source root.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
source_root: Source code root directory
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
ProjectInfo if found, None otherwise
|
|
229
|
+
"""
|
|
230
|
+
with self._lock:
|
|
231
|
+
conn = self._get_connection()
|
|
232
|
+
source_root_str = str(source_root.resolve())
|
|
233
|
+
|
|
234
|
+
row = conn.execute(
|
|
235
|
+
"SELECT * FROM projects WHERE source_root=?", (source_root_str,)
|
|
236
|
+
).fetchone()
|
|
237
|
+
|
|
238
|
+
return self._row_to_project_info(row) if row else None
|
|
239
|
+
|
|
240
|
+
def get_project_by_id(self, project_id: int) -> Optional[ProjectInfo]:
|
|
241
|
+
"""Get project information by ID.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
project_id: Project database ID
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
ProjectInfo if found, None otherwise
|
|
248
|
+
"""
|
|
249
|
+
with self._lock:
|
|
250
|
+
conn = self._get_connection()
|
|
251
|
+
|
|
252
|
+
row = conn.execute(
|
|
253
|
+
"SELECT * FROM projects WHERE id=?", (project_id,)
|
|
254
|
+
).fetchone()
|
|
255
|
+
|
|
256
|
+
return self._row_to_project_info(row) if row else None
|
|
257
|
+
|
|
258
|
+
def list_projects(self, status: Optional[str] = None) -> List[ProjectInfo]:
|
|
259
|
+
"""List all registered projects.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
status: Optional status filter ('active', 'stale', 'removed')
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
List of ProjectInfo objects
|
|
266
|
+
"""
|
|
267
|
+
with self._lock:
|
|
268
|
+
conn = self._get_connection()
|
|
269
|
+
|
|
270
|
+
if status:
|
|
271
|
+
rows = conn.execute(
|
|
272
|
+
"SELECT * FROM projects WHERE status=? ORDER BY created_at DESC",
|
|
273
|
+
(status,),
|
|
274
|
+
).fetchall()
|
|
275
|
+
else:
|
|
276
|
+
rows = conn.execute(
|
|
277
|
+
"SELECT * FROM projects ORDER BY created_at DESC"
|
|
278
|
+
).fetchall()
|
|
279
|
+
|
|
280
|
+
return [self._row_to_project_info(row) for row in rows]
|
|
281
|
+
|
|
282
|
+
def update_project_stats(
|
|
283
|
+
self, source_root: Path, total_files: int, total_dirs: int
|
|
284
|
+
) -> None:
|
|
285
|
+
"""Update project statistics.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
source_root: Source code root directory
|
|
289
|
+
total_files: Total number of indexed files
|
|
290
|
+
total_dirs: Total number of indexed directories
|
|
291
|
+
"""
|
|
292
|
+
with self._lock:
|
|
293
|
+
conn = self._get_connection()
|
|
294
|
+
source_root_str = str(source_root.resolve())
|
|
295
|
+
|
|
296
|
+
conn.execute(
|
|
297
|
+
"""
|
|
298
|
+
UPDATE projects
|
|
299
|
+
SET total_files=?, total_dirs=?, last_indexed=?
|
|
300
|
+
WHERE source_root=?
|
|
301
|
+
""",
|
|
302
|
+
(total_files, total_dirs, time.time(), source_root_str),
|
|
303
|
+
)
|
|
304
|
+
conn.commit()
|
|
305
|
+
|
|
306
|
+
def set_project_status(self, source_root: Path, status: str) -> None:
|
|
307
|
+
"""Set project status.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
source_root: Source code root directory
|
|
311
|
+
status: Status string ('active', 'stale', 'removed')
|
|
312
|
+
"""
|
|
313
|
+
with self._lock:
|
|
314
|
+
conn = self._get_connection()
|
|
315
|
+
source_root_str = str(source_root.resolve())
|
|
316
|
+
|
|
317
|
+
conn.execute(
|
|
318
|
+
"UPDATE projects SET status=? WHERE source_root=?",
|
|
319
|
+
(status, source_root_str),
|
|
320
|
+
)
|
|
321
|
+
conn.commit()
|
|
322
|
+
|
|
323
|
+
# === Directory Mapping Operations ===
|
|
324
|
+
|
|
325
|
+
def register_dir(
|
|
326
|
+
self,
|
|
327
|
+
project_id: int,
|
|
328
|
+
source_path: Path,
|
|
329
|
+
index_path: Path,
|
|
330
|
+
depth: int,
|
|
331
|
+
files_count: int = 0,
|
|
332
|
+
) -> DirMapping:
|
|
333
|
+
"""Register a directory mapping.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
project_id: Project database ID
|
|
337
|
+
source_path: Source directory path
|
|
338
|
+
index_path: Index database path
|
|
339
|
+
depth: Directory depth relative to project root
|
|
340
|
+
files_count: Number of files in directory
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
DirMapping for the registered directory
|
|
344
|
+
"""
|
|
345
|
+
with self._lock:
|
|
346
|
+
conn = self._get_connection()
|
|
347
|
+
source_path_str = str(source_path.resolve())
|
|
348
|
+
index_path_str = str(index_path.resolve())
|
|
349
|
+
now = time.time()
|
|
350
|
+
|
|
351
|
+
conn.execute(
|
|
352
|
+
"""
|
|
353
|
+
INSERT INTO dir_mapping(
|
|
354
|
+
project_id, source_path, index_path, depth, files_count, last_updated
|
|
355
|
+
)
|
|
356
|
+
VALUES(?, ?, ?, ?, ?, ?)
|
|
357
|
+
ON CONFLICT(source_path) DO UPDATE SET
|
|
358
|
+
index_path=excluded.index_path,
|
|
359
|
+
depth=excluded.depth,
|
|
360
|
+
files_count=excluded.files_count,
|
|
361
|
+
last_updated=excluded.last_updated
|
|
362
|
+
""",
|
|
363
|
+
(project_id, source_path_str, index_path_str, depth, files_count, now),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
row = conn.execute(
|
|
367
|
+
"SELECT * FROM dir_mapping WHERE source_path=?", (source_path_str,)
|
|
368
|
+
).fetchone()
|
|
369
|
+
|
|
370
|
+
conn.commit()
|
|
371
|
+
|
|
372
|
+
if not row:
|
|
373
|
+
raise StorageError(f"Failed to register directory: {source_path}")
|
|
374
|
+
|
|
375
|
+
return self._row_to_dir_mapping(row)
|
|
376
|
+
|
|
377
|
+
def unregister_dir(self, source_path: Path) -> bool:
|
|
378
|
+
"""Remove a directory mapping.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
source_path: Source directory path
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
True if directory was removed, False if not found
|
|
385
|
+
"""
|
|
386
|
+
with self._lock:
|
|
387
|
+
conn = self._get_connection()
|
|
388
|
+
source_path_str = str(source_path.resolve())
|
|
389
|
+
|
|
390
|
+
row = conn.execute(
|
|
391
|
+
"SELECT id FROM dir_mapping WHERE source_path=?", (source_path_str,)
|
|
392
|
+
).fetchone()
|
|
393
|
+
|
|
394
|
+
if not row:
|
|
395
|
+
return False
|
|
396
|
+
|
|
397
|
+
conn.execute("DELETE FROM dir_mapping WHERE source_path=?", (source_path_str,))
|
|
398
|
+
conn.commit()
|
|
399
|
+
return True
|
|
400
|
+
|
|
401
|
+
def find_index_path(self, source_path: Path) -> Optional[Path]:
|
|
402
|
+
"""Find index path for a source directory (exact match).
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
source_path: Source directory path
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
Index path if found, None otherwise
|
|
409
|
+
"""
|
|
410
|
+
with self._lock:
|
|
411
|
+
conn = self._get_connection()
|
|
412
|
+
source_path_str = str(source_path.resolve())
|
|
413
|
+
|
|
414
|
+
row = conn.execute(
|
|
415
|
+
"SELECT index_path FROM dir_mapping WHERE source_path=?",
|
|
416
|
+
(source_path_str,),
|
|
417
|
+
).fetchone()
|
|
418
|
+
|
|
419
|
+
return Path(row["index_path"]) if row else None
|
|
420
|
+
|
|
421
|
+
def find_nearest_index(self, source_path: Path) -> Optional[DirMapping]:
|
|
422
|
+
"""Find nearest indexed ancestor directory.
|
|
423
|
+
|
|
424
|
+
Searches for the closest parent directory that has an index.
|
|
425
|
+
Useful for supporting subdirectory searches.
|
|
426
|
+
|
|
427
|
+
Optimized to use single database query instead of iterating through
|
|
428
|
+
each parent directory level.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
source_path: Source directory or file path
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
DirMapping for nearest ancestor, None if not found
|
|
435
|
+
"""
|
|
436
|
+
with self._lock:
|
|
437
|
+
conn = self._get_connection()
|
|
438
|
+
source_path_resolved = source_path.resolve()
|
|
439
|
+
|
|
440
|
+
# Build list of all parent paths from deepest to shallowest
|
|
441
|
+
paths_to_check = []
|
|
442
|
+
current = source_path_resolved
|
|
443
|
+
while True:
|
|
444
|
+
paths_to_check.append(str(current))
|
|
445
|
+
parent = current.parent
|
|
446
|
+
if parent == current: # Reached filesystem root
|
|
447
|
+
break
|
|
448
|
+
current = parent
|
|
449
|
+
|
|
450
|
+
if not paths_to_check:
|
|
451
|
+
return None
|
|
452
|
+
|
|
453
|
+
# Single query with WHERE IN, ordered by path length (longest = nearest)
|
|
454
|
+
placeholders = ','.join('?' * len(paths_to_check))
|
|
455
|
+
query = f"""
|
|
456
|
+
SELECT * FROM dir_mapping
|
|
457
|
+
WHERE source_path IN ({placeholders})
|
|
458
|
+
ORDER BY LENGTH(source_path) DESC
|
|
459
|
+
LIMIT 1
|
|
460
|
+
"""
|
|
461
|
+
|
|
462
|
+
row = conn.execute(query, paths_to_check).fetchone()
|
|
463
|
+
return self._row_to_dir_mapping(row) if row else None
|
|
464
|
+
|
|
465
|
+
def find_by_source_path(self, source_path: str) -> Optional[Dict[str, str]]:
|
|
466
|
+
"""Find project by source path (exact or nearest match).
|
|
467
|
+
|
|
468
|
+
Searches for a project whose source_root matches or contains
|
|
469
|
+
the given source_path.
|
|
470
|
+
|
|
471
|
+
Args:
|
|
472
|
+
source_path: Source directory path as string
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
Dict with project info including 'index_root', or None if not found
|
|
476
|
+
"""
|
|
477
|
+
with self._lock:
|
|
478
|
+
conn = self._get_connection()
|
|
479
|
+
source_path_resolved = str(Path(source_path).resolve())
|
|
480
|
+
|
|
481
|
+
# First try exact match on projects table
|
|
482
|
+
row = conn.execute(
|
|
483
|
+
"SELECT * FROM projects WHERE source_root=?", (source_path_resolved,)
|
|
484
|
+
).fetchone()
|
|
485
|
+
|
|
486
|
+
if row:
|
|
487
|
+
return {
|
|
488
|
+
"id": str(row["id"]),
|
|
489
|
+
"source_root": row["source_root"],
|
|
490
|
+
"index_root": row["index_root"],
|
|
491
|
+
"status": row["status"] or "active",
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
# Try finding project that contains this path
|
|
495
|
+
# Build list of all parent paths
|
|
496
|
+
paths_to_check = []
|
|
497
|
+
current = Path(source_path_resolved)
|
|
498
|
+
while True:
|
|
499
|
+
paths_to_check.append(str(current))
|
|
500
|
+
parent = current.parent
|
|
501
|
+
if parent == current:
|
|
502
|
+
break
|
|
503
|
+
current = parent
|
|
504
|
+
|
|
505
|
+
if paths_to_check:
|
|
506
|
+
placeholders = ','.join('?' * len(paths_to_check))
|
|
507
|
+
query = f"""
|
|
508
|
+
SELECT * FROM projects
|
|
509
|
+
WHERE source_root IN ({placeholders})
|
|
510
|
+
ORDER BY LENGTH(source_root) DESC
|
|
511
|
+
LIMIT 1
|
|
512
|
+
"""
|
|
513
|
+
row = conn.execute(query, paths_to_check).fetchone()
|
|
514
|
+
|
|
515
|
+
if row:
|
|
516
|
+
return {
|
|
517
|
+
"id": str(row["id"]),
|
|
518
|
+
"source_root": row["source_root"],
|
|
519
|
+
"index_root": row["index_root"],
|
|
520
|
+
"status": row["status"] or "active",
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return None
|
|
524
|
+
|
|
525
|
+
def get_project_dirs(self, project_id: int) -> List[DirMapping]:
|
|
526
|
+
"""Get all directory mappings for a project.
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
project_id: Project database ID
|
|
530
|
+
|
|
531
|
+
Returns:
|
|
532
|
+
List of DirMapping objects
|
|
533
|
+
"""
|
|
534
|
+
with self._lock:
|
|
535
|
+
conn = self._get_connection()
|
|
536
|
+
|
|
537
|
+
rows = conn.execute(
|
|
538
|
+
"SELECT * FROM dir_mapping WHERE project_id=? ORDER BY depth, source_path",
|
|
539
|
+
(project_id,),
|
|
540
|
+
).fetchall()
|
|
541
|
+
|
|
542
|
+
return [self._row_to_dir_mapping(row) for row in rows]
|
|
543
|
+
|
|
544
|
+
def get_subdirs(self, source_path: Path) -> List[DirMapping]:
|
|
545
|
+
"""Get direct subdirectory mappings.
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
source_path: Parent directory path
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
List of DirMapping objects for direct children
|
|
552
|
+
"""
|
|
553
|
+
with self._lock:
|
|
554
|
+
conn = self._get_connection()
|
|
555
|
+
source_path_str = str(source_path.resolve())
|
|
556
|
+
|
|
557
|
+
# First get the parent's depth
|
|
558
|
+
parent_row = conn.execute(
|
|
559
|
+
"SELECT depth, project_id FROM dir_mapping WHERE source_path=?",
|
|
560
|
+
(source_path_str,),
|
|
561
|
+
).fetchone()
|
|
562
|
+
|
|
563
|
+
if not parent_row:
|
|
564
|
+
return []
|
|
565
|
+
|
|
566
|
+
parent_depth = int(parent_row["depth"])
|
|
567
|
+
project_id = int(parent_row["project_id"])
|
|
568
|
+
|
|
569
|
+
# Get all subdirs with depth = parent_depth + 1 and matching path prefix
|
|
570
|
+
rows = conn.execute(
|
|
571
|
+
"""
|
|
572
|
+
SELECT * FROM dir_mapping
|
|
573
|
+
WHERE project_id=? AND depth=? AND source_path LIKE ?
|
|
574
|
+
ORDER BY source_path
|
|
575
|
+
""",
|
|
576
|
+
(project_id, parent_depth + 1, f"{source_path_str}%"),
|
|
577
|
+
).fetchall()
|
|
578
|
+
|
|
579
|
+
return [self._row_to_dir_mapping(row) for row in rows]
|
|
580
|
+
|
|
581
|
+
def update_dir_stats(self, source_path: Path, files_count: int) -> None:
|
|
582
|
+
"""Update directory statistics.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
source_path: Source directory path
|
|
586
|
+
files_count: Number of files in directory
|
|
587
|
+
"""
|
|
588
|
+
with self._lock:
|
|
589
|
+
conn = self._get_connection()
|
|
590
|
+
source_path_str = str(source_path.resolve())
|
|
591
|
+
|
|
592
|
+
conn.execute(
|
|
593
|
+
"""
|
|
594
|
+
UPDATE dir_mapping
|
|
595
|
+
SET files_count=?, last_updated=?
|
|
596
|
+
WHERE source_path=?
|
|
597
|
+
""",
|
|
598
|
+
(files_count, time.time(), source_path_str),
|
|
599
|
+
)
|
|
600
|
+
conn.commit()
|
|
601
|
+
|
|
602
|
+
def update_index_paths(self, old_root: Path, new_root: Path) -> int:
|
|
603
|
+
"""Update all index paths after migration.
|
|
604
|
+
|
|
605
|
+
Replaces old_root prefix with new_root in all stored index paths.
|
|
606
|
+
|
|
607
|
+
Args:
|
|
608
|
+
old_root: Old index root directory
|
|
609
|
+
new_root: New index root directory
|
|
610
|
+
|
|
611
|
+
Returns:
|
|
612
|
+
Number of paths updated
|
|
613
|
+
"""
|
|
614
|
+
with self._lock:
|
|
615
|
+
conn = self._get_connection()
|
|
616
|
+
old_root_str = str(old_root.resolve())
|
|
617
|
+
new_root_str = str(new_root.resolve())
|
|
618
|
+
updated = 0
|
|
619
|
+
|
|
620
|
+
# Update projects
|
|
621
|
+
conn.execute(
|
|
622
|
+
"""
|
|
623
|
+
UPDATE projects
|
|
624
|
+
SET index_root = REPLACE(index_root, ?, ?)
|
|
625
|
+
WHERE index_root LIKE ?
|
|
626
|
+
""",
|
|
627
|
+
(old_root_str, new_root_str, f"{old_root_str}%"),
|
|
628
|
+
)
|
|
629
|
+
updated += conn.total_changes
|
|
630
|
+
|
|
631
|
+
# Update dir_mapping
|
|
632
|
+
conn.execute(
|
|
633
|
+
"""
|
|
634
|
+
UPDATE dir_mapping
|
|
635
|
+
SET index_path = REPLACE(index_path, ?, ?)
|
|
636
|
+
WHERE index_path LIKE ?
|
|
637
|
+
""",
|
|
638
|
+
(old_root_str, new_root_str, f"{old_root_str}%"),
|
|
639
|
+
)
|
|
640
|
+
updated += conn.total_changes
|
|
641
|
+
|
|
642
|
+
conn.commit()
|
|
643
|
+
return updated
|
|
644
|
+
|
|
645
|
+
# === Internal Methods ===
|
|
646
|
+
|
|
647
|
+
def _row_to_project_info(self, row: sqlite3.Row) -> ProjectInfo:
|
|
648
|
+
"""Convert database row to ProjectInfo."""
|
|
649
|
+
return ProjectInfo(
|
|
650
|
+
id=int(row["id"]),
|
|
651
|
+
source_root=Path(row["source_root"]),
|
|
652
|
+
index_root=Path(row["index_root"]),
|
|
653
|
+
created_at=float(row["created_at"]) if row["created_at"] else 0.0,
|
|
654
|
+
last_indexed=float(row["last_indexed"]) if row["last_indexed"] else 0.0,
|
|
655
|
+
total_files=int(row["total_files"]) if row["total_files"] else 0,
|
|
656
|
+
total_dirs=int(row["total_dirs"]) if row["total_dirs"] else 0,
|
|
657
|
+
status=str(row["status"]) if row["status"] else "active",
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
def _row_to_dir_mapping(self, row: sqlite3.Row) -> DirMapping:
|
|
661
|
+
"""Convert database row to DirMapping."""
|
|
662
|
+
return DirMapping(
|
|
663
|
+
id=int(row["id"]),
|
|
664
|
+
project_id=int(row["project_id"]),
|
|
665
|
+
source_path=Path(row["source_path"]),
|
|
666
|
+
index_path=Path(row["index_path"]),
|
|
667
|
+
depth=int(row["depth"]) if row["depth"] is not None else 0,
|
|
668
|
+
files_count=int(row["files_count"]) if row["files_count"] else 0,
|
|
669
|
+
last_updated=float(row["last_updated"]) if row["last_updated"] else 0.0,
|
|
670
|
+
)
|