claude-code-workflow 6.1.4 → 6.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/.claude/CLAUDE.md +10 -0
  2. package/.claude/agents/action-planning-agent.md +857 -778
  3. package/.claude/agents/cli-execution-agent.md +266 -269
  4. package/.claude/agents/cli-explore-agent.md +2 -2
  5. package/.claude/agents/cli-lite-planning-agent.md +142 -92
  6. package/.claude/agents/cli-planning-agent.md +4 -4
  7. package/.claude/agents/code-developer.md +7 -6
  8. package/.claude/agents/conceptual-planning-agent.md +2 -2
  9. package/.claude/agents/context-search-agent.md +31 -32
  10. package/.claude/agents/doc-generator.md +4 -4
  11. package/.claude/agents/memory-bridge.md +93 -93
  12. package/.claude/agents/test-context-search-agent.md +8 -7
  13. package/.claude/agents/test-fix-agent.md +7 -6
  14. package/.claude/commands/clean.md +516 -0
  15. package/.claude/commands/memory/compact.md +383 -0
  16. package/.claude/commands/memory/docs-full-cli.md +471 -471
  17. package/.claude/commands/memory/docs-related-cli.md +386 -386
  18. package/.claude/commands/memory/docs.md +615 -615
  19. package/.claude/commands/memory/load.md +5 -5
  20. package/.claude/commands/memory/tech-research-rules.md +310 -0
  21. package/.claude/commands/memory/update-full.md +332 -332
  22. package/.claude/commands/memory/workflow-skill-memory.md +4 -4
  23. package/.claude/commands/task/create.md +151 -151
  24. package/.claude/commands/version.md +254 -254
  25. package/.claude/commands/workflow/brainstorm/api-designer.md +587 -585
  26. package/.claude/commands/workflow/brainstorm/artifacts.md +1 -0
  27. package/.claude/commands/workflow/brainstorm/auto-parallel.md +443 -443
  28. package/.claude/commands/workflow/brainstorm/data-architect.md +220 -220
  29. package/.claude/commands/workflow/brainstorm/product-manager.md +200 -200
  30. package/.claude/commands/workflow/brainstorm/product-owner.md +200 -200
  31. package/.claude/commands/workflow/brainstorm/scrum-master.md +200 -200
  32. package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
  33. package/.claude/commands/workflow/brainstorm/system-architect.md +389 -387
  34. package/.claude/commands/workflow/brainstorm/ui-designer.md +221 -221
  35. package/.claude/commands/workflow/brainstorm/ux-expert.md +221 -221
  36. package/.claude/commands/workflow/debug.md +321 -0
  37. package/.claude/commands/workflow/execute.md +13 -0
  38. package/.claude/commands/workflow/init.md +165 -164
  39. package/.claude/commands/workflow/lite-execute.md +119 -13
  40. package/.claude/commands/workflow/lite-fix.md +623 -621
  41. package/.claude/commands/workflow/lite-plan.md +610 -592
  42. package/.claude/commands/workflow/plan.md +5 -5
  43. package/.claude/commands/workflow/review-module-cycle.md +2 -0
  44. package/.claude/commands/workflow/review-session-cycle.md +2 -0
  45. package/.claude/commands/workflow/review.md +297 -291
  46. package/.claude/commands/workflow/session/complete.md +153 -500
  47. package/.claude/commands/workflow/session/list.md +95 -95
  48. package/.claude/commands/workflow/session/resume.md +60 -60
  49. package/.claude/commands/workflow/session/start.md +199 -199
  50. package/.claude/commands/workflow/tdd-plan.md +3 -3
  51. package/.claude/commands/workflow/tdd-verify.md +23 -9
  52. package/.claude/commands/workflow/test-cycle-execute.md +2 -0
  53. package/.claude/commands/workflow/test-fix-gen.md +699 -699
  54. package/.claude/commands/workflow/tools/conflict-resolution.md +104 -18
  55. package/.claude/commands/workflow/tools/context-gather.md +436 -434
  56. package/.claude/commands/workflow/tools/task-generate-agent.md +490 -291
  57. package/.claude/commands/workflow/tools/task-generate-tdd.md +18 -10
  58. package/.claude/commands/workflow/tools/test-concept-enhanced.md +2 -1
  59. package/.claude/commands/workflow/tools/test-context-gather.md +1 -0
  60. package/.claude/commands/workflow/tools/test-task-generate.md +1 -0
  61. package/.claude/commands/workflow/ui-design/import-from-code.md +9 -6
  62. package/.claude/skills/command-guide/SKILL.md +5 -5
  63. package/.claude/skills/command-guide/index/all-commands.json +1 -1
  64. package/.claude/skills/command-guide/index/by-category.json +1 -1
  65. package/.claude/skills/command-guide/index/by-use-case.json +1 -1
  66. package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +857 -778
  67. package/.claude/skills/command-guide/reference/agents/cli-execution-agent.md +266 -269
  68. package/.claude/skills/command-guide/reference/agents/cli-explore-agent.md +2 -2
  69. package/.claude/skills/command-guide/reference/agents/cli-lite-planning-agent.md +142 -92
  70. package/.claude/skills/command-guide/reference/agents/cli-planning-agent.md +4 -4
  71. package/.claude/skills/command-guide/reference/agents/code-developer.md +7 -6
  72. package/.claude/skills/command-guide/reference/agents/conceptual-planning-agent.md +2 -2
  73. package/.claude/skills/command-guide/reference/agents/context-search-agent.md +31 -32
  74. package/.claude/skills/command-guide/reference/agents/doc-generator.md +4 -4
  75. package/.claude/skills/command-guide/reference/agents/memory-bridge.md +93 -93
  76. package/.claude/skills/command-guide/reference/agents/test-context-search-agent.md +8 -7
  77. package/.claude/skills/command-guide/reference/agents/test-fix-agent.md +7 -6
  78. package/.claude/skills/command-guide/reference/commands/memory/docs-full-cli.md +471 -471
  79. package/.claude/skills/command-guide/reference/commands/memory/docs-related-cli.md +386 -386
  80. package/.claude/skills/command-guide/reference/commands/memory/docs.md +17 -16
  81. package/.claude/skills/command-guide/reference/commands/memory/load.md +5 -5
  82. package/.claude/skills/command-guide/reference/commands/memory/tech-research.md +194 -357
  83. package/.claude/skills/command-guide/reference/commands/memory/update-full.md +332 -332
  84. package/.claude/skills/command-guide/reference/commands/memory/workflow-skill-memory.md +4 -4
  85. package/.claude/skills/command-guide/reference/commands/task/create.md +151 -151
  86. package/.claude/skills/command-guide/reference/commands/version.md +254 -254
  87. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/api-designer.md +585 -585
  88. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/auto-parallel.md +443 -443
  89. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/data-architect.md +220 -220
  90. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-manager.md +200 -200
  91. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-owner.md +200 -200
  92. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/scrum-master.md +200 -200
  93. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
  94. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/system-architect.md +387 -387
  95. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ui-designer.md +221 -221
  96. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ux-expert.md +221 -221
  97. package/.claude/skills/command-guide/reference/commands/workflow/execute.md +25 -20
  98. package/.claude/skills/command-guide/reference/commands/workflow/init.md +164 -164
  99. package/.claude/skills/command-guide/reference/commands/workflow/lite-execute.md +748 -686
  100. package/.claude/skills/command-guide/reference/commands/workflow/lite-fix.md +664 -621
  101. package/.claude/skills/command-guide/reference/commands/workflow/lite-plan.md +645 -592
  102. package/.claude/skills/command-guide/reference/commands/workflow/plan.md +5 -5
  103. package/.claude/skills/command-guide/reference/commands/workflow/review.md +25 -18
  104. package/.claude/skills/command-guide/reference/commands/workflow/session/complete.md +547 -500
  105. package/.claude/skills/command-guide/reference/commands/workflow/session/list.md +45 -27
  106. package/.claude/skills/command-guide/reference/commands/workflow/session/resume.md +35 -19
  107. package/.claude/skills/command-guide/reference/commands/workflow/session/start.md +90 -33
  108. package/.claude/skills/command-guide/reference/commands/workflow/tdd-plan.md +3 -3
  109. package/.claude/skills/command-guide/reference/commands/workflow/tdd-verify.md +23 -9
  110. package/.claude/skills/command-guide/reference/commands/workflow/test-fix-gen.md +699 -699
  111. package/.claude/skills/command-guide/reference/commands/workflow/tools/conflict-resolution.md +103 -17
  112. package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +434 -434
  113. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-agent.md +487 -291
  114. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +17 -10
  115. package/.claude/skills/command-guide/reference/commands/workflow/tools/test-concept-enhanced.md +1 -1
  116. package/.claude/skills/command-guide/reference/commands/workflow/ui-design/import-from-code.md +6 -6
  117. package/.claude/workflows/chinese-response.md +38 -0
  118. package/.claude/workflows/cli-templates/prompts/rules/rule-api.txt +122 -0
  119. package/.claude/workflows/cli-templates/prompts/rules/rule-components.txt +122 -0
  120. package/.claude/workflows/cli-templates/prompts/rules/rule-config.txt +89 -0
  121. package/.claude/workflows/cli-templates/prompts/rules/rule-core.txt +60 -0
  122. package/.claude/workflows/cli-templates/prompts/rules/rule-patterns.txt +70 -0
  123. package/.claude/workflows/cli-templates/prompts/rules/rule-testing.txt +81 -0
  124. package/.claude/workflows/cli-templates/prompts/rules/tech-rules-agent-prompt.txt +89 -0
  125. package/.claude/workflows/cli-templates/prompts/workflow/gemini-solution-design.txt +131 -131
  126. package/.claude/workflows/cli-templates/prompts/workflow/skill-conflict-patterns.txt +5 -9
  127. package/.claude/workflows/cli-templates/prompts/workflow/skill-lessons-learned.txt +5 -9
  128. package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +112 -0
  129. package/.claude/workflows/cli-templates/protocols/write-protocol.md +201 -0
  130. package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +137 -0
  131. package/.claude/workflows/cli-templates/schemas/debug-log-json-schema.json +127 -0
  132. package/.claude/workflows/cli-templates/schemas/fix-plan-json-schema.json +25 -0
  133. package/.claude/workflows/cli-templates/schemas/plan-json-schema.json +25 -0
  134. package/.claude/workflows/cli-tools-usage.md +526 -0
  135. package/{CLAUDE.md → .claude/workflows/coding-philosophy.md} +24 -45
  136. package/.claude/workflows/context-tools.md +84 -0
  137. package/.claude/workflows/file-modification.md +64 -0
  138. package/.claude/workflows/tool-strategy.md +216 -79
  139. package/.claude/workflows/windows-platform.md +16 -0
  140. package/.claude/workflows/workflow-architecture.md +942 -942
  141. package/.codex/AGENTS.md +63 -330
  142. package/.codex/prompts/debug.md +318 -0
  143. package/.codex/prompts/execute.md +273 -0
  144. package/.codex/prompts/lite-execute.md +164 -0
  145. package/.codex/prompts/lite-plan.md +469 -0
  146. package/.codex/prompts.zip +0 -0
  147. package/.gemini/GEMINI.md +25 -164
  148. package/.qwen/QWEN.md +0 -139
  149. package/README.md +29 -9
  150. package/ccw/README.md +30 -6
  151. package/ccw/bin/ccw-mcp.js +7 -0
  152. package/ccw/bin/ccw.js +9 -9
  153. package/ccw/package.json +65 -47
  154. package/ccw/src/.workflow/.cli-history/history.db +0 -0
  155. package/ccw/src/.workflow/.cli-history/history.db-shm +0 -0
  156. package/ccw/src/.workflow/.cli-history/history.db-wal +0 -0
  157. package/ccw/src/cli.ts +244 -0
  158. package/ccw/src/commands/cli.ts +740 -0
  159. package/ccw/src/commands/core-memory.ts +770 -0
  160. package/ccw/src/commands/hook.ts +315 -0
  161. package/ccw/src/commands/install.ts +519 -0
  162. package/ccw/src/commands/{list.js → list.ts} +1 -1
  163. package/ccw/src/commands/memory.ts +1090 -0
  164. package/ccw/src/commands/{serve.js → serve.ts} +14 -5
  165. package/ccw/src/commands/session-path-resolver.ts +372 -0
  166. package/ccw/src/commands/session.ts +1141 -0
  167. package/ccw/src/commands/{stop.js → stop.ts} +16 -6
  168. package/ccw/src/commands/tool.ts +201 -0
  169. package/ccw/src/commands/{uninstall.js → uninstall.ts} +89 -40
  170. package/ccw/src/commands/{upgrade.js → upgrade.ts} +68 -23
  171. package/ccw/src/commands/{view.js → view.ts} +22 -8
  172. package/ccw/src/config/storage-paths.ts +670 -0
  173. package/ccw/src/core/cache-manager.ts +294 -0
  174. package/ccw/src/core/claude-freshness.ts +319 -0
  175. package/ccw/src/core/core-memory-store.ts +1528 -0
  176. package/ccw/src/core/{dashboard-generator-patch.js → dashboard-generator-patch.ts} +18 -0
  177. package/ccw/src/core/{dashboard-generator.js → dashboard-generator.ts} +69 -12
  178. package/ccw/src/core/data-aggregator.ts +584 -0
  179. package/ccw/src/core/history-importer.ts +625 -0
  180. package/ccw/src/core/{lite-scanner.js → lite-scanner-complete.ts} +162 -66
  181. package/ccw/src/core/lite-scanner.ts +469 -0
  182. package/ccw/src/core/{manifest.js → manifest.ts} +104 -34
  183. package/ccw/src/core/memory-embedder-bridge.ts +262 -0
  184. package/ccw/src/core/memory-store.ts +978 -0
  185. package/ccw/src/core/routes/ccw-routes.ts +96 -0
  186. package/ccw/src/core/routes/claude-routes.ts +1183 -0
  187. package/ccw/src/core/routes/cli-routes.ts +561 -0
  188. package/ccw/src/core/routes/codexlens-routes.ts +806 -0
  189. package/ccw/src/core/routes/core-memory-routes.ts +605 -0
  190. package/ccw/src/core/routes/files-routes.ts +428 -0
  191. package/ccw/src/core/routes/graph-routes.md +164 -0
  192. package/ccw/src/core/routes/graph-routes.ts +626 -0
  193. package/ccw/src/core/routes/help-routes.ts +308 -0
  194. package/ccw/src/core/routes/hooks-routes.ts +405 -0
  195. package/ccw/src/core/routes/mcp-routes.ts +1271 -0
  196. package/ccw/src/core/routes/mcp-routes.ts.backup +550 -0
  197. package/ccw/src/core/routes/mcp-templates-db.ts +268 -0
  198. package/ccw/src/core/routes/memory-routes.ts +1206 -0
  199. package/ccw/src/core/routes/rules-routes.ts +526 -0
  200. package/ccw/src/core/routes/session-routes.ts +467 -0
  201. package/ccw/src/core/routes/skills-routes.ts +599 -0
  202. package/ccw/src/core/routes/status-routes.ts +57 -0
  203. package/ccw/src/core/routes/system-routes.ts +427 -0
  204. package/ccw/src/core/server.ts +431 -0
  205. package/ccw/src/core/session-clustering-service.ts +1258 -0
  206. package/ccw/src/core/session-scanner.ts +283 -0
  207. package/ccw/src/core/websocket.ts +190 -0
  208. package/ccw/src/{index.js → index.ts} +1 -0
  209. package/ccw/src/mcp-server/index.ts +186 -0
  210. package/ccw/src/templates/assets/css/github-dark.min.css +10 -0
  211. package/ccw/src/templates/assets/css/github.min.css +10 -0
  212. package/ccw/src/templates/assets/js/cytoscape.min.js +32 -0
  213. package/ccw/src/templates/assets/js/d3.min.js +2 -0
  214. package/ccw/src/templates/assets/js/highlight.min.js +1244 -0
  215. package/ccw/src/templates/assets/js/lucide.min.js +12 -0
  216. package/ccw/src/templates/assets/js/marked.min.js +69 -0
  217. package/ccw/src/templates/assets/js/tailwind.js +83 -0
  218. package/ccw/src/templates/dashboard-css/01-base.css +11 -0
  219. package/ccw/src/templates/dashboard-css/02-session.css +22 -0
  220. package/ccw/src/templates/dashboard-css/04-lite-tasks.css +10 -0
  221. package/ccw/src/templates/dashboard-css/06-cards.css +10 -4
  222. package/ccw/src/templates/dashboard-css/07-managers.css +1178 -7
  223. package/ccw/src/templates/dashboard-css/09-explorer.css +23 -12
  224. package/ccw/src/templates/dashboard-css/10-cli-status.css +337 -0
  225. package/ccw/src/templates/dashboard-css/11-cli-history.css +271 -0
  226. package/ccw/src/templates/dashboard-css/12-cli-legacy.css +796 -0
  227. package/ccw/src/templates/dashboard-css/13-cli-ccw.css +199 -0
  228. package/ccw/src/templates/dashboard-css/14-cli-modals.css +258 -0
  229. package/ccw/src/templates/dashboard-css/15-cli-endpoints.css +305 -0
  230. package/ccw/src/templates/dashboard-css/16-cli-session.css +241 -0
  231. package/ccw/src/templates/dashboard-css/17-cli-conversation.css +283 -0
  232. package/ccw/src/templates/dashboard-css/18-cli-settings.css +160 -0
  233. package/ccw/src/templates/dashboard-css/19-cli-native-session.css +496 -0
  234. package/ccw/src/templates/dashboard-css/20-cli-taskqueue.css +188 -0
  235. package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +310 -0
  236. package/ccw/src/templates/dashboard-css/22-cli-semantic.css +240 -0
  237. package/ccw/src/templates/dashboard-css/23-memory.css +2390 -0
  238. package/ccw/src/templates/dashboard-css/24-prompt-history.css +1089 -0
  239. package/ccw/src/templates/dashboard-css/25-skills-rules.css +326 -0
  240. package/ccw/src/templates/dashboard-css/26-claude-manager.css +908 -0
  241. package/ccw/src/templates/dashboard-css/27-graph-explorer.css +1678 -0
  242. package/ccw/src/templates/dashboard-css/28-mcp-manager.css +748 -0
  243. package/ccw/src/templates/dashboard-css/29-help.css +264 -0
  244. package/ccw/src/templates/dashboard-css/30-core-memory.css +1700 -0
  245. package/ccw/src/templates/dashboard-js/api.js +162 -142
  246. package/ccw/src/templates/dashboard-js/components/carousel.js +4 -4
  247. package/ccw/src/templates/dashboard-js/components/cli-history.js +876 -0
  248. package/ccw/src/templates/dashboard-js/components/cli-status.js +978 -0
  249. package/ccw/src/templates/dashboard-js/components/global-notifications.js +508 -219
  250. package/ccw/src/templates/dashboard-js/components/hook-manager.js +1277 -282
  251. package/ccw/src/templates/dashboard-js/components/index-manager.js +302 -0
  252. package/ccw/src/templates/dashboard-js/components/mcp-manager.js +718 -27
  253. package/ccw/src/templates/dashboard-js/components/modals.js +66 -0
  254. package/ccw/src/templates/dashboard-js/components/navigation.js +80 -12
  255. package/ccw/src/templates/dashboard-js/components/notifications.js +758 -194
  256. package/ccw/src/templates/dashboard-js/components/storage-manager.js +478 -0
  257. package/ccw/src/templates/dashboard-js/components/tabs-other.js +157 -6
  258. package/ccw/src/templates/dashboard-js/components/task-queue-sidebar.js +716 -0
  259. package/ccw/src/templates/dashboard-js/help-i18n.js +272 -0
  260. package/ccw/src/templates/dashboard-js/i18n.js +2807 -0
  261. package/ccw/src/templates/dashboard-js/main.js +15 -0
  262. package/ccw/src/templates/dashboard-js/state.js +243 -42
  263. package/ccw/src/templates/dashboard-js/utils.js +47 -1
  264. package/ccw/src/templates/dashboard-js/views/claude-manager.js +912 -0
  265. package/ccw/src/templates/dashboard-js/views/cli-manager.js +2272 -0
  266. package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +964 -0
  267. package/ccw/src/templates/dashboard-js/views/core-memory-clusters.js +503 -0
  268. package/ccw/src/templates/dashboard-js/views/core-memory.js +782 -0
  269. package/ccw/src/templates/dashboard-js/views/explorer.js +888 -852
  270. package/ccw/src/templates/dashboard-js/views/graph-explorer.js +1157 -0
  271. package/ccw/src/templates/dashboard-js/views/help.js +856 -0
  272. package/ccw/src/templates/dashboard-js/views/history.js +337 -0
  273. package/ccw/src/templates/dashboard-js/views/home.js +61 -15
  274. package/ccw/src/templates/dashboard-js/views/hook-manager.js +311 -43
  275. package/ccw/src/templates/dashboard-js/views/lite-tasks.js +204 -28
  276. package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2187 -411
  277. package/ccw/src/templates/dashboard-js/views/mcp-manager.js.backup +1729 -0
  278. package/ccw/src/templates/dashboard-js/views/mcp-manager.js.new +928 -0
  279. package/ccw/src/templates/dashboard-js/views/memory.js +1221 -0
  280. package/ccw/src/templates/dashboard-js/views/prompt-history.js +713 -0
  281. package/ccw/src/templates/dashboard-js/views/rules-manager.js +828 -0
  282. package/ccw/src/templates/dashboard-js/views/session-detail.js +54 -53
  283. package/ccw/src/templates/dashboard-js/views/skills-manager.js +819 -0
  284. package/ccw/src/templates/dashboard.html +185 -85
  285. package/ccw/src/templates/hooks-config-example.json +60 -0
  286. package/ccw/src/tools/classify-folders.ts +245 -0
  287. package/ccw/src/tools/cli-config-manager.ts +268 -0
  288. package/ccw/src/tools/cli-executor.ts +2014 -0
  289. package/ccw/src/tools/cli-history-store.ts +1195 -0
  290. package/ccw/src/tools/codex-lens.ts +1141 -0
  291. package/ccw/src/tools/{convert-tokens-to-css.js → convert-tokens-to-css.ts} +73 -23
  292. package/ccw/src/tools/core-memory.ts +444 -0
  293. package/ccw/src/tools/detect-changed-modules.ts +325 -0
  294. package/ccw/src/tools/{discover-design-files.js → discover-design-files.ts} +74 -24
  295. package/ccw/src/tools/edit-file.ts +568 -0
  296. package/ccw/src/tools/{generate-module-docs.js → generate-module-docs.ts} +207 -185
  297. package/ccw/src/tools/{get-modules-by-depth.js → get-modules-by-depth.ts} +120 -79
  298. package/ccw/src/tools/index.ts +370 -0
  299. package/ccw/src/tools/native-session-discovery.ts +795 -0
  300. package/ccw/src/tools/notifier.ts +129 -0
  301. package/ccw/src/tools/read-file.ts +410 -0
  302. package/ccw/src/tools/resume-strategy.ts +345 -0
  303. package/ccw/src/tools/session-content-parser.ts +619 -0
  304. package/ccw/src/tools/session-manager.ts +1026 -0
  305. package/ccw/src/tools/smart-context.ts +228 -0
  306. package/ccw/src/tools/smart-search.ts +2065 -0
  307. package/ccw/src/tools/smart-search.ts.backup +1233 -0
  308. package/ccw/src/tools/storage-manager.ts +455 -0
  309. package/ccw/src/tools/write-file.ts +222 -0
  310. package/ccw/src/types/config.ts +11 -0
  311. package/ccw/src/types/index.ts +3 -0
  312. package/ccw/src/types/session.ts +25 -0
  313. package/ccw/src/types/tool.ts +41 -0
  314. package/ccw/src/utils/{browser-launcher.js → browser-launcher.ts} +10 -8
  315. package/ccw/src/utils/file-utils.ts +48 -0
  316. package/ccw/src/utils/{path-resolver.js → path-resolver.ts} +114 -78
  317. package/ccw/src/utils/path-validator.ts +153 -0
  318. package/ccw/src/utils/{ui.js → ui.ts} +32 -25
  319. package/codex-lens/pyproject.toml +48 -0
  320. package/codex-lens/src/codexlens/.workflow/.cli-history/history.db +0 -0
  321. package/codex-lens/src/codexlens/__init__.py +28 -0
  322. package/codex-lens/src/codexlens/__main__.py +14 -0
  323. package/codex-lens/src/codexlens/__pycache__/__init__.cpython-313.pyc +0 -0
  324. package/codex-lens/src/codexlens/__pycache__/__main__.cpython-313.pyc +0 -0
  325. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  326. package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
  327. package/codex-lens/src/codexlens/__pycache__/errors.cpython-313.pyc +0 -0
  328. package/codex-lens/src/codexlens/cli/__init__.py +27 -0
  329. package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-313.pyc +0 -0
  330. package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
  331. package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
  332. package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
  333. package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
  334. package/codex-lens/src/codexlens/cli/commands.py +1931 -0
  335. package/codex-lens/src/codexlens/cli/embedding_manager.py +620 -0
  336. package/codex-lens/src/codexlens/cli/model_manager.py +289 -0
  337. package/codex-lens/src/codexlens/cli/output.py +124 -0
  338. package/codex-lens/src/codexlens/config.py +201 -0
  339. package/codex-lens/src/codexlens/entities.py +121 -0
  340. package/codex-lens/src/codexlens/errors.py +55 -0
  341. package/codex-lens/src/codexlens/indexing/README.md +77 -0
  342. package/codex-lens/src/codexlens/indexing/__init__.py +4 -0
  343. package/codex-lens/src/codexlens/indexing/__pycache__/__init__.cpython-313.pyc +0 -0
  344. package/codex-lens/src/codexlens/indexing/__pycache__/symbol_extractor.cpython-313.pyc +0 -0
  345. package/codex-lens/src/codexlens/indexing/symbol_extractor.py +243 -0
  346. package/codex-lens/src/codexlens/parsers/__init__.py +8 -0
  347. package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-313.pyc +0 -0
  348. package/codex-lens/src/codexlens/parsers/__pycache__/encoding.cpython-313.pyc +0 -0
  349. package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-313.pyc +0 -0
  350. package/codex-lens/src/codexlens/parsers/__pycache__/tokenizer.cpython-313.pyc +0 -0
  351. package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
  352. package/codex-lens/src/codexlens/parsers/encoding.py +202 -0
  353. package/codex-lens/src/codexlens/parsers/factory.py +256 -0
  354. package/codex-lens/src/codexlens/parsers/tokenizer.py +98 -0
  355. package/codex-lens/src/codexlens/parsers/treesitter_parser.py +335 -0
  356. package/codex-lens/src/codexlens/search/__init__.py +15 -0
  357. package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-313.pyc +0 -0
  358. package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
  359. package/codex-lens/src/codexlens/search/__pycache__/enrichment.cpython-313.pyc +0 -0
  360. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  361. package/codex-lens/src/codexlens/search/__pycache__/query_parser.cpython-313.pyc +0 -0
  362. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  363. package/codex-lens/src/codexlens/search/chain_search.py +647 -0
  364. package/codex-lens/src/codexlens/search/enrichment.py +150 -0
  365. package/codex-lens/src/codexlens/search/hybrid_search.py +313 -0
  366. package/codex-lens/src/codexlens/search/query_parser.py +242 -0
  367. package/codex-lens/src/codexlens/search/ranking.py +274 -0
  368. package/codex-lens/src/codexlens/semantic/__init__.py +39 -0
  369. package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
  370. package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-313.pyc +0 -0
  371. package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
  372. package/codex-lens/src/codexlens/semantic/__pycache__/code_extractor.cpython-313.pyc +0 -0
  373. package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
  374. package/codex-lens/src/codexlens/semantic/__pycache__/graph_analyzer.cpython-313.pyc +0 -0
  375. package/codex-lens/src/codexlens/semantic/__pycache__/llm_enhancer.cpython-313.pyc +0 -0
  376. package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
  377. package/codex-lens/src/codexlens/semantic/ann_index.py +414 -0
  378. package/codex-lens/src/codexlens/semantic/chunker.py +448 -0
  379. package/codex-lens/src/codexlens/semantic/code_extractor.py +274 -0
  380. package/codex-lens/src/codexlens/semantic/embedder.py +185 -0
  381. package/codex-lens/src/codexlens/semantic/vector_store.py +955 -0
  382. package/codex-lens/src/codexlens/storage/__init__.py +29 -0
  383. package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-313.pyc +0 -0
  384. package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
  385. package/codex-lens/src/codexlens/storage/__pycache__/file_cache.cpython-313.pyc +0 -0
  386. package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
  387. package/codex-lens/src/codexlens/storage/__pycache__/migration_manager.cpython-313.pyc +0 -0
  388. package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
  389. package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-313.pyc +0 -0
  390. package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-313.pyc +0 -0
  391. package/codex-lens/src/codexlens/storage/__pycache__/sqlite_utils.cpython-313.pyc +0 -0
  392. package/codex-lens/src/codexlens/storage/dir_index.py +1850 -0
  393. package/codex-lens/src/codexlens/storage/file_cache.py +32 -0
  394. package/codex-lens/src/codexlens/storage/index_tree.py +776 -0
  395. package/codex-lens/src/codexlens/storage/migration_manager.py +154 -0
  396. package/codex-lens/src/codexlens/storage/migrations/__init__.py +1 -0
  397. package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
  398. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_001_normalize_keywords.cpython-313.pyc +0 -0
  399. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_002_add_token_metadata.cpython-313.pyc +0 -0
  400. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_003_code_relationships.cpython-313.pyc +0 -0
  401. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
  402. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_005_cleanup_unused_fields.cpython-313.pyc +0 -0
  403. package/codex-lens/src/codexlens/storage/migrations/migration_001_normalize_keywords.py +123 -0
  404. package/codex-lens/src/codexlens/storage/migrations/migration_002_add_token_metadata.py +48 -0
  405. package/codex-lens/src/codexlens/storage/migrations/migration_004_dual_fts.py +232 -0
  406. package/codex-lens/src/codexlens/storage/migrations/migration_005_cleanup_unused_fields.py +196 -0
  407. package/codex-lens/src/codexlens/storage/path_mapper.py +274 -0
  408. package/codex-lens/src/codexlens/storage/registry.py +670 -0
  409. package/codex-lens/src/codexlens/storage/sqlite_store.py +576 -0
  410. package/codex-lens/src/codexlens/storage/sqlite_utils.py +64 -0
  411. package/package.json +4 -1
  412. package/.claude/commands/memory/tech-research.md +0 -477
  413. package/.claude/scripts/classify-folders.sh +0 -39
  414. package/.claude/scripts/convert_tokens_to_css.sh +0 -229
  415. package/.claude/scripts/detect_changed_modules.sh +0 -161
  416. package/.claude/scripts/discover-design-files.sh +0 -87
  417. package/.claude/scripts/extract-animations.js +0 -243
  418. package/.claude/scripts/extract-computed-styles.js +0 -118
  419. package/.claude/scripts/extract-layout-structure.js +0 -411
  420. package/.claude/scripts/generate_module_docs.sh +0 -717
  421. package/.claude/scripts/get_modules_by_depth.sh +0 -170
  422. package/.claude/scripts/ui-generate-preview.sh +0 -395
  423. package/.claude/scripts/ui-instantiate-prototypes.sh +0 -815
  424. package/.claude/scripts/update_module_claude.sh +0 -337
  425. package/.claude/workflows/context-search-strategy.md +0 -77
  426. package/.claude/workflows/intelligent-tools-strategy.md +0 -662
  427. package/ccw/src/cli.js +0 -119
  428. package/ccw/src/commands/install.js +0 -324
  429. package/ccw/src/commands/tool.js +0 -138
  430. package/ccw/src/core/data-aggregator.js +0 -409
  431. package/ccw/src/core/server.js +0 -2063
  432. package/ccw/src/core/session-scanner.js +0 -235
  433. package/ccw/src/tools/classify-folders.js +0 -204
  434. package/ccw/src/tools/detect-changed-modules.js +0 -288
  435. package/ccw/src/tools/edit-file.js +0 -266
  436. package/ccw/src/tools/index.js +0 -176
  437. package/ccw/src/utils/file-utils.js +0 -48
