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,955 @@
1
+ """Vector storage and similarity search for semantic chunks.
2
+
3
+ Optimized for high-performance similarity search using:
4
+ - HNSW index for O(log N) approximate nearest neighbor search (primary)
5
+ - Cached embedding matrix for batch operations (fallback)
6
+ - NumPy vectorized cosine similarity (fallback, 100x+ faster than loops)
7
+ - Lazy content loading (only fetch for top-k results)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import logging
14
+ import sqlite3
15
+ import threading
16
+ from pathlib import Path
17
+ from typing import Any, Dict, List, Optional, Tuple
18
+
19
+ from codexlens.entities import SearchResult, SemanticChunk
20
+ from codexlens.errors import StorageError
21
+
22
+ from . import SEMANTIC_AVAILABLE
23
+
24
+ if SEMANTIC_AVAILABLE:
25
+ import numpy as np
26
+
27
+ # Try to import ANN index (optional hnswlib dependency)
28
+ try:
29
+ from codexlens.semantic.ann_index import ANNIndex, HNSWLIB_AVAILABLE
30
+ except ImportError:
31
+ HNSWLIB_AVAILABLE = False
32
+ ANNIndex = None
33
+
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ def _cosine_similarity(a: List[float], b: List[float]) -> float:
39
+ """Compute cosine similarity between two vectors."""
40
+ if not SEMANTIC_AVAILABLE:
41
+ raise ImportError("numpy required for vector operations")
42
+
43
+ a_arr = np.array(a)
44
+ b_arr = np.array(b)
45
+
46
+ norm_a = np.linalg.norm(a_arr)
47
+ norm_b = np.linalg.norm(b_arr)
48
+
49
+ if norm_a == 0 or norm_b == 0:
50
+ return 0.0
51
+
52
+ return float(np.dot(a_arr, b_arr) / (norm_a * norm_b))
53
+
54
+
55
+ class VectorStore:
56
+ """SQLite-based vector storage with HNSW-accelerated similarity search.
57
+
58
+ Performance optimizations:
59
+ - HNSW index for O(log N) approximate nearest neighbor search
60
+ - Embedding matrix cached in memory for batch similarity computation (fallback)
61
+ - NumPy vectorized operations instead of Python loops (fallback)
62
+ - Lazy content loading - only fetch full content for top-k results
63
+ - Thread-safe cache invalidation
64
+ - Bulk insert mode for efficient batch operations
65
+ """
66
+
67
+ # Default embedding dimension (used when creating new index)
68
+ DEFAULT_DIM = 768
69
+
70
+ def __init__(self, db_path: str | Path) -> None:
71
+ if not SEMANTIC_AVAILABLE:
72
+ raise ImportError(
73
+ "Semantic search dependencies not available. "
74
+ "Install with: pip install codexlens[semantic]"
75
+ )
76
+
77
+ self.db_path = Path(db_path)
78
+ self.db_path.parent.mkdir(parents=True, exist_ok=True)
79
+
80
+ # Embedding cache for fast similarity search (fallback)
81
+ self._cache_lock = threading.RLock()
82
+ self._embedding_matrix: Optional[np.ndarray] = None
83
+ self._embedding_norms: Optional[np.ndarray] = None
84
+ self._chunk_ids: Optional[List[int]] = None
85
+ self._cache_version: int = 0
86
+
87
+ # ANN index for O(log N) search
88
+ self._ann_index: Optional[ANNIndex] = None
89
+ self._ann_dim: Optional[int] = None
90
+ self._ann_write_lock = threading.Lock() # Protects ANN index modifications
91
+
92
+ # Bulk insert mode tracking
93
+ self._bulk_insert_mode: bool = False
94
+ self._bulk_insert_ids: List[int] = []
95
+ self._bulk_insert_embeddings: List[np.ndarray] = []
96
+
97
+ self._init_schema()
98
+ self._init_ann_index()
99
+
100
+ def _init_schema(self) -> None:
101
+ """Initialize vector storage schema."""
102
+ with sqlite3.connect(self.db_path) as conn:
103
+ # Enable memory mapping for faster reads
104
+ conn.execute("PRAGMA mmap_size = 30000000000") # 30GB limit
105
+ conn.execute("""
106
+ CREATE TABLE IF NOT EXISTS semantic_chunks (
107
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
108
+ file_path TEXT NOT NULL,
109
+ content TEXT NOT NULL,
110
+ embedding BLOB NOT NULL,
111
+ metadata TEXT,
112
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
113
+ )
114
+ """)
115
+ conn.execute("""
116
+ CREATE INDEX IF NOT EXISTS idx_chunks_file
117
+ ON semantic_chunks(file_path)
118
+ """)
119
+ conn.commit()
120
+
121
+ def _init_ann_index(self) -> None:
122
+ """Initialize ANN index (lazy loading from existing data)."""
123
+ if not HNSWLIB_AVAILABLE:
124
+ logger.debug("hnswlib not available, using brute-force search")
125
+ return
126
+
127
+ # Try to detect embedding dimension from existing data
128
+ dim = self._detect_embedding_dim()
129
+ if dim is None:
130
+ # No data yet, will initialize on first add
131
+ logger.debug("No embeddings found, ANN index will be created on first add")
132
+ return
133
+
134
+ self._ann_dim = dim
135
+
136
+ try:
137
+ self._ann_index = ANNIndex(self.db_path, dim)
138
+ if self._ann_index.load():
139
+ logger.debug(
140
+ "Loaded ANN index with %d vectors", self._ann_index.count()
141
+ )
142
+ else:
143
+ # Index file doesn't exist, try to build from SQLite data
144
+ logger.debug("ANN index file not found, rebuilding from SQLite")
145
+ self._rebuild_ann_index_internal()
146
+ except Exception as e:
147
+ logger.warning("Failed to initialize ANN index: %s", e)
148
+ self._ann_index = None
149
+
150
+ def _detect_embedding_dim(self) -> Optional[int]:
151
+ """Detect embedding dimension from existing data."""
152
+ with sqlite3.connect(self.db_path) as conn:
153
+ row = conn.execute(
154
+ "SELECT embedding FROM semantic_chunks LIMIT 1"
155
+ ).fetchone()
156
+ if row and row[0]:
157
+ # Embedding is stored as float32 blob
158
+ blob = row[0]
159
+ return len(blob) // np.dtype(np.float32).itemsize
160
+ return None
161
+
162
+ @property
163
+ def dimension(self) -> Optional[int]:
164
+ """Return the dimension of embeddings in the store.
165
+
166
+ Returns:
167
+ Embedding dimension if available, None if store is empty.
168
+ """
169
+ if self._ann_dim is not None:
170
+ return self._ann_dim
171
+ self._ann_dim = self._detect_embedding_dim()
172
+ return self._ann_dim
173
+
174
+ def _rebuild_ann_index_internal(self) -> int:
175
+ """Internal method to rebuild ANN index from SQLite data."""
176
+ if self._ann_index is None:
177
+ return 0
178
+
179
+ with sqlite3.connect(self.db_path) as conn:
180
+ conn.execute("PRAGMA mmap_size = 30000000000")
181
+ rows = conn.execute(
182
+ "SELECT id, embedding FROM semantic_chunks"
183
+ ).fetchall()
184
+
185
+ if not rows:
186
+ return 0
187
+
188
+ # Extract IDs and embeddings
189
+ ids = [r[0] for r in rows]
190
+ embeddings = np.vstack([
191
+ np.frombuffer(r[1], dtype=np.float32) for r in rows
192
+ ])
193
+
194
+ # Add to ANN index
195
+ self._ann_index.add_vectors(ids, embeddings)
196
+ self._ann_index.save()
197
+
198
+ logger.info("Rebuilt ANN index with %d vectors", len(ids))
199
+ return len(ids)
200
+
201
+ def rebuild_ann_index(self) -> int:
202
+ """Rebuild HNSW index from all chunks in SQLite.
203
+
204
+ Use this method to:
205
+ - Migrate existing data to use ANN search
206
+ - Repair corrupted index
207
+ - Reclaim space after many deletions
208
+
209
+ Returns:
210
+ Number of vectors indexed.
211
+ """
212
+ if not HNSWLIB_AVAILABLE:
213
+ logger.warning("hnswlib not available, cannot rebuild ANN index")
214
+ return 0
215
+
216
+ # Detect dimension
217
+ dim = self._detect_embedding_dim()
218
+ if dim is None:
219
+ logger.warning("No embeddings found, cannot rebuild ANN index")
220
+ return 0
221
+
222
+ self._ann_dim = dim
223
+
224
+ # Create new index
225
+ try:
226
+ self._ann_index = ANNIndex(self.db_path, dim)
227
+ return self._rebuild_ann_index_internal()
228
+ except Exception as e:
229
+ logger.error("Failed to rebuild ANN index: %s", e)
230
+ self._ann_index = None
231
+ return 0
232
+
233
+ def _invalidate_cache(self) -> None:
234
+ """Invalidate the embedding cache (thread-safe)."""
235
+ with self._cache_lock:
236
+ self._embedding_matrix = None
237
+ self._embedding_norms = None
238
+ self._chunk_ids = None
239
+ self._cache_version += 1
240
+
241
+ def _refresh_cache(self) -> bool:
242
+ """Load embeddings into numpy matrix for fast similarity search.
243
+
244
+ Returns:
245
+ True if cache was refreshed successfully, False if no data.
246
+ """
247
+ with self._cache_lock:
248
+ with sqlite3.connect(self.db_path) as conn:
249
+ conn.execute("PRAGMA mmap_size = 30000000000")
250
+ rows = conn.execute(
251
+ "SELECT id, embedding FROM semantic_chunks"
252
+ ).fetchall()
253
+
254
+ if not rows:
255
+ self._embedding_matrix = None
256
+ self._embedding_norms = None
257
+ self._chunk_ids = None
258
+ return False
259
+
260
+ # Extract IDs and embeddings
261
+ self._chunk_ids = [r[0] for r in rows]
262
+
263
+ # Bulk convert binary blobs to numpy matrix
264
+ embeddings = [
265
+ np.frombuffer(r[1], dtype=np.float32) for r in rows
266
+ ]
267
+ self._embedding_matrix = np.vstack(embeddings)
268
+
269
+ # Pre-compute norms for faster similarity calculation
270
+ self._embedding_norms = np.linalg.norm(
271
+ self._embedding_matrix, axis=1, keepdims=True
272
+ )
273
+ # Avoid division by zero
274
+ self._embedding_norms = np.where(
275
+ self._embedding_norms == 0, 1e-10, self._embedding_norms
276
+ )
277
+
278
+ return True
279
+
280
+ def _ensure_ann_index(self, dim: int) -> bool:
281
+ """Ensure ANN index is initialized with correct dimension.
282
+
283
+ This method is thread-safe and uses double-checked locking.
284
+
285
+ Args:
286
+ dim: Embedding dimension
287
+
288
+ Returns:
289
+ True if ANN index is ready, False otherwise
290
+ """
291
+ if not HNSWLIB_AVAILABLE:
292
+ return False
293
+
294
+ # Fast path: index already initialized (no lock needed)
295
+ if self._ann_index is not None:
296
+ return True
297
+
298
+ # Slow path: acquire lock for initialization
299
+ with self._ann_write_lock:
300
+ # Double-check after acquiring lock
301
+ if self._ann_index is not None:
302
+ return True
303
+
304
+ try:
305
+ self._ann_dim = dim
306
+ self._ann_index = ANNIndex(self.db_path, dim)
307
+ self._ann_index.load() # Try to load existing
308
+ return True
309
+ except Exception as e:
310
+ logger.warning("Failed to initialize ANN index: %s", e)
311
+ self._ann_index = None
312
+ return False
313
+
314
+ def add_chunk(self, chunk: SemanticChunk, file_path: str) -> int:
315
+ """Add a single chunk with its embedding.
316
+
317
+ Returns:
318
+ The inserted chunk ID.
319
+ """
320
+ if chunk.embedding is None:
321
+ raise ValueError("Chunk must have embedding before adding to store")
322
+
323
+ embedding_arr = np.array(chunk.embedding, dtype=np.float32)
324
+ embedding_blob = embedding_arr.tobytes()
325
+ metadata_json = json.dumps(chunk.metadata) if chunk.metadata else None
326
+
327
+ with sqlite3.connect(self.db_path) as conn:
328
+ cursor = conn.execute(
329
+ """
330
+ INSERT INTO semantic_chunks (file_path, content, embedding, metadata)
331
+ VALUES (?, ?, ?, ?)
332
+ """,
333
+ (file_path, chunk.content, embedding_blob, metadata_json)
334
+ )
335
+ conn.commit()
336
+ chunk_id = cursor.lastrowid or 0
337
+
338
+ # Add to ANN index
339
+ if self._ensure_ann_index(len(chunk.embedding)):
340
+ with self._ann_write_lock:
341
+ try:
342
+ self._ann_index.add_vectors([chunk_id], embedding_arr.reshape(1, -1))
343
+ self._ann_index.save()
344
+ except Exception as e:
345
+ logger.warning("Failed to add to ANN index: %s", e)
346
+
347
+ # Invalidate cache after modification
348
+ self._invalidate_cache()
349
+ return chunk_id
350
+
351
+ def add_chunks(self, chunks: List[SemanticChunk], file_path: str) -> List[int]:
352
+ """Add multiple chunks with embeddings (batch insert).
353
+
354
+ Returns:
355
+ List of inserted chunk IDs.
356
+ """
357
+ if not chunks:
358
+ return []
359
+
360
+ # Prepare batch data
361
+ batch_data = []
362
+ embeddings_list = []
363
+ for chunk in chunks:
364
+ if chunk.embedding is None:
365
+ raise ValueError("All chunks must have embeddings")
366
+ embedding_arr = np.array(chunk.embedding, dtype=np.float32)
367
+ embedding_blob = embedding_arr.tobytes()
368
+ metadata_json = json.dumps(chunk.metadata) if chunk.metadata else None
369
+ batch_data.append((file_path, chunk.content, embedding_blob, metadata_json))
370
+ embeddings_list.append(embedding_arr)
371
+
372
+ # Batch insert to SQLite
373
+ with sqlite3.connect(self.db_path) as conn:
374
+ # Get starting ID before insert
375
+ row = conn.execute("SELECT MAX(id) FROM semantic_chunks").fetchone()
376
+ start_id = (row[0] or 0) + 1
377
+
378
+ conn.executemany(
379
+ """
380
+ INSERT INTO semantic_chunks (file_path, content, embedding, metadata)
381
+ VALUES (?, ?, ?, ?)
382
+ """,
383
+ batch_data
384
+ )
385
+ conn.commit()
386
+ # Calculate inserted IDs based on starting ID
387
+ ids = list(range(start_id, start_id + len(chunks)))
388
+
389
+ # Add to ANN index
390
+ if embeddings_list and self._ensure_ann_index(len(embeddings_list[0])):
391
+ with self._ann_write_lock:
392
+ try:
393
+ embeddings_matrix = np.vstack(embeddings_list)
394
+ self._ann_index.add_vectors(ids, embeddings_matrix)
395
+ self._ann_index.save()
396
+ except Exception as e:
397
+ logger.warning("Failed to add batch to ANN index: %s", e)
398
+
399
+ # Invalidate cache after modification
400
+ self._invalidate_cache()
401
+ return ids
402
+
403
+ def add_chunks_batch(
404
+ self,
405
+ chunks_with_paths: List[Tuple[SemanticChunk, str]],
406
+ update_ann: bool = True,
407
+ auto_save_ann: bool = True,
408
+ ) -> List[int]:
409
+ """Batch insert chunks from multiple files in a single transaction.
410
+
411
+ This method is optimized for bulk operations during index generation.
412
+
413
+ Args:
414
+ chunks_with_paths: List of (chunk, file_path) tuples
415
+ update_ann: If True, update ANN index with new vectors (default: True)
416
+ auto_save_ann: If True, save ANN index after update (default: True).
417
+ Set to False for bulk inserts to reduce I/O overhead.
418
+
419
+ Returns:
420
+ List of inserted chunk IDs
421
+ """
422
+ if not chunks_with_paths:
423
+ return []
424
+
425
+ # Prepare batch data
426
+ batch_data = []
427
+ embeddings_list = []
428
+ for chunk, file_path in chunks_with_paths:
429
+ if chunk.embedding is None:
430
+ raise ValueError("All chunks must have embeddings")
431
+ # Optimize: avoid repeated np.array() if already numpy
432
+ if isinstance(chunk.embedding, np.ndarray):
433
+ embedding_arr = chunk.embedding.astype(np.float32)
434
+ else:
435
+ embedding_arr = np.array(chunk.embedding, dtype=np.float32)
436
+ embedding_blob = embedding_arr.tobytes()
437
+ metadata_json = json.dumps(chunk.metadata) if chunk.metadata else None
438
+ batch_data.append((file_path, chunk.content, embedding_blob, metadata_json))
439
+ embeddings_list.append(embedding_arr)
440
+
441
+ # Batch insert to SQLite in single transaction
442
+ with sqlite3.connect(self.db_path) as conn:
443
+ # Get starting ID before insert
444
+ row = conn.execute("SELECT MAX(id) FROM semantic_chunks").fetchone()
445
+ start_id = (row[0] or 0) + 1
446
+
447
+ conn.executemany(
448
+ """
449
+ INSERT INTO semantic_chunks (file_path, content, embedding, metadata)
450
+ VALUES (?, ?, ?, ?)
451
+ """,
452
+ batch_data
453
+ )
454
+ conn.commit()
455
+ # Calculate inserted IDs based on starting ID
456
+ ids = list(range(start_id, start_id + len(chunks_with_paths)))
457
+
458
+ # Handle ANN index updates
459
+ if embeddings_list and update_ann and self._ensure_ann_index(len(embeddings_list[0])):
460
+ # In bulk insert mode, accumulate for later batch update
461
+ if self._bulk_insert_mode:
462
+ self._bulk_insert_ids.extend(ids)
463
+ self._bulk_insert_embeddings.extend(embeddings_list)
464
+ else:
465
+ # Normal mode: update immediately
466
+ with self._ann_write_lock:
467
+ try:
468
+ embeddings_matrix = np.vstack(embeddings_list)
469
+ self._ann_index.add_vectors(ids, embeddings_matrix)
470
+ if auto_save_ann:
471
+ self._ann_index.save()
472
+ except Exception as e:
473
+ logger.warning("Failed to add batch to ANN index: %s", e)
474
+
475
+ # Invalidate cache after modification
476
+ self._invalidate_cache()
477
+ return ids
478
+
479
+ def add_chunks_batch_numpy(
480
+ self,
481
+ chunks_with_paths: List[Tuple[SemanticChunk, str]],
482
+ embeddings_matrix: np.ndarray,
483
+ update_ann: bool = True,
484
+ auto_save_ann: bool = True,
485
+ ) -> List[int]:
486
+ """Batch insert chunks with pre-computed numpy embeddings matrix.
487
+
488
+ This method accepts embeddings as a numpy matrix to avoid list->array conversions.
489
+ Useful when embeddings are already in numpy format from batch encoding.
490
+
491
+ Args:
492
+ chunks_with_paths: List of (chunk, file_path) tuples (embeddings can be None)
493
+ embeddings_matrix: Pre-computed embeddings as (N, D) numpy array
494
+ update_ann: If True, update ANN index with new vectors (default: True)
495
+ auto_save_ann: If True, save ANN index after update (default: True)
496
+
497
+ Returns:
498
+ List of inserted chunk IDs
499
+ """
500
+ if not chunks_with_paths:
501
+ return []
502
+
503
+ if len(chunks_with_paths) != embeddings_matrix.shape[0]:
504
+ raise ValueError(
505
+ f"Mismatch: {len(chunks_with_paths)} chunks but "
506
+ f"{embeddings_matrix.shape[0]} embeddings"
507
+ )
508
+
509
+ # Ensure float32 format
510
+ embeddings_matrix = embeddings_matrix.astype(np.float32)
511
+
512
+ # Prepare batch data
513
+ batch_data = []
514
+ for i, (chunk, file_path) in enumerate(chunks_with_paths):
515
+ embedding_arr = embeddings_matrix[i]
516
+ embedding_blob = embedding_arr.tobytes()
517
+ metadata_json = json.dumps(chunk.metadata) if chunk.metadata else None
518
+ batch_data.append((file_path, chunk.content, embedding_blob, metadata_json))
519
+
520
+ # Batch insert to SQLite in single transaction
521
+ with sqlite3.connect(self.db_path) as conn:
522
+ # Get starting ID before insert
523
+ row = conn.execute("SELECT MAX(id) FROM semantic_chunks").fetchone()
524
+ start_id = (row[0] or 0) + 1
525
+
526
+ conn.executemany(
527
+ """
528
+ INSERT INTO semantic_chunks (file_path, content, embedding, metadata)
529
+ VALUES (?, ?, ?, ?)
530
+ """,
531
+ batch_data
532
+ )
533
+ conn.commit()
534
+ # Calculate inserted IDs based on starting ID
535
+ ids = list(range(start_id, start_id + len(chunks_with_paths)))
536
+
537
+ # Handle ANN index updates
538
+ if update_ann and self._ensure_ann_index(embeddings_matrix.shape[1]):
539
+ # In bulk insert mode, accumulate for later batch update
540
+ if self._bulk_insert_mode:
541
+ self._bulk_insert_ids.extend(ids)
542
+ # Split matrix into individual arrays for accumulation
543
+ self._bulk_insert_embeddings.extend([embeddings_matrix[i] for i in range(len(ids))])
544
+ else:
545
+ # Normal mode: update immediately
546
+ with self._ann_write_lock:
547
+ try:
548
+ self._ann_index.add_vectors(ids, embeddings_matrix)
549
+ if auto_save_ann:
550
+ self._ann_index.save()
551
+ except Exception as e:
552
+ logger.warning("Failed to add batch to ANN index: %s", e)
553
+
554
+ # Invalidate cache after modification
555
+ self._invalidate_cache()
556
+ return ids
557
+
558
+ def begin_bulk_insert(self) -> None:
559
+ """Begin bulk insert mode - disable ANN auto-update for better performance.
560
+
561
+ Usage:
562
+ store.begin_bulk_insert()
563
+ try:
564
+ for batch in batches:
565
+ store.add_chunks_batch(batch, auto_save_ann=False)
566
+ finally:
567
+ store.end_bulk_insert()
568
+
569
+ Or use context manager:
570
+ with store.bulk_insert():
571
+ for batch in batches:
572
+ store.add_chunks_batch(batch)
573
+ """
574
+ self._bulk_insert_mode = True
575
+ self._bulk_insert_ids.clear()
576
+ self._bulk_insert_embeddings.clear()
577
+ logger.debug("Entered bulk insert mode")
578
+
579
+ def end_bulk_insert(self) -> None:
580
+ """End bulk insert mode and rebuild ANN index from accumulated data.
581
+
582
+ This method should be called after all bulk inserts are complete to
583
+ update the ANN index in a single batch operation.
584
+ """
585
+ if not self._bulk_insert_mode:
586
+ logger.warning("end_bulk_insert called but not in bulk insert mode")
587
+ return
588
+
589
+ self._bulk_insert_mode = False
590
+
591
+ # Update ANN index with all accumulated data
592
+ if self._bulk_insert_ids and self._bulk_insert_embeddings:
593
+ if self._ensure_ann_index(len(self._bulk_insert_embeddings[0])):
594
+ with self._ann_write_lock:
595
+ try:
596
+ embeddings_matrix = np.vstack(self._bulk_insert_embeddings)
597
+ self._ann_index.add_vectors(self._bulk_insert_ids, embeddings_matrix)
598
+ self._ann_index.save()
599
+ logger.info(
600
+ "Bulk insert complete: added %d vectors to ANN index",
601
+ len(self._bulk_insert_ids)
602
+ )
603
+ except Exception as e:
604
+ logger.error("Failed to update ANN index after bulk insert: %s", e)
605
+
606
+ # Clear accumulated data
607
+ self._bulk_insert_ids.clear()
608
+ self._bulk_insert_embeddings.clear()
609
+ logger.debug("Exited bulk insert mode")
610
+
611
+ class BulkInsertContext:
612
+ """Context manager for bulk insert operations."""
613
+
614
+ def __init__(self, store: "VectorStore") -> None:
615
+ self.store = store
616
+
617
+ def __enter__(self) -> "VectorStore":
618
+ self.store.begin_bulk_insert()
619
+ return self.store
620
+
621
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
622
+ self.store.end_bulk_insert()
623
+
624
+ def bulk_insert(self) -> "VectorStore.BulkInsertContext":
625
+ """Return a context manager for bulk insert operations.
626
+
627
+ Usage:
628
+ with store.bulk_insert():
629
+ for batch in batches:
630
+ store.add_chunks_batch(batch)
631
+ """
632
+ return self.BulkInsertContext(self)
633
+
634
+ def delete_file_chunks(self, file_path: str) -> int:
635
+ """Delete all chunks for a file.
636
+
637
+ Returns:
638
+ Number of deleted chunks.
639
+ """
640
+ # Get chunk IDs before deletion (for ANN index)
641
+ chunk_ids_to_delete = []
642
+ if self._ann_index is not None:
643
+ with sqlite3.connect(self.db_path) as conn:
644
+ rows = conn.execute(
645
+ "SELECT id FROM semantic_chunks WHERE file_path = ?",
646
+ (file_path,)
647
+ ).fetchall()
648
+ chunk_ids_to_delete = [r[0] for r in rows]
649
+
650
+ # Delete from SQLite
651
+ with sqlite3.connect(self.db_path) as conn:
652
+ cursor = conn.execute(
653
+ "DELETE FROM semantic_chunks WHERE file_path = ?",
654
+ (file_path,)
655
+ )
656
+ conn.commit()
657
+ deleted = cursor.rowcount
658
+
659
+ # Remove from ANN index
660
+ if deleted > 0 and self._ann_index is not None and chunk_ids_to_delete:
661
+ with self._ann_write_lock:
662
+ try:
663
+ self._ann_index.remove_vectors(chunk_ids_to_delete)
664
+ self._ann_index.save()
665
+ except Exception as e:
666
+ logger.warning("Failed to remove from ANN index: %s", e)
667
+
668
+ if deleted > 0:
669
+ self._invalidate_cache()
670
+ return deleted
671
+
672
+ def search_similar(
673
+ self,
674
+ query_embedding: List[float],
675
+ top_k: int = 10,
676
+ min_score: float = 0.0,
677
+ return_full_content: bool = True,
678
+ ) -> List[SearchResult]:
679
+ """Find chunks most similar to query embedding.
680
+
681
+ Uses HNSW index for O(log N) search when available, falls back to
682
+ brute-force NumPy search otherwise.
683
+
684
+ Args:
685
+ query_embedding: Query vector.
686
+ top_k: Maximum results to return.
687
+ min_score: Minimum similarity score (0-1).
688
+ return_full_content: If True, return full code block content.
689
+
690
+ Returns:
691
+ List of SearchResult ordered by similarity (highest first).
692
+ """
693
+ query_vec = np.array(query_embedding, dtype=np.float32)
694
+
695
+ # Try HNSW search first (O(log N))
696
+ if (
697
+ HNSWLIB_AVAILABLE
698
+ and self._ann_index is not None
699
+ and self._ann_index.is_loaded
700
+ and self._ann_index.count() > 0
701
+ ):
702
+ try:
703
+ return self._search_with_ann(
704
+ query_vec, top_k, min_score, return_full_content
705
+ )
706
+ except Exception as e:
707
+ logger.warning("ANN search failed, falling back to brute-force: %s", e)
708
+
709
+ # Fallback to brute-force search (O(N))
710
+ return self._search_brute_force(
711
+ query_vec, top_k, min_score, return_full_content
712
+ )
713
+
714
+ def _search_with_ann(
715
+ self,
716
+ query_vec: np.ndarray,
717
+ top_k: int,
718
+ min_score: float,
719
+ return_full_content: bool,
720
+ ) -> List[SearchResult]:
721
+ """Search using HNSW index (O(log N)).
722
+
723
+ Args:
724
+ query_vec: Query vector as numpy array
725
+ top_k: Maximum results to return
726
+ min_score: Minimum similarity score (0-1)
727
+ return_full_content: If True, return full code block content
728
+
729
+ Returns:
730
+ List of SearchResult ordered by similarity (highest first)
731
+ """
732
+ # Limit top_k to available vectors to prevent hnswlib error
733
+ ann_count = self._ann_index.count()
734
+ effective_top_k = min(top_k, ann_count) if ann_count > 0 else 0
735
+
736
+ if effective_top_k == 0:
737
+ return []
738
+
739
+ # HNSW search returns (ids, distances)
740
+ # For cosine space: distance = 1 - similarity
741
+ ids, distances = self._ann_index.search(query_vec, effective_top_k)
742
+
743
+ if not ids:
744
+ return []
745
+
746
+ # Convert distances to similarity scores
747
+ scores = [1.0 - d for d in distances]
748
+
749
+ # Filter by min_score
750
+ filtered = [
751
+ (chunk_id, score)
752
+ for chunk_id, score in zip(ids, scores)
753
+ if score >= min_score
754
+ ]
755
+
756
+ if not filtered:
757
+ return []
758
+
759
+ top_ids = [f[0] for f in filtered]
760
+ top_scores = [f[1] for f in filtered]
761
+
762
+ # Fetch content from SQLite
763
+ return self._fetch_results_by_ids(top_ids, top_scores, return_full_content)
764
+
765
+ def _search_brute_force(
766
+ self,
767
+ query_vec: np.ndarray,
768
+ top_k: int,
769
+ min_score: float,
770
+ return_full_content: bool,
771
+ ) -> List[SearchResult]:
772
+ """Brute-force search using NumPy (O(N) fallback).
773
+
774
+ Args:
775
+ query_vec: Query vector as numpy array
776
+ top_k: Maximum results to return
777
+ min_score: Minimum similarity score (0-1)
778
+ return_full_content: If True, return full code block content
779
+
780
+ Returns:
781
+ List of SearchResult ordered by similarity (highest first)
782
+ """
783
+ logger.warning(
784
+ "Using brute-force vector search (hnswlib not available). "
785
+ "This may cause high memory usage for large indexes. "
786
+ "Install hnswlib for better performance: pip install hnswlib"
787
+ )
788
+
789
+ with self._cache_lock:
790
+ # Refresh cache if needed
791
+ if self._embedding_matrix is None:
792
+ if not self._refresh_cache():
793
+ return [] # No data
794
+
795
+ # Vectorized cosine similarity
796
+ query_vec = query_vec.reshape(1, -1)
797
+ query_norm = np.linalg.norm(query_vec)
798
+ if query_norm == 0:
799
+ return []
800
+
801
+ # Compute all similarities at once: (N,) scores
802
+ # similarity = (A @ B.T) / (||A|| * ||B||)
803
+ dot_products = np.dot(self._embedding_matrix, query_vec.T).flatten()
804
+ scores = dot_products / (self._embedding_norms.flatten() * query_norm)
805
+
806
+ # Filter by min_score and get top-k indices
807
+ valid_mask = scores >= min_score
808
+ valid_indices = np.where(valid_mask)[0]
809
+
810
+ if len(valid_indices) == 0:
811
+ return []
812
+
813
+ # Sort by score descending and take top_k
814
+ valid_scores = scores[valid_indices]
815
+ sorted_order = np.argsort(valid_scores)[::-1][:top_k]
816
+ top_indices = valid_indices[sorted_order]
817
+ top_scores = valid_scores[sorted_order]
818
+
819
+ # Get chunk IDs for top results
820
+ top_ids = [self._chunk_ids[i] for i in top_indices]
821
+
822
+ # Fetch content only for top-k results (lazy loading)
823
+ results = self._fetch_results_by_ids(
824
+ top_ids, top_scores.tolist(), return_full_content
825
+ )
826
+
827
+ return results
828
+
829
+ def _fetch_results_by_ids(
830
+ self,
831
+ chunk_ids: List[int],
832
+ scores: List[float],
833
+ return_full_content: bool,
834
+ ) -> List[SearchResult]:
835
+ """Fetch full result data for specific chunk IDs.
836
+
837
+ Args:
838
+ chunk_ids: List of chunk IDs to fetch.
839
+ scores: Corresponding similarity scores.
840
+ return_full_content: Whether to include full content.
841
+
842
+ Returns:
843
+ List of SearchResult objects.
844
+ """
845
+ if not chunk_ids:
846
+ return []
847
+
848
+ # Build parameterized query for IN clause
849
+ placeholders = ",".join("?" * len(chunk_ids))
850
+ query = f"""
851
+ SELECT id, file_path, content, metadata
852
+ FROM semantic_chunks
853
+ WHERE id IN ({placeholders})
854
+ """
855
+
856
+ with sqlite3.connect(self.db_path) as conn:
857
+ conn.execute("PRAGMA mmap_size = 30000000000")
858
+ rows = conn.execute(query, chunk_ids).fetchall()
859
+
860
+ # Build ID -> row mapping
861
+ id_to_row = {r[0]: r for r in rows}
862
+
863
+ results = []
864
+ for chunk_id, score in zip(chunk_ids, scores):
865
+ row = id_to_row.get(chunk_id)
866
+ if not row:
867
+ continue
868
+
869
+ _, file_path, content, metadata_json = row
870
+ metadata = json.loads(metadata_json) if metadata_json else {}
871
+
872
+ # Build excerpt (short preview)
873
+ excerpt = content[:200] + "..." if len(content) > 200 else content
874
+
875
+ # Extract symbol information from metadata
876
+ symbol_name = metadata.get("symbol_name")
877
+ symbol_kind = metadata.get("symbol_kind")
878
+ start_line = metadata.get("start_line")
879
+ end_line = metadata.get("end_line")
880
+
881
+ # Build Symbol object if we have symbol info
882
+ symbol = None
883
+ if symbol_name and symbol_kind and start_line and end_line:
884
+ try:
885
+ from codexlens.entities import Symbol
886
+ symbol = Symbol(
887
+ name=symbol_name,
888
+ kind=symbol_kind,
889
+ range=(start_line, end_line)
890
+ )
891
+ except Exception:
892
+ pass
893
+
894
+ results.append(SearchResult(
895
+ path=file_path,
896
+ score=score,
897
+ excerpt=excerpt,
898
+ content=content if return_full_content else None,
899
+ symbol=symbol,
900
+ metadata=metadata,
901
+ start_line=start_line,
902
+ end_line=end_line,
903
+ symbol_name=symbol_name,
904
+ symbol_kind=symbol_kind,
905
+ ))
906
+
907
+ return results
908
+
909
+ def count_chunks(self) -> int:
910
+ """Count total chunks in store."""
911
+ with sqlite3.connect(self.db_path) as conn:
912
+ row = conn.execute("SELECT COUNT(*) FROM semantic_chunks").fetchone()
913
+ return row[0] if row else 0
914
+
915
+ def clear_cache(self) -> None:
916
+ """Manually clear the embedding cache."""
917
+ self._invalidate_cache()
918
+
919
+ @property
920
+ def ann_available(self) -> bool:
921
+ """Check if ANN index is available and ready."""
922
+ return (
923
+ HNSWLIB_AVAILABLE
924
+ and self._ann_index is not None
925
+ and self._ann_index.is_loaded
926
+ )
927
+
928
+ @property
929
+ def ann_count(self) -> int:
930
+ """Get number of vectors in ANN index."""
931
+ if self._ann_index is not None:
932
+ return self._ann_index.count()
933
+ return 0
934
+
935
+ def close(self) -> None:
936
+ """Close the vector store and release resources.
937
+
938
+ This ensures SQLite connections are closed and ANN index is cleared,
939
+ allowing temporary files to be deleted on Windows.
940
+ """
941
+ with self._cache_lock:
942
+ self._embedding_matrix = None
943
+ self._embedding_norms = None
944
+ self._chunk_ids = None
945
+
946
+ with self._ann_write_lock:
947
+ self._ann_index = None
948
+
949
+ def __enter__(self) -> "VectorStore":
950
+ """Context manager entry."""
951
+ return self
952
+
953
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
954
+ """Context manager exit - close resources."""
955
+ self.close()