spiracha 1.4.0 → 2.0.0

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