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,647 @@
|
|
|
1
|
+
"""Chain search engine for recursive multi-directory searching.
|
|
2
|
+
|
|
3
|
+
Provides parallel search across directory hierarchies using indexed _index.db files.
|
|
4
|
+
Supports depth-limited traversal, result aggregation, and symbol search.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import List, Optional, Dict, Any
|
|
13
|
+
import logging
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from codexlens.entities import SearchResult, Symbol
|
|
17
|
+
from codexlens.storage.registry import RegistryStore, DirMapping
|
|
18
|
+
from codexlens.storage.dir_index import DirIndexStore, SubdirLink
|
|
19
|
+
from codexlens.storage.path_mapper import PathMapper
|
|
20
|
+
from codexlens.storage.sqlite_store import SQLiteStore
|
|
21
|
+
from codexlens.search.hybrid_search import HybridSearchEngine
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class SearchOptions:
|
|
26
|
+
"""Configuration options for chain search.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
depth: Maximum search depth (-1 = unlimited, 0 = current dir only)
|
|
30
|
+
max_workers: Number of parallel worker threads
|
|
31
|
+
limit_per_dir: Maximum results per directory
|
|
32
|
+
total_limit: Total result limit across all directories
|
|
33
|
+
include_symbols: Whether to include symbol search results
|
|
34
|
+
files_only: Return only file paths without excerpts
|
|
35
|
+
include_semantic: Whether to include semantic keyword search results
|
|
36
|
+
hybrid_mode: Enable hybrid search with RRF fusion (default False)
|
|
37
|
+
enable_fuzzy: Enable fuzzy FTS in hybrid mode (default True)
|
|
38
|
+
enable_vector: Enable vector semantic search (default False)
|
|
39
|
+
pure_vector: If True, only use vector search without FTS fallback (default False)
|
|
40
|
+
hybrid_weights: Custom RRF weights for hybrid search (optional)
|
|
41
|
+
group_results: Enable grouping of similar results (default False)
|
|
42
|
+
grouping_threshold: Score threshold for grouping similar results (default 0.01)
|
|
43
|
+
"""
|
|
44
|
+
depth: int = -1
|
|
45
|
+
max_workers: int = 8
|
|
46
|
+
limit_per_dir: int = 10
|
|
47
|
+
total_limit: int = 100
|
|
48
|
+
include_symbols: bool = False
|
|
49
|
+
files_only: bool = False
|
|
50
|
+
include_semantic: bool = False
|
|
51
|
+
hybrid_mode: bool = False
|
|
52
|
+
enable_fuzzy: bool = True
|
|
53
|
+
enable_vector: bool = False
|
|
54
|
+
pure_vector: bool = False
|
|
55
|
+
hybrid_weights: Optional[Dict[str, float]] = None
|
|
56
|
+
group_results: bool = False
|
|
57
|
+
grouping_threshold: float = 0.01
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class SearchStats:
|
|
62
|
+
"""Statistics collected during search execution.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
dirs_searched: Number of directories searched
|
|
66
|
+
files_matched: Number of files with matches
|
|
67
|
+
time_ms: Total search time in milliseconds
|
|
68
|
+
errors: List of error messages encountered
|
|
69
|
+
"""
|
|
70
|
+
dirs_searched: int = 0
|
|
71
|
+
files_matched: int = 0
|
|
72
|
+
time_ms: float = 0
|
|
73
|
+
errors: List[str] = field(default_factory=list)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class ChainSearchResult:
|
|
78
|
+
"""Comprehensive search result with metadata.
|
|
79
|
+
|
|
80
|
+
Attributes:
|
|
81
|
+
query: Original search query
|
|
82
|
+
results: List of SearchResult objects
|
|
83
|
+
symbols: List of Symbol objects (if include_symbols=True)
|
|
84
|
+
stats: SearchStats with execution metrics
|
|
85
|
+
"""
|
|
86
|
+
query: str
|
|
87
|
+
results: List[SearchResult]
|
|
88
|
+
symbols: List[Symbol]
|
|
89
|
+
stats: SearchStats
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ChainSearchEngine:
|
|
93
|
+
"""Parallel chain search engine for hierarchical directory indexes.
|
|
94
|
+
|
|
95
|
+
Searches across multiple directory indexes in parallel, following subdirectory
|
|
96
|
+
links to recursively traverse the file tree. Supports depth limits, result
|
|
97
|
+
aggregation, and both content and symbol searches.
|
|
98
|
+
|
|
99
|
+
Thread-safe with configurable parallelism.
|
|
100
|
+
|
|
101
|
+
Attributes:
|
|
102
|
+
registry: Global project registry
|
|
103
|
+
mapper: Path mapping utility
|
|
104
|
+
logger: Python logger instance
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self,
|
|
108
|
+
registry: RegistryStore,
|
|
109
|
+
mapper: PathMapper,
|
|
110
|
+
max_workers: int = 8):
|
|
111
|
+
"""Initialize chain search engine.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
registry: Global project registry for path lookups
|
|
115
|
+
mapper: Path mapper for source/index conversions
|
|
116
|
+
max_workers: Maximum parallel workers (default 8)
|
|
117
|
+
"""
|
|
118
|
+
self.registry = registry
|
|
119
|
+
self.mapper = mapper
|
|
120
|
+
self.logger = logging.getLogger(__name__)
|
|
121
|
+
self._max_workers = max_workers
|
|
122
|
+
self._executor: Optional[ThreadPoolExecutor] = None
|
|
123
|
+
|
|
124
|
+
def _get_executor(self, max_workers: Optional[int] = None) -> ThreadPoolExecutor:
|
|
125
|
+
"""Get or create the shared thread pool executor.
|
|
126
|
+
|
|
127
|
+
Lazy initialization to avoid creating executor if never used.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
max_workers: Override default max_workers if specified
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
ThreadPoolExecutor instance
|
|
134
|
+
"""
|
|
135
|
+
workers = max_workers or self._max_workers
|
|
136
|
+
if self._executor is None:
|
|
137
|
+
self._executor = ThreadPoolExecutor(max_workers=workers)
|
|
138
|
+
return self._executor
|
|
139
|
+
|
|
140
|
+
def close(self) -> None:
|
|
141
|
+
"""Shutdown the thread pool executor."""
|
|
142
|
+
if self._executor is not None:
|
|
143
|
+
self._executor.shutdown(wait=True)
|
|
144
|
+
self._executor = None
|
|
145
|
+
|
|
146
|
+
def __enter__(self) -> "ChainSearchEngine":
|
|
147
|
+
"""Context manager entry."""
|
|
148
|
+
return self
|
|
149
|
+
|
|
150
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
|
|
151
|
+
"""Context manager exit."""
|
|
152
|
+
self.close()
|
|
153
|
+
|
|
154
|
+
def search(self, query: str,
|
|
155
|
+
source_path: Path,
|
|
156
|
+
options: Optional[SearchOptions] = None) -> ChainSearchResult:
|
|
157
|
+
"""Execute chain search from source_path with recursive traversal.
|
|
158
|
+
|
|
159
|
+
Process:
|
|
160
|
+
1. Locate starting index for source_path
|
|
161
|
+
2. Collect all child indexes based on depth limit
|
|
162
|
+
3. Search indexes in parallel using ThreadPoolExecutor
|
|
163
|
+
4. Aggregate, deduplicate, and rank results
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
query: FTS5 search query string
|
|
167
|
+
source_path: Starting directory path
|
|
168
|
+
options: Search configuration (uses defaults if None)
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
ChainSearchResult with results, symbols, and statistics
|
|
172
|
+
|
|
173
|
+
Examples:
|
|
174
|
+
>>> engine = ChainSearchEngine(registry, mapper)
|
|
175
|
+
>>> result = engine.search("authentication", Path("D:/project/src"))
|
|
176
|
+
>>> for r in result.results[:5]:
|
|
177
|
+
... print(f"{r.path}: {r.score:.2f}")
|
|
178
|
+
"""
|
|
179
|
+
options = options or SearchOptions()
|
|
180
|
+
start_time = time.time()
|
|
181
|
+
stats = SearchStats()
|
|
182
|
+
|
|
183
|
+
# Step 1: Find starting index
|
|
184
|
+
start_index = self._find_start_index(source_path)
|
|
185
|
+
if not start_index:
|
|
186
|
+
self.logger.warning(f"No index found for {source_path}")
|
|
187
|
+
stats.time_ms = (time.time() - start_time) * 1000
|
|
188
|
+
return ChainSearchResult(
|
|
189
|
+
query=query,
|
|
190
|
+
results=[],
|
|
191
|
+
symbols=[],
|
|
192
|
+
stats=stats
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Step 2: Collect all index paths to search
|
|
196
|
+
index_paths = self._collect_index_paths(start_index, options.depth)
|
|
197
|
+
stats.dirs_searched = len(index_paths)
|
|
198
|
+
|
|
199
|
+
if not index_paths:
|
|
200
|
+
self.logger.warning(f"No indexes collected from {start_index}")
|
|
201
|
+
stats.time_ms = (time.time() - start_time) * 1000
|
|
202
|
+
return ChainSearchResult(
|
|
203
|
+
query=query,
|
|
204
|
+
results=[],
|
|
205
|
+
symbols=[],
|
|
206
|
+
stats=stats
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Step 3: Parallel search
|
|
210
|
+
results, search_stats = self._search_parallel(
|
|
211
|
+
index_paths, query, options
|
|
212
|
+
)
|
|
213
|
+
stats.errors = search_stats.errors
|
|
214
|
+
|
|
215
|
+
# Step 4: Merge and rank
|
|
216
|
+
final_results = self._merge_and_rank(results, options.total_limit)
|
|
217
|
+
|
|
218
|
+
# Step 5: Optional grouping of similar results
|
|
219
|
+
if options.group_results:
|
|
220
|
+
from codexlens.search.ranking import group_similar_results
|
|
221
|
+
final_results = group_similar_results(
|
|
222
|
+
final_results, score_threshold_abs=options.grouping_threshold
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
stats.files_matched = len(final_results)
|
|
226
|
+
|
|
227
|
+
# Optional: Symbol search
|
|
228
|
+
symbols = []
|
|
229
|
+
if options.include_symbols:
|
|
230
|
+
symbols = self._search_symbols_parallel(
|
|
231
|
+
index_paths, query, None, options.total_limit
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
stats.time_ms = (time.time() - start_time) * 1000
|
|
235
|
+
|
|
236
|
+
return ChainSearchResult(
|
|
237
|
+
query=query,
|
|
238
|
+
results=final_results,
|
|
239
|
+
symbols=symbols,
|
|
240
|
+
stats=stats
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def search_files_only(self, query: str,
|
|
244
|
+
source_path: Path,
|
|
245
|
+
options: Optional[SearchOptions] = None) -> List[str]:
|
|
246
|
+
"""Search and return only matching file paths.
|
|
247
|
+
|
|
248
|
+
Faster than full search when excerpts are not needed.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
query: FTS5 search query string
|
|
252
|
+
source_path: Starting directory path
|
|
253
|
+
options: Search configuration (uses defaults if None)
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
List of file paths as strings
|
|
257
|
+
|
|
258
|
+
Examples:
|
|
259
|
+
>>> engine = ChainSearchEngine(registry, mapper)
|
|
260
|
+
>>> paths = engine.search_files_only("TODO", Path("D:/project"))
|
|
261
|
+
>>> print(f"Found {len(paths)} files with TODOs")
|
|
262
|
+
"""
|
|
263
|
+
options = options or SearchOptions()
|
|
264
|
+
options.files_only = True
|
|
265
|
+
|
|
266
|
+
result = self.search(query, source_path, options)
|
|
267
|
+
return [r.path for r in result.results]
|
|
268
|
+
|
|
269
|
+
def search_symbols(self, name: str,
|
|
270
|
+
source_path: Path,
|
|
271
|
+
kind: Optional[str] = None,
|
|
272
|
+
options: Optional[SearchOptions] = None) -> List[Symbol]:
|
|
273
|
+
"""Chain symbol search across directory hierarchy.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
name: Symbol name pattern (partial match supported)
|
|
277
|
+
source_path: Starting directory path
|
|
278
|
+
kind: Optional symbol kind filter (e.g., 'function', 'class')
|
|
279
|
+
options: Search configuration (uses defaults if None)
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
List of Symbol objects sorted by name
|
|
283
|
+
|
|
284
|
+
Examples:
|
|
285
|
+
>>> engine = ChainSearchEngine(registry, mapper)
|
|
286
|
+
>>> funcs = engine.search_symbols("init", Path("D:/project"), kind="function")
|
|
287
|
+
>>> for sym in funcs[:10]:
|
|
288
|
+
... print(f"{sym.name} ({sym.kind}): lines {sym.range}")
|
|
289
|
+
"""
|
|
290
|
+
options = options or SearchOptions()
|
|
291
|
+
|
|
292
|
+
start_index = self._find_start_index(source_path)
|
|
293
|
+
if not start_index:
|
|
294
|
+
self.logger.warning(f"No index found for {source_path}")
|
|
295
|
+
return []
|
|
296
|
+
|
|
297
|
+
index_paths = self._collect_index_paths(start_index, options.depth)
|
|
298
|
+
if not index_paths:
|
|
299
|
+
return []
|
|
300
|
+
|
|
301
|
+
return self._search_symbols_parallel(
|
|
302
|
+
index_paths, name, kind, options.total_limit
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# === Internal Methods ===
|
|
306
|
+
|
|
307
|
+
def _find_start_index(self, source_path: Path) -> Optional[Path]:
|
|
308
|
+
"""Find index database path for source directory.
|
|
309
|
+
|
|
310
|
+
Attempts exact match first, then searches for nearest ancestor index.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
source_path: Source directory path
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Path to _index.db file, or None if not found
|
|
317
|
+
"""
|
|
318
|
+
source_path = source_path.resolve()
|
|
319
|
+
|
|
320
|
+
# Try exact match first
|
|
321
|
+
exact_index = self.mapper.source_to_index_db(source_path)
|
|
322
|
+
if exact_index.exists():
|
|
323
|
+
self.logger.debug(f"Found exact index: {exact_index}")
|
|
324
|
+
return exact_index
|
|
325
|
+
|
|
326
|
+
# Try nearest ancestor via registry
|
|
327
|
+
nearest = self.registry.find_nearest_index(source_path)
|
|
328
|
+
if nearest:
|
|
329
|
+
self.logger.debug(f"Found nearest index: {nearest.index_path}")
|
|
330
|
+
return nearest.index_path
|
|
331
|
+
|
|
332
|
+
self.logger.warning(f"No index found for {source_path}")
|
|
333
|
+
return None
|
|
334
|
+
|
|
335
|
+
def _collect_index_paths(self, start_index: Path,
|
|
336
|
+
depth: int) -> List[Path]:
|
|
337
|
+
"""Recursively collect all subdirectory index paths.
|
|
338
|
+
|
|
339
|
+
Traverses directory tree via subdirs table in each _index.db,
|
|
340
|
+
respecting depth limit.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
start_index: Starting _index.db path
|
|
344
|
+
depth: Maximum depth (-1 = unlimited, 0 = current only)
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
List of _index.db paths to search
|
|
348
|
+
"""
|
|
349
|
+
collected = []
|
|
350
|
+
visited = set()
|
|
351
|
+
|
|
352
|
+
def _collect_recursive(index_path: Path, current_depth: int):
|
|
353
|
+
# Normalize path to avoid duplicates
|
|
354
|
+
normalized = index_path.resolve()
|
|
355
|
+
if normalized in visited:
|
|
356
|
+
return
|
|
357
|
+
visited.add(normalized)
|
|
358
|
+
|
|
359
|
+
# Add current index
|
|
360
|
+
if normalized.exists():
|
|
361
|
+
collected.append(normalized)
|
|
362
|
+
else:
|
|
363
|
+
self.logger.debug(f"Index does not exist: {normalized}")
|
|
364
|
+
return
|
|
365
|
+
|
|
366
|
+
# Check depth limit
|
|
367
|
+
if depth >= 0 and current_depth >= depth:
|
|
368
|
+
return
|
|
369
|
+
|
|
370
|
+
# Read subdirs and recurse
|
|
371
|
+
try:
|
|
372
|
+
with DirIndexStore(normalized) as store:
|
|
373
|
+
subdirs = store.get_subdirs()
|
|
374
|
+
for subdir in subdirs:
|
|
375
|
+
_collect_recursive(subdir.index_path, current_depth + 1)
|
|
376
|
+
except Exception as exc:
|
|
377
|
+
self.logger.warning(f"Failed to read subdirs from {normalized}: {exc}")
|
|
378
|
+
|
|
379
|
+
_collect_recursive(start_index, 0)
|
|
380
|
+
self.logger.info(f"Collected {len(collected)} indexes (depth={depth})")
|
|
381
|
+
return collected
|
|
382
|
+
|
|
383
|
+
def _search_parallel(self, index_paths: List[Path],
|
|
384
|
+
query: str,
|
|
385
|
+
options: SearchOptions) -> tuple[List[SearchResult], SearchStats]:
|
|
386
|
+
"""Search multiple indexes in parallel using shared ThreadPoolExecutor.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
index_paths: List of _index.db paths to search
|
|
390
|
+
query: FTS5 query string
|
|
391
|
+
options: Search configuration
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
Tuple of (all results, search statistics)
|
|
395
|
+
"""
|
|
396
|
+
all_results = []
|
|
397
|
+
stats = SearchStats()
|
|
398
|
+
|
|
399
|
+
executor = self._get_executor(options.max_workers)
|
|
400
|
+
# Submit all search tasks
|
|
401
|
+
future_to_path = {
|
|
402
|
+
executor.submit(
|
|
403
|
+
self._search_single_index,
|
|
404
|
+
idx_path,
|
|
405
|
+
query,
|
|
406
|
+
options.limit_per_dir,
|
|
407
|
+
options.files_only,
|
|
408
|
+
options.include_semantic,
|
|
409
|
+
options.hybrid_mode,
|
|
410
|
+
options.enable_fuzzy,
|
|
411
|
+
options.enable_vector,
|
|
412
|
+
options.pure_vector,
|
|
413
|
+
options.hybrid_weights
|
|
414
|
+
): idx_path
|
|
415
|
+
for idx_path in index_paths
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
# Collect results as they complete
|
|
419
|
+
for future in as_completed(future_to_path):
|
|
420
|
+
idx_path = future_to_path[future]
|
|
421
|
+
try:
|
|
422
|
+
results = future.result()
|
|
423
|
+
all_results.extend(results)
|
|
424
|
+
self.logger.debug(f"Got {len(results)} results from {idx_path.parent.name}")
|
|
425
|
+
except Exception as exc:
|
|
426
|
+
error_msg = f"Search failed for {idx_path}: {exc}"
|
|
427
|
+
self.logger.error(error_msg)
|
|
428
|
+
stats.errors.append(error_msg)
|
|
429
|
+
|
|
430
|
+
return all_results, stats
|
|
431
|
+
|
|
432
|
+
def _search_single_index(self, index_path: Path,
|
|
433
|
+
query: str,
|
|
434
|
+
limit: int,
|
|
435
|
+
files_only: bool = False,
|
|
436
|
+
include_semantic: bool = False,
|
|
437
|
+
hybrid_mode: bool = False,
|
|
438
|
+
enable_fuzzy: bool = True,
|
|
439
|
+
enable_vector: bool = False,
|
|
440
|
+
pure_vector: bool = False,
|
|
441
|
+
hybrid_weights: Optional[Dict[str, float]] = None) -> List[SearchResult]:
|
|
442
|
+
"""Search a single index database.
|
|
443
|
+
|
|
444
|
+
Handles exceptions gracefully, returning empty list on failure.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
index_path: Path to _index.db file
|
|
448
|
+
query: FTS5 query string (for FTS) or natural language query (for vector)
|
|
449
|
+
limit: Maximum results from this index
|
|
450
|
+
files_only: If True, skip snippet generation for faster search
|
|
451
|
+
include_semantic: If True, also search semantic keywords and merge results
|
|
452
|
+
hybrid_mode: If True, use hybrid search with RRF fusion
|
|
453
|
+
enable_fuzzy: Enable fuzzy FTS in hybrid mode
|
|
454
|
+
enable_vector: Enable vector semantic search
|
|
455
|
+
pure_vector: If True, only use vector search without FTS fallback
|
|
456
|
+
hybrid_weights: Custom RRF weights for hybrid search
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
List of SearchResult objects (empty on error)
|
|
460
|
+
"""
|
|
461
|
+
try:
|
|
462
|
+
# Use hybrid search if enabled
|
|
463
|
+
if hybrid_mode:
|
|
464
|
+
hybrid_engine = HybridSearchEngine(weights=hybrid_weights)
|
|
465
|
+
fts_results = hybrid_engine.search(
|
|
466
|
+
index_path,
|
|
467
|
+
query,
|
|
468
|
+
limit=limit,
|
|
469
|
+
enable_fuzzy=enable_fuzzy,
|
|
470
|
+
enable_vector=enable_vector,
|
|
471
|
+
pure_vector=pure_vector,
|
|
472
|
+
)
|
|
473
|
+
else:
|
|
474
|
+
# Single-FTS search (exact or fuzzy mode)
|
|
475
|
+
with DirIndexStore(index_path) as store:
|
|
476
|
+
# Get FTS results
|
|
477
|
+
if files_only:
|
|
478
|
+
# Fast path: return paths only without snippets
|
|
479
|
+
paths = store.search_files_only(query, limit=limit)
|
|
480
|
+
fts_results = [SearchResult(path=p, score=0.0, excerpt="") for p in paths]
|
|
481
|
+
else:
|
|
482
|
+
# Use fuzzy FTS if enable_fuzzy=True (mode="fuzzy"), otherwise exact FTS
|
|
483
|
+
if enable_fuzzy:
|
|
484
|
+
fts_results = store.search_fts_fuzzy(query, limit=limit)
|
|
485
|
+
else:
|
|
486
|
+
fts_results = store.search_fts(query, limit=limit)
|
|
487
|
+
|
|
488
|
+
# Optionally add semantic keyword results
|
|
489
|
+
if include_semantic:
|
|
490
|
+
try:
|
|
491
|
+
semantic_matches = store.search_semantic_keywords(query)
|
|
492
|
+
# Convert semantic matches to SearchResult with 0.8x weight
|
|
493
|
+
for file_entry, keywords in semantic_matches:
|
|
494
|
+
# Create excerpt from keywords
|
|
495
|
+
excerpt = f"Keywords: {', '.join(keywords[:5])}"
|
|
496
|
+
# Use a base score of 10.0 for semantic matches, weighted by 0.8
|
|
497
|
+
semantic_result = SearchResult(
|
|
498
|
+
path=str(file_entry.full_path),
|
|
499
|
+
score=10.0 * 0.8,
|
|
500
|
+
excerpt=excerpt
|
|
501
|
+
)
|
|
502
|
+
fts_results.append(semantic_result)
|
|
503
|
+
except Exception as sem_exc:
|
|
504
|
+
self.logger.debug(f"Semantic search error in {index_path}: {sem_exc}")
|
|
505
|
+
|
|
506
|
+
return fts_results
|
|
507
|
+
except Exception as exc:
|
|
508
|
+
self.logger.debug(f"Search error in {index_path}: {exc}")
|
|
509
|
+
return []
|
|
510
|
+
|
|
511
|
+
def _merge_and_rank(self, results: List[SearchResult],
|
|
512
|
+
limit: int) -> List[SearchResult]:
|
|
513
|
+
"""Aggregate, deduplicate, and rank results.
|
|
514
|
+
|
|
515
|
+
Process:
|
|
516
|
+
1. Deduplicate by path (keep highest score)
|
|
517
|
+
2. Sort by score descending
|
|
518
|
+
3. Limit to requested count
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
results: Raw results from all indexes
|
|
522
|
+
limit: Maximum results to return
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
Deduplicated and ranked results
|
|
526
|
+
"""
|
|
527
|
+
# Deduplicate by path, keeping best score
|
|
528
|
+
path_to_result: Dict[str, SearchResult] = {}
|
|
529
|
+
for result in results:
|
|
530
|
+
path = result.path
|
|
531
|
+
if path not in path_to_result or result.score > path_to_result[path].score:
|
|
532
|
+
path_to_result[path] = result
|
|
533
|
+
|
|
534
|
+
# Sort by score descending
|
|
535
|
+
unique_results = list(path_to_result.values())
|
|
536
|
+
unique_results.sort(key=lambda r: r.score, reverse=True)
|
|
537
|
+
|
|
538
|
+
# Apply limit
|
|
539
|
+
return unique_results[:limit]
|
|
540
|
+
|
|
541
|
+
def _search_symbols_parallel(self, index_paths: List[Path],
|
|
542
|
+
name: str,
|
|
543
|
+
kind: Optional[str],
|
|
544
|
+
limit: int) -> List[Symbol]:
|
|
545
|
+
"""Search symbols across multiple indexes in parallel.
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
index_paths: List of _index.db paths to search
|
|
549
|
+
name: Symbol name pattern
|
|
550
|
+
kind: Optional symbol kind filter
|
|
551
|
+
limit: Total symbol limit
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
Deduplicated and sorted symbols
|
|
555
|
+
"""
|
|
556
|
+
all_symbols = []
|
|
557
|
+
|
|
558
|
+
executor = self._get_executor()
|
|
559
|
+
# Submit all symbol search tasks
|
|
560
|
+
future_to_path = {
|
|
561
|
+
executor.submit(
|
|
562
|
+
self._search_symbols_single,
|
|
563
|
+
idx_path,
|
|
564
|
+
name,
|
|
565
|
+
kind
|
|
566
|
+
): idx_path
|
|
567
|
+
for idx_path in index_paths
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
# Collect results
|
|
571
|
+
for future in as_completed(future_to_path):
|
|
572
|
+
try:
|
|
573
|
+
symbols = future.result()
|
|
574
|
+
all_symbols.extend(symbols)
|
|
575
|
+
except Exception as exc:
|
|
576
|
+
self.logger.error(f"Symbol search failed: {exc}")
|
|
577
|
+
|
|
578
|
+
# Deduplicate by (name, kind, range)
|
|
579
|
+
seen = set()
|
|
580
|
+
unique_symbols = []
|
|
581
|
+
for sym in all_symbols:
|
|
582
|
+
key = (sym.name, sym.kind, sym.range)
|
|
583
|
+
if key not in seen:
|
|
584
|
+
seen.add(key)
|
|
585
|
+
unique_symbols.append(sym)
|
|
586
|
+
|
|
587
|
+
# Sort by name
|
|
588
|
+
unique_symbols.sort(key=lambda s: s.name)
|
|
589
|
+
|
|
590
|
+
return unique_symbols[:limit]
|
|
591
|
+
|
|
592
|
+
def _search_symbols_single(self, index_path: Path,
|
|
593
|
+
name: str,
|
|
594
|
+
kind: Optional[str]) -> List[Symbol]:
|
|
595
|
+
"""Search symbols in a single index.
|
|
596
|
+
|
|
597
|
+
Args:
|
|
598
|
+
index_path: Path to _index.db file
|
|
599
|
+
name: Symbol name pattern
|
|
600
|
+
kind: Optional symbol kind filter
|
|
601
|
+
|
|
602
|
+
Returns:
|
|
603
|
+
List of Symbol objects (empty on error)
|
|
604
|
+
"""
|
|
605
|
+
try:
|
|
606
|
+
with DirIndexStore(index_path) as store:
|
|
607
|
+
return store.search_symbols(name, kind=kind)
|
|
608
|
+
except Exception as exc:
|
|
609
|
+
self.logger.debug(f"Symbol search error in {index_path}: {exc}")
|
|
610
|
+
return []
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
# === Convenience Functions ===
|
|
614
|
+
|
|
615
|
+
def quick_search(query: str,
|
|
616
|
+
source_path: Path,
|
|
617
|
+
depth: int = -1) -> List[SearchResult]:
|
|
618
|
+
"""Quick search convenience function with automatic initialization.
|
|
619
|
+
|
|
620
|
+
Creates temporary registry and mapper instances for one-off searches.
|
|
621
|
+
For repeated searches, create a ChainSearchEngine instance directly.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
query: FTS5 search query string
|
|
625
|
+
source_path: Starting directory path
|
|
626
|
+
depth: Maximum search depth (-1 = unlimited)
|
|
627
|
+
|
|
628
|
+
Returns:
|
|
629
|
+
List of SearchResult objects sorted by relevance
|
|
630
|
+
|
|
631
|
+
Examples:
|
|
632
|
+
>>> from pathlib import Path
|
|
633
|
+
>>> results = quick_search("authentication", Path("D:/project/src"))
|
|
634
|
+
>>> print(f"Found {len(results)} matches")
|
|
635
|
+
"""
|
|
636
|
+
registry = RegistryStore()
|
|
637
|
+
registry.initialize()
|
|
638
|
+
|
|
639
|
+
mapper = PathMapper()
|
|
640
|
+
|
|
641
|
+
with ChainSearchEngine(registry, mapper) as engine:
|
|
642
|
+
options = SearchOptions(depth=depth)
|
|
643
|
+
result = engine.search(query, source_path, options)
|
|
644
|
+
|
|
645
|
+
registry.close()
|
|
646
|
+
|
|
647
|
+
return result.results
|