@@ -0,0 +1,647 @@
1
+ """Chain search engine for recursive multi-directory searching.
2
+
3
+ Provides parallel search across directory hierarchies using indexed _index.db files.
4
+ Supports depth-limited traversal, result aggregation, and symbol search.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from concurrent.futures import ThreadPoolExecutor, as_completed
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import List, Optional, Dict, Any
13
+ import logging
14
+ import time
15
+
16
+ from codexlens.entities import SearchResult, Symbol
17
+ from codexlens.storage.registry import RegistryStore, DirMapping
18
+ from codexlens.storage.dir_index import DirIndexStore, SubdirLink
19
+ from codexlens.storage.path_mapper import PathMapper
20
+ from codexlens.storage.sqlite_store import SQLiteStore
21
+ from codexlens.search.hybrid_search import HybridSearchEngine
22
+
23
+
24
+ @dataclass
25
+ class SearchOptions:
26
+ """Configuration options for chain search.
27
+
28
+ Attributes:
29
+ depth: Maximum search depth (-1 = unlimited, 0 = current dir only)
30
+ max_workers: Number of parallel worker threads
31
+ limit_per_dir: Maximum results per directory
32
+ total_limit: Total result limit across all directories
33
+ include_symbols: Whether to include symbol search results
34
+ files_only: Return only file paths without excerpts
35
+ include_semantic: Whether to include semantic keyword search results
36
+ hybrid_mode: Enable hybrid search with RRF fusion (default False)
37
+ enable_fuzzy: Enable fuzzy FTS in hybrid mode (default True)
38
+ enable_vector: Enable vector semantic search (default False)
39
+ pure_vector: If True, only use vector search without FTS fallback (default False)
40
+ hybrid_weights: Custom RRF weights for hybrid search (optional)
41
+ group_results: Enable grouping of similar results (default False)
42
+ grouping_threshold: Score threshold for grouping similar results (default 0.01)
43
+ """
44
+ depth: int = -1
45
+ max_workers: int = 8
46
+ limit_per_dir: int = 10
47
+ total_limit: int = 100
48
+ include_symbols: bool = False
49
+ files_only: bool = False
50
+ include_semantic: bool = False
51
+ hybrid_mode: bool = False
52
+ enable_fuzzy: bool = True
53
+ enable_vector: bool = False
54
+ pure_vector: bool = False
55
+ hybrid_weights: Optional[Dict[str, float]] = None
56
+ group_results: bool = False
57
+ grouping_threshold: float = 0.01
58
+
59
+
60
+ @dataclass
61
+ class SearchStats:
62
+ """Statistics collected during search execution.
63
+
64
+ Attributes:
65
+ dirs_searched: Number of directories searched
66
+ files_matched: Number of files with matches
67
+ time_ms: Total search time in milliseconds
68
+ errors: List of error messages encountered
69
+ """
70
+ dirs_searched: int = 0
71
+ files_matched: int = 0
72
+ time_ms: float = 0
73
+ errors: List[str] = field(default_factory=list)
74
+
75
+
76
+ @dataclass
77
+ class ChainSearchResult:
78
+ """Comprehensive search result with metadata.
79
+
80
+ Attributes:
81
+ query: Original search query
82
+ results: List of SearchResult objects
83
+ symbols: List of Symbol objects (if include_symbols=True)
84
+ stats: SearchStats with execution metrics
85
+ """
86
+ query: str
87
+ results: List[SearchResult]
88
+ symbols: List[Symbol]
89
+ stats: SearchStats
90
+
91
+
92
+ class ChainSearchEngine:
93
+ """Parallel chain search engine for hierarchical directory indexes.
94
+
95
+ Searches across multiple directory indexes in parallel, following subdirectory
96
+ links to recursively traverse the file tree. Supports depth limits, result
97
+ aggregation, and both content and symbol searches.
98
+
99
+ Thread-safe with configurable parallelism.
100
+
101
+ Attributes:
102
+ registry: Global project registry
103
+ mapper: Path mapping utility
104
+ logger: Python logger instance
105
+ """
106
+
107
+ def __init__(self,
108
+ registry: RegistryStore,
109
+ mapper: PathMapper,
110
+ max_workers: int = 8):
111
+ """Initialize chain search engine.
112
+
113
+ Args:
114
+ registry: Global project registry for path lookups
115
+ mapper: Path mapper for source/index conversions
116
+ max_workers: Maximum parallel workers (default 8)
117
+ """
118
+ self.registry = registry
119
+ self.mapper = mapper
120
+ self.logger = logging.getLogger(__name__)
121
+ self._max_workers = max_workers
122
+ self._executor: Optional[ThreadPoolExecutor] = None
123
+
124
+ def _get_executor(self, max_workers: Optional[int] = None) -> ThreadPoolExecutor:
125
+ """Get or create the shared thread pool executor.
126
+
127
+ Lazy initialization to avoid creating executor if never used.
128
+
129
+ Args:
130
+ max_workers: Override default max_workers if specified
131
+
132
+ Returns:
133
+ ThreadPoolExecutor instance
134
+ """
135
+ workers = max_workers or self._max_workers
136
+ if self._executor is None:
137
+ self._executor = ThreadPoolExecutor(max_workers=workers)
138
+ return self._executor
139
+
140
+ def close(self) -> None:
141
+ """Shutdown the thread pool executor."""
142
+ if self._executor is not None:
143
+ self._executor.shutdown(wait=True)
144
+ self._executor = None
145
+
146
+ def __enter__(self) -> "ChainSearchEngine":
147
+ """Context manager entry."""
148
+ return self
149
+
150
+ def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
151
+ """Context manager exit."""
152
+ self.close()
153
+
154
+ def search(self, query: str,
155
+ source_path: Path,
156
+ options: Optional[SearchOptions] = None) -> ChainSearchResult:
157
+ """Execute chain search from source_path with recursive traversal.
158
+
159
+ Process:
160
+ 1. Locate starting index for source_path
161
+ 2. Collect all child indexes based on depth limit
162
+ 3. Search indexes in parallel using ThreadPoolExecutor
163
+ 4. Aggregate, deduplicate, and rank results
164
+
165
+ Args:
166
+ query: FTS5 search query string
167
+ source_path: Starting directory path
168
+ options: Search configuration (uses defaults if None)
169
+
170
+ Returns:
171
+ ChainSearchResult with results, symbols, and statistics
172
+
173
+ Examples:
174
+ >>> engine = ChainSearchEngine(registry, mapper)
175
+ >>> result = engine.search("authentication", Path("D:/project/src"))
176
+ >>> for r in result.results[:5]:
177
+ ... print(f"{r.path}: {r.score:.2f}")
178
+ """
179
+ options = options or SearchOptions()
180
+ start_time = time.time()
181
+ stats = SearchStats()
182
+
183
+ # Step 1: Find starting index
184
+ start_index = self._find_start_index(source_path)
185
+ if not start_index:
186
+ self.logger.warning(f"No index found for {source_path}")
187
+ stats.time_ms = (time.time() - start_time) * 1000
188
+ return ChainSearchResult(
189
+ query=query,
190
+ results=[],
191
+ symbols=[],
192
+ stats=stats
193
+ )
194
+
195
+ # Step 2: Collect all index paths to search
196
+ index_paths = self._collect_index_paths(start_index, options.depth)
197
+ stats.dirs_searched = len(index_paths)
198
+
199
+ if not index_paths:
200
+ self.logger.warning(f"No indexes collected from {start_index}")
201
+ stats.time_ms = (time.time() - start_time) * 1000
202
+ return ChainSearchResult(
203
+ query=query,
204
+ results=[],
205
+ symbols=[],
206
+ stats=stats
207
+ )
208
+
209
+ # Step 3: Parallel search
210
+ results, search_stats = self._search_parallel(
211
+ index_paths, query, options
212
+ )
213
+ stats.errors = search_stats.errors
214
+
215
+ # Step 4: Merge and rank
216
+ final_results = self._merge_and_rank(results, options.total_limit)
217
+
218
+ # Step 5: Optional grouping of similar results
219
+ if options.group_results:
220
+ from codexlens.search.ranking import group_similar_results
221
+ final_results = group_similar_results(
222
+ final_results, score_threshold_abs=options.grouping_threshold
223
+ )
224
+
225
+ stats.files_matched = len(final_results)
226
+
227
+ # Optional: Symbol search
228
+ symbols = []
229
+ if options.include_symbols:
230
+ symbols = self._search_symbols_parallel(
231
+ index_paths, query, None, options.total_limit
232
+ )
233
+
234
+ stats.time_ms = (time.time() - start_time) * 1000
235
+
236
+ return ChainSearchResult(
237
+ query=query,
238
+ results=final_results,
239
+ symbols=symbols,
240
+ stats=stats
241
+ )
242
+
243
+ def search_files_only(self, query: str,
244
+ source_path: Path,
245
+ options: Optional[SearchOptions] = None) -> List[str]:
246
+ """Search and return only matching file paths.
247
+
248
+ Faster than full search when excerpts are not needed.
249
+
250
+ Args:
251
+ query: FTS5 search query string
252
+ source_path: Starting directory path
253
+ options: Search configuration (uses defaults if None)
254
+
255
+ Returns:
256
+ List of file paths as strings
257
+
258
+ Examples:
259
+ >>> engine = ChainSearchEngine(registry, mapper)
260
+ >>> paths = engine.search_files_only("TODO", Path("D:/project"))
261
+ >>> print(f"Found {len(paths)} files with TODOs")
262
+ """
263
+ options = options or SearchOptions()
264
+ options.files_only = True
265
+
266
+ result = self.search(query, source_path, options)
267
+ return [r.path for r in result.results]
268
+
269
+ def search_symbols(self, name: str,
270
+ source_path: Path,
271
+ kind: Optional[str] = None,
272
+ options: Optional[SearchOptions] = None) -> List[Symbol]:
273
+ """Chain symbol search across directory hierarchy.
274
+
275
+ Args:
276
+ name: Symbol name pattern (partial match supported)
277
+ source_path: Starting directory path
278
+ kind: Optional symbol kind filter (e.g., 'function', 'class')
279
+ options: Search configuration (uses defaults if None)
280
+
281
+ Returns:
282
+ List of Symbol objects sorted by name
283
+
284
+ Examples:
285
+ >>> engine = ChainSearchEngine(registry, mapper)
286
+ >>> funcs = engine.search_symbols("init", Path("D:/project"), kind="function")
287
+ >>> for sym in funcs[:10]:
288
+ ... print(f"{sym.name} ({sym.kind}): lines {sym.range}")
289
+ """
290
+ options = options or SearchOptions()
291
+
292
+ start_index = self._find_start_index(source_path)
293
+ if not start_index:
294
+ self.logger.warning(f"No index found for {source_path}")
295
+ return []
296
+
297
+ index_paths = self._collect_index_paths(start_index, options.depth)
298
+ if not index_paths:
299
+ return []
300
+
301
+ return self._search_symbols_parallel(
302
+ index_paths, name, kind, options.total_limit
303
+ )
304
+
305
+ # === Internal Methods ===
306
+
307
+ def _find_start_index(self, source_path: Path) -> Optional[Path]:
308
+ """Find index database path for source directory.
309
+
310
+ Attempts exact match first, then searches for nearest ancestor index.
311
+
312
+ Args:
313
+ source_path: Source directory path
314
+
315
+ Returns:
316
+ Path to _index.db file, or None if not found
317
+ """
318
+ source_path = source_path.resolve()
319
+
320
+ # Try exact match first
321
+ exact_index = self.mapper.source_to_index_db(source_path)
322
+ if exact_index.exists():
323
+ self.logger.debug(f"Found exact index: {exact_index}")
324
+ return exact_index
325
+
326
+ # Try nearest ancestor via registry
327
+ nearest = self.registry.find_nearest_index(source_path)
328
+ if nearest:
329
+ self.logger.debug(f"Found nearest index: {nearest.index_path}")
330
+ return nearest.index_path
331
+
332
+ self.logger.warning(f"No index found for {source_path}")
333
+ return None
334
+
335
+ def _collect_index_paths(self, start_index: Path,
336
+ depth: int) -> List[Path]:
337
+ """Recursively collect all subdirectory index paths.
338
+
339
+ Traverses directory tree via subdirs table in each _index.db,
340
+ respecting depth limit.
341
+
342
+ Args:
343
+ start_index: Starting _index.db path
344
+ depth: Maximum depth (-1 = unlimited, 0 = current only)
345
+
346
+ Returns:
347
+ List of _index.db paths to search
348
+ """
349
+ collected = []
350
+ visited = set()
351
+
352
+ def _collect_recursive(index_path: Path, current_depth: int):
353
+ # Normalize path to avoid duplicates
354
+ normalized = index_path.resolve()
355
+ if normalized in visited:
356
+ return
357
+ visited.add(normalized)
358
+
359
+ # Add current index
360
+ if normalized.exists():
361
+ collected.append(normalized)
362
+ else:
363
+ self.logger.debug(f"Index does not exist: {normalized}")
364
+ return
365
+
366
+ # Check depth limit
367
+ if depth >= 0 and current_depth >= depth:
368
+ return
369
+
370
+ # Read subdirs and recurse
371
+ try:
372
+ with DirIndexStore(normalized) as store:
373
+ subdirs = store.get_subdirs()
374
+ for subdir in subdirs:
375
+ _collect_recursive(subdir.index_path, current_depth + 1)
376
+ except Exception as exc:
377
+ self.logger.warning(f"Failed to read subdirs from {normalized}: {exc}")
378
+
379
+ _collect_recursive(start_index, 0)
380
+ self.logger.info(f"Collected {len(collected)} indexes (depth={depth})")
381
+ return collected
382
+
383
+ def _search_parallel(self, index_paths: List[Path],
384
+ query: str,
385
+ options: SearchOptions) -> tuple[List[SearchResult], SearchStats]:
386
+ """Search multiple indexes in parallel using shared ThreadPoolExecutor.
387
+
388
+ Args:
389
+ index_paths: List of _index.db paths to search
390
+ query: FTS5 query string
391
+ options: Search configuration
392
+
393
+ Returns:
394
+ Tuple of (all results, search statistics)
395
+ """
396
+ all_results = []
397
+ stats = SearchStats()
398
+
399
+ executor = self._get_executor(options.max_workers)
400
+ # Submit all search tasks
401
+ future_to_path = {
402
+ executor.submit(
403
+ self._search_single_index,
404
+ idx_path,
405
+ query,
406
+ options.limit_per_dir,
407
+ options.files_only,
408
+ options.include_semantic,
409
+ options.hybrid_mode,
410
+ options.enable_fuzzy,
411
+ options.enable_vector,
412
+ options.pure_vector,
413
+ options.hybrid_weights
414
+ ): idx_path
415
+ for idx_path in index_paths
416
+ }
417
+
418
+ # Collect results as they complete
419
+ for future in as_completed(future_to_path):
420
+ idx_path = future_to_path[future]
421
+ try:
422
+ results = future.result()
423
+ all_results.extend(results)
424
+ self.logger.debug(f"Got {len(results)} results from {idx_path.parent.name}")
425
+ except Exception as exc:
426
+ error_msg = f"Search failed for {idx_path}: {exc}"
427
+ self.logger.error(error_msg)
428
+ stats.errors.append(error_msg)
429
+
430
+ return all_results, stats
431
+
432
+ def _search_single_index(self, index_path: Path,
433
+ query: str,
434
+ limit: int,
435
+ files_only: bool = False,
436
+ include_semantic: bool = False,
437
+ hybrid_mode: bool = False,
438
+ enable_fuzzy: bool = True,
439
+ enable_vector: bool = False,
440
+ pure_vector: bool = False,
441
+ hybrid_weights: Optional[Dict[str, float]] = None) -> List[SearchResult]:
442
+ """Search a single index database.
443
+
444
+ Handles exceptions gracefully, returning empty list on failure.
445
+
446
+ Args:
447
+ index_path: Path to _index.db file
448
+ query: FTS5 query string (for FTS) or natural language query (for vector)
449
+ limit: Maximum results from this index
450
+ files_only: If True, skip snippet generation for faster search
451
+ include_semantic: If True, also search semantic keywords and merge results
452
+ hybrid_mode: If True, use hybrid search with RRF fusion
453
+ enable_fuzzy: Enable fuzzy FTS in hybrid mode
454
+ enable_vector: Enable vector semantic search
455
+ pure_vector: If True, only use vector search without FTS fallback
456
+ hybrid_weights: Custom RRF weights for hybrid search
457
+
458
+ Returns:
459
+ List of SearchResult objects (empty on error)
460
+ """
461
+ try:
462
+ # Use hybrid search if enabled
463
+ if hybrid_mode:
464
+ hybrid_engine = HybridSearchEngine(weights=hybrid_weights)
465
+ fts_results = hybrid_engine.search(
466
+ index_path,
467
+ query,
468
+ limit=limit,
469
+ enable_fuzzy=enable_fuzzy,
470
+ enable_vector=enable_vector,
471
+ pure_vector=pure_vector,
472
+ )
473
+ else:
474
+ # Single-FTS search (exact or fuzzy mode)
475
+ with DirIndexStore(index_path) as store:
476
+ # Get FTS results
477
+ if files_only:
478
+ # Fast path: return paths only without snippets
479
+ paths = store.search_files_only(query, limit=limit)
480
+ fts_results = [SearchResult(path=p, score=0.0, excerpt="") for p in paths]
481
+ else:
482
+ # Use fuzzy FTS if enable_fuzzy=True (mode="fuzzy"), otherwise exact FTS
483
+ if enable_fuzzy:
484
+ fts_results = store.search_fts_fuzzy(query, limit=limit)
485
+ else:
486
+ fts_results = store.search_fts(query, limit=limit)
487
+
488
+ # Optionally add semantic keyword results
489
+ if include_semantic:
490
+ try:
491
+ semantic_matches = store.search_semantic_keywords(query)
492
+ # Convert semantic matches to SearchResult with 0.8x weight
493
+ for file_entry, keywords in semantic_matches:
494
+ # Create excerpt from keywords
495
+ excerpt = f"Keywords: {', '.join(keywords[:5])}"
496
+ # Use a base score of 10.0 for semantic matches, weighted by 0.8
497
+ semantic_result = SearchResult(
498
+ path=str(file_entry.full_path),
499
+ score=10.0 * 0.8,
500
+ excerpt=excerpt
501
+ )
502
+ fts_results.append(semantic_result)
503
+ except Exception as sem_exc:
504
+ self.logger.debug(f"Semantic search error in {index_path}: {sem_exc}")
505
+
506
+ return fts_results
507
+ except Exception as exc:
508
+ self.logger.debug(f"Search error in {index_path}: {exc}")
509
+ return []
510
+
511
+ def _merge_and_rank(self, results: List[SearchResult],
512
+ limit: int) -> List[SearchResult]:
513
+ """Aggregate, deduplicate, and rank results.
514
+
515
+ Process:
516
+ 1. Deduplicate by path (keep highest score)
517
+ 2. Sort by score descending
518
+ 3. Limit to requested count
519
+
520
+ Args:
521
+ results: Raw results from all indexes
522
+ limit: Maximum results to return
523
+
524
+ Returns:
525
+ Deduplicated and ranked results
526
+ """
527
+ # Deduplicate by path, keeping best score
528
+ path_to_result: Dict[str, SearchResult] = {}
529
+ for result in results:
530
+ path = result.path
531
+ if path not in path_to_result or result.score > path_to_result[path].score:
532
+ path_to_result[path] = result
533
+
534
+ # Sort by score descending
535
+ unique_results = list(path_to_result.values())
536
+ unique_results.sort(key=lambda r: r.score, reverse=True)
537
+
538
+ # Apply limit
539
+ return unique_results[:limit]
540
+
541
+ def _search_symbols_parallel(self, index_paths: List[Path],
542
+ name: str,
543
+ kind: Optional[str],
544
+ limit: int) -> List[Symbol]:
545
+ """Search symbols across multiple indexes in parallel.
546
+
547
+ Args:
548
+ index_paths: List of _index.db paths to search
549
+ name: Symbol name pattern
550
+ kind: Optional symbol kind filter
551
+ limit: Total symbol limit
552
+
553
+ Returns:
554
+ Deduplicated and sorted symbols
555
+ """
556
+ all_symbols = []
557
+
558
+ executor = self._get_executor()
559
+ # Submit all symbol search tasks
560
+ future_to_path = {
561
+ executor.submit(
562
+ self._search_symbols_single,
563
+ idx_path,
564
+ name,
565
+ kind
566
+ ): idx_path
567
+ for idx_path in index_paths
568
+ }
569
+
570
+ # Collect results
571
+ for future in as_completed(future_to_path):
572
+ try:
573
+ symbols = future.result()
574
+ all_symbols.extend(symbols)
575
+ except Exception as exc:
576
+ self.logger.error(f"Symbol search failed: {exc}")
577
+
578
+ # Deduplicate by (name, kind, range)
579
+ seen = set()
580
+ unique_symbols = []
581
+ for sym in all_symbols:
582
+ key = (sym.name, sym.kind, sym.range)
583
+ if key not in seen:
584
+ seen.add(key)
585
+ unique_symbols.append(sym)
586
+
587
+ # Sort by name
588
+ unique_symbols.sort(key=lambda s: s.name)
589
+
590
+ return unique_symbols[:limit]
591
+
592
+ def _search_symbols_single(self, index_path: Path,
593
+ name: str,
594
+ kind: Optional[str]) -> List[Symbol]:
595
+ """Search symbols in a single index.
596
+
597
+ Args:
598
+ index_path: Path to _index.db file
599
+ name: Symbol name pattern
600
+ kind: Optional symbol kind filter
601
+
602
+ Returns:
603
+ List of Symbol objects (empty on error)
604
+ """
605
+ try:
606
+ with DirIndexStore(index_path) as store:
607
+ return store.search_symbols(name, kind=kind)
608
+ except Exception as exc:
609
+ self.logger.debug(f"Symbol search error in {index_path}: {exc}")
610
+ return []
611
+
612
+
613
+ # === Convenience Functions ===
614
+
615
+ def quick_search(query: str,
616
+ source_path: Path,
617
+ depth: int = -1) -> List[SearchResult]:
618
+ """Quick search convenience function with automatic initialization.
619
+
620
+ Creates temporary registry and mapper instances for one-off searches.
621
+ For repeated searches, create a ChainSearchEngine instance directly.
622
+
623
+ Args:
624
+ query: FTS5 search query string
625
+ source_path: Starting directory path
626
+ depth: Maximum search depth (-1 = unlimited)
627
+
628
+ Returns:
629
+ List of SearchResult objects sorted by relevance
630
+
631
+ Examples:
632
+ >>> from pathlib import Path
633
+ >>> results = quick_search("authentication", Path("D:/project/src"))
634
+ >>> print(f"Found {len(results)} matches")
635
+ """
636
+ registry = RegistryStore()
637
+ registry.initialize()
638
+
639
+ mapper = PathMapper()
640
+
641
+ with ChainSearchEngine(registry, mapper) as engine:
642
+ options = SearchOptions(depth=depth)
643
+ result = engine.search(query, source_path, options)
644
+
645
+ registry.close()
646
+
647
+ return result.results