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,576 @@
|
|
|
1
|
+
"""SQLite storage for CodexLens indexing and search."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sqlite3
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import asdict
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
from codexlens.entities import IndexedFile, SearchResult, Symbol
|
|
14
|
+
from codexlens.errors import StorageError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SQLiteStore:
|
|
18
|
+
"""SQLiteStore providing FTS5 search and symbol lookup.
|
|
19
|
+
|
|
20
|
+
Implements thread-local connection pooling for improved performance.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Maximum number of connections to keep in pool to prevent memory leaks
|
|
24
|
+
MAX_POOL_SIZE = 32
|
|
25
|
+
# Idle timeout in seconds (10 minutes)
|
|
26
|
+
IDLE_TIMEOUT = 600
|
|
27
|
+
|
|
28
|
+
def __init__(self, db_path: str | Path) -> None:
|
|
29
|
+
self.db_path = Path(db_path)
|
|
30
|
+
self._lock = threading.RLock()
|
|
31
|
+
self._local = threading.local()
|
|
32
|
+
self._pool_lock = threading.Lock()
|
|
33
|
+
# Pool stores (connection, last_access_time) tuples
|
|
34
|
+
self._pool: Dict[int, Tuple[sqlite3.Connection, float]] = {}
|
|
35
|
+
self._pool_generation = 0
|
|
36
|
+
|
|
37
|
+
def _get_connection(self) -> sqlite3.Connection:
|
|
38
|
+
"""Get or create a thread-local database connection."""
|
|
39
|
+
thread_id = threading.get_ident()
|
|
40
|
+
current_time = time.time()
|
|
41
|
+
|
|
42
|
+
if getattr(self._local, "generation", None) == self._pool_generation:
|
|
43
|
+
conn = getattr(self._local, "conn", None)
|
|
44
|
+
if conn is not None:
|
|
45
|
+
# Update last access time
|
|
46
|
+
with self._pool_lock:
|
|
47
|
+
if thread_id in self._pool:
|
|
48
|
+
self._pool[thread_id] = (conn, current_time)
|
|
49
|
+
return conn
|
|
50
|
+
|
|
51
|
+
with self._pool_lock:
|
|
52
|
+
pool_entry = self._pool.get(thread_id)
|
|
53
|
+
if pool_entry is not None:
|
|
54
|
+
conn, _ = pool_entry
|
|
55
|
+
# Update last access time
|
|
56
|
+
self._pool[thread_id] = (conn, current_time)
|
|
57
|
+
else:
|
|
58
|
+
# Clean up stale and idle connections if pool is too large
|
|
59
|
+
if len(self._pool) >= self.MAX_POOL_SIZE:
|
|
60
|
+
self._cleanup_stale_connections()
|
|
61
|
+
|
|
62
|
+
conn = sqlite3.connect(self.db_path, check_same_thread=False)
|
|
63
|
+
conn.row_factory = sqlite3.Row
|
|
64
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
65
|
+
conn.execute("PRAGMA synchronous=NORMAL")
|
|
66
|
+
conn.execute("PRAGMA foreign_keys=ON")
|
|
67
|
+
# Memory-mapped I/O for faster reads (30GB limit)
|
|
68
|
+
conn.execute("PRAGMA mmap_size=30000000000")
|
|
69
|
+
self._pool[thread_id] = (conn, current_time)
|
|
70
|
+
|
|
71
|
+
self._local.conn = conn
|
|
72
|
+
self._local.generation = self._pool_generation
|
|
73
|
+
return conn
|
|
74
|
+
|
|
75
|
+
def _cleanup_stale_connections(self) -> None:
|
|
76
|
+
"""Remove connections for threads that no longer exist or have been idle too long."""
|
|
77
|
+
current_time = time.time()
|
|
78
|
+
# Get list of active thread IDs
|
|
79
|
+
active_threads = {t.ident for t in threading.enumerate() if t.ident is not None}
|
|
80
|
+
|
|
81
|
+
# Find connections to remove: dead threads or idle timeout exceeded
|
|
82
|
+
stale_ids = []
|
|
83
|
+
for tid, (conn, last_access) in list(self._pool.items()):
|
|
84
|
+
is_dead_thread = tid not in active_threads
|
|
85
|
+
is_idle = (current_time - last_access) > self.IDLE_TIMEOUT
|
|
86
|
+
if is_dead_thread or is_idle:
|
|
87
|
+
stale_ids.append(tid)
|
|
88
|
+
|
|
89
|
+
# Close and remove stale connections
|
|
90
|
+
for tid in stale_ids:
|
|
91
|
+
try:
|
|
92
|
+
conn, _ = self._pool[tid]
|
|
93
|
+
conn.close()
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
96
|
+
del self._pool[tid]
|
|
97
|
+
|
|
98
|
+
def close(self) -> None:
|
|
99
|
+
"""Close all pooled connections."""
|
|
100
|
+
with self._lock:
|
|
101
|
+
with self._pool_lock:
|
|
102
|
+
for conn, _ in self._pool.values():
|
|
103
|
+
conn.close()
|
|
104
|
+
self._pool.clear()
|
|
105
|
+
self._pool_generation += 1
|
|
106
|
+
|
|
107
|
+
if hasattr(self._local, "conn"):
|
|
108
|
+
self._local.conn = None
|
|
109
|
+
if hasattr(self._local, "generation"):
|
|
110
|
+
self._local.generation = self._pool_generation
|
|
111
|
+
|
|
112
|
+
def __enter__(self) -> SQLiteStore:
|
|
113
|
+
self.initialize()
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
|
|
117
|
+
self.close()
|
|
118
|
+
|
|
119
|
+
def execute_query(
|
|
120
|
+
self,
|
|
121
|
+
sql: str,
|
|
122
|
+
params: tuple = (),
|
|
123
|
+
allow_writes: bool = False
|
|
124
|
+
) -> List[Dict[str, Any]]:
|
|
125
|
+
"""Execute a raw SQL query and return results as dictionaries.
|
|
126
|
+
|
|
127
|
+
This is the public API for executing custom queries without bypassing
|
|
128
|
+
encapsulation via _get_connection().
|
|
129
|
+
|
|
130
|
+
By default, only SELECT queries are allowed. Use allow_writes=True
|
|
131
|
+
for trusted internal code that needs to execute other statements.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
sql: SQL query string with ? placeholders for parameters
|
|
135
|
+
params: Tuple of parameter values to bind
|
|
136
|
+
allow_writes: If True, allow non-SELECT statements (default False)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
List of result rows as dictionaries
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
StorageError: If query execution fails or validation fails
|
|
143
|
+
"""
|
|
144
|
+
# Validate query type for security
|
|
145
|
+
sql_stripped = sql.strip().upper()
|
|
146
|
+
if not allow_writes:
|
|
147
|
+
# Only allow SELECT and WITH (for CTEs) statements
|
|
148
|
+
if not (sql_stripped.startswith("SELECT") or sql_stripped.startswith("WITH")):
|
|
149
|
+
raise StorageError(
|
|
150
|
+
"Only SELECT queries are allowed. "
|
|
151
|
+
"Use allow_writes=True for trusted internal operations.",
|
|
152
|
+
db_path=str(self.db_path),
|
|
153
|
+
operation="execute_query",
|
|
154
|
+
details={"query_type": sql_stripped.split()[0] if sql_stripped else "EMPTY"}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
conn = self._get_connection()
|
|
159
|
+
rows = conn.execute(sql, params).fetchall()
|
|
160
|
+
return [dict(row) for row in rows]
|
|
161
|
+
except sqlite3.Error as e:
|
|
162
|
+
raise StorageError(
|
|
163
|
+
f"Query execution failed: {e}",
|
|
164
|
+
db_path=str(self.db_path),
|
|
165
|
+
operation="execute_query",
|
|
166
|
+
details={"error_type": type(e).__name__}
|
|
167
|
+
) from e
|
|
168
|
+
|
|
169
|
+
def initialize(self) -> None:
|
|
170
|
+
with self._lock:
|
|
171
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
172
|
+
conn = self._get_connection()
|
|
173
|
+
self._create_schema(conn)
|
|
174
|
+
self._ensure_fts_external_content(conn)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def add_file(self, indexed_file: IndexedFile, content: str) -> None:
|
|
178
|
+
with self._lock:
|
|
179
|
+
conn = self._get_connection()
|
|
180
|
+
path = str(Path(indexed_file.path).resolve())
|
|
181
|
+
language = indexed_file.language
|
|
182
|
+
mtime = Path(path).stat().st_mtime if Path(path).exists() else None
|
|
183
|
+
line_count = content.count(chr(10)) + 1
|
|
184
|
+
|
|
185
|
+
conn.execute(
|
|
186
|
+
"""
|
|
187
|
+
INSERT INTO files(path, language, content, mtime, line_count)
|
|
188
|
+
VALUES(?, ?, ?, ?, ?)
|
|
189
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
190
|
+
language=excluded.language,
|
|
191
|
+
content=excluded.content,
|
|
192
|
+
mtime=excluded.mtime,
|
|
193
|
+
line_count=excluded.line_count
|
|
194
|
+
""",
|
|
195
|
+
(path, language, content, mtime, line_count),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
row = conn.execute("SELECT id FROM files WHERE path=?", (path,)).fetchone()
|
|
199
|
+
if not row:
|
|
200
|
+
raise StorageError(f"Failed to read file id for {path}")
|
|
201
|
+
file_id = int(row["id"])
|
|
202
|
+
|
|
203
|
+
conn.execute("DELETE FROM symbols WHERE file_id=?", (file_id,))
|
|
204
|
+
if indexed_file.symbols:
|
|
205
|
+
conn.executemany(
|
|
206
|
+
"""
|
|
207
|
+
INSERT INTO symbols(file_id, name, kind, start_line, end_line)
|
|
208
|
+
VALUES(?, ?, ?, ?, ?)
|
|
209
|
+
""",
|
|
210
|
+
[
|
|
211
|
+
(file_id, s.name, s.kind, s.range[0], s.range[1])
|
|
212
|
+
for s in indexed_file.symbols
|
|
213
|
+
],
|
|
214
|
+
)
|
|
215
|
+
conn.commit()
|
|
216
|
+
|
|
217
|
+
def add_files(self, files_data: List[tuple[IndexedFile, str]]) -> None:
|
|
218
|
+
"""Add multiple files in a single transaction for better performance.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
files_data: List of (indexed_file, content) tuples
|
|
222
|
+
"""
|
|
223
|
+
with self._lock:
|
|
224
|
+
conn = self._get_connection()
|
|
225
|
+
try:
|
|
226
|
+
conn.execute("BEGIN")
|
|
227
|
+
|
|
228
|
+
for indexed_file, content in files_data:
|
|
229
|
+
path = str(Path(indexed_file.path).resolve())
|
|
230
|
+
language = indexed_file.language
|
|
231
|
+
mtime = Path(path).stat().st_mtime if Path(path).exists() else None
|
|
232
|
+
line_count = content.count(chr(10)) + 1
|
|
233
|
+
|
|
234
|
+
conn.execute(
|
|
235
|
+
"""
|
|
236
|
+
INSERT INTO files(path, language, content, mtime, line_count)
|
|
237
|
+
VALUES(?, ?, ?, ?, ?)
|
|
238
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
239
|
+
language=excluded.language,
|
|
240
|
+
content=excluded.content,
|
|
241
|
+
mtime=excluded.mtime,
|
|
242
|
+
line_count=excluded.line_count
|
|
243
|
+
""",
|
|
244
|
+
(path, language, content, mtime, line_count),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
row = conn.execute("SELECT id FROM files WHERE path=?", (path,)).fetchone()
|
|
248
|
+
if not row:
|
|
249
|
+
raise StorageError(f"Failed to read file id for {path}")
|
|
250
|
+
file_id = int(row["id"])
|
|
251
|
+
|
|
252
|
+
conn.execute("DELETE FROM symbols WHERE file_id=?", (file_id,))
|
|
253
|
+
if indexed_file.symbols:
|
|
254
|
+
conn.executemany(
|
|
255
|
+
"""
|
|
256
|
+
INSERT INTO symbols(file_id, name, kind, start_line, end_line)
|
|
257
|
+
VALUES(?, ?, ?, ?, ?)
|
|
258
|
+
""",
|
|
259
|
+
[
|
|
260
|
+
(file_id, s.name, s.kind, s.range[0], s.range[1])
|
|
261
|
+
for s in indexed_file.symbols
|
|
262
|
+
],
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
conn.commit()
|
|
266
|
+
except Exception:
|
|
267
|
+
conn.rollback()
|
|
268
|
+
raise
|
|
269
|
+
|
|
270
|
+
def remove_file(self, path: str | Path) -> bool:
|
|
271
|
+
"""Remove a file from the index."""
|
|
272
|
+
with self._lock:
|
|
273
|
+
conn = self._get_connection()
|
|
274
|
+
resolved_path = str(Path(path).resolve())
|
|
275
|
+
|
|
276
|
+
row = conn.execute(
|
|
277
|
+
"SELECT id FROM files WHERE path=?", (resolved_path,)
|
|
278
|
+
).fetchone()
|
|
279
|
+
|
|
280
|
+
if not row:
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
file_id = int(row["id"])
|
|
284
|
+
conn.execute("DELETE FROM files WHERE id=?", (file_id,))
|
|
285
|
+
conn.commit()
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
def file_exists(self, path: str | Path) -> bool:
|
|
289
|
+
"""Check if a file exists in the index."""
|
|
290
|
+
with self._lock:
|
|
291
|
+
conn = self._get_connection()
|
|
292
|
+
resolved_path = str(Path(path).resolve())
|
|
293
|
+
row = conn.execute(
|
|
294
|
+
"SELECT 1 FROM files WHERE path=?", (resolved_path,)
|
|
295
|
+
).fetchone()
|
|
296
|
+
return row is not None
|
|
297
|
+
|
|
298
|
+
def get_file_mtime(self, path: str | Path) -> float | None:
|
|
299
|
+
"""Get the stored mtime for a file."""
|
|
300
|
+
with self._lock:
|
|
301
|
+
conn = self._get_connection()
|
|
302
|
+
resolved_path = str(Path(path).resolve())
|
|
303
|
+
row = conn.execute(
|
|
304
|
+
"SELECT mtime FROM files WHERE path=?", (resolved_path,)
|
|
305
|
+
).fetchone()
|
|
306
|
+
return float(row["mtime"]) if row and row["mtime"] else None
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def search_fts(self, query: str, *, limit: int = 20, offset: int = 0) -> List[SearchResult]:
|
|
310
|
+
with self._lock:
|
|
311
|
+
conn = self._get_connection()
|
|
312
|
+
try:
|
|
313
|
+
rows = conn.execute(
|
|
314
|
+
"""
|
|
315
|
+
SELECT rowid, path, bm25(files_fts) AS rank,
|
|
316
|
+
snippet(files_fts, 2, '[bold red]', '[/bold red]', "...", 20) AS excerpt
|
|
317
|
+
FROM files_fts
|
|
318
|
+
WHERE files_fts MATCH ?
|
|
319
|
+
ORDER BY rank
|
|
320
|
+
LIMIT ? OFFSET ?
|
|
321
|
+
""",
|
|
322
|
+
(query, limit, offset),
|
|
323
|
+
).fetchall()
|
|
324
|
+
except sqlite3.DatabaseError as exc:
|
|
325
|
+
raise StorageError(f"FTS search failed: {exc}") from exc
|
|
326
|
+
|
|
327
|
+
results: List[SearchResult] = []
|
|
328
|
+
for row in rows:
|
|
329
|
+
rank = float(row["rank"]) if row["rank"] is not None else 0.0
|
|
330
|
+
score = abs(rank) if rank < 0 else 0.0
|
|
331
|
+
results.append(
|
|
332
|
+
SearchResult(
|
|
333
|
+
path=row["path"],
|
|
334
|
+
score=score,
|
|
335
|
+
excerpt=row["excerpt"],
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
return results
|
|
339
|
+
|
|
340
|
+
def search_files_only(
|
|
341
|
+
self, query: str, *, limit: int = 20, offset: int = 0
|
|
342
|
+
) -> List[str]:
|
|
343
|
+
"""Search indexed file contents and return only file paths."""
|
|
344
|
+
with self._lock:
|
|
345
|
+
conn = self._get_connection()
|
|
346
|
+
try:
|
|
347
|
+
rows = conn.execute(
|
|
348
|
+
"""
|
|
349
|
+
SELECT path
|
|
350
|
+
FROM files_fts
|
|
351
|
+
WHERE files_fts MATCH ?
|
|
352
|
+
ORDER BY bm25(files_fts)
|
|
353
|
+
LIMIT ? OFFSET ?
|
|
354
|
+
""",
|
|
355
|
+
(query, limit, offset),
|
|
356
|
+
).fetchall()
|
|
357
|
+
except sqlite3.DatabaseError as exc:
|
|
358
|
+
raise StorageError(f"FTS search failed: {exc}") from exc
|
|
359
|
+
|
|
360
|
+
return [row["path"] for row in rows]
|
|
361
|
+
|
|
362
|
+
def search_symbols(
|
|
363
|
+
self, name: str, *, kind: Optional[str] = None, limit: int = 50
|
|
364
|
+
) -> List[Symbol]:
|
|
365
|
+
pattern = f"%{name}%"
|
|
366
|
+
with self._lock:
|
|
367
|
+
conn = self._get_connection()
|
|
368
|
+
if kind:
|
|
369
|
+
rows = conn.execute(
|
|
370
|
+
"""
|
|
371
|
+
SELECT name, kind, start_line, end_line
|
|
372
|
+
FROM symbols
|
|
373
|
+
WHERE name LIKE ? AND kind=?
|
|
374
|
+
ORDER BY name
|
|
375
|
+
LIMIT ?
|
|
376
|
+
""",
|
|
377
|
+
(pattern, kind, limit),
|
|
378
|
+
).fetchall()
|
|
379
|
+
else:
|
|
380
|
+
rows = conn.execute(
|
|
381
|
+
"""
|
|
382
|
+
SELECT name, kind, start_line, end_line
|
|
383
|
+
FROM symbols
|
|
384
|
+
WHERE name LIKE ?
|
|
385
|
+
ORDER BY name
|
|
386
|
+
LIMIT ?
|
|
387
|
+
""",
|
|
388
|
+
(pattern, limit),
|
|
389
|
+
).fetchall()
|
|
390
|
+
|
|
391
|
+
return [
|
|
392
|
+
Symbol(name=row["name"], kind=row["kind"], range=(row["start_line"], row["end_line"]))
|
|
393
|
+
for row in rows
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def stats(self) -> Dict[str, Any]:
|
|
398
|
+
with self._lock:
|
|
399
|
+
conn = self._get_connection()
|
|
400
|
+
file_count = conn.execute("SELECT COUNT(*) AS c FROM files").fetchone()["c"]
|
|
401
|
+
symbol_count = conn.execute("SELECT COUNT(*) AS c FROM symbols").fetchone()["c"]
|
|
402
|
+
lang_rows = conn.execute(
|
|
403
|
+
"SELECT language, COUNT(*) AS c FROM files GROUP BY language ORDER BY c DESC"
|
|
404
|
+
).fetchall()
|
|
405
|
+
languages = {row["language"]: row["c"] for row in lang_rows}
|
|
406
|
+
# Include relationship count if table exists
|
|
407
|
+
relationship_count = 0
|
|
408
|
+
try:
|
|
409
|
+
rel_row = conn.execute("SELECT COUNT(*) AS c FROM code_relationships").fetchone()
|
|
410
|
+
relationship_count = int(rel_row["c"]) if rel_row else 0
|
|
411
|
+
except sqlite3.DatabaseError:
|
|
412
|
+
pass
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
"files": int(file_count),
|
|
416
|
+
"symbols": int(symbol_count),
|
|
417
|
+
"relationships": relationship_count,
|
|
418
|
+
"languages": languages,
|
|
419
|
+
"db_path": str(self.db_path),
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _connect(self) -> sqlite3.Connection:
|
|
424
|
+
"""Legacy method for backward compatibility."""
|
|
425
|
+
return self._get_connection()
|
|
426
|
+
|
|
427
|
+
def _create_schema(self, conn: sqlite3.Connection) -> None:
|
|
428
|
+
try:
|
|
429
|
+
conn.execute(
|
|
430
|
+
"""
|
|
431
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
432
|
+
id INTEGER PRIMARY KEY,
|
|
433
|
+
path TEXT UNIQUE NOT NULL,
|
|
434
|
+
language TEXT NOT NULL,
|
|
435
|
+
content TEXT NOT NULL,
|
|
436
|
+
mtime REAL,
|
|
437
|
+
line_count INTEGER
|
|
438
|
+
)
|
|
439
|
+
"""
|
|
440
|
+
)
|
|
441
|
+
conn.execute(
|
|
442
|
+
"""
|
|
443
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
444
|
+
id INTEGER PRIMARY KEY,
|
|
445
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
446
|
+
name TEXT NOT NULL,
|
|
447
|
+
kind TEXT NOT NULL,
|
|
448
|
+
start_line INTEGER NOT NULL,
|
|
449
|
+
end_line INTEGER NOT NULL
|
|
450
|
+
)
|
|
451
|
+
"""
|
|
452
|
+
)
|
|
453
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name)")
|
|
454
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind)")
|
|
455
|
+
conn.execute(
|
|
456
|
+
"""
|
|
457
|
+
CREATE TABLE IF NOT EXISTS code_relationships (
|
|
458
|
+
id INTEGER PRIMARY KEY,
|
|
459
|
+
source_symbol_id INTEGER NOT NULL REFERENCES symbols(id) ON DELETE CASCADE,
|
|
460
|
+
target_qualified_name TEXT NOT NULL,
|
|
461
|
+
relationship_type TEXT NOT NULL,
|
|
462
|
+
source_line INTEGER NOT NULL,
|
|
463
|
+
target_file TEXT
|
|
464
|
+
)
|
|
465
|
+
"""
|
|
466
|
+
)
|
|
467
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_rel_target ON code_relationships(target_qualified_name)")
|
|
468
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_rel_source ON code_relationships(source_symbol_id)")
|
|
469
|
+
conn.commit()
|
|
470
|
+
except sqlite3.DatabaseError as exc:
|
|
471
|
+
raise StorageError(f"Failed to initialize database schema: {exc}") from exc
|
|
472
|
+
|
|
473
|
+
def _ensure_fts_external_content(self, conn: sqlite3.Connection) -> None:
|
|
474
|
+
"""Ensure files_fts is an FTS5 external-content table (no content duplication)."""
|
|
475
|
+
try:
|
|
476
|
+
sql_row = conn.execute(
|
|
477
|
+
"SELECT sql FROM sqlite_master WHERE type='table' AND name='files_fts'"
|
|
478
|
+
).fetchone()
|
|
479
|
+
sql = str(sql_row["sql"]) if sql_row and sql_row["sql"] else None
|
|
480
|
+
|
|
481
|
+
if sql is None:
|
|
482
|
+
self._create_external_fts(conn)
|
|
483
|
+
conn.commit()
|
|
484
|
+
return
|
|
485
|
+
|
|
486
|
+
if (
|
|
487
|
+
"content='files'" in sql
|
|
488
|
+
or 'content="files"' in sql
|
|
489
|
+
or "content=files" in sql
|
|
490
|
+
):
|
|
491
|
+
self._create_fts_triggers(conn)
|
|
492
|
+
conn.commit()
|
|
493
|
+
return
|
|
494
|
+
|
|
495
|
+
self._migrate_fts_to_external(conn)
|
|
496
|
+
except sqlite3.DatabaseError as exc:
|
|
497
|
+
raise StorageError(f"Failed to ensure FTS schema: {exc}") from exc
|
|
498
|
+
|
|
499
|
+
def _create_external_fts(self, conn: sqlite3.Connection) -> None:
|
|
500
|
+
conn.execute(
|
|
501
|
+
"""
|
|
502
|
+
CREATE VIRTUAL TABLE files_fts USING fts5(
|
|
503
|
+
path UNINDEXED,
|
|
504
|
+
language UNINDEXED,
|
|
505
|
+
content,
|
|
506
|
+
content='files',
|
|
507
|
+
content_rowid='id',
|
|
508
|
+
tokenize="unicode61 tokenchars '_'"
|
|
509
|
+
)
|
|
510
|
+
"""
|
|
511
|
+
)
|
|
512
|
+
self._create_fts_triggers(conn)
|
|
513
|
+
|
|
514
|
+
def _create_fts_triggers(self, conn: sqlite3.Connection) -> None:
|
|
515
|
+
conn.execute(
|
|
516
|
+
"""
|
|
517
|
+
CREATE TRIGGER IF NOT EXISTS files_ai AFTER INSERT ON files BEGIN
|
|
518
|
+
INSERT INTO files_fts(rowid, path, language, content)
|
|
519
|
+
VALUES(new.id, new.path, new.language, new.content);
|
|
520
|
+
END
|
|
521
|
+
"""
|
|
522
|
+
)
|
|
523
|
+
conn.execute(
|
|
524
|
+
"""
|
|
525
|
+
CREATE TRIGGER IF NOT EXISTS files_ad AFTER DELETE ON files BEGIN
|
|
526
|
+
INSERT INTO files_fts(files_fts, rowid, path, language, content)
|
|
527
|
+
VALUES('delete', old.id, old.path, old.language, old.content);
|
|
528
|
+
END
|
|
529
|
+
"""
|
|
530
|
+
)
|
|
531
|
+
conn.execute(
|
|
532
|
+
"""
|
|
533
|
+
CREATE TRIGGER IF NOT EXISTS files_au AFTER UPDATE ON files BEGIN
|
|
534
|
+
INSERT INTO files_fts(files_fts, rowid, path, language, content)
|
|
535
|
+
VALUES('delete', old.id, old.path, old.language, old.content);
|
|
536
|
+
INSERT INTO files_fts(rowid, path, language, content)
|
|
537
|
+
VALUES(new.id, new.path, new.language, new.content);
|
|
538
|
+
END
|
|
539
|
+
"""
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
def _migrate_fts_to_external(self, conn: sqlite3.Connection) -> None:
|
|
543
|
+
"""Migrate legacy files_fts (with duplicated content) to external content."""
|
|
544
|
+
try:
|
|
545
|
+
conn.execute("BEGIN")
|
|
546
|
+
conn.execute("DROP TRIGGER IF EXISTS files_ai")
|
|
547
|
+
conn.execute("DROP TRIGGER IF EXISTS files_ad")
|
|
548
|
+
conn.execute("DROP TRIGGER IF EXISTS files_au")
|
|
549
|
+
|
|
550
|
+
conn.execute("ALTER TABLE files_fts RENAME TO files_fts_legacy")
|
|
551
|
+
self._create_external_fts(conn)
|
|
552
|
+
conn.execute("INSERT INTO files_fts(files_fts) VALUES('rebuild')")
|
|
553
|
+
conn.execute("DROP TABLE files_fts_legacy")
|
|
554
|
+
conn.commit()
|
|
555
|
+
except sqlite3.DatabaseError:
|
|
556
|
+
try:
|
|
557
|
+
conn.rollback()
|
|
558
|
+
except Exception:
|
|
559
|
+
pass
|
|
560
|
+
|
|
561
|
+
try:
|
|
562
|
+
conn.execute("DROP TABLE IF EXISTS files_fts")
|
|
563
|
+
except Exception:
|
|
564
|
+
pass
|
|
565
|
+
|
|
566
|
+
try:
|
|
567
|
+
conn.execute("ALTER TABLE files_fts_legacy RENAME TO files_fts")
|
|
568
|
+
conn.commit()
|
|
569
|
+
except Exception:
|
|
570
|
+
pass
|
|
571
|
+
raise
|
|
572
|
+
|
|
573
|
+
try:
|
|
574
|
+
conn.execute("VACUUM")
|
|
575
|
+
except sqlite3.DatabaseError:
|
|
576
|
+
pass
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""SQLite utility functions for CodexLens storage layer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import sqlite3
|
|
7
|
+
|
|
8
|
+
log = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def check_trigram_support(conn: sqlite3.Connection) -> bool:
|
|
12
|
+
"""Check if SQLite supports trigram tokenizer for FTS5.
|
|
13
|
+
|
|
14
|
+
Trigram tokenizer requires SQLite >= 3.34.0.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
conn: Database connection to test
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
True if trigram tokenizer is available, False otherwise
|
|
21
|
+
"""
|
|
22
|
+
try:
|
|
23
|
+
# Test by creating a temporary virtual table with trigram tokenizer
|
|
24
|
+
conn.execute(
|
|
25
|
+
"""
|
|
26
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS test_trigram_check
|
|
27
|
+
USING fts5(test_content, tokenize='trigram')
|
|
28
|
+
"""
|
|
29
|
+
)
|
|
30
|
+
# Clean up test table
|
|
31
|
+
conn.execute("DROP TABLE IF EXISTS test_trigram_check")
|
|
32
|
+
conn.commit()
|
|
33
|
+
return True
|
|
34
|
+
except sqlite3.OperationalError as e:
|
|
35
|
+
# Trigram tokenizer not available
|
|
36
|
+
if "unrecognized tokenizer" in str(e).lower():
|
|
37
|
+
log.debug("Trigram tokenizer not available in this SQLite version")
|
|
38
|
+
return False
|
|
39
|
+
# Other operational errors should be re-raised
|
|
40
|
+
raise
|
|
41
|
+
except Exception:
|
|
42
|
+
# Any other exception means trigram is not supported
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_sqlite_version(conn: sqlite3.Connection) -> tuple[int, int, int]:
|
|
47
|
+
"""Get SQLite version as (major, minor, patch) tuple.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
conn: Database connection
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Version tuple, e.g., (3, 34, 1)
|
|
54
|
+
"""
|
|
55
|
+
row = conn.execute("SELECT sqlite_version()").fetchone()
|
|
56
|
+
version_str = row[0] if row else "0.0.0"
|
|
57
|
+
parts = version_str.split('.')
|
|
58
|
+
try:
|
|
59
|
+
major = int(parts[0]) if len(parts) > 0 else 0
|
|
60
|
+
minor = int(parts[1]) if len(parts) > 1 else 0
|
|
61
|
+
patch = int(parts[2]) if len(parts) > 2 else 0
|
|
62
|
+
return (major, minor, patch)
|
|
63
|
+
except (ValueError, IndexError):
|
|
64
|
+
return (0, 0, 0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-workflow",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.2",
|
|
4
4
|
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "ccw/src/index.js",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"ccw/bin/",
|
|
43
|
+
"ccw/dist/",
|
|
43
44
|
"ccw/src/",
|
|
44
45
|
"ccw/package.json",
|
|
45
46
|
".claude/agents/",
|
|
@@ -53,6 +54,8 @@
|
|
|
53
54
|
".codex/",
|
|
54
55
|
".gemini/",
|
|
55
56
|
".qwen/",
|
|
57
|
+
"codex-lens/src/codexlens/",
|
|
58
|
+
"codex-lens/pyproject.toml",
|
|
56
59
|
"CLAUDE.md",
|
|
57
60
|
"README.md"
|
|
58
61
|
],
|