spiracha 1.5.0 → 2.1.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 (401) hide show
  1. package/AGENTS.md +114 -205
  2. package/README.md +94 -269
  3. package/apps/ui/AGENTS.md +17 -7
  4. package/apps/ui/README.md +52 -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 +245 -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 +125 -0
  11. package/apps/ui/src/components/breadcrumbs.tsx +71 -0
  12. package/apps/ui/src/components/claude-code-sessions-table.tsx +150 -0
  13. package/apps/ui/src/components/claude-code-workspaces-table.tsx +57 -0
  14. package/apps/ui/src/components/cursor-threads-table.tsx +152 -0
  15. package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
  16. package/apps/ui/src/components/data-table.tsx +243 -0
  17. package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
  18. package/apps/ui/src/components/export-dialog.tsx +174 -0
  19. package/apps/ui/src/components/grok-sessions-table.tsx +146 -0
  20. package/apps/ui/src/components/grok-workspaces-table.tsx +53 -0
  21. package/apps/ui/src/components/json-panel.tsx +17 -0
  22. package/apps/ui/src/components/kiro-sessions-table.tsx +148 -0
  23. package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
  24. package/apps/ui/src/components/list-search-input.tsx +18 -0
  25. package/apps/ui/src/components/loading-panel.tsx +23 -0
  26. package/apps/ui/src/components/metadata-section.tsx +31 -0
  27. package/apps/ui/src/components/metric-card.tsx +19 -0
  28. package/apps/ui/src/components/opencode-sessions-table.tsx +164 -0
  29. package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
  30. package/apps/ui/src/components/page-header.tsx +39 -0
  31. package/apps/ui/src/components/projects-loading-state.tsx +74 -0
  32. package/apps/ui/src/components/projects-table.tsx +108 -0
  33. package/apps/ui/src/components/qoder-sessions-table.tsx +133 -0
  34. package/apps/ui/src/components/qoder-workspaces-table.tsx +57 -0
  35. package/apps/ui/src/components/recent-threads-list.tsx +52 -0
  36. package/apps/ui/src/components/reload-error-panel.tsx +20 -0
  37. package/apps/ui/src/components/selection-actions-toolbar.tsx +80 -0
  38. package/apps/ui/src/components/text-document-panel.tsx +19 -0
  39. package/apps/ui/src/components/theme-toggle.tsx +48 -0
  40. package/apps/ui/src/components/threads-table.tsx +165 -0
  41. package/apps/ui/src/components/transcript-view.tsx +648 -0
  42. package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
  43. package/apps/ui/src/components/ui/badge.tsx +45 -0
  44. package/apps/ui/src/components/ui/button.tsx +62 -0
  45. package/apps/ui/src/components/ui/checkbox.tsx +29 -0
  46. package/apps/ui/src/components/ui/dialog.tsx +137 -0
  47. package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
  48. package/apps/ui/src/components/ui/input.tsx +21 -0
  49. package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
  50. package/apps/ui/src/components/ui/select.tsx +163 -0
  51. package/apps/ui/src/components/ui/separator.tsx +28 -0
  52. package/apps/ui/src/components/ui/sheet.tsx +107 -0
  53. package/apps/ui/src/components/ui/skeleton.tsx +7 -0
  54. package/apps/ui/src/components/ui/table.tsx +76 -0
  55. package/apps/ui/src/components/ui/tabs.tsx +71 -0
  56. package/apps/ui/src/components/ui/tooltip.tsx +44 -0
  57. package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
  58. package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
  59. package/apps/ui/src/lib/antigravity-conversation-state.ts +41 -0
  60. package/apps/ui/src/lib/antigravity-queries.ts +33 -0
  61. package/apps/ui/src/lib/antigravity-server.ts +273 -0
  62. package/apps/ui/src/lib/antigravity-transcript-events.ts +343 -0
  63. package/apps/ui/src/lib/claude-code-queries.ts +34 -0
  64. package/apps/ui/src/lib/claude-code-server.ts +243 -0
  65. package/apps/ui/src/lib/claude-code-transcript-events.ts +161 -0
  66. package/apps/ui/src/lib/codex-queries.ts +82 -0
  67. package/apps/ui/src/lib/codex-server.ts +291 -0
  68. package/apps/ui/src/lib/cursor-queries.ts +37 -0
  69. package/apps/ui/src/lib/cursor-server.ts +315 -0
  70. package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
  71. package/apps/ui/src/lib/download.ts +166 -0
  72. package/apps/ui/src/lib/formatters.ts +147 -0
  73. package/apps/ui/src/lib/grok-queries.ts +22 -0
  74. package/apps/ui/src/lib/grok-server.ts +169 -0
  75. package/apps/ui/src/lib/grok-transcript-events.ts +149 -0
  76. package/apps/ui/src/lib/kiro-queries.ts +22 -0
  77. package/apps/ui/src/lib/kiro-server.ts +151 -0
  78. package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
  79. package/apps/ui/src/lib/opencode-queries.ts +37 -0
  80. package/apps/ui/src/lib/opencode-server.ts +151 -0
  81. package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
  82. package/apps/ui/src/lib/package-metadata.ts +16 -0
  83. package/apps/ui/src/lib/path-utils.ts +13 -0
  84. package/apps/ui/src/lib/qoder-queries.ts +22 -0
  85. package/apps/ui/src/lib/qoder-server.ts +135 -0
  86. package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
  87. package/apps/ui/src/lib/route-search.ts +182 -0
  88. package/apps/ui/src/lib/settings-store.tsx +59 -0
  89. package/apps/ui/src/lib/source-session-export-server.ts +139 -0
  90. package/apps/ui/src/lib/text-filter.ts +22 -0
  91. package/apps/ui/src/lib/thread-id.ts +3 -0
  92. package/apps/ui/src/lib/thread-transcript-load.ts +15 -0
  93. package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
  94. package/apps/ui/src/lib/utils.ts +7 -0
  95. package/apps/ui/src/lib/workspace-delete-navigation.ts +12 -0
  96. package/apps/ui/src/routeTree.gen.ts +837 -0
  97. package/apps/ui/src/router.tsx +20 -0
  98. package/apps/ui/src/routes/$threadId.tsx +22 -0
  99. package/apps/ui/src/routes/__root.tsx +127 -0
  100. package/apps/ui/src/routes/analytics.tsx +143 -0
  101. package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +504 -0
  102. package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +365 -0
  103. package/apps/ui/src/routes/antigravity.index.tsx +51 -0
  104. package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
  105. package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
  106. package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +16 -0
  107. package/apps/ui/src/routes/api.v1.conversations.delete.ts +12 -0
  108. package/apps/ui/src/routes/api.v1.conversations.export.ts +12 -0
  109. package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
  110. package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
  111. package/apps/ui/src/routes/api.v1.sources.ts +12 -0
  112. package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +468 -0
  113. package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +274 -0
  114. package/apps/ui/src/routes/claude-code.index.tsx +48 -0
  115. package/apps/ui/src/routes/codex.$project.tsx +390 -0
  116. package/apps/ui/src/routes/codex.index.tsx +116 -0
  117. package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
  118. package/apps/ui/src/routes/cursor.$workspaceKey.tsx +426 -0
  119. package/apps/ui/src/routes/cursor.index.tsx +120 -0
  120. package/apps/ui/src/routes/grok-sessions.$sessionId.tsx +388 -0
  121. package/apps/ui/src/routes/grok.$workspaceKey.tsx +301 -0
  122. package/apps/ui/src/routes/grok.index.tsx +48 -0
  123. package/apps/ui/src/routes/index.tsx +124 -0
  124. package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +396 -0
  125. package/apps/ui/src/routes/kiro.$workspaceKey.tsx +303 -0
  126. package/apps/ui/src/routes/kiro.index.tsx +48 -0
  127. package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +420 -0
  128. package/apps/ui/src/routes/opencode.$workspaceKey.tsx +327 -0
  129. package/apps/ui/src/routes/opencode.index.tsx +61 -0
  130. package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +344 -0
  131. package/apps/ui/src/routes/qoder.$workspaceKey.tsx +186 -0
  132. package/apps/ui/src/routes/qoder.index.tsx +48 -0
  133. package/apps/ui/src/routes/settings.tsx +91 -0
  134. package/apps/ui/src/routes/threads.$threadId.tsx +1103 -0
  135. package/apps/ui/src/styles.css +122 -0
  136. package/apps/ui/tsconfig.json +29 -0
  137. package/apps/ui/vite.config.ts +84 -0
  138. package/bin/spiracha.ts +32 -0
  139. package/package.json +54 -80
  140. package/src/client.ts +423 -0
  141. package/src/lib/antigravity-db.ts +414 -44
  142. package/src/lib/antigravity-exporter-types.ts +4 -0
  143. package/src/lib/antigravity-projects.ts +201 -0
  144. package/src/lib/claude-code-db.ts +498 -32
  145. package/src/lib/claude-code-exporter-types.ts +5 -0
  146. package/src/lib/claude-code-transcript-phase.ts +60 -1
  147. package/src/lib/claude-code-transcript.ts +8 -5
  148. package/src/lib/codex-analytics.ts +1 -1
  149. package/src/lib/codex-browser-db.ts +410 -74
  150. package/src/lib/codex-browser-export.ts +29 -41
  151. package/src/lib/codex-browser-types.ts +1 -1
  152. package/src/lib/codex-thread-cache.ts +41 -13
  153. package/src/lib/codex-thread-parser.ts +86 -35
  154. package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
  155. package/src/lib/codex-transcript-filter.ts +31 -0
  156. package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +104 -48
  157. package/src/lib/concurrency.ts +41 -0
  158. package/src/lib/conversation-api.ts +1172 -0
  159. package/src/lib/conversation-data/adapter-helpers.ts +110 -0
  160. package/src/lib/conversation-data/antigravity-adapter.ts +167 -0
  161. package/src/lib/conversation-data/claude-code-adapter.ts +206 -0
  162. package/src/lib/conversation-data/codex-adapter.ts +269 -0
  163. package/src/lib/conversation-data/cursor-adapter.ts +205 -0
  164. package/src/lib/conversation-data/grok-adapter.ts +210 -0
  165. package/src/lib/conversation-data/index.ts +386 -0
  166. package/src/lib/conversation-data/kiro-adapter.ts +174 -0
  167. package/src/lib/conversation-data/message-selector.ts +37 -0
  168. package/src/lib/conversation-data/opencode-adapter.ts +202 -0
  169. package/src/lib/conversation-data/path-match.ts +79 -0
  170. package/src/lib/conversation-data/qoder-adapter.ts +255 -0
  171. package/src/lib/conversation-data/types.ts +159 -0
  172. package/src/lib/conversation-zip-export.ts +57 -0
  173. package/src/lib/cursor-db.ts +100 -25
  174. package/src/lib/cursor-exporter-types.ts +0 -19
  175. package/src/lib/grok-db.ts +1026 -0
  176. package/src/lib/grok-exporter-types.ts +110 -0
  177. package/src/lib/grok-transcript-phase.ts +52 -0
  178. package/src/lib/grok-transcript.ts +154 -0
  179. package/src/lib/kiro-db.ts +52 -1
  180. package/src/lib/model-label.ts +11 -3
  181. package/src/lib/opencode-db.ts +598 -55
  182. package/src/lib/opencode-transcript.ts +8 -5
  183. package/src/lib/qoder-acp-client.ts +268 -0
  184. package/src/lib/qoder-db.ts +1601 -0
  185. package/src/lib/qoder-exporter-types.ts +114 -0
  186. package/src/lib/qoder-transcript-phase.ts +41 -0
  187. package/src/lib/qoder-transcript.ts +112 -0
  188. package/src/lib/shared.ts +12 -0
  189. package/src/lib/transcript-load-limiter.ts +82 -0
  190. package/src/lib/ui-cache.ts +43 -17
  191. package/src/lib/ui-export-archive.ts +60 -0
  192. package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
  193. package/apps/ui/dist/client/assets/analytics-DOy1b6Sh.js +0 -1
  194. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DqySVO2K.js +0 -7
  195. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
  196. package/apps/ui/dist/client/assets/antigravity-keychain-panel-B77qJz6f.js +0 -1
  197. package/apps/ui/dist/client/assets/antigravity._workspaceKey-B3NM6gRH.js +0 -1
  198. package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
  199. package/apps/ui/dist/client/assets/antigravity.index-DP9JC2P9.js +0 -1
  200. package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
  201. package/apps/ui/dist/client/assets/badge-C3i4OKs8.js +0 -1
  202. package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
  203. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-8pQ55ci2.js +0 -1
  204. package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-DVz5UmtU.js +0 -1
  205. package/apps/ui/dist/client/assets/claude-code._workspaceKey-BBKskSum.js +0 -1
  206. package/apps/ui/dist/client/assets/claude-code._workspaceKey-OkikMUFs.js +0 -1
  207. package/apps/ui/dist/client/assets/claude-code.index-Cwu76dbb.js +0 -1
  208. package/apps/ui/dist/client/assets/claude-code.index-D6eMd08t.js +0 -1
  209. package/apps/ui/dist/client/assets/createServerFn-B-KkwmHU.js +0 -3
  210. package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
  211. package/apps/ui/dist/client/assets/cursor-threads._composerId-hBWlP1D-.js +0 -1
  212. package/apps/ui/dist/client/assets/cursor._workspaceKey-BFPkIRi-.js +0 -1
  213. package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
  214. package/apps/ui/dist/client/assets/cursor.index-8y4o20kb.js +0 -2
  215. package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
  216. package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
  217. package/apps/ui/dist/client/assets/delete-confirm-dialog-Bxs07u-j.js +0 -7
  218. package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
  219. package/apps/ui/dist/client/assets/dist-BrJhN2A0.js +0 -5
  220. package/apps/ui/dist/client/assets/dist-Cmd3AIfD.js +0 -1
  221. package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
  222. package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
  223. package/apps/ui/dist/client/assets/dropdown-menu-D5cvPaZt.js +0 -1
  224. package/apps/ui/dist/client/assets/es2015-ad6040dw.js +0 -41
  225. package/apps/ui/dist/client/assets/export-dialog-Dr8d2MqD.js +0 -1
  226. package/apps/ui/dist/client/assets/formatters-BtqyrX_c.js +0 -1
  227. package/apps/ui/dist/client/assets/index-C-STgIH4.js +0 -166
  228. package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
  229. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-CKgfJ7GR.js +0 -1
  230. package/apps/ui/dist/client/assets/kiro-sessions._sessionId-zvIJHtQF.js +0 -1
  231. package/apps/ui/dist/client/assets/kiro._workspaceKey-DhVuJuZB.js +0 -1
  232. package/apps/ui/dist/client/assets/kiro._workspaceKey-QArsjkTY.js +0 -1
  233. package/apps/ui/dist/client/assets/kiro.index-C-fr4m7i.js +0 -1
  234. package/apps/ui/dist/client/assets/kiro.index-OYgAdtZd.js +0 -1
  235. package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
  236. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-DLjiTst-.js +0 -1
  237. package/apps/ui/dist/client/assets/opencode-sessions._sessionId-biBwrRbt.js +0 -8
  238. package/apps/ui/dist/client/assets/opencode._workspaceKey-C7OIzMOJ.js +0 -1
  239. package/apps/ui/dist/client/assets/opencode._workspaceKey-sBakX0oI.js +0 -1
  240. package/apps/ui/dist/client/assets/opencode.index-Bl_kNs9N.js +0 -1
  241. package/apps/ui/dist/client/assets/opencode.index-CN53bVmb.js +0 -1
  242. package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
  243. package/apps/ui/dist/client/assets/preload-helper-B8fFrdxC.js +0 -1
  244. package/apps/ui/dist/client/assets/projects._project-D8WloxQN.js +0 -1
  245. package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
  246. package/apps/ui/dist/client/assets/projects.index-5xRtuJdX.js +0 -3
  247. package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
  248. package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
  249. package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
  250. package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
  251. package/apps/ui/dist/client/assets/routes-DnzF_9CX.js +0 -1
  252. package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
  253. package/apps/ui/dist/client/assets/select-CYF6pF37.js +0 -1
  254. package/apps/ui/dist/client/assets/settings-Dx70jgP_.js +0 -1
  255. package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
  256. package/apps/ui/dist/client/assets/styles-B7Zi9EFA.css +0 -1
  257. package/apps/ui/dist/client/assets/tabs-CmuhkT-c.js +0 -7
  258. package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
  259. package/apps/ui/dist/client/assets/thread-transcript-stats-BUNz6u7F.js +0 -1
  260. package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
  261. package/apps/ui/dist/client/assets/threads._threadId-BMRSFow3.js +0 -1
  262. package/apps/ui/dist/client/assets/useMutation-BSFult2i.js +0 -1
  263. package/apps/ui/dist/client/assets/useQuery-CjINKZf1.js +0 -1
  264. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CQ8Jjs3B.js +0 -387
  265. package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
  266. package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
  267. package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
  268. package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
  269. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BKOyL2T6.js +0 -613
  270. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
  271. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-eS87Dx7g.js +0 -20
  272. package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
  273. package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
  274. package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
  275. package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
  276. package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
  277. package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
  278. package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
  279. package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
  280. package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
  281. package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
  282. package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
  283. package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
  284. package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
  285. package/apps/ui/dist/server/assets/claude-code-db-Ik3VStKZ.js +0 -361
  286. package/apps/ui/dist/server/assets/claude-code-queries-Cgzi4K-e.js +0 -37
  287. package/apps/ui/dist/server/assets/claude-code-server-PfraClXS.js +0 -72
  288. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-C55oe6zj.js +0 -18
  289. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-D1RYYi3j.js +0 -473
  290. package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-DSvPLEUL.js +0 -11
  291. package/apps/ui/dist/server/assets/claude-code-transcript-C-bxh3cS.js +0 -139
  292. package/apps/ui/dist/server/assets/claude-code-transcript-phase-DmUMc8VU.js +0 -10
  293. package/apps/ui/dist/server/assets/claude-code._workspaceKey-B5Xg_-tE.js +0 -28
  294. package/apps/ui/dist/server/assets/claude-code._workspaceKey-BPlzyXPa.js +0 -11
  295. package/apps/ui/dist/server/assets/claude-code._workspaceKey-DqDve2wl.js +0 -191
  296. package/apps/ui/dist/server/assets/claude-code.index-B7yYR7dD.js +0 -92
  297. package/apps/ui/dist/server/assets/claude-code.index-BUHThG1W.js +0 -11
  298. package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
  299. package/apps/ui/dist/server/assets/codex-server-CGXU4H8-.js +0 -2294
  300. package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
  301. package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
  302. package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
  303. package/apps/ui/dist/server/assets/cursor-db-CZnYy7aT.js +0 -830
  304. package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
  305. package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
  306. package/apps/ui/dist/server/assets/cursor-recovery-CXpO8KcD.js +0 -361
  307. package/apps/ui/dist/server/assets/cursor-server-DNu52A3K.js +0 -213
  308. package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
  309. package/apps/ui/dist/server/assets/cursor-threads._composerId-Ckn6ItHI.js +0 -582
  310. package/apps/ui/dist/server/assets/cursor-threads._composerId-p9-ARAzc.js +0 -18
  311. package/apps/ui/dist/server/assets/cursor-transcript-B7tGnqtn.js +0 -125
  312. package/apps/ui/dist/server/assets/cursor._workspaceKey-CZuohIUL.js +0 -28
  313. package/apps/ui/dist/server/assets/cursor._workspaceKey-DPp5JSjy.js +0 -401
  314. package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
  315. package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
  316. package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
  317. package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
  318. package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
  319. package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
  320. package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
  321. package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
  322. package/apps/ui/dist/server/assets/export-dialog-D88ze9Gy.js +0 -240
  323. package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
  324. package/apps/ui/dist/server/assets/kiro-db--fXrED_-.js +0 -552
  325. package/apps/ui/dist/server/assets/kiro-queries-CzNlM2ok.js +0 -37
  326. package/apps/ui/dist/server/assets/kiro-server-9WRxg2Au.js +0 -72
  327. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-CRY2c7YO.js +0 -11
  328. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-F1UlwxmR.js +0 -449
  329. package/apps/ui/dist/server/assets/kiro-sessions._sessionId-bvTulT7C.js +0 -18
  330. package/apps/ui/dist/server/assets/kiro-transcript-DoekQBaH.js +0 -112
  331. package/apps/ui/dist/server/assets/kiro-transcript-phase-CJG40nQg.js +0 -27
  332. package/apps/ui/dist/server/assets/kiro._workspaceKey-BDe9sQwo.js +0 -11
  333. package/apps/ui/dist/server/assets/kiro._workspaceKey-CJsjgNwO.js +0 -28
  334. package/apps/ui/dist/server/assets/kiro._workspaceKey-D_-4Qb7y.js +0 -193
  335. package/apps/ui/dist/server/assets/kiro.index-C__-UROk.js +0 -11
  336. package/apps/ui/dist/server/assets/kiro.index-CmTbqeYj.js +0 -93
  337. package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
  338. package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
  339. package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
  340. package/apps/ui/dist/server/assets/opencode-db-BM7KjOzc.js +0 -397
  341. package/apps/ui/dist/server/assets/opencode-queries-BeNYXnQv.js +0 -50
  342. package/apps/ui/dist/server/assets/opencode-server-BpQ_kRze.js +0 -72
  343. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-CGQ-t0vD.js +0 -518
  344. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-D8Q7in_Z.js +0 -18
  345. package/apps/ui/dist/server/assets/opencode-sessions._sessionId-DKOvi0lV.js +0 -11
  346. package/apps/ui/dist/server/assets/opencode-think-tags-CAoD-EcZ.js +0 -87
  347. package/apps/ui/dist/server/assets/opencode-transcript-BNTcGNym.js +0 -141
  348. package/apps/ui/dist/server/assets/opencode-transcript-phase-OqLkiQD0.js +0 -32
  349. package/apps/ui/dist/server/assets/opencode._workspaceKey-BdqcWHw1.js +0 -11
  350. package/apps/ui/dist/server/assets/opencode._workspaceKey-CfH96HTT.js +0 -28
  351. package/apps/ui/dist/server/assets/opencode._workspaceKey-DdbHPDrO.js +0 -207
  352. package/apps/ui/dist/server/assets/opencode.index-BbVzkJXP.js +0 -11
  353. package/apps/ui/dist/server/assets/opencode.index-Vh_Gq_1L.js +0 -93
  354. package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
  355. package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
  356. package/apps/ui/dist/server/assets/projects._project-4io5LO0E.js +0 -20
  357. package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
  358. package/apps/ui/dist/server/assets/projects._project-D6ZoGhJ8.js +0 -395
  359. package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
  360. package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
  361. package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
  362. package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
  363. package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
  364. package/apps/ui/dist/server/assets/router-Sac2DGk6.js +0 -518
  365. package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
  366. package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
  367. package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
  368. package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
  369. package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
  370. package/apps/ui/dist/server/assets/shared-DyhChtHf.js +0 -137
  371. package/apps/ui/dist/server/assets/source-session-export-server-DNutCawb.js +0 -38
  372. package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
  373. package/apps/ui/dist/server/assets/start-B_HFwkkk.js +0 -4
  374. package/apps/ui/dist/server/assets/tabs-DkaChNVg.js +0 -501
  375. package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
  376. package/apps/ui/dist/server/assets/thread-transcript-stats-Dh34mt2u.js +0 -45
  377. package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
  378. package/apps/ui/dist/server/assets/threads._threadId-BeXEc4Lo.js +0 -648
  379. package/apps/ui/dist/server/assets/threads._threadId-Ci0S4mKU.js +0 -18
  380. package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
  381. package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
  382. package/apps/ui/dist/server/server.js +0 -5895
  383. package/bin/codex-chats-claude.js +0 -5
  384. package/bin/codex-chats.js +0 -5
  385. package/bin/spiracha.js +0 -5
  386. package/src/export-chats.ts +0 -120
  387. package/src/export-claude.ts +0 -36
  388. package/src/export-cursor.ts +0 -244
  389. package/src/lib/claude-exporter.ts +0 -864
  390. package/src/lib/codex-exporter-cli.ts +0 -277
  391. package/src/lib/codex-exporter-db.ts +0 -319
  392. package/src/lib/codex-exporter.ts +0 -115
  393. package/src/lib/cursor-exporter.ts +0 -266
  394. package/src/lib/interactive-cli.ts +0 -433
  395. package/src/lib/native-open.ts +0 -54
  396. package/src/mcp-server.ts +0 -137
  397. package/src/spiracha.ts +0 -116
  398. package/src/ui-cli.ts +0 -310
  399. /package/apps/ui/{dist/client → public}/icon.svg +0 -0
  400. /package/apps/ui/{dist/client → public}/manifest.json +0 -0
  401. /package/apps/ui/{dist/client → public}/robots.txt +0 -0
