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,301 @@
1
+ import type { GrokSessionSummary, GrokWorkspaceGroup } from '@spiracha/lib/grok-exporter-types';
2
+ import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
3
+ import { createFileRoute, useNavigate } from '@tanstack/react-router';
4
+ import { Trash2 } from 'lucide-react';
5
+ import { useDeferredValue, useMemo, useState } from 'react';
6
+ import { DeleteConfirmDialog } from '#/components/delete-confirm-dialog';
7
+ import { ExportDialog } from '#/components/export-dialog';
8
+ import { GrokSessionsTable } from '#/components/grok-sessions-table';
9
+ import { ListSearchInput } from '#/components/list-search-input';
10
+ import { LoadingPanel } from '#/components/loading-panel';
11
+ import { PageHeader } from '#/components/page-header';
12
+ import { ReloadErrorPanel } from '#/components/reload-error-panel';
13
+ import { Button } from '#/components/ui/button';
14
+ import { downloadTextFile, downloadUrlFile } from '#/lib/download';
15
+ import { grokSessionsQueryOptions, grokWorkspacesQueryOptions } from '#/lib/grok-queries';
16
+ import {
17
+ deleteGrokSessionFn,
18
+ deleteGrokSessionsFn,
19
+ exportGrokSessionFn,
20
+ exportGrokSessionsFn,
21
+ } from '#/lib/grok-server';
22
+ import { matchesTextQuery } from '#/lib/text-filter';
23
+ import { isWorkspaceEmptiedByDelete } from '#/lib/workspace-delete-navigation';
24
+
25
+ type ExportDialogOptions = {
26
+ includeCommentary: boolean;
27
+ includeMetadata: boolean;
28
+ includeTools: boolean;
29
+ outputFormat: 'md' | 'txt';
30
+ zipArchive: boolean;
31
+ };
32
+
33
+ type PendingSessionDelete = {
34
+ scope: 'all' | 'selected';
35
+ sessions: GrokSessionSummary[];
36
+ };
37
+
38
+ type PendingSessionExport = {
39
+ label: string;
40
+ sessionIds: string[];
41
+ };
42
+
43
+ const findWorkspaceOrThrow = (workspaces: GrokWorkspaceGroup[], workspaceKey: string) => {
44
+ const workspace = workspaces.find((candidate) => candidate.key === workspaceKey);
45
+ if (!workspace) {
46
+ throw new Error(`Grok workspace not found: ${workspaceKey}`);
47
+ }
48
+
49
+ return workspace;
50
+ };
51
+
52
+ const buildSessionExport = (selectedSessions: GrokSessionSummary[]): PendingSessionExport => ({
53
+ label: selectedSessions.length === 1 ? selectedSessions[0]!.title : `${selectedSessions.length} selected sessions`,
54
+ sessionIds: selectedSessions.map((session) => session.sessionId),
55
+ });
56
+
57
+ const getDeleteConfirmLabel = (pendingDelete: PendingSessionDelete | null, isPending: boolean) => {
58
+ if (isPending) {
59
+ return 'Deleting...';
60
+ }
61
+
62
+ if (pendingDelete?.scope === 'all') {
63
+ return 'Delete all';
64
+ }
65
+
66
+ return pendingDelete && pendingDelete.sessions.length > 1 ? 'Delete sessions' : 'Delete session';
67
+ };
68
+
69
+ const getDeleteDescription = (pendingDelete: PendingSessionDelete | null) => {
70
+ if (!pendingDelete) {
71
+ return 'Permanently delete the selected Grok sessions from local history.';
72
+ }
73
+
74
+ if (pendingDelete.scope === 'all') {
75
+ return `Permanently delete all ${pendingDelete.sessions.length} Grok sessions in this workspace from local history. This removes their session directories and transcript files under ~/.grok/sessions.`;
76
+ }
77
+
78
+ if (pendingDelete.sessions.length === 1) {
79
+ return `Permanently delete "${pendingDelete.sessions[0]!.title}" from Grok history. This removes the session directory and transcript files under ~/.grok/sessions.`;
80
+ }
81
+
82
+ return `Permanently delete ${pendingDelete.sessions.length} selected Grok sessions from local history. This removes their session directories and transcript files under ~/.grok/sessions.`;
83
+ };
84
+
85
+ const getDeleteTitle = (pendingDelete: PendingSessionDelete | null) => {
86
+ if (pendingDelete?.scope === 'all') {
87
+ return `Delete all ${pendingDelete.sessions.length} Grok sessions?`;
88
+ }
89
+
90
+ return pendingDelete && pendingDelete.sessions.length > 1
91
+ ? `Delete ${pendingDelete.sessions.length} Grok sessions?`
92
+ : 'Delete this Grok session?';
93
+ };
94
+
95
+ export const Route = createFileRoute('/grok/$workspaceKey')({
96
+ component: GrokWorkspacePage,
97
+ errorComponent: GrokWorkspaceErrorComponent,
98
+ loader: async ({ context, params }) => {
99
+ const workspaces = await context.queryClient.ensureQueryData(grokWorkspacesQueryOptions());
100
+ findWorkspaceOrThrow(workspaces, params.workspaceKey);
101
+ await context.queryClient.ensureQueryData(grokSessionsQueryOptions(params.workspaceKey));
102
+ },
103
+ pendingComponent: () => <LoadingPanel description="Loading Grok sessions." title="Loading workspace" />,
104
+ });
105
+
106
+ function GrokWorkspaceErrorComponent({ error }: { error: Error }) {
107
+ return <ReloadErrorPanel description={error.message} title="Failed to load Grok workspace" />;
108
+ }
109
+
110
+ function GrokWorkspacePage() {
111
+ const navigate = useNavigate({ from: Route.fullPath });
112
+ const params = Route.useParams();
113
+ const queryClient = useQueryClient();
114
+ const workspaces = useSuspenseQuery(grokWorkspacesQueryOptions()).data;
115
+ const workspace = findWorkspaceOrThrow(workspaces, params.workspaceKey);
116
+ const sessions = useSuspenseQuery(grokSessionsQueryOptions(workspace.key)).data;
117
+ const [searchInput, setSearchInput] = useState('');
118
+ const [pendingDelete, setPendingDelete] = useState<PendingSessionDelete | null>(null);
119
+ const [pendingExport, setPendingExport] = useState<PendingSessionExport | null>(null);
120
+ const deferredSearch = useDeferredValue(searchInput);
121
+
122
+ const exportMutation = useMutation({
123
+ mutationFn: async (options: ExportDialogOptions) => {
124
+ if (!pendingExport) {
125
+ throw new Error('No Grok session selected for export');
126
+ }
127
+
128
+ const download =
129
+ pendingExport.sessionIds.length === 1
130
+ ? await exportGrokSessionFn({
131
+ data: {
132
+ includeCommentary: options.includeCommentary,
133
+ includeMetadata: options.includeMetadata,
134
+ includeTools: options.includeTools,
135
+ outputFormat: options.outputFormat,
136
+ sessionId: pendingExport.sessionIds[0]!,
137
+ zipArchive: options.zipArchive,
138
+ },
139
+ })
140
+ : await exportGrokSessionsFn({
141
+ data: {
142
+ includeCommentary: options.includeCommentary,
143
+ includeMetadata: options.includeMetadata,
144
+ includeTools: options.includeTools,
145
+ outputFormat: options.outputFormat,
146
+ sessionIds: pendingExport.sessionIds,
147
+ zipArchive: options.zipArchive,
148
+ },
149
+ });
150
+ if (download.mode === 'download') {
151
+ downloadTextFile(download.fileName, download.content, download.mimeType);
152
+ return;
153
+ }
154
+
155
+ await downloadUrlFile(download.fileName, download.downloadUrl);
156
+ },
157
+ onSuccess: () => {
158
+ setPendingExport(null);
159
+ },
160
+ });
161
+
162
+ const deleteMutation = useMutation({
163
+ mutationFn: async (sessionIds: string[]) =>
164
+ sessionIds.length === 1
165
+ ? deleteGrokSessionFn({ data: { sessionId: sessionIds[0]! } })
166
+ : deleteGrokSessionsFn({ data: { sessionIds } }),
167
+ onSuccess: async (_result, sessionIds) => {
168
+ const workspaceEmptied = isWorkspaceEmptiedByDelete(sessions, sessionIds, (session) => session.sessionId);
169
+ setPendingDelete(null);
170
+ if (workspaceEmptied) {
171
+ await navigate({ to: '/grok' });
172
+ }
173
+
174
+ await Promise.all([
175
+ queryClient.invalidateQueries({ queryKey: ['grok-workspaces'] }),
176
+ queryClient.invalidateQueries({ queryKey: ['grok-sessions', workspace.key] }),
177
+ ...sessionIds.map((sessionId) =>
178
+ queryClient.invalidateQueries({ queryKey: ['grok-session', sessionId] }),
179
+ ),
180
+ ]);
181
+ },
182
+ });
183
+
184
+ const visibleSessions = useMemo(
185
+ () =>
186
+ sessions.filter((session) =>
187
+ matchesTextQuery(deferredSearch, [
188
+ session.title,
189
+ session.sessionId,
190
+ session.agentName,
191
+ session.currentModelId,
192
+ session.modelLabel,
193
+ session.gitBranch,
194
+ ]),
195
+ ),
196
+ [deferredSearch, sessions],
197
+ );
198
+ const visibleSessionsById = useMemo(
199
+ () => new Map(visibleSessions.map((session) => [session.sessionId, session])),
200
+ [visibleSessions],
201
+ );
202
+ const lookupSelectedSessions = (sessionIds: string[]) =>
203
+ sessionIds
204
+ .map((sessionId) => visibleSessionsById.get(sessionId) ?? null)
205
+ .filter((session): session is GrokSessionSummary => session !== null);
206
+ const openExportForSessions = (selectedSessions: GrokSessionSummary[]) => {
207
+ if (selectedSessions.length === 0) {
208
+ return;
209
+ }
210
+
211
+ setPendingExport(buildSessionExport(selectedSessions));
212
+ };
213
+ const openDeleteForSessions = (selectedSessions: GrokSessionSummary[], scope: PendingSessionDelete['scope']) => {
214
+ if (selectedSessions.length > 0) {
215
+ setPendingDelete({ scope, sessions: selectedSessions });
216
+ }
217
+ };
218
+
219
+ return (
220
+ <div className="space-y-6">
221
+ <PageHeader
222
+ actions={
223
+ <div className="flex flex-col gap-2 sm:flex-row">
224
+ <Button
225
+ className="rounded-full"
226
+ disabled={deleteMutation.isPending || sessions.length === 0}
227
+ type="button"
228
+ variant="destructive"
229
+ onClick={() => openDeleteForSessions(sessions, 'all')}
230
+ >
231
+ <Trash2 className="size-4" />
232
+ Delete all
233
+ </Button>
234
+ <ListSearchInput
235
+ placeholder="Search session title, id, model, or branch"
236
+ value={searchInput}
237
+ onValueChange={setSearchInput}
238
+ />
239
+ </div>
240
+ }
241
+ eyebrow="Grok workspace"
242
+ subtitle="Inspect local Grok CLI sessions, reasoning summaries, tool calls, and exportable conversation text."
243
+ title={workspace.label}
244
+ />
245
+
246
+ <GrokSessionsTable
247
+ sessions={visibleSessions}
248
+ onDeleteSession={(session) => openDeleteForSessions([session], 'selected')}
249
+ onDeleteSessions={(sessionIds) => openDeleteForSessions(lookupSelectedSessions(sessionIds), 'selected')}
250
+ onExportSession={(session) => openExportForSessions([session])}
251
+ onExportSessions={(sessionIds) => openExportForSessions(lookupSelectedSessions(sessionIds))}
252
+ />
253
+
254
+ <ExportDialog
255
+ errorMessage={
256
+ exportMutation.isError
257
+ ? exportMutation.error instanceof Error
258
+ ? exportMutation.error.message
259
+ : 'Session export failed'
260
+ : null
261
+ }
262
+ forceZipArchive={pendingExport ? pendingExport.sessionIds.length > 1 : false}
263
+ open={pendingExport !== null}
264
+ pending={exportMutation.isPending}
265
+ title={pendingExport ? `Export ${pendingExport.label}` : 'Export session'}
266
+ onExport={(options) => exportMutation.mutate(options)}
267
+ onOpenChange={(open) => {
268
+ if (!open) {
269
+ setPendingExport(null);
270
+ exportMutation.reset();
271
+ }
272
+ }}
273
+ />
274
+
275
+ <DeleteConfirmDialog
276
+ confirmLabel={getDeleteConfirmLabel(pendingDelete, deleteMutation.isPending)}
277
+ description={getDeleteDescription(pendingDelete)}
278
+ errorMessage={
279
+ deleteMutation.isError
280
+ ? deleteMutation.error instanceof Error
281
+ ? deleteMutation.error.message
282
+ : 'Session delete failed'
283
+ : null
284
+ }
285
+ open={pendingDelete !== null}
286
+ title={getDeleteTitle(pendingDelete)}
287
+ onConfirm={() => {
288
+ if (pendingDelete) {
289
+ deleteMutation.mutate(pendingDelete.sessions.map((session) => session.sessionId));
290
+ }
291
+ }}
292
+ onOpenChange={(open) => {
293
+ if (!open) {
294
+ setPendingDelete(null);
295
+ deleteMutation.reset();
296
+ }
297
+ }}
298
+ />
299
+ </div>
300
+ );
301
+ }
@@ -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 { GrokWorkspacesTable } from '#/components/grok-workspaces-table';
5
+ import { ListSearchInput } from '#/components/list-search-input';
6
+ import { PageHeader } from '#/components/page-header';
7
+ import { ReloadErrorPanel } from '#/components/reload-error-panel';
8
+ import { grokWorkspacesQueryOptions } from '#/lib/grok-queries';
9
+ import { matchesTextQuery } from '#/lib/text-filter';
10
+
11
+ const GrokErrorComponent = ({ error }: { error: Error }) => {
12
+ return <ReloadErrorPanel description={error.message} title="Failed to load Grok workspaces" />;
13
+ };
14
+
15
+ const GrokPage = () => {
16
+ const workspaces = useSuspenseQuery(grokWorkspacesQueryOptions()).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 Grok CLI session directories under ~/.grok/sessions."
36
+ title="Grok"
37
+ />
38
+
39
+ <GrokWorkspacesTable workspaces={visibleWorkspaces} />
40
+ </div>
41
+ );
42
+ };
43
+
44
+ export const Route = createFileRoute('/grok/')({
45
+ component: GrokPage,
46
+ errorComponent: GrokErrorComponent,
47
+ loader: ({ context }) => context.queryClient.ensureQueryData(grokWorkspacesQueryOptions()),
48
+ });
@@ -0,0 +1,124 @@
1
+ import { useSuspenseQuery } from '@tanstack/react-query';
2
+ import { createFileRoute, Link } from '@tanstack/react-router';
3
+ import { MetricCard } from '#/components/metric-card';
4
+ import { PageHeader } from '#/components/page-header';
5
+ import { RecentThreadsList } from '#/components/recent-threads-list';
6
+ import { dashboardQueryOptions } from '#/lib/codex-queries';
7
+ import { formatNumber, formatTokens } from '#/lib/formatters';
8
+
9
+ export const Route = createFileRoute('/')({
10
+ component: DashboardPage,
11
+ loader: ({ context }) => context.queryClient.ensureQueryData(dashboardQueryOptions()),
12
+ });
13
+
14
+ function DashboardErrorComponent({ error }: { error: Error }) {
15
+ const isSqlite = error.message.includes('unable to open database') || error.message.includes('database is locked');
16
+ return (
17
+ <div className="rounded-xl border border-[var(--border)] bg-[var(--panel)] px-6 py-10 text-center">
18
+ <p className="font-medium text-[var(--destructive)] text-sm">
19
+ {isSqlite ? 'Database unavailable' : 'Failed to load dashboard'}
20
+ </p>
21
+ <p className="mt-2 text-[var(--muted-foreground)] text-sm">
22
+ {isSqlite ? 'Codex may have an exclusive lock on the database. Reload to retry.' : error.message}
23
+ </p>
24
+ <button
25
+ className="mt-4 text-[var(--accent)] text-sm underline-offset-2 hover:underline"
26
+ type="button"
27
+ onClick={() => window.location.reload()}
28
+ >
29
+ Reload
30
+ </button>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ Route.update({ errorComponent: DashboardErrorComponent });
36
+
37
+ function DashboardPage() {
38
+ const dashboard = useSuspenseQuery(dashboardQueryOptions()).data;
39
+
40
+ return (
41
+ <div className="space-y-6">
42
+ <PageHeader
43
+ actions={
44
+ <img
45
+ alt="Spiracha icon"
46
+ className="size-16 shrink-0 rounded-2xl border border-[var(--border)] bg-white/95 p-2 shadow-[var(--panel-shadow)]"
47
+ src="/icon.svg"
48
+ />
49
+ }
50
+ eyebrow="Overview"
51
+ subtitle="A compact readout of local Codex activity, project distribution, and recent thread movement."
52
+ title="Dashboard"
53
+ />
54
+
55
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
56
+ <MetricCard label="Threads" value={formatNumber(dashboard.totalThreads)} />
57
+ <MetricCard label="Projects" value={formatNumber(dashboard.totalProjects)} />
58
+ <MetricCard label="Tokens" value={formatTokens(dashboard.totalTokens)} />
59
+ <MetricCard
60
+ helper={`${formatNumber(dashboard.archivedThreads)} archived`}
61
+ label="Active"
62
+ value={formatNumber(dashboard.activeThreads)}
63
+ />
64
+ </div>
65
+
66
+ <div className="grid min-w-0 gap-4 xl:grid-cols-[1.4fr_1fr]">
67
+ <section className="min-w-0 overflow-hidden rounded-[1.8rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
68
+ <div className="flex items-center justify-between gap-3">
69
+ <div className="min-w-0">
70
+ <p className="font-semibold text-sm">Recent threads</p>
71
+ <p className="mt-0.5 text-[var(--muted-foreground)] text-xs">
72
+ Most recently updated threads across the local Codex database.
73
+ </p>
74
+ </div>
75
+ <Link className="shrink-0 font-medium text-[var(--accent)] text-sm" to="/codex">
76
+ View Codex
77
+ </Link>
78
+ </div>
79
+ <RecentThreadsList threads={dashboard.recentThreads} />
80
+ </section>
81
+
82
+ <div className="grid min-w-0 gap-4">
83
+ <section className="min-w-0 overflow-hidden rounded-[1.8rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
84
+ <p className="font-semibold text-sm">Top projects by thread count</p>
85
+ <div className="mt-3 space-y-2">
86
+ {dashboard.topProjectsByThreadCount.map((project) => (
87
+ <Link
88
+ key={project.name}
89
+ className="flex items-center justify-between gap-3 rounded-lg border border-[var(--border)] bg-[var(--panel-secondary)] px-3.5 py-2.5 text-sm"
90
+ params={{ project: project.name }}
91
+ to="/codex/$project"
92
+ >
93
+ <span className="truncate">{project.name}</span>
94
+ <span className="shrink-0 font-mono text-[var(--muted-foreground)]">
95
+ {formatNumber(project.threadCount)}
96
+ </span>
97
+ </Link>
98
+ ))}
99
+ </div>
100
+ </section>
101
+
102
+ <section className="min-w-0 overflow-hidden rounded-[1.8rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
103
+ <p className="font-semibold text-sm">Top projects by tokens</p>
104
+ <div className="mt-3 space-y-2">
105
+ {dashboard.topProjectsByTokens.map((project) => (
106
+ <Link
107
+ key={project.name}
108
+ className="flex items-center justify-between gap-3 rounded-lg border border-[var(--border)] bg-[var(--panel-secondary)] px-3.5 py-2.5 text-sm"
109
+ params={{ project: project.name }}
110
+ to="/codex/$project"
111
+ >
112
+ <span className="truncate">{project.name}</span>
113
+ <span className="shrink-0 font-mono text-[var(--muted-foreground)]">
114
+ {formatTokens(project.totalTokens)}
115
+ </span>
116
+ </Link>
117
+ ))}
118
+ </div>
119
+ </section>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ );
124
+ }