spiracha 1.5.0 → 2.0.0

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 (365) hide show
  1. package/AGENTS.md +114 -205
  2. package/README.md +89 -269
  3. package/apps/ui/AGENTS.md +17 -7
  4. package/apps/ui/README.md +34 -8
  5. package/apps/ui/components.json +21 -0
  6. package/apps/ui/package.json +65 -0
  7. package/apps/ui/src/components/antigravity-conversations-table.tsx +206 -0
  8. package/apps/ui/src/components/antigravity-keychain-panel.tsx +76 -0
  9. package/apps/ui/src/components/antigravity-workspaces-table.tsx +63 -0
  10. package/apps/ui/src/components/app-shell.tsx +124 -0
  11. package/apps/ui/src/components/breadcrumbs.tsx +53 -0
  12. package/apps/ui/src/components/claude-code-sessions-table.tsx +114 -0
  13. package/apps/ui/src/components/claude-code-workspaces-table.tsx +55 -0
  14. package/apps/ui/src/components/cursor-threads-table.tsx +189 -0
  15. package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
  16. package/apps/ui/src/components/data-table.tsx +241 -0
  17. package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
  18. package/apps/ui/src/components/export-dialog.tsx +184 -0
  19. package/apps/ui/src/components/json-panel.tsx +17 -0
  20. package/apps/ui/src/components/kiro-sessions-table.tsx +112 -0
  21. package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
  22. package/apps/ui/src/components/list-search-input.tsx +18 -0
  23. package/apps/ui/src/components/loading-panel.tsx +23 -0
  24. package/apps/ui/src/components/metadata-section.tsx +31 -0
  25. package/apps/ui/src/components/metric-card.tsx +19 -0
  26. package/apps/ui/src/components/opencode-sessions-table.tsx +128 -0
  27. package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
  28. package/apps/ui/src/components/page-header.tsx +33 -0
  29. package/apps/ui/src/components/projects-loading-state.tsx +74 -0
  30. package/apps/ui/src/components/projects-table.tsx +108 -0
  31. package/apps/ui/src/components/qoder-sessions-table.tsx +112 -0
  32. package/apps/ui/src/components/qoder-workspaces-table.tsx +53 -0
  33. package/apps/ui/src/components/recent-threads-list.tsx +52 -0
  34. package/apps/ui/src/components/reload-error-panel.tsx +20 -0
  35. package/apps/ui/src/components/text-document-panel.tsx +19 -0
  36. package/apps/ui/src/components/theme-toggle.tsx +48 -0
  37. package/apps/ui/src/components/threads-table.tsx +202 -0
  38. package/apps/ui/src/components/transcript-view.tsx +552 -0
  39. package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
  40. package/apps/ui/src/components/ui/badge.tsx +45 -0
  41. package/apps/ui/src/components/ui/button.tsx +62 -0
  42. package/apps/ui/src/components/ui/checkbox.tsx +29 -0
  43. package/apps/ui/src/components/ui/dialog.tsx +137 -0
  44. package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
  45. package/apps/ui/src/components/ui/input.tsx +21 -0
  46. package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
  47. package/apps/ui/src/components/ui/select.tsx +163 -0
  48. package/apps/ui/src/components/ui/separator.tsx +28 -0
  49. package/apps/ui/src/components/ui/sheet.tsx +107 -0
  50. package/apps/ui/src/components/ui/skeleton.tsx +7 -0
  51. package/apps/ui/src/components/ui/table.tsx +76 -0
  52. package/apps/ui/src/components/ui/tabs.tsx +71 -0
  53. package/apps/ui/src/components/ui/tooltip.tsx +44 -0
  54. package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
  55. package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
  56. package/apps/ui/src/lib/antigravity-conversation-state.ts +28 -0
  57. package/apps/ui/src/lib/antigravity-queries.ts +33 -0
  58. package/apps/ui/src/lib/antigravity-server.ts +128 -0
  59. package/apps/ui/src/lib/antigravity-transcript-events.ts +335 -0
  60. package/apps/ui/src/lib/claude-code-queries.ts +26 -0
  61. package/apps/ui/src/lib/claude-code-server.ts +75 -0
  62. package/apps/ui/src/lib/claude-code-transcript-events.ts +154 -0
  63. package/apps/ui/src/lib/codex-queries.ts +66 -0
  64. package/apps/ui/src/lib/codex-server.ts +209 -0
  65. package/apps/ui/src/lib/cursor-queries.ts +37 -0
  66. package/apps/ui/src/lib/cursor-server.ts +272 -0
  67. package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
  68. package/apps/ui/src/lib/download.ts +166 -0
  69. package/apps/ui/src/lib/formatters.ts +149 -0
  70. package/apps/ui/src/lib/kiro-queries.ts +22 -0
  71. package/apps/ui/src/lib/kiro-server.ts +73 -0
  72. package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
  73. package/apps/ui/src/lib/opencode-queries.ts +37 -0
  74. package/apps/ui/src/lib/opencode-server.ts +73 -0
  75. package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
  76. package/apps/ui/src/lib/package-metadata.ts +16 -0
  77. package/apps/ui/src/lib/path-utils.ts +13 -0
  78. package/apps/ui/src/lib/qoder-queries.ts +22 -0
  79. package/apps/ui/src/lib/qoder-server.ts +79 -0
  80. package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
  81. package/apps/ui/src/lib/route-search.ts +68 -0
  82. package/apps/ui/src/lib/settings-store.tsx +59 -0
  83. package/apps/ui/src/lib/source-session-export-server.ts +56 -0
  84. package/apps/ui/src/lib/text-filter.ts +22 -0
  85. package/apps/ui/src/lib/thread-id.ts +3 -0
  86. package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
  87. package/apps/ui/src/lib/utils.ts +7 -0
  88. package/apps/ui/src/routeTree.gen.ts +730 -0
  89. package/apps/ui/src/router.tsx +20 -0
  90. package/apps/ui/src/routes/$threadId.tsx +22 -0
  91. package/apps/ui/src/routes/__root.tsx +127 -0
  92. package/apps/ui/src/routes/analytics.tsx +143 -0
  93. package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +438 -0
  94. package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +127 -0
  95. package/apps/ui/src/routes/antigravity.index.tsx +51 -0
  96. package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
  97. package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
  98. package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +12 -0
  99. package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
  100. package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
  101. package/apps/ui/src/routes/api.v1.sources.ts +12 -0
  102. package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +342 -0
  103. package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +135 -0
  104. package/apps/ui/src/routes/claude-code.index.tsx +48 -0
  105. package/apps/ui/src/routes/codex.$project.tsx +390 -0
  106. package/apps/ui/src/routes/codex.index.tsx +116 -0
  107. package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
  108. package/apps/ui/src/routes/cursor.$workspaceKey.tsx +416 -0
  109. package/apps/ui/src/routes/cursor.index.tsx +120 -0
  110. package/apps/ui/src/routes/index.tsx +124 -0
  111. package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +345 -0
  112. package/apps/ui/src/routes/kiro.$workspaceKey.tsx +136 -0
  113. package/apps/ui/src/routes/kiro.index.tsx +48 -0
  114. package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +334 -0
  115. package/apps/ui/src/routes/opencode.$workspaceKey.tsx +135 -0
  116. package/apps/ui/src/routes/opencode.index.tsx +48 -0
  117. package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +341 -0
  118. package/apps/ui/src/routes/qoder.$workspaceKey.tsx +139 -0
  119. package/apps/ui/src/routes/qoder.index.tsx +48 -0
  120. package/apps/ui/src/routes/settings.tsx +91 -0
  121. package/apps/ui/src/routes/threads.$threadId.tsx +619 -0
  122. package/apps/ui/src/styles.css +122 -0
  123. package/apps/ui/tsconfig.json +29 -0
  124. package/apps/ui/vite.config.ts +84 -0
  125. package/bin/spiracha.ts +32 -0
  126. package/package.json +46 -73
  127. package/src/client.ts +290 -0
  128. package/src/lib/antigravity-db.ts +213 -19
  129. package/src/lib/antigravity-exporter-types.ts +1 -0
  130. package/src/lib/codex-analytics.ts +1 -1
  131. package/src/lib/codex-browser-db.ts +410 -74
  132. package/src/lib/codex-browser-export.ts +6 -14
  133. package/src/lib/codex-browser-types.ts +1 -1
  134. package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
  135. package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +64 -28
  136. package/src/lib/conversation-api.ts +792 -0
  137. package/src/lib/conversation-data/adapter-helpers.ts +110 -0
  138. package/src/lib/conversation-data/antigravity-adapter.ts +148 -0
  139. package/src/lib/conversation-data/claude-code-adapter.ts +175 -0
  140. package/src/lib/conversation-data/codex-adapter.ts +245 -0
  141. package/src/lib/conversation-data/cursor-adapter.ts +171 -0
  142. package/src/lib/conversation-data/index.ts +311 -0
  143. package/src/lib/conversation-data/kiro-adapter.ts +140 -0
  144. package/src/lib/conversation-data/message-selector.ts +37 -0
  145. package/src/lib/conversation-data/opencode-adapter.ts +180 -0
  146. package/src/lib/conversation-data/path-match.ts +79 -0
  147. package/src/lib/conversation-data/qoder-adapter.ts +230 -0
  148. package/src/lib/conversation-data/types.ts +116 -0
  149. package/src/lib/cursor-db.ts +67 -21
  150. package/src/lib/cursor-exporter-types.ts +0 -19
  151. package/src/lib/qoder-acp-client.ts +268 -0
  152. package/src/lib/qoder-db.ts +1601 -0
  153. package/src/lib/qoder-exporter-types.ts +114 -0
  154. package/src/lib/qoder-transcript-phase.ts +41 -0
  155. package/src/lib/qoder-transcript.ts +112 -0
  156. package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
  157. package/apps/ui/dist/client/assets/analytics-DOy1b6Sh.js +0 -1
  158. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DqySVO2K.js +0 -7
  159. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
  160. package/apps/ui/dist/client/assets/antigravity-keychain-panel-B77qJz6f.js +0 -1
  161. package/apps/ui/dist/client/assets/antigravity._workspaceKey-B3NM6gRH.js +0 -1
  162. package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
  163. package/apps/ui/dist/client/assets/antigravity.index-DP9JC2P9.js +0 -1
  164. package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
  165. package/apps/ui/dist/client/assets/badge-C3i4OKs8.js +0 -1
  166. package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
  167. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-8pQ55ci2.js +0 -1
  168. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-DVz5UmtU.js +0 -1
  169. package/apps/ui/dist/client/assets/claude-code._workspaceKey-BBKskSum.js +0 -1
  170. package/apps/ui/dist/client/assets/claude-code._workspaceKey-OkikMUFs.js +0 -1
  171. package/apps/ui/dist/client/assets/claude-code.index-Cwu76dbb.js +0 -1
  172. package/apps/ui/dist/client/assets/claude-code.index-D6eMd08t.js +0 -1
  173. package/apps/ui/dist/client/assets/createServerFn-B-KkwmHU.js +0 -3
  174. package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
  175. package/apps/ui/dist/client/assets/cursor-threads._composerId-hBWlP1D-.js +0 -1
  176. package/apps/ui/dist/client/assets/cursor._workspaceKey-BFPkIRi-.js +0 -1
  177. package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
  178. package/apps/ui/dist/client/assets/cursor.index-8y4o20kb.js +0 -2
  179. package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
  180. package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
  181. package/apps/ui/dist/client/assets/delete-confirm-dialog-Bxs07u-j.js +0 -7
  182. package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
  183. package/apps/ui/dist/client/assets/dist-BrJhN2A0.js +0 -5
  184. package/apps/ui/dist/client/assets/dist-Cmd3AIfD.js +0 -1
  185. package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
  186. package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
  187. package/apps/ui/dist/client/assets/dropdown-menu-D5cvPaZt.js +0 -1
  188. package/apps/ui/dist/client/assets/es2015-ad6040dw.js +0 -41
  189. package/apps/ui/dist/client/assets/export-dialog-Dr8d2MqD.js +0 -1
  190. package/apps/ui/dist/client/assets/formatters-BtqyrX_c.js +0 -1
  191. package/apps/ui/dist/client/assets/index-C-STgIH4.js +0 -166
  192. package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
  193. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-CKgfJ7GR.js +0 -1
  194. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-zvIJHtQF.js +0 -1
  195. package/apps/ui/dist/client/assets/kiro._workspaceKey-DhVuJuZB.js +0 -1
  196. package/apps/ui/dist/client/assets/kiro._workspaceKey-QArsjkTY.js +0 -1
  197. package/apps/ui/dist/client/assets/kiro.index-C-fr4m7i.js +0 -1
  198. package/apps/ui/dist/client/assets/kiro.index-OYgAdtZd.js +0 -1
  199. package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
  200. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-DLjiTst-.js +0 -1
  201. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-biBwrRbt.js +0 -8
  202. package/apps/ui/dist/client/assets/opencode._workspaceKey-C7OIzMOJ.js +0 -1
  203. package/apps/ui/dist/client/assets/opencode._workspaceKey-sBakX0oI.js +0 -1
  204. package/apps/ui/dist/client/assets/opencode.index-Bl_kNs9N.js +0 -1
  205. package/apps/ui/dist/client/assets/opencode.index-CN53bVmb.js +0 -1
  206. package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
  207. package/apps/ui/dist/client/assets/preload-helper-B8fFrdxC.js +0 -1
  208. package/apps/ui/dist/client/assets/projects._project-D8WloxQN.js +0 -1
  209. package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
  210. package/apps/ui/dist/client/assets/projects.index-5xRtuJdX.js +0 -3
  211. package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
  212. package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
  213. package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
  214. package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
  215. package/apps/ui/dist/client/assets/routes-DnzF_9CX.js +0 -1
  216. package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
  217. package/apps/ui/dist/client/assets/select-CYF6pF37.js +0 -1
  218. package/apps/ui/dist/client/assets/settings-Dx70jgP_.js +0 -1
  219. package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
  220. package/apps/ui/dist/client/assets/styles-B7Zi9EFA.css +0 -1
  221. package/apps/ui/dist/client/assets/tabs-CmuhkT-c.js +0 -7
  222. package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
  223. package/apps/ui/dist/client/assets/thread-transcript-stats-BUNz6u7F.js +0 -1
  224. package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
  225. package/apps/ui/dist/client/assets/threads._threadId-BMRSFow3.js +0 -1
  226. package/apps/ui/dist/client/assets/useMutation-BSFult2i.js +0 -1
  227. package/apps/ui/dist/client/assets/useQuery-CjINKZf1.js +0 -1
  228. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CQ8Jjs3B.js +0 -387
  229. package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
  230. package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
  231. package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
  232. package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
  233. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BKOyL2T6.js +0 -613
  234. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
  235. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-eS87Dx7g.js +0 -20
  236. package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
  237. package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
  238. package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
  239. package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
  240. package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
  241. package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
  242. package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
  243. package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
  244. package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
  245. package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
  246. package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
  247. package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
  248. package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
  249. package/apps/ui/dist/server/assets/claude-code-db-Ik3VStKZ.js +0 -361
  250. package/apps/ui/dist/server/assets/claude-code-queries-Cgzi4K-e.js +0 -37
  251. package/apps/ui/dist/server/assets/claude-code-server-PfraClXS.js +0 -72
  252. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-C55oe6zj.js +0 -18
  253. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-D1RYYi3j.js +0 -473
  254. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-DSvPLEUL.js +0 -11
  255. package/apps/ui/dist/server/assets/claude-code-transcript-C-bxh3cS.js +0 -139
  256. package/apps/ui/dist/server/assets/claude-code-transcript-phase-DmUMc8VU.js +0 -10
  257. package/apps/ui/dist/server/assets/claude-code._workspaceKey-B5Xg_-tE.js +0 -28
  258. package/apps/ui/dist/server/assets/claude-code._workspaceKey-BPlzyXPa.js +0 -11
  259. package/apps/ui/dist/server/assets/claude-code._workspaceKey-DqDve2wl.js +0 -191
  260. package/apps/ui/dist/server/assets/claude-code.index-B7yYR7dD.js +0 -92
  261. package/apps/ui/dist/server/assets/claude-code.index-BUHThG1W.js +0 -11
  262. package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
  263. package/apps/ui/dist/server/assets/codex-server-CGXU4H8-.js +0 -2294
  264. package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
  265. package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
  266. package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
  267. package/apps/ui/dist/server/assets/cursor-db-CZnYy7aT.js +0 -830
  268. package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
  269. package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
  270. package/apps/ui/dist/server/assets/cursor-recovery-CXpO8KcD.js +0 -361
  271. package/apps/ui/dist/server/assets/cursor-server-DNu52A3K.js +0 -213
  272. package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
  273. package/apps/ui/dist/server/assets/cursor-threads._composerId-Ckn6ItHI.js +0 -582
  274. package/apps/ui/dist/server/assets/cursor-threads._composerId-p9-ARAzc.js +0 -18
  275. package/apps/ui/dist/server/assets/cursor-transcript-B7tGnqtn.js +0 -125
  276. package/apps/ui/dist/server/assets/cursor._workspaceKey-CZuohIUL.js +0 -28
  277. package/apps/ui/dist/server/assets/cursor._workspaceKey-DPp5JSjy.js +0 -401
  278. package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
  279. package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
  280. package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
  281. package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
  282. package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
  283. package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
  284. package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
  285. package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
  286. package/apps/ui/dist/server/assets/export-dialog-D88ze9Gy.js +0 -240
  287. package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
  288. package/apps/ui/dist/server/assets/kiro-db--fXrED_-.js +0 -552
  289. package/apps/ui/dist/server/assets/kiro-queries-CzNlM2ok.js +0 -37
  290. package/apps/ui/dist/server/assets/kiro-server-9WRxg2Au.js +0 -72
  291. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-CRY2c7YO.js +0 -11
  292. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-F1UlwxmR.js +0 -449
  293. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-bvTulT7C.js +0 -18
  294. package/apps/ui/dist/server/assets/kiro-transcript-DoekQBaH.js +0 -112
  295. package/apps/ui/dist/server/assets/kiro-transcript-phase-CJG40nQg.js +0 -27
  296. package/apps/ui/dist/server/assets/kiro._workspaceKey-BDe9sQwo.js +0 -11
  297. package/apps/ui/dist/server/assets/kiro._workspaceKey-CJsjgNwO.js +0 -28
  298. package/apps/ui/dist/server/assets/kiro._workspaceKey-D_-4Qb7y.js +0 -193
  299. package/apps/ui/dist/server/assets/kiro.index-C__-UROk.js +0 -11
  300. package/apps/ui/dist/server/assets/kiro.index-CmTbqeYj.js +0 -93
  301. package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
  302. package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
  303. package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
  304. package/apps/ui/dist/server/assets/opencode-db-BM7KjOzc.js +0 -397
  305. package/apps/ui/dist/server/assets/opencode-queries-BeNYXnQv.js +0 -50
  306. package/apps/ui/dist/server/assets/opencode-server-BpQ_kRze.js +0 -72
  307. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-CGQ-t0vD.js +0 -518
  308. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-D8Q7in_Z.js +0 -18
  309. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-DKOvi0lV.js +0 -11
  310. package/apps/ui/dist/server/assets/opencode-think-tags-CAoD-EcZ.js +0 -87
  311. package/apps/ui/dist/server/assets/opencode-transcript-BNTcGNym.js +0 -141
  312. package/apps/ui/dist/server/assets/opencode-transcript-phase-OqLkiQD0.js +0 -32
  313. package/apps/ui/dist/server/assets/opencode._workspaceKey-BdqcWHw1.js +0 -11
  314. package/apps/ui/dist/server/assets/opencode._workspaceKey-CfH96HTT.js +0 -28
  315. package/apps/ui/dist/server/assets/opencode._workspaceKey-DdbHPDrO.js +0 -207
  316. package/apps/ui/dist/server/assets/opencode.index-BbVzkJXP.js +0 -11
  317. package/apps/ui/dist/server/assets/opencode.index-Vh_Gq_1L.js +0 -93
  318. package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
  319. package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
  320. package/apps/ui/dist/server/assets/projects._project-4io5LO0E.js +0 -20
  321. package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
  322. package/apps/ui/dist/server/assets/projects._project-D6ZoGhJ8.js +0 -395
  323. package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
  324. package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
  325. package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
  326. package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
  327. package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
  328. package/apps/ui/dist/server/assets/router-Sac2DGk6.js +0 -518
  329. package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
  330. package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
  331. package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
  332. package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
  333. package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
  334. package/apps/ui/dist/server/assets/shared-DyhChtHf.js +0 -137
  335. package/apps/ui/dist/server/assets/source-session-export-server-DNutCawb.js +0 -38
  336. package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
  337. package/apps/ui/dist/server/assets/start-B_HFwkkk.js +0 -4
  338. package/apps/ui/dist/server/assets/tabs-DkaChNVg.js +0 -501
  339. package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
  340. package/apps/ui/dist/server/assets/thread-transcript-stats-Dh34mt2u.js +0 -45
  341. package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
  342. package/apps/ui/dist/server/assets/threads._threadId-BeXEc4Lo.js +0 -648
  343. package/apps/ui/dist/server/assets/threads._threadId-Ci0S4mKU.js +0 -18
  344. package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
  345. package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
  346. package/apps/ui/dist/server/server.js +0 -5895
  347. package/bin/codex-chats-claude.js +0 -5
  348. package/bin/codex-chats.js +0 -5
  349. package/bin/spiracha.js +0 -5
  350. package/src/export-chats.ts +0 -120
  351. package/src/export-claude.ts +0 -36
  352. package/src/export-cursor.ts +0 -244
  353. package/src/lib/claude-exporter.ts +0 -864
  354. package/src/lib/codex-exporter-cli.ts +0 -277
  355. package/src/lib/codex-exporter-db.ts +0 -319
  356. package/src/lib/codex-exporter.ts +0 -115
  357. package/src/lib/cursor-exporter.ts +0 -266
  358. package/src/lib/interactive-cli.ts +0 -433
  359. package/src/lib/native-open.ts +0 -54
  360. package/src/mcp-server.ts +0 -137
  361. package/src/spiracha.ts +0 -116
  362. package/src/ui-cli.ts +0 -310
  363. /package/apps/ui/{dist/client → public}/icon.svg +0 -0
  364. /package/apps/ui/{dist/client → public}/manifest.json +0 -0
  365. /package/apps/ui/{dist/client → public}/robots.txt +0 -0
