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,341 @@
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 { 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
+ { label: 'Created', value: formatDateTime(detail.session.createdAtMs) },
75
+ {
76
+ label: 'Last active',
77
+ value: formatDateTime(detail.session.lastActiveAtMs),
78
+ },
79
+ ];
80
+
81
+ const buildTranscriptStatsItems = (
82
+ detail: QoderSessionTranscript,
83
+ events: ThreadEvent[],
84
+ stats: ThreadTranscriptStats,
85
+ ) => [
86
+ { label: 'Event kinds', value: formatList([...new Set(events.map((event) => event.kind))]) },
87
+ { label: 'Messages', value: formatNumber(stats.messageCount) },
88
+ { label: 'User messages', value: formatNumber(stats.userMessageCount) },
89
+ { label: 'Assistant messages', value: formatNumber(stats.assistantMessageCount) },
90
+ { label: 'Final answers', value: formatNumber(stats.finalAnswerCount) },
91
+ { label: 'File operations', value: formatNumber(detail.session.fileOperationCount) },
92
+ { label: 'Snapshots', value: formatNumber(detail.session.snapshotFileCount) },
93
+ { label: 'Renderable parts', value: formatNumber(detail.renderablePartCount) },
94
+ ];
95
+
96
+ const QoderTranscriptControls = ({
97
+ rawJsonDisabled = false,
98
+ showCommentary,
99
+ showExtraEvents,
100
+ showRawJson,
101
+ showToolCalls,
102
+ showUserMessages,
103
+ onShowCommentaryChange,
104
+ onShowExtraEventsChange,
105
+ onShowRawJsonChange,
106
+ onShowToolCallsChange,
107
+ onShowUserMessagesChange,
108
+ }: TranscriptControlsProps) => {
109
+ return (
110
+ <div className="flex flex-wrap gap-4 rounded-xl border border-[var(--border)] bg-[var(--panel)] px-4 py-3 shadow-[var(--panel-shadow)]">
111
+ <div className="flex items-center gap-2 text-sm">
112
+ <Checkbox
113
+ checked={showToolCalls}
114
+ id="qoder-transcript-show-tool-calls"
115
+ onCheckedChange={(checked) => onShowToolCallsChange(checked === true)}
116
+ />
117
+ <label htmlFor="qoder-transcript-show-tool-calls">Show tool calls</label>
118
+ </div>
119
+ <div className="flex items-center gap-2 text-sm">
120
+ <Checkbox
121
+ checked={showCommentary}
122
+ id="qoder-transcript-show-commentary"
123
+ onCheckedChange={(checked) => onShowCommentaryChange(checked === true)}
124
+ />
125
+ <label htmlFor="qoder-transcript-show-commentary">Show commentary</label>
126
+ </div>
127
+ <div className="flex items-center gap-2 text-sm">
128
+ <Checkbox
129
+ checked={showExtraEvents}
130
+ id="qoder-transcript-show-extra-events"
131
+ onCheckedChange={(checked) => onShowExtraEventsChange(checked === true)}
132
+ />
133
+ <label htmlFor="qoder-transcript-show-extra-events">Show extra events</label>
134
+ </div>
135
+ <div className="flex items-center gap-2 text-sm">
136
+ <Checkbox
137
+ checked={showRawJson}
138
+ disabled={rawJsonDisabled}
139
+ id="qoder-transcript-show-raw-json"
140
+ onCheckedChange={(checked) => onShowRawJsonChange(checked === true)}
141
+ />
142
+ <label htmlFor="qoder-transcript-show-raw-json">Raw JSON</label>
143
+ </div>
144
+ <div className="flex items-center gap-2 text-sm">
145
+ <Checkbox
146
+ checked={showUserMessages}
147
+ id="qoder-transcript-show-user-messages"
148
+ onCheckedChange={(checked) => onShowUserMessagesChange(checked === true)}
149
+ />
150
+ <label htmlFor="qoder-transcript-show-user-messages">User</label>
151
+ </div>
152
+ </div>
153
+ );
154
+ };
155
+
156
+ const QoderRawPanels = ({ detail, events }: { detail: QoderSessionTranscript; events: ThreadEvent[] }) => {
157
+ return (
158
+ <div className="space-y-4">
159
+ <JsonPanel title="Session summary" value={detail.session} />
160
+ <JsonPanel title="Qoder entries" value={detail.entries} />
161
+ <JsonPanel title="Raw Qoder session" value={detail.rawSession} />
162
+ <JsonPanel title="Transcript events" value={events} />
163
+ </div>
164
+ );
165
+ };
166
+
167
+ const QoderSessionDetailPage = () => {
168
+ const detail = useSuspenseQuery(qoderSessionDetailQueryOptions(Route.useParams().sessionId)).data;
169
+ const [pendingExport, setPendingExport] = useState(false);
170
+ const [showToolCalls, setShowToolCalls] = useState(false);
171
+ const [showCommentary, setShowCommentary] = useState(false);
172
+ const [showExtraEvents, setShowExtraEvents] = useState(false);
173
+ const [showRawJson, setShowRawJson] = useState(false);
174
+ const [showUserMessages, setShowUserMessages] = useState(true);
175
+ const transcriptEvents = useMemo(() => qoderTranscriptToThreadEvents(detail), [detail]);
176
+ const transcriptStats = useMemo(() => getQoderThreadTranscriptStats(transcriptEvents), [transcriptEvents]);
177
+ const modelLabel = detail.session.model ?? 'Qoder';
178
+
179
+ const exportSessionMutation = useMutation({
180
+ mutationFn: async (options: ExportDialogOptions) => {
181
+ const download = await exportQoderSessionFn({
182
+ data: {
183
+ includeCommentary: options.includeCommentary,
184
+ includeMetadata: options.includeMetadata,
185
+ includeTools: options.includeTools,
186
+ outputFormat: options.outputFormat,
187
+ sessionId: detail.session.sessionId,
188
+ zipArchive: options.zipArchive,
189
+ },
190
+ });
191
+ if (download.mode === 'download') {
192
+ downloadTextFile(download.fileName, download.content, download.mimeType);
193
+ return;
194
+ }
195
+
196
+ await downloadUrlFile(download.fileName, download.downloadUrl);
197
+ },
198
+ onSuccess: () => {
199
+ setPendingExport(false);
200
+ },
201
+ });
202
+
203
+ return (
204
+ <div className="space-y-6">
205
+ <PageHeader
206
+ actions={
207
+ <Button
208
+ className="rounded-full"
209
+ type="button"
210
+ variant="outline"
211
+ onClick={() => setPendingExport(true)}
212
+ >
213
+ <Download className="mr-2 size-4" />
214
+ Export
215
+ </Button>
216
+ }
217
+ breadcrumb={
218
+ <Breadcrumbs
219
+ items={[
220
+ { label: 'Qoder', to: '/qoder' },
221
+ {
222
+ label: detail.session.workspaceLabel,
223
+ params: { workspaceKey: detail.session.workspaceKey },
224
+ to: '/qoder/$workspaceKey',
225
+ },
226
+ { label: detail.session.title },
227
+ ]}
228
+ />
229
+ }
230
+ eyebrow="Qoder session"
231
+ subtitle="Session detail for the selected Qoder local history entry and local state."
232
+ title={detail.session.title}
233
+ />
234
+
235
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
236
+ <MetricCard label="Prompts" value={formatNumber(detail.session.messageCount)} />
237
+ <MetricCard label="File ops" value={formatNumber(detail.session.fileOperationCount)} />
238
+ <MetricCard label="Snapshots" value={formatNumber(detail.session.snapshotFileCount)} />
239
+ <MetricCard label="Model" value={modelLabel} />
240
+ </div>
241
+
242
+ <Tabs className="space-y-4" defaultValue="transcript">
243
+ <TabsList className="grid w-fit min-w-[24rem] grid-cols-3 rounded-full border border-[var(--border)] bg-[var(--panel)] p-1">
244
+ <TabsTrigger className="rounded-full px-5 text-sm" value="transcript">
245
+ Transcript
246
+ </TabsTrigger>
247
+ <TabsTrigger className="rounded-full px-5 text-sm" value="metadata">
248
+ Metadata
249
+ </TabsTrigger>
250
+ <TabsTrigger className="rounded-full px-5 text-sm" value="raw">
251
+ Raw
252
+ </TabsTrigger>
253
+ </TabsList>
254
+
255
+ <TabsContent className="space-y-3" value="transcript">
256
+ <QoderTranscriptControls
257
+ rawJsonDisabled={transcriptEvents.length === 0}
258
+ showCommentary={showCommentary}
259
+ showExtraEvents={showExtraEvents}
260
+ showRawJson={showRawJson}
261
+ showToolCalls={showToolCalls}
262
+ showUserMessages={showUserMessages}
263
+ onShowCommentaryChange={setShowCommentary}
264
+ onShowExtraEventsChange={setShowExtraEvents}
265
+ onShowRawJsonChange={setShowRawJson}
266
+ onShowToolCallsChange={setShowToolCalls}
267
+ onShowUserMessagesChange={setShowUserMessages}
268
+ />
269
+ {transcriptEvents.length > 0 ? (
270
+ <TranscriptView
271
+ assistantModel={modelLabel}
272
+ events={transcriptEvents}
273
+ projectPath={detail.session.worktree}
274
+ showCommentary={showCommentary}
275
+ showExtraEvents={showExtraEvents}
276
+ showRawJson={showRawJson}
277
+ showToolCalls={showToolCalls}
278
+ showUserMessages={showUserMessages}
279
+ />
280
+ ) : (
281
+ <section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
282
+ <h3 className="font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]">
283
+ Transcript
284
+ </h3>
285
+ <p className="mt-4 text-[var(--muted-foreground)] text-sm">
286
+ No renderable Qoder transcript content was found for this session.
287
+ </p>
288
+ </section>
289
+ )}
290
+ </TabsContent>
291
+
292
+ <TabsContent value="metadata">
293
+ <div className="grid gap-4 xl:grid-cols-[0.95fr_1.05fr]">
294
+ <MetadataSection items={buildSessionMetadata(detail)} title="Session metadata" />
295
+ <MetadataSection
296
+ items={buildTranscriptStatsItems(detail, transcriptEvents, transcriptStats)}
297
+ title="Transcript stats"
298
+ />
299
+ </div>
300
+ </TabsContent>
301
+
302
+ <TabsContent value="raw">
303
+ <QoderRawPanels detail={detail} events={transcriptEvents} />
304
+ </TabsContent>
305
+ </Tabs>
306
+
307
+ <ExportDialog
308
+ errorMessage={
309
+ exportSessionMutation.isError
310
+ ? exportSessionMutation.error instanceof Error
311
+ ? exportSessionMutation.error.message
312
+ : 'Export failed'
313
+ : null
314
+ }
315
+ open={pendingExport}
316
+ pending={exportSessionMutation.isPending}
317
+ title={`Export ${detail.session.title}`}
318
+ onExport={(options) => exportSessionMutation.mutate(options)}
319
+ onOpenChange={(open) => {
320
+ setPendingExport(open);
321
+ if (!open) {
322
+ exportSessionMutation.reset();
323
+ }
324
+ }}
325
+ />
326
+ </div>
327
+ );
328
+ };
329
+
330
+ export const Route = createFileRoute('/qoder-sessions/$sessionId')({
331
+ component: QoderSessionDetailPage,
332
+ errorComponent: QoderSessionDetailErrorComponent,
333
+ loader: ({ context, params }) =>
334
+ context.queryClient.ensureQueryData(qoderSessionDetailQueryOptions(params.sessionId)),
335
+ pendingComponent: () => (
336
+ <LoadingPanel
337
+ description="Loading the Qoder transcript, file operations, and session metadata."
338
+ title="Loading session"
339
+ />
340
+ ),
341
+ });
@@ -0,0 +1,139 @@
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, 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 } 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
+ const findWorkspaceOrThrow = (workspaces: QoderWorkspaceGroup[], workspaceKey: string) => {
25
+ const workspace = workspaces.find((candidate) => candidate.key === workspaceKey);
26
+ if (!workspace) {
27
+ throw new Error(`Qoder workspace not found: ${workspaceKey}`);
28
+ }
29
+
30
+ return workspace;
31
+ };
32
+
33
+ const QoderWorkspaceErrorComponent = ({ error }: { error: Error }) => {
34
+ return <ReloadErrorPanel description={error.message} title="Failed to load Qoder workspace" />;
35
+ };
36
+
37
+ const QoderWorkspacePage = () => {
38
+ const params = Route.useParams();
39
+ const workspaces = useSuspenseQuery(qoderWorkspacesQueryOptions()).data;
40
+ const workspace = findWorkspaceOrThrow(workspaces, params.workspaceKey);
41
+ const sessions = useSuspenseQuery(qoderSessionsQueryOptions(workspace.key)).data;
42
+ const [searchInput, setSearchInput] = useState('');
43
+ const [pendingExport, setPendingExport] = useState<QoderSessionSummary | null>(null);
44
+ const deferredSearch = useDeferredValue(searchInput);
45
+
46
+ const exportMutation = useMutation({
47
+ mutationFn: async (options: ExportDialogOptions) => {
48
+ if (!pendingExport) {
49
+ throw new Error('No Qoder session selected for export');
50
+ }
51
+
52
+ const download = await exportQoderSessionFn({
53
+ data: {
54
+ includeCommentary: options.includeCommentary,
55
+ includeMetadata: options.includeMetadata,
56
+ includeTools: options.includeTools,
57
+ outputFormat: options.outputFormat,
58
+ sessionId: pendingExport.sessionId,
59
+ zipArchive: options.zipArchive,
60
+ },
61
+ });
62
+ if (download.mode === 'download') {
63
+ downloadTextFile(download.fileName, download.content, download.mimeType);
64
+ return;
65
+ }
66
+
67
+ await downloadUrlFile(download.fileName, download.downloadUrl);
68
+ },
69
+ onSuccess: () => {
70
+ setPendingExport(null);
71
+ },
72
+ });
73
+
74
+ const visibleSessions = sessions.filter((session) =>
75
+ matchesTextQuery(deferredSearch, [
76
+ session.title,
77
+ session.sessionId,
78
+ session.taskId,
79
+ session.requestId,
80
+ session.model,
81
+ session.status,
82
+ session.executionMode,
83
+ session.agentClass,
84
+ session.query,
85
+ session.sourceStatePath,
86
+ ]),
87
+ );
88
+
89
+ return (
90
+ <div className="space-y-6">
91
+ <PageHeader
92
+ actions={
93
+ <ListSearchInput
94
+ placeholder="Search session title, id, status, or request"
95
+ value={searchInput}
96
+ onValueChange={setSearchInput}
97
+ />
98
+ }
99
+ eyebrow="Qoder workspace"
100
+ subtitle="Inspect local Qoder prompts, session metadata, and file-operation history."
101
+ title={workspace.label}
102
+ />
103
+
104
+ <QoderSessionsTable sessions={visibleSessions} onExportSession={setPendingExport} />
105
+
106
+ <ExportDialog
107
+ open={pendingExport !== null}
108
+ pending={exportMutation.isPending}
109
+ title={pendingExport ? `Export ${pendingExport.title}` : 'Export session'}
110
+ onExport={(options) => exportMutation.mutate(options)}
111
+ onOpenChange={(open) => {
112
+ if (!open) {
113
+ setPendingExport(null);
114
+ exportMutation.reset();
115
+ }
116
+ }}
117
+ />
118
+
119
+ {exportMutation.isError ? (
120
+ <p className="text-[var(--destructive)] text-sm">
121
+ {exportMutation.error instanceof Error ? exportMutation.error.message : 'Session export failed'}
122
+ </p>
123
+ ) : null}
124
+ </div>
125
+ );
126
+ };
127
+
128
+ export const Route = createFileRoute('/qoder/$workspaceKey')({
129
+ component: QoderWorkspacePage,
130
+ errorComponent: QoderWorkspaceErrorComponent,
131
+ loader: async ({ context, params }) => {
132
+ const workspaces = await context.queryClient.ensureQueryData(qoderWorkspacesQueryOptions());
133
+ findWorkspaceOrThrow(workspaces, params.workspaceKey);
134
+ await context.queryClient.ensureQueryData(qoderSessionsQueryOptions(params.workspaceKey));
135
+ },
136
+ pendingComponent: () => (
137
+ <LoadingPanel description="Loading Qoder sessions and local metadata." title="Loading workspace" />
138
+ ),
139
+ });
@@ -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
+ }