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
@@ -1,830 +0,0 @@
1
- import { n as asObject, r as asString, t as asNumber } from "./shared-CPRNYIql.js";
2
- import { getCursorGlobalDbPath, getCursorProjectsDir, getCursorWorkspaceStorageDir, resolveCursorUserDir } from "./cursor-exporter-types-CI3goo-c.js";
3
- import { Database, constants } from "bun:sqlite";
4
- import { readdir, stat } from "node:fs/promises";
5
- import path from "node:path";
6
- import { createHash } from "node:crypto";
7
- //#region ../../src/lib/cursor-db.ts
8
- var CURSOR_READONLY_DB_OPEN_FLAGS = constants.SQLITE_OPEN_READONLY | constants.SQLITE_OPEN_URI;
9
- var getCursorReadonlyDbUri = (dbPath) => {
10
- const normalizedPath = dbPath.replace(/\\/gu, "/");
11
- return `file://${(normalizedPath.startsWith("/") ? normalizedPath : `/${normalizedPath}`).split("/").map((segment) => /^[A-Za-z]:$/u.test(segment) ? segment : encodeURIComponent(segment)).join("/")}?immutable=1`;
12
- };
13
- var openCursorReadonlyDb = (dbPath) => {
14
- return new Database(getCursorReadonlyDbUri(dbPath), CURSOR_READONLY_DB_OPEN_FLAGS);
15
- };
16
- var pathExists = async (target) => {
17
- try {
18
- await stat(target);
19
- return true;
20
- } catch {
21
- return false;
22
- }
23
- };
24
- var isMissingOrUnreadableCursorStoreError = (error) => {
25
- const code = error.code;
26
- return code === "ENOENT" || code === "ENOTDIR" || code === "SQLITE_CANTOPEN";
27
- };
28
- var readItemValue = (db, key) => {
29
- const row = db.query("SELECT value FROM ItemTable WHERE key = ?").get(key);
30
- if (!row?.value) return null;
31
- try {
32
- return JSON.parse(row.value);
33
- } catch {
34
- return null;
35
- }
36
- };
37
- var readKvValue = (db, key) => {
38
- const row = db.query("SELECT value FROM cursorDiskKV WHERE key = ?").get(key);
39
- if (!row?.value) return null;
40
- try {
41
- return JSON.parse(row.value);
42
- } catch {
43
- return null;
44
- }
45
- };
46
- var decodeCursorUri = (uri) => {
47
- if (!uri) return "";
48
- if (uri.startsWith("file://")) return decodeURIComponent(uri.slice(7));
49
- return uri;
50
- };
51
- var normalizeCursorPath = (value) => {
52
- const decoded = decodeCursorUri(value.trim());
53
- if (!decoded) return "";
54
- return decoded.replace(/\/+$/u, "") || decoded;
55
- };
56
- var warnCursorDataIssue = (event, details) => {
57
- console.warn(`[spiracha:cursor] ${event}`, details);
58
- };
59
- var stripJsonComments = (value) => {
60
- return value.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/(^|\s)\/\/.*$/gmu, "$1");
61
- };
62
- var parseCodeWorkspaceJson = (text) => {
63
- try {
64
- return JSON.parse(text);
65
- } catch {
66
- return JSON.parse(stripJsonComments(text));
67
- }
68
- };
69
- var parseCodeWorkspaceFolders = async (workspaceFilePath) => {
70
- if (!workspaceFilePath.endsWith(".code-workspace")) return [];
71
- try {
72
- const data = parseCodeWorkspaceJson(await Bun.file(workspaceFilePath).text());
73
- const folders = [];
74
- for (const entry of data.folders ?? []) {
75
- const folderPath = entry.path;
76
- if (!folderPath) continue;
77
- folders.push(folderPath.startsWith("/") ? normalizeCursorPath(folderPath) : normalizeCursorPath(path.join(path.dirname(workspaceFilePath), folderPath)));
78
- }
79
- return folders;
80
- } catch (error) {
81
- warnCursorDataIssue("invalid_code_workspace_json", {
82
- error: error instanceof Error ? error.message : String(error),
83
- workspaceFilePath
84
- });
85
- return [];
86
- }
87
- };
88
- var loadGlobalComposerHeaders = (globalDbPath) => {
89
- try {
90
- const db = openCursorReadonlyDb(globalDbPath);
91
- try {
92
- return readItemValue(db, "composer.composerHeaders")?.allComposers ?? [];
93
- } finally {
94
- db.close();
95
- }
96
- } catch (error) {
97
- warnCursorDataIssue("global_composer_headers_unavailable", {
98
- error: error instanceof Error ? error.message : String(error),
99
- globalDbPath
100
- });
101
- return [];
102
- }
103
- };
104
- var readBucketWorkspaceJson = async (workspaceJsonPath) => {
105
- try {
106
- return await Bun.file(workspaceJsonPath).json();
107
- } catch {
108
- return null;
109
- }
110
- };
111
- var resolveBucketIdentity = async (wsData, bucketId) => {
112
- if (wsData.folder) {
113
- const folder = normalizeCursorPath(wsData.folder);
114
- return {
115
- folders: folder ? [folder] : [],
116
- kind: "folder",
117
- label: folder ? path.basename(folder) : bucketId,
118
- uri: wsData.folder
119
- };
120
- }
121
- if (wsData.workspace) {
122
- const workspacePath = normalizeCursorPath(wsData.workspace);
123
- return {
124
- folders: workspacePath ? await parseCodeWorkspaceFolders(workspacePath) : [],
125
- kind: "workspace",
126
- label: workspacePath ? path.basename(workspacePath) : bucketId,
127
- uri: wsData.workspace
128
- };
129
- }
130
- return {
131
- folders: [],
132
- kind: "unknown",
133
- label: bucketId,
134
- uri: ""
135
- };
136
- };
137
- var readBucketComposerIds = (dbPath) => {
138
- try {
139
- const db = openCursorReadonlyDb(dbPath);
140
- try {
141
- return (readItemValue(db, "composer.composerData")?.allComposers ?? []).map((entry) => entry.composerId).filter((value) => Boolean(value));
142
- } finally {
143
- db.close();
144
- }
145
- } catch {
146
- return [];
147
- }
148
- };
149
- var loadCursorBuckets = async (userDir = resolveCursorUserDir()) => {
150
- const workspaceStorageDir = getCursorWorkspaceStorageDir(userDir);
151
- let bucketIds = [];
152
- try {
153
- bucketIds = await readdir(workspaceStorageDir);
154
- } catch {
155
- return [];
156
- }
157
- const globalDbPath = getCursorGlobalDbPath(userDir);
158
- const headerIdsByBucket = /* @__PURE__ */ new Map();
159
- if (await pathExists(globalDbPath)) for (const header of loadGlobalComposerHeaders(globalDbPath)) {
160
- const id = header.workspaceIdentifier?.id;
161
- if (id && header.composerId) {
162
- const set = headerIdsByBucket.get(id) ?? /* @__PURE__ */ new Set();
163
- set.add(header.composerId);
164
- headerIdsByBucket.set(id, set);
165
- }
166
- }
167
- const buckets = [];
168
- for (const bucketId of bucketIds) {
169
- const bucket = await buildBucket(workspaceStorageDir, bucketId, headerIdsByBucket);
170
- if (bucket) buckets.push(bucket);
171
- }
172
- return buckets;
173
- };
174
- var buildBucket = async (workspaceStorageDir, bucketId, headerIdsByBucket) => {
175
- const root = path.join(workspaceStorageDir, bucketId);
176
- const workspaceJsonPath = path.join(root, "workspace.json");
177
- const dbPath = path.join(root, "state.vscdb");
178
- if (!await pathExists(workspaceJsonPath) || !await pathExists(dbPath)) return null;
179
- const wsData = await readBucketWorkspaceJson(workspaceJsonPath);
180
- if (!wsData || !wsData.folder && !wsData.workspace) return null;
181
- let identity;
182
- let dbStat;
183
- let composerIds;
184
- try {
185
- identity = await resolveBucketIdentity(wsData, bucketId);
186
- dbStat = await stat(dbPath);
187
- composerIds = readBucketComposerIds(dbPath);
188
- } catch (error) {
189
- if (isMissingOrUnreadableCursorStoreError(error)) return null;
190
- throw error;
191
- }
192
- const headerIds = headerIdsByBucket.get(bucketId) ?? /* @__PURE__ */ new Set();
193
- const threadComposerIds = [...new Set([...composerIds, ...headerIds])];
194
- return {
195
- bucketId,
196
- composerCount: composerIds.length,
197
- dbPath,
198
- dbSizeBytes: dbStat.size,
199
- folders: identity.folders,
200
- globalHeaderCount: headerIds.size,
201
- kind: identity.kind,
202
- label: identity.label,
203
- mtimeMs: dbStat.mtimeMs,
204
- threadComposerIds,
205
- uri: identity.uri,
206
- workspaceJsonPath
207
- };
208
- };
209
- var getCursorWorkspaceGroupKey = (bucket) => {
210
- if (bucket.kind === "folder" && bucket.folders[0]) return `folder:${bucket.folders[0]}`;
211
- if (bucket.kind === "workspace") return `workspace:${normalizeCursorPath(bucket.uri)}`;
212
- return `unknown:${bucket.bucketId}`;
213
- };
214
- var groupCursorBuckets = (buckets) => {
215
- const grouped = /* @__PURE__ */ new Map();
216
- for (const bucket of buckets) {
217
- const key = getCursorWorkspaceGroupKey(bucket);
218
- const list = grouped.get(key) ?? [];
219
- list.push(bucket);
220
- grouped.set(key, list);
221
- }
222
- const groups = [];
223
- for (const [key, list] of grouped.entries()) {
224
- const ranked = [...list].sort((a, b) => b.mtimeMs - a.mtimeMs || b.dbSizeBytes - a.dbSizeBytes);
225
- const primary = ranked[0];
226
- const newest = ranked[0];
227
- const threadCount = new Set(ranked.flatMap((bucket) => bucket.threadComposerIds)).size;
228
- const olderWithData = ranked.slice(1).some((bucket) => bucket.composerCount > 0 || bucket.globalHeaderCount > 0);
229
- groups.push({
230
- buckets: ranked,
231
- folders: primary.folders,
232
- key,
233
- kind: primary.kind,
234
- label: primary.label,
235
- lastActiveMs: Math.max(...ranked.map((bucket) => bucket.mtimeMs)),
236
- needsRecovery: ranked.length > 1 && olderWithData && newest.composerCount === 0,
237
- threadCount,
238
- uri: primary.uri
239
- });
240
- }
241
- return groups.sort((a, b) => b.lastActiveMs - a.lastActiveMs);
242
- };
243
- var listCursorWorkspaceGroups = async (userDir = resolveCursorUserDir()) => {
244
- return (await discoverCursorWorkspaces(userDir)).groups;
245
- };
246
- var countBubbles = (db, composerId) => {
247
- const row = db.query("SELECT COUNT(*) AS count, COALESCE(SUM(length(value)), 0) AS bytes FROM cursorDiskKV WHERE key LIKE ?").get(`bubbleId:${composerId}:%`);
248
- return {
249
- bytes: row.bytes,
250
- count: row.count
251
- };
252
- };
253
- var findCursorTranscriptDirs = async (composerId, userDir = resolveCursorUserDir()) => {
254
- const projectsDir = getCursorProjectsDir(userDir);
255
- if (!await pathExists(projectsDir)) return [];
256
- const matches = [];
257
- let projectDirs = [];
258
- try {
259
- projectDirs = await readdir(projectsDir);
260
- } catch {
261
- return [];
262
- }
263
- for (const projectDir of projectDirs) {
264
- const transcriptDir = path.join(projectsDir, projectDir, "agent-transcripts", composerId);
265
- if (await pathExists(transcriptDir)) matches.push(transcriptDir);
266
- }
267
- return matches;
268
- };
269
- var listCursorThreadsForGroup = async (group, userDir = resolveCursorUserDir(), options = {}) => {
270
- const threads = (await discoverCursorWorkspaces(userDir)).threadsByKey.get(group.key) ?? [];
271
- if (options.includeTranscriptDirs === false) return threads;
272
- return Promise.all(threads.map(async (thread) => ({
273
- ...thread,
274
- transcriptDirs: await findCursorTranscriptDirs(thread.composerId, userDir)
275
- })));
276
- };
277
- var DISCOVERY_TTL_MS = 6e4;
278
- var UNKNOWN_GROUP_KEY = "unknown";
279
- var discoveryCache = null;
280
- var invalidateCursorDiscoveryCache = () => {
281
- discoveryCache = null;
282
- };
283
- var DEV_CONTAINER_DIRS = [
284
- "workspace",
285
- "projects",
286
- "dev",
287
- "code",
288
- "repos",
289
- "src",
290
- "Documents",
291
- "Downloads",
292
- "Desktop"
293
- ];
294
- var REVERSE_WORKSPACE_ROOT_RE = /^\/Users\/[^/]+\/workspace\/reverse\/[^/]+/u;
295
- var CONTAINER_ROOT_RE = new RegExp(`^(/Users/[^/]+/(?:${DEV_CONTAINER_DIRS.join("|")})/[^/]+)`);
296
- var ABS_PATH_RE = /\/Users\/[^"'\s:,)\]]+/g;
297
- var isNoisePath = (value) => /\/Library(?:\/|$)|\/\.cursor(?:\/|$)|\/node_modules\/|\/\.git\/|^\/tmp|^\/var|^\/private|\/\.Trash\//u.test(value) || /^\/Users\/[^/]+\/(?:Downloads|Desktop)$/u.test(value);
298
- var stripLikelyFileName = (value) => {
299
- return path.basename(value).includes(".") ? path.dirname(value) : value;
300
- };
301
- var containerRootFromPath = (value) => {
302
- const candidate = stripLikelyFileName(normalizeCursorPath(value));
303
- const reverseMatch = candidate.match(REVERSE_WORKSPACE_ROOT_RE);
304
- if (reverseMatch) return reverseMatch[0] ?? null;
305
- const match = candidate.match(CONTAINER_ROOT_RE);
306
- if (match) return match[1] ?? null;
307
- const parts = candidate.split("/");
308
- if (parts.length >= 4 && parts[1] === "Users") return `/${parts.slice(1, 4).join("/")}`;
309
- return null;
310
- };
311
- var inferFolderFromPaths = (paths) => {
312
- const counts = /* @__PURE__ */ new Map();
313
- for (const value of paths) {
314
- if (isNoisePath(value)) continue;
315
- const root = containerRootFromPath(value);
316
- if (root) counts.set(root, (counts.get(root) ?? 0) + 1);
317
- }
318
- let best = null;
319
- let bestCount = 0;
320
- for (const [root, count] of counts) if (count > bestCount) {
321
- best = root;
322
- bestCount = count;
323
- }
324
- return best;
325
- };
326
- var inferFolderFromBlob = (blob) => {
327
- const matches = blob.match(ABS_PATH_RE);
328
- return matches ? inferFolderFromPaths(matches) : null;
329
- };
330
- var readCursorFileHistoryProjectActivity = async (userDir) => {
331
- const historyDir = path.join(userDir, "History");
332
- let entries = [];
333
- try {
334
- entries = await readdir(historyDir, { withFileTypes: true });
335
- } catch {
336
- return /* @__PURE__ */ new Map();
337
- }
338
- const activity = /* @__PURE__ */ new Map();
339
- for (const entry of entries) {
340
- if (!entry.isDirectory()) continue;
341
- const entriesPath = path.join(historyDir, entry.name, "entries.json");
342
- let data;
343
- try {
344
- data = await Bun.file(entriesPath).json();
345
- } catch {
346
- continue;
347
- }
348
- const folder = containerRootFromPath(typeof data.resource === "string" ? data.resource : "");
349
- if (!folder || isNoisePath(folder)) continue;
350
- const lastActiveMs = Math.max(0, ...(data.entries ?? []).map((item) => item.timestamp ?? 0));
351
- activity.set(folder, Math.max(activity.get(folder) ?? 0, lastActiveMs));
352
- }
353
- return activity;
354
- };
355
- var inferFolderFromBubbles = (db, composerId) => {
356
- const rows = db.query("SELECT value FROM cursorDiskKV WHERE key LIKE ? LIMIT 80").all(`bubbleId:${composerId}:%`);
357
- const paths = [];
358
- for (const { value } of rows) {
359
- let bubble;
360
- try {
361
- bubble = JSON.parse(value);
362
- } catch {
363
- continue;
364
- }
365
- const tool = asObject(bubble.toolFormerData ?? null);
366
- if (!tool) continue;
367
- const matches = `${asString(tool.rawArgs ?? null) ?? ""} ${asString(tool.params ?? null) ?? ""}`.match(ABS_PATH_RE);
368
- if (matches) paths.push(...matches);
369
- if (paths.length > 200) break;
370
- }
371
- return inferFolderFromPaths(paths);
372
- };
373
- var readAllHeads = (db) => {
374
- const rows = db.query(`SELECT substr(key, 14) AS id, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'`).all();
375
- return new Map(rows.map((row) => [row.id, parseGlobalHead(row.value)]));
376
- };
377
- var parseGlobalHead = (value) => {
378
- let parsed = {};
379
- try {
380
- parsed = JSON.parse(value);
381
- } catch {
382
- return {
383
- createdAtMs: null,
384
- lastUpdatedAtMs: null,
385
- mode: null,
386
- name: null,
387
- pathHint: inferFolderFromBlob(value)
388
- };
389
- }
390
- return {
391
- createdAtMs: asNumber(parsed.createdAt ?? null),
392
- lastUpdatedAtMs: asNumber(parsed.lastUpdatedAt ?? null),
393
- mode: asString(parsed.unifiedMode ?? null),
394
- name: asString(parsed.name ?? null),
395
- pathHint: inferFolderFromBlob(value)
396
- };
397
- };
398
- var readBubbleStats = (db) => {
399
- const rows = db.query(`SELECT substr(key, 10, instr(substr(key, 10), ':') - 1) AS id,
400
- COUNT(*) AS count,
401
- COALESCE(SUM(length(value)), 0) AS bytes
402
- FROM cursorDiskKV WHERE key GLOB 'bubbleId:*:*' GROUP BY id`).all();
403
- return new Map(rows.map((row) => [row.id, {
404
- bytes: row.bytes,
405
- count: row.count
406
- }]));
407
- };
408
- var readHeaderInfo = (globalDbPath) => {
409
- const info = /* @__PURE__ */ new Map();
410
- for (const header of loadGlobalComposerHeaders(globalDbPath)) {
411
- if (!header.composerId) continue;
412
- const identifier = header.workspaceIdentifier;
413
- const uriPath = identifier?.uri?.path ?? identifier?.uri?.fsPath ?? null;
414
- info.set(header.composerId, {
415
- bucketId: identifier?.id ?? null,
416
- name: typeof header.name === "string" ? header.name : null,
417
- uriPath: uriPath ? normalizeCursorPath(uriPath) : null
418
- });
419
- }
420
- return info;
421
- };
422
- var collectBucketComposerIds = (buckets) => {
423
- const map = /* @__PURE__ */ new Map();
424
- for (const bucket of buckets) for (const composerId of readBucketComposerIds(bucket.dbPath)) if (!map.has(composerId)) map.set(composerId, bucket.bucketId);
425
- return map;
426
- };
427
- var findLinkedBucketId = (composerId, headerInfo, bucketIdToGroupKey, bucketComposerIds) => {
428
- if (headerInfo?.bucketId && bucketIdToGroupKey.has(headerInfo.bucketId)) return headerInfo.bucketId;
429
- return bucketComposerIds.get(composerId) ?? null;
430
- };
431
- var resolveThreadFolderHint = (composerId, head, headerInfo, stat, linkedBucketId, bucketIdToGroupKey, bucketIdToFolder, db) => {
432
- if (linkedBucketId && bucketIdToGroupKey.has(linkedBucketId)) return {
433
- folder: bucketIdToFolder.get(linkedBucketId) ?? null,
434
- groupKey: bucketIdToGroupKey.get(linkedBucketId)
435
- };
436
- if (headerInfo?.uriPath) return {
437
- folder: headerInfo.uriPath,
438
- groupKey: `folder:${headerInfo.uriPath}`
439
- };
440
- if (head?.pathHint) return {
441
- folder: head.pathHint,
442
- groupKey: `folder:${head.pathHint}`
443
- };
444
- if (stat.count > 0) {
445
- const folder = inferFolderFromBubbles(db, composerId);
446
- return {
447
- folder,
448
- groupKey: folder ? `folder:${folder}` : UNKNOWN_GROUP_KEY
449
- };
450
- }
451
- return {
452
- folder: null,
453
- groupKey: UNKNOWN_GROUP_KEY
454
- };
455
- };
456
- var resolveThreadFolder = (composerId, head, headerInfo, stat, bucketIdToGroupKey, bucketIdToFolder, bucketComposerIds, db) => {
457
- const linkedBucketId = findLinkedBucketId(composerId, headerInfo, bucketIdToGroupKey, bucketComposerIds);
458
- const { folder, groupKey } = resolveThreadFolderHint(composerId, head, headerInfo, stat, linkedBucketId, bucketIdToGroupKey, bucketIdToFolder, db);
459
- return {
460
- bucketId: linkedBucketId,
461
- composerId,
462
- createdAtMs: head?.createdAtMs ?? null,
463
- folder,
464
- groupKey,
465
- groupLabel: folder ? path.basename(folder) : "Unknown project",
466
- lastUpdatedAtMs: head?.lastUpdatedAtMs ?? null,
467
- mode: head?.mode ?? null,
468
- name: head?.name || headerInfo?.name || "(untitled)",
469
- stat
470
- };
471
- };
472
- var toThreadSummary = (resolved) => ({
473
- bubbleBytes: resolved.stat.bytes,
474
- bubbleCount: resolved.stat.count,
475
- bucketId: resolved.bucketId,
476
- composerId: resolved.composerId,
477
- createdAtMs: resolved.createdAtMs,
478
- lastUpdatedAtMs: resolved.lastUpdatedAtMs,
479
- mode: resolved.mode,
480
- name: resolved.name,
481
- transcriptDirs: [],
482
- workspaceKey: resolved.groupKey,
483
- workspaceLabel: resolved.groupLabel
484
- });
485
- var assembleDiscovery = (resolved, bucketGroups, fileHistoryActivity) => {
486
- const threadsByKey = /* @__PURE__ */ new Map();
487
- const lastActiveByKey = /* @__PURE__ */ new Map();
488
- for (const thread of resolved) {
489
- if (thread.groupKey === UNKNOWN_GROUP_KEY && thread.stat.count === 0) continue;
490
- const list = threadsByKey.get(thread.groupKey) ?? [];
491
- list.push(toThreadSummary(thread));
492
- threadsByKey.set(thread.groupKey, list);
493
- lastActiveByKey.set(thread.groupKey, Math.max(lastActiveByKey.get(thread.groupKey) ?? 0, thread.lastUpdatedAtMs ?? 0));
494
- }
495
- for (const [folder, lastActiveMs] of fileHistoryActivity) {
496
- const key = `folder:${folder}`;
497
- lastActiveByKey.set(key, Math.max(lastActiveByKey.get(key) ?? 0, lastActiveMs));
498
- }
499
- for (const list of threadsByKey.values()) list.sort((a, b) => (b.lastUpdatedAtMs ?? 0) - (a.lastUpdatedAtMs ?? 0));
500
- return {
501
- groups: buildDiscoveryGroups(threadsByKey, bucketGroups, lastActiveByKey),
502
- threadsByKey
503
- };
504
- };
505
- var mergeBucketGroup = (bucketGroup, threadsByKey, lastActiveByKey) => {
506
- const threads = threadsByKey.get(bucketGroup.key) ?? [];
507
- return {
508
- ...bucketGroup,
509
- lastActiveMs: Math.max(bucketGroup.lastActiveMs, lastActiveByKey.get(bucketGroup.key) ?? 0),
510
- threadCount: threads.length || bucketGroup.threadCount
511
- };
512
- };
513
- var buildBucketlessGroup = (key, threadCount, lastActiveMs) => {
514
- const isUnknown = key === UNKNOWN_GROUP_KEY;
515
- const folder = isUnknown ? "" : key.slice(7);
516
- return {
517
- buckets: [],
518
- folders: folder ? [folder] : [],
519
- key,
520
- kind: isUnknown ? "unknown" : "folder",
521
- label: isUnknown ? "Unknown project" : path.basename(folder),
522
- lastActiveMs,
523
- needsRecovery: false,
524
- threadCount,
525
- uri: folder ? `file://${folder}` : ""
526
- };
527
- };
528
- var buildDiscoveryGroups = (threadsByKey, bucketGroups, lastActiveByKey) => {
529
- const seen = new Set(bucketGroups.map((group) => group.key));
530
- const groups = bucketGroups.map((group) => mergeBucketGroup(group, threadsByKey, lastActiveByKey));
531
- const keys = new Set([...threadsByKey.keys(), ...lastActiveByKey.keys()]);
532
- for (const key of keys) {
533
- const threads = threadsByKey.get(key) ?? [];
534
- if (!seen.has(key) && (threads.length > 0 || key !== UNKNOWN_GROUP_KEY)) {
535
- groups.push(buildBucketlessGroup(key, threads.length, lastActiveByKey.get(key) ?? 0));
536
- seen.add(key);
537
- }
538
- }
539
- return groups.sort((a, b) => b.lastActiveMs - a.lastActiveMs);
540
- };
541
- var buildDiscovery = async (userDir) => {
542
- const buckets = await loadCursorBuckets(userDir);
543
- const bucketGroups = groupCursorBuckets(buckets);
544
- const fileHistoryActivity = await readCursorFileHistoryProjectActivity(userDir);
545
- const globalDbPath = getCursorGlobalDbPath(userDir);
546
- if (!await pathExists(globalDbPath)) return assembleDiscovery([], bucketGroups, fileHistoryActivity);
547
- const bucketIdToGroupKey = /* @__PURE__ */ new Map();
548
- const bucketIdToFolder = /* @__PURE__ */ new Map();
549
- for (const group of bucketGroups) for (const bucket of group.buckets) {
550
- bucketIdToGroupKey.set(bucket.bucketId, group.key);
551
- bucketIdToFolder.set(bucket.bucketId, group.folders[0] ?? null);
552
- }
553
- const headerInfo = readHeaderInfo(globalDbPath);
554
- const bucketComposerIds = collectBucketComposerIds(buckets);
555
- const db = openCursorReadonlyDb(globalDbPath);
556
- try {
557
- const heads = readAllHeads(db);
558
- const stats = readBubbleStats(db);
559
- const universe = new Set([
560
- ...heads.keys(),
561
- ...headerInfo.keys(),
562
- ...bucketComposerIds.keys()
563
- ]);
564
- const resolved = [];
565
- for (const composerId of universe) resolved.push(resolveThreadFolder(composerId, heads.get(composerId), headerInfo.get(composerId), stats.get(composerId) ?? {
566
- bytes: 0,
567
- count: 0
568
- }, bucketIdToGroupKey, bucketIdToFolder, bucketComposerIds, db));
569
- return assembleDiscovery(resolved, bucketGroups, fileHistoryActivity);
570
- } finally {
571
- db.close();
572
- }
573
- };
574
- var discoverCursorWorkspaces = async (userDir) => {
575
- const now = Date.now();
576
- if (discoveryCache && discoveryCache.userDir === userDir && now - discoveryCache.at < DISCOVERY_TTL_MS) return discoveryCache.value;
577
- const value = await buildDiscovery(userDir);
578
- discoveryCache = {
579
- at: now,
580
- userDir,
581
- value
582
- };
583
- return value;
584
- };
585
- var readCursorThreadHead = (globalDbPath, composerId) => {
586
- const db = openCursorReadonlyDb(globalDbPath);
587
- try {
588
- const head = readKvValue(db, `composerData:${composerId}`);
589
- if (!head) return null;
590
- const headerList = Array.isArray(head.fullConversationHeadersOnly) ? head.fullConversationHeadersOnly : [];
591
- const orderedBubbleIds = headerList.map((item) => asString(asObject(item)?.bubbleId ?? null)).filter((value) => Boolean(value));
592
- return {
593
- composerId,
594
- createdAtMs: asNumber(head.createdAt ?? null),
595
- lastUpdatedAtMs: asNumber(head.lastUpdatedAt ?? null),
596
- mode: asString(head.unifiedMode ?? null),
597
- name: asString(head.name ?? null),
598
- orderedBubbleIds,
599
- totalBubbleHeaders: headerList.length
600
- };
601
- } finally {
602
- db.close();
603
- }
604
- };
605
- var toBubbleKind = (rawType) => {
606
- if (rawType === 1) return "user";
607
- if (rawType === 2) return "assistant";
608
- return "unknown";
609
- };
610
- var parseToolCall = (raw) => {
611
- const data = asObject(raw);
612
- if (!data) return null;
613
- const name = asString(data.name ?? null);
614
- if (!name) return null;
615
- return {
616
- argumentsText: asString(data.rawArgs ?? null) ?? asString(data.params ?? null),
617
- callId: asString(data.toolCallId ?? null),
618
- name,
619
- resultText: asString(data.result ?? null),
620
- status: asString(data.status ?? null)
621
- };
622
- };
623
- var parseCursorBubble = (bubbleId, raw) => {
624
- const thinking = asObject(raw.thinking ?? null);
625
- return {
626
- bubbleId,
627
- createdAtMs: asNumber(raw.createdAt ?? null),
628
- kind: toBubbleKind(raw.type ?? null),
629
- text: asString(raw.text ?? null) ?? "",
630
- thinking: thinking ? asString(thinking.text ?? null) : null,
631
- toolCall: parseToolCall(raw.toolFormerData ?? null)
632
- };
633
- };
634
- var readBubble = (db, composerId, bubbleId) => {
635
- const raw = readKvValue(db, `bubbleId:${composerId}:${bubbleId}`);
636
- if (!raw) return null;
637
- return parseCursorBubble(bubbleId, raw);
638
- };
639
- var isRenderableBubble = (bubble) => {
640
- return Boolean(bubble.text.trim() || bubble.thinking?.trim() || bubble.toolCall);
641
- };
642
- var normalizeBubbleText = (value) => {
643
- return (value ?? "").replace(/\s+/gu, " ").trim();
644
- };
645
- var hashText = (value) => createHash("sha1").update(value).digest("hex").slice(0, 12);
646
- var stableStringifyJson = (value) => {
647
- if (Array.isArray(value)) return `[${value.map(stableStringifyJson).join(",")}]`;
648
- if (value && typeof value === "object") return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringifyJson(value[key])}`).join(",")}}`;
649
- return JSON.stringify(value);
650
- };
651
- var normalizeToolArgumentsText = (value) => {
652
- if (!value?.trim()) return "";
653
- try {
654
- return stableStringifyJson(JSON.parse(value));
655
- } catch {
656
- return normalizeBubbleText(value);
657
- }
658
- };
659
- var hasRenderableTextSuperset = (left, right) => {
660
- const normalizedLeft = normalizeBubbleText(left);
661
- const normalizedRight = normalizeBubbleText(right);
662
- if (!normalizedLeft && !normalizedRight) return true;
663
- return normalizedLeft === normalizedRight || normalizedLeft.startsWith(normalizedRight) || normalizedRight.startsWith(normalizedLeft);
664
- };
665
- var haveSameToolIdentity = (left, right) => {
666
- if (!left && !right) return true;
667
- if (!left || !right) return false;
668
- return left.name === right.name && normalizeToolArgumentsText(left.argumentsText) === normalizeToolArgumentsText(right.argumentsText);
669
- };
670
- var areEquivalentBubbles = (left, right) => {
671
- return left.kind === right.kind && hasRenderableTextSuperset(left.text, right.text) && Boolean(normalizeBubbleText(left.thinking)) === Boolean(normalizeBubbleText(right.thinking)) && haveSameToolIdentity(left.toolCall, right.toolCall);
672
- };
673
- var findAgentTailStartIndex = (existingBubbles, agentBubbles) => {
674
- const maxOverlap = Math.min(existingBubbles.length, agentBubbles.length);
675
- for (let overlapLength = maxOverlap; overlapLength > 0; overlapLength -= 1) {
676
- const existingStart = existingBubbles.length - overlapLength;
677
- if (agentBubbles.slice(0, overlapLength).every((bubble, index) => areEquivalentBubbles(existingBubbles[existingStart + index], bubble))) return overlapLength;
678
- }
679
- return 0;
680
- };
681
- var hasEquivalentBubble = (bubbles, candidate) => {
682
- return bubbles.some((bubble) => areEquivalentBubbles(bubble, candidate));
683
- };
684
- var getAgentTranscriptContentParts = (entry) => {
685
- const content = asObject(entry.message ?? null)?.content ?? entry.content ?? null;
686
- if (Array.isArray(content)) return content.map((part) => asObject(part)).filter((part) => Boolean(part));
687
- if (typeof content === "string") return [{
688
- text: content,
689
- type: "text"
690
- }];
691
- return [];
692
- };
693
- var parseAgentTranscriptToolCall = (parts) => {
694
- const toolUse = parts.find((part) => asString(part.type ?? null) === "tool_use");
695
- if (!toolUse) return null;
696
- const name = asString(toolUse.name ?? null);
697
- if (!name) return null;
698
- return {
699
- argumentsText: toolUse.input === void 0 ? null : JSON.stringify(toolUse.input),
700
- callId: asString(toolUse.id ?? null),
701
- name,
702
- resultText: null,
703
- status: null
704
- };
705
- };
706
- var parseAgentTranscriptBubble = (filePath, lineNumber, raw) => {
707
- const message = asObject(raw.message ?? null);
708
- const role = asString(raw.role ?? message?.role ?? null);
709
- const kind = role === "user" || role === "assistant" ? role : "unknown";
710
- const parts = getAgentTranscriptContentParts(raw);
711
- const text = parts.filter((part) => asString(part.type ?? null) === "text").map((part) => asString(part.text ?? null)).filter((value) => Boolean(value?.trim())).join("\n\n");
712
- const toolCall = parseAgentTranscriptToolCall(parts);
713
- const bubble = {
714
- bubbleId: `agent-transcript:${hashText(path.resolve(filePath))}:${path.basename(filePath)}:${lineNumber}`,
715
- createdAtMs: null,
716
- kind,
717
- text,
718
- thinking: null,
719
- toolCall
720
- };
721
- return isRenderableBubble(bubble) ? bubble : null;
722
- };
723
- var readCursorAgentTranscriptFile = async (filePath) => {
724
- let text = "";
725
- try {
726
- text = await Bun.file(filePath).text();
727
- } catch (error) {
728
- warnCursorDataIssue("agent_transcript_unreadable", {
729
- error: error instanceof Error ? error.message : String(error),
730
- filePath
731
- });
732
- return [];
733
- }
734
- const bubbles = [];
735
- for (const [index, line] of text.split(/\n/u).entries()) {
736
- if (!line.trim()) continue;
737
- let raw;
738
- try {
739
- raw = JSON.parse(line);
740
- } catch (error) {
741
- warnCursorDataIssue("invalid_agent_transcript_jsonl", {
742
- error: error instanceof Error ? error.message : String(error),
743
- filePath,
744
- lineNumber: index + 1
745
- });
746
- continue;
747
- }
748
- const entry = asObject(raw);
749
- if (!entry) continue;
750
- const bubble = parseAgentTranscriptBubble(filePath, index + 1, entry);
751
- if (bubble) bubbles.push(bubble);
752
- }
753
- return bubbles;
754
- };
755
- var listCursorAgentTranscriptFiles = async (transcriptDir, composerId) => {
756
- const preferred = path.join(transcriptDir, `${composerId}.jsonl`);
757
- const files = /* @__PURE__ */ new Set();
758
- if (await pathExists(preferred)) files.add(preferred);
759
- let entries = [];
760
- try {
761
- entries = await readdir(transcriptDir);
762
- } catch {
763
- return [...files];
764
- }
765
- for (const entry of entries) if (entry.endsWith(".jsonl")) files.add(path.join(transcriptDir, entry));
766
- return [...files].sort();
767
- };
768
- var readCursorAgentTranscriptBubbles = async (composerId, userDir) => {
769
- const transcriptDirs = await findCursorTranscriptDirs(composerId, userDir);
770
- const bubbles = [];
771
- for (const transcriptDir of transcriptDirs.sort()) {
772
- const files = await listCursorAgentTranscriptFiles(transcriptDir, composerId);
773
- for (const file of files) bubbles.push(...await readCursorAgentTranscriptFile(file));
774
- }
775
- return bubbles;
776
- };
777
- var mergeAgentTranscriptTail = (transcript, agentBubbles) => {
778
- const tailStartIndex = findAgentTailStartIndex(transcript.bubbles, agentBubbles);
779
- const seen = [...transcript.bubbles];
780
- const appended = [];
781
- const candidates = agentBubbles.slice(tailStartIndex);
782
- for (const bubble of candidates) {
783
- if (hasEquivalentBubble(seen, bubble)) continue;
784
- seen.push(bubble);
785
- appended.push(bubble);
786
- }
787
- if (appended.length === 0) return transcript;
788
- return {
789
- ...transcript,
790
- bubbles: [...transcript.bubbles, ...appended],
791
- renderableBubbleCount: transcript.renderableBubbleCount + appended.length
792
- };
793
- };
794
- var inferCursorUserDirFromGlobalDbPath = (globalDbPath) => {
795
- const globalStorageDir = path.dirname(globalDbPath);
796
- return path.basename(globalStorageDir) === "globalStorage" ? path.dirname(globalStorageDir) : resolveCursorUserDir();
797
- };
798
- var readCursorThreadTranscript = (globalDbPath, composerId) => {
799
- const head = readCursorThreadHead(globalDbPath, composerId);
800
- if (!head) return null;
801
- const db = openCursorReadonlyDb(globalDbPath);
802
- try {
803
- const orderedIds = head.orderedBubbleIds.length > 0 ? head.orderedBubbleIds : readAllBubbleIds(db, composerId);
804
- const bubbles = [];
805
- for (const bubbleId of orderedIds) {
806
- const bubble = readBubble(db, composerId, bubbleId);
807
- if (bubble && isRenderableBubble(bubble)) bubbles.push(bubble);
808
- }
809
- const totalBubbleRows = countBubbles(db, composerId).count;
810
- return {
811
- bubbles,
812
- head,
813
- omittedBubbleCount: Math.max(totalBubbleRows - orderedIds.length, 0),
814
- renderableBubbleCount: bubbles.length
815
- };
816
- } finally {
817
- db.close();
818
- }
819
- };
820
- var readCursorThreadTranscriptWithAgentFiles = async (globalDbPath, composerId, userDir = inferCursorUserDirFromGlobalDbPath(globalDbPath)) => {
821
- const transcript = readCursorThreadTranscript(globalDbPath, composerId);
822
- if (!transcript) return null;
823
- return mergeAgentTranscriptTail(transcript, await readCursorAgentTranscriptBubbles(composerId, userDir));
824
- };
825
- var readAllBubbleIds = (db, composerId) => {
826
- const prefix = `bubbleId:${composerId}:`;
827
- return db.query("SELECT key FROM cursorDiskKV WHERE key LIKE ? ORDER BY key ASC").all(`${prefix}%`).map((row) => row.key.slice(prefix.length));
828
- };
829
- //#endregion
830
- export { findCursorTranscriptDirs, invalidateCursorDiscoveryCache, listCursorThreadsForGroup, listCursorWorkspaceGroups, loadGlobalComposerHeaders, openCursorReadonlyDb, readCursorThreadTranscriptWithAgentFiles };