spiracha 1.4.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 (319) hide show
  1. package/AGENTS.md +116 -164
  2. package/README.md +92 -237
  3. package/apps/ui/AGENTS.md +36 -7
  4. package/apps/ui/README.md +79 -6
  5. package/apps/ui/components.json +21 -0
  6. package/apps/ui/package.json +65 -0
  7. package/apps/ui/{dist/client → public}/icon.svg +2 -1
  8. package/apps/ui/src/components/antigravity-conversations-table.tsx +206 -0
  9. package/apps/ui/src/components/antigravity-keychain-panel.tsx +76 -0
  10. package/apps/ui/src/components/antigravity-workspaces-table.tsx +63 -0
  11. package/apps/ui/src/components/app-shell.tsx +124 -0
  12. package/apps/ui/src/components/breadcrumbs.tsx +53 -0
  13. package/apps/ui/src/components/claude-code-sessions-table.tsx +114 -0
  14. package/apps/ui/src/components/claude-code-workspaces-table.tsx +55 -0
  15. package/apps/ui/src/components/cursor-threads-table.tsx +189 -0
  16. package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
  17. package/apps/ui/src/components/data-table.tsx +241 -0
  18. package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
  19. package/apps/ui/src/components/export-dialog.tsx +184 -0
  20. package/apps/ui/src/components/json-panel.tsx +17 -0
  21. package/apps/ui/src/components/kiro-sessions-table.tsx +112 -0
  22. package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
  23. package/apps/ui/src/components/list-search-input.tsx +18 -0
  24. package/apps/ui/src/components/loading-panel.tsx +23 -0
  25. package/apps/ui/src/components/metadata-section.tsx +31 -0
  26. package/apps/ui/src/components/metric-card.tsx +19 -0
  27. package/apps/ui/src/components/opencode-sessions-table.tsx +128 -0
  28. package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
  29. package/apps/ui/src/components/page-header.tsx +33 -0
  30. package/apps/ui/src/components/projects-loading-state.tsx +74 -0
  31. package/apps/ui/src/components/projects-table.tsx +108 -0
  32. package/apps/ui/src/components/qoder-sessions-table.tsx +112 -0
  33. package/apps/ui/src/components/qoder-workspaces-table.tsx +53 -0
  34. package/apps/ui/src/components/recent-threads-list.tsx +52 -0
  35. package/apps/ui/src/components/reload-error-panel.tsx +20 -0
  36. package/apps/ui/src/components/text-document-panel.tsx +19 -0
  37. package/apps/ui/src/components/theme-toggle.tsx +48 -0
  38. package/apps/ui/src/components/threads-table.tsx +202 -0
  39. package/apps/ui/src/components/transcript-view.tsx +552 -0
  40. package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
  41. package/apps/ui/src/components/ui/badge.tsx +45 -0
  42. package/apps/ui/src/components/ui/button.tsx +62 -0
  43. package/apps/ui/src/components/ui/checkbox.tsx +29 -0
  44. package/apps/ui/src/components/ui/dialog.tsx +137 -0
  45. package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
  46. package/apps/ui/src/components/ui/input.tsx +21 -0
  47. package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
  48. package/apps/ui/src/components/ui/select.tsx +163 -0
  49. package/apps/ui/src/components/ui/separator.tsx +28 -0
  50. package/apps/ui/src/components/ui/sheet.tsx +107 -0
  51. package/apps/ui/src/components/ui/skeleton.tsx +7 -0
  52. package/apps/ui/src/components/ui/table.tsx +76 -0
  53. package/apps/ui/src/components/ui/tabs.tsx +71 -0
  54. package/apps/ui/src/components/ui/tooltip.tsx +44 -0
  55. package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
  56. package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
  57. package/apps/ui/src/lib/antigravity-conversation-state.ts +28 -0
  58. package/apps/ui/src/lib/antigravity-queries.ts +33 -0
  59. package/apps/ui/src/lib/antigravity-server.ts +128 -0
  60. package/apps/ui/src/lib/antigravity-transcript-events.ts +335 -0
  61. package/apps/ui/src/lib/claude-code-queries.ts +26 -0
  62. package/apps/ui/src/lib/claude-code-server.ts +75 -0
  63. package/apps/ui/src/lib/claude-code-transcript-events.ts +154 -0
  64. package/apps/ui/src/lib/codex-queries.ts +66 -0
  65. package/apps/ui/src/lib/codex-server.ts +209 -0
  66. package/apps/ui/src/lib/cursor-queries.ts +37 -0
  67. package/apps/ui/src/lib/cursor-server.ts +272 -0
  68. package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
  69. package/apps/ui/src/lib/download.ts +166 -0
  70. package/apps/ui/src/lib/formatters.ts +149 -0
  71. package/apps/ui/src/lib/kiro-queries.ts +22 -0
  72. package/apps/ui/src/lib/kiro-server.ts +73 -0
  73. package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
  74. package/apps/ui/src/lib/opencode-queries.ts +37 -0
  75. package/apps/ui/src/lib/opencode-server.ts +73 -0
  76. package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
  77. package/apps/ui/src/lib/package-metadata.ts +16 -0
  78. package/apps/ui/src/lib/path-utils.ts +13 -0
  79. package/apps/ui/src/lib/qoder-queries.ts +22 -0
  80. package/apps/ui/src/lib/qoder-server.ts +79 -0
  81. package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
  82. package/apps/ui/src/lib/route-search.ts +68 -0
  83. package/apps/ui/src/lib/settings-store.tsx +59 -0
  84. package/apps/ui/src/lib/source-session-export-server.ts +56 -0
  85. package/apps/ui/src/lib/text-filter.ts +22 -0
  86. package/apps/ui/src/lib/thread-id.ts +3 -0
  87. package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
  88. package/apps/ui/src/lib/utils.ts +7 -0
  89. package/apps/ui/src/routeTree.gen.ts +730 -0
  90. package/apps/ui/src/router.tsx +20 -0
  91. package/apps/ui/src/routes/$threadId.tsx +22 -0
  92. package/apps/ui/src/routes/__root.tsx +127 -0
  93. package/apps/ui/src/routes/analytics.tsx +143 -0
  94. package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +438 -0
  95. package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +127 -0
  96. package/apps/ui/src/routes/antigravity.index.tsx +51 -0
  97. package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
  98. package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
  99. package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +12 -0
  100. package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
  101. package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
  102. package/apps/ui/src/routes/api.v1.sources.ts +12 -0
  103. package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +342 -0
  104. package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +135 -0
  105. package/apps/ui/src/routes/claude-code.index.tsx +48 -0
  106. package/apps/ui/src/routes/codex.$project.tsx +390 -0
  107. package/apps/ui/src/routes/codex.index.tsx +116 -0
  108. package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
  109. package/apps/ui/src/routes/cursor.$workspaceKey.tsx +416 -0
  110. package/apps/ui/src/routes/cursor.index.tsx +120 -0
  111. package/apps/ui/src/routes/index.tsx +124 -0
  112. package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +345 -0
  113. package/apps/ui/src/routes/kiro.$workspaceKey.tsx +136 -0
  114. package/apps/ui/src/routes/kiro.index.tsx +48 -0
  115. package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +334 -0
  116. package/apps/ui/src/routes/opencode.$workspaceKey.tsx +135 -0
  117. package/apps/ui/src/routes/opencode.index.tsx +48 -0
  118. package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +341 -0
  119. package/apps/ui/src/routes/qoder.$workspaceKey.tsx +139 -0
  120. package/apps/ui/src/routes/qoder.index.tsx +48 -0
  121. package/apps/ui/src/routes/settings.tsx +91 -0
  122. package/apps/ui/src/routes/threads.$threadId.tsx +619 -0
  123. package/apps/ui/src/styles.css +122 -0
  124. package/apps/ui/tsconfig.json +29 -0
  125. package/apps/ui/vite.config.ts +84 -0
  126. package/bin/spiracha.ts +32 -0
  127. package/package.json +50 -60
  128. package/src/client.ts +290 -0
  129. package/src/lib/antigravity-db.ts +213 -19
  130. package/src/lib/antigravity-exporter-types.ts +1 -0
  131. package/src/lib/claude-code-db.ts +655 -0
  132. package/src/lib/claude-code-exporter-types.ts +110 -0
  133. package/src/lib/claude-code-transcript-phase.ts +25 -0
  134. package/src/lib/claude-code-transcript.ts +180 -0
  135. package/src/lib/codex-analytics.ts +1 -1
  136. package/src/lib/codex-browser-db.ts +856 -61
  137. package/src/lib/codex-browser-export.ts +24 -16
  138. package/src/lib/codex-browser-types.ts +1 -1
  139. package/src/lib/codex-thread-cache.ts +17 -1
  140. package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
  141. package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +64 -28
  142. package/src/lib/concurrency.ts +25 -0
  143. package/src/lib/conversation-api.ts +792 -0
  144. package/src/lib/conversation-data/adapter-helpers.ts +110 -0
  145. package/src/lib/conversation-data/antigravity-adapter.ts +148 -0
  146. package/src/lib/conversation-data/claude-code-adapter.ts +175 -0
  147. package/src/lib/conversation-data/codex-adapter.ts +245 -0
  148. package/src/lib/conversation-data/cursor-adapter.ts +171 -0
  149. package/src/lib/conversation-data/index.ts +311 -0
  150. package/src/lib/conversation-data/kiro-adapter.ts +140 -0
  151. package/src/lib/conversation-data/message-selector.ts +37 -0
  152. package/src/lib/conversation-data/opencode-adapter.ts +180 -0
  153. package/src/lib/conversation-data/path-match.ts +79 -0
  154. package/src/lib/conversation-data/qoder-adapter.ts +230 -0
  155. package/src/lib/conversation-data/types.ts +116 -0
  156. package/src/lib/cursor-db.ts +67 -21
  157. package/src/lib/cursor-exporter-types.ts +0 -19
  158. package/src/lib/kiro-db.ts +949 -0
  159. package/src/lib/kiro-exporter-types.ts +100 -0
  160. package/src/lib/kiro-transcript-phase.ts +41 -0
  161. package/src/lib/kiro-transcript.ts +115 -0
  162. package/src/lib/opencode-db.ts +610 -0
  163. package/src/lib/opencode-exporter-types.ts +129 -0
  164. package/src/lib/opencode-think-tags.ts +126 -0
  165. package/src/lib/opencode-transcript-phase.ts +50 -0
  166. package/src/lib/opencode-transcript.ts +194 -0
  167. package/src/lib/qoder-acp-client.ts +268 -0
  168. package/src/lib/qoder-db.ts +1601 -0
  169. package/src/lib/qoder-exporter-types.ts +114 -0
  170. package/src/lib/qoder-transcript-phase.ts +41 -0
  171. package/src/lib/qoder-transcript.ts +112 -0
  172. package/src/lib/shared.ts +25 -0
  173. package/src/lib/ui-export-archive.ts +61 -0
  174. package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
  175. package/apps/ui/dist/client/assets/analytics-DK2jdvmI.js +0 -1
  176. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DmD0sN8X.js +0 -7
  177. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
  178. package/apps/ui/dist/client/assets/antigravity-keychain-panel-BRHVjFAM.js +0 -1
  179. package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
  180. package/apps/ui/dist/client/assets/antigravity._workspaceKey-CGV7amas.js +0 -1
  181. package/apps/ui/dist/client/assets/antigravity.index-DEEkjlZa.js +0 -1
  182. package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
  183. package/apps/ui/dist/client/assets/badge-DfWvPfKF.js +0 -1
  184. package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
  185. package/apps/ui/dist/client/assets/createServerFn-CgRRVpBH.js +0 -3
  186. package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
  187. package/apps/ui/dist/client/assets/cursor-threads._composerId-Ti2gTm63.js +0 -1
  188. package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
  189. package/apps/ui/dist/client/assets/cursor._workspaceKey-DzD_c6j9.js +0 -1
  190. package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
  191. package/apps/ui/dist/client/assets/cursor.index-PkCFVcl4.js +0 -2
  192. package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
  193. package/apps/ui/dist/client/assets/delete-confirm-dialog-BHRmyLHr.js +0 -11
  194. package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
  195. package/apps/ui/dist/client/assets/dist-BhZOxAPP.js +0 -1
  196. package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
  197. package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
  198. package/apps/ui/dist/client/assets/dropdown-menu-Dy5cLzTy.js +0 -1
  199. package/apps/ui/dist/client/assets/es2015-BkCWttyM.js +0 -41
  200. package/apps/ui/dist/client/assets/export-dialog-C86SYHJn.js +0 -1
  201. package/apps/ui/dist/client/assets/formatters-BM9kB7ed.js +0 -1
  202. package/apps/ui/dist/client/assets/index-CPvAP-jk.js +0 -149
  203. package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
  204. package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
  205. package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
  206. package/apps/ui/dist/client/assets/projects._project-Ckf6muWZ.js +0 -1
  207. package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
  208. package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
  209. package/apps/ui/dist/client/assets/projects.index-V5ibNZII.js +0 -3
  210. package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
  211. package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
  212. package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
  213. package/apps/ui/dist/client/assets/routes-Df2tgpyd.js +0 -1
  214. package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
  215. package/apps/ui/dist/client/assets/select-Bf2GwfPR.js +0 -1
  216. package/apps/ui/dist/client/assets/settings-5VQIzME0.js +0 -1
  217. package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
  218. package/apps/ui/dist/client/assets/styles-BhRkXgwB.css +0 -1
  219. package/apps/ui/dist/client/assets/tabs-OO4VU5KR.js +0 -7
  220. package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
  221. package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
  222. package/apps/ui/dist/client/assets/threads._threadId-BgDhFj2I.js +0 -1
  223. package/apps/ui/dist/client/assets/useMutation-CC_B7uy5.js +0 -1
  224. package/apps/ui/dist/client/assets/useQuery-C5By6WKU.js +0 -1
  225. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CRW2kqj0.js +0 -232
  226. package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
  227. package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
  228. package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
  229. package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
  230. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BtexWY-K.js +0 -613
  231. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
  232. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-zMrR6v6R.js +0 -20
  233. package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
  234. package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
  235. package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
  236. package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
  237. package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
  238. package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
  239. package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
  240. package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
  241. package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
  242. package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
  243. package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
  244. package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
  245. package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
  246. package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
  247. package/apps/ui/dist/server/assets/codex-server-CNXSJuc2.js +0 -2004
  248. package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
  249. package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
  250. package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
  251. package/apps/ui/dist/server/assets/cursor-db-cYZEU3WQ.js +0 -830
  252. package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
  253. package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
  254. package/apps/ui/dist/server/assets/cursor-recovery-nq-kR62j.js +0 -361
  255. package/apps/ui/dist/server/assets/cursor-server-C3q7hrp-.js +0 -213
  256. package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
  257. package/apps/ui/dist/server/assets/cursor-threads._composerId-BPJFWfJj.js +0 -18
  258. package/apps/ui/dist/server/assets/cursor-threads._composerId-BkMMTQ3v.js +0 -582
  259. package/apps/ui/dist/server/assets/cursor-transcript-2iL3KFSK.js +0 -125
  260. package/apps/ui/dist/server/assets/cursor._workspaceKey-C5PdTaKB.js +0 -28
  261. package/apps/ui/dist/server/assets/cursor._workspaceKey-Ckj7apKI.js +0 -401
  262. package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
  263. package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
  264. package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
  265. package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
  266. package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
  267. package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
  268. package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
  269. package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
  270. package/apps/ui/dist/server/assets/export-dialog-BItjWgkZ.js +0 -240
  271. package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
  272. package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
  273. package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
  274. package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
  275. package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
  276. package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
  277. package/apps/ui/dist/server/assets/projects._project-Bpbc3C-L.js +0 -20
  278. package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
  279. package/apps/ui/dist/server/assets/projects._project-CvZho6EQ.js +0 -395
  280. package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
  281. package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
  282. package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
  283. package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
  284. package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
  285. package/apps/ui/dist/server/assets/router-6m-ihwqA.js +0 -410
  286. package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
  287. package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
  288. package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
  289. package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
  290. package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
  291. package/apps/ui/dist/server/assets/shared-CPRNYIql.js +0 -134
  292. package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
  293. package/apps/ui/dist/server/assets/start-CI_0bSiY.js +0 -4
  294. package/apps/ui/dist/server/assets/tabs-CGA13IZM.js +0 -502
  295. package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
  296. package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
  297. package/apps/ui/dist/server/assets/threads._threadId-Bs0yuqO9.js +0 -18
  298. package/apps/ui/dist/server/assets/threads._threadId-D2GYkVn6.js +0 -639
  299. package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
  300. package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
  301. package/apps/ui/dist/server/server.js +0 -5847
  302. package/bin/codex-chats-claude.js +0 -5
  303. package/bin/codex-chats.js +0 -5
  304. package/bin/spiracha.js +0 -5
  305. package/src/export-chats.ts +0 -120
  306. package/src/export-claude.ts +0 -36
  307. package/src/export-cursor.ts +0 -244
  308. package/src/lib/claude-exporter.ts +0 -864
  309. package/src/lib/codex-exporter-cli.ts +0 -277
  310. package/src/lib/codex-exporter-db.ts +0 -319
  311. package/src/lib/codex-exporter.ts +0 -115
  312. package/src/lib/cursor-exporter.ts +0 -266
  313. package/src/lib/interactive-cli.ts +0 -433
  314. package/src/lib/native-open.ts +0 -54
  315. package/src/mcp-server.ts +0 -137
  316. package/src/spiracha.ts +0 -116
  317. package/src/ui-cli.ts +0 -310
  318. /package/apps/ui/{dist/client → public}/manifest.json +0 -0
  319. /package/apps/ui/{dist/client → public}/robots.txt +0 -0
