spiracha 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +114 -205
- package/README.md +89 -269
- package/apps/ui/AGENTS.md +17 -7
- package/apps/ui/README.md +34 -8
- package/apps/ui/components.json +21 -0
- package/apps/ui/package.json +65 -0
- package/apps/ui/src/components/antigravity-conversations-table.tsx +206 -0
- package/apps/ui/src/components/antigravity-keychain-panel.tsx +76 -0
- package/apps/ui/src/components/antigravity-workspaces-table.tsx +63 -0
- package/apps/ui/src/components/app-shell.tsx +124 -0
- package/apps/ui/src/components/breadcrumbs.tsx +53 -0
- package/apps/ui/src/components/claude-code-sessions-table.tsx +114 -0
- package/apps/ui/src/components/claude-code-workspaces-table.tsx +55 -0
- package/apps/ui/src/components/cursor-threads-table.tsx +189 -0
- package/apps/ui/src/components/cursor-workspaces-table.tsx +140 -0
- package/apps/ui/src/components/data-table.tsx +241 -0
- package/apps/ui/src/components/delete-confirm-dialog.tsx +88 -0
- package/apps/ui/src/components/export-dialog.tsx +184 -0
- package/apps/ui/src/components/json-panel.tsx +17 -0
- package/apps/ui/src/components/kiro-sessions-table.tsx +112 -0
- package/apps/ui/src/components/kiro-workspaces-table.tsx +53 -0
- package/apps/ui/src/components/list-search-input.tsx +18 -0
- package/apps/ui/src/components/loading-panel.tsx +23 -0
- package/apps/ui/src/components/metadata-section.tsx +31 -0
- package/apps/ui/src/components/metric-card.tsx +19 -0
- package/apps/ui/src/components/opencode-sessions-table.tsx +128 -0
- package/apps/ui/src/components/opencode-workspaces-table.tsx +57 -0
- package/apps/ui/src/components/page-header.tsx +33 -0
- package/apps/ui/src/components/projects-loading-state.tsx +74 -0
- package/apps/ui/src/components/projects-table.tsx +108 -0
- package/apps/ui/src/components/qoder-sessions-table.tsx +112 -0
- package/apps/ui/src/components/qoder-workspaces-table.tsx +53 -0
- package/apps/ui/src/components/recent-threads-list.tsx +52 -0
- package/apps/ui/src/components/reload-error-panel.tsx +20 -0
- package/apps/ui/src/components/text-document-panel.tsx +19 -0
- package/apps/ui/src/components/theme-toggle.tsx +48 -0
- package/apps/ui/src/components/threads-table.tsx +202 -0
- package/apps/ui/src/components/transcript-view.tsx +552 -0
- package/apps/ui/src/components/ui/alert-dialog.tsx +160 -0
- package/apps/ui/src/components/ui/badge.tsx +45 -0
- package/apps/ui/src/components/ui/button.tsx +62 -0
- package/apps/ui/src/components/ui/checkbox.tsx +29 -0
- package/apps/ui/src/components/ui/dialog.tsx +137 -0
- package/apps/ui/src/components/ui/dropdown-menu.tsx +217 -0
- package/apps/ui/src/components/ui/input.tsx +21 -0
- package/apps/ui/src/components/ui/scroll-area.tsx +46 -0
- package/apps/ui/src/components/ui/select.tsx +163 -0
- package/apps/ui/src/components/ui/separator.tsx +28 -0
- package/apps/ui/src/components/ui/sheet.tsx +107 -0
- package/apps/ui/src/components/ui/skeleton.tsx +7 -0
- package/apps/ui/src/components/ui/table.tsx +76 -0
- package/apps/ui/src/components/ui/tabs.tsx +71 -0
- package/apps/ui/src/components/ui/tooltip.tsx +44 -0
- package/apps/ui/src/integrations/tanstack-query/devtools.tsx +6 -0
- package/apps/ui/src/integrations/tanstack-query/root-provider.tsx +10 -0
- package/apps/ui/src/lib/antigravity-conversation-state.ts +28 -0
- package/apps/ui/src/lib/antigravity-queries.ts +33 -0
- package/apps/ui/src/lib/antigravity-server.ts +128 -0
- package/apps/ui/src/lib/antigravity-transcript-events.ts +335 -0
- package/apps/ui/src/lib/claude-code-queries.ts +26 -0
- package/apps/ui/src/lib/claude-code-server.ts +75 -0
- package/apps/ui/src/lib/claude-code-transcript-events.ts +154 -0
- package/apps/ui/src/lib/codex-queries.ts +66 -0
- package/apps/ui/src/lib/codex-server.ts +209 -0
- package/apps/ui/src/lib/cursor-queries.ts +37 -0
- package/apps/ui/src/lib/cursor-server.ts +272 -0
- package/apps/ui/src/lib/cursor-transcript-events.ts +229 -0
- package/apps/ui/src/lib/download.ts +166 -0
- package/apps/ui/src/lib/formatters.ts +149 -0
- package/apps/ui/src/lib/kiro-queries.ts +22 -0
- package/apps/ui/src/lib/kiro-server.ts +73 -0
- package/apps/ui/src/lib/kiro-transcript-events.ts +113 -0
- package/apps/ui/src/lib/opencode-queries.ts +37 -0
- package/apps/ui/src/lib/opencode-server.ts +73 -0
- package/apps/ui/src/lib/opencode-transcript-events.ts +221 -0
- package/apps/ui/src/lib/package-metadata.ts +16 -0
- package/apps/ui/src/lib/path-utils.ts +13 -0
- package/apps/ui/src/lib/qoder-queries.ts +22 -0
- package/apps/ui/src/lib/qoder-server.ts +79 -0
- package/apps/ui/src/lib/qoder-transcript-events.ts +139 -0
- package/apps/ui/src/lib/route-search.ts +68 -0
- package/apps/ui/src/lib/settings-store.tsx +59 -0
- package/apps/ui/src/lib/source-session-export-server.ts +56 -0
- package/apps/ui/src/lib/text-filter.ts +22 -0
- package/apps/ui/src/lib/thread-id.ts +3 -0
- package/apps/ui/src/lib/thread-transcript-stats.ts +57 -0
- package/apps/ui/src/lib/utils.ts +7 -0
- package/apps/ui/src/routeTree.gen.ts +730 -0
- package/apps/ui/src/router.tsx +20 -0
- package/apps/ui/src/routes/$threadId.tsx +22 -0
- package/apps/ui/src/routes/__root.tsx +127 -0
- package/apps/ui/src/routes/analytics.tsx +143 -0
- package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +438 -0
- package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +127 -0
- package/apps/ui/src/routes/antigravity.index.tsx +51 -0
- package/apps/ui/src/routes/api.v1.conversation-query.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.$source.$id.export.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.ts +12 -0
- package/apps/ui/src/routes/api.v1.resolve.ts +12 -0
- package/apps/ui/src/routes/api.v1.sources.ts +12 -0
- package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +342 -0
- package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +135 -0
- package/apps/ui/src/routes/claude-code.index.tsx +48 -0
- package/apps/ui/src/routes/codex.$project.tsx +390 -0
- package/apps/ui/src/routes/codex.index.tsx +116 -0
- package/apps/ui/src/routes/cursor-threads.$composerId.tsx +435 -0
- package/apps/ui/src/routes/cursor.$workspaceKey.tsx +416 -0
- package/apps/ui/src/routes/cursor.index.tsx +120 -0
- package/apps/ui/src/routes/index.tsx +124 -0
- package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +345 -0
- package/apps/ui/src/routes/kiro.$workspaceKey.tsx +136 -0
- package/apps/ui/src/routes/kiro.index.tsx +48 -0
- package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +334 -0
- package/apps/ui/src/routes/opencode.$workspaceKey.tsx +135 -0
- package/apps/ui/src/routes/opencode.index.tsx +48 -0
- package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +341 -0
- package/apps/ui/src/routes/qoder.$workspaceKey.tsx +139 -0
- package/apps/ui/src/routes/qoder.index.tsx +48 -0
- package/apps/ui/src/routes/settings.tsx +91 -0
- package/apps/ui/src/routes/threads.$threadId.tsx +619 -0
- package/apps/ui/src/styles.css +122 -0
- package/apps/ui/tsconfig.json +29 -0
- package/apps/ui/vite.config.ts +84 -0
- package/bin/spiracha.ts +32 -0
- package/package.json +46 -73
- package/src/client.ts +290 -0
- package/src/lib/antigravity-db.ts +213 -19
- package/src/lib/antigravity-exporter-types.ts +1 -0
- package/src/lib/codex-analytics.ts +1 -1
- package/src/lib/codex-browser-db.ts +410 -74
- package/src/lib/codex-browser-export.ts +6 -14
- package/src/lib/codex-browser-types.ts +1 -1
- package/src/lib/{codex-exporter-types.ts → codex-thread-types.ts} +2 -30
- package/src/lib/{codex-exporter-transcript.ts → codex-transcript-renderer.ts} +64 -28
- package/src/lib/conversation-api.ts +792 -0
- package/src/lib/conversation-data/adapter-helpers.ts +110 -0
- package/src/lib/conversation-data/antigravity-adapter.ts +148 -0
- package/src/lib/conversation-data/claude-code-adapter.ts +175 -0
- package/src/lib/conversation-data/codex-adapter.ts +245 -0
- package/src/lib/conversation-data/cursor-adapter.ts +171 -0
- package/src/lib/conversation-data/index.ts +311 -0
- package/src/lib/conversation-data/kiro-adapter.ts +140 -0
- package/src/lib/conversation-data/message-selector.ts +37 -0
- package/src/lib/conversation-data/opencode-adapter.ts +180 -0
- package/src/lib/conversation-data/path-match.ts +79 -0
- package/src/lib/conversation-data/qoder-adapter.ts +230 -0
- package/src/lib/conversation-data/types.ts +116 -0
- package/src/lib/cursor-db.ts +67 -21
- package/src/lib/cursor-exporter-types.ts +0 -19
- package/src/lib/qoder-acp-client.ts +268 -0
- package/src/lib/qoder-db.ts +1601 -0
- package/src/lib/qoder-exporter-types.ts +114 -0
- package/src/lib/qoder-transcript-phase.ts +41 -0
- package/src/lib/qoder-transcript.ts +112 -0
- package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
- package/apps/ui/dist/client/assets/analytics-DOy1b6Sh.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DqySVO2K.js +0 -7
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-keychain-panel-B77qJz6f.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-B3NM6gRH.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-DP9JC2P9.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
- package/apps/ui/dist/client/assets/badge-C3i4OKs8.js +0 -1
- package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
- package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-8pQ55ci2.js +0 -1
- package/apps/ui/dist/client/assets/claude-code-sessions._sessionId-DVz5UmtU.js +0 -1
- package/apps/ui/dist/client/assets/claude-code._workspaceKey-BBKskSum.js +0 -1
- package/apps/ui/dist/client/assets/claude-code._workspaceKey-OkikMUFs.js +0 -1
- package/apps/ui/dist/client/assets/claude-code.index-Cwu76dbb.js +0 -1
- package/apps/ui/dist/client/assets/claude-code.index-D6eMd08t.js +0 -1
- package/apps/ui/dist/client/assets/createServerFn-B-KkwmHU.js +0 -3
- package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +0 -1
- package/apps/ui/dist/client/assets/cursor-threads._composerId-hBWlP1D-.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-BFPkIRi-.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
- package/apps/ui/dist/client/assets/cursor.index-8y4o20kb.js +0 -2
- package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
- package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
- package/apps/ui/dist/client/assets/delete-confirm-dialog-Bxs07u-j.js +0 -7
- package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
- package/apps/ui/dist/client/assets/dist-BrJhN2A0.js +0 -5
- package/apps/ui/dist/client/assets/dist-Cmd3AIfD.js +0 -1
- package/apps/ui/dist/client/assets/dist-DvMS2965.js +0 -1
- package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +0 -1
- package/apps/ui/dist/client/assets/dropdown-menu-D5cvPaZt.js +0 -1
- package/apps/ui/dist/client/assets/es2015-ad6040dw.js +0 -41
- package/apps/ui/dist/client/assets/export-dialog-Dr8d2MqD.js +0 -1
- package/apps/ui/dist/client/assets/formatters-BtqyrX_c.js +0 -1
- package/apps/ui/dist/client/assets/index-C-STgIH4.js +0 -166
- package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
- package/apps/ui/dist/client/assets/kiro-sessions._sessionId-CKgfJ7GR.js +0 -1
- package/apps/ui/dist/client/assets/kiro-sessions._sessionId-zvIJHtQF.js +0 -1
- package/apps/ui/dist/client/assets/kiro._workspaceKey-DhVuJuZB.js +0 -1
- package/apps/ui/dist/client/assets/kiro._workspaceKey-QArsjkTY.js +0 -1
- package/apps/ui/dist/client/assets/kiro.index-C-fr4m7i.js +0 -1
- package/apps/ui/dist/client/assets/kiro.index-OYgAdtZd.js +0 -1
- package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
- package/apps/ui/dist/client/assets/opencode-sessions._sessionId-DLjiTst-.js +0 -1
- package/apps/ui/dist/client/assets/opencode-sessions._sessionId-biBwrRbt.js +0 -8
- package/apps/ui/dist/client/assets/opencode._workspaceKey-C7OIzMOJ.js +0 -1
- package/apps/ui/dist/client/assets/opencode._workspaceKey-sBakX0oI.js +0 -1
- package/apps/ui/dist/client/assets/opencode.index-Bl_kNs9N.js +0 -1
- package/apps/ui/dist/client/assets/opencode.index-CN53bVmb.js +0 -1
- package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
- package/apps/ui/dist/client/assets/preload-helper-B8fFrdxC.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-D8WloxQN.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-5xRtuJdX.js +0 -3
- package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
- package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +0 -1
- package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +0 -1
- package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +0 -1
- package/apps/ui/dist/client/assets/routes-DnzF_9CX.js +0 -1
- package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
- package/apps/ui/dist/client/assets/select-CYF6pF37.js +0 -1
- package/apps/ui/dist/client/assets/settings-Dx70jgP_.js +0 -1
- package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
- package/apps/ui/dist/client/assets/styles-B7Zi9EFA.css +0 -1
- package/apps/ui/dist/client/assets/tabs-CmuhkT-c.js +0 -7
- package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
- package/apps/ui/dist/client/assets/thread-transcript-stats-BUNz6u7F.js +0 -1
- package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
- package/apps/ui/dist/client/assets/threads._threadId-BMRSFow3.js +0 -1
- package/apps/ui/dist/client/assets/useMutation-BSFult2i.js +0 -1
- package/apps/ui/dist/client/assets/useQuery-CjINKZf1.js +0 -1
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CQ8Jjs3B.js +0 -387
- package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +0 -6
- package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +0 -16
- package/apps/ui/dist/server/assets/analytics-CtpOMVMO.js +0 -146
- package/apps/ui/dist/server/assets/antigravity-conversation-state-HgzS302O.js +0 -16
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BKOyL2T6.js +0 -613
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-D426O-64.js +0 -11
- package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-eS87Dx7g.js +0 -20
- package/apps/ui/dist/server/assets/antigravity-db-Bh8_U9uw.js +0 -580
- package/apps/ui/dist/server/assets/antigravity-keychain-DOiuHDwK.js +0 -126
- package/apps/ui/dist/server/assets/antigravity-keychain-panel-CC7FLmAK.js +0 -55
- package/apps/ui/dist/server/assets/antigravity-queries-CGrJO9Vr.js +0 -37
- package/apps/ui/dist/server/assets/antigravity-server-CHRjVFqN.js +0 -114
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-3m_MzNFA.js +0 -11
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-B0QeYUOh.js +0 -28
- package/apps/ui/dist/server/assets/antigravity._workspaceKey-C3kP-qyN.js +0 -210
- package/apps/ui/dist/server/assets/antigravity.index-CPCYkqCi.js +0 -104
- package/apps/ui/dist/server/assets/antigravity.index-DudTB3Tq.js +0 -11
- package/apps/ui/dist/server/assets/badge-EvdhKK_Z.js +0 -26
- package/apps/ui/dist/server/assets/button-CmTDnzOn.js +0 -46
- package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +0 -19
- package/apps/ui/dist/server/assets/claude-code-db-Ik3VStKZ.js +0 -361
- package/apps/ui/dist/server/assets/claude-code-queries-Cgzi4K-e.js +0 -37
- package/apps/ui/dist/server/assets/claude-code-server-PfraClXS.js +0 -72
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-C55oe6zj.js +0 -18
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-D1RYYi3j.js +0 -473
- package/apps/ui/dist/server/assets/claude-code-sessions._sessionId-DSvPLEUL.js +0 -11
- package/apps/ui/dist/server/assets/claude-code-transcript-C-bxh3cS.js +0 -139
- package/apps/ui/dist/server/assets/claude-code-transcript-phase-DmUMc8VU.js +0 -10
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-B5Xg_-tE.js +0 -28
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-BPlzyXPa.js +0 -11
- package/apps/ui/dist/server/assets/claude-code._workspaceKey-DqDve2wl.js +0 -191
- package/apps/ui/dist/server/assets/claude-code.index-B7yYR7dD.js +0 -92
- package/apps/ui/dist/server/assets/claude-code.index-BUHThG1W.js +0 -11
- package/apps/ui/dist/server/assets/codex-queries-CtgeZ7VL.js +0 -99
- package/apps/ui/dist/server/assets/codex-server-CGXU4H8-.js +0 -2294
- package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +0 -18
- package/apps/ui/dist/server/assets/createServerRpc-CF_DEwnm.js +0 -12
- package/apps/ui/dist/server/assets/createSsrRpc-C754NPki.js +0 -16
- package/apps/ui/dist/server/assets/cursor-db-CZnYy7aT.js +0 -830
- package/apps/ui/dist/server/assets/cursor-exporter-types-CI3goo-c.js +0 -34
- package/apps/ui/dist/server/assets/cursor-queries-NCIM0Nat.js +0 -67
- package/apps/ui/dist/server/assets/cursor-recovery-CXpO8KcD.js +0 -361
- package/apps/ui/dist/server/assets/cursor-server-DNu52A3K.js +0 -213
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BB0Y_Mao.js +0 -11
- package/apps/ui/dist/server/assets/cursor-threads._composerId-Ckn6ItHI.js +0 -582
- package/apps/ui/dist/server/assets/cursor-threads._composerId-p9-ARAzc.js +0 -18
- package/apps/ui/dist/server/assets/cursor-transcript-B7tGnqtn.js +0 -125
- package/apps/ui/dist/server/assets/cursor._workspaceKey-CZuohIUL.js +0 -28
- package/apps/ui/dist/server/assets/cursor._workspaceKey-DPp5JSjy.js +0 -401
- package/apps/ui/dist/server/assets/cursor._workspaceKey-nmg3YIOQ.js +0 -11
- package/apps/ui/dist/server/assets/cursor.index-CcsX7DG0.js +0 -11
- package/apps/ui/dist/server/assets/cursor.index-DkWT-mxw.js +0 -189
- package/apps/ui/dist/server/assets/data-table-Cdct823O.js +0 -189
- package/apps/ui/dist/server/assets/delete-confirm-dialog-BcBNCxWB.js +0 -144
- package/apps/ui/dist/server/assets/download-DMmiy1xf.js +0 -92
- package/apps/ui/dist/server/assets/dropdown-menu-Dy_9t6TN.js +0 -36
- package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +0 -5
- package/apps/ui/dist/server/assets/export-dialog-D88ze9Gy.js +0 -240
- package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
- package/apps/ui/dist/server/assets/kiro-db--fXrED_-.js +0 -552
- package/apps/ui/dist/server/assets/kiro-queries-CzNlM2ok.js +0 -37
- package/apps/ui/dist/server/assets/kiro-server-9WRxg2Au.js +0 -72
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-CRY2c7YO.js +0 -11
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-F1UlwxmR.js +0 -449
- package/apps/ui/dist/server/assets/kiro-sessions._sessionId-bvTulT7C.js +0 -18
- package/apps/ui/dist/server/assets/kiro-transcript-DoekQBaH.js +0 -112
- package/apps/ui/dist/server/assets/kiro-transcript-phase-CJG40nQg.js +0 -27
- package/apps/ui/dist/server/assets/kiro._workspaceKey-BDe9sQwo.js +0 -11
- package/apps/ui/dist/server/assets/kiro._workspaceKey-CJsjgNwO.js +0 -28
- package/apps/ui/dist/server/assets/kiro._workspaceKey-D_-4Qb7y.js +0 -193
- package/apps/ui/dist/server/assets/kiro.index-C__-UROk.js +0 -11
- package/apps/ui/dist/server/assets/kiro.index-CmTbqeYj.js +0 -93
- package/apps/ui/dist/server/assets/loading-panel-BGFnWseS.js +0 -27
- package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +0 -23
- package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +0 -13
- package/apps/ui/dist/server/assets/opencode-db-BM7KjOzc.js +0 -397
- package/apps/ui/dist/server/assets/opencode-queries-BeNYXnQv.js +0 -50
- package/apps/ui/dist/server/assets/opencode-server-BpQ_kRze.js +0 -72
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-CGQ-t0vD.js +0 -518
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-D8Q7in_Z.js +0 -18
- package/apps/ui/dist/server/assets/opencode-sessions._sessionId-DKOvi0lV.js +0 -11
- package/apps/ui/dist/server/assets/opencode-think-tags-CAoD-EcZ.js +0 -87
- package/apps/ui/dist/server/assets/opencode-transcript-BNTcGNym.js +0 -141
- package/apps/ui/dist/server/assets/opencode-transcript-phase-OqLkiQD0.js +0 -32
- package/apps/ui/dist/server/assets/opencode._workspaceKey-BdqcWHw1.js +0 -11
- package/apps/ui/dist/server/assets/opencode._workspaceKey-CfH96HTT.js +0 -28
- package/apps/ui/dist/server/assets/opencode._workspaceKey-DdbHPDrO.js +0 -207
- package/apps/ui/dist/server/assets/opencode.index-BbVzkJXP.js +0 -11
- package/apps/ui/dist/server/assets/opencode.index-Vh_Gq_1L.js +0 -93
- package/apps/ui/dist/server/assets/page-header-VNSaM3xd.js +0 -29
- package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +0 -31
- package/apps/ui/dist/server/assets/projects._project-4io5LO0E.js +0 -20
- package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
- package/apps/ui/dist/server/assets/projects._project-D6ZoGhJ8.js +0 -395
- package/apps/ui/dist/server/assets/projects.index-BLXOx5eL.js +0 -12
- package/apps/ui/dist/server/assets/projects.index-BkLiF2FF.js +0 -182
- package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +0 -14
- package/apps/ui/dist/server/assets/reload-error-panel-BJMxY3U1.js +0 -25
- package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +0 -38
- package/apps/ui/dist/server/assets/router-Sac2DGk6.js +0 -518
- package/apps/ui/dist/server/assets/routes-Bsact1uB.js +0 -184
- package/apps/ui/dist/server/assets/routes-BzkJgq4W.js +0 -34
- package/apps/ui/dist/server/assets/select-GW76p-ld.js +0 -76
- package/apps/ui/dist/server/assets/settings-OayxIYQQ.js +0 -100
- package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +0 -52
- package/apps/ui/dist/server/assets/shared-DyhChtHf.js +0 -137
- package/apps/ui/dist/server/assets/source-session-export-server-DNutCawb.js +0 -38
- package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
- package/apps/ui/dist/server/assets/start-B_HFwkkk.js +0 -4
- package/apps/ui/dist/server/assets/tabs-DkaChNVg.js +0 -501
- package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
- package/apps/ui/dist/server/assets/thread-transcript-stats-Dh34mt2u.js +0 -45
- package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
- package/apps/ui/dist/server/assets/threads._threadId-BeXEc4Lo.js +0 -648
- package/apps/ui/dist/server/assets/threads._threadId-Ci0S4mKU.js +0 -18
- package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +0 -83
- package/apps/ui/dist/server/assets/utils-C_uf36nf.js +0 -8
- package/apps/ui/dist/server/server.js +0 -5895
- package/bin/codex-chats-claude.js +0 -5
- package/bin/codex-chats.js +0 -5
- package/bin/spiracha.js +0 -5
- package/src/export-chats.ts +0 -120
- package/src/export-claude.ts +0 -36
- package/src/export-cursor.ts +0 -244
- package/src/lib/claude-exporter.ts +0 -864
- package/src/lib/codex-exporter-cli.ts +0 -277
- package/src/lib/codex-exporter-db.ts +0 -319
- package/src/lib/codex-exporter.ts +0 -115
- package/src/lib/cursor-exporter.ts +0 -266
- package/src/lib/interactive-cli.ts +0 -433
- package/src/lib/native-open.ts +0 -54
- package/src/mcp-server.ts +0 -137
- package/src/spiracha.ts +0 -116
- package/src/ui-cli.ts +0 -310
- /package/apps/ui/{dist/client → public}/icon.svg +0 -0
- /package/apps/ui/{dist/client → public}/manifest.json +0 -0
- /package/apps/ui/{dist/client → public}/robots.txt +0 -0
|
@@ -1,830 +0,0 @@
|
|
|
1
|
-
import { i as asString, n as asNumber, r as asObject } from "./shared-DyhChtHf.js";
|
|
2
|
-
import { getCursorGlobalDbPath, getCursorProjectsDir, getCursorWorkspaceStorageDir, resolveCursorUserDir } from "./cursor-exporter-types-CI3goo-c.js";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import { readdir, stat } from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { Database, constants } from "bun:sqlite";
|
|
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 };
|