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,670 @@
1
+ """Global project registry for CodexLens - SQLite storage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sqlite3
6
+ import threading
7
+ import time
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ from codexlens.errors import StorageError
13
+
14
+
15
+ @dataclass
16
+ class ProjectInfo:
17
+ """Registered project information."""
18
+
19
+ id: int
20
+ source_root: Path
21
+ index_root: Path
22
+ created_at: float
23
+ last_indexed: float
24
+ total_files: int
25
+ total_dirs: int
26
+ status: str
27
+
28
+
29
+ @dataclass
30
+ class DirMapping:
31
+ """Directory to index path mapping."""
32
+
33
+ id: int
34
+ project_id: int
35
+ source_path: Path
36
+ index_path: Path
37
+ depth: int
38
+ files_count: int
39
+ last_updated: float
40
+
41
+
42
+ class RegistryStore:
43
+ """Global project registry - SQLite storage.
44
+
45
+ Manages indexed projects and directory-to-index path mappings.
46
+ Thread-safe with connection pooling.
47
+ """
48
+
49
+ DEFAULT_DB_PATH = Path.home() / ".codexlens" / "registry.db"
50
+
51
+ def __init__(self, db_path: Path | None = None) -> None:
52
+ self.db_path = (db_path or self.DEFAULT_DB_PATH).resolve()
53
+ self._lock = threading.RLock()
54
+ self._local = threading.local()
55
+ self._pool_lock = threading.Lock()
56
+ self._pool: Dict[int, sqlite3.Connection] = {}
57
+ self._pool_generation = 0
58
+
59
+ def _get_connection(self) -> sqlite3.Connection:
60
+ """Get or create a thread-local database connection."""
61
+ thread_id = threading.get_ident()
62
+ if getattr(self._local, "generation", None) == self._pool_generation:
63
+ conn = getattr(self._local, "conn", None)
64
+ if conn is not None:
65
+ return conn
66
+
67
+ with self._pool_lock:
68
+ conn = self._pool.get(thread_id)
69
+ if conn is None:
70
+ conn = sqlite3.connect(self.db_path, check_same_thread=False)
71
+ conn.row_factory = sqlite3.Row
72
+ conn.execute("PRAGMA journal_mode=WAL")
73
+ conn.execute("PRAGMA synchronous=NORMAL")
74
+ conn.execute("PRAGMA foreign_keys=ON")
75
+ self._pool[thread_id] = conn
76
+
77
+ self._local.conn = conn
78
+ self._local.generation = self._pool_generation
79
+ return conn
80
+
81
+ def close(self) -> None:
82
+ """Close all pooled connections."""
83
+ with self._lock:
84
+ with self._pool_lock:
85
+ for conn in self._pool.values():
86
+ conn.close()
87
+ self._pool.clear()
88
+ self._pool_generation += 1
89
+
90
+ if hasattr(self._local, "conn"):
91
+ self._local.conn = None
92
+ if hasattr(self._local, "generation"):
93
+ self._local.generation = self._pool_generation
94
+
95
+ def __enter__(self) -> RegistryStore:
96
+ self.initialize()
97
+ return self
98
+
99
+ def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
100
+ self.close()
101
+
102
+ def initialize(self) -> None:
103
+ """Create database and schema."""
104
+ with self._lock:
105
+ self.db_path.parent.mkdir(parents=True, exist_ok=True)
106
+ conn = self._get_connection()
107
+ self._create_schema(conn)
108
+
109
+ def _create_schema(self, conn: sqlite3.Connection) -> None:
110
+ """Create database schema."""
111
+ try:
112
+ conn.execute(
113
+ """
114
+ CREATE TABLE IF NOT EXISTS projects (
115
+ id INTEGER PRIMARY KEY,
116
+ source_root TEXT UNIQUE NOT NULL,
117
+ index_root TEXT NOT NULL,
118
+ created_at REAL,
119
+ last_indexed REAL,
120
+ total_files INTEGER DEFAULT 0,
121
+ total_dirs INTEGER DEFAULT 0,
122
+ status TEXT DEFAULT 'active'
123
+ )
124
+ """
125
+ )
126
+
127
+ conn.execute(
128
+ """
129
+ CREATE TABLE IF NOT EXISTS dir_mapping (
130
+ id INTEGER PRIMARY KEY,
131
+ project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
132
+ source_path TEXT NOT NULL,
133
+ index_path TEXT NOT NULL,
134
+ depth INTEGER,
135
+ files_count INTEGER DEFAULT 0,
136
+ last_updated REAL,
137
+ UNIQUE(source_path)
138
+ )
139
+ """
140
+ )
141
+
142
+ conn.execute(
143
+ "CREATE INDEX IF NOT EXISTS idx_dir_source ON dir_mapping(source_path)"
144
+ )
145
+ conn.execute(
146
+ "CREATE INDEX IF NOT EXISTS idx_dir_project ON dir_mapping(project_id)"
147
+ )
148
+ conn.execute(
149
+ "CREATE INDEX IF NOT EXISTS idx_project_source ON projects(source_root)"
150
+ )
151
+
152
+ conn.commit()
153
+ except sqlite3.DatabaseError as exc:
154
+ raise StorageError(f"Failed to initialize registry schema: {exc}") from exc
155
+
156
+ # === Project Operations ===
157
+
158
+ def register_project(self, source_root: Path, index_root: Path) -> ProjectInfo:
159
+ """Register a new project or update existing one.
160
+
161
+ Args:
162
+ source_root: Source code root directory
163
+ index_root: Index storage root directory
164
+
165
+ Returns:
166
+ ProjectInfo for the registered project
167
+ """
168
+ with self._lock:
169
+ conn = self._get_connection()
170
+ source_root_str = str(source_root.resolve())
171
+ index_root_str = str(index_root.resolve())
172
+ now = time.time()
173
+
174
+ conn.execute(
175
+ """
176
+ INSERT INTO projects(source_root, index_root, created_at, last_indexed)
177
+ VALUES(?, ?, ?, ?)
178
+ ON CONFLICT(source_root) DO UPDATE SET
179
+ index_root=excluded.index_root,
180
+ last_indexed=excluded.last_indexed,
181
+ status='active'
182
+ """,
183
+ (source_root_str, index_root_str, now, now),
184
+ )
185
+
186
+ row = conn.execute(
187
+ "SELECT * FROM projects WHERE source_root=?", (source_root_str,)
188
+ ).fetchone()
189
+
190
+ conn.commit()
191
+
192
+ if not row:
193
+ raise StorageError(f"Failed to register project: {source_root}")
194
+
195
+ return self._row_to_project_info(row)
196
+
197
+ def unregister_project(self, source_root: Path) -> bool:
198
+ """Remove a project registration (cascades to directory mappings).
199
+
200
+ Args:
201
+ source_root: Source code root directory
202
+
203
+ Returns:
204
+ True if project was removed, False if not found
205
+ """
206
+ with self._lock:
207
+ conn = self._get_connection()
208
+ source_root_str = str(source_root.resolve())
209
+
210
+ row = conn.execute(
211
+ "SELECT id FROM projects WHERE source_root=?", (source_root_str,)
212
+ ).fetchone()
213
+
214
+ if not row:
215
+ return False
216
+
217
+ conn.execute("DELETE FROM projects WHERE source_root=?", (source_root_str,))
218
+ conn.commit()
219
+ return True
220
+
221
+ def get_project(self, source_root: Path) -> Optional[ProjectInfo]:
222
+ """Get project information by source root.
223
+
224
+ Args:
225
+ source_root: Source code root directory
226
+
227
+ Returns:
228
+ ProjectInfo if found, None otherwise
229
+ """
230
+ with self._lock:
231
+ conn = self._get_connection()
232
+ source_root_str = str(source_root.resolve())
233
+
234
+ row = conn.execute(
235
+ "SELECT * FROM projects WHERE source_root=?", (source_root_str,)
236
+ ).fetchone()
237
+
238
+ return self._row_to_project_info(row) if row else None
239
+
240
+ def get_project_by_id(self, project_id: int) -> Optional[ProjectInfo]:
241
+ """Get project information by ID.
242
+
243
+ Args:
244
+ project_id: Project database ID
245
+
246
+ Returns:
247
+ ProjectInfo if found, None otherwise
248
+ """
249
+ with self._lock:
250
+ conn = self._get_connection()
251
+
252
+ row = conn.execute(
253
+ "SELECT * FROM projects WHERE id=?", (project_id,)
254
+ ).fetchone()
255
+
256
+ return self._row_to_project_info(row) if row else None
257
+
258
+ def list_projects(self, status: Optional[str] = None) -> List[ProjectInfo]:
259
+ """List all registered projects.
260
+
261
+ Args:
262
+ status: Optional status filter ('active', 'stale', 'removed')
263
+
264
+ Returns:
265
+ List of ProjectInfo objects
266
+ """
267
+ with self._lock:
268
+ conn = self._get_connection()
269
+
270
+ if status:
271
+ rows = conn.execute(
272
+ "SELECT * FROM projects WHERE status=? ORDER BY created_at DESC",
273
+ (status,),
274
+ ).fetchall()
275
+ else:
276
+ rows = conn.execute(
277
+ "SELECT * FROM projects ORDER BY created_at DESC"
278
+ ).fetchall()
279
+
280
+ return [self._row_to_project_info(row) for row in rows]
281
+
282
+ def update_project_stats(
283
+ self, source_root: Path, total_files: int, total_dirs: int
284
+ ) -> None:
285
+ """Update project statistics.
286
+
287
+ Args:
288
+ source_root: Source code root directory
289
+ total_files: Total number of indexed files
290
+ total_dirs: Total number of indexed directories
291
+ """
292
+ with self._lock:
293
+ conn = self._get_connection()
294
+ source_root_str = str(source_root.resolve())
295
+
296
+ conn.execute(
297
+ """
298
+ UPDATE projects
299
+ SET total_files=?, total_dirs=?, last_indexed=?
300
+ WHERE source_root=?
301
+ """,
302
+ (total_files, total_dirs, time.time(), source_root_str),
303
+ )
304
+ conn.commit()
305
+
306
+ def set_project_status(self, source_root: Path, status: str) -> None:
307
+ """Set project status.
308
+
309
+ Args:
310
+ source_root: Source code root directory
311
+ status: Status string ('active', 'stale', 'removed')
312
+ """
313
+ with self._lock:
314
+ conn = self._get_connection()
315
+ source_root_str = str(source_root.resolve())
316
+
317
+ conn.execute(
318
+ "UPDATE projects SET status=? WHERE source_root=?",
319
+ (status, source_root_str),
320
+ )
321
+ conn.commit()
322
+
323
+ # === Directory Mapping Operations ===
324
+
325
+ def register_dir(
326
+ self,
327
+ project_id: int,
328
+ source_path: Path,
329
+ index_path: Path,
330
+ depth: int,
331
+ files_count: int = 0,
332
+ ) -> DirMapping:
333
+ """Register a directory mapping.
334
+
335
+ Args:
336
+ project_id: Project database ID
337
+ source_path: Source directory path
338
+ index_path: Index database path
339
+ depth: Directory depth relative to project root
340
+ files_count: Number of files in directory
341
+
342
+ Returns:
343
+ DirMapping for the registered directory
344
+ """
345
+ with self._lock:
346
+ conn = self._get_connection()
347
+ source_path_str = str(source_path.resolve())
348
+ index_path_str = str(index_path.resolve())
349
+ now = time.time()
350
+
351
+ conn.execute(
352
+ """
353
+ INSERT INTO dir_mapping(
354
+ project_id, source_path, index_path, depth, files_count, last_updated
355
+ )
356
+ VALUES(?, ?, ?, ?, ?, ?)
357
+ ON CONFLICT(source_path) DO UPDATE SET
358
+ index_path=excluded.index_path,
359
+ depth=excluded.depth,
360
+ files_count=excluded.files_count,
361
+ last_updated=excluded.last_updated
362
+ """,
363
+ (project_id, source_path_str, index_path_str, depth, files_count, now),
364
+ )
365
+
366
+ row = conn.execute(
367
+ "SELECT * FROM dir_mapping WHERE source_path=?", (source_path_str,)
368
+ ).fetchone()
369
+
370
+ conn.commit()
371
+
372
+ if not row:
373
+ raise StorageError(f"Failed to register directory: {source_path}")
374
+
375
+ return self._row_to_dir_mapping(row)
376
+
377
+ def unregister_dir(self, source_path: Path) -> bool:
378
+ """Remove a directory mapping.
379
+
380
+ Args:
381
+ source_path: Source directory path
382
+
383
+ Returns:
384
+ True if directory was removed, False if not found
385
+ """
386
+ with self._lock:
387
+ conn = self._get_connection()
388
+ source_path_str = str(source_path.resolve())
389
+
390
+ row = conn.execute(
391
+ "SELECT id FROM dir_mapping WHERE source_path=?", (source_path_str,)
392
+ ).fetchone()
393
+
394
+ if not row:
395
+ return False
396
+
397
+ conn.execute("DELETE FROM dir_mapping WHERE source_path=?", (source_path_str,))
398
+ conn.commit()
399
+ return True
400
+
401
+ def find_index_path(self, source_path: Path) -> Optional[Path]:
402
+ """Find index path for a source directory (exact match).
403
+
404
+ Args:
405
+ source_path: Source directory path
406
+
407
+ Returns:
408
+ Index path if found, None otherwise
409
+ """
410
+ with self._lock:
411
+ conn = self._get_connection()
412
+ source_path_str = str(source_path.resolve())
413
+
414
+ row = conn.execute(
415
+ "SELECT index_path FROM dir_mapping WHERE source_path=?",
416
+ (source_path_str,),
417
+ ).fetchone()
418
+
419
+ return Path(row["index_path"]) if row else None
420
+
421
+ def find_nearest_index(self, source_path: Path) -> Optional[DirMapping]:
422
+ """Find nearest indexed ancestor directory.
423
+
424
+ Searches for the closest parent directory that has an index.
425
+ Useful for supporting subdirectory searches.
426
+
427
+ Optimized to use single database query instead of iterating through
428
+ each parent directory level.
429
+
430
+ Args:
431
+ source_path: Source directory or file path
432
+
433
+ Returns:
434
+ DirMapping for nearest ancestor, None if not found
435
+ """
436
+ with self._lock:
437
+ conn = self._get_connection()
438
+ source_path_resolved = source_path.resolve()
439
+
440
+ # Build list of all parent paths from deepest to shallowest
441
+ paths_to_check = []
442
+ current = source_path_resolved
443
+ while True:
444
+ paths_to_check.append(str(current))
445
+ parent = current.parent
446
+ if parent == current: # Reached filesystem root
447
+ break
448
+ current = parent
449
+
450
+ if not paths_to_check:
451
+ return None
452
+
453
+ # Single query with WHERE IN, ordered by path length (longest = nearest)
454
+ placeholders = ','.join('?' * len(paths_to_check))
455
+ query = f"""
456
+ SELECT * FROM dir_mapping
457
+ WHERE source_path IN ({placeholders})
458
+ ORDER BY LENGTH(source_path) DESC
459
+ LIMIT 1
460
+ """
461
+
462
+ row = conn.execute(query, paths_to_check).fetchone()
463
+ return self._row_to_dir_mapping(row) if row else None
464
+
465
+ def find_by_source_path(self, source_path: str) -> Optional[Dict[str, str]]:
466
+ """Find project by source path (exact or nearest match).
467
+
468
+ Searches for a project whose source_root matches or contains
469
+ the given source_path.
470
+
471
+ Args:
472
+ source_path: Source directory path as string
473
+
474
+ Returns:
475
+ Dict with project info including 'index_root', or None if not found
476
+ """
477
+ with self._lock:
478
+ conn = self._get_connection()
479
+ source_path_resolved = str(Path(source_path).resolve())
480
+
481
+ # First try exact match on projects table
482
+ row = conn.execute(
483
+ "SELECT * FROM projects WHERE source_root=?", (source_path_resolved,)
484
+ ).fetchone()
485
+
486
+ if row:
487
+ return {
488
+ "id": str(row["id"]),
489
+ "source_root": row["source_root"],
490
+ "index_root": row["index_root"],
491
+ "status": row["status"] or "active",
492
+ }
493
+
494
+ # Try finding project that contains this path
495
+ # Build list of all parent paths
496
+ paths_to_check = []
497
+ current = Path(source_path_resolved)
498
+ while True:
499
+ paths_to_check.append(str(current))
500
+ parent = current.parent
501
+ if parent == current:
502
+ break
503
+ current = parent
504
+
505
+ if paths_to_check:
506
+ placeholders = ','.join('?' * len(paths_to_check))
507
+ query = f"""
508
+ SELECT * FROM projects
509
+ WHERE source_root IN ({placeholders})
510
+ ORDER BY LENGTH(source_root) DESC
511
+ LIMIT 1
512
+ """
513
+ row = conn.execute(query, paths_to_check).fetchone()
514
+
515
+ if row:
516
+ return {
517
+ "id": str(row["id"]),
518
+ "source_root": row["source_root"],
519
+ "index_root": row["index_root"],
520
+ "status": row["status"] or "active",
521
+ }
522
+
523
+ return None
524
+
525
+ def get_project_dirs(self, project_id: int) -> List[DirMapping]:
526
+ """Get all directory mappings for a project.
527
+
528
+ Args:
529
+ project_id: Project database ID
530
+
531
+ Returns:
532
+ List of DirMapping objects
533
+ """
534
+ with self._lock:
535
+ conn = self._get_connection()
536
+
537
+ rows = conn.execute(
538
+ "SELECT * FROM dir_mapping WHERE project_id=? ORDER BY depth, source_path",
539
+ (project_id,),
540
+ ).fetchall()
541
+
542
+ return [self._row_to_dir_mapping(row) for row in rows]
543
+
544
+ def get_subdirs(self, source_path: Path) -> List[DirMapping]:
545
+ """Get direct subdirectory mappings.
546
+
547
+ Args:
548
+ source_path: Parent directory path
549
+
550
+ Returns:
551
+ List of DirMapping objects for direct children
552
+ """
553
+ with self._lock:
554
+ conn = self._get_connection()
555
+ source_path_str = str(source_path.resolve())
556
+
557
+ # First get the parent's depth
558
+ parent_row = conn.execute(
559
+ "SELECT depth, project_id FROM dir_mapping WHERE source_path=?",
560
+ (source_path_str,),
561
+ ).fetchone()
562
+
563
+ if not parent_row:
564
+ return []
565
+
566
+ parent_depth = int(parent_row["depth"])
567
+ project_id = int(parent_row["project_id"])
568
+
569
+ # Get all subdirs with depth = parent_depth + 1 and matching path prefix
570
+ rows = conn.execute(
571
+ """
572
+ SELECT * FROM dir_mapping
573
+ WHERE project_id=? AND depth=? AND source_path LIKE ?
574
+ ORDER BY source_path
575
+ """,
576
+ (project_id, parent_depth + 1, f"{source_path_str}%"),
577
+ ).fetchall()
578
+
579
+ return [self._row_to_dir_mapping(row) for row in rows]
580
+
581
+ def update_dir_stats(self, source_path: Path, files_count: int) -> None:
582
+ """Update directory statistics.
583
+
584
+ Args:
585
+ source_path: Source directory path
586
+ files_count: Number of files in directory
587
+ """
588
+ with self._lock:
589
+ conn = self._get_connection()
590
+ source_path_str = str(source_path.resolve())
591
+
592
+ conn.execute(
593
+ """
594
+ UPDATE dir_mapping
595
+ SET files_count=?, last_updated=?
596
+ WHERE source_path=?
597
+ """,
598
+ (files_count, time.time(), source_path_str),
599
+ )
600
+ conn.commit()
601
+
602
+ def update_index_paths(self, old_root: Path, new_root: Path) -> int:
603
+ """Update all index paths after migration.
604
+
605
+ Replaces old_root prefix with new_root in all stored index paths.
606
+
607
+ Args:
608
+ old_root: Old index root directory
609
+ new_root: New index root directory
610
+
611
+ Returns:
612
+ Number of paths updated
613
+ """
614
+ with self._lock:
615
+ conn = self._get_connection()
616
+ old_root_str = str(old_root.resolve())
617
+ new_root_str = str(new_root.resolve())
618
+ updated = 0
619
+
620
+ # Update projects
621
+ conn.execute(
622
+ """
623
+ UPDATE projects
624
+ SET index_root = REPLACE(index_root, ?, ?)
625
+ WHERE index_root LIKE ?
626
+ """,
627
+ (old_root_str, new_root_str, f"{old_root_str}%"),
628
+ )
629
+ updated += conn.total_changes
630
+
631
+ # Update dir_mapping
632
+ conn.execute(
633
+ """
634
+ UPDATE dir_mapping
635
+ SET index_path = REPLACE(index_path, ?, ?)
636
+ WHERE index_path LIKE ?
637
+ """,
638
+ (old_root_str, new_root_str, f"{old_root_str}%"),
639
+ )
640
+ updated += conn.total_changes
641
+
642
+ conn.commit()
643
+ return updated
644
+
645
+ # === Internal Methods ===
646
+
647
+ def _row_to_project_info(self, row: sqlite3.Row) -> ProjectInfo:
648
+ """Convert database row to ProjectInfo."""
649
+ return ProjectInfo(
650
+ id=int(row["id"]),
651
+ source_root=Path(row["source_root"]),
652
+ index_root=Path(row["index_root"]),
653
+ created_at=float(row["created_at"]) if row["created_at"] else 0.0,
654
+ last_indexed=float(row["last_indexed"]) if row["last_indexed"] else 0.0,
655
+ total_files=int(row["total_files"]) if row["total_files"] else 0,
656
+ total_dirs=int(row["total_dirs"]) if row["total_dirs"] else 0,
657
+ status=str(row["status"]) if row["status"] else "active",
658
+ )
659
+
660
+ def _row_to_dir_mapping(self, row: sqlite3.Row) -> DirMapping:
661
+ """Convert database row to DirMapping."""
662
+ return DirMapping(
663
+ id=int(row["id"]),
664
+ project_id=int(row["project_id"]),
665
+ source_path=Path(row["source_path"]),
666
+ index_path=Path(row["index_path"]),
667
+ depth=int(row["depth"]) if row["depth"] is not None else 0,
668
+ files_count=int(row["files_count"]) if row["files_count"] else 0,
669
+ last_updated=float(row["last_updated"]) if row["last_updated"] else 0.0,
670
+ )