@@ -1,864 +0,0 @@
1
- import { access, lstat, readdir } from 'node:fs/promises';
2
- import path from 'node:path';
3
- import {
4
- asBoolean,
5
- asNumber,
6
- asObject,
7
- asString,
8
- CliUsageError,
9
- cleanExtractedText,
10
- cleanInlineTitle,
11
- type ExportFormat,
12
- expandHome,
13
- formatInlineLiteral,
14
- type JsonValue,
15
- type MetadataEntry,
16
- readJsonlObjects,
17
- renderCodeBlock,
18
- renderDocumentTitle,
19
- renderMetadataBlock,
20
- renderSection,
21
- writeExportFile,
22
- } from './shared';
23
-
24
- export type ClaudeCliOptions = {
25
- inputPath: string;
26
- outputPath: string | null;
27
- outputFormat: ExportFormat;
28
- includeTools: boolean;
29
- };
30
-
31
- export type ClaudeExportResult = {
32
- outputPath: string;
33
- sourcePath: string;
34
- };
35
-
36
- type ClaudeExportMetadata = {
37
- sessionId?: string;
38
- cliSessionId?: string;
39
- cwd?: string;
40
- originCwd?: string;
41
- createdAt?: number;
42
- lastActivityAt?: number;
43
- model?: string;
44
- effort?: string;
45
- isArchived?: boolean;
46
- title?: string;
47
- titleSource?: string;
48
- permissionMode?: string;
49
- prNumber?: number;
50
- prUrl?: string;
51
- prRepository?: string;
52
- prState?: string;
53
- completedTurns?: number;
54
- };
55
-
56
- type ClaudeSource = {
57
- jsonlPath: string;
58
- metadataPath: string | null;
59
- metadata: ClaudeExportMetadata | null;
60
- outputBaseName: string;
61
- };
62
-
63
- type ClaudeSessionMeta = {
64
- sessionId?: string;
65
- cwd?: string;
66
- entrypoint?: string;
67
- version?: string;
68
- gitBranch?: string;
69
- model?: string;
70
- firstTimestamp?: string;
71
- lastTimestamp?: string;
72
- };
73
-
74
- type BashToolCall = {
75
- id: string;
76
- command: string;
77
- };
78
-
79
- type ConvertState = {
80
- bashCallsById: Map<string, BashToolCall>;
81
- firstUserText: string | null;
82
- };
83
-
84
- export const DEFAULT_OUTPUT_DIR = path.join(process.cwd(), 'exports', 'claude');
85
-
86
- export const parseClaudeCliArgs = (argv: string[]): ClaudeCliOptions => {
87
- let inputPath: string | null = null;
88
- let outputPath: string | null = null;
89
- let outputFormat: ExportFormat = 'md';
90
- let includeTools = false;
91
-
92
- for (let index = 0; index < argv.length; index += 1) {
93
- const nextIndex = applyClaudeCliArg(argv, index, {
94
- includeTools,
95
- inputPath,
96
- outputFormat,
97
- outputPath,
98
- });
99
-
100
- includeTools = nextIndex.state.includeTools;
101
- inputPath = nextIndex.state.inputPath;
102
- outputFormat = nextIndex.state.outputFormat;
103
- outputPath = nextIndex.state.outputPath;
104
- index = nextIndex.index;
105
- }
106
-
107
- if (!inputPath) {
108
- throw new CliUsageError('A Claude export path is required.');
109
- }
110
-
111
- return {
112
- includeTools,
113
- inputPath,
114
- outputFormat,
115
- outputPath,
116
- };
117
- };
118
-
119
- type ClaudeCliState = {
120
- inputPath: string | null;
121
- outputPath: string | null;
122
- outputFormat: ExportFormat;
123
- includeTools: boolean;
124
- };
125
-
126
- type ClaudeCliNext = {
127
- index: number;
128
- state: ClaudeCliState;
129
- };
130
-
131
- const applyClaudeCliArg = (argv: string[], index: number, state: ClaudeCliState): ClaudeCliNext => {
132
- const arg = argv[index];
133
-
134
- if (arg === '--input' || arg === '-i') {
135
- return {
136
- index: index + 1,
137
- state: {
138
- ...state,
139
- inputPath: expandHome(requireValue(argv[index + 1], arg)),
140
- },
141
- };
142
- }
143
-
144
- if (arg === '--output' || arg === '-o') {
145
- return {
146
- index: index + 1,
147
- state: {
148
- ...state,
149
- outputPath: expandHome(requireValue(argv[index + 1], arg)),
150
- },
151
- };
152
- }
153
-
154
- if (arg === '--tools') {
155
- return {
156
- index,
157
- state: {
158
- ...state,
159
- includeTools: true,
160
- },
161
- };
162
- }
163
-
164
- if (arg.startsWith('--output-format=')) {
165
- return {
166
- index,
167
- state: {
168
- ...state,
169
- outputFormat: parseExportFormat(arg.slice('--output-format='.length)),
170
- },
171
- };
172
- }
173
-
174
- if (arg === '--output-format') {
175
- return {
176
- index: index + 1,
177
- state: {
178
- ...state,
179
- outputFormat: parseExportFormat(requireValue(argv[index + 1], '--output-format')),
180
- },
181
- };
182
- }
183
-
184
- if (!arg.startsWith('-') && !state.inputPath) {
185
- return {
186
- index,
187
- state: {
188
- ...state,
189
- inputPath: expandHome(arg),
190
- },
191
- };
192
- }
193
-
194
- if (!arg.startsWith('-') && !state.outputPath) {
195
- return {
196
- index,
197
- state: {
198
- ...state,
199
- outputPath: expandHome(arg),
200
- },
201
- };
202
- }
203
-
204
- throw new CliUsageError(`Unknown argument: ${arg}`);
205
- };
206
-
207
- export const getClaudeHelpText = (): string => {
208
- return [
209
- 'Export a Claude Code transcript JSONL to Markdown or TXT.',
210
- '',
211
- 'Usage:',
212
- ' codex-chats-claude --input PATH [--output PATH] [--output-format md|txt] [--tools]',
213
- ' codex-chats-claude PATH [OUTPUT_PATH]',
214
- '',
215
- 'Input:',
216
- ' PATH can be either the exported Claude .jsonl file or the export directory that contains metadata.json.',
217
- '',
218
- 'Options:',
219
- ' --input, -i Claude transcript file or export directory',
220
- ' --output, -o Output file path, or output directory when no .md/.txt extension is supplied',
221
- ' --output-format Output file format: md or txt (default: md)',
222
- ' --tools Include Bash tool calls and their outputs',
223
- ' --help, -h Show this help text',
224
- '',
225
- 'Default output:',
226
- ` ${DEFAULT_OUTPUT_DIR}/<session-id>.md`,
227
- ].join('\n');
228
- };
229
-
230
- export const runClaudeExport = async (options: ClaudeCliOptions): Promise<ClaudeExportResult> => {
231
- const source = await resolveClaudeSource(options.inputPath);
232
- const outputPath = await resolveOutputPath(source, options);
233
- const content = await convertClaudeTranscript(source, outputPath, options);
234
-
235
- if (!content) {
236
- throw new Error(`No transcript content found in ${source.jsonlPath}`);
237
- }
238
-
239
- await writeExportFile(outputPath, content);
240
-
241
- return {
242
- outputPath,
243
- sourcePath: source.jsonlPath,
244
- };
245
- };
246
-
247
- const resolveClaudeSource = async (inputPath: string): Promise<ClaudeSource> => {
248
- const resolvedInput = path.resolve(inputPath);
249
- const stats = await lstat(resolvedInput).catch(() => null);
250
-
251
- if (!stats) {
252
- throw new Error(`Input path does not exist: ${resolvedInput}`);
253
- }
254
-
255
- if (stats.isFile()) {
256
- return await resolveClaudeFileSource(resolvedInput);
257
- }
258
-
259
- if (!stats.isDirectory()) {
260
- throw new Error(`Unsupported input path: ${resolvedInput}`);
261
- }
262
-
263
- return await resolveClaudeDirectorySource(resolvedInput);
264
- };
265
-
266
- const findMetadataPathForJsonl = async (jsonlPath: string): Promise<string | null> => {
267
- const parentMetadataPath = path.join(path.dirname(jsonlPath), 'metadata.json');
268
- return (await fileExists(parentMetadataPath)) ? parentMetadataPath : null;
269
- };
270
-
271
- const readClaudeMetadata = async (metadataPath: string | null): Promise<ClaudeExportMetadata | null> => {
272
- if (!metadataPath) {
273
- return null;
274
- }
275
-
276
- try {
277
- const raw = await Bun.file(metadataPath).text();
278
- return parseClaudeMetadata(JSON.parse(raw) as Record<string, JsonValue>);
279
- } catch {
280
- return null;
281
- }
282
- };
283
-
284
- const resolveOutputPath = async (source: ClaudeSource, options: ClaudeCliOptions): Promise<string> => {
285
- const fileName = `${source.outputBaseName}.${options.outputFormat}`;
286
-
287
- if (!options.outputPath) {
288
- return path.join(DEFAULT_OUTPUT_DIR, fileName);
289
- }
290
-
291
- const resolvedOutput = path.resolve(options.outputPath);
292
- const extension = path.extname(resolvedOutput).toLowerCase();
293
-
294
- if (extension === '.md' || extension === '.txt') {
295
- return resolvedOutput;
296
- }
297
-
298
- const stats = await lstat(resolvedOutput).catch(() => null);
299
- if (stats?.isDirectory()) {
300
- return path.join(resolvedOutput, fileName);
301
- }
302
-
303
- return path.join(resolvedOutput, fileName);
304
- };
305
-
306
- const convertClaudeTranscript = async (
307
- source: ClaudeSource,
308
- outputPath: string,
309
- options: ClaudeCliOptions,
310
- ): Promise<string | null> => {
311
- const sessionMeta: ClaudeSessionMeta = {
312
- cwd: source.metadata?.cwd ?? source.metadata?.originCwd,
313
- model: source.metadata?.model,
314
- sessionId: source.metadata?.cliSessionId ?? source.metadata?.sessionId,
315
- };
316
- const state: ConvertState = {
317
- bashCallsById: new Map(),
318
- firstUserText: null,
319
- };
320
- const sections: string[] = [];
321
-
322
- try {
323
- for await (const parsed of readJsonlObjects(source.jsonlPath)) {
324
- captureClaudeSessionMeta(parsed, sessionMeta);
325
- sections.push(...extractClaudeBlocks(parsed, options, state));
326
- }
327
- } catch (error) {
328
- const message = error instanceof Error ? error.message : String(error);
329
- throw new Error(`Failed to read Claude transcript ${source.jsonlPath}: ${message}`);
330
- }
331
-
332
- if (sections.length === 0) {
333
- return null;
334
- }
335
-
336
- const title = getTitle(source, state, sessionMeta);
337
- const metadata = buildMetadataEntries(source, outputPath, sessionMeta);
338
- const parts = [
339
- renderDocumentTitle(title, options.outputFormat),
340
- '',
341
- renderMetadataBlock(metadata, options.outputFormat),
342
- ...sections,
343
- ].filter(Boolean);
344
-
345
- return parts.join('\n').trimEnd() + '\n';
346
- };
347
-
348
- const resolveClaudeFileSource = async (resolvedInput: string): Promise<ClaudeSource> => {
349
- if (!resolvedInput.endsWith('.jsonl')) {
350
- throw new Error(`Expected a .jsonl file, got: ${resolvedInput}`);
351
- }
352
-
353
- const metadataPath = await findMetadataPathForJsonl(resolvedInput);
354
- const metadata = await readClaudeMetadata(metadataPath);
355
-
356
- return {
357
- jsonlPath: resolvedInput,
358
- metadata,
359
- metadataPath,
360
- outputBaseName: metadata?.cliSessionId ?? path.basename(resolvedInput, '.jsonl'),
361
- };
362
- };
363
-
364
- const resolveClaudeDirectorySource = async (resolvedInput: string): Promise<ClaudeSource> => {
365
- const metadataPathCandidate = path.join(resolvedInput, 'metadata.json');
366
- const metadataPath = (await fileExists(metadataPathCandidate)) ? metadataPathCandidate : null;
367
- const metadata = await readClaudeMetadata(metadataPath);
368
- const jsonlPath = await findClaudeJsonlPath(resolvedInput, metadata);
369
-
370
- return {
371
- jsonlPath,
372
- metadata,
373
- metadataPath,
374
- outputBaseName: metadata?.cliSessionId ?? path.basename(jsonlPath, '.jsonl'),
375
- };
376
- };
377
-
378
- const findClaudeJsonlPath = async (resolvedInput: string, metadata: ClaudeExportMetadata | null): Promise<string> => {
379
- if (metadata?.cliSessionId) {
380
- const metadataJsonlPath = path.join(resolvedInput, `${metadata.cliSessionId}.jsonl`);
381
- if (await fileExists(metadataJsonlPath)) {
382
- return metadataJsonlPath;
383
- }
384
- }
385
-
386
- const files = (await readdir(resolvedInput, { withFileTypes: true }))
387
- .filter((entry) => entry.isFile() && entry.name.endsWith('.jsonl'))
388
- .map((entry) => path.join(resolvedInput, entry.name))
389
- .sort();
390
-
391
- if (files.length === 1) {
392
- return files[0] ?? resolvedInput;
393
- }
394
-
395
- if (files.length > 1) {
396
- throw new Error(
397
- `Multiple top-level .jsonl files found in ${resolvedInput}; pass the specific transcript file instead.`,
398
- );
399
- }
400
-
401
- throw new Error(`No top-level Claude transcript .jsonl found in ${resolvedInput}`);
402
- };
403
-
404
- const parseClaudeMetadata = (parsed: Record<string, JsonValue>): ClaudeExportMetadata => ({
405
- ...buildClaudeMetadataIdentity(parsed),
406
- ...buildClaudeMetadataContext(parsed),
407
- ...buildClaudeMetadataActivity(parsed),
408
- ...buildClaudeMetadataProject(parsed),
409
- });
410
-
411
- const buildClaudeMetadataIdentity = (
412
- parsed: Record<string, JsonValue>,
413
- ): Pick<ClaudeExportMetadata, 'cliSessionId' | 'sessionId'> => ({
414
- cliSessionId: asString(parsed.cliSessionId) ?? undefined,
415
- sessionId: asString(parsed.sessionId) ?? undefined,
416
- });
417
-
418
- const buildClaudeMetadataContext = (
419
- parsed: Record<string, JsonValue>,
420
- ): Pick<
421
- ClaudeExportMetadata,
422
- 'cwd' | 'originCwd' | 'model' | 'effort' | 'permissionMode' | 'title' | 'titleSource'
423
- > => ({
424
- cwd: asString(parsed.cwd) ?? undefined,
425
- effort: asString(parsed.effort) ?? undefined,
426
- model: asString(parsed.model) ?? undefined,
427
- originCwd: asString(parsed.originCwd) ?? undefined,
428
- permissionMode: asString(parsed.permissionMode) ?? undefined,
429
- title: asString(parsed.title) ?? undefined,
430
- titleSource: asString(parsed.titleSource) ?? undefined,
431
- });
432
-
433
- const buildClaudeMetadataActivity = (
434
- parsed: Record<string, JsonValue>,
435
- ): Pick<ClaudeExportMetadata, 'completedTurns' | 'createdAt' | 'lastActivityAt' | 'isArchived'> => ({
436
- completedTurns: asNumber(parsed.completedTurns) ?? undefined,
437
- createdAt: asNumber(parsed.createdAt) ?? undefined,
438
- isArchived: typeof parsed.isArchived === 'boolean' ? parsed.isArchived : undefined,
439
- lastActivityAt: asNumber(parsed.lastActivityAt) ?? undefined,
440
- });
441
-
442
- const buildClaudeMetadataProject = (
443
- parsed: Record<string, JsonValue>,
444
- ): Pick<ClaudeExportMetadata, 'prNumber' | 'prRepository' | 'prState' | 'prUrl'> => ({
445
- prNumber: asNumber(parsed.prNumber) ?? undefined,
446
- prRepository: asString(parsed.prRepository) ?? undefined,
447
- prState: asString(parsed.prState) ?? undefined,
448
- prUrl: asString(parsed.prUrl) ?? undefined,
449
- });
450
-
451
- const captureClaudeSessionMeta = (parsed: Record<string, JsonValue>, meta: ClaudeSessionMeta) => {
452
- meta.sessionId = asString(parsed.sessionId) ?? meta.sessionId;
453
- meta.cwd = asString(parsed.cwd) ?? meta.cwd;
454
- meta.entrypoint = asString(parsed.entrypoint) ?? meta.entrypoint;
455
- meta.version = asString(parsed.version) ?? meta.version;
456
- meta.gitBranch = asString(parsed.gitBranch) ?? meta.gitBranch;
457
-
458
- const timestamp = asString(parsed.timestamp);
459
- if (timestamp) {
460
- meta.firstTimestamp = meta.firstTimestamp && meta.firstTimestamp < timestamp ? meta.firstTimestamp : timestamp;
461
- meta.lastTimestamp = meta.lastTimestamp && meta.lastTimestamp > timestamp ? meta.lastTimestamp : timestamp;
462
- }
463
-
464
- const message = asObject(parsed.message);
465
- meta.model = asString(message?.model ?? null) ?? meta.model;
466
- };
467
-
468
- const extractClaudeBlocks = (
469
- parsed: Record<string, JsonValue>,
470
- options: ClaudeCliOptions,
471
- state: ConvertState,
472
- ): string[] => {
473
- if (asBoolean(parsed.isCompactSummary)) {
474
- return [];
475
- }
476
-
477
- const type = asString(parsed.type);
478
- if (type === 'assistant') {
479
- return extractAssistantBlocks(parsed, options, state);
480
- }
481
-
482
- if (type === 'user') {
483
- return extractUserBlocks(parsed, options, state);
484
- }
485
-
486
- return [];
487
- };
488
-
489
- const extractAssistantBlocks = (
490
- parsed: Record<string, JsonValue>,
491
- options: ClaudeCliOptions,
492
- state: ConvertState,
493
- ): string[] => {
494
- const message = asObject(parsed.message);
495
- if (!message || asString(message.role) !== 'assistant') {
496
- return [];
497
- }
498
-
499
- return extractBlocksFromContentSequence('assistant', message.content, options, state);
500
- };
501
-
502
- const extractUserBlocks = (
503
- parsed: Record<string, JsonValue>,
504
- options: ClaudeCliOptions,
505
- state: ConvertState,
506
- ): string[] => {
507
- const message = asObject(parsed.message);
508
- if (!message || asString(message.role) !== 'user') {
509
- return [];
510
- }
511
-
512
- return extractBlocksFromContentSequence('user', message.content, options, state);
513
- };
514
-
515
- const extractBlocksFromContentSequence = (
516
- role: 'user' | 'assistant',
517
- content: JsonValue,
518
- options: ClaudeCliOptions,
519
- state: ConvertState,
520
- ): string[] => {
521
- const items = Array.isArray(content) ? content : [content];
522
- const blocks: string[] = [];
523
- const textParts: string[] = [];
524
-
525
- const flushText = () => {
526
- const text = cleanExtractedText(textParts.join('\n\n')).trim();
527
- textParts.length = 0;
528
-
529
- if (!text) {
530
- return;
531
- }
532
-
533
- if (role === 'user' && !state.firstUserText) {
534
- state.firstUserText = text;
535
- }
536
-
537
- blocks.push(renderSection(role === 'user' ? 'User' : 'Assistant', text, options.outputFormat));
538
- };
539
-
540
- for (const item of items) {
541
- appendClaudeContentItem(item, options, state, blocks, textParts, flushText);
542
- }
543
-
544
- flushText();
545
- return blocks;
546
- };
547
-
548
- const appendClaudeContentItem = (
549
- item: JsonValue,
550
- options: ClaudeCliOptions,
551
- state: ConvertState,
552
- blocks: string[],
553
- textParts: string[],
554
- flushText: () => void,
555
- ) => {
556
- if (typeof item === 'string') {
557
- textParts.push(item);
558
- return;
559
- }
560
-
561
- const contentItem = asObject(item);
562
- if (!contentItem) {
563
- return;
564
- }
565
-
566
- appendClaudeStructuredContentItem(contentItem, options, state, blocks, textParts, flushText);
567
- };
568
-
569
- const appendClaudeStructuredContentItem = (
570
- contentItem: Record<string, JsonValue>,
571
- options: ClaudeCliOptions,
572
- state: ConvertState,
573
- blocks: string[],
574
- textParts: string[],
575
- flushText: () => void,
576
- ) => {
577
- const type = asString(contentItem.type);
578
- if (isClaudeTextContentType(type)) {
579
- appendClaudeTextContentItem(contentItem, textParts);
580
- return;
581
- }
582
-
583
- if (type === 'thinking') {
584
- flushText();
585
- return;
586
- }
587
-
588
- if (type === 'tool_use') {
589
- appendClaudeToolUseContentItem(contentItem, options, state, blocks, flushText);
590
- return;
591
- }
592
-
593
- if (type === 'tool_result') {
594
- appendClaudeToolResultContentItem(contentItem, options, state, blocks, flushText);
595
- return;
596
- }
597
-
598
- appendClaudeFallbackContentItem(contentItem, textParts);
599
- };
600
-
601
- const appendClaudeTextContentItem = (contentItem: Record<string, JsonValue>, textParts: string[]) => {
602
- const text = asString(contentItem.text);
603
- if (text) {
604
- textParts.push(text);
605
- }
606
- };
607
-
608
- const appendClaudeToolUseContentItem = (
609
- contentItem: Record<string, JsonValue>,
610
- options: ClaudeCliOptions,
611
- state: ConvertState,
612
- blocks: string[],
613
- flushText: () => void,
614
- ) => {
615
- flushText();
616
- captureBashToolCall(contentItem, state);
617
-
618
- if (options.includeTools) {
619
- const block = renderToolCallBlock(contentItem, options.outputFormat);
620
- if (block) {
621
- blocks.push(block);
622
- }
623
- }
624
- };
625
-
626
- const appendClaudeToolResultContentItem = (
627
- contentItem: Record<string, JsonValue>,
628
- options: ClaudeCliOptions,
629
- state: ConvertState,
630
- blocks: string[],
631
- flushText: () => void,
632
- ) => {
633
- flushText();
634
-
635
- if (options.includeTools) {
636
- const block = renderToolResultBlock(contentItem, state, options.outputFormat);
637
- if (block) {
638
- blocks.push(block);
639
- }
640
- }
641
- };
642
-
643
- const appendClaudeFallbackContentItem = (contentItem: Record<string, JsonValue>, textParts: string[]) => {
644
- const fallbackText = asString(contentItem.text);
645
- if (fallbackText) {
646
- textParts.push(fallbackText);
647
- }
648
- };
649
-
650
- const isClaudeTextContentType = (type: string | null): boolean =>
651
- type === 'text' || type === 'input_text' || type === 'output_text';
652
-
653
- const captureBashToolCall = (contentItem: Record<string, JsonValue>, state: ConvertState) => {
654
- if (asString(contentItem.name) !== 'Bash') {
655
- return;
656
- }
657
-
658
- const toolId = asString(contentItem.id);
659
- const input = asObject(contentItem.input);
660
- const command = asString(input?.command ?? null);
661
-
662
- if (!toolId || !command) {
663
- return;
664
- }
665
-
666
- state.bashCallsById.set(toolId, {
667
- command,
668
- id: toolId,
669
- });
670
- };
671
-
672
- const renderToolCallBlock = (contentItem: Record<string, JsonValue>, outputFormat: ExportFormat): string => {
673
- if (asString(contentItem.name) !== 'Bash') {
674
- return '';
675
- }
676
-
677
- const input = asObject(contentItem.input);
678
- const command = asString(input?.command ?? null)?.trim();
679
- if (!command) {
680
- return '';
681
- }
682
-
683
- return renderSection('Tool', `Command: ${formatInlineLiteral(command, outputFormat)}`, outputFormat);
684
- };
685
-
686
- const renderToolResultBlock = (
687
- contentItem: Record<string, JsonValue>,
688
- state: ConvertState,
689
- outputFormat: ExportFormat,
690
- ): string => {
691
- const toolUseId = asString(contentItem.tool_use_id);
692
- if (!toolUseId || !state.bashCallsById.has(toolUseId)) {
693
- return '';
694
- }
695
-
696
- const outputText = cleanExtractedText(extractClaudeText(contentItem.content)).trim();
697
- if (!outputText) {
698
- return '';
699
- }
700
-
701
- const lines: string[] = [];
702
- if (asBoolean(contentItem.is_error)) {
703
- lines.push('Error: true', '');
704
- }
705
- lines.push(renderCodeBlock(outputText, outputFormat));
706
-
707
- return renderSection('Tool Output', lines.join('\n'), outputFormat);
708
- };
709
-
710
- const getTitle = (source: ClaudeSource, state: ConvertState, sessionMeta: ClaudeSessionMeta): string => {
711
- if (source.metadata?.title) {
712
- return cleanInlineTitle(source.metadata.title);
713
- }
714
-
715
- if (state.firstUserText) {
716
- return cleanInlineTitle(state.firstUserText);
717
- }
718
-
719
- return source.metadata?.cliSessionId ?? sessionMeta.sessionId ?? path.basename(source.jsonlPath, '.jsonl');
720
- };
721
-
722
- const buildMetadataEntries = (
723
- source: ClaudeSource,
724
- outputPath: string,
725
- sessionMeta: ClaudeSessionMeta,
726
- ): MetadataEntry[] => {
727
- return [
728
- { key: 'exported_from', value: 'claude_code_session_export_jsonl' },
729
- ...buildClaudeSourceMetadataEntries(source, outputPath),
730
- ...buildClaudeTimelineMetadataEntries(source, sessionMeta),
731
- ...buildClaudePullRequestMetadataEntries(source),
732
- ];
733
- };
734
-
735
- const buildClaudeSourceMetadataEntries = (source: ClaudeSource, outputPath: string): MetadataEntry[] => {
736
- return [
737
- {
738
- key: 'session_id',
739
- value: source.metadata?.cliSessionId ?? source.metadata?.sessionId ?? null,
740
- },
741
- { key: 'title', value: source.metadata?.title ?? null },
742
- { key: 'source_transcript_path', value: source.jsonlPath },
743
- { key: 'source_metadata_path', value: source.metadataPath },
744
- { key: 'output_path', value: outputPath },
745
- ];
746
- };
747
-
748
- const buildClaudeTimelineMetadataEntries = (source: ClaudeSource, sessionMeta: ClaudeSessionMeta): MetadataEntry[] => {
749
- return [
750
- {
751
- key: 'cwd',
752
- value: source.metadata?.cwd ?? source.metadata?.originCwd ?? sessionMeta.cwd ?? null,
753
- },
754
- { key: 'entrypoint', value: sessionMeta.entrypoint ?? null },
755
- { key: 'model', value: source.metadata?.model ?? sessionMeta.model ?? null },
756
- { key: 'effort', value: source.metadata?.effort ?? null },
757
- { key: 'permission_mode', value: source.metadata?.permissionMode ?? null },
758
- { key: 'is_archived', value: source.metadata?.isArchived ?? null },
759
- { key: 'completed_turns', value: source.metadata?.completedTurns ?? null },
760
- { key: 'created_at_unix_ms', value: source.metadata?.createdAt ?? null },
761
- { key: 'created_at_iso', value: formatUnixMillis(source.metadata?.createdAt ?? null) },
762
- {
763
- key: 'last_activity_at_unix_ms',
764
- value: source.metadata?.lastActivityAt ?? null,
765
- },
766
- {
767
- key: 'last_activity_at_iso',
768
- value: formatUnixMillis(source.metadata?.lastActivityAt ?? null),
769
- },
770
- { key: 'first_event_at_iso', value: sessionMeta.firstTimestamp ?? null },
771
- { key: 'last_event_at_iso', value: sessionMeta.lastTimestamp ?? null },
772
- { key: 'version', value: sessionMeta.version ?? null },
773
- { key: 'git_branch', value: sessionMeta.gitBranch ?? null },
774
- ];
775
- };
776
-
777
- const buildClaudePullRequestMetadataEntries = (source: ClaudeSource): MetadataEntry[] => {
778
- return [
779
- { key: 'pr_number', value: source.metadata?.prNumber ?? null },
780
- { key: 'pr_url', value: source.metadata?.prUrl ?? null },
781
- { key: 'pr_repository', value: source.metadata?.prRepository ?? null },
782
- { key: 'pr_state', value: source.metadata?.prState ?? null },
783
- ];
784
- };
785
-
786
- const formatUnixMillis = (value: number | null): string | null => {
787
- if (value === null || value === undefined) {
788
- return null;
789
- }
790
-
791
- return new Date(value).toISOString();
792
- };
793
-
794
- const extractClaudeText = (content: JsonValue): string => {
795
- if (typeof content === 'string') {
796
- return content;
797
- }
798
-
799
- if (Array.isArray(content)) {
800
- return content
801
- .map((item) => extractClaudeContentPart(item))
802
- .filter((part) => part.length > 0)
803
- .join('\n\n');
804
- }
805
-
806
- const object = asObject(content);
807
- if (!object) {
808
- return '';
809
- }
810
-
811
- const type = asString(object.type);
812
- const text = asString(object.text);
813
-
814
- if ((type === 'text' || type === 'input_text' || type === 'output_text') && text) {
815
- return text;
816
- }
817
-
818
- return text ?? '';
819
- };
820
-
821
- const extractClaudeContentPart = (value: JsonValue): string => {
822
- if (typeof value === 'string') {
823
- return value;
824
- }
825
-
826
- const object = asObject(value);
827
- if (!object) {
828
- return '';
829
- }
830
-
831
- const type = asString(object.type);
832
- const text = asString(object.text);
833
-
834
- if ((type === 'text' || type === 'input_text' || type === 'output_text') && text) {
835
- return text;
836
- }
837
-
838
- return text ?? '';
839
- };
840
-
841
- const fileExists = async (targetPath: string): Promise<boolean> => {
842
- try {
843
- await access(targetPath);
844
- return true;
845
- } catch {
846
- return false;
847
- }
848
- };
849
-
850
- const requireValue = (value: string | undefined, flag: string): string => {
851
- if (!value || (value.startsWith('-') && value !== '-')) {
852
- throw new CliUsageError(`Missing value for ${flag}`);
853
- }
854
-
855
- return value;
856
- };
857
-
858
- const parseExportFormat = (value: string): ExportFormat => {
859
- if (value === 'md' || value === 'txt') {
860
- return value;
861
- }
862
-
863
- throw new CliUsageError(`Unsupported output format: ${value}`);
864
- };