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
@@ -0,0 +1,53 @@
1
+ import type { KiroWorkspaceGroup } from '@spiracha/lib/kiro-exporter-types';
2
+ import { Link } from '@tanstack/react-router';
3
+ import { createColumnHelper } from '@tanstack/react-table';
4
+ import { DataTable } from '#/components/data-table';
5
+ import { formatDateTime, formatNumber } from '#/lib/formatters';
6
+
7
+ type KiroWorkspacesTableProps = {
8
+ workspaces: KiroWorkspaceGroup[];
9
+ };
10
+
11
+ const columnHelper = createColumnHelper<KiroWorkspaceGroup>();
12
+
13
+ const columns = [
14
+ columnHelper.accessor('label', {
15
+ cell: (info) => (
16
+ <Link
17
+ className="block w-[16rem] max-w-[22rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto"
18
+ params={{ workspaceKey: info.row.original.key }}
19
+ to="/kiro/$workspaceKey"
20
+ >
21
+ <p className="truncate font-medium underline-offset-2 hover:underline">{info.getValue()}</p>
22
+ <p className="truncate text-[var(--muted-foreground)] text-xs">{info.row.original.worktree}</p>
23
+ </Link>
24
+ ),
25
+ header: 'Workspace',
26
+ }),
27
+ columnHelper.accessor('sessionCount', {
28
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
29
+ header: 'Sessions',
30
+ }),
31
+ columnHelper.accessor('messageCount', {
32
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
33
+ header: 'Messages',
34
+ }),
35
+ columnHelper.accessor('imageCount', {
36
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
37
+ header: 'Images',
38
+ }),
39
+ columnHelper.accessor('lastActiveAtMs', {
40
+ cell: (info) => (
41
+ <span className="whitespace-nowrap text-sm" suppressHydrationWarning>
42
+ {formatDateTime(info.getValue())}
43
+ </span>
44
+ ),
45
+ header: 'Last active',
46
+ }),
47
+ ] as const;
48
+
49
+ export const KiroWorkspacesTable = ({ workspaces }: KiroWorkspacesTableProps) => {
50
+ return (
51
+ <DataTable columns={columns} data={workspaces} emptyMessage="No Kiro workspaces match the current search." />
52
+ );
53
+ };
@@ -0,0 +1,18 @@
1
+ import { Input } from '#/components/ui/input';
2
+
3
+ type ListSearchInputProps = {
4
+ placeholder: string;
5
+ value: string;
6
+ onValueChange: (value: string) => void;
7
+ };
8
+
9
+ export function ListSearchInput({ placeholder, value, onValueChange }: ListSearchInputProps) {
10
+ return (
11
+ <Input
12
+ className="h-10 w-full rounded-full border-[var(--border)] bg-[var(--panel)] px-4 sm:w-[20rem]"
13
+ placeholder={placeholder}
14
+ value={value}
15
+ onChange={(event) => onValueChange(event.target.value)}
16
+ />
17
+ );
18
+ }
@@ -0,0 +1,23 @@
1
+ import { Loader2 } from 'lucide-react';
2
+
3
+ type LoadingPanelProps = {
4
+ description?: string;
5
+ title?: string;
6
+ };
7
+
8
+ export function LoadingPanel({
9
+ description = 'Fetching local data. Larger workspaces can take a moment.',
10
+ title = 'Loading',
11
+ }: LoadingPanelProps) {
12
+ return (
13
+ <div className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] px-6 py-10 text-center shadow-[var(--panel-shadow)]">
14
+ <div className="flex justify-center">
15
+ <div className="rounded-full border border-[var(--border)] bg-[var(--panel-secondary)] p-3">
16
+ <Loader2 className="size-5 animate-spin text-[var(--accent)]" />
17
+ </div>
18
+ </div>
19
+ <p className="mt-4 font-medium text-sm">{title}</p>
20
+ <p className="mt-2 text-[var(--muted-foreground)] text-sm">{description}</p>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ type MetadataItem = {
4
+ label: string;
5
+ value: ReactNode;
6
+ };
7
+
8
+ type MetadataSectionProps = {
9
+ items: MetadataItem[];
10
+ title: string;
11
+ };
12
+
13
+ export function MetadataSection({ items, title }: MetadataSectionProps) {
14
+ return (
15
+ <section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
16
+ <h3 className="font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]">
17
+ {title}
18
+ </h3>
19
+ <dl className="mt-4 space-y-3">
20
+ {items.map((item) => (
21
+ <div key={item.label} className="grid gap-1 sm:grid-cols-[11rem_1fr] sm:items-start">
22
+ <dt className="font-medium text-[var(--muted-foreground)] text-xs uppercase tracking-[0.14em]">
23
+ {item.label}
24
+ </dt>
25
+ <dd className="min-w-0 text-sm leading-6">{item.value}</dd>
26
+ </div>
27
+ ))}
28
+ </dl>
29
+ </section>
30
+ );
31
+ }
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ type MetricCardProps = {
4
+ helper?: string;
5
+ label: string;
6
+ value: ReactNode;
7
+ };
8
+
9
+ export function MetricCard({ helper, label, value }: MetricCardProps) {
10
+ return (
11
+ <div className="rounded-2xl border border-[var(--border)] bg-[var(--panel)] p-4 shadow-[var(--panel-shadow)]">
12
+ <p className="font-semibold text-[10px] text-[var(--muted-foreground)] uppercase tracking-[0.18em]">
13
+ {label}
14
+ </p>
15
+ <p className="mt-2 truncate font-semibold text-lg tracking-[-0.03em]">{value}</p>
16
+ {helper ? <p className="mt-1.5 text-[var(--muted-foreground)] text-xs">{helper}</p> : null}
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,128 @@
1
+ import type { OpenCodeSessionSummary } from '@spiracha/lib/opencode-exporter-types';
2
+ import { Link } from '@tanstack/react-router';
3
+ import type { SortingState } from '@tanstack/react-table';
4
+ import { createColumnHelper } from '@tanstack/react-table';
5
+ import { Download, MoreHorizontal } from 'lucide-react';
6
+ import { useMemo } from 'react';
7
+ import { DataTable } from '#/components/data-table';
8
+ import { Badge } from '#/components/ui/badge';
9
+ import { Button } from '#/components/ui/button';
10
+ import {
11
+ DropdownMenu,
12
+ DropdownMenuContent,
13
+ DropdownMenuItem,
14
+ DropdownMenuTrigger,
15
+ } from '#/components/ui/dropdown-menu';
16
+ import { formatDateTime, formatNumber, formatTokens } from '#/lib/formatters';
17
+
18
+ type OpenCodeSessionsTableProps = {
19
+ onExportSession: (session: OpenCodeSessionSummary) => void;
20
+ sessions: OpenCodeSessionSummary[];
21
+ };
22
+
23
+ const columnHelper = createColumnHelper<OpenCodeSessionSummary>();
24
+ const defaultSorting: SortingState = [{ desc: true, id: 'updatedAt' }];
25
+
26
+ const formatCost = (value: number) => {
27
+ if (value <= 0) {
28
+ return '$0';
29
+ }
30
+
31
+ return `$${value.toFixed(value < 0.01 ? 4 : 2)}`;
32
+ };
33
+
34
+ const columns = (onExportSession: (session: OpenCodeSessionSummary) => void) =>
35
+ [
36
+ columnHelper.accessor('title', {
37
+ cell: (info) => (
38
+ <Link
39
+ className="block w-[16rem] max-w-[22rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto"
40
+ params={{ sessionId: info.row.original.sessionId }}
41
+ to="/opencode-sessions/$sessionId"
42
+ >
43
+ <div className="flex items-center gap-2">
44
+ <p className="truncate font-medium underline-offset-2 hover:underline">{info.getValue()}</p>
45
+ {info.row.original.archivedAtMs ? <Badge variant="outline">archived</Badge> : null}
46
+ </div>
47
+ <p className="truncate text-[var(--muted-foreground)] text-xs">
48
+ {info.row.original.slug} · {info.row.original.sessionId}
49
+ </p>
50
+ </Link>
51
+ ),
52
+ header: 'Session',
53
+ }),
54
+ columnHelper.accessor('lastUpdatedAtMs', {
55
+ cell: (info) => (
56
+ <span className="whitespace-nowrap text-sm" suppressHydrationWarning>
57
+ {formatDateTime(info.getValue())}
58
+ </span>
59
+ ),
60
+ header: 'Updated',
61
+ id: 'updatedAt',
62
+ }),
63
+ columnHelper.accessor('agent', {
64
+ cell: (info) => <span className="font-mono text-sm">{info.getValue() ?? 'unknown'}</span>,
65
+ header: 'Agent',
66
+ }),
67
+ columnHelper.accessor('modelLabel', {
68
+ cell: (info) => <span className="text-sm">{info.getValue() ?? 'unknown'}</span>,
69
+ header: 'Model',
70
+ }),
71
+ columnHelper.accessor('messageCount', {
72
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
73
+ header: 'Messages',
74
+ }),
75
+ columnHelper.accessor('totalTokens', {
76
+ cell: (info) => (
77
+ <span className="whitespace-nowrap font-mono text-sm">{formatTokens(info.getValue())}</span>
78
+ ),
79
+ header: 'Tokens',
80
+ }),
81
+ columnHelper.accessor('cost', {
82
+ cell: (info) => <span className="font-mono text-sm">{formatCost(info.getValue())}</span>,
83
+ header: 'Cost',
84
+ }),
85
+ columnHelper.display({
86
+ cell: (info) => (
87
+ <DropdownMenu>
88
+ <DropdownMenuTrigger asChild>
89
+ <Button
90
+ aria-label={`Actions for ${info.row.original.title}`}
91
+ className="rounded-full"
92
+ size="icon"
93
+ type="button"
94
+ variant="ghost"
95
+ onClick={(event) => event.stopPropagation()}
96
+ >
97
+ <MoreHorizontal className="size-4" />
98
+ </Button>
99
+ </DropdownMenuTrigger>
100
+ <DropdownMenuContent align="end">
101
+ <DropdownMenuItem
102
+ disabled={info.row.original.renderablePartCount === 0}
103
+ onClick={() => onExportSession(info.row.original)}
104
+ >
105
+ <Download className="mr-2 size-4" />
106
+ Export session
107
+ </DropdownMenuItem>
108
+ </DropdownMenuContent>
109
+ </DropdownMenu>
110
+ ),
111
+ enableSorting: false,
112
+ header: '',
113
+ id: 'actions',
114
+ }),
115
+ ] as const;
116
+
117
+ export const OpenCodeSessionsTable = ({ onExportSession, sessions }: OpenCodeSessionsTableProps) => {
118
+ const tableColumns = useMemo(() => columns(onExportSession), [onExportSession]);
119
+
120
+ return (
121
+ <DataTable
122
+ columns={tableColumns}
123
+ data={sessions}
124
+ emptyMessage="No OpenCode sessions match the current workspace filter."
125
+ initialSorting={defaultSorting}
126
+ />
127
+ );
128
+ };
@@ -0,0 +1,57 @@
1
+ import type { OpenCodeWorkspaceGroup } from '@spiracha/lib/opencode-exporter-types';
2
+ import { Link } from '@tanstack/react-router';
3
+ import { createColumnHelper } from '@tanstack/react-table';
4
+ import { DataTable } from '#/components/data-table';
5
+ import { formatDateTime, formatNumber } from '#/lib/formatters';
6
+
7
+ type OpenCodeWorkspacesTableProps = {
8
+ workspaces: OpenCodeWorkspaceGroup[];
9
+ };
10
+
11
+ const columnHelper = createColumnHelper<OpenCodeWorkspaceGroup>();
12
+
13
+ const columns = [
14
+ columnHelper.accessor('label', {
15
+ cell: (info) => (
16
+ <Link
17
+ className="block w-[16rem] max-w-[22rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto"
18
+ params={{ workspaceKey: info.row.original.key }}
19
+ to="/opencode/$workspaceKey"
20
+ >
21
+ <p className="truncate font-medium underline-offset-2 hover:underline">{info.getValue()}</p>
22
+ <p className="truncate text-[var(--muted-foreground)] text-xs">{info.row.original.worktree}</p>
23
+ </Link>
24
+ ),
25
+ header: 'Workspace',
26
+ }),
27
+ columnHelper.accessor('sessionCount', {
28
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
29
+ header: 'Sessions',
30
+ }),
31
+ columnHelper.accessor('messageCount', {
32
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
33
+ header: 'Messages',
34
+ }),
35
+ columnHelper.accessor('partCount', {
36
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
37
+ header: 'Parts',
38
+ }),
39
+ columnHelper.accessor('lastActiveMs', {
40
+ cell: (info) => (
41
+ <span className="whitespace-nowrap text-sm" suppressHydrationWarning>
42
+ {formatDateTime(info.getValue())}
43
+ </span>
44
+ ),
45
+ header: 'Last updated',
46
+ }),
47
+ ] as const;
48
+
49
+ export const OpenCodeWorkspacesTable = ({ workspaces }: OpenCodeWorkspacesTableProps) => {
50
+ return (
51
+ <DataTable
52
+ columns={columns}
53
+ data={workspaces}
54
+ emptyMessage="No OpenCode workspaces match the current search."
55
+ />
56
+ );
57
+ };
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ type PageHeaderProps = {
4
+ actions?: ReactNode;
5
+ breadcrumb?: ReactNode;
6
+ eyebrow?: string;
7
+ subtitle?: string;
8
+ title: string;
9
+ };
10
+
11
+ export function PageHeader({ actions, breadcrumb, eyebrow, subtitle, title }: PageHeaderProps) {
12
+ return (
13
+ <div className="flex flex-col gap-4 border-[var(--border)] border-b pb-5 sm:flex-row sm:items-end sm:justify-between">
14
+ <div className="space-y-2">
15
+ {breadcrumb ? <div>{breadcrumb}</div> : null}
16
+ {eyebrow ? (
17
+ <p className="font-semibold text-[11px] text-[var(--muted-foreground)] uppercase tracking-[0.18em]">
18
+ {eyebrow}
19
+ </p>
20
+ ) : null}
21
+ <div>
22
+ <h2 className="font-semibold text-2xl tracking-[-0.03em] sm:text-[2rem]">{title}</h2>
23
+ {subtitle ? (
24
+ <p className="mt-2 max-w-[60rem] whitespace-pre-wrap break-words text-[var(--muted-foreground)] text-sm">
25
+ {subtitle}
26
+ </p>
27
+ ) : null}
28
+ </div>
29
+ </div>
30
+ {actions ? <div className="flex flex-wrap items-center gap-2">{actions}</div> : null}
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,74 @@
1
+ import { LoadingPanel } from '#/components/loading-panel';
2
+ import { PageHeader } from '#/components/page-header';
3
+ import { Skeleton } from '#/components/ui/skeleton';
4
+
5
+ const loadingColumns = ['Project', 'Threads', 'Tokens', 'Last updated', 'Models', 'Archived'] as const;
6
+ const loadingRows = ['project-a', 'project-b', 'project-c', 'project-d', 'project-e'] as const;
7
+
8
+ export function ProjectsLoadingState() {
9
+ return (
10
+ <div aria-busy="true" aria-live="polite" className="space-y-6">
11
+ <PageHeader
12
+ actions={<Skeleton className="h-10 w-full min-w-[16rem] max-w-[20rem]" />}
13
+ eyebrow="Inventory"
14
+ subtitle="Reading the local Codex database and checking fallback session files."
15
+ title="Codex"
16
+ />
17
+
18
+ <div role="status">
19
+ <LoadingPanel
20
+ description="Scanning project summaries. Large local histories can take a moment."
21
+ title="Loading Codex projects"
22
+ />
23
+ </div>
24
+
25
+ <div className="w-full overflow-x-auto rounded-[1.5rem] border border-[var(--border)] bg-[var(--panel)]">
26
+ <table className="w-full min-w-[56rem]">
27
+ <thead className="bg-[var(--panel-secondary)]">
28
+ <tr className="border-[var(--border)] border-b">
29
+ {loadingColumns.map((column) => (
30
+ <th
31
+ key={column}
32
+ className="h-10 whitespace-nowrap px-4 text-left font-semibold text-[11px] text-[var(--muted-foreground)] uppercase tracking-[0.18em]"
33
+ >
34
+ {column}
35
+ </th>
36
+ ))}
37
+ </tr>
38
+ </thead>
39
+ <tbody>
40
+ {loadingRows.map((rowId, rowIndex) => (
41
+ <tr
42
+ key={rowId}
43
+ aria-label={`Loading project row ${rowIndex + 1}`}
44
+ className="border-[var(--border)] border-b last:border-b-0"
45
+ >
46
+ <td className="px-4 py-3">
47
+ <div className="space-y-2">
48
+ <Skeleton aria-hidden className="h-4 w-44" />
49
+ <Skeleton aria-hidden className="h-3 w-20" />
50
+ </div>
51
+ </td>
52
+ <td className="px-4 py-3">
53
+ <Skeleton aria-hidden className="h-4 w-12" />
54
+ </td>
55
+ <td className="px-4 py-3">
56
+ <Skeleton aria-hidden className="h-4 w-16" />
57
+ </td>
58
+ <td className="px-4 py-3">
59
+ <Skeleton aria-hidden className="h-4 w-28" />
60
+ </td>
61
+ <td className="px-4 py-3">
62
+ <Skeleton aria-hidden className="h-4 w-32" />
63
+ </td>
64
+ <td className="px-4 py-3">
65
+ <Skeleton aria-hidden className="h-4 w-10" />
66
+ </td>
67
+ </tr>
68
+ ))}
69
+ </tbody>
70
+ </table>
71
+ </div>
72
+ </div>
73
+ );
74
+ }
@@ -0,0 +1,108 @@
1
+ import type { ProjectSummary } from '@spiracha/lib/codex-browser-types';
2
+ import { Link } from '@tanstack/react-router';
3
+ import { createColumnHelper } from '@tanstack/react-table';
4
+ import { MoreHorizontal, Trash2 } from 'lucide-react';
5
+ import { DataTable } from '#/components/data-table';
6
+ import { Button } from '#/components/ui/button';
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuItem,
11
+ DropdownMenuTrigger,
12
+ } from '#/components/ui/dropdown-menu';
13
+ import { formatDateTime, formatList, formatNumber, formatTokens } from '#/lib/formatters';
14
+
15
+ type ProjectsTableProps = {
16
+ projects: ProjectSummary[];
17
+ onDeleteProject: (project: ProjectSummary) => void;
18
+ };
19
+
20
+ const columnHelper = createColumnHelper<ProjectSummary>();
21
+
22
+ const columns = (onDeleteProject: (project: ProjectSummary) => void) =>
23
+ [
24
+ columnHelper.accessor('name', {
25
+ cell: (info) => (
26
+ <Link
27
+ className="block w-[14rem] max-w-[18rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto"
28
+ params={{ project: info.row.original.name }}
29
+ to="/codex/$project"
30
+ >
31
+ <p className="font-medium underline-offset-2 hover:underline">{info.getValue()}</p>
32
+ <p className="text-[var(--muted-foreground)] text-xs">
33
+ {formatNumber(info.row.original.cwdPaths.length)} cwd path
34
+ {info.row.original.cwdPaths.length === 1 ? '' : 's'}
35
+ </p>
36
+ </Link>
37
+ ),
38
+ header: 'Project',
39
+ }),
40
+ columnHelper.accessor('threadCount', {
41
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
42
+ header: 'Threads',
43
+ }),
44
+ columnHelper.accessor('totalTokens', {
45
+ cell: (info) => <span className="font-mono text-sm">{formatTokens(info.getValue())}</span>,
46
+ header: 'Tokens',
47
+ }),
48
+ columnHelper.accessor('lastUpdatedAtMs', {
49
+ cell: (info) => (
50
+ <span className="text-sm" suppressHydrationWarning>
51
+ {formatDateTime(info.getValue())}
52
+ </span>
53
+ ),
54
+ header: 'Last updated',
55
+ }),
56
+ columnHelper.display({
57
+ cell: (info) => (
58
+ <span className="text-[var(--muted-foreground)] text-sm">
59
+ {formatList(info.row.original.modelNames)}
60
+ </span>
61
+ ),
62
+ header: 'Models',
63
+ id: 'models',
64
+ }),
65
+ columnHelper.accessor('archivedThreadCount', {
66
+ cell: (info) => <span className="text-sm">{formatNumber(info.getValue())}</span>,
67
+ header: 'Archived',
68
+ }),
69
+ columnHelper.display({
70
+ cell: (info) => (
71
+ <DropdownMenu>
72
+ <DropdownMenuTrigger asChild>
73
+ <Button
74
+ className="rounded-full"
75
+ size="icon"
76
+ type="button"
77
+ variant="ghost"
78
+ onClick={(event) => event.stopPropagation()}
79
+ >
80
+ <MoreHorizontal className="size-4" />
81
+ </Button>
82
+ </DropdownMenuTrigger>
83
+ <DropdownMenuContent align="end">
84
+ <DropdownMenuItem
85
+ className="text-[var(--destructive)]"
86
+ onClick={() => onDeleteProject(info.row.original)}
87
+ >
88
+ <Trash2 className="mr-2 size-4" />
89
+ Delete project
90
+ </DropdownMenuItem>
91
+ </DropdownMenuContent>
92
+ </DropdownMenu>
93
+ ),
94
+ header: '',
95
+ id: 'actions',
96
+ }),
97
+ ] as const;
98
+
99
+ export function ProjectsTable({ projects, onDeleteProject }: ProjectsTableProps) {
100
+ return (
101
+ <DataTable
102
+ columns={columns(onDeleteProject)}
103
+ data={projects}
104
+ emptyMessage="No projects match the current search."
105
+ initialSorting={[{ desc: true, id: 'lastUpdatedAtMs' }]}
106
+ />
107
+ );
108
+ }
@@ -0,0 +1,112 @@
1
+ import type { QoderSessionSummary } from '@spiracha/lib/qoder-exporter-types';
2
+ import { Link } from '@tanstack/react-router';
3
+ import type { SortingState } from '@tanstack/react-table';
4
+ import { createColumnHelper } from '@tanstack/react-table';
5
+ import { Download, MoreHorizontal } from 'lucide-react';
6
+ import { useMemo } from 'react';
7
+ import { DataTable } from '#/components/data-table';
8
+ import { Button } from '#/components/ui/button';
9
+ import {
10
+ DropdownMenu,
11
+ DropdownMenuContent,
12
+ DropdownMenuItem,
13
+ DropdownMenuTrigger,
14
+ } from '#/components/ui/dropdown-menu';
15
+ import { formatDateTime, formatNumber } from '#/lib/formatters';
16
+
17
+ type QoderSessionsTableProps = {
18
+ onExportSession: (session: QoderSessionSummary) => void;
19
+ sessions: QoderSessionSummary[];
20
+ };
21
+
22
+ const columnHelper = createColumnHelper<QoderSessionSummary>();
23
+ const defaultSorting: SortingState = [{ desc: true, id: 'lastActive' }];
24
+
25
+ const columns = (onExportSession: (session: QoderSessionSummary) => void) =>
26
+ [
27
+ columnHelper.accessor('title', {
28
+ cell: (info) => (
29
+ <Link
30
+ className="block w-[16rem] max-w-[22rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto"
31
+ params={{ sessionId: info.row.original.sessionId }}
32
+ to="/qoder-sessions/$sessionId"
33
+ >
34
+ <p className="truncate font-medium underline-offset-2 hover:underline">{info.getValue()}</p>
35
+ <p className="truncate text-[var(--muted-foreground)] text-xs">{info.row.original.sessionId}</p>
36
+ </Link>
37
+ ),
38
+ header: 'Session',
39
+ }),
40
+ columnHelper.accessor('lastActiveAtMs', {
41
+ cell: (info) => <span className="whitespace-nowrap text-sm">{formatDateTime(info.getValue())}</span>,
42
+ header: 'Updated',
43
+ id: 'lastActive',
44
+ }),
45
+ columnHelper.accessor('status', {
46
+ cell: (info) => <span className="font-mono text-sm">{info.getValue() ?? 'unknown'}</span>,
47
+ header: 'Status',
48
+ }),
49
+ columnHelper.accessor('model', {
50
+ cell: (info) => <span className="font-mono text-sm">{info.getValue() ?? 'unknown'}</span>,
51
+ header: 'Model',
52
+ }),
53
+ columnHelper.accessor('messageCount', {
54
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
55
+ header: 'Prompts',
56
+ }),
57
+ columnHelper.accessor('fileOperationCount', {
58
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
59
+ header: 'File ops',
60
+ }),
61
+ columnHelper.accessor('snapshotFileCount', {
62
+ cell: (info) => <span className="font-mono text-sm">{formatNumber(info.getValue())}</span>,
63
+ header: 'Snapshots',
64
+ }),
65
+ columnHelper.accessor('executionMode', {
66
+ cell: (info) => <span className="font-mono text-sm">{info.getValue() ?? 'unknown'}</span>,
67
+ header: 'Mode',
68
+ }),
69
+ columnHelper.display({
70
+ cell: (info) => (
71
+ <DropdownMenu>
72
+ <DropdownMenuTrigger asChild>
73
+ <Button
74
+ aria-label={`Actions for ${info.row.original.title}`}
75
+ className="rounded-full"
76
+ size="icon"
77
+ type="button"
78
+ variant="ghost"
79
+ onClick={(event) => event.stopPropagation()}
80
+ >
81
+ <MoreHorizontal className="size-4" />
82
+ </Button>
83
+ </DropdownMenuTrigger>
84
+ <DropdownMenuContent align="end">
85
+ <DropdownMenuItem
86
+ disabled={info.row.original.renderablePartCount === 0}
87
+ onClick={() => onExportSession(info.row.original)}
88
+ >
89
+ <Download className="mr-2 size-4" />
90
+ Export session
91
+ </DropdownMenuItem>
92
+ </DropdownMenuContent>
93
+ </DropdownMenu>
94
+ ),
95
+ enableSorting: false,
96
+ header: '',
97
+ id: 'actions',
98
+ }),
99
+ ] as const;
100
+
101
+ export const QoderSessionsTable = ({ onExportSession, sessions }: QoderSessionsTableProps) => {
102
+ const tableColumns = useMemo(() => columns(onExportSession), [onExportSession]);
103
+
104
+ return (
105
+ <DataTable
106
+ columns={tableColumns}
107
+ data={sessions}
108
+ emptyMessage="No Qoder sessions match the current workspace filter."
109
+ initialSorting={defaultSorting}
110
+ />
111
+ );
112
+ };