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,876 @@
1
+ // CLI History Component
2
+ // Displays execution history with filtering, search, and delete
3
+ // Supports native session linking and full conversation parsing
4
+
5
+ // ========== CLI History State ==========
6
+ let cliExecutionHistory = [];
7
+ let cliHistoryFilter = null; // Filter by tool
8
+ let cliHistorySearch = ''; // Search query
9
+ let cliHistoryLimit = 50;
10
+ let showNativeOnly = false; // Filter to show only native-linked executions
11
+
12
+ // ========== Data Loading ==========
13
+ async function loadCliHistory(options = {}) {
14
+ try {
15
+ const { limit = cliHistoryLimit, tool = cliHistoryFilter, status = null } = options;
16
+
17
+ // Use history-native endpoint to get native session info
18
+ let url = `/api/cli/history-native?path=${encodeURIComponent(projectPath)}&limit=${limit}`;
19
+ if (tool) url += `&tool=${tool}`;
20
+ if (status) url += `&status=${status}`;
21
+ if (cliHistorySearch) url += `&search=${encodeURIComponent(cliHistorySearch)}`;
22
+
23
+ const response = await fetch(url);
24
+ if (!response.ok) throw new Error('Failed to load CLI history');
25
+ const data = await response.json();
26
+ cliExecutionHistory = data.executions || [];
27
+
28
+ return data;
29
+ } catch (err) {
30
+ console.error('Failed to load CLI history:', err);
31
+ return { executions: [], total: 0, count: 0 };
32
+ }
33
+ }
34
+
35
+ // Load native session content for a specific execution
36
+ async function loadNativeSessionContent(executionId) {
37
+ try {
38
+ const url = `/api/cli/native-session?path=${encodeURIComponent(projectPath)}&id=${encodeURIComponent(executionId)}`;
39
+ const response = await fetch(url);
40
+ if (!response.ok) return null;
41
+ return await response.json();
42
+ } catch (err) {
43
+ console.error('Failed to load native session:', err);
44
+ return null;
45
+ }
46
+ }
47
+
48
+ // Load enriched conversation (CCW + Native merged)
49
+ async function loadEnrichedConversation(executionId) {
50
+ try {
51
+ const url = `/api/cli/enriched?path=${encodeURIComponent(projectPath)}&id=${encodeURIComponent(executionId)}`;
52
+ const response = await fetch(url);
53
+ if (!response.ok) return null;
54
+ return await response.json();
55
+ } catch (err) {
56
+ console.error('Failed to load enriched conversation:', err);
57
+ return null;
58
+ }
59
+ }
60
+
61
+ async function loadExecutionDetail(executionId, sourceDir) {
62
+ try {
63
+ // If sourceDir provided, use it to build the correct path
64
+ const basePath = sourceDir && sourceDir !== '.'
65
+ ? projectPath + '/' + sourceDir
66
+ : projectPath;
67
+ const url = `/api/cli/execution?path=${encodeURIComponent(basePath)}&id=${encodeURIComponent(executionId)}`;
68
+ const response = await fetch(url);
69
+ if (!response.ok) throw new Error('Execution not found');
70
+ return await response.json();
71
+ } catch (err) {
72
+ console.error('Failed to load execution detail:', err);
73
+ return null;
74
+ }
75
+ }
76
+
77
+ // ========== Rendering ==========
78
+ function renderCliHistory() {
79
+ const container = document.getElementById('cli-history-panel');
80
+ if (!container) return;
81
+
82
+ // Filter by search query
83
+ const filteredHistory = cliHistorySearch
84
+ ? cliExecutionHistory.filter(exec =>
85
+ exec.prompt_preview.toLowerCase().includes(cliHistorySearch.toLowerCase()) ||
86
+ exec.tool.toLowerCase().includes(cliHistorySearch.toLowerCase())
87
+ )
88
+ : cliExecutionHistory;
89
+
90
+ if (cliExecutionHistory.length === 0) {
91
+ container.innerHTML = `
92
+ <div class="cli-history-header">
93
+ <h3><i data-lucide="history" class="w-4 h-4"></i> Execution History</h3>
94
+ <div class="cli-history-controls">
95
+ ${renderHistorySearch()}
96
+ ${renderToolFilter()}
97
+ <button class="btn-icon" onclick="refreshCliHistory()" title="Refresh">
98
+ <i data-lucide="refresh-cw" class="w-4 h-4"></i>
99
+ </button>
100
+ </div>
101
+ </div>
102
+ <div class="empty-state">
103
+ <i data-lucide="terminal" class="w-8 h-8"></i>
104
+ <p>No executions yet</p>
105
+ </div>
106
+ `;
107
+
108
+ if (window.lucide) lucide.createIcons();
109
+ return;
110
+ }
111
+
112
+ const historyHtml = filteredHistory.length === 0
113
+ ? `<div class="empty-state">
114
+ <i data-lucide="search-x" class="w-6 h-6"></i>
115
+ <p>No matching results</p>
116
+ </div>`
117
+ : filteredHistory.map(exec => {
118
+ const statusIcon = exec.status === 'success' ? 'check-circle' :
119
+ exec.status === 'timeout' ? 'clock' : 'x-circle';
120
+ const statusClass = exec.status === 'success' ? 'text-success' :
121
+ exec.status === 'timeout' ? 'text-warning' : 'text-destructive';
122
+ const duration = formatDuration(exec.duration_ms);
123
+ const timeAgo = getTimeAgo(new Date(exec.updated_at || exec.timestamp));
124
+ const turnBadge = exec.turn_count && exec.turn_count > 1
125
+ ? `<span class="cli-turn-badge">${exec.turn_count} turns</span>`
126
+ : '';
127
+
128
+ // Native session indicator
129
+ const hasNative = exec.hasNativeSession || exec.nativeSessionId;
130
+ const nativeBadge = hasNative
131
+ ? `<span class="cli-native-badge" title="Native session: ${exec.nativeSessionId}">
132
+ <i data-lucide="file-json" class="w-3 h-3"></i>
133
+ </span>`
134
+ : '';
135
+
136
+ return `
137
+ <div class="cli-history-item ${hasNative ? 'has-native' : ''}">
138
+ <div class="cli-history-item-content" onclick="showExecutionDetail('${exec.id}')">
139
+ <div class="cli-history-item-header">
140
+ <span class="cli-tool-tag cli-tool-${exec.tool}">${exec.tool.toUpperCase()}</span>
141
+ <span class="cli-mode-tag">${exec.mode || 'analysis'}</span>
142
+ <span class="cli-status-badge ${statusClass}">
143
+ <i data-lucide="${statusIcon}" class="w-3 h-3"></i> ${exec.status}
144
+ </span>
145
+ ${nativeBadge}
146
+ </div>
147
+ <div class="cli-history-prompt">${escapeHtml(exec.prompt_preview)}</div>
148
+ <div class="cli-history-meta">
149
+ <span><i data-lucide="clock" class="w-3 h-3"></i> ${timeAgo}</span>
150
+ <span><i data-lucide="timer" class="w-3 h-3"></i> ${duration}</span>
151
+ <span><i data-lucide="hash" class="w-3 h-3"></i> ${exec.id.split('-')[0]}</span>
152
+ ${turnBadge}
153
+ </div>
154
+ </div>
155
+ <div class="cli-history-actions">
156
+ ${hasNative ? `
157
+ <button class="btn-icon" onclick="event.stopPropagation(); showNativeSessionDetail('${exec.id}')" title="View Native Session">
158
+ <i data-lucide="file-json" class="w-3.5 h-3.5"></i>
159
+ </button>
160
+ ` : ''}
161
+ <button class="btn-icon" onclick="event.stopPropagation(); showExecutionDetail('${exec.id}')" title="View Details">
162
+ <i data-lucide="eye" class="w-3.5 h-3.5"></i>
163
+ </button>
164
+ <button class="btn-icon btn-danger" onclick="event.stopPropagation(); confirmDeleteExecution('${exec.id}')" title="Delete">
165
+ <i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
166
+ </button>
167
+ </div>
168
+ </div>
169
+ `;
170
+ }).join('');
171
+
172
+ container.innerHTML = `
173
+ <div class="cli-history-header">
174
+ <h3><i data-lucide="history" class="w-4 h-4"></i> Execution History</h3>
175
+ <div class="cli-history-controls">
176
+ ${renderHistorySearch()}
177
+ ${renderToolFilter()}
178
+ <button class="btn-icon" onclick="refreshCliHistory()" title="Refresh">
179
+ <i data-lucide="refresh-cw" class="w-4 h-4"></i>
180
+ </button>
181
+ </div>
182
+ </div>
183
+ <div class="cli-history-list">
184
+ ${historyHtml}
185
+ </div>
186
+ `;
187
+
188
+ if (window.lucide) lucide.createIcons();
189
+ }
190
+
191
+ function renderHistorySearch() {
192
+ return `
193
+ <input type="text"
194
+ class="cli-history-search"
195
+ placeholder="Search history..."
196
+ value="${escapeHtml(cliHistorySearch)}"
197
+ onkeyup="searchCliHistory(this.value)"
198
+ oninput="searchCliHistory(this.value)">
199
+ `;
200
+ }
201
+
202
+ function renderToolFilter() {
203
+ const tools = ['all', 'gemini', 'qwen', 'codex'];
204
+ return `
205
+ <select class="cli-tool-filter" onchange="filterCliHistory(this.value)">
206
+ ${tools.map(tool => `
207
+ <option value="${tool === 'all' ? '' : tool}" ${cliHistoryFilter === (tool === 'all' ? null : tool) ? 'selected' : ''}>
208
+ ${tool === 'all' ? 'All Tools' : tool.charAt(0).toUpperCase() + tool.slice(1)}
209
+ </option>
210
+ `).join('')}
211
+ </select>
212
+ `;
213
+ }
214
+
215
+ // ========== Execution Detail Modal ==========
216
+ async function showExecutionDetail(executionId, sourceDir) {
217
+ const conversation = await loadExecutionDetail(executionId, sourceDir);
218
+ if (!conversation) {
219
+ showRefreshToast('Conversation not found', 'error');
220
+ return;
221
+ }
222
+
223
+ // Handle both old (single execution) and new (conversation) formats
224
+ const isConversation = conversation.turns && Array.isArray(conversation.turns);
225
+ const turnCount = isConversation ? conversation.turn_count : 1;
226
+ const totalDuration = isConversation ? conversation.total_duration_ms : conversation.duration_ms;
227
+ const latestStatus = isConversation ? conversation.latest_status : conversation.status;
228
+ const createdAt = isConversation ? conversation.created_at : conversation.timestamp;
229
+
230
+ // Build turns HTML with improved multi-turn display
231
+ let turnsHtml = '';
232
+ if (isConversation && conversation.turns.length > 0) {
233
+ turnsHtml = conversation.turns.map((turn, idx) => {
234
+ const isFirst = idx === 0;
235
+ const isLast = idx === conversation.turns.length - 1;
236
+ const turnTime = new Date(turn.timestamp).toLocaleTimeString();
237
+ const statusIcon = turn.status === 'success' ? 'check-circle' :
238
+ turn.status === 'timeout' ? 'clock' : 'x-circle';
239
+
240
+ return `
241
+ <div class="cli-turn-section ${isLast ? 'cli-turn-latest' : ''}">
242
+ <div class="cli-turn-header">
243
+ <div class="cli-turn-marker">
244
+ <span class="cli-turn-number">${isFirst ? '▶' : '↳'} Turn ${turn.turn}</span>
245
+ ${isLast ? '<span class="cli-turn-latest-badge">Latest</span>' : ''}
246
+ </div>
247
+ <div class="cli-turn-meta">
248
+ <span class="cli-turn-time"><i data-lucide="clock" class="w-3 h-3"></i> ${turnTime}</span>
249
+ <span class="cli-turn-status status-${turn.status}">
250
+ <i data-lucide="${statusIcon}" class="w-3 h-3"></i> ${turn.status}
251
+ </span>
252
+ <span class="cli-turn-duration">${formatDuration(turn.duration_ms)}</span>
253
+ </div>
254
+ </div>
255
+ <div class="cli-turn-body">
256
+ <div class="cli-detail-section cli-prompt-section">
257
+ <h4><i data-lucide="user" class="w-3.5 h-3.5"></i> User Prompt</h4>
258
+ <pre class="cli-detail-prompt">${escapeHtml(turn.prompt)}</pre>
259
+ </div>
260
+ ${turn.output.stdout ? `
261
+ <div class="cli-detail-section cli-output-section">
262
+ <h4><i data-lucide="bot" class="w-3.5 h-3.5"></i> Assistant Response</h4>
263
+ <pre class="cli-detail-output">${escapeHtml(turn.output.stdout)}</pre>
264
+ </div>
265
+ ` : ''}
266
+ ${turn.output.stderr ? `
267
+ <div class="cli-detail-section cli-detail-error-section">
268
+ <h4><i data-lucide="alert-triangle" class="w-3.5 h-3.5"></i> Errors</h4>
269
+ <pre class="cli-detail-error">${escapeHtml(turn.output.stderr)}</pre>
270
+ </div>
271
+ ` : ''}
272
+ ${turn.output.truncated ? `
273
+ <p class="cli-truncated-notice">
274
+ <i data-lucide="info" class="w-3 h-3"></i>
275
+ Output was truncated due to size.
276
+ </p>
277
+ ` : ''}
278
+ </div>
279
+ </div>
280
+ `;
281
+ }).join('<div class="cli-turn-connector"><div class="cli-turn-line"></div></div>');
282
+ } else {
283
+ // Legacy single execution format
284
+ const detail = conversation;
285
+ turnsHtml = `
286
+ <div class="cli-turn-section">
287
+ <div class="cli-turn-body">
288
+ <div class="cli-detail-section cli-prompt-section">
289
+ <h4><i data-lucide="user" class="w-3.5 h-3.5"></i> User Prompt</h4>
290
+ <pre class="cli-detail-prompt">${escapeHtml(detail.prompt)}</pre>
291
+ </div>
292
+ ${detail.output.stdout ? `
293
+ <div class="cli-detail-section cli-output-section">
294
+ <h4><i data-lucide="bot" class="w-3.5 h-3.5"></i> Assistant Response</h4>
295
+ <pre class="cli-detail-output">${escapeHtml(detail.output.stdout)}</pre>
296
+ </div>
297
+ ` : ''}
298
+ ${detail.output.stderr ? `
299
+ <div class="cli-detail-section cli-detail-error-section">
300
+ <h4><i data-lucide="alert-triangle" class="w-3.5 h-3.5"></i> Errors</h4>
301
+ <pre class="cli-detail-error">${escapeHtml(detail.output.stderr)}</pre>
302
+ </div>
303
+ ` : ''}
304
+ ${detail.output.truncated ? `
305
+ <p class="cli-truncated-notice">
306
+ <i data-lucide="info" class="w-3 h-3"></i>
307
+ Output was truncated due to size.
308
+ </p>
309
+ ` : ''}
310
+ </div>
311
+ </div>
312
+ `;
313
+ }
314
+
315
+ // Build concatenated prompt view (for multi-turn conversations)
316
+ let concatenatedPromptHtml = '';
317
+ if (isConversation && conversation.turns.length > 1) {
318
+ concatenatedPromptHtml = `
319
+ <div class="cli-concat-section" id="concatPromptSection" style="display: none;">
320
+ <div class="cli-detail-section">
321
+ <h4><i data-lucide="layers" class="w-3.5 h-3.5"></i> Concatenated Prompt (sent to CLI)</h4>
322
+ <div class="cli-concat-format-selector">
323
+ <button class="btn btn-xs ${true ? 'btn-primary' : 'btn-outline'}" onclick="switchConcatFormat('plain', '${executionId}')">Plain</button>
324
+ <button class="btn btn-xs btn-outline" onclick="switchConcatFormat('yaml', '${executionId}')">YAML</button>
325
+ <button class="btn btn-xs btn-outline" onclick="switchConcatFormat('json', '${executionId}')">JSON</button>
326
+ </div>
327
+ <pre class="cli-detail-output cli-concat-output" id="concatPromptOutput">${escapeHtml(buildConcatenatedPrompt(conversation, 'plain'))}</pre>
328
+ </div>
329
+ </div>
330
+ `;
331
+ }
332
+
333
+ // Check if native session is available
334
+ const hasNativeSession = conversation.hasNativeSession || conversation.nativeSessionId;
335
+
336
+ const modalContent = `
337
+ <div class="cli-detail-header">
338
+ <div class="cli-detail-info">
339
+ <span class="cli-tool-tag cli-tool-${conversation.tool}">${conversation.tool}</span>
340
+ ${turnCount > 1 ? `<span class="cli-turn-badge"><i data-lucide="messages-square" class="w-3 h-3"></i> ${turnCount} turns</span>` : ''}
341
+ <span class="cli-detail-status status-${latestStatus}">${latestStatus}</span>
342
+ <span class="text-muted-foreground">${formatDuration(totalDuration)}</span>
343
+ </div>
344
+ <div class="cli-detail-meta">
345
+ <span><i data-lucide="cpu" class="w-3 h-3"></i> ${conversation.model || 'default'}</span>
346
+ <span><i data-lucide="toggle-right" class="w-3 h-3"></i> ${conversation.mode}</span>
347
+ <span><i data-lucide="calendar" class="w-3 h-3"></i> ${new Date(createdAt).toLocaleString()}</span>
348
+ <span><i data-lucide="hash" class="w-3 h-3"></i> ${executionId.split('-')[0]}</span>
349
+ </div>
350
+ ${hasNativeSession ? `
351
+ <div class="cli-detail-native-action">
352
+ <button class="btn btn-sm btn-primary" onclick="showNativeSessionDetail('${executionId}')">
353
+ <i data-lucide="eye" class="w-3.5 h-3.5"></i> View Full Process Conversation
354
+ </button>
355
+ </div>
356
+ ` : ''}
357
+ </div>
358
+ ${turnCount > 1 ? `
359
+ <div class="cli-view-toggle">
360
+ <button class="btn btn-sm btn-outline active" onclick="toggleConversationView('turns')">
361
+ <i data-lucide="list" class="w-3.5 h-3.5"></i> Per-Turn View
362
+ </button>
363
+ <button class="btn btn-sm btn-outline" onclick="toggleConversationView('concat')">
364
+ <i data-lucide="layers" class="w-3.5 h-3.5"></i> Concatenated View
365
+ </button>
366
+ </div>
367
+ ` : ''}
368
+ <div class="cli-turns-container" id="turnsContainer">
369
+ ${turnsHtml}
370
+ </div>
371
+ ${concatenatedPromptHtml}
372
+ <div class="cli-detail-actions">
373
+ <button class="btn btn-sm btn-outline" onclick="copyConversationId('${executionId}')">
374
+ <i data-lucide="copy" class="w-3.5 h-3.5"></i> Copy ID
375
+ </button>
376
+ ${turnCount > 1 ? `
377
+ <button class="btn btn-sm btn-outline" onclick="copyConcatenatedPrompt('${executionId}')">
378
+ <i data-lucide="clipboard-copy" class="w-3.5 h-3.5"></i> Copy Full Prompt
379
+ </button>
380
+ ` : ''}
381
+ <button class="btn btn-sm btn-outline btn-danger" onclick="confirmDeleteExecution('${executionId}'); closeModal();">
382
+ <i data-lucide="trash-2" class="w-3.5 h-3.5"></i> Delete
383
+ </button>
384
+ </div>
385
+ `;
386
+
387
+ // Store conversation data for format switching
388
+ window._currentConversation = conversation;
389
+
390
+ showModal('Conversation Detail', modalContent);
391
+ }
392
+
393
+ // ========== Actions ==========
394
+ async function filterCliHistory(tool) {
395
+ cliHistoryFilter = tool || null;
396
+ await loadCliHistory();
397
+ renderCliHistory();
398
+ }
399
+
400
+ function searchCliHistory(query) {
401
+ cliHistorySearch = query;
402
+ renderCliHistory();
403
+ // Preserve focus and cursor position
404
+ const searchInput = document.querySelector('.cli-history-search');
405
+ if (searchInput) {
406
+ searchInput.focus();
407
+ searchInput.setSelectionRange(query.length, query.length);
408
+ }
409
+ }
410
+
411
+ async function refreshCliHistory() {
412
+ await loadCliHistory();
413
+ renderCliHistory();
414
+ showRefreshToast('History refreshed', 'success');
415
+ }
416
+
417
+ // ========== Delete Execution ==========
418
+ function confirmDeleteExecution(executionId, sourceDir) {
419
+ if (confirm('Delete this execution record? This action cannot be undone.')) {
420
+ deleteExecution(executionId, sourceDir);
421
+ }
422
+ }
423
+
424
+ async function deleteExecution(executionId, sourceDir) {
425
+ try {
426
+ // Build correct path - use sourceDir if provided for recursive items
427
+ const basePath = sourceDir && sourceDir !== '.'
428
+ ? projectPath + '/' + sourceDir
429
+ : projectPath;
430
+
431
+ const response = await fetch(`/api/cli/execution?path=${encodeURIComponent(basePath)}&id=${encodeURIComponent(executionId)}`, {
432
+ method: 'DELETE'
433
+ });
434
+
435
+ if (!response.ok) {
436
+ const error = await response.json();
437
+ throw new Error(error.error || 'Failed to delete');
438
+ }
439
+
440
+ // Reload fresh data from server and re-render
441
+ await loadCliHistory();
442
+
443
+ // Render appropriate view based on current view
444
+ if (typeof currentView !== 'undefined' && (currentView === 'history' || currentView === 'cli-history')) {
445
+ renderCliHistoryView();
446
+ } else {
447
+ renderCliHistory();
448
+ }
449
+ showRefreshToast('Execution deleted', 'success');
450
+ } catch (err) {
451
+ console.error('Failed to delete execution:', err);
452
+ showRefreshToast('Delete failed: ' + err.message, 'error');
453
+ }
454
+ }
455
+
456
+ // ========== Copy Functions ==========
457
+ async function copyExecutionPrompt(executionId) {
458
+ const detail = await loadExecutionDetail(executionId);
459
+ if (!detail) {
460
+ showRefreshToast('Execution not found', 'error');
461
+ return;
462
+ }
463
+
464
+ if (navigator.clipboard) {
465
+ try {
466
+ await navigator.clipboard.writeText(detail.prompt);
467
+ showRefreshToast('Prompt copied to clipboard', 'success');
468
+ } catch (err) {
469
+ showRefreshToast('Failed to copy', 'error');
470
+ }
471
+ }
472
+ }
473
+
474
+ async function copyConversationId(conversationId) {
475
+ if (navigator.clipboard) {
476
+ try {
477
+ await navigator.clipboard.writeText(conversationId);
478
+ showRefreshToast('ID copied to clipboard', 'success');
479
+ } catch (err) {
480
+ showRefreshToast('Failed to copy', 'error');
481
+ }
482
+ }
483
+ }
484
+
485
+ // ========== Concatenated Prompt Functions ==========
486
+
487
+ /**
488
+ * Build concatenated prompt from conversation turns
489
+ * Formats: plain, yaml, json
490
+ */
491
+ function buildConcatenatedPrompt(conversation, format) {
492
+ if (!conversation || !conversation.turns || conversation.turns.length === 0) {
493
+ return '';
494
+ }
495
+
496
+ const turns = conversation.turns;
497
+
498
+ switch (format) {
499
+ case 'yaml':
500
+ return buildYamlPrompt(conversation);
501
+ case 'json':
502
+ return buildJsonPrompt(conversation);
503
+ case 'plain':
504
+ default:
505
+ return buildPlainPrompt(conversation);
506
+ }
507
+ }
508
+
509
+ function buildPlainPrompt(conversation) {
510
+ const parts = [];
511
+ parts.push('=== CONVERSATION HISTORY ===');
512
+ parts.push('');
513
+
514
+ for (const turn of conversation.turns) {
515
+ parts.push('--- Turn ' + turn.turn + ' ---');
516
+ parts.push('USER:');
517
+ parts.push(turn.prompt);
518
+ parts.push('');
519
+ parts.push('ASSISTANT:');
520
+ parts.push(turn.output.stdout || '[No output]');
521
+ parts.push('');
522
+ }
523
+
524
+ parts.push('=== NEW REQUEST ===');
525
+ parts.push('');
526
+ parts.push('[Your next prompt here]');
527
+
528
+ return parts.join('\n');
529
+ }
530
+
531
+ function buildYamlPrompt(conversation) {
532
+ const lines = [];
533
+ lines.push('context:');
534
+ lines.push(' tool: ' + conversation.tool);
535
+ lines.push(' model: ' + (conversation.model || 'default'));
536
+ lines.push(' mode: ' + conversation.mode);
537
+ lines.push('');
538
+ lines.push('conversation:');
539
+
540
+ for (const turn of conversation.turns) {
541
+ lines.push(' - turn: ' + turn.turn);
542
+ lines.push(' timestamp: ' + turn.timestamp);
543
+ lines.push(' status: ' + turn.status);
544
+ lines.push(' user: |');
545
+ turn.prompt.split('\n').forEach(function(line) {
546
+ lines.push(' ' + line);
547
+ });
548
+ lines.push(' assistant: |');
549
+ (turn.output.stdout || '[No output]').split('\n').forEach(function(line) {
550
+ lines.push(' ' + line);
551
+ });
552
+ lines.push('');
553
+ }
554
+
555
+ lines.push('new_request: |');
556
+ lines.push(' [Your next prompt here]');
557
+
558
+ return lines.join('\n');
559
+ }
560
+
561
+ function buildJsonPrompt(conversation) {
562
+ const data = {
563
+ context: {
564
+ tool: conversation.tool,
565
+ model: conversation.model || 'default',
566
+ mode: conversation.mode
567
+ },
568
+ conversation: conversation.turns.map(function(turn) {
569
+ return {
570
+ turn: turn.turn,
571
+ timestamp: turn.timestamp,
572
+ status: turn.status,
573
+ user: turn.prompt,
574
+ assistant: turn.output.stdout || '[No output]'
575
+ };
576
+ }),
577
+ new_request: '[Your next prompt here]'
578
+ };
579
+ return JSON.stringify(data, null, 2);
580
+ }
581
+
582
+ /**
583
+ * Toggle between per-turn and concatenated views
584
+ */
585
+ function toggleConversationView(view) {
586
+ var turnsContainer = document.getElementById('turnsContainer');
587
+ var concatSection = document.getElementById('concatPromptSection');
588
+ var buttons = document.querySelectorAll('.cli-view-toggle button');
589
+
590
+ if (view === 'concat') {
591
+ if (turnsContainer) turnsContainer.style.display = 'none';
592
+ if (concatSection) concatSection.style.display = 'block';
593
+ buttons.forEach(function(btn, idx) {
594
+ btn.classList.toggle('active', idx === 1);
595
+ });
596
+ } else {
597
+ if (turnsContainer) turnsContainer.style.display = 'block';
598
+ if (concatSection) concatSection.style.display = 'none';
599
+ buttons.forEach(function(btn, idx) {
600
+ btn.classList.toggle('active', idx === 0);
601
+ });
602
+ }
603
+
604
+ if (window.lucide) lucide.createIcons();
605
+ }
606
+
607
+ /**
608
+ * Switch concatenation format (plain/yaml/json)
609
+ */
610
+ function switchConcatFormat(format, executionId) {
611
+ var conversation = window._currentConversation;
612
+ if (!conversation) return;
613
+
614
+ var output = document.getElementById('concatPromptOutput');
615
+ if (output) {
616
+ output.textContent = buildConcatenatedPrompt(conversation, format);
617
+ }
618
+
619
+ // Update button states
620
+ var buttons = document.querySelectorAll('.cli-concat-format-selector button');
621
+ buttons.forEach(function(btn) {
622
+ var btnFormat = btn.textContent.toLowerCase();
623
+ btn.className = 'btn btn-xs ' + (btnFormat === format ? 'btn-primary' : 'btn-outline');
624
+ });
625
+ }
626
+
627
+ /**
628
+ * Copy concatenated prompt to clipboard
629
+ */
630
+ async function copyConcatenatedPrompt(executionId) {
631
+ var conversation = window._currentConversation;
632
+ if (!conversation) {
633
+ showRefreshToast('Conversation not found', 'error');
634
+ return;
635
+ }
636
+
637
+ var prompt = buildConcatenatedPrompt(conversation, 'plain');
638
+ if (navigator.clipboard) {
639
+ try {
640
+ await navigator.clipboard.writeText(prompt);
641
+ showRefreshToast('Full prompt copied to clipboard', 'success');
642
+ } catch (err) {
643
+ showRefreshToast('Failed to copy', 'error');
644
+ }
645
+ }
646
+ }
647
+
648
+ // ========== Native Session Detail ==========
649
+
650
+ /**
651
+ * Show native session detail modal with full conversation content
652
+ */
653
+ async function showNativeSessionDetail(executionId) {
654
+ // Load native session content
655
+ const nativeSession = await loadNativeSessionContent(executionId);
656
+
657
+ if (!nativeSession) {
658
+ showRefreshToast('Native session not found', 'error');
659
+ return;
660
+ }
661
+
662
+ // Build turns HTML from native session
663
+ const turnsHtml = nativeSession.turns && nativeSession.turns.length > 0
664
+ ? nativeSession.turns.map((turn, idx) => {
665
+ const isLast = idx === nativeSession.turns.length - 1;
666
+ const roleIcon = turn.role === 'user' ? 'user' : 'bot';
667
+ const roleClass = turn.role === 'user' ? 'user' : 'assistant';
668
+
669
+ // Token info
670
+ const tokenInfo = turn.tokens
671
+ ? `<span class="native-turn-tokens">
672
+ <i data-lucide="coins" class="w-3 h-3"></i>
673
+ ${turn.tokens.total || 0} tokens
674
+ (in: ${turn.tokens.input || 0}, out: ${turn.tokens.output || 0}${turn.tokens.cached ? `, cached: ${turn.tokens.cached}` : ''})
675
+ </span>`
676
+ : '';
677
+
678
+ // Thoughts section (collapsible)
679
+ const thoughtsHtml = turn.thoughts && turn.thoughts.length > 0
680
+ ? `<div class="native-thoughts-section">
681
+ <details class="turn-thinking-details">
682
+ <summary class="turn-thinking-summary">
683
+ <i data-lucide="brain" class="w-3 h-3"></i>
684
+ 💭 Thinking Process (${turn.thoughts.length} thoughts)
685
+ </summary>
686
+ <div class="turn-thinking-content">
687
+ <ul class="native-thoughts-list">
688
+ ${turn.thoughts.map(t => `<li>${escapeHtml(t)}</li>`).join('')}
689
+ </ul>
690
+ </div>
691
+ </details>
692
+ </div>`
693
+ : '';
694
+
695
+ // Tool calls section (collapsible for each call)
696
+ const toolCallsHtml = turn.toolCalls && turn.toolCalls.length > 0
697
+ ? `<div class="native-tools-section">
698
+ <div class="turn-tool-calls-header">
699
+ <i data-lucide="wrench" class="w-3 h-3"></i>
700
+ <strong>Tool Calls (${turn.toolCalls.length})</strong>
701
+ </div>
702
+ <div class="native-tools-list">
703
+ ${turn.toolCalls.map((tc, tcIdx) => `
704
+ <details class="turn-tool-call-details" ${tcIdx === 0 ? 'open' : ''}>
705
+ <summary class="turn-tool-call-summary">
706
+ <span class="native-tool-name">🔧 ${escapeHtml(tc.name)}</span>
707
+ ${tc.output ? `<span class="native-tool-size">(${tc.output.length} chars)</span>` : ''}
708
+ </summary>
709
+ <div class="turn-tool-call-content">
710
+ ${tc.input ? `
711
+ <div class="turn-tool-input">
712
+ <strong>Input:</strong>
713
+ <pre>${escapeHtml(JSON.stringify(tc.input, null, 2))}</pre>
714
+ </div>
715
+ ` : ''}
716
+ ${tc.output ? `
717
+ <div class="turn-tool-output">
718
+ <strong>Output:</strong>
719
+ <pre class="native-tool-output">${escapeHtml(tc.output)}</pre>
720
+ </div>
721
+ ` : ''}
722
+ </div>
723
+ </details>
724
+ `).join('')}
725
+ </div>
726
+ </div>`
727
+ : '';
728
+
729
+ return `
730
+ <div class="native-turn ${roleClass} ${isLast ? 'latest' : ''}">
731
+ <div class="native-turn-header">
732
+ <span class="native-turn-role">
733
+ <i data-lucide="${roleIcon}" class="w-3.5 h-3.5"></i>
734
+ ${turn.role === 'user' ? 'User' : 'Assistant'}
735
+ </span>
736
+ <span class="native-turn-number">Turn ${turn.turnNumber}</span>
737
+ ${tokenInfo}
738
+ ${isLast ? '<span class="native-turn-latest">Latest</span>' : ''}
739
+ </div>
740
+ <div class="native-turn-content">
741
+ <pre>${escapeHtml(turn.content)}</pre>
742
+ </div>
743
+ ${thoughtsHtml}
744
+ ${toolCallsHtml}
745
+ </div>
746
+ `;
747
+ }).join('')
748
+ : '<p class="text-muted-foreground">No conversation turns found</p>';
749
+
750
+ // Total tokens summary
751
+ const totalTokensHtml = nativeSession.totalTokens
752
+ ? `<div class="native-tokens-summary">
753
+ <i data-lucide="bar-chart-3" class="w-4 h-4"></i>
754
+ <strong>Total Tokens:</strong>
755
+ ${nativeSession.totalTokens.total || 0}
756
+ (Input: ${nativeSession.totalTokens.input || 0},
757
+ Output: ${nativeSession.totalTokens.output || 0}
758
+ ${nativeSession.totalTokens.cached ? `, Cached: ${nativeSession.totalTokens.cached}` : ''})
759
+ </div>`
760
+ : '';
761
+
762
+ const modalContent = `
763
+ <div class="native-session-detail">
764
+ <div class="native-session-header">
765
+ <div class="native-session-info">
766
+ <span class="cli-tool-tag cli-tool-${nativeSession.tool}">${nativeSession.tool.toUpperCase()}</span>
767
+ ${nativeSession.model ? `<span class="native-model"><i data-lucide="cpu" class="w-3 h-3"></i> ${nativeSession.model}</span>` : ''}
768
+ <span class="native-session-id"><i data-lucide="fingerprint" class="w-3 h-3"></i> ${nativeSession.sessionId}</span>
769
+ </div>
770
+ <div class="native-session-meta">
771
+ <span><i data-lucide="calendar" class="w-3 h-3"></i> ${new Date(nativeSession.startTime).toLocaleString()}</span>
772
+ ${nativeSession.workingDir ? `<span><i data-lucide="folder" class="w-3 h-3"></i> ${nativeSession.workingDir}</span>` : ''}
773
+ ${nativeSession.projectHash ? `<span><i data-lucide="hash" class="w-3 h-3"></i> ${nativeSession.projectHash.substring(0, 12)}...</span>` : ''}
774
+ </div>
775
+ </div>
776
+ ${totalTokensHtml}
777
+ <div class="native-turns-container">
778
+ ${turnsHtml}
779
+ </div>
780
+ <div class="native-session-actions">
781
+ <button class="btn btn-sm btn-outline" onclick="copyNativeSessionId('${nativeSession.sessionId}')">
782
+ <i data-lucide="copy" class="w-3.5 h-3.5"></i> Copy Session ID
783
+ </button>
784
+ <button class="btn btn-sm btn-outline" onclick="copyNativeSessionPath('${executionId}')">
785
+ <i data-lucide="file" class="w-3.5 h-3.5"></i> Copy File Path
786
+ </button>
787
+ <button class="btn btn-sm btn-outline" onclick="exportNativeSession('${executionId}')">
788
+ <i data-lucide="download" class="w-3.5 h-3.5"></i> Export JSON
789
+ </button>
790
+ </div>
791
+ </div>
792
+ `;
793
+
794
+ // Store for export
795
+ window._currentNativeSession = nativeSession;
796
+
797
+ showModal('Native Session Detail', modalContent, { size: 'lg' });
798
+ }
799
+
800
+ /**
801
+ * Copy native session ID to clipboard
802
+ */
803
+ async function copyNativeSessionId(sessionId) {
804
+ if (navigator.clipboard) {
805
+ try {
806
+ await navigator.clipboard.writeText(sessionId);
807
+ showRefreshToast('Session ID copied', 'success');
808
+ } catch (err) {
809
+ showRefreshToast('Failed to copy', 'error');
810
+ }
811
+ }
812
+ }
813
+
814
+ /**
815
+ * Copy native session file path
816
+ */
817
+ async function copyNativeSessionPath(executionId) {
818
+ // Find execution in history
819
+ const exec = cliExecutionHistory.find(e => e.id === executionId);
820
+ if (exec && exec.nativeSessionPath) {
821
+ if (navigator.clipboard) {
822
+ try {
823
+ await navigator.clipboard.writeText(exec.nativeSessionPath);
824
+ showRefreshToast('File path copied', 'success');
825
+ } catch (err) {
826
+ showRefreshToast('Failed to copy', 'error');
827
+ }
828
+ }
829
+ } else {
830
+ showRefreshToast('Path not available', 'error');
831
+ }
832
+ }
833
+
834
+ /**
835
+ * Export native session as JSON file
836
+ */
837
+ function exportNativeSession(executionId) {
838
+ const session = window._currentNativeSession;
839
+ if (!session) {
840
+ showRefreshToast('No session data', 'error');
841
+ return;
842
+ }
843
+
844
+ const blob = new Blob([JSON.stringify(session, null, 2)], { type: 'application/json' });
845
+ const url = URL.createObjectURL(blob);
846
+ const a = document.createElement('a');
847
+ a.href = url;
848
+ a.download = `native-session-${session.sessionId}.json`;
849
+ document.body.appendChild(a);
850
+ a.click();
851
+ document.body.removeChild(a);
852
+ URL.revokeObjectURL(url);
853
+ showRefreshToast('Session exported', 'success');
854
+ }
855
+
856
+ // ========== Helpers ==========
857
+ function formatDuration(ms) {
858
+ if (ms >= 60000) {
859
+ const mins = Math.floor(ms / 60000);
860
+ const secs = Math.round((ms % 60000) / 1000);
861
+ return `${mins}m ${secs}s`;
862
+ } else if (ms >= 1000) {
863
+ return `${(ms / 1000).toFixed(1)}s`;
864
+ }
865
+ return `${ms}ms`;
866
+ }
867
+
868
+ function getTimeAgo(date) {
869
+ const seconds = Math.floor((new Date() - date) / 1000);
870
+
871
+ if (seconds < 60) return 'just now';
872
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
873
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
874
+ if (seconds < 604800) return `${Math.floor(seconds / 86400)}d ago`;
875
+ return date.toLocaleDateString();
876
+ }