@@ -0,0 +1,12 @@
1
+ import { createFileRoute } from '@tanstack/react-router';
2
+
3
+ export const Route = createFileRoute('/api/v1/resolve')({
4
+ server: {
5
+ handlers: {
6
+ GET: async ({ request }) => {
7
+ const { handleConversationApiRequest } = await import('@spiracha/lib/conversation-api');
8
+ return handleConversationApiRequest(request);
9
+ },
10
+ },
11
+ },
12
+ });
@@ -0,0 +1,12 @@
1
+ import { createFileRoute } from '@tanstack/react-router';
2
+
3
+ export const Route = createFileRoute('/api/v1/sources')({
4
+ server: {
5
+ handlers: {
6
+ GET: async ({ request }) => {
7
+ const { handleConversationApiRequest } = await import('@spiracha/lib/conversation-api');
8
+ return handleConversationApiRequest(request);
9
+ },
10
+ },
11
+ },
12
+ });
@@ -0,0 +1,342 @@
1
+ import type { ClaudeCodeSessionTranscript } from '@spiracha/lib/claude-code-exporter-types';
2
+ import type { ThreadEvent, ThreadTranscriptStats } from '@spiracha/lib/codex-browser-types';
3
+ import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
4
+ import { createFileRoute, Link } from '@tanstack/react-router';
5
+ import { Download } from 'lucide-react';
6
+ import { useMemo, useState } from 'react';
7
+ import { Breadcrumbs } from '#/components/breadcrumbs';
8
+ import { ExportDialog } from '#/components/export-dialog';
9
+ import { JsonPanel } from '#/components/json-panel';
10
+ import { LoadingPanel } from '#/components/loading-panel';
11
+ import { MetadataSection } from '#/components/metadata-section';
12
+ import { MetricCard } from '#/components/metric-card';
13
+ import { PageHeader } from '#/components/page-header';
14
+ import { ReloadErrorPanel } from '#/components/reload-error-panel';
15
+ import { TranscriptView } from '#/components/transcript-view';
16
+ import { Button } from '#/components/ui/button';
17
+ import { Checkbox } from '#/components/ui/checkbox';
18
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '#/components/ui/tabs';
19
+ import { claudeCodeSessionDetailQueryOptions } from '#/lib/claude-code-queries';
20
+ import { exportClaudeCodeSessionFn } from '#/lib/claude-code-server';
21
+ import {
22
+ claudeCodeTranscriptToThreadEvents,
23
+ getClaudeCodeThreadTranscriptStats,
24
+ } from '#/lib/claude-code-transcript-events';
25
+ import { downloadTextFile, downloadUrlFile } from '#/lib/download';
26
+ import { formatDateTime, formatList, formatNumber, formatTokens } from '#/lib/formatters';
27
+
28
+ type ExportDialogOptions = {
29
+ includeCommentary: boolean;
30
+ includeMetadata: boolean;
31
+ includeTools: boolean;
32
+ outputFormat: 'md' | 'txt';
33
+ zipArchive: boolean;
34
+ };
35
+
36
+ type TranscriptControlsProps = {
37
+ rawJsonDisabled?: boolean;
38
+ showCommentary: boolean;
39
+ showExtraEvents: boolean;
40
+ showRawJson: boolean;
41
+ showToolCalls: boolean;
42
+ showUserMessages: boolean;
43
+ onShowCommentaryChange: (checked: boolean) => void;
44
+ onShowExtraEventsChange: (checked: boolean) => void;
45
+ onShowRawJsonChange: (checked: boolean) => void;
46
+ onShowToolCallsChange: (checked: boolean) => void;
47
+ onShowUserMessagesChange: (checked: boolean) => void;
48
+ };
49
+
50
+ export const Route = createFileRoute('/claude-code-sessions/$sessionId')({
51
+ component: ClaudeCodeSessionDetailPage,
52
+ errorComponent: ClaudeCodeSessionDetailErrorComponent,
53
+ loader: ({ context, params }) =>
54
+ context.queryClient.ensureQueryData(claudeCodeSessionDetailQueryOptions(params.sessionId)),
55
+ pendingComponent: () => (
56
+ <LoadingPanel
57
+ description="Loading the Claude Code transcript, messages, tool calls, and session metadata."
58
+ title="Loading session"
59
+ />
60
+ ),
61
+ });
62
+
63
+ function ClaudeCodeSessionDetailErrorComponent({ error }: { error: Error }) {
64
+ return <ReloadErrorPanel description={error.message} title="Failed to load Claude Code session" />;
65
+ }
66
+
67
+ const buildSessionMetadata = (detail: ClaudeCodeSessionTranscript) => [
68
+ { label: 'Session ID', value: <span data-mono="true">{detail.session.sessionId}</span> },
69
+ {
70
+ label: 'Workspace',
71
+ value: (
72
+ <Link
73
+ className="text-[var(--accent)]"
74
+ params={{ workspaceKey: detail.session.workspaceKey }}
75
+ to="/claude-code/$workspaceKey"
76
+ >
77
+ {detail.session.workspaceLabel}
78
+ </Link>
79
+ ),
80
+ },
81
+ { label: 'Worktree', value: detail.session.worktree },
82
+ { label: 'CWD', value: detail.session.cwd },
83
+ { label: 'Model', value: detail.session.model ?? 'unknown' },
84
+ { label: 'Version', value: detail.session.version ?? 'unknown' },
85
+ { label: 'Git branch', value: detail.session.gitBranch ?? 'unknown' },
86
+ { label: 'Created', value: <span suppressHydrationWarning>{formatDateTime(detail.session.createdAtMs)}</span> },
87
+ {
88
+ label: 'Last active',
89
+ value: <span suppressHydrationWarning>{formatDateTime(detail.session.lastActiveAtMs)}</span>,
90
+ },
91
+ { label: 'Source file', value: detail.session.filePath },
92
+ ];
93
+
94
+ const buildTranscriptStatsItems = (
95
+ detail: ClaudeCodeSessionTranscript,
96
+ events: ThreadEvent[],
97
+ stats: ThreadTranscriptStats,
98
+ ) => [
99
+ { label: 'Event kinds', value: formatList([...new Set(events.map((event) => event.kind))]) },
100
+ { label: 'Messages', value: formatNumber(stats.messageCount) },
101
+ { label: 'User messages', value: formatNumber(stats.userMessageCount) },
102
+ { label: 'Assistant messages', value: formatNumber(stats.assistantMessageCount) },
103
+ { label: 'Reasoning events', value: formatNumber(events.filter((event) => event.kind === 'reasoning').length) },
104
+ { label: 'Final answers', value: formatNumber(stats.finalAnswerCount) },
105
+ { label: 'Tool calls', value: formatNumber(stats.toolCallCount) },
106
+ { label: 'Tool outputs', value: formatNumber(stats.toolOutputCount) },
107
+ { label: 'Attachments', value: formatNumber(detail.session.attachmentCount) },
108
+ { label: 'Renderable parts', value: formatNumber(detail.renderablePartCount) },
109
+ ];
110
+
111
+ const ClaudeCodeTranscriptControls = ({
112
+ rawJsonDisabled = false,
113
+ showCommentary,
114
+ showExtraEvents,
115
+ showRawJson,
116
+ showToolCalls,
117
+ showUserMessages,
118
+ onShowCommentaryChange,
119
+ onShowExtraEventsChange,
120
+ onShowRawJsonChange,
121
+ onShowToolCallsChange,
122
+ onShowUserMessagesChange,
123
+ }: TranscriptControlsProps) => {
124
+ return (
125
+ <div className="flex flex-wrap gap-4 rounded-xl border border-[var(--border)] bg-[var(--panel)] px-4 py-3 shadow-[var(--panel-shadow)]">
126
+ <div className="flex items-center gap-2 text-sm">
127
+ <Checkbox
128
+ checked={showToolCalls}
129
+ id="claude-code-transcript-show-tool-calls"
130
+ onCheckedChange={(checked) => onShowToolCallsChange(checked === true)}
131
+ />
132
+ <label htmlFor="claude-code-transcript-show-tool-calls">Show tool calls</label>
133
+ </div>
134
+ <div className="flex items-center gap-2 text-sm">
135
+ <Checkbox
136
+ checked={showCommentary}
137
+ id="claude-code-transcript-show-commentary"
138
+ onCheckedChange={(checked) => onShowCommentaryChange(checked === true)}
139
+ />
140
+ <label htmlFor="claude-code-transcript-show-commentary">Show commentary</label>
141
+ </div>
142
+ <div className="flex items-center gap-2 text-sm">
143
+ <Checkbox
144
+ checked={showExtraEvents}
145
+ id="claude-code-transcript-show-extra-events"
146
+ onCheckedChange={(checked) => onShowExtraEventsChange(checked === true)}
147
+ />
148
+ <label htmlFor="claude-code-transcript-show-extra-events">Show extra events</label>
149
+ </div>
150
+ <div className="flex items-center gap-2 text-sm">
151
+ <Checkbox
152
+ checked={showRawJson}
153
+ disabled={rawJsonDisabled}
154
+ id="claude-code-transcript-show-raw-json"
155
+ onCheckedChange={(checked) => onShowRawJsonChange(checked === true)}
156
+ />
157
+ <label htmlFor="claude-code-transcript-show-raw-json">Raw JSON</label>
158
+ </div>
159
+ <div className="flex items-center gap-2 text-sm">
160
+ <Checkbox
161
+ checked={showUserMessages}
162
+ id="claude-code-transcript-show-user-messages"
163
+ onCheckedChange={(checked) => onShowUserMessagesChange(checked === true)}
164
+ />
165
+ <label htmlFor="claude-code-transcript-show-user-messages">User</label>
166
+ </div>
167
+ </div>
168
+ );
169
+ };
170
+
171
+ function ClaudeCodeRawPanels({ detail, events }: { detail: ClaudeCodeSessionTranscript; events: ThreadEvent[] }) {
172
+ return (
173
+ <div className="space-y-4">
174
+ <JsonPanel title="Session summary" value={detail.session} />
175
+ <JsonPanel title="Claude Code entries" value={detail.entries} />
176
+ <JsonPanel title="Raw JSONL events" value={detail.rawEvents} />
177
+ <JsonPanel title="Transcript events" value={events} />
178
+ </div>
179
+ );
180
+ }
181
+
182
+ function ClaudeCodeSessionDetailPage() {
183
+ const detail = useSuspenseQuery(claudeCodeSessionDetailQueryOptions(Route.useParams().sessionId)).data;
184
+ const [pendingExport, setPendingExport] = useState(false);
185
+ const [showToolCalls, setShowToolCalls] = useState(false);
186
+ const [showCommentary, setShowCommentary] = useState(false);
187
+ const [showExtraEvents, setShowExtraEvents] = useState(false);
188
+ const [showRawJson, setShowRawJson] = useState(false);
189
+ const [showUserMessages, setShowUserMessages] = useState(true);
190
+ const transcriptEvents = useMemo(() => claudeCodeTranscriptToThreadEvents(detail), [detail]);
191
+ const transcriptStats = useMemo(() => getClaudeCodeThreadTranscriptStats(transcriptEvents), [transcriptEvents]);
192
+
193
+ const exportSessionMutation = useMutation({
194
+ mutationFn: async (options: ExportDialogOptions) => {
195
+ const download = await exportClaudeCodeSessionFn({
196
+ data: {
197
+ includeCommentary: options.includeCommentary,
198
+ includeMetadata: options.includeMetadata,
199
+ includeTools: options.includeTools,
200
+ outputFormat: options.outputFormat,
201
+ sessionId: detail.session.sessionId,
202
+ zipArchive: options.zipArchive,
203
+ },
204
+ });
205
+ if (download.mode === 'download') {
206
+ downloadTextFile(download.fileName, download.content, download.mimeType);
207
+ return;
208
+ }
209
+
210
+ await downloadUrlFile(download.fileName, download.downloadUrl);
211
+ },
212
+ onSuccess: () => {
213
+ setPendingExport(false);
214
+ },
215
+ });
216
+
217
+ return (
218
+ <div className="space-y-6">
219
+ <PageHeader
220
+ actions={
221
+ <Button
222
+ className="rounded-full"
223
+ type="button"
224
+ variant="outline"
225
+ onClick={() => setPendingExport(true)}
226
+ >
227
+ <Download className="mr-2 size-4" />
228
+ Export
229
+ </Button>
230
+ }
231
+ breadcrumb={
232
+ <Breadcrumbs
233
+ items={[
234
+ { label: 'Claude Code', to: '/claude-code' },
235
+ {
236
+ label: detail.session.workspaceLabel,
237
+ params: { workspaceKey: detail.session.workspaceKey },
238
+ to: '/claude-code/$workspaceKey',
239
+ },
240
+ { label: detail.session.title },
241
+ ]}
242
+ />
243
+ }
244
+ eyebrow="Claude Code session"
245
+ subtitle="Session detail for the selected Claude Code project conversation."
246
+ title={detail.session.title}
247
+ />
248
+
249
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
250
+ <MetricCard label="Messages" value={formatNumber(detail.session.messageCount)} />
251
+ <MetricCard label="Tool calls" value={formatNumber(detail.session.toolCallCount)} />
252
+ <MetricCard label="Tool outputs" value={formatNumber(detail.session.toolResultCount)} />
253
+ <MetricCard label="Tokens" value={formatTokens(detail.session.totalTokens)} />
254
+ </div>
255
+
256
+ <Tabs className="space-y-4" defaultValue="transcript">
257
+ <TabsList className="grid w-fit min-w-[24rem] grid-cols-3 rounded-full border border-[var(--border)] bg-[var(--panel)] p-1">
258
+ <TabsTrigger className="rounded-full px-5 text-sm" value="transcript">
259
+ Transcript
260
+ </TabsTrigger>
261
+ <TabsTrigger className="rounded-full px-5 text-sm" value="metadata">
262
+ Metadata
263
+ </TabsTrigger>
264
+ <TabsTrigger className="rounded-full px-5 text-sm" value="raw">
265
+ Raw
266
+ </TabsTrigger>
267
+ </TabsList>
268
+
269
+ <TabsContent className="space-y-3" value="transcript">
270
+ <ClaudeCodeTranscriptControls
271
+ rawJsonDisabled={transcriptEvents.length === 0}
272
+ showCommentary={showCommentary}
273
+ showExtraEvents={showExtraEvents}
274
+ showRawJson={showRawJson}
275
+ showToolCalls={showToolCalls}
276
+ showUserMessages={showUserMessages}
277
+ onShowCommentaryChange={setShowCommentary}
278
+ onShowExtraEventsChange={setShowExtraEvents}
279
+ onShowRawJsonChange={setShowRawJson}
280
+ onShowToolCallsChange={setShowToolCalls}
281
+ onShowUserMessagesChange={setShowUserMessages}
282
+ />
283
+ {transcriptEvents.length > 0 ? (
284
+ <TranscriptView
285
+ assistantModel={detail.session.model}
286
+ events={transcriptEvents}
287
+ projectPath={detail.session.worktree}
288
+ showCommentary={showCommentary}
289
+ showExtraEvents={showExtraEvents}
290
+ showRawJson={showRawJson}
291
+ showToolCalls={showToolCalls}
292
+ showUserMessages={showUserMessages}
293
+ />
294
+ ) : (
295
+ <section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
296
+ <h3 className="font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]">
297
+ Transcript
298
+ </h3>
299
+ <p className="mt-4 text-[var(--muted-foreground)] text-sm">
300
+ No renderable Claude Code transcript content was found for this session.
301
+ </p>
302
+ </section>
303
+ )}
304
+ </TabsContent>
305
+
306
+ <TabsContent value="metadata">
307
+ <div className="grid gap-4 xl:grid-cols-[0.95fr_1.05fr]">
308
+ <MetadataSection items={buildSessionMetadata(detail)} title="Session metadata" />
309
+ <MetadataSection
310
+ items={buildTranscriptStatsItems(detail, transcriptEvents, transcriptStats)}
311
+ title="Transcript stats"
312
+ />
313
+ </div>
314
+ </TabsContent>
315
+
316
+ <TabsContent value="raw">
317
+ <ClaudeCodeRawPanels detail={detail} events={transcriptEvents} />
318
+ </TabsContent>
319
+ </Tabs>
320
+
321
+ <ExportDialog
322
+ errorMessage={
323
+ exportSessionMutation.isError
324
+ ? exportSessionMutation.error instanceof Error
325
+ ? exportSessionMutation.error.message
326
+ : 'Export failed'
327
+ : null
328
+ }
329
+ open={pendingExport}
330
+ pending={exportSessionMutation.isPending}
331
+ title={`Export ${detail.session.title}`}
332
+ onExport={(options) => exportSessionMutation.mutate(options)}
333
+ onOpenChange={(open) => {
334
+ setPendingExport(open);
335
+ if (!open) {
336
+ exportSessionMutation.reset();
337
+ }
338
+ }}
339
+ />
340
+ </div>
341
+ );
342
+ }
@@ -0,0 +1,135 @@
1
+ import type { ClaudeCodeSessionSummary, ClaudeCodeWorkspaceGroup } from '@spiracha/lib/claude-code-exporter-types';
2
+ import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
3
+ import { createFileRoute } from '@tanstack/react-router';
4
+ import { useDeferredValue, useState } from 'react';
5
+ import { ClaudeCodeSessionsTable } from '#/components/claude-code-sessions-table';
6
+ import { ExportDialog } from '#/components/export-dialog';
7
+ import { ListSearchInput } from '#/components/list-search-input';
8
+ import { LoadingPanel } from '#/components/loading-panel';
9
+ import { PageHeader } from '#/components/page-header';
10
+ import { ReloadErrorPanel } from '#/components/reload-error-panel';
11
+ import { claudeCodeSessionsQueryOptions, claudeCodeWorkspacesQueryOptions } from '#/lib/claude-code-queries';
12
+ import { exportClaudeCodeSessionFn } from '#/lib/claude-code-server';
13
+ import { downloadTextFile, downloadUrlFile } from '#/lib/download';
14
+ import { matchesTextQuery } from '#/lib/text-filter';
15
+
16
+ type ExportDialogOptions = {
17
+ includeCommentary: boolean;
18
+ includeMetadata: boolean;
19
+ includeTools: boolean;
20
+ outputFormat: 'md' | 'txt';
21
+ zipArchive: boolean;
22
+ };
23
+
24
+ const findWorkspaceOrThrow = (workspaces: ClaudeCodeWorkspaceGroup[], workspaceKey: string) => {
25
+ const workspace = workspaces.find((candidate) => candidate.key === workspaceKey);
26
+ if (!workspace) {
27
+ throw new Error(`Claude Code workspace not found: ${workspaceKey}`);
28
+ }
29
+
30
+ return workspace;
31
+ };
32
+
33
+ export const Route = createFileRoute('/claude-code/$workspaceKey')({
34
+ component: ClaudeCodeWorkspacePage,
35
+ errorComponent: ClaudeCodeWorkspaceErrorComponent,
36
+ loader: async ({ context, params }) => {
37
+ const workspaces = await context.queryClient.ensureQueryData(claudeCodeWorkspacesQueryOptions());
38
+ findWorkspaceOrThrow(workspaces, params.workspaceKey);
39
+ await context.queryClient.ensureQueryData(claudeCodeSessionsQueryOptions(params.workspaceKey));
40
+ },
41
+ pendingComponent: () => (
42
+ <LoadingPanel description="Loading Claude Code sessions and transcript metadata." title="Loading workspace" />
43
+ ),
44
+ });
45
+
46
+ function ClaudeCodeWorkspaceErrorComponent({ error }: { error: Error }) {
47
+ return <ReloadErrorPanel description={error.message} title="Failed to load Claude Code workspace" />;
48
+ }
49
+
50
+ function ClaudeCodeWorkspacePage() {
51
+ const params = Route.useParams();
52
+ const workspaces = useSuspenseQuery(claudeCodeWorkspacesQueryOptions()).data;
53
+ const workspace = findWorkspaceOrThrow(workspaces, params.workspaceKey);
54
+ const sessions = useSuspenseQuery(claudeCodeSessionsQueryOptions(workspace.key)).data;
55
+ const [searchInput, setSearchInput] = useState('');
56
+ const [pendingExport, setPendingExport] = useState<ClaudeCodeSessionSummary | null>(null);
57
+ const deferredSearch = useDeferredValue(searchInput);
58
+
59
+ const exportMutation = useMutation({
60
+ mutationFn: async (options: ExportDialogOptions) => {
61
+ if (!pendingExport) {
62
+ throw new Error('No Claude Code session selected for export');
63
+ }
64
+
65
+ const download = await exportClaudeCodeSessionFn({
66
+ data: {
67
+ includeCommentary: options.includeCommentary,
68
+ includeMetadata: options.includeMetadata,
69
+ includeTools: options.includeTools,
70
+ outputFormat: options.outputFormat,
71
+ sessionId: pendingExport.sessionId,
72
+ zipArchive: options.zipArchive,
73
+ },
74
+ });
75
+ if (download.mode === 'download') {
76
+ downloadTextFile(download.fileName, download.content, download.mimeType);
77
+ return;
78
+ }
79
+
80
+ await downloadUrlFile(download.fileName, download.downloadUrl);
81
+ },
82
+ onSuccess: () => {
83
+ setPendingExport(null);
84
+ },
85
+ });
86
+
87
+ const visibleSessions = sessions.filter((session) =>
88
+ matchesTextQuery(deferredSearch, [
89
+ session.title,
90
+ session.sessionId,
91
+ session.model,
92
+ session.version,
93
+ session.gitBranch,
94
+ session.filePath,
95
+ ]),
96
+ );
97
+
98
+ return (
99
+ <div className="space-y-6">
100
+ <PageHeader
101
+ actions={
102
+ <ListSearchInput
103
+ placeholder="Search session title, id, model, or version"
104
+ value={searchInput}
105
+ onValueChange={setSearchInput}
106
+ />
107
+ }
108
+ eyebrow="Claude Code workspace"
109
+ subtitle="Inspect local Claude Code sessions, user prompts, assistant responses, tool calls, and token totals."
110
+ title={workspace.label}
111
+ />
112
+
113
+ <ClaudeCodeSessionsTable sessions={visibleSessions} onExportSession={setPendingExport} />
114
+
115
+ <ExportDialog
116
+ open={pendingExport !== null}
117
+ pending={exportMutation.isPending}
118
+ title={pendingExport ? `Export ${pendingExport.title}` : 'Export session'}
119
+ onExport={(options) => exportMutation.mutate(options)}
120
+ onOpenChange={(open) => {
121
+ if (!open) {
122
+ setPendingExport(null);
123
+ exportMutation.reset();
124
+ }
125
+ }}
126
+ />
127
+
128
+ {exportMutation.isError ? (
129
+ <p className="text-[var(--destructive)] text-sm">
130
+ {exportMutation.error instanceof Error ? exportMutation.error.message : 'Session export failed'}
131
+ </p>
132
+ ) : null}
133
+ </div>
134
+ );
135
+ }
@@ -0,0 +1,48 @@
1
+ import { useSuspenseQuery } from '@tanstack/react-query';
2
+ import { createFileRoute } from '@tanstack/react-router';
3
+ import { useDeferredValue, useState } from 'react';
4
+ import { ClaudeCodeWorkspacesTable } from '#/components/claude-code-workspaces-table';
5
+ import { ListSearchInput } from '#/components/list-search-input';
6
+ import { PageHeader } from '#/components/page-header';
7
+ import { ReloadErrorPanel } from '#/components/reload-error-panel';
8
+ import { claudeCodeWorkspacesQueryOptions } from '#/lib/claude-code-queries';
9
+ import { matchesTextQuery } from '#/lib/text-filter';
10
+
11
+ export const Route = createFileRoute('/claude-code/')({
12
+ component: ClaudeCodePage,
13
+ errorComponent: ClaudeCodeErrorComponent,
14
+ loader: ({ context }) => context.queryClient.ensureQueryData(claudeCodeWorkspacesQueryOptions()),
15
+ });
16
+
17
+ function ClaudeCodeErrorComponent({ error }: { error: Error }) {
18
+ return <ReloadErrorPanel description={error.message} title="Failed to load Claude Code workspaces" />;
19
+ }
20
+
21
+ function ClaudeCodePage() {
22
+ const workspaces = useSuspenseQuery(claudeCodeWorkspacesQueryOptions()).data;
23
+ const [searchInput, setSearchInput] = useState('');
24
+ const deferredSearch = useDeferredValue(searchInput);
25
+
26
+ const visibleWorkspaces = workspaces.filter((workspace) =>
27
+ matchesTextQuery(deferredSearch, [workspace.label, workspace.worktree, workspace.key, workspace.directoryName]),
28
+ );
29
+
30
+ return (
31
+ <div className="space-y-6">
32
+ <PageHeader
33
+ actions={
34
+ <ListSearchInput
35
+ placeholder="Search workspace name or path"
36
+ value={searchInput}
37
+ onValueChange={setSearchInput}
38
+ />
39
+ }
40
+ eyebrow="Inventory"
41
+ subtitle="Workspace groups are derived from local Claude Code project JSONL transcripts."
42
+ title="Claude Code"
43
+ />
44
+
45
+ <ClaudeCodeWorkspacesTable workspaces={visibleWorkspaces} />
46
+ </div>
47
+ );
48
+ }