@@ -0,0 +1,344 @@
1
+ import type { ThreadEvent, ThreadTranscriptStats } from '@spiracha/lib/codex-browser-types';
2
+ import type { QoderSessionTranscript } from '@spiracha/lib/qoder-exporter-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 { DEFAULT_SHOW_USER_MESSAGES, 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 { downloadTextFile, downloadUrlFile } from '#/lib/download';
20
+ import { formatDateTime, formatList, formatNumber } from '#/lib/formatters';
21
+ import { qoderSessionDetailQueryOptions } from '#/lib/qoder-queries';
22
+ import { exportQoderSessionFn } from '#/lib/qoder-server';
23
+ import { getQoderThreadTranscriptStats, qoderTranscriptToThreadEvents } from '#/lib/qoder-transcript-events';
24
+
25
+ type ExportDialogOptions = {
26
+ includeCommentary: boolean;
27
+ includeMetadata: boolean;
28
+ includeTools: boolean;
29
+ outputFormat: 'md' | 'txt';
30
+ zipArchive: boolean;
31
+ };
32
+
33
+ type TranscriptControlsProps = {
34
+ rawJsonDisabled?: boolean;
35
+ showCommentary: boolean;
36
+ showExtraEvents: boolean;
37
+ showRawJson: boolean;
38
+ showToolCalls: boolean;
39
+ showUserMessages: boolean;
40
+ onShowCommentaryChange: (checked: boolean) => void;
41
+ onShowExtraEventsChange: (checked: boolean) => void;
42
+ onShowRawJsonChange: (checked: boolean) => void;
43
+ onShowToolCallsChange: (checked: boolean) => void;
44
+ onShowUserMessagesChange: (checked: boolean) => void;
45
+ };
46
+
47
+ const QoderSessionDetailErrorComponent = ({ error }: { error: Error }) => {
48
+ return <ReloadErrorPanel description={error.message} title="Failed to load Qoder session" />;
49
+ };
50
+
51
+ const buildSessionMetadata = (detail: QoderSessionTranscript) => [
52
+ { label: 'Session ID', value: <span data-mono="true">{detail.session.sessionId}</span> },
53
+ { label: 'Task ID', value: detail.session.taskId ?? 'unknown' },
54
+ { label: 'Request ID', value: detail.session.requestId ?? 'unknown' },
55
+ {
56
+ label: 'Workspace',
57
+ value: (
58
+ <Link
59
+ className="text-[var(--accent)]"
60
+ params={{ workspaceKey: detail.session.workspaceKey }}
61
+ to="/qoder/$workspaceKey"
62
+ >
63
+ {detail.session.workspaceLabel}
64
+ </Link>
65
+ ),
66
+ },
67
+ { label: 'Worktree', value: detail.session.worktree },
68
+ { label: 'Workspace data ID', value: detail.session.workspaceStorageId ?? 'unknown' },
69
+ { label: 'State file', value: detail.session.sourceStatePath ?? 'unknown' },
70
+ { label: 'Status', value: detail.session.status ?? 'unknown' },
71
+ { label: 'Model', value: detail.session.model ?? 'unknown' },
72
+ { label: 'Execution mode', value: detail.session.executionMode ?? 'unknown' },
73
+ { label: 'Agent class', value: detail.session.agentClass ?? 'unknown' },
74
+ {
75
+ label: 'Created',
76
+ value: <span suppressHydrationWarning>{formatDateTime(detail.session.createdAtMs)}</span>,
77
+ },
78
+ {
79
+ label: 'Last active',
80
+ value: <span suppressHydrationWarning>{formatDateTime(detail.session.lastActiveAtMs)}</span>,
81
+ },
82
+ ];
83
+
84
+ const buildTranscriptStatsItems = (
85
+ detail: QoderSessionTranscript,
86
+ events: ThreadEvent[],
87
+ stats: ThreadTranscriptStats,
88
+ ) => [
89
+ { label: 'Event kinds', value: formatList([...new Set(events.map((event) => event.kind))]) },
90
+ { label: 'Messages', value: formatNumber(stats.messageCount) },
91
+ { label: 'User messages', value: formatNumber(stats.userMessageCount) },
92
+ { label: 'Assistant messages', value: formatNumber(stats.assistantMessageCount) },
93
+ { label: 'Final answers', value: formatNumber(stats.finalAnswerCount) },
94
+ { label: 'File operations', value: formatNumber(detail.session.fileOperationCount) },
95
+ { label: 'Snapshots', value: formatNumber(detail.session.snapshotFileCount) },
96
+ { label: 'Renderable parts', value: formatNumber(detail.renderablePartCount) },
97
+ ];
98
+
99
+ const QoderTranscriptControls = ({
100
+ rawJsonDisabled = false,
101
+ showCommentary,
102
+ showExtraEvents,
103
+ showRawJson,
104
+ showToolCalls,
105
+ showUserMessages,
106
+ onShowCommentaryChange,
107
+ onShowExtraEventsChange,
108
+ onShowRawJsonChange,
109
+ onShowToolCallsChange,
110
+ onShowUserMessagesChange,
111
+ }: TranscriptControlsProps) => {
112
+ return (
113
+ <div className="flex flex-wrap gap-4 rounded-xl border border-[var(--border)] bg-[var(--panel)] px-4 py-3 shadow-[var(--panel-shadow)]">
114
+ <div className="flex items-center gap-2 text-sm">
115
+ <Checkbox
116
+ checked={showToolCalls}
117
+ id="qoder-transcript-show-tool-calls"
118
+ onCheckedChange={(checked) => onShowToolCallsChange(checked === true)}
119
+ />
120
+ <label htmlFor="qoder-transcript-show-tool-calls">Show tool calls</label>
121
+ </div>
122
+ <div className="flex items-center gap-2 text-sm">
123
+ <Checkbox
124
+ checked={showCommentary}
125
+ id="qoder-transcript-show-commentary"
126
+ onCheckedChange={(checked) => onShowCommentaryChange(checked === true)}
127
+ />
128
+ <label htmlFor="qoder-transcript-show-commentary">Show commentary</label>
129
+ </div>
130
+ <div className="flex items-center gap-2 text-sm">
131
+ <Checkbox
132
+ checked={showExtraEvents}
133
+ id="qoder-transcript-show-extra-events"
134
+ onCheckedChange={(checked) => onShowExtraEventsChange(checked === true)}
135
+ />
136
+ <label htmlFor="qoder-transcript-show-extra-events">Show extra events</label>
137
+ </div>
138
+ <div className="flex items-center gap-2 text-sm">
139
+ <Checkbox
140
+ checked={showRawJson}
141
+ disabled={rawJsonDisabled}
142
+ id="qoder-transcript-show-raw-json"
143
+ onCheckedChange={(checked) => onShowRawJsonChange(checked === true)}
144
+ />
145
+ <label htmlFor="qoder-transcript-show-raw-json">Raw JSON</label>
146
+ </div>
147
+ <div className="flex items-center gap-2 text-sm">
148
+ <Checkbox
149
+ checked={showUserMessages}
150
+ id="qoder-transcript-show-user-messages"
151
+ onCheckedChange={(checked) => onShowUserMessagesChange(checked === true)}
152
+ />
153
+ <label htmlFor="qoder-transcript-show-user-messages">User</label>
154
+ </div>
155
+ </div>
156
+ );
157
+ };
158
+
159
+ const QoderRawPanels = ({ detail, events }: { detail: QoderSessionTranscript; events: ThreadEvent[] }) => {
160
+ return (
161
+ <div className="space-y-4">
162
+ <JsonPanel title="Session summary" value={detail.session} />
163
+ <JsonPanel title="Qoder entries" value={detail.entries} />
164
+ <JsonPanel title="Raw Qoder session" value={detail.rawSession} />
165
+ <JsonPanel title="Transcript events" value={events} />
166
+ </div>
167
+ );
168
+ };
169
+
170
+ const QoderSessionDetailPage = () => {
171
+ const detail = useSuspenseQuery(qoderSessionDetailQueryOptions(Route.useParams().sessionId)).data;
172
+ const [pendingExport, setPendingExport] = useState(false);
173
+ const [showToolCalls, setShowToolCalls] = useState(false);
174
+ const [showCommentary, setShowCommentary] = useState(false);
175
+ const [showExtraEvents, setShowExtraEvents] = useState(false);
176
+ const [showRawJson, setShowRawJson] = useState(false);
177
+ const [showUserMessages, setShowUserMessages] = useState(DEFAULT_SHOW_USER_MESSAGES);
178
+ const transcriptEvents = useMemo(() => qoderTranscriptToThreadEvents(detail), [detail]);
179
+ const transcriptStats = useMemo(() => getQoderThreadTranscriptStats(transcriptEvents), [transcriptEvents]);
180
+ const modelLabel = detail.session.model ?? 'Qoder';
181
+
182
+ const exportSessionMutation = useMutation({
183
+ mutationFn: async (options: ExportDialogOptions) => {
184
+ const download = await exportQoderSessionFn({
185
+ data: {
186
+ includeCommentary: options.includeCommentary,
187
+ includeMetadata: options.includeMetadata,
188
+ includeTools: options.includeTools,
189
+ outputFormat: options.outputFormat,
190
+ sessionId: detail.session.sessionId,
191
+ zipArchive: options.zipArchive,
192
+ },
193
+ });
194
+ if (download.mode === 'download') {
195
+ downloadTextFile(download.fileName, download.content, download.mimeType);
196
+ return;
197
+ }
198
+
199
+ await downloadUrlFile(download.fileName, download.downloadUrl);
200
+ },
201
+ onSuccess: () => {
202
+ setPendingExport(false);
203
+ },
204
+ });
205
+
206
+ return (
207
+ <div className="space-y-6">
208
+ <PageHeader
209
+ actions={
210
+ <Button
211
+ className="rounded-full"
212
+ type="button"
213
+ variant="outline"
214
+ onClick={() => setPendingExport(true)}
215
+ >
216
+ <Download className="mr-2 size-4" />
217
+ Export
218
+ </Button>
219
+ }
220
+ breadcrumb={
221
+ <Breadcrumbs
222
+ items={[
223
+ { label: 'Qoder', to: '/qoder' },
224
+ {
225
+ label: detail.session.workspaceLabel,
226
+ params: { workspaceKey: detail.session.workspaceKey },
227
+ to: '/qoder/$workspaceKey',
228
+ },
229
+ { label: detail.session.title },
230
+ ]}
231
+ />
232
+ }
233
+ eyebrow="Qoder session"
234
+ subtitle="Session detail for the selected Qoder local history entry and local state."
235
+ title={detail.session.title}
236
+ />
237
+
238
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
239
+ <MetricCard label="Prompts" value={formatNumber(detail.session.messageCount)} />
240
+ <MetricCard label="File ops" value={formatNumber(detail.session.fileOperationCount)} />
241
+ <MetricCard label="Snapshots" value={formatNumber(detail.session.snapshotFileCount)} />
242
+ <MetricCard label="Model" value={modelLabel} />
243
+ </div>
244
+
245
+ <Tabs className="space-y-4" defaultValue="transcript">
246
+ <TabsList className="grid w-fit min-w-[24rem] grid-cols-3 rounded-full border border-[var(--border)] bg-[var(--panel)] p-1">
247
+ <TabsTrigger className="rounded-full px-5 text-sm" value="transcript">
248
+ Transcript
249
+ </TabsTrigger>
250
+ <TabsTrigger className="rounded-full px-5 text-sm" value="metadata">
251
+ Metadata
252
+ </TabsTrigger>
253
+ <TabsTrigger className="rounded-full px-5 text-sm" value="raw">
254
+ Raw
255
+ </TabsTrigger>
256
+ </TabsList>
257
+
258
+ <TabsContent className="space-y-3" value="transcript">
259
+ <QoderTranscriptControls
260
+ rawJsonDisabled={transcriptEvents.length === 0}
261
+ showCommentary={showCommentary}
262
+ showExtraEvents={showExtraEvents}
263
+ showRawJson={showRawJson}
264
+ showToolCalls={showToolCalls}
265
+ showUserMessages={showUserMessages}
266
+ onShowCommentaryChange={setShowCommentary}
267
+ onShowExtraEventsChange={setShowExtraEvents}
268
+ onShowRawJsonChange={setShowRawJson}
269
+ onShowToolCallsChange={setShowToolCalls}
270
+ onShowUserMessagesChange={setShowUserMessages}
271
+ />
272
+ {transcriptEvents.length > 0 ? (
273
+ <TranscriptView
274
+ assistantModel={modelLabel}
275
+ events={transcriptEvents}
276
+ projectPath={detail.session.worktree}
277
+ showCommentary={showCommentary}
278
+ showExtraEvents={showExtraEvents}
279
+ showRawJson={showRawJson}
280
+ showToolCalls={showToolCalls}
281
+ showUserMessages={showUserMessages}
282
+ />
283
+ ) : (
284
+ <section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
285
+ <h3 className="font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]">
286
+ Transcript
287
+ </h3>
288
+ <p className="mt-4 text-[var(--muted-foreground)] text-sm">
289
+ No renderable Qoder transcript content was found for this session.
290
+ </p>
291
+ </section>
292
+ )}
293
+ </TabsContent>
294
+
295
+ <TabsContent value="metadata">
296
+ <div className="grid gap-4 xl:grid-cols-[0.95fr_1.05fr]">
297
+ <MetadataSection items={buildSessionMetadata(detail)} title="Session metadata" />
298
+ <MetadataSection
299
+ items={buildTranscriptStatsItems(detail, transcriptEvents, transcriptStats)}
300
+ title="Transcript stats"
301
+ />
302
+ </div>
303
+ </TabsContent>
304
+
305
+ <TabsContent value="raw">
306
+ <QoderRawPanels detail={detail} events={transcriptEvents} />
307
+ </TabsContent>
308
+ </Tabs>
309
+
310
+ <ExportDialog
311
+ errorMessage={
312
+ exportSessionMutation.isError
313
+ ? exportSessionMutation.error instanceof Error
314
+ ? exportSessionMutation.error.message
315
+ : 'Export failed'
316
+ : null
317
+ }
318
+ open={pendingExport}
319
+ pending={exportSessionMutation.isPending}
320
+ title={`Export ${detail.session.title}`}
321
+ onExport={(options) => exportSessionMutation.mutate(options)}
322
+ onOpenChange={(open) => {
323
+ setPendingExport(open);
324
+ if (!open) {
325
+ exportSessionMutation.reset();
326
+ }
327
+ }}
328
+ />
329
+ </div>
330
+ );
331
+ };
332
+
333
+ export const Route = createFileRoute('/qoder-sessions/$sessionId')({
334
+ component: QoderSessionDetailPage,
335
+ errorComponent: QoderSessionDetailErrorComponent,
336
+ loader: ({ context, params }) =>
337
+ context.queryClient.ensureQueryData(qoderSessionDetailQueryOptions(params.sessionId)),
338
+ pendingComponent: () => (
339
+ <LoadingPanel
340
+ description="Loading the Qoder transcript, file operations, and session metadata."
341
+ title="Loading session"
342
+ />
343
+ ),
344
+ });
@@ -0,0 +1,186 @@
1
+ import type { QoderSessionSummary, QoderWorkspaceGroup } from '@spiracha/lib/qoder-exporter-types';
2
+ import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
3
+ import { createFileRoute } from '@tanstack/react-router';
4
+ import { useDeferredValue, useMemo, useState } from 'react';
5
+ import { ExportDialog } from '#/components/export-dialog';
6
+ import { ListSearchInput } from '#/components/list-search-input';
7
+ import { LoadingPanel } from '#/components/loading-panel';
8
+ import { PageHeader } from '#/components/page-header';
9
+ import { QoderSessionsTable } from '#/components/qoder-sessions-table';
10
+ import { ReloadErrorPanel } from '#/components/reload-error-panel';
11
+ import { downloadTextFile, downloadUrlFile } from '#/lib/download';
12
+ import { qoderSessionsQueryOptions, qoderWorkspacesQueryOptions } from '#/lib/qoder-queries';
13
+ import { exportQoderSessionFn, exportQoderSessionsFn } from '#/lib/qoder-server';
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
+ type PendingSessionExport = {
25
+ label: string;
26
+ sessionIds: string[];
27
+ };
28
+
29
+ const findWorkspaceOrThrow = (workspaces: QoderWorkspaceGroup[], workspaceKey: string) => {
30
+ const workspace = workspaces.find((candidate) => candidate.key === workspaceKey);
31
+ if (!workspace) {
32
+ throw new Error(`Qoder workspace not found: ${workspaceKey}`);
33
+ }
34
+
35
+ return workspace;
36
+ };
37
+
38
+ const QoderWorkspaceErrorComponent = ({ error }: { error: Error }) => {
39
+ return <ReloadErrorPanel description={error.message} title="Failed to load Qoder workspace" />;
40
+ };
41
+
42
+ const QoderWorkspacePage = () => {
43
+ const params = Route.useParams();
44
+ const workspaces = useSuspenseQuery(qoderWorkspacesQueryOptions()).data;
45
+ const workspace = findWorkspaceOrThrow(workspaces, params.workspaceKey);
46
+ const sessions = useSuspenseQuery(qoderSessionsQueryOptions(workspace.key)).data;
47
+ const [searchInput, setSearchInput] = useState('');
48
+ const [pendingExport, setPendingExport] = useState<PendingSessionExport | null>(null);
49
+ const deferredSearch = useDeferredValue(searchInput);
50
+
51
+ const exportMutation = useMutation({
52
+ mutationFn: async (options: ExportDialogOptions) => {
53
+ if (!pendingExport) {
54
+ throw new Error('No Qoder session selected for export');
55
+ }
56
+
57
+ const download =
58
+ pendingExport.sessionIds.length === 1
59
+ ? await exportQoderSessionFn({
60
+ data: {
61
+ includeCommentary: options.includeCommentary,
62
+ includeMetadata: options.includeMetadata,
63
+ includeTools: options.includeTools,
64
+ outputFormat: options.outputFormat,
65
+ sessionId: pendingExport.sessionIds[0]!,
66
+ zipArchive: options.zipArchive,
67
+ },
68
+ })
69
+ : await exportQoderSessionsFn({
70
+ data: {
71
+ includeCommentary: options.includeCommentary,
72
+ includeMetadata: options.includeMetadata,
73
+ includeTools: options.includeTools,
74
+ outputFormat: options.outputFormat,
75
+ sessionIds: pendingExport.sessionIds,
76
+ zipArchive: options.zipArchive,
77
+ },
78
+ });
79
+ if (download.mode === 'download') {
80
+ downloadTextFile(download.fileName, download.content, download.mimeType);
81
+ return;
82
+ }
83
+
84
+ await downloadUrlFile(download.fileName, download.downloadUrl);
85
+ },
86
+ onSuccess: () => {
87
+ setPendingExport(null);
88
+ },
89
+ });
90
+
91
+ const visibleSessions = useMemo(
92
+ () =>
93
+ sessions.filter((session) =>
94
+ matchesTextQuery(deferredSearch, [
95
+ session.title,
96
+ session.sessionId,
97
+ session.taskId,
98
+ session.requestId,
99
+ session.model,
100
+ session.status,
101
+ session.executionMode,
102
+ session.agentClass,
103
+ session.query,
104
+ session.sourceStatePath,
105
+ ]),
106
+ ),
107
+ [deferredSearch, sessions],
108
+ );
109
+ const visibleSessionsById = useMemo(
110
+ () => new Map(visibleSessions.map((session) => [session.sessionId, session])),
111
+ [visibleSessions],
112
+ );
113
+ const lookupSelectedSessions = (sessionIds: string[]) =>
114
+ sessionIds
115
+ .map((sessionId) => visibleSessionsById.get(sessionId) ?? null)
116
+ .filter((session): session is QoderSessionSummary => session !== null);
117
+ const openExportForSessions = (selectedSessions: QoderSessionSummary[]) => {
118
+ if (selectedSessions.length === 0) {
119
+ return;
120
+ }
121
+
122
+ setPendingExport({
123
+ label:
124
+ selectedSessions.length === 1
125
+ ? selectedSessions[0]!.title
126
+ : `${selectedSessions.length} selected sessions`,
127
+ sessionIds: selectedSessions.map((session) => session.sessionId),
128
+ });
129
+ };
130
+
131
+ return (
132
+ <div className="space-y-6">
133
+ <PageHeader
134
+ actions={
135
+ <ListSearchInput
136
+ placeholder="Search session title, id, status, or request"
137
+ value={searchInput}
138
+ onValueChange={setSearchInput}
139
+ />
140
+ }
141
+ eyebrow="Qoder workspace"
142
+ subtitle="Inspect local Qoder prompts, session metadata, and file-operation history."
143
+ title={workspace.label}
144
+ />
145
+
146
+ <QoderSessionsTable
147
+ sessions={visibleSessions}
148
+ onExportSession={(session) => openExportForSessions([session])}
149
+ onExportSessions={(sessionIds) => openExportForSessions(lookupSelectedSessions(sessionIds))}
150
+ />
151
+
152
+ <ExportDialog
153
+ forceZipArchive={pendingExport ? pendingExport.sessionIds.length > 1 : false}
154
+ open={pendingExport !== null}
155
+ pending={exportMutation.isPending}
156
+ title={pendingExport ? `Export ${pendingExport.label}` : 'Export session'}
157
+ onExport={(options) => exportMutation.mutate(options)}
158
+ onOpenChange={(open) => {
159
+ if (!open) {
160
+ setPendingExport(null);
161
+ exportMutation.reset();
162
+ }
163
+ }}
164
+ />
165
+
166
+ {exportMutation.isError ? (
167
+ <p className="text-[var(--destructive)] text-sm">
168
+ {exportMutation.error instanceof Error ? exportMutation.error.message : 'Session export failed'}
169
+ </p>
170
+ ) : null}
171
+ </div>
172
+ );
173
+ };
174
+
175
+ export const Route = createFileRoute('/qoder/$workspaceKey')({
176
+ component: QoderWorkspacePage,
177
+ errorComponent: QoderWorkspaceErrorComponent,
178
+ loader: async ({ context, params }) => {
179
+ const workspaces = await context.queryClient.ensureQueryData(qoderWorkspacesQueryOptions());
180
+ findWorkspaceOrThrow(workspaces, params.workspaceKey);
181
+ await context.queryClient.ensureQueryData(qoderSessionsQueryOptions(params.workspaceKey));
182
+ },
183
+ pendingComponent: () => (
184
+ <LoadingPanel description="Loading Qoder sessions and local metadata." title="Loading workspace" />
185
+ ),
186
+ });
@@ -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 { ListSearchInput } from '#/components/list-search-input';
5
+ import { PageHeader } from '#/components/page-header';
6
+ import { QoderWorkspacesTable } from '#/components/qoder-workspaces-table';
7
+ import { ReloadErrorPanel } from '#/components/reload-error-panel';
8
+ import { qoderWorkspacesQueryOptions } from '#/lib/qoder-queries';
9
+ import { matchesTextQuery } from '#/lib/text-filter';
10
+
11
+ const QoderErrorComponent = ({ error }: { error: Error }) => {
12
+ return <ReloadErrorPanel description={error.message} title="Failed to load Qoder workspaces" />;
13
+ };
14
+
15
+ const QoderPage = () => {
16
+ const workspaces = useSuspenseQuery(qoderWorkspacesQueryOptions()).data;
17
+ const [searchInput, setSearchInput] = useState('');
18
+ const deferredSearch = useDeferredValue(searchInput);
19
+
20
+ const visibleWorkspaces = workspaces.filter((workspace) =>
21
+ matchesTextQuery(deferredSearch, [workspace.label, workspace.worktree, workspace.key]),
22
+ );
23
+
24
+ return (
25
+ <div className="space-y-6">
26
+ <PageHeader
27
+ actions={
28
+ <ListSearchInput
29
+ placeholder="Search workspace name or path"
30
+ value={searchInput}
31
+ onValueChange={setSearchInput}
32
+ />
33
+ }
34
+ eyebrow="Inventory"
35
+ subtitle="Workspace groups are derived from local Qoder history and workspace data."
36
+ title="Qoder"
37
+ />
38
+
39
+ <QoderWorkspacesTable workspaces={visibleWorkspaces} />
40
+ </div>
41
+ );
42
+ };
43
+
44
+ export const Route = createFileRoute('/qoder/')({
45
+ component: QoderPage,
46
+ errorComponent: QoderErrorComponent,
47
+ loader: ({ context }) => context.queryClient.ensureQueryData(qoderWorkspacesQueryOptions()),
48
+ });
@@ -0,0 +1,91 @@
1
+ import { createFileRoute } from '@tanstack/react-router';
2
+ import { Settings2 } from 'lucide-react';
3
+ import { PageHeader } from '#/components/page-header';
4
+ import { Checkbox } from '#/components/ui/checkbox';
5
+ import { useSettings } from '#/lib/settings-store';
6
+
7
+ export const Route = createFileRoute('/settings')({
8
+ component: SettingsPage,
9
+ });
10
+
11
+ function SettingsPage() {
12
+ const { settings, updateSetting } = useSettings();
13
+
14
+ return (
15
+ <div className="space-y-6">
16
+ <PageHeader
17
+ eyebrow="Configuration"
18
+ subtitle="Control how paths and usernames appear in transcript messages."
19
+ title="Settings"
20
+ />
21
+
22
+ <section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
23
+ <h3 className="flex items-center gap-2 font-semibold text-[var(--muted-foreground)] text-xs uppercase tracking-[0.18em]">
24
+ <Settings2 className="size-3.5" />
25
+ Privacy
26
+ </h3>
27
+
28
+ <div className="mt-5 space-y-5">
29
+ <div className="flex items-center gap-3">
30
+ <Checkbox
31
+ checked={settings.redactUsername}
32
+ id="redact-username"
33
+ onCheckedChange={(checked) => updateSetting('redactUsername', checked === true)}
34
+ />
35
+ <div className="min-w-0">
36
+ <label
37
+ className="cursor-pointer font-medium text-sm leading-none"
38
+ htmlFor="redact-username"
39
+ >
40
+ Redact Username
41
+ </label>
42
+ <p className="mt-1.5 text-[var(--muted-foreground)] text-xs leading-5">
43
+ Replaces{' '}
44
+ <code className="rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]">
45
+ /Users/[username]/
46
+ </code>{' '}
47
+ with{' '}
48
+ <code className="rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]">
49
+ ~/
50
+ </code>{' '}
51
+ in all transcript messages.
52
+ </p>
53
+ </div>
54
+ </div>
55
+
56
+ <div className="flex items-center gap-3">
57
+ <Checkbox
58
+ checked={settings.convertToProjectRoot}
59
+ id="convert-project-root"
60
+ onCheckedChange={(checked) => updateSetting('convertToProjectRoot', checked === true)}
61
+ />
62
+ <div className="min-w-0">
63
+ <label
64
+ className="cursor-pointer font-medium text-sm leading-none"
65
+ htmlFor="convert-project-root"
66
+ >
67
+ Convert Absolute Paths to Project Root
68
+ </label>
69
+ <p className="mt-1.5 text-[var(--muted-foreground)] text-xs leading-5">
70
+ Converts the current thread or project cwd from{' '}
71
+ <code className="rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]">
72
+ /Users/[username]/workspace/[projectname]/
73
+ </code>{' '}
74
+ to project-root-relative paths, so{' '}
75
+ <code className="rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]">
76
+ /Users/jane/workspace/myapp/src/index.ts
77
+ </code>{' '}
78
+ appear as{' '}
79
+ <code className="rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]">
80
+ src/index.ts
81
+ </code>
82
+ . If Redact Username is also enabled, remaining absolute paths outside the project root
83
+ are redacted afterward.
84
+ </p>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </section>
89
+ </div>
90
+ );
91
+ }