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.
- package/AGENTS.md +116 -164
- package/README.md +92 -237
- package/apps/ui/AGENTS.md +36 -7
- package/apps/ui/README.md +79 -6
- package/apps/ui/components.json +21 -0
- package/apps/ui/package.json +65 -0
- package/apps/ui/{dist/client → public}/icon.svg +2 -1
- 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 +50 -60
- 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/claude-code-db.ts +655 -0
- package/src/lib/claude-code-exporter-types.ts +110 -0
- package/src/lib/claude-code-transcript-phase.ts +25 -0
- package/src/lib/claude-code-transcript.ts +180 -0
- package/src/lib/codex-analytics.ts +1 -1
- package/src/lib/codex-browser-db.ts +856 -61
- package/src/lib/codex-browser-export.ts +24 -16
- package/src/lib/codex-browser-types.ts +1 -1
- package/src/lib/codex-thread-cache.ts +17 -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/concurrency.ts +25 -0
- 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/kiro-db.ts +949 -0
- package/src/lib/kiro-exporter-types.ts +100 -0
- package/src/lib/kiro-transcript-phase.ts +41 -0
- package/src/lib/kiro-transcript.ts +115 -0
- package/src/lib/opencode-db.ts +610 -0
- package/src/lib/opencode-exporter-types.ts +129 -0
- package/src/lib/opencode-think-tags.ts +126 -0
- package/src/lib/opencode-transcript-phase.ts +50 -0
- package/src/lib/opencode-transcript.ts +194 -0
- 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/src/lib/shared.ts +25 -0
- package/src/lib/ui-export-archive.ts +61 -0
- package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +0 -1
- package/apps/ui/dist/client/assets/analytics-DK2jdvmI.js +0 -1
- package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DmD0sN8X.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-BRHVjFAM.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +0 -1
- package/apps/ui/dist/client/assets/antigravity._workspaceKey-CGV7amas.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-DEEkjlZa.js +0 -1
- package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +0 -1
- package/apps/ui/dist/client/assets/badge-DfWvPfKF.js +0 -1
- package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +0 -1
- package/apps/ui/dist/client/assets/createServerFn-CgRRVpBH.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-Ti2gTm63.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +0 -1
- package/apps/ui/dist/client/assets/cursor._workspaceKey-DzD_c6j9.js +0 -1
- package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +0 -1
- package/apps/ui/dist/client/assets/cursor.index-PkCFVcl4.js +0 -2
- package/apps/ui/dist/client/assets/data-table-Br78_dk3.js +0 -4
- package/apps/ui/dist/client/assets/delete-confirm-dialog-BHRmyLHr.js +0 -11
- package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +0 -1
- package/apps/ui/dist/client/assets/dist-BhZOxAPP.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-Dy5cLzTy.js +0 -1
- package/apps/ui/dist/client/assets/es2015-BkCWttyM.js +0 -41
- package/apps/ui/dist/client/assets/export-dialog-C86SYHJn.js +0 -1
- package/apps/ui/dist/client/assets/formatters-BM9kB7ed.js +0 -1
- package/apps/ui/dist/client/assets/index-CPvAP-jk.js +0 -149
- package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +0 -1
- package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +0 -1
- package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-Ckf6muWZ.js +0 -1
- package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +0 -1
- package/apps/ui/dist/client/assets/projects.index-V5ibNZII.js +0 -3
- 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-Df2tgpyd.js +0 -1
- package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +0 -1
- package/apps/ui/dist/client/assets/select-Bf2GwfPR.js +0 -1
- package/apps/ui/dist/client/assets/settings-5VQIzME0.js +0 -1
- package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +0 -1
- package/apps/ui/dist/client/assets/styles-BhRkXgwB.css +0 -1
- package/apps/ui/dist/client/assets/tabs-OO4VU5KR.js +0 -7
- package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +0 -2
- package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +0 -1
- package/apps/ui/dist/client/assets/threads._threadId-BgDhFj2I.js +0 -1
- package/apps/ui/dist/client/assets/useMutation-CC_B7uy5.js +0 -1
- package/apps/ui/dist/client/assets/useQuery-C5By6WKU.js +0 -1
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CRW2kqj0.js +0 -232
- 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-BtexWY-K.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-zMrR6v6R.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/codex-queries-CtgeZ7VL.js +0 -99
- package/apps/ui/dist/server/assets/codex-server-CNXSJuc2.js +0 -2004
- 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-cYZEU3WQ.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-nq-kR62j.js +0 -361
- package/apps/ui/dist/server/assets/cursor-server-C3q7hrp-.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-BPJFWfJj.js +0 -18
- package/apps/ui/dist/server/assets/cursor-threads._composerId-BkMMTQ3v.js +0 -582
- package/apps/ui/dist/server/assets/cursor-transcript-2iL3KFSK.js +0 -125
- package/apps/ui/dist/server/assets/cursor._workspaceKey-C5PdTaKB.js +0 -28
- package/apps/ui/dist/server/assets/cursor._workspaceKey-Ckj7apKI.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-BItjWgkZ.js +0 -240
- package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +0 -91
- 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/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-Bpbc3C-L.js +0 -20
- package/apps/ui/dist/server/assets/projects._project-Bshqk7JA.js +0 -12
- package/apps/ui/dist/server/assets/projects._project-CvZho6EQ.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-6m-ihwqA.js +0 -410
- 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-CPRNYIql.js +0 -134
- package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +0 -13
- package/apps/ui/dist/server/assets/start-CI_0bSiY.js +0 -4
- package/apps/ui/dist/server/assets/tabs-CGA13IZM.js +0 -502
- package/apps/ui/dist/server/assets/text-filter-CGKxMCKt.js +0 -36
- package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +0 -26
- package/apps/ui/dist/server/assets/threads._threadId-Bs0yuqO9.js +0 -18
- package/apps/ui/dist/server/assets/threads._threadId-D2GYkVn6.js +0 -639
- 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 -5847
- 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}/manifest.json +0 -0
- /package/apps/ui/{dist/client → public}/robots.txt +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createServerFn } from '@tanstack/react-start';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { canExportAntigravityConversation, isAntigravityConversationLocked } from './antigravity-conversation-state';
|
|
4
|
+
|
|
5
|
+
const workspaceSchema = z.object({
|
|
6
|
+
workspaceKey: z.string().min(1),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const conversationSchema = z.object({
|
|
10
|
+
conversationId: z.string().min(1),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const exportSchema = z.object({
|
|
14
|
+
conversationId: z.string().min(1),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const listAntigravityWorkspacesFn = createServerFn({ method: 'GET' }).handler(async () => {
|
|
18
|
+
const { listAntigravityWorkspaceGroups } = await import('@spiracha/lib/antigravity-db');
|
|
19
|
+
return listAntigravityWorkspaceGroups();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const getAntigravityDecryptionStateFn = createServerFn({ method: 'GET' }).handler(async () => {
|
|
23
|
+
const { getAntigravityDecryptionState } = await import('@spiracha/lib/antigravity-keychain');
|
|
24
|
+
return getAntigravityDecryptionState();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const unlockAntigravityDecryptionFn = createServerFn({ method: 'POST' }).handler(async () => {
|
|
28
|
+
const { unlockAntigravityDecryption } = await import('@spiracha/lib/antigravity-keychain');
|
|
29
|
+
return unlockAntigravityDecryption();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const listAntigravityConversationsFn = createServerFn({ method: 'GET' })
|
|
33
|
+
.validator(workspaceSchema)
|
|
34
|
+
.handler(async ({ data }) => {
|
|
35
|
+
const { listAntigravityConversationsForGroup } = await import('@spiracha/lib/antigravity-db');
|
|
36
|
+
return listAntigravityConversationsForGroup(data.workspaceKey);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const findAntigravityConversationById = async (conversationId: string) => {
|
|
40
|
+
const { listAntigravityConversations } = await import('@spiracha/lib/antigravity-db');
|
|
41
|
+
const conversation = (await listAntigravityConversations()).find(
|
|
42
|
+
(candidate) => candidate.conversationId === conversationId,
|
|
43
|
+
);
|
|
44
|
+
if (!conversation) {
|
|
45
|
+
throw new Error(`Antigravity conversation not found: ${conversationId}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return conversation;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const loadAntigravityConversationDetail = async (conversationId: string) => {
|
|
52
|
+
const { renderAntigravityArtifactsMarkdown, renderAntigravityConversationMarkdown } = await import(
|
|
53
|
+
'@spiracha/lib/antigravity-db'
|
|
54
|
+
);
|
|
55
|
+
const { getCachedAntigravityKeychainSecret } = await import('@spiracha/lib/antigravity-keychain');
|
|
56
|
+
const conversation = await findAntigravityConversationById(conversationId);
|
|
57
|
+
const keychainSecret = getCachedAntigravityKeychainSecret();
|
|
58
|
+
const hasKeychainSecret = Boolean(keychainSecret);
|
|
59
|
+
const transcriptLocked = isAntigravityConversationLocked(conversation, hasKeychainSecret);
|
|
60
|
+
const conversationMarkdown = transcriptLocked
|
|
61
|
+
? null
|
|
62
|
+
: await renderAntigravityConversationMarkdown(conversation, { keychainSecret });
|
|
63
|
+
const artifactsMarkdown =
|
|
64
|
+
conversation.artifactCount > 0 ? await renderAntigravityArtifactsMarkdown(conversation) : null;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
artifactsMarkdown,
|
|
68
|
+
conversation,
|
|
69
|
+
// Suppress the duplicate panel when artifactsMarkdown and conversationMarkdown are identical.
|
|
70
|
+
conversationMarkdown: conversationMarkdown === artifactsMarkdown ? null : conversationMarkdown,
|
|
71
|
+
transcriptLocked,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const loadAntigravityConversationExport = async (conversationId: string) => {
|
|
76
|
+
const { renderAntigravityConversationMarkdown } = await import('@spiracha/lib/antigravity-db');
|
|
77
|
+
const { getCachedAntigravityKeychainSecret } = await import('@spiracha/lib/antigravity-keychain');
|
|
78
|
+
const conversation = await findAntigravityConversationById(conversationId);
|
|
79
|
+
const keychainSecret = getCachedAntigravityKeychainSecret();
|
|
80
|
+
const hasKeychainSecret = Boolean(keychainSecret);
|
|
81
|
+
|
|
82
|
+
if (!canExportAntigravityConversation(conversation, hasKeychainSecret)) {
|
|
83
|
+
if (isAntigravityConversationLocked(conversation, hasKeychainSecret)) {
|
|
84
|
+
throw new Error('Unlock Antigravity Keychain access before exporting transcript logs.');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw new Error(`No exportable Antigravity transcript found for conversation: ${conversationId}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const content = await renderAntigravityConversationMarkdown(conversation, { keychainSecret });
|
|
91
|
+
if (!content) {
|
|
92
|
+
throw new Error(`No exportable Antigravity transcript found for conversation: ${conversationId}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
content,
|
|
97
|
+
filename: `${conversationId}.md`,
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const getAntigravityConversationDetailFn = createServerFn({ method: 'GET' })
|
|
102
|
+
.validator(conversationSchema)
|
|
103
|
+
.handler(async ({ data }) => {
|
|
104
|
+
return loadAntigravityConversationDetail(data.conversationId);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export const exportAntigravityArtifactsFn = createServerFn({ method: 'POST' })
|
|
108
|
+
.validator(exportSchema)
|
|
109
|
+
.handler(async ({ data }) => {
|
|
110
|
+
const { renderAntigravityArtifactsMarkdown } = await import('@spiracha/lib/antigravity-db');
|
|
111
|
+
const conversation = await findAntigravityConversationById(data.conversationId);
|
|
112
|
+
|
|
113
|
+
const content = await renderAntigravityArtifactsMarkdown(conversation);
|
|
114
|
+
if (!content) {
|
|
115
|
+
throw new Error(`No Markdown artifacts found for conversation: ${data.conversationId}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
content,
|
|
120
|
+
filename: `${data.conversationId}-artifacts.md`,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export const exportAntigravityConversationFn = createServerFn({ method: 'POST' })
|
|
125
|
+
.validator(exportSchema)
|
|
126
|
+
.handler(async ({ data }) => {
|
|
127
|
+
return loadAntigravityConversationExport(data.conversationId);
|
|
128
|
+
});
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import type { ThreadEvent, ThreadTranscriptStats } from '@spiracha/lib/codex-browser-types';
|
|
2
|
+
import type { JsonValue } from '@spiracha/lib/shared';
|
|
3
|
+
|
|
4
|
+
type MarkdownSection = {
|
|
5
|
+
body: string;
|
|
6
|
+
heading: string;
|
|
7
|
+
sequence: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type ParsedAssistantSection = {
|
|
11
|
+
content: string;
|
|
12
|
+
thinking: string;
|
|
13
|
+
toolCalls: ParsedToolCall[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type ParsedToolCall = {
|
|
17
|
+
argumentsText: string | null;
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const HEADING_PATTERN = /^##\s+(.+)$/u;
|
|
22
|
+
const TIMESTAMP_PATTERN = /^_Timestamp:\s*(.+?)_$/u;
|
|
23
|
+
const TOOL_HEADING_PATTERN = /^tool:\s*(.+)$/iu;
|
|
24
|
+
|
|
25
|
+
const splitMarkdownSections = (markdown: string | null): MarkdownSection[] => {
|
|
26
|
+
if (!markdown?.trim()) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const sections: MarkdownSection[] = [];
|
|
31
|
+
const lines = markdown.split(/\r?\n/u);
|
|
32
|
+
let currentHeading: string | null = null;
|
|
33
|
+
let currentLines: string[] = [];
|
|
34
|
+
|
|
35
|
+
const flush = () => {
|
|
36
|
+
if (!currentHeading) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
sections.push({
|
|
41
|
+
body: currentLines.join('\n').trim(),
|
|
42
|
+
heading: currentHeading,
|
|
43
|
+
sequence: sections.length * 10,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const heading = HEADING_PATTERN.exec(line);
|
|
49
|
+
if (heading) {
|
|
50
|
+
flush();
|
|
51
|
+
currentHeading = heading[1]!.trim();
|
|
52
|
+
currentLines = [];
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (currentHeading) {
|
|
57
|
+
currentLines.push(line);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
flush();
|
|
62
|
+
return sections;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const extractTimestamp = (body: string): { body: string; timestamp: string | null } => {
|
|
66
|
+
const lines = body.split(/\r?\n/u);
|
|
67
|
+
const timestamp = TIMESTAMP_PATTERN.exec(lines[0]?.trim() ?? '')?.[1]?.trim() ?? null;
|
|
68
|
+
return {
|
|
69
|
+
body: timestamp ? lines.slice(1).join('\n').trim() : body.trim(),
|
|
70
|
+
timestamp,
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const sectionBodyBeforeSubheading = (body: string): string => {
|
|
75
|
+
return body.split(/^###\s+/mu)[0]?.trim() ?? '';
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const extractSubheadingBlock = (body: string, title: string): string => {
|
|
79
|
+
const lines = body.split(/\r?\n/u);
|
|
80
|
+
const startIndex = lines.findIndex((line) => line.trim().toLowerCase() === `### ${title.toLowerCase()}`);
|
|
81
|
+
if (startIndex === -1) {
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const nextSubheadingIndex = lines.findIndex((line, index) => index > startIndex && line.startsWith('### '));
|
|
86
|
+
return lines
|
|
87
|
+
.slice(startIndex + 1, nextSubheadingIndex === -1 ? undefined : nextSubheadingIndex)
|
|
88
|
+
.join('\n')
|
|
89
|
+
.trim();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const parseToolCalls = (body: string): ParsedToolCall[] => {
|
|
93
|
+
const toolCallsBlock = extractSubheadingBlock(body, 'Tool Calls');
|
|
94
|
+
if (!toolCallsBlock) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const calls: ParsedToolCall[] = [];
|
|
99
|
+
const callPattern = /-\s+`([^`]+)`(?:\s*\n+```json\s*\n([\s\S]*?)\n```)?/gu;
|
|
100
|
+
for (const match of toolCallsBlock.matchAll(callPattern)) {
|
|
101
|
+
calls.push({
|
|
102
|
+
argumentsText: match[2]?.trim() ?? null,
|
|
103
|
+
name: match[1]?.trim() || 'unknown',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return calls;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const parseAssistantSection = (body: string): ParsedAssistantSection => {
|
|
111
|
+
const content = sectionBodyBeforeSubheading(body);
|
|
112
|
+
const thinkingBlock = extractSubheadingBlock(body, 'Thinking');
|
|
113
|
+
const [thinking, ...remainingThinkingBlock] = thinkingBlock.split(/\n{2,}/u).map((entry) => entry.trim());
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
content: content || remainingThinkingBlock.join('\n\n').trim(),
|
|
117
|
+
thinking: thinking ?? '',
|
|
118
|
+
toolCalls: parseToolCalls(body),
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const buildRaw = (section: MarkdownSection, extra: Record<string, JsonValue> = {}): Record<string, JsonValue> => ({
|
|
123
|
+
heading: section.heading,
|
|
124
|
+
source: 'antigravity_markdown',
|
|
125
|
+
...extra,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const buildMessageEvent = (
|
|
129
|
+
section: MarkdownSection,
|
|
130
|
+
role: string,
|
|
131
|
+
text: string,
|
|
132
|
+
timestamp: string | null,
|
|
133
|
+
phase: string | null,
|
|
134
|
+
sequenceOffset = 0,
|
|
135
|
+
): ThreadEvent => ({
|
|
136
|
+
isHiddenByDefault: role !== 'assistant' && role !== 'user',
|
|
137
|
+
kind: 'message',
|
|
138
|
+
memoryCitation: null,
|
|
139
|
+
model: null,
|
|
140
|
+
phase,
|
|
141
|
+
raw: buildRaw(section, { role }),
|
|
142
|
+
role,
|
|
143
|
+
sequence: section.sequence + sequenceOffset,
|
|
144
|
+
text,
|
|
145
|
+
timestamp,
|
|
146
|
+
variant: role === 'user' ? 'user_message' : role === 'assistant' ? 'agent_message' : 'message',
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const buildToolCallEvent = (
|
|
150
|
+
section: MarkdownSection,
|
|
151
|
+
toolCall: ParsedToolCall,
|
|
152
|
+
timestamp: string | null,
|
|
153
|
+
sequenceOffset: number,
|
|
154
|
+
): ThreadEvent => ({
|
|
155
|
+
argumentsParseFailed: false,
|
|
156
|
+
argumentsText: toolCall.argumentsText,
|
|
157
|
+
callId: null,
|
|
158
|
+
command: [toolCall.name, toolCall.argumentsText].filter(Boolean).join('\n'),
|
|
159
|
+
kind: 'tool_call',
|
|
160
|
+
name: toolCall.name,
|
|
161
|
+
raw: buildRaw(section, {
|
|
162
|
+
argumentsText: toolCall.argumentsText,
|
|
163
|
+
name: toolCall.name,
|
|
164
|
+
}),
|
|
165
|
+
sequence: section.sequence + sequenceOffset,
|
|
166
|
+
timestamp,
|
|
167
|
+
workdir: null,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const buildToolOutputEvent = (
|
|
171
|
+
section: MarkdownSection,
|
|
172
|
+
toolName: string,
|
|
173
|
+
outputText: string,
|
|
174
|
+
timestamp: string | null,
|
|
175
|
+
): ThreadEvent => ({
|
|
176
|
+
callId: null,
|
|
177
|
+
exitCode: null,
|
|
178
|
+
kind: 'tool_output',
|
|
179
|
+
outputText,
|
|
180
|
+
raw: buildRaw(section, {
|
|
181
|
+
name: toolName,
|
|
182
|
+
}),
|
|
183
|
+
sequence: section.sequence,
|
|
184
|
+
summary: outputText,
|
|
185
|
+
timestamp,
|
|
186
|
+
wallTime: null,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const getFinalAssistantSectionSequences = (sections: MarkdownSection[]): Set<number> => {
|
|
190
|
+
const finalAssistantSectionSequences = new Set<number>();
|
|
191
|
+
let currentAssistantContentSequence: number | null = null;
|
|
192
|
+
|
|
193
|
+
const flushAssistantRun = () => {
|
|
194
|
+
if (currentAssistantContentSequence !== null) {
|
|
195
|
+
finalAssistantSectionSequences.add(currentAssistantContentSequence);
|
|
196
|
+
}
|
|
197
|
+
currentAssistantContentSequence = null;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
for (const section of sections) {
|
|
201
|
+
const heading = section.heading.toLowerCase();
|
|
202
|
+
if (heading === 'user') {
|
|
203
|
+
flushAssistantRun();
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (heading !== 'assistant') {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const { body } = extractTimestamp(section.body);
|
|
212
|
+
const parsed = parseAssistantSection(body);
|
|
213
|
+
if (parsed.toolCalls.length > 0) {
|
|
214
|
+
currentAssistantContentSequence = null;
|
|
215
|
+
} else if (parsed.content) {
|
|
216
|
+
currentAssistantContentSequence = section.sequence;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
flushAssistantRun();
|
|
221
|
+
return finalAssistantSectionSequences;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const textSectionToEvents = (
|
|
225
|
+
section: MarkdownSection,
|
|
226
|
+
role: string,
|
|
227
|
+
body: string,
|
|
228
|
+
timestamp: string | null,
|
|
229
|
+
): ThreadEvent[] => {
|
|
230
|
+
return body ? [buildMessageEvent(section, role, body, timestamp, null)] : [];
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const toolOutputSectionToEvents = (
|
|
234
|
+
section: MarkdownSection,
|
|
235
|
+
toolName: string,
|
|
236
|
+
body: string,
|
|
237
|
+
timestamp: string | null,
|
|
238
|
+
): ThreadEvent[] => {
|
|
239
|
+
return body ? [buildToolOutputEvent(section, toolName, body, timestamp)] : [];
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const assistantSectionToEvents = (
|
|
243
|
+
section: MarkdownSection,
|
|
244
|
+
body: string,
|
|
245
|
+
timestamp: string | null,
|
|
246
|
+
finalAssistantSectionSequences: Set<number>,
|
|
247
|
+
): ThreadEvent[] => {
|
|
248
|
+
const parsed = parseAssistantSection(body);
|
|
249
|
+
const events: ThreadEvent[] = [];
|
|
250
|
+
if (parsed.thinking) {
|
|
251
|
+
events.push(buildMessageEvent(section, 'assistant', parsed.thinking, timestamp, 'commentary'));
|
|
252
|
+
}
|
|
253
|
+
if (parsed.content) {
|
|
254
|
+
const phase = finalAssistantSectionSequences.has(section.sequence) ? 'final_answer' : 'commentary';
|
|
255
|
+
events.push(buildMessageEvent(section, 'assistant', parsed.content, timestamp, phase, 1));
|
|
256
|
+
}
|
|
257
|
+
parsed.toolCalls.forEach((toolCall, index) => {
|
|
258
|
+
events.push(buildToolCallEvent(section, toolCall, timestamp, 2 + index));
|
|
259
|
+
});
|
|
260
|
+
return events;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const sectionToEvents = (section: MarkdownSection, finalAssistantSectionSequences: Set<number>): ThreadEvent[] => {
|
|
264
|
+
const { body, timestamp } = extractTimestamp(section.body);
|
|
265
|
+
const heading = section.heading.toLowerCase();
|
|
266
|
+
const toolHeading = TOOL_HEADING_PATTERN.exec(section.heading);
|
|
267
|
+
|
|
268
|
+
if (heading === 'user') {
|
|
269
|
+
return textSectionToEvents(section, 'user', body, timestamp);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (toolHeading) {
|
|
273
|
+
return toolOutputSectionToEvents(section, toolHeading[1]?.trim() || 'unknown', body, timestamp);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (heading === 'assistant') {
|
|
277
|
+
return assistantSectionToEvents(section, body, timestamp, finalAssistantSectionSequences);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (heading === 'system') {
|
|
281
|
+
return textSectionToEvents(section, 'system', body, timestamp);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return textSectionToEvents(section, 'event', body, timestamp);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export const antigravityMarkdownToThreadEvents = (markdown: string | null): ThreadEvent[] => {
|
|
288
|
+
const sections = splitMarkdownSections(markdown);
|
|
289
|
+
const finalAssistantSectionSequences = getFinalAssistantSectionSequences(sections);
|
|
290
|
+
return sections.flatMap((section) => sectionToEvents(section, finalAssistantSectionSequences));
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const updateMessageStats = (stats: ThreadTranscriptStats, event: Extract<ThreadEvent, { kind: 'message' }>) => {
|
|
294
|
+
stats.messageCount += 1;
|
|
295
|
+
if (event.role === 'assistant') {
|
|
296
|
+
stats.assistantMessageCount += 1;
|
|
297
|
+
}
|
|
298
|
+
if (event.role === 'user') {
|
|
299
|
+
stats.userMessageCount += 1;
|
|
300
|
+
}
|
|
301
|
+
if (event.phase === 'commentary') {
|
|
302
|
+
stats.commentaryCount += 1;
|
|
303
|
+
}
|
|
304
|
+
if (event.phase === 'final_answer') {
|
|
305
|
+
stats.finalAnswerCount += 1;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
export const getAntigravityThreadTranscriptStats = (events: ThreadEvent[]): ThreadTranscriptStats => {
|
|
310
|
+
const stats: ThreadTranscriptStats = {
|
|
311
|
+
assistantMessageCount: 0,
|
|
312
|
+
commentaryCount: 0,
|
|
313
|
+
execCommandCount: 0,
|
|
314
|
+
finalAnswerCount: 0,
|
|
315
|
+
messageCount: 0,
|
|
316
|
+
toolCallCount: 0,
|
|
317
|
+
toolOutputCount: 0,
|
|
318
|
+
userMessageCount: 0,
|
|
319
|
+
webSearchEventCount: 0,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
for (const event of events) {
|
|
323
|
+
if (event.kind === 'message') {
|
|
324
|
+
updateMessageStats(stats, event);
|
|
325
|
+
}
|
|
326
|
+
if (event.kind === 'tool_call') {
|
|
327
|
+
stats.toolCallCount += 1;
|
|
328
|
+
}
|
|
329
|
+
if (event.kind === 'tool_output') {
|
|
330
|
+
stats.toolOutputCount += 1;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return stats;
|
|
335
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { queryOptions } from '@tanstack/react-query';
|
|
2
|
+
import {
|
|
3
|
+
getClaudeCodeSessionDetailFn,
|
|
4
|
+
listClaudeCodeSessionsFn,
|
|
5
|
+
listClaudeCodeWorkspacesFn,
|
|
6
|
+
} from './claude-code-server';
|
|
7
|
+
|
|
8
|
+
export const claudeCodeWorkspacesQueryOptions = () =>
|
|
9
|
+
queryOptions({
|
|
10
|
+
queryFn: () => listClaudeCodeWorkspacesFn(),
|
|
11
|
+
queryKey: ['claude-code-workspaces'],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const claudeCodeSessionsQueryOptions = (workspaceKey: string | null) =>
|
|
15
|
+
queryOptions({
|
|
16
|
+
enabled: workspaceKey !== null,
|
|
17
|
+
queryFn: () => listClaudeCodeSessionsFn({ data: { workspaceKey: workspaceKey ?? '' } }),
|
|
18
|
+
queryKey: ['claude-code-sessions', workspaceKey ?? 'none'],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const claudeCodeSessionDetailQueryOptions = (sessionId: string | null) =>
|
|
22
|
+
queryOptions({
|
|
23
|
+
enabled: sessionId !== null,
|
|
24
|
+
queryFn: () => getClaudeCodeSessionDetailFn({ data: { sessionId: sessionId ?? '' } }),
|
|
25
|
+
queryKey: ['claude-code-session', sessionId ?? 'none'],
|
|
26
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createServerFn } from '@tanstack/react-start';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { renderSourceSessionDownload } from './source-session-export-server';
|
|
4
|
+
|
|
5
|
+
const workspaceSchema = z.object({
|
|
6
|
+
workspaceKey: z.string().min(1),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const sessionSchema = z.object({
|
|
10
|
+
sessionId: z.string().min(1),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const exportSessionSchema = z.object({
|
|
14
|
+
includeCommentary: z.boolean().default(true),
|
|
15
|
+
includeMetadata: z.boolean().default(true),
|
|
16
|
+
includeTools: z.boolean().default(true),
|
|
17
|
+
outputFormat: z.enum(['md', 'txt']).default('md'),
|
|
18
|
+
sessionId: z.string().min(1),
|
|
19
|
+
zipArchive: z.boolean().default(false),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const listClaudeCodeWorkspacesFn = createServerFn({ method: 'GET' }).handler(async () => {
|
|
23
|
+
const { listClaudeCodeWorkspaceGroups } = await import('@spiracha/lib/claude-code-db');
|
|
24
|
+
return listClaudeCodeWorkspaceGroups();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const listClaudeCodeSessionsFn = createServerFn({ method: 'GET' })
|
|
28
|
+
.validator(workspaceSchema)
|
|
29
|
+
.handler(async ({ data }) => {
|
|
30
|
+
const { listClaudeCodeSessionsForGroup } = await import('@spiracha/lib/claude-code-db');
|
|
31
|
+
return listClaudeCodeSessionsForGroup(data.workspaceKey);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const loadClaudeCodeSessionTranscript = async (sessionId: string) => {
|
|
35
|
+
const { readClaudeCodeSessionTranscript, resolveClaudeCodeProjectsDir } = await import(
|
|
36
|
+
'@spiracha/lib/claude-code-db'
|
|
37
|
+
);
|
|
38
|
+
const transcript = await readClaudeCodeSessionTranscript(resolveClaudeCodeProjectsDir(), sessionId);
|
|
39
|
+
if (!transcript) {
|
|
40
|
+
throw new Error(`Claude Code session not found: ${sessionId}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return transcript;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const getClaudeCodeSessionDetailFn = createServerFn({ method: 'GET' })
|
|
47
|
+
.validator(sessionSchema)
|
|
48
|
+
.handler(async ({ data }) => {
|
|
49
|
+
return loadClaudeCodeSessionTranscript(data.sessionId);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const exportClaudeCodeSessionFn = createServerFn({ method: 'POST' })
|
|
53
|
+
.validator(exportSessionSchema)
|
|
54
|
+
.handler(async ({ data }) => {
|
|
55
|
+
const { renderClaudeCodeTranscript } = await import('@spiracha/lib/claude-code-transcript');
|
|
56
|
+
const transcript = await loadClaudeCodeSessionTranscript(data.sessionId);
|
|
57
|
+
const content = renderClaudeCodeTranscript(transcript, {
|
|
58
|
+
includeCommentary: data.includeCommentary,
|
|
59
|
+
includeMetadata: data.includeMetadata,
|
|
60
|
+
includeTools: data.includeTools,
|
|
61
|
+
outputFormat: data.outputFormat,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!content) {
|
|
65
|
+
throw new Error(`Claude Code session has no exportable content: ${data.sessionId}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return renderSourceSessionDownload({
|
|
69
|
+
content,
|
|
70
|
+
fallbackBaseName: 'claude-code-session',
|
|
71
|
+
fileBaseName: transcript.session.title || transcript.session.sessionId,
|
|
72
|
+
outputFormat: data.outputFormat,
|
|
73
|
+
zipArchive: data.zipArchive,
|
|
74
|
+
});
|
|
75
|
+
});
|