claude-code-workflow 6.3.18 → 6.3.20
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 +8 -5
- package/.claude/agents/action-planning-agent.md +26 -2
- package/.claude/agents/code-developer.md +132 -43
- package/.claude/agents/debug-explore-agent.md +434 -0
- package/.claude/agents/issue-plan-agent.md +31 -2
- package/.claude/agents/test-fix-agent.md +14 -0
- package/.claude/commands/issue/discover.md +41 -0
- package/.claude/commands/issue/execute.md +200 -19
- package/.claude/commands/issue/new.md +93 -3
- package/.claude/commands/issue/plan.md +9 -3
- package/.claude/commands/issue/queue.md +94 -39
- package/.claude/commands/memory/swagger-docs.md +773 -0
- package/.claude/commands/workflow/brainstorm/auto-parallel.md +21 -21
- package/.claude/commands/workflow/execute.md +54 -34
- package/.claude/commands/workflow/lite-execute.md +48 -164
- package/.claude/commands/workflow/lite-fix.md +4 -4
- package/.claude/commands/workflow/lite-plan.md +5 -5
- package/.claude/commands/workflow/plan.md +27 -27
- package/.claude/commands/workflow/review.md +42 -17
- package/.claude/commands/workflow/tdd-plan.md +25 -25
- package/.claude/commands/workflow/test-fix-gen.md +10 -10
- package/.claude/commands/workflow/test-gen.md +14 -14
- package/.claude/commands/workflow/ui-design/explore-auto.md +21 -21
- package/.claude/commands/workflow/ui-design/imitate-auto.md +24 -24
- package/.claude/skills/_shared/SKILL-DESIGN-SPEC.md +693 -0
- package/.claude/skills/ccw/SKILL.md +462 -0
- package/.claude/skills/ccw/index/command-capabilities.json +127 -0
- package/.claude/skills/ccw/index/intent-rules.json +136 -0
- package/.claude/skills/ccw/index/workflow-chains.json +451 -0
- package/.claude/skills/ccw/phases/actions/bugfix.md +218 -0
- package/.claude/skills/ccw/phases/actions/coupled.md +194 -0
- package/.claude/skills/ccw/phases/actions/docs.md +93 -0
- package/.claude/skills/ccw/phases/actions/full.md +154 -0
- package/.claude/skills/ccw/phases/actions/issue.md +201 -0
- package/.claude/skills/ccw/phases/actions/rapid.md +104 -0
- package/.claude/skills/ccw/phases/actions/review-fix.md +84 -0
- package/.claude/skills/ccw/phases/actions/tdd.md +66 -0
- package/.claude/skills/ccw/phases/actions/ui.md +79 -0
- package/.claude/skills/ccw/phases/orchestrator.md +435 -0
- package/.claude/skills/ccw/specs/intent-classification.md +336 -0
- package/.claude/skills/ccw-help/SKILL.md +177 -0
- package/.claude/skills/ccw-help/index/all-agents.json +82 -0
- package/.claude/skills/{command-guide → ccw-help}/index/all-commands.json +183 -73
- package/.claude/skills/{command-guide → ccw-help}/index/by-category.json +187 -73
- package/.claude/skills/{command-guide → ccw-help}/index/by-use-case.json +295 -185
- package/.claude/skills/{command-guide → ccw-help}/index/command-relationships.json +19 -166
- package/.claude/skills/{command-guide → ccw-help}/index/essential-commands.json +10 -10
- package/.claude/skills/ccw-help/scripts/analyze_commands.py +337 -0
- package/.claude/skills/code-reviewer/README.md +340 -0
- package/.claude/skills/code-reviewer/SKILL.md +308 -0
- package/.claude/skills/code-reviewer/phases/01-code-discovery.md +246 -0
- package/.claude/skills/code-reviewer/phases/02-security-analysis.md +442 -0
- package/.claude/skills/code-reviewer/phases/03-best-practices-review.md +36 -0
- package/.claude/skills/code-reviewer/phases/04-report-generation.md +278 -0
- package/.claude/skills/code-reviewer/specs/best-practices-requirements.md +346 -0
- package/.claude/skills/code-reviewer/specs/quality-standards.md +252 -0
- package/.claude/skills/code-reviewer/specs/security-requirements.md +243 -0
- package/.claude/skills/code-reviewer/templates/best-practice-finding.md +234 -0
- package/.claude/skills/code-reviewer/templates/report-template.md +316 -0
- package/.claude/skills/code-reviewer/templates/security-finding.md +161 -0
- package/.claude/skills/skill-generator/SKILL.md +187 -0
- package/.claude/skills/skill-generator/phases/01-requirements-discovery.md +239 -0
- package/.claude/skills/skill-generator/phases/02-structure-generation.md +207 -0
- package/.claude/skills/skill-generator/phases/03-phase-generation.md +802 -0
- package/.claude/skills/skill-generator/phases/04-specs-templates.md +328 -0
- package/.claude/skills/skill-generator/phases/05-validation.md +334 -0
- package/.claude/skills/skill-generator/specs/cli-integration.md +448 -0
- package/.claude/skills/skill-generator/specs/execution-modes.md +396 -0
- package/.claude/skills/skill-generator/specs/scripting-integration.md +265 -0
- package/.claude/skills/skill-generator/specs/skill-requirements.md +466 -0
- package/.claude/skills/skill-generator/templates/autonomous-action.md +517 -0
- package/.claude/skills/skill-generator/templates/autonomous-orchestrator.md +276 -0
- package/.claude/skills/skill-generator/templates/code-analysis-action.md +503 -0
- package/.claude/skills/skill-generator/templates/llm-action.md +355 -0
- package/.claude/skills/skill-generator/templates/script-bash.md +277 -0
- package/.claude/skills/skill-generator/templates/script-python.md +198 -0
- package/.claude/skills/skill-generator/templates/sequential-phase.md +441 -0
- package/.claude/skills/skill-generator/templates/skill-md.md +156 -0
- package/.claude/workflows/chinese-response.md +15 -28
- package/.claude/workflows/cli-templates/prompts/documentation/swagger-api.txt +266 -0
- package/.claude/workflows/cli-tools-usage.md +221 -177
- package/.claude/workflows/windows-platform.md +13 -10
- package/.codex/prompts/issue-execute.md +310 -82
- package/.codex/prompts/issue-queue.md +22 -0
- package/.codex/prompts/lite-execute.md +36 -11
- package/README.md +309 -305
- package/ccw/README.md +10 -4
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +4 -1
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +131 -34
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/issue.d.ts +152 -0
- package/ccw/dist/commands/issue.d.ts.map +1 -1
- package/ccw/dist/commands/issue.js +550 -85
- package/ccw/dist/commands/issue.js.map +1 -1
- package/ccw/dist/commands/serve.d.ts +1 -0
- package/ccw/dist/commands/serve.d.ts.map +1 -1
- package/ccw/dist/commands/serve.js +12 -5
- package/ccw/dist/commands/serve.js.map +1 -1
- package/ccw/dist/commands/stop.d.ts.map +1 -1
- package/ccw/dist/commands/stop.js +29 -5
- package/ccw/dist/commands/stop.js.map +1 -1
- package/ccw/dist/commands/tool.d.ts.map +1 -1
- package/ccw/dist/commands/tool.js +19 -2
- package/ccw/dist/commands/tool.js.map +1 -1
- package/ccw/dist/commands/view.d.ts +1 -0
- package/ccw/dist/commands/view.d.ts.map +1 -1
- package/ccw/dist/commands/view.js +10 -3
- package/ccw/dist/commands/view.js.map +1 -1
- package/ccw/dist/config/cli-settings-manager.d.ts +86 -0
- package/ccw/dist/config/cli-settings-manager.d.ts.map +1 -0
- package/ccw/dist/config/cli-settings-manager.js +392 -0
- package/ccw/dist/config/cli-settings-manager.js.map +1 -0
- package/ccw/dist/config/litellm-api-config-manager.d.ts +71 -5
- package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.js +290 -20
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -1
- package/ccw/dist/core/auth/csrf-manager.d.ts +18 -0
- package/ccw/dist/core/auth/csrf-manager.d.ts.map +1 -0
- package/ccw/dist/core/auth/csrf-manager.js +80 -0
- package/ccw/dist/core/auth/csrf-manager.js.map +1 -0
- package/ccw/dist/core/auth/csrf-middleware.d.ts +8 -0
- package/ccw/dist/core/auth/csrf-middleware.d.ts.map +1 -0
- package/ccw/dist/core/auth/csrf-middleware.js +141 -0
- package/ccw/dist/core/auth/csrf-middleware.js.map +1 -0
- package/ccw/dist/core/auth/middleware.d.ts +15 -0
- package/ccw/dist/core/auth/middleware.d.ts.map +1 -0
- package/ccw/dist/core/auth/middleware.js +76 -0
- package/ccw/dist/core/auth/middleware.js.map +1 -0
- package/ccw/dist/core/auth/token-manager.d.ts +41 -0
- package/ccw/dist/core/auth/token-manager.d.ts.map +1 -0
- package/ccw/dist/core/auth/token-manager.js +171 -0
- package/ccw/dist/core/auth/token-manager.js.map +1 -0
- package/ccw/dist/core/cache-manager.d.ts +6 -6
- package/ccw/dist/core/cache-manager.d.ts.map +1 -1
- package/ccw/dist/core/cache-manager.js +70 -48
- package/ccw/dist/core/cache-manager.js.map +1 -1
- package/ccw/dist/core/claude-freshness.d.ts.map +1 -1
- package/ccw/dist/core/claude-freshness.js +23 -3
- package/ccw/dist/core/claude-freshness.js.map +1 -1
- package/ccw/dist/core/core-memory-store.d.ts.map +1 -1
- package/ccw/dist/core/core-memory-store.js +2 -1
- package/ccw/dist/core/core-memory-store.js.map +1 -1
- package/ccw/dist/core/cors.d.ts +3 -0
- package/ccw/dist/core/cors.d.ts.map +1 -0
- package/ccw/dist/core/cors.js +10 -0
- package/ccw/dist/core/cors.js.map +1 -0
- package/ccw/dist/core/dashboard-generator-patch.js +0 -1
- package/ccw/dist/core/dashboard-generator-patch.js.map +1 -1
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
- package/ccw/dist/core/dashboard-generator.js +417 -416
- package/ccw/dist/core/dashboard-generator.js.map +1 -1
- package/ccw/dist/core/data-aggregator.js +2 -2
- package/ccw/dist/core/data-aggregator.js.map +1 -1
- package/ccw/dist/core/lite-scanner.d.ts +1 -1
- package/ccw/dist/core/lite-scanner.d.ts.map +1 -1
- package/ccw/dist/core/lite-scanner.js +130 -127
- package/ccw/dist/core/lite-scanner.js.map +1 -1
- package/ccw/dist/core/routes/auth-routes.d.ts +12 -0
- package/ccw/dist/core/routes/auth-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/auth-routes.js +80 -0
- package/ccw/dist/core/routes/auth-routes.js.map +1 -0
- package/ccw/dist/core/routes/ccw-routes.d.ts +1 -14
- package/ccw/dist/core/routes/ccw-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/ccw-routes.js +9 -4
- package/ccw/dist/core/routes/ccw-routes.js.map +1 -1
- package/ccw/dist/core/routes/claude-routes.d.ts +1 -14
- package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/claude-routes.js +98 -39
- package/ccw/dist/core/routes/claude-routes.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts +14 -12
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +122 -43
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/cli-settings-routes.d.ts +11 -0
- package/ccw/dist/core/routes/cli-settings-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/cli-settings-routes.js +204 -0
- package/ccw/dist/core/routes/cli-settings-routes.js.map +1 -0
- package/ccw/dist/core/routes/codexlens/config-handlers.d.ts +6 -0
- package/ccw/dist/core/routes/codexlens/config-handlers.d.ts.map +1 -0
- package/ccw/dist/core/routes/codexlens/config-handlers.js +1195 -0
- package/ccw/dist/core/routes/codexlens/config-handlers.js.map +1 -0
- package/ccw/dist/core/routes/codexlens/index-handlers.d.ts +10 -0
- package/ccw/dist/core/routes/codexlens/index-handlers.d.ts.map +1 -0
- package/ccw/dist/core/routes/codexlens/index-handlers.js +322 -0
- package/ccw/dist/core/routes/codexlens/index-handlers.js.map +1 -0
- package/ccw/dist/core/routes/codexlens/semantic-handlers.d.ts +6 -0
- package/ccw/dist/core/routes/codexlens/semantic-handlers.d.ts.map +1 -0
- package/ccw/dist/core/routes/codexlens/semantic-handlers.js +865 -0
- package/ccw/dist/core/routes/codexlens/semantic-handlers.js.map +1 -0
- package/ccw/dist/core/routes/codexlens/utils.d.ts +23 -0
- package/ccw/dist/core/routes/codexlens/utils.d.ts.map +1 -0
- package/ccw/dist/core/routes/codexlens/utils.js +85 -0
- package/ccw/dist/core/routes/codexlens/utils.js.map +1 -0
- package/ccw/dist/core/routes/codexlens/watcher-handlers.d.ts +13 -0
- package/ccw/dist/core/routes/codexlens/watcher-handlers.d.ts.map +1 -0
- package/ccw/dist/core/routes/codexlens/watcher-handlers.js +235 -0
- package/ccw/dist/core/routes/codexlens/watcher-handlers.js.map +1 -0
- package/ccw/dist/core/routes/codexlens-routes.d.ts +2 -11
- package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.js +10 -981
- package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
- package/ccw/dist/core/routes/discovery-routes.d.ts +1 -35
- package/ccw/dist/core/routes/discovery-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/discovery-routes.js +25 -0
- package/ccw/dist/core/routes/discovery-routes.js.map +1 -1
- package/ccw/dist/core/routes/files-routes.d.ts +1 -14
- package/ccw/dist/core/routes/files-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/files-routes.js +57 -14
- package/ccw/dist/core/routes/files-routes.js.map +1 -1
- package/ccw/dist/core/routes/graph-routes.d.ts +1 -14
- package/ccw/dist/core/routes/graph-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/graph-routes.js +36 -37
- package/ccw/dist/core/routes/graph-routes.js.map +1 -1
- package/ccw/dist/core/routes/help-routes.d.ts +1 -14
- package/ccw/dist/core/routes/help-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/help-routes.js +5 -0
- package/ccw/dist/core/routes/help-routes.js.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.d.ts +4 -14
- package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.js +43 -21
- package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
- package/ccw/dist/core/routes/issue-routes.d.ts +1 -34
- package/ccw/dist/core/routes/issue-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/issue-routes.js +24 -0
- package/ccw/dist/core/routes/issue-routes.js.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.d.ts +1 -14
- package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.js +513 -48
- package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
- package/ccw/dist/core/routes/litellm-routes.d.ts +1 -14
- package/ccw/dist/core/routes/litellm-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/litellm-routes.js +28 -11
- package/ccw/dist/core/routes/litellm-routes.js.map +1 -1
- package/ccw/dist/core/routes/mcp-routes.d.ts +1 -14
- package/ccw/dist/core/routes/mcp-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/mcp-routes.js +99 -30
- package/ccw/dist/core/routes/mcp-routes.js.map +1 -1
- package/ccw/dist/core/routes/mcp-templates-db.d.ts.map +1 -1
- package/ccw/dist/core/routes/mcp-templates-db.js +30 -31
- package/ccw/dist/core/routes/mcp-templates-db.js.map +1 -1
- package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/memory-routes.js +74 -24
- package/ccw/dist/core/routes/memory-routes.js.map +1 -1
- package/ccw/dist/core/routes/nav-status-routes.d.ts +3 -0
- package/ccw/dist/core/routes/nav-status-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/nav-status-routes.js +217 -0
- package/ccw/dist/core/routes/nav-status-routes.js.map +1 -0
- package/ccw/dist/core/routes/rules-routes.d.ts +1 -14
- package/ccw/dist/core/routes/rules-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/rules-routes.js +481 -58
- package/ccw/dist/core/routes/rules-routes.js.map +1 -1
- package/ccw/dist/core/routes/session-routes.d.ts +1 -14
- package/ccw/dist/core/routes/session-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/session-routes.js +15 -3
- package/ccw/dist/core/routes/session-routes.js.map +1 -1
- package/ccw/dist/core/routes/skills-routes.d.ts +1 -14
- package/ccw/dist/core/routes/skills-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/skills-routes.js +394 -112
- package/ccw/dist/core/routes/skills-routes.js.map +1 -1
- package/ccw/dist/core/routes/status-routes.d.ts +1 -14
- package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/status-routes.js +4 -0
- package/ccw/dist/core/routes/status-routes.js.map +1 -1
- package/ccw/dist/core/routes/system-routes.d.ts +4 -10
- package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/system-routes.js +6 -4
- package/ccw/dist/core/routes/system-routes.js.map +1 -1
- package/ccw/dist/core/routes/types.d.ts +19 -0
- package/ccw/dist/core/routes/types.d.ts.map +1 -0
- package/ccw/dist/core/routes/types.js +2 -0
- package/ccw/dist/core/routes/types.js.map +1 -0
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +206 -29
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/core/services/api-key-tester.d.ts +42 -0
- package/ccw/dist/core/services/api-key-tester.d.ts.map +1 -0
- package/ccw/dist/core/services/api-key-tester.js +126 -0
- package/ccw/dist/core/services/api-key-tester.js.map +1 -0
- package/ccw/dist/core/services/health-check-service.d.ts +88 -0
- package/ccw/dist/core/services/health-check-service.d.ts.map +1 -0
- package/ccw/dist/core/services/health-check-service.js +293 -0
- package/ccw/dist/core/services/health-check-service.js.map +1 -0
- package/ccw/dist/core/websocket.d.ts +9 -7
- package/ccw/dist/core/websocket.d.ts.map +1 -1
- package/ccw/dist/core/websocket.js +9 -4
- package/ccw/dist/core/websocket.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +152 -28
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.js +490 -100
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
- package/ccw/dist/tools/cli-config-manager.d.ts +24 -8
- package/ccw/dist/tools/cli-config-manager.d.ts.map +1 -1
- package/ccw/dist/tools/cli-config-manager.js +76 -156
- package/ccw/dist/tools/cli-config-manager.js.map +1 -1
- package/ccw/dist/tools/cli-executor-core.d.ts +85 -0
- package/ccw/dist/tools/cli-executor-core.d.ts.map +1 -0
- package/ccw/dist/tools/cli-executor-core.js +1310 -0
- package/ccw/dist/tools/cli-executor-core.js.map +1 -0
- package/ccw/dist/tools/cli-executor-state.d.ts +241 -0
- package/ccw/dist/tools/cli-executor-state.d.ts.map +1 -0
- package/ccw/dist/tools/cli-executor-state.js +392 -0
- package/ccw/dist/tools/cli-executor-state.js.map +1 -0
- package/ccw/dist/tools/cli-executor-utils.d.ts +36 -0
- package/ccw/dist/tools/cli-executor-utils.d.ts.map +1 -0
- package/ccw/dist/tools/cli-executor-utils.js +298 -0
- package/ccw/dist/tools/cli-executor-utils.js.map +1 -0
- package/ccw/dist/tools/cli-executor.d.ts +3 -377
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor.js +3 -1884
- package/ccw/dist/tools/cli-executor.js.map +1 -1
- package/ccw/dist/tools/cli-history-store.d.ts +2 -0
- package/ccw/dist/tools/cli-history-store.d.ts.map +1 -1
- package/ccw/dist/tools/cli-history-store.js.map +1 -1
- package/ccw/dist/tools/cli-output-converter.d.ts +192 -0
- package/ccw/dist/tools/cli-output-converter.d.ts.map +1 -0
- package/ccw/dist/tools/cli-output-converter.js +1047 -0
- package/ccw/dist/tools/cli-output-converter.js.map +1 -0
- package/ccw/dist/tools/cli-prompt-builder.d.ts +113 -0
- package/ccw/dist/tools/cli-prompt-builder.d.ts.map +1 -0
- package/ccw/dist/tools/cli-prompt-builder.js +363 -0
- package/ccw/dist/tools/cli-prompt-builder.js.map +1 -0
- package/ccw/dist/tools/codex-lens.d.ts +15 -1
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +289 -55
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/detect-changed-modules.d.ts.map +1 -1
- package/ccw/dist/tools/detect-changed-modules.js +22 -4
- package/ccw/dist/tools/detect-changed-modules.js.map +1 -1
- package/ccw/dist/tools/index.d.ts.map +1 -1
- package/ccw/dist/tools/index.js +2 -0
- package/ccw/dist/tools/index.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -1
- package/ccw/dist/tools/litellm-client.js +10 -4
- package/ccw/dist/tools/litellm-client.js.map +1 -1
- package/ccw/dist/tools/litellm-executor.d.ts +2 -4
- package/ccw/dist/tools/litellm-executor.d.ts.map +1 -1
- package/ccw/dist/tools/litellm-executor.js +39 -8
- package/ccw/dist/tools/litellm-executor.js.map +1 -1
- package/ccw/dist/tools/native-session-discovery.d.ts +2 -0
- package/ccw/dist/tools/native-session-discovery.d.ts.map +1 -1
- package/ccw/dist/tools/native-session-discovery.js +197 -1
- package/ccw/dist/tools/native-session-discovery.js.map +1 -1
- package/ccw/dist/tools/session-manager.d.ts.map +1 -1
- package/ccw/dist/tools/session-manager.js +79 -0
- package/ccw/dist/tools/session-manager.js.map +1 -1
- package/ccw/dist/tools/skill-context-loader.d.ts +15 -0
- package/ccw/dist/tools/skill-context-loader.d.ts.map +1 -0
- package/ccw/dist/tools/skill-context-loader.js +198 -0
- package/ccw/dist/tools/skill-context-loader.js.map +1 -0
- package/ccw/dist/tools/smart-search.d.ts +8 -3
- package/ccw/dist/tools/smart-search.d.ts.map +1 -1
- package/ccw/dist/tools/smart-search.js +378 -75
- package/ccw/dist/tools/smart-search.js.map +1 -1
- package/ccw/dist/types/cli-settings.d.ts +86 -0
- package/ccw/dist/types/cli-settings.d.ts.map +1 -0
- package/ccw/dist/types/cli-settings.js +54 -0
- package/ccw/dist/types/cli-settings.js.map +1 -0
- package/ccw/dist/types/litellm-api-config.d.ts +40 -1
- package/ccw/dist/types/litellm-api-config.d.ts.map +1 -1
- package/ccw/dist/utils/exec-constants.d.ts +25 -0
- package/ccw/dist/utils/exec-constants.d.ts.map +1 -0
- package/ccw/dist/utils/exec-constants.js +25 -0
- package/ccw/dist/utils/exec-constants.js.map +1 -0
- package/ccw/dist/utils/path-resolver.d.ts +1 -0
- package/ccw/dist/utils/path-resolver.d.ts.map +1 -1
- package/ccw/dist/utils/path-resolver.js +48 -3
- package/ccw/dist/utils/path-resolver.js.map +1 -1
- package/ccw/dist/utils/path-validator.d.ts.map +1 -1
- package/ccw/dist/utils/path-validator.js +25 -6
- package/ccw/dist/utils/path-validator.js.map +1 -1
- package/ccw/dist/utils/python-utils.d.ts.map +1 -1
- package/ccw/dist/utils/python-utils.js +27 -7
- package/ccw/dist/utils/python-utils.js.map +1 -1
- package/ccw/dist/utils/shell-escape.d.ts +8 -0
- package/ccw/dist/utils/shell-escape.d.ts.map +1 -0
- package/ccw/dist/utils/shell-escape.js +24 -0
- package/ccw/dist/utils/shell-escape.js.map +1 -0
- package/ccw/dist/utils/uv-manager.d.ts +167 -0
- package/ccw/dist/utils/uv-manager.d.ts.map +1 -0
- package/ccw/dist/utils/uv-manager.js +644 -0
- package/ccw/dist/utils/uv-manager.js.map +1 -0
- package/ccw/src/cli.ts +4 -1
- package/ccw/src/commands/cli.ts +132 -34
- package/ccw/src/commands/issue.ts +605 -91
- package/ccw/src/commands/serve.ts +15 -5
- package/ccw/src/commands/stop.ts +32 -5
- package/ccw/src/commands/tool.ts +17 -2
- package/ccw/src/commands/view.ts +13 -3
- package/ccw/src/config/cli-settings-manager.ts +460 -0
- package/ccw/src/config/litellm-api-config-manager.ts +392 -57
- package/ccw/src/core/auth/csrf-manager.ts +104 -0
- package/ccw/src/core/auth/csrf-middleware.ts +159 -0
- package/ccw/src/core/auth/middleware.ts +94 -0
- package/ccw/src/core/auth/token-manager.ts +219 -0
- package/ccw/src/core/cache-manager.ts +64 -52
- package/ccw/src/core/claude-freshness.ts +26 -6
- package/ccw/src/core/core-memory-store.ts +2 -1
- package/ccw/src/core/cors.ts +10 -0
- package/ccw/src/core/dashboard-generator-patch.ts +47 -48
- package/ccw/src/core/dashboard-generator.ts +797 -744
- package/ccw/src/core/data-aggregator.ts +667 -667
- package/ccw/src/core/lite-scanner.ts +156 -140
- package/ccw/src/core/routes/auth-routes.ts +98 -0
- package/ccw/src/core/routes/ccw-routes.ts +10 -20
- package/ccw/src/core/routes/claude-routes.ts +101 -51
- package/ccw/src/core/routes/cli-routes.ts +152 -55
- package/ccw/src/core/routes/cli-settings-routes.ts +232 -0
- package/ccw/src/core/routes/codexlens/README.md +37 -0
- package/ccw/src/core/routes/codexlens/config-handlers.ts +1269 -0
- package/ccw/src/core/routes/codexlens/index-handlers.ts +354 -0
- package/ccw/src/core/routes/codexlens/semantic-handlers.ts +931 -0
- package/ccw/src/core/routes/codexlens/utils.ts +96 -0
- package/ccw/src/core/routes/codexlens/watcher-handlers.ts +265 -0
- package/ccw/src/core/routes/codexlens-routes.ts +11 -1044
- package/ccw/src/core/routes/discovery-routes.ts +1 -12
- package/ccw/src/core/routes/files-routes.ts +112 -40
- package/ccw/src/core/routes/graph-routes.ts +39 -46
- package/ccw/src/core/routes/help-routes.ts +2 -12
- package/ccw/src/core/routes/hooks-routes.ts +83 -44
- package/ccw/src/core/routes/issue-routes.ts +1 -12
- package/ccw/src/core/routes/litellm-api-routes.ts +574 -60
- package/ccw/src/core/routes/litellm-routes.ts +35 -27
- package/ccw/src/core/routes/mcp-routes.ts +157 -60
- package/ccw/src/core/routes/mcp-routes.ts.backup +549 -550
- package/ccw/src/core/routes/mcp-templates-db.ts +267 -268
- package/ccw/src/core/routes/memory-routes.ts +76 -22
- package/ccw/src/core/routes/nav-status-routes.ts +231 -0
- package/ccw/src/core/routes/rules-routes.ts +600 -81
- package/ccw/src/core/routes/session-routes.ts +28 -22
- package/ccw/src/core/routes/skills-routes.ts +452 -132
- package/ccw/src/core/routes/status-routes.ts +1 -12
- package/ccw/src/core/routes/system-routes.ts +15 -22
- package/ccw/src/core/routes/types.ts +25 -0
- package/ccw/src/core/server.ts +657 -468
- package/ccw/src/core/services/api-key-tester.ts +160 -0
- package/ccw/src/core/services/health-check-service.ts +366 -0
- package/ccw/src/core/websocket.ts +20 -12
- package/ccw/src/templates/dashboard-css/01-base.css +109 -0
- package/ccw/src/templates/dashboard-css/10-cli-status.css +202 -0
- package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +308 -0
- package/ccw/src/templates/dashboard-css/30-core-memory.css +20 -0
- package/ccw/src/templates/dashboard-css/31-api-settings.css +751 -14
- package/ccw/src/templates/dashboard-css/33-cli-stream-viewer.css +230 -2
- package/ccw/src/templates/dashboard-js/api.js +5 -0
- package/ccw/src/templates/dashboard-js/components/cli-status.js +279 -107
- package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +262 -20
- package/ccw/src/templates/dashboard-js/components/hook-manager.js +105 -5
- package/ccw/src/templates/dashboard-js/components/mcp-manager.js +317 -0
- package/ccw/src/templates/dashboard-js/components/navigation.js +45 -0
- package/ccw/src/templates/dashboard-js/components/notifications.js +128 -0
- package/ccw/src/templates/dashboard-js/i18n.js +4448 -3983
- package/ccw/src/templates/dashboard-js/main.js +71 -0
- package/ccw/src/templates/dashboard-js/services.js +289 -0
- package/ccw/src/templates/dashboard-js/views/api-settings.js +5613 -3361
- package/ccw/src/templates/dashboard-js/views/claude-manager.js +1 -7
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +581 -87
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +6101 -1965
- package/ccw/src/templates/dashboard-js/views/core-memory.js +129 -20
- package/ccw/src/templates/dashboard-js/views/hook-manager.js +17 -3
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js +63 -0
- package/ccw/src/templates/dashboard-js/views/project-overview.js +182 -37
- package/ccw/src/templates/dashboard-js/views/rules-manager.js +26 -3
- package/ccw/src/templates/dashboard-js/views/skills-manager.js +2 -42
- package/ccw/src/templates/dashboard.html +6 -0
- package/ccw/src/tools/README.md +29 -0
- package/ccw/src/tools/claude-cli-tools.ts +640 -125
- package/ccw/src/tools/cli-config-manager.ts +102 -172
- package/ccw/src/tools/cli-executor-core.ts +1533 -0
- package/ccw/src/tools/cli-executor-state.ts +560 -0
- package/ccw/src/tools/cli-executor-utils.ts +349 -0
- package/ccw/src/tools/cli-executor.ts +3 -2309
- package/ccw/src/tools/cli-history-store.ts +2 -0
- package/ccw/src/tools/cli-output-converter.ts +1237 -0
- package/ccw/src/tools/cli-prompt-builder.ts +487 -0
- package/ccw/src/tools/codex-lens.ts +324 -59
- package/ccw/src/tools/detect-changed-modules.ts +24 -6
- package/ccw/src/tools/index.ts +2 -0
- package/ccw/src/tools/litellm-client.ts +10 -4
- package/ccw/src/tools/litellm-executor.ts +146 -114
- package/ccw/src/tools/native-session-discovery.ts +209 -1
- package/ccw/src/tools/session-manager.ts +88 -0
- package/ccw/src/tools/skill-context-loader.ts +213 -0
- package/ccw/src/tools/smart-search.ts +427 -76
- package/ccw/src/types/cli-settings.ts +137 -0
- package/ccw/src/types/litellm-api-config.ts +55 -1
- package/ccw/src/utils/exec-constants.ts +24 -0
- package/ccw/src/utils/path-resolver.ts +49 -3
- package/ccw/src/utils/path-validator.ts +28 -6
- package/ccw/src/utils/python-utils.ts +140 -121
- package/ccw/src/utils/shell-escape.ts +30 -0
- package/ccw/src/utils/uv-manager.ts +796 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +270 -251
- package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +33 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/loader.py +343 -316
- package/ccw-litellm/src/ccw_litellm/config/models.py +162 -130
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-312.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-310.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-312.pyc +0 -0
- package/codex-lens/pyproject.toml +43 -0
- package/codex-lens/src/codexlens/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/__main__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/__main__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/entities.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/entities.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/env_config.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/env_config.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/env_config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/errors.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/errors.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-312.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-310.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-312.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-310.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-312.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-310.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +4416 -2295
- package/codex-lens/src/codexlens/cli/embedding_manager.py +777 -15
- package/codex-lens/src/codexlens/cli/model_manager.py +676 -0
- package/codex-lens/src/codexlens/config.py +356 -12
- package/codex-lens/src/codexlens/entities.py +4 -1
- package/codex-lens/src/codexlens/env_config.py +304 -0
- package/codex-lens/src/codexlens/indexing/__init__.py +23 -1
- package/codex-lens/src/codexlens/indexing/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/indexing/__pycache__/embedding.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/embedding.py +582 -0
- package/codex-lens/src/codexlens/indexing/symbol_extractor.py +62 -28
- package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-312.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-310.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/tokenizer.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/factory.py +139 -10
- package/codex-lens/src/codexlens/parsers/treesitter_parser.py +487 -13
- package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/binary_searcher.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-312.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__/graph_expander.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/binary_searcher.py +277 -0
- package/codex-lens/src/codexlens/search/chain_search.py +1652 -8
- package/codex-lens/src/codexlens/search/enrichment.py +21 -0
- package/codex-lens/src/codexlens/search/graph_expander.py +264 -0
- package/codex-lens/src/codexlens/search/hybrid_search.py +772 -37
- package/codex-lens/src/codexlens/search/ranking.py +397 -8
- package/codex-lens/src/codexlens/semantic/SPLADE_IMPLEMENTATION.md +225 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/reranker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/splade_encoder.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/splade_encoder.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/splade_encoder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-312.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 +654 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +328 -23
- package/codex-lens/src/codexlens/semantic/factory.py +63 -3
- package/codex-lens/src/codexlens/semantic/gpu_support.py +19 -2
- package/codex-lens/src/codexlens/semantic/litellm_embedder.py +144 -144
- package/codex-lens/src/codexlens/semantic/reranker/__init__.py +25 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/api_reranker.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/api_reranker.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/api_reranker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/base.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/base.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/base.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/factory.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/factory.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/fastembed_reranker.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/fastembed_reranker.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/fastembed_reranker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/legacy.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/legacy.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/legacy.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/litellm_reranker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/onnx_reranker.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/onnx_reranker.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/__pycache__/onnx_reranker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/reranker/api_reranker.py +403 -0
- package/codex-lens/src/codexlens/semantic/reranker/base.py +46 -0
- package/codex-lens/src/codexlens/semantic/reranker/factory.py +159 -0
- package/codex-lens/src/codexlens/semantic/reranker/fastembed_reranker.py +257 -0
- package/codex-lens/src/codexlens/semantic/reranker/legacy.py +91 -0
- package/codex-lens/src/codexlens/semantic/reranker/litellm_reranker.py +214 -0
- package/codex-lens/src/codexlens/semantic/reranker/onnx_reranker.py +268 -0
- package/codex-lens/src/codexlens/semantic/splade_encoder.py +567 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +472 -352
- package/codex-lens/src/codexlens/storage/__init__.py +3 -0
- package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/global_index.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/global_index.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/merkle_tree.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/splade_index.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/splade_index.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/splade_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-312.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-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_utils.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/vector_meta_store.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/vector_meta_store.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/vector_meta_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/dir_index.py +310 -12
- package/codex-lens/src/codexlens/storage/index_tree.py +240 -25
- package/codex-lens/src/codexlens/storage/merkle_tree.py +136 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_006_enhance_relationships.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_007_add_graph_neighbors.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_007_add_graph_neighbors.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_007_add_graph_neighbors.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_008_add_merkle_hashes.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_009_add_splade.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_010_add_multi_vector_chunks.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_006_enhance_relationships.py +37 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_007_add_graph_neighbors.py +47 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_008_add_merkle_hashes.py +81 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_009_add_splade.py +103 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_010_add_multi_vector_chunks.py +162 -0
- package/codex-lens/src/codexlens/storage/splade_index.py +578 -0
- package/codex-lens/src/codexlens/storage/sqlite_store.py +508 -184
- package/codex-lens/src/codexlens/storage/vector_meta_store.py +415 -0
- package/codex-lens/src/codexlens/watcher/__init__.py +17 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/__init__.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/__init__.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/events.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/events.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/events.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/file_watcher.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/file_watcher.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/file_watcher.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/incremental_indexer.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/incremental_indexer.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/incremental_indexer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/manager.cpython-310.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/manager.cpython-312.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/__pycache__/manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/watcher/events.py +82 -0
- package/codex-lens/src/codexlens/watcher/file_watcher.py +347 -0
- package/codex-lens/src/codexlens/watcher/incremental_indexer.py +369 -0
- package/codex-lens/src/codexlens/watcher/manager.py +255 -0
- package/package.json +4 -1
- package/.claude/commands/workflow/docs/analyze.md +0 -1467
- package/.claude/commands/workflow/docs/copyright.md +0 -1265
- package/.claude/skills/command-guide/SKILL.md +0 -388
- package/.claude/skills/command-guide/UPDATE-GUIDELINE.md +0 -592
- package/.claude/skills/command-guide/guides/cli-tools-guide.md +0 -410
- package/.claude/skills/command-guide/guides/examples.md +0 -537
- package/.claude/skills/command-guide/guides/getting-started.md +0 -242
- package/.claude/skills/command-guide/guides/implementation-details.md +0 -1010
- package/.claude/skills/command-guide/guides/index-structure.md +0 -326
- package/.claude/skills/command-guide/guides/troubleshooting.md +0 -92
- package/.claude/skills/command-guide/guides/ui-design-workflow-guide.md +0 -316
- package/.claude/skills/command-guide/guides/workflow-patterns.md +0 -662
- package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +0 -855
- package/.claude/skills/command-guide/reference/agents/cli-execution-agent.md +0 -267
- package/.claude/skills/command-guide/reference/agents/cli-explore-agent.md +0 -182
- package/.claude/skills/command-guide/reference/agents/cli-lite-planning-agent.md +0 -446
- package/.claude/skills/command-guide/reference/agents/cli-planning-agent.md +0 -558
- package/.claude/skills/command-guide/reference/agents/code-developer.md +0 -311
- package/.claude/skills/command-guide/reference/agents/conceptual-planning-agent.md +0 -308
- package/.claude/skills/command-guide/reference/agents/context-search-agent.md +0 -581
- package/.claude/skills/command-guide/reference/agents/doc-generator.md +0 -330
- package/.claude/skills/command-guide/reference/agents/memory-bridge.md +0 -94
- package/.claude/skills/command-guide/reference/agents/test-context-search-agent.md +0 -400
- package/.claude/skills/command-guide/reference/agents/test-fix-agent.md +0 -344
- package/.claude/skills/command-guide/reference/agents/ui-design-agent.md +0 -593
- package/.claude/skills/command-guide/reference/agents/universal-executor.md +0 -131
- package/.claude/skills/command-guide/reference/commands/cli/cli-init.md +0 -440
- package/.claude/skills/command-guide/reference/commands/enhance-prompt.md +0 -93
- package/.claude/skills/command-guide/reference/commands/memory/code-map-memory.md +0 -687
- package/.claude/skills/command-guide/reference/commands/memory/docs-full-cli.md +0 -471
- package/.claude/skills/command-guide/reference/commands/memory/docs-related-cli.md +0 -386
- package/.claude/skills/command-guide/reference/commands/memory/docs.md +0 -616
- package/.claude/skills/command-guide/reference/commands/memory/load-skill-memory.md +0 -182
- package/.claude/skills/command-guide/reference/commands/memory/load.md +0 -240
- package/.claude/skills/command-guide/reference/commands/memory/skill-memory.md +0 -525
- package/.claude/skills/command-guide/reference/commands/memory/style-skill-memory.md +0 -396
- package/.claude/skills/command-guide/reference/commands/memory/tech-research.md +0 -314
- package/.claude/skills/command-guide/reference/commands/memory/update-full.md +0 -332
- package/.claude/skills/command-guide/reference/commands/memory/update-related.md +0 -332
- package/.claude/skills/command-guide/reference/commands/memory/workflow-skill-memory.md +0 -517
- package/.claude/skills/command-guide/reference/commands/task/breakdown.md +0 -204
- package/.claude/skills/command-guide/reference/commands/task/create.md +0 -152
- package/.claude/skills/command-guide/reference/commands/task/execute.md +0 -270
- package/.claude/skills/command-guide/reference/commands/task/replan.md +0 -437
- package/.claude/skills/command-guide/reference/commands/version.md +0 -254
- package/.claude/skills/command-guide/reference/commands/workflow/action-plan-verify.md +0 -447
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/api-designer.md +0 -585
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/artifacts.md +0 -452
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/auto-parallel.md +0 -443
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/data-architect.md +0 -220
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-manager.md +0 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-owner.md +0 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/scrum-master.md +0 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/subject-matter-expert.md +0 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/synthesis.md +0 -398
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/system-architect.md +0 -387
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ui-designer.md +0 -221
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ux-expert.md +0 -221
- package/.claude/skills/command-guide/reference/commands/workflow/execute.md +0 -465
- package/.claude/skills/command-guide/reference/commands/workflow/init.md +0 -164
- package/.claude/skills/command-guide/reference/commands/workflow/lite-execute.md +0 -748
- package/.claude/skills/command-guide/reference/commands/workflow/lite-fix.md +0 -664
- package/.claude/skills/command-guide/reference/commands/workflow/lite-plan.md +0 -645
- package/.claude/skills/command-guide/reference/commands/workflow/plan.md +0 -551
- package/.claude/skills/command-guide/reference/commands/workflow/replan.md +0 -515
- package/.claude/skills/command-guide/reference/commands/workflow/review-fix.md +0 -606
- package/.claude/skills/command-guide/reference/commands/workflow/review-module-cycle.md +0 -765
- package/.claude/skills/command-guide/reference/commands/workflow/review-session-cycle.md +0 -776
- package/.claude/skills/command-guide/reference/commands/workflow/review.md +0 -298
- package/.claude/skills/command-guide/reference/commands/workflow/session/complete.md +0 -547
- package/.claude/skills/command-guide/reference/commands/workflow/session/list.md +0 -114
- package/.claude/skills/command-guide/reference/commands/workflow/session/resume.md +0 -77
- package/.claude/skills/command-guide/reference/commands/workflow/session/start.md +0 -257
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-plan.md +0 -460
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-verify.md +0 -400
- package/.claude/skills/command-guide/reference/commands/workflow/test-cycle-execute.md +0 -498
- package/.claude/skills/command-guide/reference/commands/workflow/test-fix-gen.md +0 -699
- package/.claude/skills/command-guide/reference/commands/workflow/test-gen.md +0 -529
- package/.claude/skills/command-guide/reference/commands/workflow/tools/conflict-resolution.md +0 -766
- package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +0 -433
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-agent.md +0 -487
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +0 -518
- package/.claude/skills/command-guide/reference/commands/workflow/tools/tdd-coverage-analysis.md +0 -309
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-concept-enhanced.md +0 -163
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-context-gather.md +0 -232
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-task-generate.md +0 -254
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/animation-extract.md +0 -1150
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/codify-style.md +0 -652
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/design-sync.md +0 -454
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/explore-auto.md +0 -678
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/generate.md +0 -504
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/imitate-auto.md +0 -745
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/import-from-code.md +0 -537
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/layout-extract.md +0 -788
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/reference-page-generator.md +0 -356
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/style-extract.md +0 -773
- package/.claude/skills/command-guide/scripts/analyze_commands.py +0 -502
- package/.claude/skills/command-guide/scripts/update-index.sh +0 -130
- package/.claude/skills/command-guide/templates/issue-bug.md +0 -104
- package/.claude/skills/command-guide/templates/issue-diagnosis.md +0 -275
- package/.claude/skills/command-guide/templates/issue-feature.md +0 -97
- package/.claude/skills/command-guide/templates/issue-question.md +0 -141
|
@@ -8,7 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import logging
|
|
10
10
|
import time
|
|
11
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
11
|
+
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError, as_completed
|
|
12
12
|
from contextlib import contextmanager
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Any, Dict, List, Optional
|
|
@@ -31,17 +31,33 @@ def timer(name: str, logger: logging.Logger, level: int = logging.DEBUG):
|
|
|
31
31
|
logger.log(level, "[TIMING] %s: %.2fms", name, elapsed_ms)
|
|
32
32
|
|
|
33
33
|
from codexlens.config import Config
|
|
34
|
+
from codexlens.config import VECTORS_HNSW_NAME
|
|
34
35
|
from codexlens.entities import SearchResult
|
|
35
36
|
from codexlens.search.ranking import (
|
|
37
|
+
DEFAULT_WEIGHTS,
|
|
38
|
+
FTS_FALLBACK_WEIGHTS,
|
|
39
|
+
QueryIntent,
|
|
36
40
|
apply_symbol_boost,
|
|
41
|
+
cross_encoder_rerank,
|
|
42
|
+
detect_query_intent,
|
|
43
|
+
filter_results_by_category,
|
|
37
44
|
get_rrf_weights,
|
|
38
45
|
reciprocal_rank_fusion,
|
|
39
46
|
rerank_results,
|
|
47
|
+
simple_weighted_fusion,
|
|
40
48
|
tag_search_source,
|
|
41
49
|
)
|
|
42
50
|
from codexlens.storage.dir_index import DirIndexStore
|
|
43
51
|
|
|
44
52
|
|
|
53
|
+
# Three-way fusion weights (FTS + Vector + SPLADE)
|
|
54
|
+
THREE_WAY_WEIGHTS = {
|
|
55
|
+
"exact": 0.2,
|
|
56
|
+
"splade": 0.3,
|
|
57
|
+
"vector": 0.5,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
45
61
|
class HybridSearchEngine:
|
|
46
62
|
"""Hybrid search engine with parallel execution and RRF fusion.
|
|
47
63
|
|
|
@@ -53,12 +69,9 @@ class HybridSearchEngine:
|
|
|
53
69
|
default_weights: Default RRF weights for each source
|
|
54
70
|
"""
|
|
55
71
|
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"fuzzy": 0.1,
|
|
60
|
-
"vector": 0.6,
|
|
61
|
-
}
|
|
72
|
+
# NOTE: DEFAULT_WEIGHTS imported from ranking.py - single source of truth
|
|
73
|
+
# Default RRF weights: SPLADE-based hybrid (splade: 0.4, vector: 0.6)
|
|
74
|
+
# FTS fallback mode uses FTS_FALLBACK_WEIGHTS (exact: 0.3, fuzzy: 0.1, vector: 0.6)
|
|
62
75
|
|
|
63
76
|
def __init__(
|
|
64
77
|
self,
|
|
@@ -72,11 +85,24 @@ class HybridSearchEngine:
|
|
|
72
85
|
weights: Optional custom RRF weights (default: DEFAULT_WEIGHTS)
|
|
73
86
|
config: Optional runtime config (enables optional reranking features)
|
|
74
87
|
embedder: Optional embedder instance for embedding-based reranking
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
TypeError: If weights is not a dict (e.g., if a Path is passed)
|
|
75
91
|
"""
|
|
76
92
|
self.logger = logging.getLogger(__name__)
|
|
77
|
-
|
|
93
|
+
|
|
94
|
+
# Validate weights type to catch common usage errors
|
|
95
|
+
if weights is not None and not isinstance(weights, dict):
|
|
96
|
+
raise TypeError(
|
|
97
|
+
f"weights must be a dict, got {type(weights).__name__}. "
|
|
98
|
+
f"Did you mean to pass index_path to search() instead of __init__()?"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
self.weights = weights or DEFAULT_WEIGHTS.copy()
|
|
78
102
|
self._config = config
|
|
79
103
|
self.embedder = embedder
|
|
104
|
+
self.reranker: Any = None
|
|
105
|
+
self._use_gpu = config.embedding_use_gpu if config else True
|
|
80
106
|
|
|
81
107
|
def search(
|
|
82
108
|
self,
|
|
@@ -86,6 +112,7 @@ class HybridSearchEngine:
|
|
|
86
112
|
enable_fuzzy: bool = True,
|
|
87
113
|
enable_vector: bool = False,
|
|
88
114
|
pure_vector: bool = False,
|
|
115
|
+
enable_splade: bool = False,
|
|
89
116
|
) -> List[SearchResult]:
|
|
90
117
|
"""Execute hybrid search with parallel retrieval and RRF fusion.
|
|
91
118
|
|
|
@@ -96,6 +123,7 @@ class HybridSearchEngine:
|
|
|
96
123
|
enable_fuzzy: Enable fuzzy FTS search (default True)
|
|
97
124
|
enable_vector: Enable vector search (default False)
|
|
98
125
|
pure_vector: If True, only use vector search without FTS fallback (default False)
|
|
126
|
+
enable_splade: If True, force SPLADE sparse neural search (default False)
|
|
99
127
|
|
|
100
128
|
Returns:
|
|
101
129
|
List of SearchResult objects sorted by fusion score
|
|
@@ -109,12 +137,53 @@ class HybridSearchEngine:
|
|
|
109
137
|
>>> results = engine.search(Path("project/_index.db"),
|
|
110
138
|
... "how to authenticate users",
|
|
111
139
|
... enable_vector=True, pure_vector=True)
|
|
140
|
+
>>> # SPLADE sparse neural search
|
|
141
|
+
>>> results = engine.search(Path("project/_index.db"), "auth flow",
|
|
142
|
+
... enable_splade=True, enable_vector=True)
|
|
112
143
|
>>> for r in results[:5]:
|
|
113
144
|
... print(f"{r.path}: {r.score:.3f}")
|
|
114
145
|
"""
|
|
146
|
+
# Defensive: avoid creating/locking an index database when callers pass
|
|
147
|
+
# an empty placeholder file (common in tests and misconfigured callers).
|
|
148
|
+
try:
|
|
149
|
+
if index_path.exists() and index_path.stat().st_size == 0:
|
|
150
|
+
return []
|
|
151
|
+
except OSError:
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
# Detect query intent early for category filtering at index level
|
|
155
|
+
query_intent = detect_query_intent(query)
|
|
156
|
+
# Map intent to category for vector search:
|
|
157
|
+
# - KEYWORD (code intent) -> filter to 'code' only
|
|
158
|
+
# - SEMANTIC (doc intent) -> no filter (allow docs to surface)
|
|
159
|
+
# - MIXED -> no filter (allow all)
|
|
160
|
+
vector_category: Optional[str] = None
|
|
161
|
+
if query_intent == QueryIntent.KEYWORD:
|
|
162
|
+
vector_category = "code"
|
|
163
|
+
|
|
115
164
|
# Determine which backends to use
|
|
116
165
|
backends = {}
|
|
117
166
|
|
|
167
|
+
# Check if SPLADE is available
|
|
168
|
+
splade_available = False
|
|
169
|
+
# Respect config.enable_splade flag and use_fts_fallback flag
|
|
170
|
+
if self._config and getattr(self._config, 'use_fts_fallback', False):
|
|
171
|
+
# Config explicitly requests FTS fallback - disable SPLADE
|
|
172
|
+
splade_available = False
|
|
173
|
+
elif self._config and not getattr(self._config, 'enable_splade', True):
|
|
174
|
+
# Config explicitly disabled SPLADE
|
|
175
|
+
splade_available = False
|
|
176
|
+
else:
|
|
177
|
+
# Check if SPLADE dependencies are available
|
|
178
|
+
try:
|
|
179
|
+
from codexlens.semantic.splade_encoder import check_splade_available
|
|
180
|
+
ok, _ = check_splade_available()
|
|
181
|
+
if ok:
|
|
182
|
+
# SPLADE tables are in main index database, will check table existence in _search_splade
|
|
183
|
+
splade_available = True
|
|
184
|
+
except Exception:
|
|
185
|
+
pass
|
|
186
|
+
|
|
118
187
|
if pure_vector:
|
|
119
188
|
# Pure vector mode: only use vector search, no FTS fallback
|
|
120
189
|
if enable_vector:
|
|
@@ -127,17 +196,41 @@ class HybridSearchEngine:
|
|
|
127
196
|
"To use pure vector search, enable vector search mode."
|
|
128
197
|
)
|
|
129
198
|
backends["exact"] = True
|
|
199
|
+
elif enable_splade:
|
|
200
|
+
# Explicit SPLADE mode requested via CLI --method splade
|
|
201
|
+
if splade_available:
|
|
202
|
+
backends["splade"] = True
|
|
203
|
+
if enable_vector:
|
|
204
|
+
backends["vector"] = True
|
|
205
|
+
else:
|
|
206
|
+
# SPLADE requested but not available - warn and fallback
|
|
207
|
+
self.logger.warning(
|
|
208
|
+
"SPLADE search requested but not available. "
|
|
209
|
+
"Falling back to FTS. Run 'codexlens index splade' to enable."
|
|
210
|
+
)
|
|
211
|
+
backends["exact"] = True
|
|
212
|
+
if enable_fuzzy:
|
|
213
|
+
backends["fuzzy"] = True
|
|
214
|
+
if enable_vector:
|
|
215
|
+
backends["vector"] = True
|
|
130
216
|
else:
|
|
131
|
-
# Hybrid mode:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
backends["
|
|
135
|
-
|
|
136
|
-
|
|
217
|
+
# Hybrid mode: default to SPLADE if available, otherwise use FTS
|
|
218
|
+
if splade_available:
|
|
219
|
+
# Default: enable SPLADE, disable exact and fuzzy
|
|
220
|
+
backends["splade"] = True
|
|
221
|
+
if enable_vector:
|
|
222
|
+
backends["vector"] = True
|
|
223
|
+
else:
|
|
224
|
+
# Fallback mode: enable exact+fuzzy when SPLADE unavailable
|
|
225
|
+
backends["exact"] = True
|
|
226
|
+
if enable_fuzzy:
|
|
227
|
+
backends["fuzzy"] = True
|
|
228
|
+
if enable_vector:
|
|
229
|
+
backends["vector"] = True
|
|
137
230
|
|
|
138
231
|
# Execute parallel searches
|
|
139
232
|
with timer("parallel_search_total", self.logger):
|
|
140
|
-
results_map = self._search_parallel(index_path, query, backends, limit)
|
|
233
|
+
results_map = self._search_parallel(index_path, query, backends, limit, vector_category)
|
|
141
234
|
|
|
142
235
|
# Provide helpful message if pure-vector mode returns no results
|
|
143
236
|
if pure_vector and enable_vector and len(results_map.get("vector", [])) == 0:
|
|
@@ -156,9 +249,22 @@ class HybridSearchEngine:
|
|
|
156
249
|
if source in results_map
|
|
157
250
|
}
|
|
158
251
|
|
|
159
|
-
|
|
252
|
+
# Determine fusion method from config (default: rrf)
|
|
253
|
+
fusion_method = "rrf"
|
|
254
|
+
rrf_k = 60
|
|
255
|
+
if self._config is not None:
|
|
256
|
+
fusion_method = getattr(self._config, "fusion_method", "rrf") or "rrf"
|
|
257
|
+
rrf_k = getattr(self._config, "rrf_k", 60) or 60
|
|
258
|
+
|
|
259
|
+
with timer("fusion", self.logger):
|
|
160
260
|
adaptive_weights = get_rrf_weights(query, active_weights)
|
|
161
|
-
|
|
261
|
+
if fusion_method == "simple":
|
|
262
|
+
fused_results = simple_weighted_fusion(results_map, adaptive_weights)
|
|
263
|
+
else:
|
|
264
|
+
# Default to RRF
|
|
265
|
+
fused_results = reciprocal_rank_fusion(
|
|
266
|
+
results_map, adaptive_weights, k=rrf_k
|
|
267
|
+
)
|
|
162
268
|
|
|
163
269
|
# Optional: boost results that include explicit symbol matches
|
|
164
270
|
boost_factor = (
|
|
@@ -180,7 +286,41 @@ class HybridSearchEngine:
|
|
|
180
286
|
query,
|
|
181
287
|
fused_results[:100],
|
|
182
288
|
self.embedder,
|
|
183
|
-
top_k=
|
|
289
|
+
top_k=(
|
|
290
|
+
100
|
|
291
|
+
if self._config.enable_cross_encoder_rerank
|
|
292
|
+
else self._config.reranking_top_k
|
|
293
|
+
),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Optional: cross-encoder reranking as a second stage
|
|
297
|
+
if (
|
|
298
|
+
self._config is not None
|
|
299
|
+
and self._config.enable_reranking
|
|
300
|
+
and self._config.enable_cross_encoder_rerank
|
|
301
|
+
):
|
|
302
|
+
with timer("cross_encoder_rerank", self.logger):
|
|
303
|
+
if self.reranker is None:
|
|
304
|
+
self.reranker = self._get_cross_encoder_reranker()
|
|
305
|
+
if self.reranker is not None:
|
|
306
|
+
fused_results = cross_encoder_rerank(
|
|
307
|
+
query,
|
|
308
|
+
fused_results,
|
|
309
|
+
self.reranker,
|
|
310
|
+
top_k=self._config.reranker_top_k,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# Apply category filtering to avoid code/doc pollution
|
|
314
|
+
# This ensures KEYWORD queries return code files, SEMANTIC queries prefer docs
|
|
315
|
+
enable_category_filter = (
|
|
316
|
+
self._config is None
|
|
317
|
+
or getattr(self._config, 'enable_category_filter', True)
|
|
318
|
+
)
|
|
319
|
+
if enable_category_filter and not pure_vector:
|
|
320
|
+
with timer("category_filter", self.logger):
|
|
321
|
+
query_intent = detect_query_intent(query)
|
|
322
|
+
fused_results = filter_results_by_category(
|
|
323
|
+
fused_results, query_intent, allow_mixed=True
|
|
184
324
|
)
|
|
185
325
|
|
|
186
326
|
# Apply final limit
|
|
@@ -222,12 +362,71 @@ class HybridSearchEngine:
|
|
|
222
362
|
)
|
|
223
363
|
return None
|
|
224
364
|
|
|
365
|
+
def _get_cross_encoder_reranker(self) -> Any:
|
|
366
|
+
if self._config is None:
|
|
367
|
+
return None
|
|
368
|
+
|
|
369
|
+
try:
|
|
370
|
+
from codexlens.semantic.reranker import (
|
|
371
|
+
check_reranker_available,
|
|
372
|
+
get_reranker,
|
|
373
|
+
)
|
|
374
|
+
except Exception as exc:
|
|
375
|
+
self.logger.debug("Reranker factory unavailable: %s", exc)
|
|
376
|
+
return None
|
|
377
|
+
|
|
378
|
+
backend = (getattr(self._config, "reranker_backend", "") or "").strip().lower() or "onnx"
|
|
379
|
+
|
|
380
|
+
ok, err = check_reranker_available(backend)
|
|
381
|
+
if not ok:
|
|
382
|
+
self.logger.debug(
|
|
383
|
+
"Reranker backend unavailable (backend=%s): %s",
|
|
384
|
+
backend,
|
|
385
|
+
err,
|
|
386
|
+
)
|
|
387
|
+
return None
|
|
388
|
+
|
|
389
|
+
try:
|
|
390
|
+
model_name = (getattr(self._config, "reranker_model", "") or "").strip() or None
|
|
391
|
+
|
|
392
|
+
if backend != "legacy" and model_name == "cross-encoder/ms-marco-MiniLM-L-6-v2":
|
|
393
|
+
model_name = None
|
|
394
|
+
|
|
395
|
+
device: str | None = None
|
|
396
|
+
kwargs: dict[str, Any] = {}
|
|
397
|
+
|
|
398
|
+
if backend == "onnx":
|
|
399
|
+
kwargs["use_gpu"] = bool(getattr(self._config, "embedding_use_gpu", True))
|
|
400
|
+
elif backend == "legacy":
|
|
401
|
+
if not bool(getattr(self._config, "embedding_use_gpu", True)):
|
|
402
|
+
device = "cpu"
|
|
403
|
+
elif backend == "api":
|
|
404
|
+
# Pass max_input_tokens for adaptive batching
|
|
405
|
+
max_tokens = getattr(self._config, "reranker_max_input_tokens", None)
|
|
406
|
+
if max_tokens:
|
|
407
|
+
kwargs["max_input_tokens"] = max_tokens
|
|
408
|
+
|
|
409
|
+
return get_reranker(
|
|
410
|
+
backend=backend,
|
|
411
|
+
model_name=model_name,
|
|
412
|
+
device=device,
|
|
413
|
+
**kwargs,
|
|
414
|
+
)
|
|
415
|
+
except Exception as exc:
|
|
416
|
+
self.logger.debug(
|
|
417
|
+
"Failed to initialize reranker (backend=%s): %s",
|
|
418
|
+
backend,
|
|
419
|
+
exc,
|
|
420
|
+
)
|
|
421
|
+
return None
|
|
422
|
+
|
|
225
423
|
def _search_parallel(
|
|
226
424
|
self,
|
|
227
425
|
index_path: Path,
|
|
228
426
|
query: str,
|
|
229
427
|
backends: Dict[str, bool],
|
|
230
428
|
limit: int,
|
|
429
|
+
category: Optional[str] = None,
|
|
231
430
|
) -> Dict[str, List[SearchResult]]:
|
|
232
431
|
"""Execute parallel searches across enabled backends.
|
|
233
432
|
|
|
@@ -236,6 +435,7 @@ class HybridSearchEngine:
|
|
|
236
435
|
query: FTS5 query string
|
|
237
436
|
backends: Dictionary of backend name to enabled flag
|
|
238
437
|
limit: Results limit per backend
|
|
438
|
+
category: Optional category filter for vector search ('code' or 'doc')
|
|
239
439
|
|
|
240
440
|
Returns:
|
|
241
441
|
Dictionary mapping source name to results list
|
|
@@ -266,27 +466,44 @@ class HybridSearchEngine:
|
|
|
266
466
|
if backends.get("vector"):
|
|
267
467
|
submit_times["vector"] = time.perf_counter()
|
|
268
468
|
future = executor.submit(
|
|
269
|
-
self._search_vector, index_path, query, limit
|
|
469
|
+
self._search_vector, index_path, query, limit, category
|
|
270
470
|
)
|
|
271
471
|
future_to_source[future] = "vector"
|
|
272
472
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
473
|
+
if backends.get("splade"):
|
|
474
|
+
submit_times["splade"] = time.perf_counter()
|
|
475
|
+
future = executor.submit(
|
|
476
|
+
self._search_splade, index_path, query, limit
|
|
477
|
+
)
|
|
478
|
+
future_to_source[future] = "splade"
|
|
479
|
+
|
|
480
|
+
# Collect results as they complete with timeout protection
|
|
481
|
+
try:
|
|
482
|
+
for future in as_completed(future_to_source, timeout=30.0):
|
|
483
|
+
source = future_to_source[future]
|
|
484
|
+
elapsed_ms = (time.perf_counter() - submit_times[source]) * 1000
|
|
485
|
+
timing_data[source] = elapsed_ms
|
|
486
|
+
try:
|
|
487
|
+
results = future.result(timeout=10.0)
|
|
488
|
+
# Tag results with source for debugging
|
|
489
|
+
tagged_results = tag_search_source(results, source)
|
|
490
|
+
results_map[source] = tagged_results
|
|
491
|
+
self.logger.debug(
|
|
492
|
+
"[TIMING] %s_search: %.2fms (%d results)",
|
|
493
|
+
source, elapsed_ms, len(results)
|
|
494
|
+
)
|
|
495
|
+
except (Exception, FuturesTimeoutError) as exc:
|
|
496
|
+
self.logger.error("Search failed for %s: %s", source, exc)
|
|
497
|
+
results_map[source] = []
|
|
498
|
+
except FuturesTimeoutError:
|
|
499
|
+
self.logger.warning("Search timeout: some backends did not respond in time")
|
|
500
|
+
# Cancel remaining futures
|
|
501
|
+
for future in future_to_source:
|
|
502
|
+
future.cancel()
|
|
503
|
+
# Set empty results for sources that didn't complete
|
|
504
|
+
for source in backends:
|
|
505
|
+
if source not in results_map:
|
|
506
|
+
results_map[source] = []
|
|
290
507
|
|
|
291
508
|
# Log timing summary
|
|
292
509
|
if timing_data:
|
|
@@ -339,20 +556,419 @@ class HybridSearchEngine:
|
|
|
339
556
|
self.logger.debug("Fuzzy search error: %s", exc)
|
|
340
557
|
return []
|
|
341
558
|
|
|
559
|
+
def _find_vectors_hnsw(self, index_path: Path) -> Optional[Path]:
|
|
560
|
+
"""Find the centralized _vectors.hnsw file by traversing up from index_path.
|
|
561
|
+
|
|
562
|
+
Similar to _search_splade's approach, this method searches for the
|
|
563
|
+
centralized dense vector index file in parent directories.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
index_path: Path to the current _index.db file
|
|
567
|
+
|
|
568
|
+
Returns:
|
|
569
|
+
Path to _vectors.hnsw if found, None otherwise
|
|
570
|
+
"""
|
|
571
|
+
current_dir = index_path.parent
|
|
572
|
+
for _ in range(10): # Limit search depth
|
|
573
|
+
candidate = current_dir / VECTORS_HNSW_NAME
|
|
574
|
+
if candidate.exists():
|
|
575
|
+
return candidate
|
|
576
|
+
parent = current_dir.parent
|
|
577
|
+
if parent == current_dir: # Reached root
|
|
578
|
+
break
|
|
579
|
+
current_dir = parent
|
|
580
|
+
return None
|
|
581
|
+
|
|
582
|
+
def _search_vector_centralized(
|
|
583
|
+
self,
|
|
584
|
+
index_path: Path,
|
|
585
|
+
hnsw_path: Path,
|
|
586
|
+
query: str,
|
|
587
|
+
limit: int,
|
|
588
|
+
category: Optional[str] = None,
|
|
589
|
+
) -> List[SearchResult]:
|
|
590
|
+
"""Search using centralized vector index.
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
index_path: Path to _index.db file (for metadata lookup)
|
|
594
|
+
hnsw_path: Path to centralized _vectors.hnsw file
|
|
595
|
+
query: Natural language query string
|
|
596
|
+
limit: Maximum results
|
|
597
|
+
category: Optional category filter ('code' or 'doc')
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
List of SearchResult objects ordered by semantic similarity
|
|
601
|
+
"""
|
|
602
|
+
try:
|
|
603
|
+
import sqlite3
|
|
604
|
+
import json
|
|
605
|
+
from codexlens.semantic.factory import get_embedder
|
|
606
|
+
from codexlens.semantic.ann_index import ANNIndex
|
|
607
|
+
|
|
608
|
+
# Get model config from the first index database we can find
|
|
609
|
+
# (all indexes should use the same embedding model)
|
|
610
|
+
index_root = hnsw_path.parent
|
|
611
|
+
model_config = None
|
|
612
|
+
|
|
613
|
+
# Try to get model config from the centralized index root first
|
|
614
|
+
# (not the sub-directory index_path, which may have outdated config)
|
|
615
|
+
try:
|
|
616
|
+
from codexlens.semantic.vector_store import VectorStore
|
|
617
|
+
central_index_path = index_root / "_index.db"
|
|
618
|
+
if central_index_path.exists():
|
|
619
|
+
with VectorStore(central_index_path) as vs:
|
|
620
|
+
model_config = vs.get_model_config()
|
|
621
|
+
self.logger.debug(
|
|
622
|
+
"Loaded model config from centralized index: %s",
|
|
623
|
+
model_config
|
|
624
|
+
)
|
|
625
|
+
except Exception as e:
|
|
626
|
+
self.logger.debug("Failed to load model config from centralized index: %s", e)
|
|
627
|
+
|
|
628
|
+
# Detect dimension from HNSW file if model config not found
|
|
629
|
+
if model_config is None:
|
|
630
|
+
self.logger.debug("Model config not found, will detect from HNSW index")
|
|
631
|
+
# Create a temporary ANNIndex to load and detect dimension
|
|
632
|
+
# We need to know the dimension to properly load the index
|
|
633
|
+
|
|
634
|
+
# Get embedder based on model config or default
|
|
635
|
+
if model_config:
|
|
636
|
+
backend = model_config.get("backend", "fastembed")
|
|
637
|
+
model_name = model_config["model_name"]
|
|
638
|
+
model_profile = model_config["model_profile"]
|
|
639
|
+
embedding_dim = model_config["embedding_dim"]
|
|
640
|
+
|
|
641
|
+
if backend == "litellm":
|
|
642
|
+
embedder = get_embedder(backend="litellm", model=model_name)
|
|
643
|
+
else:
|
|
644
|
+
embedder = get_embedder(backend="fastembed", profile=model_profile)
|
|
645
|
+
else:
|
|
646
|
+
# Default to code profile
|
|
647
|
+
embedder = get_embedder(backend="fastembed", profile="code")
|
|
648
|
+
embedding_dim = embedder.embedding_dim
|
|
649
|
+
|
|
650
|
+
# Load centralized ANN index
|
|
651
|
+
start_load = time.perf_counter()
|
|
652
|
+
ann_index = ANNIndex.create_central(
|
|
653
|
+
index_root=index_root,
|
|
654
|
+
dim=embedding_dim,
|
|
655
|
+
)
|
|
656
|
+
if not ann_index.load():
|
|
657
|
+
self.logger.warning("Failed to load centralized vector index from %s", hnsw_path)
|
|
658
|
+
return []
|
|
659
|
+
self.logger.debug(
|
|
660
|
+
"[TIMING] central_ann_load: %.2fms (%d vectors)",
|
|
661
|
+
(time.perf_counter() - start_load) * 1000,
|
|
662
|
+
ann_index.count()
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
# Generate query embedding
|
|
666
|
+
start_embed = time.perf_counter()
|
|
667
|
+
query_embedding = embedder.embed_single(query)
|
|
668
|
+
self.logger.debug(
|
|
669
|
+
"[TIMING] query_embedding: %.2fms",
|
|
670
|
+
(time.perf_counter() - start_embed) * 1000
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
# Search ANN index
|
|
674
|
+
start_search = time.perf_counter()
|
|
675
|
+
import numpy as np
|
|
676
|
+
query_vec = np.array(query_embedding, dtype=np.float32)
|
|
677
|
+
ids, distances = ann_index.search(query_vec, top_k=limit * 2) # Fetch extra for filtering
|
|
678
|
+
self.logger.debug(
|
|
679
|
+
"[TIMING] central_ann_search: %.2fms (%d results)",
|
|
680
|
+
(time.perf_counter() - start_search) * 1000,
|
|
681
|
+
len(ids) if ids else 0
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
if not ids:
|
|
685
|
+
return []
|
|
686
|
+
|
|
687
|
+
# Convert distances to similarity scores (for cosine: score = 1 - distance)
|
|
688
|
+
scores = [1.0 - d for d in distances]
|
|
689
|
+
|
|
690
|
+
# Fetch chunk metadata from semantic_chunks tables
|
|
691
|
+
# We need to search across all _index.db files in the project
|
|
692
|
+
results = self._fetch_chunks_by_ids_centralized(
|
|
693
|
+
index_root, ids, scores, category
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
return results[:limit]
|
|
697
|
+
|
|
698
|
+
except ImportError as exc:
|
|
699
|
+
self.logger.debug("Semantic dependencies not available: %s", exc)
|
|
700
|
+
return []
|
|
701
|
+
except Exception as exc:
|
|
702
|
+
self.logger.error("Centralized vector search error: %s", exc)
|
|
703
|
+
return []
|
|
704
|
+
|
|
705
|
+
def _fetch_chunks_by_ids_centralized(
|
|
706
|
+
self,
|
|
707
|
+
index_root: Path,
|
|
708
|
+
chunk_ids: List[int],
|
|
709
|
+
scores: List[float],
|
|
710
|
+
category: Optional[str] = None,
|
|
711
|
+
) -> List[SearchResult]:
|
|
712
|
+
"""Fetch chunk metadata from centralized _vectors_meta.db for fast lookup.
|
|
713
|
+
|
|
714
|
+
This method uses the centralized VectorMetadataStore for O(1) lookup
|
|
715
|
+
instead of traversing all _index.db files (O(n) where n = number of indexes).
|
|
716
|
+
|
|
717
|
+
Falls back to the legacy per-index lookup if centralized metadata is unavailable.
|
|
718
|
+
|
|
719
|
+
Args:
|
|
720
|
+
index_root: Root directory containing _vectors_meta.db
|
|
721
|
+
chunk_ids: List of chunk IDs from ANN search
|
|
722
|
+
scores: Corresponding similarity scores
|
|
723
|
+
category: Optional category filter
|
|
724
|
+
|
|
725
|
+
Returns:
|
|
726
|
+
List of SearchResult objects
|
|
727
|
+
"""
|
|
728
|
+
from codexlens.config import VECTORS_META_DB_NAME
|
|
729
|
+
|
|
730
|
+
# Build score map
|
|
731
|
+
score_map = {cid: score for cid, score in zip(chunk_ids, scores)}
|
|
732
|
+
|
|
733
|
+
# Try centralized metadata store first (fast path)
|
|
734
|
+
vectors_meta_path = index_root / VECTORS_META_DB_NAME
|
|
735
|
+
if vectors_meta_path.exists():
|
|
736
|
+
try:
|
|
737
|
+
return self._fetch_from_vector_meta_store(
|
|
738
|
+
vectors_meta_path, chunk_ids, score_map, category
|
|
739
|
+
)
|
|
740
|
+
except Exception as e:
|
|
741
|
+
self.logger.warning(
|
|
742
|
+
"Centralized metadata lookup failed, falling back to legacy traversal: %s. "
|
|
743
|
+
"Consider regenerating embeddings with: codexlens embeddings-generate --centralized",
|
|
744
|
+
e
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
# Fallback: traverse _index.db files (legacy path)
|
|
748
|
+
return self._fetch_chunks_by_ids_legacy(
|
|
749
|
+
index_root, chunk_ids, score_map, category
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
def _fetch_from_vector_meta_store(
|
|
753
|
+
self,
|
|
754
|
+
meta_db_path: Path,
|
|
755
|
+
chunk_ids: List[int],
|
|
756
|
+
score_map: Dict[int, float],
|
|
757
|
+
category: Optional[str] = None,
|
|
758
|
+
) -> List[SearchResult]:
|
|
759
|
+
"""Fetch chunks from centralized VectorMetadataStore.
|
|
760
|
+
|
|
761
|
+
Args:
|
|
762
|
+
meta_db_path: Path to _vectors_meta.db
|
|
763
|
+
chunk_ids: List of chunk IDs to fetch
|
|
764
|
+
score_map: Mapping of chunk_id to score
|
|
765
|
+
category: Optional category filter
|
|
766
|
+
|
|
767
|
+
Returns:
|
|
768
|
+
List of SearchResult objects
|
|
769
|
+
"""
|
|
770
|
+
from codexlens.storage.vector_meta_store import VectorMetadataStore
|
|
771
|
+
|
|
772
|
+
results = []
|
|
773
|
+
|
|
774
|
+
with VectorMetadataStore(meta_db_path) as meta_store:
|
|
775
|
+
rows = meta_store.get_chunks_by_ids(chunk_ids, category=category)
|
|
776
|
+
|
|
777
|
+
for row in rows:
|
|
778
|
+
chunk_id = row["chunk_id"]
|
|
779
|
+
file_path = row["file_path"]
|
|
780
|
+
content = row["content"] or ""
|
|
781
|
+
metadata = row.get("metadata") or {}
|
|
782
|
+
start_line = row.get("start_line")
|
|
783
|
+
end_line = row.get("end_line")
|
|
784
|
+
|
|
785
|
+
score = score_map.get(chunk_id, 0.0)
|
|
786
|
+
|
|
787
|
+
# Build excerpt
|
|
788
|
+
excerpt = content[:200] + "..." if len(content) > 200 else content
|
|
789
|
+
|
|
790
|
+
# Extract symbol information
|
|
791
|
+
symbol_name = metadata.get("symbol_name")
|
|
792
|
+
symbol_kind = metadata.get("symbol_kind")
|
|
793
|
+
|
|
794
|
+
# Build Symbol object if available
|
|
795
|
+
symbol = None
|
|
796
|
+
if symbol_name and symbol_kind and start_line and end_line:
|
|
797
|
+
try:
|
|
798
|
+
from codexlens.entities import Symbol
|
|
799
|
+
symbol = Symbol(
|
|
800
|
+
name=symbol_name,
|
|
801
|
+
kind=symbol_kind,
|
|
802
|
+
range=(start_line, end_line)
|
|
803
|
+
)
|
|
804
|
+
except Exception:
|
|
805
|
+
pass
|
|
806
|
+
|
|
807
|
+
results.append(SearchResult(
|
|
808
|
+
path=file_path,
|
|
809
|
+
score=score,
|
|
810
|
+
excerpt=excerpt,
|
|
811
|
+
content=content,
|
|
812
|
+
symbol=symbol,
|
|
813
|
+
metadata=metadata,
|
|
814
|
+
start_line=start_line,
|
|
815
|
+
end_line=end_line,
|
|
816
|
+
symbol_name=symbol_name,
|
|
817
|
+
symbol_kind=symbol_kind,
|
|
818
|
+
))
|
|
819
|
+
|
|
820
|
+
# Sort by score descending
|
|
821
|
+
results.sort(key=lambda r: r.score, reverse=True)
|
|
822
|
+
return results
|
|
823
|
+
|
|
824
|
+
def _fetch_chunks_by_ids_legacy(
|
|
825
|
+
self,
|
|
826
|
+
index_root: Path,
|
|
827
|
+
chunk_ids: List[int],
|
|
828
|
+
score_map: Dict[int, float],
|
|
829
|
+
category: Optional[str] = None,
|
|
830
|
+
) -> List[SearchResult]:
|
|
831
|
+
"""Legacy fallback: fetch chunk metadata by traversing all _index.db files.
|
|
832
|
+
|
|
833
|
+
This is the O(n) fallback path used when centralized metadata is unavailable.
|
|
834
|
+
|
|
835
|
+
Args:
|
|
836
|
+
index_root: Root directory containing _index.db files
|
|
837
|
+
chunk_ids: List of chunk IDs from ANN search
|
|
838
|
+
score_map: Mapping of chunk_id to score
|
|
839
|
+
category: Optional category filter
|
|
840
|
+
|
|
841
|
+
Returns:
|
|
842
|
+
List of SearchResult objects
|
|
843
|
+
"""
|
|
844
|
+
import sqlite3
|
|
845
|
+
import json
|
|
846
|
+
|
|
847
|
+
# Find all _index.db files
|
|
848
|
+
index_files = list(index_root.rglob("_index.db"))
|
|
849
|
+
|
|
850
|
+
results = []
|
|
851
|
+
found_ids = set()
|
|
852
|
+
|
|
853
|
+
for index_path in index_files:
|
|
854
|
+
try:
|
|
855
|
+
with sqlite3.connect(index_path) as conn:
|
|
856
|
+
conn.row_factory = sqlite3.Row
|
|
857
|
+
|
|
858
|
+
# Check if semantic_chunks table exists
|
|
859
|
+
cursor = conn.execute(
|
|
860
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='semantic_chunks'"
|
|
861
|
+
)
|
|
862
|
+
if cursor.fetchone() is None:
|
|
863
|
+
continue
|
|
864
|
+
|
|
865
|
+
# Build query for chunk IDs we haven't found yet
|
|
866
|
+
remaining_ids = [cid for cid in chunk_ids if cid not in found_ids]
|
|
867
|
+
if not remaining_ids:
|
|
868
|
+
break
|
|
869
|
+
|
|
870
|
+
placeholders = ",".join("?" * len(remaining_ids))
|
|
871
|
+
|
|
872
|
+
if category:
|
|
873
|
+
query = f"""
|
|
874
|
+
SELECT id, file_path, content, metadata
|
|
875
|
+
FROM semantic_chunks
|
|
876
|
+
WHERE id IN ({placeholders}) AND category = ?
|
|
877
|
+
"""
|
|
878
|
+
params = remaining_ids + [category]
|
|
879
|
+
else:
|
|
880
|
+
query = f"""
|
|
881
|
+
SELECT id, file_path, content, metadata
|
|
882
|
+
FROM semantic_chunks
|
|
883
|
+
WHERE id IN ({placeholders})
|
|
884
|
+
"""
|
|
885
|
+
params = remaining_ids
|
|
886
|
+
|
|
887
|
+
rows = conn.execute(query, params).fetchall()
|
|
888
|
+
|
|
889
|
+
for row in rows:
|
|
890
|
+
chunk_id = row["id"]
|
|
891
|
+
if chunk_id in found_ids:
|
|
892
|
+
continue
|
|
893
|
+
found_ids.add(chunk_id)
|
|
894
|
+
|
|
895
|
+
file_path = row["file_path"]
|
|
896
|
+
content = row["content"]
|
|
897
|
+
metadata_json = row["metadata"]
|
|
898
|
+
metadata = json.loads(metadata_json) if metadata_json else {}
|
|
899
|
+
|
|
900
|
+
score = score_map.get(chunk_id, 0.0)
|
|
901
|
+
|
|
902
|
+
# Build excerpt
|
|
903
|
+
excerpt = content[:200] + "..." if len(content) > 200 else content
|
|
904
|
+
|
|
905
|
+
# Extract symbol information
|
|
906
|
+
symbol_name = metadata.get("symbol_name")
|
|
907
|
+
symbol_kind = metadata.get("symbol_kind")
|
|
908
|
+
start_line = metadata.get("start_line")
|
|
909
|
+
end_line = metadata.get("end_line")
|
|
910
|
+
|
|
911
|
+
# Build Symbol object if available
|
|
912
|
+
symbol = None
|
|
913
|
+
if symbol_name and symbol_kind and start_line and end_line:
|
|
914
|
+
try:
|
|
915
|
+
from codexlens.entities import Symbol
|
|
916
|
+
symbol = Symbol(
|
|
917
|
+
name=symbol_name,
|
|
918
|
+
kind=symbol_kind,
|
|
919
|
+
range=(start_line, end_line)
|
|
920
|
+
)
|
|
921
|
+
except Exception:
|
|
922
|
+
pass
|
|
923
|
+
|
|
924
|
+
results.append(SearchResult(
|
|
925
|
+
path=file_path,
|
|
926
|
+
score=score,
|
|
927
|
+
excerpt=excerpt,
|
|
928
|
+
content=content,
|
|
929
|
+
symbol=symbol,
|
|
930
|
+
metadata=metadata,
|
|
931
|
+
start_line=start_line,
|
|
932
|
+
end_line=end_line,
|
|
933
|
+
symbol_name=symbol_name,
|
|
934
|
+
symbol_kind=symbol_kind,
|
|
935
|
+
))
|
|
936
|
+
|
|
937
|
+
except Exception as e:
|
|
938
|
+
self.logger.debug("Failed to fetch chunks from %s: %s", index_path, e)
|
|
939
|
+
continue
|
|
940
|
+
|
|
941
|
+
# Sort by score descending
|
|
942
|
+
results.sort(key=lambda r: r.score, reverse=True)
|
|
943
|
+
return results
|
|
944
|
+
|
|
342
945
|
def _search_vector(
|
|
343
|
-
self, index_path: Path, query: str, limit: int
|
|
946
|
+
self, index_path: Path, query: str, limit: int, category: Optional[str] = None
|
|
344
947
|
) -> List[SearchResult]:
|
|
345
948
|
"""Execute vector similarity search using semantic embeddings.
|
|
346
949
|
|
|
950
|
+
Supports both centralized vector storage (single _vectors.hnsw at project root)
|
|
951
|
+
and distributed storage (per-directory .hnsw files).
|
|
952
|
+
|
|
347
953
|
Args:
|
|
348
954
|
index_path: Path to _index.db file
|
|
349
955
|
query: Natural language query string
|
|
350
956
|
limit: Maximum results
|
|
957
|
+
category: Optional category filter ('code' or 'doc')
|
|
351
958
|
|
|
352
959
|
Returns:
|
|
353
960
|
List of SearchResult objects ordered by semantic similarity
|
|
354
961
|
"""
|
|
355
962
|
try:
|
|
963
|
+
# First, check for centralized vector index
|
|
964
|
+
central_hnsw_path = self._find_vectors_hnsw(index_path)
|
|
965
|
+
if central_hnsw_path is not None:
|
|
966
|
+
self.logger.debug("Found centralized vector index at %s", central_hnsw_path)
|
|
967
|
+
return self._search_vector_centralized(
|
|
968
|
+
index_path, central_hnsw_path, query, limit, category
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
# Fallback to distributed (per-index) vector storage
|
|
356
972
|
# Check if semantic chunks table exists
|
|
357
973
|
import sqlite3
|
|
358
974
|
|
|
@@ -466,6 +1082,7 @@ class HybridSearchEngine:
|
|
|
466
1082
|
top_k=limit,
|
|
467
1083
|
min_score=0.0, # Return all results, let RRF handle filtering
|
|
468
1084
|
return_full_content=True,
|
|
1085
|
+
category=category,
|
|
469
1086
|
)
|
|
470
1087
|
self.logger.debug(
|
|
471
1088
|
"[TIMING] vector_similarity_search: %.2fms (%d results)",
|
|
@@ -480,3 +1097,121 @@ class HybridSearchEngine:
|
|
|
480
1097
|
except Exception as exc:
|
|
481
1098
|
self.logger.error("Vector search error: %s", exc)
|
|
482
1099
|
return []
|
|
1100
|
+
|
|
1101
|
+
def _search_splade(
|
|
1102
|
+
self, index_path: Path, query: str, limit: int
|
|
1103
|
+
) -> List[SearchResult]:
|
|
1104
|
+
"""SPLADE sparse retrieval via inverted index.
|
|
1105
|
+
|
|
1106
|
+
Args:
|
|
1107
|
+
index_path: Path to _index.db file
|
|
1108
|
+
query: Natural language query string
|
|
1109
|
+
limit: Maximum results
|
|
1110
|
+
|
|
1111
|
+
Returns:
|
|
1112
|
+
List of SearchResult ordered by SPLADE score
|
|
1113
|
+
"""
|
|
1114
|
+
try:
|
|
1115
|
+
from codexlens.semantic.splade_encoder import get_splade_encoder, check_splade_available
|
|
1116
|
+
from codexlens.storage.splade_index import SpladeIndex
|
|
1117
|
+
from codexlens.config import SPLADE_DB_NAME
|
|
1118
|
+
import sqlite3
|
|
1119
|
+
import json
|
|
1120
|
+
|
|
1121
|
+
# Check dependencies
|
|
1122
|
+
ok, err = check_splade_available()
|
|
1123
|
+
if not ok:
|
|
1124
|
+
self.logger.debug("SPLADE not available: %s", err)
|
|
1125
|
+
return []
|
|
1126
|
+
|
|
1127
|
+
# SPLADE index is stored in _splade.db at the project index root
|
|
1128
|
+
# Traverse up from the current index to find the root _splade.db
|
|
1129
|
+
current_dir = index_path.parent
|
|
1130
|
+
splade_db_path = None
|
|
1131
|
+
for _ in range(10): # Limit search depth
|
|
1132
|
+
candidate = current_dir / SPLADE_DB_NAME
|
|
1133
|
+
if candidate.exists():
|
|
1134
|
+
splade_db_path = candidate
|
|
1135
|
+
break
|
|
1136
|
+
parent = current_dir.parent
|
|
1137
|
+
if parent == current_dir: # Reached root
|
|
1138
|
+
break
|
|
1139
|
+
current_dir = parent
|
|
1140
|
+
|
|
1141
|
+
if not splade_db_path:
|
|
1142
|
+
self.logger.debug("SPLADE index not found in ancestor directories of %s", index_path)
|
|
1143
|
+
return []
|
|
1144
|
+
|
|
1145
|
+
splade_index = SpladeIndex(splade_db_path)
|
|
1146
|
+
if not splade_index.has_index():
|
|
1147
|
+
self.logger.debug("SPLADE index not initialized")
|
|
1148
|
+
return []
|
|
1149
|
+
|
|
1150
|
+
# Encode query to sparse vector
|
|
1151
|
+
encoder = get_splade_encoder(use_gpu=self._use_gpu)
|
|
1152
|
+
query_sparse = encoder.encode_text(query)
|
|
1153
|
+
|
|
1154
|
+
# Search inverted index for top matches
|
|
1155
|
+
raw_results = splade_index.search(query_sparse, limit=limit, min_score=0.0)
|
|
1156
|
+
|
|
1157
|
+
if not raw_results:
|
|
1158
|
+
return []
|
|
1159
|
+
|
|
1160
|
+
# Fetch chunk details from splade_chunks table (self-contained)
|
|
1161
|
+
chunk_ids = [chunk_id for chunk_id, _ in raw_results]
|
|
1162
|
+
score_map = {chunk_id: score for chunk_id, score in raw_results}
|
|
1163
|
+
|
|
1164
|
+
# Get chunk metadata from SPLADE database
|
|
1165
|
+
rows = splade_index.get_chunks_by_ids(chunk_ids)
|
|
1166
|
+
|
|
1167
|
+
# Build SearchResult objects
|
|
1168
|
+
results = []
|
|
1169
|
+
for row in rows:
|
|
1170
|
+
chunk_id = row["id"]
|
|
1171
|
+
file_path = row["file_path"]
|
|
1172
|
+
content = row["content"]
|
|
1173
|
+
metadata_json = row["metadata"]
|
|
1174
|
+
metadata = json.loads(metadata_json) if metadata_json else {}
|
|
1175
|
+
|
|
1176
|
+
score = score_map.get(chunk_id, 0.0)
|
|
1177
|
+
|
|
1178
|
+
# Build excerpt (short preview)
|
|
1179
|
+
excerpt = content[:200] + "..." if len(content) > 200 else content
|
|
1180
|
+
|
|
1181
|
+
# Extract symbol information from metadata
|
|
1182
|
+
symbol_name = metadata.get("symbol_name")
|
|
1183
|
+
symbol_kind = metadata.get("symbol_kind")
|
|
1184
|
+
start_line = metadata.get("start_line")
|
|
1185
|
+
end_line = metadata.get("end_line")
|
|
1186
|
+
|
|
1187
|
+
# Build Symbol object if we have symbol info
|
|
1188
|
+
symbol = None
|
|
1189
|
+
if symbol_name and symbol_kind and start_line and end_line:
|
|
1190
|
+
try:
|
|
1191
|
+
from codexlens.entities import Symbol
|
|
1192
|
+
symbol = Symbol(
|
|
1193
|
+
name=symbol_name,
|
|
1194
|
+
kind=symbol_kind,
|
|
1195
|
+
range=(start_line, end_line)
|
|
1196
|
+
)
|
|
1197
|
+
except Exception:
|
|
1198
|
+
pass
|
|
1199
|
+
|
|
1200
|
+
results.append(SearchResult(
|
|
1201
|
+
path=file_path,
|
|
1202
|
+
score=score,
|
|
1203
|
+
excerpt=excerpt,
|
|
1204
|
+
content=content,
|
|
1205
|
+
symbol=symbol,
|
|
1206
|
+
metadata=metadata,
|
|
1207
|
+
start_line=start_line,
|
|
1208
|
+
end_line=end_line,
|
|
1209
|
+
symbol_name=symbol_name,
|
|
1210
|
+
symbol_kind=symbol_kind,
|
|
1211
|
+
))
|
|
1212
|
+
|
|
1213
|
+
return results
|
|
1214
|
+
|
|
1215
|
+
except Exception as exc:
|
|
1216
|
+
self.logger.debug("SPLADE search error: %s", exc)
|
|
1217
|
+
return []
|