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,795 @@
1
+ /**
2
+ * Native Session Discovery - Discovers and tracks native CLI tool sessions
3
+ * Supports Gemini, Qwen, and Codex session formats
4
+ */
5
+
6
+ import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
7
+ import { join, basename, resolve } from 'path';
8
+ // basename is used for extracting session ID from filename
9
+ import { createHash } from 'crypto';
10
+ import { homedir } from 'os';
11
+
12
+ // Types
13
+ export interface NativeSession {
14
+ sessionId: string; // Native UUID
15
+ tool: string; // gemini | qwen | codex
16
+ filePath: string; // Full path to session file
17
+ projectHash?: string; // Project directory hash (Gemini/Qwen)
18
+ createdAt: Date;
19
+ updatedAt: Date;
20
+ }
21
+
22
+ export interface SessionDiscoveryOptions {
23
+ workingDir?: string; // Project working directory
24
+ limit?: number; // Max sessions to return
25
+ afterTimestamp?: Date; // Only sessions after this time
26
+ }
27
+
28
+ /**
29
+ * Calculate project hash (same algorithm as Gemini/Qwen)
30
+ * Note: Gemini/Qwen use the absolute path AS-IS without normalization
31
+ * On Windows, this means using backslashes and original case
32
+ */
33
+ export function calculateProjectHash(projectDir: string): string {
34
+ // resolve() returns absolute path with native separators (backslash on Windows)
35
+ const absolutePath = resolve(projectDir);
36
+ return createHash('sha256').update(absolutePath).digest('hex');
37
+ }
38
+
39
+ /**
40
+ * Get home directory path
41
+ */
42
+ function getHomePath(): string {
43
+ return homedir().replace(/\\/g, '/');
44
+ }
45
+
46
+ /**
47
+ * Base session discoverer interface
48
+ */
49
+ abstract class SessionDiscoverer {
50
+ abstract tool: string;
51
+ abstract basePath: string;
52
+
53
+ /**
54
+ * Get all sessions for a project
55
+ */
56
+ abstract getSessions(options?: SessionDiscoveryOptions): NativeSession[];
57
+
58
+ /**
59
+ * Get the latest session
60
+ */
61
+ getLatestSession(options?: SessionDiscoveryOptions): NativeSession | null {
62
+ const sessions = this.getSessions({ ...options, limit: 1 });
63
+ return sessions.length > 0 ? sessions[0] : null;
64
+ }
65
+
66
+ /**
67
+ * Find session by ID
68
+ */
69
+ abstract findSessionById(sessionId: string): NativeSession | null;
70
+
71
+ /**
72
+ * Track new session created during execution
73
+ * @param beforeTimestamp - Filter sessions created after this time
74
+ * @param workingDir - Project working directory
75
+ * @param prompt - Optional prompt content for precise matching (fallback)
76
+ */
77
+ async trackNewSession(
78
+ beforeTimestamp: Date,
79
+ workingDir: string,
80
+ prompt?: string
81
+ ): Promise<NativeSession | null> {
82
+ const sessions = this.getSessions({
83
+ workingDir,
84
+ afterTimestamp: beforeTimestamp,
85
+ limit: 10 // Get more candidates for prompt matching
86
+ });
87
+
88
+ if (sessions.length === 0) return null;
89
+
90
+ // If only one session or no prompt provided, return the latest
91
+ if (sessions.length === 1 || !prompt) {
92
+ return sessions[0];
93
+ }
94
+
95
+ // Try to match by prompt content (fallback for parallel execution)
96
+ const matched = this.matchSessionByPrompt(sessions, prompt);
97
+ return matched || sessions[0]; // Fallback to latest if no match
98
+ }
99
+
100
+ /**
101
+ * Match session by prompt content
102
+ * Searches for the prompt in session's user messages
103
+ */
104
+ matchSessionByPrompt(sessions: NativeSession[], prompt: string): NativeSession | null {
105
+ // Normalize prompt for comparison (first 200 chars)
106
+ const promptPrefix = prompt.substring(0, 200).trim();
107
+ if (!promptPrefix) return null;
108
+
109
+ for (const session of sessions) {
110
+ try {
111
+ const userMessage = this.extractFirstUserMessage(session.filePath);
112
+ if (userMessage && userMessage.includes(promptPrefix)) {
113
+ return session;
114
+ }
115
+ } catch {
116
+ // Skip sessions that can't be read
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+
122
+ /**
123
+ * Extract first user message from session file
124
+ * Override in subclass for tool-specific format
125
+ */
126
+ abstract extractFirstUserMessage(filePath: string): string | null;
127
+ }
128
+
129
+ /**
130
+ * Gemini Session Discoverer
131
+ * Path: ~/.gemini/tmp/<projectHash>/chats/session-*.json
132
+ */
133
+ class GeminiSessionDiscoverer extends SessionDiscoverer {
134
+ tool = 'gemini';
135
+ basePath = join(getHomePath(), '.gemini', 'tmp');
136
+
137
+ getSessions(options: SessionDiscoveryOptions = {}): NativeSession[] {
138
+ const { workingDir, limit, afterTimestamp } = options;
139
+ const sessions: NativeSession[] = [];
140
+
141
+ try {
142
+ if (!existsSync(this.basePath)) return [];
143
+
144
+ // If workingDir provided, only look in that project's folder
145
+ let projectDirs: string[];
146
+ if (workingDir) {
147
+ const projectHash = calculateProjectHash(workingDir);
148
+ const projectPath = join(this.basePath, projectHash);
149
+ projectDirs = existsSync(projectPath) ? [projectHash] : [];
150
+ } else {
151
+ projectDirs = readdirSync(this.basePath).filter(d => {
152
+ const fullPath = join(this.basePath, d);
153
+ return statSync(fullPath).isDirectory();
154
+ });
155
+ }
156
+
157
+ for (const projectHash of projectDirs) {
158
+ const chatsDir = join(this.basePath, projectHash, 'chats');
159
+ if (!existsSync(chatsDir)) continue;
160
+
161
+ const sessionFiles = readdirSync(chatsDir)
162
+ .filter(f => f.startsWith('session-') && f.endsWith('.json'))
163
+ .map(f => ({
164
+ name: f,
165
+ path: join(chatsDir, f),
166
+ stat: statSync(join(chatsDir, f))
167
+ }))
168
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
169
+
170
+ for (const file of sessionFiles) {
171
+ if (afterTimestamp && file.stat.mtime <= afterTimestamp) continue;
172
+
173
+ try {
174
+ const content = JSON.parse(readFileSync(file.path, 'utf8'));
175
+ sessions.push({
176
+ sessionId: content.sessionId,
177
+ tool: this.tool,
178
+ filePath: file.path,
179
+ projectHash,
180
+ createdAt: new Date(content.startTime || file.stat.birthtime),
181
+ updatedAt: new Date(content.lastUpdated || file.stat.mtime)
182
+ });
183
+ } catch {
184
+ // Skip invalid files
185
+ }
186
+ }
187
+ }
188
+
189
+ // Sort by updatedAt descending
190
+ sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
191
+
192
+ return limit ? sessions.slice(0, limit) : sessions;
193
+ } catch {
194
+ return [];
195
+ }
196
+ }
197
+
198
+ findSessionById(sessionId: string): NativeSession | null {
199
+ const sessions = this.getSessions();
200
+ return sessions.find(s => s.sessionId === sessionId) || null;
201
+ }
202
+
203
+ /**
204
+ * Extract first user message from Gemini session file
205
+ * Format: { "messages": [{ "type": "user", "content": "..." }] }
206
+ */
207
+ extractFirstUserMessage(filePath: string): string | null {
208
+ try {
209
+ const content = JSON.parse(readFileSync(filePath, 'utf8'));
210
+ if (content.messages && Array.isArray(content.messages)) {
211
+ const userMsg = content.messages.find((m: { type: string }) => m.type === 'user');
212
+ return userMsg?.content || null;
213
+ }
214
+ return null;
215
+ } catch {
216
+ return null;
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Encode a path to Qwen's project folder name format
223
+ * D:\Claude_dms3 -> D--Claude-dms3
224
+ * Rules: : -> -, \ -> -, _ -> -
225
+ */
226
+ function encodeQwenProjectPath(projectDir: string): string {
227
+ const absolutePath = resolve(projectDir);
228
+ // Replace : -> -, \ -> -, _ -> -
229
+ return absolutePath
230
+ .replace(/:/g, '-')
231
+ .replace(/\\/g, '-')
232
+ .replace(/_/g, '-');
233
+ }
234
+
235
+ /**
236
+ * Qwen Session Discoverer
237
+ * New path: ~/.qwen/projects/<path-encoded>/chats/<uuid>.jsonl
238
+ * Old path: ~/.qwen/tmp/<projectHash>/chats/session-*.json (deprecated, fallback)
239
+ */
240
+ class QwenSessionDiscoverer extends SessionDiscoverer {
241
+ tool = 'qwen';
242
+ basePath = join(getHomePath(), '.qwen', 'projects');
243
+ legacyBasePath = join(getHomePath(), '.qwen', 'tmp');
244
+
245
+ getSessions(options: SessionDiscoveryOptions = {}): NativeSession[] {
246
+ const { workingDir, limit, afterTimestamp } = options;
247
+ const sessions: NativeSession[] = [];
248
+
249
+ // Try new format first (projects folder)
250
+ try {
251
+ if (existsSync(this.basePath)) {
252
+ let projectDirs: string[];
253
+ if (workingDir) {
254
+ const encodedPath = encodeQwenProjectPath(workingDir);
255
+ const projectPath = join(this.basePath, encodedPath);
256
+ projectDirs = existsSync(projectPath) ? [encodedPath] : [];
257
+ } else {
258
+ projectDirs = readdirSync(this.basePath).filter(d => {
259
+ const fullPath = join(this.basePath, d);
260
+ return statSync(fullPath).isDirectory();
261
+ });
262
+ }
263
+
264
+ for (const projectFolder of projectDirs) {
265
+ const chatsDir = join(this.basePath, projectFolder, 'chats');
266
+ if (!existsSync(chatsDir)) continue;
267
+
268
+ // New format: <uuid>.jsonl files
269
+ const sessionFiles = readdirSync(chatsDir)
270
+ .filter(f => f.endsWith('.jsonl'))
271
+ .map(f => ({
272
+ name: f,
273
+ path: join(chatsDir, f),
274
+ stat: statSync(join(chatsDir, f))
275
+ }))
276
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
277
+
278
+ for (const file of sessionFiles) {
279
+ if (afterTimestamp && file.stat.mtime <= afterTimestamp) continue;
280
+
281
+ try {
282
+ // Parse JSONL - read first line for session info
283
+ const content = readFileSync(file.path, 'utf8');
284
+ const firstLine = content.split('\n')[0];
285
+ const firstEntry = JSON.parse(firstLine);
286
+
287
+ // Session ID is in the filename or first entry
288
+ const sessionId = firstEntry.sessionId || basename(file.name, '.jsonl');
289
+
290
+ // Find timestamp from entries
291
+ let createdAt = file.stat.birthtime;
292
+ let updatedAt = file.stat.mtime;
293
+
294
+ if (firstEntry.timestamp) {
295
+ createdAt = new Date(firstEntry.timestamp);
296
+ }
297
+
298
+ // Get last entry for updatedAt
299
+ const lines = content.trim().split('\n').filter(l => l.trim());
300
+ if (lines.length > 0) {
301
+ try {
302
+ const lastEntry = JSON.parse(lines[lines.length - 1]);
303
+ if (lastEntry.timestamp) {
304
+ updatedAt = new Date(lastEntry.timestamp);
305
+ }
306
+ } catch { /* ignore */ }
307
+ }
308
+
309
+ sessions.push({
310
+ sessionId,
311
+ tool: this.tool,
312
+ filePath: file.path,
313
+ projectHash: projectFolder, // Using encoded path as project identifier
314
+ createdAt,
315
+ updatedAt
316
+ });
317
+ } catch {
318
+ // Skip invalid files
319
+ }
320
+ }
321
+ }
322
+ }
323
+ } catch { /* ignore errors */ }
324
+
325
+ // Fallback to legacy format (tmp folder with hash)
326
+ try {
327
+ if (existsSync(this.legacyBasePath)) {
328
+ let projectDirs: string[];
329
+ if (workingDir) {
330
+ const projectHash = calculateProjectHash(workingDir);
331
+ const projectPath = join(this.legacyBasePath, projectHash);
332
+ projectDirs = existsSync(projectPath) ? [projectHash] : [];
333
+ } else {
334
+ projectDirs = readdirSync(this.legacyBasePath).filter(d => {
335
+ const fullPath = join(this.legacyBasePath, d);
336
+ return statSync(fullPath).isDirectory();
337
+ });
338
+ }
339
+
340
+ for (const projectHash of projectDirs) {
341
+ const chatsDir = join(this.legacyBasePath, projectHash, 'chats');
342
+ if (!existsSync(chatsDir)) continue;
343
+
344
+ const sessionFiles = readdirSync(chatsDir)
345
+ .filter(f => f.startsWith('session-') && f.endsWith('.json'))
346
+ .map(f => ({
347
+ name: f,
348
+ path: join(chatsDir, f),
349
+ stat: statSync(join(chatsDir, f))
350
+ }))
351
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
352
+
353
+ for (const file of sessionFiles) {
354
+ if (afterTimestamp && file.stat.mtime <= afterTimestamp) continue;
355
+
356
+ try {
357
+ const content = JSON.parse(readFileSync(file.path, 'utf8'));
358
+ sessions.push({
359
+ sessionId: content.sessionId,
360
+ tool: this.tool,
361
+ filePath: file.path,
362
+ projectHash,
363
+ createdAt: new Date(content.startTime || file.stat.birthtime),
364
+ updatedAt: new Date(content.lastUpdated || file.stat.mtime)
365
+ });
366
+ } catch {
367
+ // Skip invalid files
368
+ }
369
+ }
370
+ }
371
+ }
372
+ } catch { /* ignore errors */ }
373
+
374
+ // Sort by updatedAt descending and dedupe by sessionId
375
+ sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
376
+
377
+ // Dedupe (new format takes precedence as it's checked first)
378
+ const seen = new Set<string>();
379
+ const uniqueSessions = sessions.filter(s => {
380
+ if (seen.has(s.sessionId)) return false;
381
+ seen.add(s.sessionId);
382
+ return true;
383
+ });
384
+
385
+ return limit ? uniqueSessions.slice(0, limit) : uniqueSessions;
386
+ }
387
+
388
+ findSessionById(sessionId: string): NativeSession | null {
389
+ const sessions = this.getSessions();
390
+ return sessions.find(s => s.sessionId === sessionId) || null;
391
+ }
392
+
393
+ /**
394
+ * Extract first user message from Qwen session file
395
+ * New format (.jsonl): { type: "user", message: { role: "user", parts: [{ text: "..." }] } }
396
+ * Legacy format (.json): { "messages": [{ "type": "user", "content": "..." }] }
397
+ */
398
+ extractFirstUserMessage(filePath: string): string | null {
399
+ try {
400
+ const content = readFileSync(filePath, 'utf8');
401
+
402
+ // Check if JSONL (new format) or JSON (legacy)
403
+ if (filePath.endsWith('.jsonl')) {
404
+ // JSONL format - find first user message
405
+ const lines = content.split('\n').filter(l => l.trim());
406
+ for (const line of lines) {
407
+ try {
408
+ const entry = JSON.parse(line);
409
+ // New Qwen format: { type: "user", message: { parts: [{ text: "..." }] } }
410
+ if (entry.type === 'user' && entry.message?.parts?.[0]?.text) {
411
+ return entry.message.parts[0].text;
412
+ }
413
+ // Alternative format
414
+ if (entry.role === 'user' && entry.content) {
415
+ return entry.content;
416
+ }
417
+ } catch { /* skip invalid lines */ }
418
+ }
419
+ } else {
420
+ // Legacy JSON format
421
+ const data = JSON.parse(content);
422
+ if (data.messages && Array.isArray(data.messages)) {
423
+ const userMsg = data.messages.find((m: { type: string }) => m.type === 'user');
424
+ return userMsg?.content || null;
425
+ }
426
+ }
427
+ return null;
428
+ } catch {
429
+ return null;
430
+ }
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Codex Session Discoverer
436
+ * Path: ~/.codex/sessions/YYYY/MM/DD/rollout-*-<uuid>.jsonl
437
+ */
438
+ class CodexSessionDiscoverer extends SessionDiscoverer {
439
+ tool = 'codex';
440
+ basePath = join(getHomePath(), '.codex', 'sessions');
441
+
442
+ getSessions(options: SessionDiscoveryOptions = {}): NativeSession[] {
443
+ const { limit, afterTimestamp } = options;
444
+ const sessions: NativeSession[] = [];
445
+
446
+ try {
447
+ if (!existsSync(this.basePath)) return [];
448
+
449
+ // Get year directories (e.g., 2025)
450
+ const yearDirs = readdirSync(this.basePath)
451
+ .filter(d => /^\d{4}$/.test(d))
452
+ .sort((a, b) => b.localeCompare(a)); // Descending
453
+
454
+ for (const year of yearDirs) {
455
+ const yearPath = join(this.basePath, year);
456
+ if (!statSync(yearPath).isDirectory()) continue;
457
+
458
+ // Get month directories
459
+ const monthDirs = readdirSync(yearPath)
460
+ .filter(d => /^\d{2}$/.test(d))
461
+ .sort((a, b) => b.localeCompare(a));
462
+
463
+ for (const month of monthDirs) {
464
+ const monthPath = join(yearPath, month);
465
+ if (!statSync(monthPath).isDirectory()) continue;
466
+
467
+ // Get day directories
468
+ const dayDirs = readdirSync(monthPath)
469
+ .filter(d => /^\d{2}$/.test(d))
470
+ .sort((a, b) => b.localeCompare(a));
471
+
472
+ for (const day of dayDirs) {
473
+ const dayPath = join(monthPath, day);
474
+ if (!statSync(dayPath).isDirectory()) continue;
475
+
476
+ // Get session files
477
+ const sessionFiles = readdirSync(dayPath)
478
+ .filter(f => f.startsWith('rollout-') && f.endsWith('.jsonl'))
479
+ .map(f => ({
480
+ name: f,
481
+ path: join(dayPath, f),
482
+ stat: statSync(join(dayPath, f))
483
+ }))
484
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
485
+
486
+ for (const file of sessionFiles) {
487
+ if (afterTimestamp && file.stat.mtime <= afterTimestamp) continue;
488
+
489
+ try {
490
+ // Parse first line for session_meta
491
+ const firstLine = readFileSync(file.path, 'utf8').split('\n')[0];
492
+ const meta = JSON.parse(firstLine);
493
+
494
+ if (meta.type === 'session_meta' && meta.payload?.id) {
495
+ sessions.push({
496
+ sessionId: meta.payload.id,
497
+ tool: this.tool,
498
+ filePath: file.path,
499
+ createdAt: new Date(meta.payload.timestamp || file.stat.birthtime),
500
+ updatedAt: file.stat.mtime
501
+ });
502
+ }
503
+ } catch {
504
+ // Try extracting UUID from filename
505
+ const uuidMatch = file.name.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i);
506
+ if (uuidMatch) {
507
+ sessions.push({
508
+ sessionId: uuidMatch[1],
509
+ tool: this.tool,
510
+ filePath: file.path,
511
+ createdAt: file.stat.birthtime,
512
+ updatedAt: file.stat.mtime
513
+ });
514
+ }
515
+ }
516
+ }
517
+ }
518
+ }
519
+ }
520
+
521
+ sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
522
+ return limit ? sessions.slice(0, limit) : sessions;
523
+ } catch {
524
+ return [];
525
+ }
526
+ }
527
+
528
+ findSessionById(sessionId: string): NativeSession | null {
529
+ const sessions = this.getSessions();
530
+ return sessions.find(s => s.sessionId === sessionId) || null;
531
+ }
532
+
533
+ /**
534
+ * Extract first user message from Codex session file (.jsonl)
535
+ * Format: {"type":"event_msg","payload":{"type":"user_message","message":"..."}}
536
+ */
537
+ extractFirstUserMessage(filePath: string): string | null {
538
+ try {
539
+ const content = readFileSync(filePath, 'utf8');
540
+ const lines = content.split('\n').filter(l => l.trim());
541
+
542
+ for (const line of lines) {
543
+ try {
544
+ const entry = JSON.parse(line);
545
+ // Look for user_message event
546
+ if (entry.type === 'event_msg' &&
547
+ entry.payload?.type === 'user_message' &&
548
+ entry.payload?.message) {
549
+ return entry.payload.message;
550
+ }
551
+ } catch { /* skip invalid lines */ }
552
+ }
553
+ return null;
554
+ } catch {
555
+ return null;
556
+ }
557
+ }
558
+ }
559
+
560
+ /**
561
+ * Claude Code Session Discoverer
562
+ * Path: ~/.claude/projects/<projectHash>/sessions/*.jsonl
563
+ * Claude Code stores sessions with UUID-based session IDs
564
+ */
565
+ class ClaudeSessionDiscoverer extends SessionDiscoverer {
566
+ tool = 'claude';
567
+ basePath = join(getHomePath(), '.claude', 'projects');
568
+
569
+ getSessions(options: SessionDiscoveryOptions = {}): NativeSession[] {
570
+ const { workingDir, limit, afterTimestamp } = options;
571
+ const sessions: NativeSession[] = [];
572
+
573
+ try {
574
+ if (!existsSync(this.basePath)) return [];
575
+
576
+ // If workingDir provided, only look in that project's folder
577
+ let projectDirs: string[];
578
+ if (workingDir) {
579
+ const projectHash = calculateProjectHash(workingDir);
580
+ const projectPath = join(this.basePath, projectHash);
581
+ projectDirs = existsSync(projectPath) ? [projectHash] : [];
582
+ } else {
583
+ projectDirs = readdirSync(this.basePath).filter(d => {
584
+ const fullPath = join(this.basePath, d);
585
+ return statSync(fullPath).isDirectory();
586
+ });
587
+ }
588
+
589
+ for (const projectHash of projectDirs) {
590
+ // Claude Code stores session files directly in project folder (not in 'sessions' subdirectory)
591
+ // e.g., ~/.claude/projects/D--Claude-dms3/<uuid>.jsonl
592
+ const projectDir = join(this.basePath, projectHash);
593
+ if (!existsSync(projectDir)) continue;
594
+
595
+ const sessionFiles = readdirSync(projectDir)
596
+ .filter(f => f.endsWith('.jsonl') || f.endsWith('.json'))
597
+ .map(f => ({
598
+ name: f,
599
+ path: join(projectDir, f),
600
+ stat: statSync(join(projectDir, f))
601
+ }))
602
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
603
+
604
+ for (const file of sessionFiles) {
605
+ if (afterTimestamp && file.stat.mtime <= afterTimestamp) continue;
606
+
607
+ try {
608
+ // Extract session ID from filename or content
609
+ const uuidMatch = file.name.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
610
+ if (uuidMatch) {
611
+ sessions.push({
612
+ sessionId: uuidMatch[1],
613
+ tool: this.tool,
614
+ filePath: file.path,
615
+ projectHash,
616
+ createdAt: file.stat.birthtime,
617
+ updatedAt: file.stat.mtime
618
+ });
619
+ } else {
620
+ // Try reading first line for session metadata
621
+ const firstLine = readFileSync(file.path, 'utf8').split('\n')[0];
622
+ const meta = JSON.parse(firstLine);
623
+ if (meta.session_id) {
624
+ sessions.push({
625
+ sessionId: meta.session_id,
626
+ tool: this.tool,
627
+ filePath: file.path,
628
+ projectHash,
629
+ createdAt: new Date(meta.timestamp || file.stat.birthtime),
630
+ updatedAt: file.stat.mtime
631
+ });
632
+ }
633
+ }
634
+ } catch {
635
+ // Skip invalid files
636
+ }
637
+ }
638
+ }
639
+
640
+ sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
641
+ return limit ? sessions.slice(0, limit) : sessions;
642
+ } catch {
643
+ return [];
644
+ }
645
+ }
646
+
647
+ findSessionById(sessionId: string): NativeSession | null {
648
+ const sessions = this.getSessions();
649
+ return sessions.find(s => s.sessionId === sessionId) || null;
650
+ }
651
+
652
+ /**
653
+ * Extract first user message from Claude Code session file (.jsonl)
654
+ * Format: {"type":"user","message":{"role":"user","content":"..."},"isMeta":false,...}
655
+ */
656
+ extractFirstUserMessage(filePath: string): string | null {
657
+ try {
658
+ const content = readFileSync(filePath, 'utf8');
659
+ const lines = content.split('\n').filter(l => l.trim());
660
+
661
+ for (const line of lines) {
662
+ try {
663
+ const entry = JSON.parse(line);
664
+ // Claude Code format: type="user", message.role="user", message.content="..."
665
+ // Skip meta messages and command messages
666
+ if (entry.type === 'user' &&
667
+ entry.message?.role === 'user' &&
668
+ entry.message?.content &&
669
+ !entry.isMeta &&
670
+ !entry.message.content.startsWith('<command-')) {
671
+ return entry.message.content;
672
+ }
673
+ } catch { /* skip invalid lines */ }
674
+ }
675
+ return null;
676
+ } catch {
677
+ return null;
678
+ }
679
+ }
680
+ }
681
+
682
+ // Singleton discoverers
683
+ const discoverers: Record<string, SessionDiscoverer> = {
684
+ gemini: new GeminiSessionDiscoverer(),
685
+ qwen: new QwenSessionDiscoverer(),
686
+ codex: new CodexSessionDiscoverer(),
687
+ claude: new ClaudeSessionDiscoverer()
688
+ };
689
+
690
+ /**
691
+ * Get session discoverer for a tool
692
+ */
693
+ export function getDiscoverer(tool: string): SessionDiscoverer | null {
694
+ return discoverers[tool] || null;
695
+ }
696
+
697
+ /**
698
+ * Get latest native session for a tool
699
+ */
700
+ export function getLatestNativeSession(
701
+ tool: string,
702
+ workingDir?: string
703
+ ): NativeSession | null {
704
+ const discoverer = discoverers[tool];
705
+ if (!discoverer) return null;
706
+ return discoverer.getLatestSession({ workingDir });
707
+ }
708
+
709
+ /**
710
+ * Find native session by ID
711
+ */
712
+ export function findNativeSessionById(
713
+ tool: string,
714
+ sessionId: string
715
+ ): NativeSession | null {
716
+ const discoverer = discoverers[tool];
717
+ if (!discoverer) return null;
718
+ return discoverer.findSessionById(sessionId);
719
+ }
720
+
721
+ /**
722
+ * Track new session created during execution
723
+ * @param tool - CLI tool name (gemini, qwen, codex, claude)
724
+ * @param beforeTimestamp - Filter sessions created after this time
725
+ * @param workingDir - Project working directory
726
+ * @param prompt - Optional prompt for precise matching in parallel execution
727
+ */
728
+ export async function trackNewSession(
729
+ tool: string,
730
+ beforeTimestamp: Date,
731
+ workingDir: string,
732
+ prompt?: string
733
+ ): Promise<NativeSession | null> {
734
+ const discoverer = discoverers[tool];
735
+ if (!discoverer) return null;
736
+ return discoverer.trackNewSession(beforeTimestamp, workingDir, prompt);
737
+ }
738
+
739
+ /**
740
+ * Get all sessions for a tool
741
+ */
742
+ export function getNativeSessions(
743
+ tool: string,
744
+ options?: SessionDiscoveryOptions
745
+ ): NativeSession[] {
746
+ const discoverer = discoverers[tool];
747
+ if (!discoverer) return [];
748
+ return discoverer.getSessions(options);
749
+ }
750
+
751
+ /**
752
+ * Check if a tool supports native resume
753
+ */
754
+ export function supportsNativeResume(tool: string): boolean {
755
+ return tool in discoverers;
756
+ }
757
+
758
+ /**
759
+ * Get native resume command arguments for a tool
760
+ */
761
+ export function getNativeResumeArgs(
762
+ tool: string,
763
+ sessionId: string | 'latest'
764
+ ): string[] {
765
+ switch (tool) {
766
+ case 'gemini':
767
+ // gemini -r <uuid> or -r latest
768
+ return ['-r', sessionId];
769
+
770
+ case 'qwen':
771
+ // qwen --continue (latest) or --resume <uuid>
772
+ if (sessionId === 'latest') {
773
+ return ['--continue'];
774
+ }
775
+ return ['--resume', sessionId];
776
+
777
+ case 'codex':
778
+ // codex resume <uuid> or codex resume --last
779
+ if (sessionId === 'latest') {
780
+ return ['resume', '--last'];
781
+ }
782
+ return ['resume', sessionId];
783
+
784
+ default:
785
+ return [];
786
+ }
787
+ }
788
+
789
+ /**
790
+ * Get base path for a tool's sessions
791
+ */
792
+ export function getToolSessionPath(tool: string): string | null {
793
+ const discoverer = discoverers[tool];
794
+ return discoverer?.basePath || null;
795
+ }