spiracha 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/AGENTS.md +7 -1
  2. package/README.md +6 -0
  3. package/apps/ui/AGENTS.md +8 -0
  4. package/apps/ui/README.md +10 -3
  5. package/apps/ui/dist/client/assets/analytics-DK2jdvmI.js +1 -0
  6. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-DmD0sN8X.js +7 -0
  7. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-KAcXtNxI.js +1 -0
  8. package/apps/ui/dist/client/assets/antigravity-keychain-panel-BRHVjFAM.js +1 -0
  9. package/apps/ui/dist/client/assets/antigravity._workspaceKey-ByitGWki.js +1 -0
  10. package/apps/ui/dist/client/assets/antigravity._workspaceKey-CGV7amas.js +1 -0
  11. package/apps/ui/dist/client/assets/antigravity.index-DEEkjlZa.js +1 -0
  12. package/apps/ui/dist/client/assets/antigravity.index-DgW25FEV.js +1 -0
  13. package/apps/ui/dist/client/assets/badge-DfWvPfKF.js +1 -0
  14. package/apps/ui/dist/client/assets/checkbox-CdHitXWQ.js +1 -0
  15. package/apps/ui/dist/client/assets/createServerFn-CgRRVpBH.js +3 -0
  16. package/apps/ui/dist/client/assets/cursor-threads._composerId-CmBFclK8.js +1 -0
  17. package/apps/ui/dist/client/assets/cursor-threads._composerId-Ti2gTm63.js +1 -0
  18. package/apps/ui/dist/client/assets/cursor._workspaceKey-CaAZjGmm.js +1 -0
  19. package/apps/ui/dist/client/assets/cursor._workspaceKey-DzD_c6j9.js +1 -0
  20. package/apps/ui/dist/client/assets/cursor.index-CrnMwDCi.js +1 -0
  21. package/apps/ui/dist/client/assets/cursor.index-PkCFVcl4.js +2 -0
  22. package/apps/ui/dist/client/assets/{data-table-Cj-v-uyB.js → data-table-Br78_dk3.js} +2 -2
  23. package/apps/ui/dist/client/assets/delete-confirm-dialog-BHRmyLHr.js +11 -0
  24. package/apps/ui/dist/client/assets/dist-B9pyYCVU.js +1 -0
  25. package/apps/ui/dist/client/assets/dist-BhZOxAPP.js +1 -0
  26. package/apps/ui/dist/client/assets/dist-DvMS2965.js +1 -0
  27. package/apps/ui/dist/client/assets/download-Bf0QJKD3.js +1 -0
  28. package/apps/ui/dist/client/assets/dropdown-menu-Dy5cLzTy.js +1 -0
  29. package/apps/ui/dist/client/assets/es2015-BkCWttyM.js +41 -0
  30. package/apps/ui/dist/client/assets/export-dialog-C86SYHJn.js +1 -0
  31. package/apps/ui/dist/client/assets/formatters-BM9kB7ed.js +1 -0
  32. package/apps/ui/dist/client/assets/index-CPvAP-jk.js +149 -0
  33. package/apps/ui/dist/client/assets/jsx-runtime-DGeXAQPT.js +1 -0
  34. package/apps/ui/dist/client/assets/metric-card-Dplm0ZiM.js +1 -0
  35. package/apps/ui/dist/client/assets/page-header-Cwa3p6Tl.js +1 -0
  36. package/apps/ui/dist/client/assets/projects._project-Ckf6muWZ.js +1 -0
  37. package/apps/ui/dist/client/assets/projects._project-DSDwH4NF.js +1 -0
  38. package/apps/ui/dist/client/assets/projects.index-DEfhWqa8.js +1 -0
  39. package/apps/ui/dist/client/assets/projects.index-V5ibNZII.js +3 -0
  40. package/apps/ui/dist/client/assets/react-dom-DL96Jor4.js +1 -0
  41. package/apps/ui/dist/client/assets/refresh-ccw-BgKjOiA6.js +1 -0
  42. package/apps/ui/dist/client/assets/reload-error-panel-AHqu0akK.js +1 -0
  43. package/apps/ui/dist/client/assets/routes-Df2tgpyd.js +1 -0
  44. package/apps/ui/dist/client/assets/scroll-text-fM0sMOB3.js +1 -0
  45. package/apps/ui/dist/client/assets/select-Bf2GwfPR.js +1 -0
  46. package/apps/ui/dist/client/assets/settings-5VQIzME0.js +1 -0
  47. package/apps/ui/dist/client/assets/sqlite-error-B6uZjUe8.js +1 -0
  48. package/apps/ui/dist/client/assets/styles-BhRkXgwB.css +1 -0
  49. package/apps/ui/dist/client/assets/tabs-OO4VU5KR.js +7 -0
  50. package/apps/ui/dist/client/assets/text-filter-DVAZySUS.js +2 -0
  51. package/apps/ui/dist/client/assets/threads._threadId-BB9tTsV4.js +1 -0
  52. package/apps/ui/dist/client/assets/threads._threadId-BgDhFj2I.js +1 -0
  53. package/apps/ui/dist/client/assets/useMutation-CC_B7uy5.js +1 -0
  54. package/apps/ui/dist/client/assets/useQuery-C5By6WKU.js +1 -0
  55. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CRW2kqj0.js +232 -0
  56. package/apps/ui/dist/server/assets/analytics-CYP_LT5Y.js +16 -0
  57. package/apps/ui/dist/server/assets/{analytics-CBNOYZwJ.js → analytics-CtpOMVMO.js} +13 -6
  58. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-BtexWY-K.js +613 -0
  59. package/apps/ui/dist/server/assets/{antigravity-conversations._conversationId-BIdYNy68.js → antigravity-conversations._conversationId-zMrR6v6R.js} +2 -2
  60. package/apps/ui/dist/server/assets/{antigravity-db-D9gW1D8G.js → antigravity-db-Bh8_U9uw.js} +11 -7
  61. package/apps/ui/dist/server/assets/{antigravity-keychain-panel-DcLyBBwd.js → antigravity-keychain-panel-CC7FLmAK.js} +1 -1
  62. package/apps/ui/dist/server/assets/{antigravity-queries-CgQhlQ7J.js → antigravity-queries-CGrJO9Vr.js} +6 -6
  63. package/apps/ui/dist/server/assets/{antigravity-server-DFUx4Khk.js → antigravity-server-CHRjVFqN.js} +11 -11
  64. package/apps/ui/dist/server/assets/{antigravity._workspaceKey-DnSlSC-C.js → antigravity._workspaceKey-B0QeYUOh.js} +2 -2
  65. package/apps/ui/dist/server/assets/{antigravity._workspaceKey-D42ixtzp.js → antigravity._workspaceKey-C3kP-qyN.js} +5 -5
  66. package/apps/ui/dist/server/assets/{antigravity.index-DZVT-cac.js → antigravity.index-CPCYkqCi.js} +2 -2
  67. package/apps/ui/dist/server/assets/{codex-queries-eOJGfHQj.js → codex-queries-CtgeZ7VL.js} +16 -14
  68. package/apps/ui/dist/server/assets/{codex-server-nrETIF--.js → codex-server-CNXSJuc2.js} +105 -120
  69. package/apps/ui/dist/server/assets/concurrency-VXtYvlGh.js +18 -0
  70. package/apps/ui/dist/server/assets/{createServerRpc-BtXIw2iP.js → createServerRpc-CF_DEwnm.js} +1 -1
  71. package/apps/ui/dist/server/assets/{createSsrRpc-COf5Zuye.js → createSsrRpc-C754NPki.js} +1 -1
  72. package/apps/ui/dist/server/assets/{cursor-db-B7agkAvM.js → cursor-db-cYZEU3WQ.js} +194 -7
  73. package/apps/ui/dist/server/assets/{cursor-queries-BMhuJeUO.js → cursor-queries-NCIM0Nat.js} +13 -11
  74. package/apps/ui/dist/server/assets/{cursor-recovery-9bJLs7vG.js → cursor-recovery-nq-kR62j.js} +1 -1
  75. package/apps/ui/dist/server/assets/{cursor-server-BgylIFgn.js → cursor-server-C3q7hrp-.js} +70 -41
  76. package/apps/ui/dist/server/assets/{cursor-threads._composerId-DXffY_CK.js → cursor-threads._composerId-BPJFWfJj.js} +2 -2
  77. package/apps/ui/dist/server/assets/cursor-threads._composerId-BkMMTQ3v.js +582 -0
  78. package/apps/ui/dist/server/assets/{cursor._workspaceKey-BP2J1x_x.js → cursor._workspaceKey-C5PdTaKB.js} +2 -2
  79. package/apps/ui/dist/server/assets/{cursor._workspaceKey-BQd0e-Pd.js → cursor._workspaceKey-Ckj7apKI.js} +11 -9
  80. package/apps/ui/dist/server/assets/{cursor.index-CQVxtCm8.js → cursor.index-DkWT-mxw.js} +2 -2
  81. package/apps/ui/dist/server/assets/{delete-confirm-dialog-PCD7S0_M.js → delete-confirm-dialog-BcBNCxWB.js} +5 -1
  82. package/apps/ui/dist/server/assets/empty-plugin-adapters-B3lHh1La.js +5 -0
  83. package/apps/ui/dist/server/assets/{export-dialog-DaPlOGFT.js → export-dialog-BItjWgkZ.js} +53 -11
  84. package/apps/ui/dist/server/assets/{projects._project-DUN3iWfg.js → projects._project-Bpbc3C-L.js} +5 -3
  85. package/apps/ui/dist/server/assets/{projects._project-Dim9Y0kD.js → projects._project-CvZho6EQ.js} +42 -12
  86. package/apps/ui/dist/server/assets/{projects.index-DjSQK5dm.js → projects.index-BkLiF2FF.js} +21 -7
  87. package/apps/ui/dist/server/assets/projects.index-DesYXwfi.js +14 -0
  88. package/apps/ui/dist/server/assets/route-search-ts4W9MJ9.js +38 -0
  89. package/apps/ui/dist/server/assets/{router-DrDgc-LD.js → router-6m-ihwqA.js} +78 -62
  90. package/apps/ui/dist/server/assets/{routes-B-GlEe2C.js → routes-Bsact1uB.js} +2 -2
  91. package/apps/ui/dist/server/assets/{routes-CNHAUMwo.js → routes-BzkJgq4W.js} +2 -2
  92. package/apps/ui/dist/server/assets/start-CI_0bSiY.js +4 -0
  93. package/apps/ui/dist/server/assets/tabs-CGA13IZM.js +502 -0
  94. package/apps/ui/dist/server/assets/{threads._threadId-CJzm4KrZ.js → threads._threadId-Bs0yuqO9.js} +2 -2
  95. package/apps/ui/dist/server/assets/threads._threadId-D2GYkVn6.js +639 -0
  96. package/apps/ui/dist/server/assets/ui-export-files-BHLX9bhN.js +83 -0
  97. package/apps/ui/dist/server/server.js +340 -218
  98. package/package.json +9 -9
  99. package/src/lib/antigravity-db.ts +20 -13
  100. package/src/lib/codex-analytics.ts +118 -18
  101. package/src/lib/codex-browser-db.ts +1 -25
  102. package/src/lib/codex-browser-export.ts +9 -69
  103. package/src/lib/cursor-db.ts +318 -7
  104. package/src/lib/cursor-exporter.ts +2 -2
  105. package/src/lib/ui-cache.ts +14 -1
  106. package/src/spiracha.ts +20 -1
  107. package/apps/ui/dist/client/assets/analytics-B_hYz65v.js +0 -1
  108. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-qiyygB7e.js +0 -1
  109. package/apps/ui/dist/client/assets/antigravity-conversations._conversationId-z1SQC2Kg.js +0 -1
  110. package/apps/ui/dist/client/assets/antigravity-keychain-panel-dYuRWtCf.js +0 -1
  111. package/apps/ui/dist/client/assets/antigravity._workspaceKey-CliqUr7o.js +0 -1
  112. package/apps/ui/dist/client/assets/antigravity._workspaceKey-CnoBzyX6.js +0 -1
  113. package/apps/ui/dist/client/assets/antigravity.index-CakfZz_E.js +0 -1
  114. package/apps/ui/dist/client/assets/antigravity.index-DY7M1KhG.js +0 -1
  115. package/apps/ui/dist/client/assets/badge-aHE9ETVe.js +0 -1
  116. package/apps/ui/dist/client/assets/checkbox-DN3XnJaA.js +0 -1
  117. package/apps/ui/dist/client/assets/cursor-threads._composerId-BMQyx8qG.js +0 -1
  118. package/apps/ui/dist/client/assets/cursor-threads._composerId-BTlaA-tV.js +0 -1
  119. package/apps/ui/dist/client/assets/cursor._workspaceKey-CrgrfevV.js +0 -1
  120. package/apps/ui/dist/client/assets/cursor._workspaceKey-bYS2syGL.js +0 -1
  121. package/apps/ui/dist/client/assets/cursor.index-CTqZMPYU.js +0 -1
  122. package/apps/ui/dist/client/assets/cursor.index-Clsz4E_e.js +0 -2
  123. package/apps/ui/dist/client/assets/delete-confirm-dialog-DTpzBiNK.js +0 -11
  124. package/apps/ui/dist/client/assets/dist-BNAn99Pu.js +0 -1
  125. package/apps/ui/dist/client/assets/download-P3Rp23Ad.js +0 -1
  126. package/apps/ui/dist/client/assets/dropdown-menu-3qB5j9nt.js +0 -1
  127. package/apps/ui/dist/client/assets/es2015-Dwm_turD.js +0 -41
  128. package/apps/ui/dist/client/assets/export-dialog-CazdrASq.js +0 -1
  129. package/apps/ui/dist/client/assets/formatters-BdnWuM1z.js +0 -1
  130. package/apps/ui/dist/client/assets/index-BVFnfS78.js +0 -22
  131. package/apps/ui/dist/client/assets/json-panel-DLkS30sQ.js +0 -1
  132. package/apps/ui/dist/client/assets/metadata-section-jnIkB7dB.js +0 -1
  133. package/apps/ui/dist/client/assets/metric-card-CBZuWLzQ.js +0 -1
  134. package/apps/ui/dist/client/assets/page-header-CnD21cPn.js +0 -1
  135. package/apps/ui/dist/client/assets/projects._project-BLszwvYL.js +0 -1
  136. package/apps/ui/dist/client/assets/projects._project-DvLxYbvk.js +0 -1
  137. package/apps/ui/dist/client/assets/projects.index-COn8woBR.js +0 -1
  138. package/apps/ui/dist/client/assets/projects.index-DYs98skV.js +0 -3
  139. package/apps/ui/dist/client/assets/refresh-ccw-BDrYXjtD.js +0 -1
  140. package/apps/ui/dist/client/assets/reload-error-panel-DLAg0AW2.js +0 -1
  141. package/apps/ui/dist/client/assets/routes-BtF5-coe.js +0 -1
  142. package/apps/ui/dist/client/assets/scroll-text-CqaFm9by.js +0 -1
  143. package/apps/ui/dist/client/assets/select-DbnpwqL6.js +0 -1
  144. package/apps/ui/dist/client/assets/settings-CGX3VleN.js +0 -1
  145. package/apps/ui/dist/client/assets/styles-Ch0r3kMZ.css +0 -1
  146. package/apps/ui/dist/client/assets/text-document-panel-DPleOmmq.js +0 -1
  147. package/apps/ui/dist/client/assets/text-filter-7M6wRo-t.js +0 -2
  148. package/apps/ui/dist/client/assets/threads._threadId-D5w76IB-.js +0 -7
  149. package/apps/ui/dist/client/assets/threads._threadId-Dx85sI9P.js +0 -1
  150. package/apps/ui/dist/client/assets/useMutation-MZ3Hr9h9.js +0 -1
  151. package/apps/ui/dist/client/assets/useQuery-Cb4V0AT0.js +0 -1
  152. package/apps/ui/dist/server/assets/__23tanstack-start-plugin-adapters-BzCA6dXo.js +0 -5
  153. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-CBbkUXw6.js +0 -227
  154. package/apps/ui/dist/server/assets/antigravity-conversations._conversationId-B9Rm4EXh.js +0 -212
  155. package/apps/ui/dist/server/assets/cursor-threads._composerId-BsxFKzoJ.js +0 -218
  156. package/apps/ui/dist/server/assets/json-panel-RYsxWFae.js +0 -16
  157. package/apps/ui/dist/server/assets/metadata-section-D6Lbc7D6.js +0 -54
  158. package/apps/ui/dist/server/assets/start-HeKLHD9b.js +0 -4
  159. package/apps/ui/dist/server/assets/text-document-panel-D8JbQWAn.js +0 -23
  160. package/apps/ui/dist/server/assets/threads._threadId-DODTYddm.js +0 -1052
@@ -1,4 +1,5 @@
1
1
  import { constants, Database } from 'bun:sqlite';
2
+ import { createHash } from 'node:crypto';
2
3
  import { readdir, stat } from 'node:fs/promises';
3
4
  import path from 'node:path';
4
5
  import {
@@ -109,13 +110,29 @@ const normalizeCursorPath = (value: string): string => {
109
110
  return decoded.replace(/\/+$/u, '') || decoded;
110
111
  };
111
112
 
113
+ const warnCursorDataIssue = (event: string, details: Record<string, unknown>) => {
114
+ console.warn(`[spiracha:cursor] ${event}`, details);
115
+ };
116
+
117
+ const stripJsonComments = (value: string): string => {
118
+ return value.replace(/\/\*[\s\S]*?\*\//gu, '').replace(/(^|\s)\/\/.*$/gmu, '$1');
119
+ };
120
+
121
+ const parseCodeWorkspaceJson = (text: string): { folders?: Array<{ path?: string }> } => {
122
+ try {
123
+ return JSON.parse(text) as { folders?: Array<{ path?: string }> };
124
+ } catch {
125
+ return JSON.parse(stripJsonComments(text)) as { folders?: Array<{ path?: string }> };
126
+ }
127
+ };
128
+
112
129
  const parseCodeWorkspaceFolders = async (workspaceFilePath: string): Promise<string[]> => {
113
130
  if (!workspaceFilePath.endsWith('.code-workspace')) {
114
131
  return [];
115
132
  }
116
133
 
117
134
  try {
118
- const data = (await Bun.file(workspaceFilePath).json()) as { folders?: Array<{ path?: string }> };
135
+ const data = parseCodeWorkspaceJson(await Bun.file(workspaceFilePath).text());
119
136
  const folders: string[] = [];
120
137
  for (const entry of data.folders ?? []) {
121
138
  const folderPath = entry.path;
@@ -131,18 +148,30 @@ const parseCodeWorkspaceFolders = async (workspaceFilePath: string): Promise<str
131
148
  }
132
149
 
133
150
  return folders;
134
- } catch {
151
+ } catch (error) {
152
+ warnCursorDataIssue('invalid_code_workspace_json', {
153
+ error: error instanceof Error ? error.message : String(error),
154
+ workspaceFilePath,
155
+ });
135
156
  return [];
136
157
  }
137
158
  };
138
159
 
139
160
  export const loadGlobalComposerHeaders = (globalDbPath: string): ComposerEntry[] => {
140
- const db = openCursorReadonlyDb(globalDbPath);
141
161
  try {
142
- const data = readItemValue<{ allComposers?: ComposerEntry[] }>(db, COMPOSER_HEADERS_KEY);
143
- return data?.allComposers ?? [];
144
- } finally {
145
- db.close();
162
+ const db = openCursorReadonlyDb(globalDbPath);
163
+ try {
164
+ const data = readItemValue<{ allComposers?: ComposerEntry[] }>(db, COMPOSER_HEADERS_KEY);
165
+ return data?.allComposers ?? [];
166
+ } finally {
167
+ db.close();
168
+ }
169
+ } catch (error) {
170
+ warnCursorDataIssue('global_composer_headers_unavailable', {
171
+ error: error instanceof Error ? error.message : String(error),
172
+ globalDbPath,
173
+ });
174
+ return [];
146
175
  }
147
176
  };
148
177
 
@@ -1055,6 +1084,274 @@ const isRenderableBubble = (bubble: CursorBubble): boolean => {
1055
1084
  return Boolean(bubble.text.trim() || bubble.thinking?.trim() || bubble.toolCall);
1056
1085
  };
1057
1086
 
1087
+ const normalizeBubbleText = (value: string | null): string => {
1088
+ return (value ?? '').replace(/\s+/gu, ' ').trim();
1089
+ };
1090
+
1091
+ const hashText = (value: string) => createHash('sha1').update(value).digest('hex').slice(0, 12);
1092
+
1093
+ const stableStringifyJson = (value: JsonValue): string => {
1094
+ if (Array.isArray(value)) {
1095
+ return `[${value.map(stableStringifyJson).join(',')}]`;
1096
+ }
1097
+
1098
+ if (value && typeof value === 'object') {
1099
+ return `{${Object.keys(value)
1100
+ .sort()
1101
+ .map((key) => `${JSON.stringify(key)}:${stableStringifyJson((value as Record<string, JsonValue>)[key]!)}`)
1102
+ .join(',')}}`;
1103
+ }
1104
+
1105
+ return JSON.stringify(value);
1106
+ };
1107
+
1108
+ const normalizeToolArgumentsText = (value: string | null): string => {
1109
+ if (!value?.trim()) {
1110
+ return '';
1111
+ }
1112
+
1113
+ try {
1114
+ return stableStringifyJson(JSON.parse(value) as JsonValue);
1115
+ } catch {
1116
+ return normalizeBubbleText(value);
1117
+ }
1118
+ };
1119
+
1120
+ const hasRenderableTextSuperset = (left: string | null, right: string | null): boolean => {
1121
+ const normalizedLeft = normalizeBubbleText(left);
1122
+ const normalizedRight = normalizeBubbleText(right);
1123
+ if (!normalizedLeft && !normalizedRight) {
1124
+ return true;
1125
+ }
1126
+
1127
+ return (
1128
+ normalizedLeft === normalizedRight ||
1129
+ normalizedLeft.startsWith(normalizedRight) ||
1130
+ normalizedRight.startsWith(normalizedLeft)
1131
+ );
1132
+ };
1133
+
1134
+ const haveSameToolIdentity = (left: CursorToolCall | null, right: CursorToolCall | null): boolean => {
1135
+ if (!left && !right) {
1136
+ return true;
1137
+ }
1138
+
1139
+ if (!left || !right) {
1140
+ return false;
1141
+ }
1142
+
1143
+ return (
1144
+ left.name === right.name &&
1145
+ normalizeToolArgumentsText(left.argumentsText) === normalizeToolArgumentsText(right.argumentsText)
1146
+ );
1147
+ };
1148
+
1149
+ const areEquivalentBubbles = (left: CursorBubble, right: CursorBubble): boolean => {
1150
+ return (
1151
+ left.kind === right.kind &&
1152
+ hasRenderableTextSuperset(left.text, right.text) &&
1153
+ Boolean(normalizeBubbleText(left.thinking)) === Boolean(normalizeBubbleText(right.thinking)) &&
1154
+ haveSameToolIdentity(left.toolCall, right.toolCall)
1155
+ );
1156
+ };
1157
+
1158
+ const findAgentTailStartIndex = (existingBubbles: CursorBubble[], agentBubbles: CursorBubble[]): number => {
1159
+ const maxOverlap = Math.min(existingBubbles.length, agentBubbles.length);
1160
+ // Agent transcript files contain the complete run while SQLite may lag or truncate the tail.
1161
+ // Match the longest SQLite suffix to the agent prefix, then append only the remaining agent tail.
1162
+ for (let overlapLength = maxOverlap; overlapLength > 0; overlapLength -= 1) {
1163
+ const existingStart = existingBubbles.length - overlapLength;
1164
+ const matches = agentBubbles
1165
+ .slice(0, overlapLength)
1166
+ .every((bubble, index) => areEquivalentBubbles(existingBubbles[existingStart + index]!, bubble));
1167
+ if (matches) {
1168
+ return overlapLength;
1169
+ }
1170
+ }
1171
+
1172
+ return 0;
1173
+ };
1174
+
1175
+ const hasEquivalentBubble = (bubbles: CursorBubble[], candidate: CursorBubble): boolean => {
1176
+ return bubbles.some((bubble) => areEquivalentBubbles(bubble, candidate));
1177
+ };
1178
+
1179
+ const getAgentTranscriptContentParts = (entry: Record<string, JsonValue>): Record<string, JsonValue>[] => {
1180
+ const message = asObject(entry.message ?? null);
1181
+ const content = message?.content ?? entry.content ?? null;
1182
+ if (Array.isArray(content)) {
1183
+ return content.map((part) => asObject(part)).filter((part): part is Record<string, JsonValue> => Boolean(part));
1184
+ }
1185
+
1186
+ if (typeof content === 'string') {
1187
+ return [{ text: content, type: 'text' }];
1188
+ }
1189
+
1190
+ return [];
1191
+ };
1192
+
1193
+ const parseAgentTranscriptToolCall = (parts: Record<string, JsonValue>[]): CursorToolCall | null => {
1194
+ const toolUse = parts.find((part) => asString(part.type ?? null) === 'tool_use');
1195
+ if (!toolUse) {
1196
+ return null;
1197
+ }
1198
+
1199
+ const name = asString(toolUse.name ?? null);
1200
+ if (!name) {
1201
+ return null;
1202
+ }
1203
+
1204
+ return {
1205
+ argumentsText: toolUse.input === undefined ? null : JSON.stringify(toolUse.input),
1206
+ callId: asString(toolUse.id ?? null),
1207
+ name,
1208
+ resultText: null,
1209
+ status: null,
1210
+ };
1211
+ };
1212
+
1213
+ const parseAgentTranscriptBubble = (
1214
+ filePath: string,
1215
+ lineNumber: number,
1216
+ raw: Record<string, JsonValue>,
1217
+ ): CursorBubble | null => {
1218
+ const message = asObject(raw.message ?? null);
1219
+ const role = asString(raw.role ?? message?.role ?? null);
1220
+ const kind = role === 'user' || role === 'assistant' ? role : 'unknown';
1221
+ const parts = getAgentTranscriptContentParts(raw);
1222
+ const text = parts
1223
+ .filter((part) => asString(part.type ?? null) === 'text')
1224
+ .map((part) => asString(part.text ?? null))
1225
+ .filter((value): value is string => Boolean(value?.trim()))
1226
+ .join('\n\n');
1227
+ const toolCall = parseAgentTranscriptToolCall(parts);
1228
+ const bubble: CursorBubble = {
1229
+ bubbleId: `agent-transcript:${hashText(path.resolve(filePath))}:${path.basename(filePath)}:${lineNumber}`,
1230
+ createdAtMs: null,
1231
+ kind,
1232
+ text,
1233
+ thinking: null,
1234
+ toolCall,
1235
+ };
1236
+
1237
+ return isRenderableBubble(bubble) ? bubble : null;
1238
+ };
1239
+
1240
+ const readCursorAgentTranscriptFile = async (filePath: string): Promise<CursorBubble[]> => {
1241
+ let text = '';
1242
+ try {
1243
+ text = await Bun.file(filePath).text();
1244
+ } catch (error) {
1245
+ warnCursorDataIssue('agent_transcript_unreadable', {
1246
+ error: error instanceof Error ? error.message : String(error),
1247
+ filePath,
1248
+ });
1249
+ return [];
1250
+ }
1251
+
1252
+ const bubbles: CursorBubble[] = [];
1253
+ for (const [index, line] of text.split(/\n/u).entries()) {
1254
+ if (!line.trim()) {
1255
+ continue;
1256
+ }
1257
+
1258
+ let raw: JsonValue;
1259
+ try {
1260
+ raw = JSON.parse(line) as JsonValue;
1261
+ } catch (error) {
1262
+ warnCursorDataIssue('invalid_agent_transcript_jsonl', {
1263
+ error: error instanceof Error ? error.message : String(error),
1264
+ filePath,
1265
+ lineNumber: index + 1,
1266
+ });
1267
+ continue;
1268
+ }
1269
+
1270
+ const entry = asObject(raw);
1271
+ if (!entry) {
1272
+ continue;
1273
+ }
1274
+
1275
+ const bubble = parseAgentTranscriptBubble(filePath, index + 1, entry);
1276
+ if (bubble) {
1277
+ bubbles.push(bubble);
1278
+ }
1279
+ }
1280
+
1281
+ return bubbles;
1282
+ };
1283
+
1284
+ const listCursorAgentTranscriptFiles = async (transcriptDir: string, composerId: string): Promise<string[]> => {
1285
+ const preferred = path.join(transcriptDir, `${composerId}.jsonl`);
1286
+ const files = new Set<string>();
1287
+ if (await pathExists(preferred)) {
1288
+ files.add(preferred);
1289
+ }
1290
+
1291
+ let entries: string[] = [];
1292
+ try {
1293
+ entries = await readdir(transcriptDir);
1294
+ } catch {
1295
+ return [...files];
1296
+ }
1297
+
1298
+ for (const entry of entries) {
1299
+ if (entry.endsWith('.jsonl')) {
1300
+ files.add(path.join(transcriptDir, entry));
1301
+ }
1302
+ }
1303
+
1304
+ return [...files].sort();
1305
+ };
1306
+
1307
+ const readCursorAgentTranscriptBubbles = async (composerId: string, userDir: string): Promise<CursorBubble[]> => {
1308
+ const transcriptDirs = await findCursorTranscriptDirs(composerId, userDir);
1309
+ const bubbles: CursorBubble[] = [];
1310
+ for (const transcriptDir of transcriptDirs.sort()) {
1311
+ const files = await listCursorAgentTranscriptFiles(transcriptDir, composerId);
1312
+ for (const file of files) {
1313
+ bubbles.push(...(await readCursorAgentTranscriptFile(file)));
1314
+ }
1315
+ }
1316
+
1317
+ return bubbles;
1318
+ };
1319
+
1320
+ const mergeAgentTranscriptTail = (
1321
+ transcript: CursorThreadTranscript,
1322
+ agentBubbles: CursorBubble[],
1323
+ ): CursorThreadTranscript => {
1324
+ const tailStartIndex = findAgentTailStartIndex(transcript.bubbles, agentBubbles);
1325
+ const seen = [...transcript.bubbles];
1326
+ const appended: CursorBubble[] = [];
1327
+ const candidates = agentBubbles.slice(tailStartIndex);
1328
+ for (const bubble of candidates) {
1329
+ if (hasEquivalentBubble(seen, bubble)) {
1330
+ continue;
1331
+ }
1332
+
1333
+ seen.push(bubble);
1334
+ appended.push(bubble);
1335
+ }
1336
+
1337
+ if (appended.length === 0) {
1338
+ return transcript;
1339
+ }
1340
+
1341
+ return {
1342
+ ...transcript,
1343
+ bubbles: [...transcript.bubbles, ...appended],
1344
+ renderableBubbleCount: transcript.renderableBubbleCount + appended.length,
1345
+ };
1346
+ };
1347
+
1348
+ const inferCursorUserDirFromGlobalDbPath = (globalDbPath: string): string => {
1349
+ const globalStorageDir = path.dirname(globalDbPath);
1350
+ return path.basename(globalStorageDir) === 'globalStorage'
1351
+ ? path.dirname(globalStorageDir)
1352
+ : resolveCursorUserDir();
1353
+ };
1354
+
1058
1355
  export const readCursorThreadTranscript = (globalDbPath: string, composerId: string): CursorThreadTranscript | null => {
1059
1356
  const head = readCursorThreadHead(globalDbPath, composerId);
1060
1357
  if (!head) {
@@ -1087,6 +1384,20 @@ export const readCursorThreadTranscript = (globalDbPath: string, composerId: str
1087
1384
  }
1088
1385
  };
1089
1386
 
1387
+ export const readCursorThreadTranscriptWithAgentFiles = async (
1388
+ globalDbPath: string,
1389
+ composerId: string,
1390
+ userDir = inferCursorUserDirFromGlobalDbPath(globalDbPath),
1391
+ ): Promise<CursorThreadTranscript | null> => {
1392
+ const transcript = readCursorThreadTranscript(globalDbPath, composerId);
1393
+ if (!transcript) {
1394
+ return null;
1395
+ }
1396
+
1397
+ const agentBubbles = await readCursorAgentTranscriptBubbles(composerId, userDir);
1398
+ return mergeAgentTranscriptTail(transcript, agentBubbles);
1399
+ };
1400
+
1090
1401
  const readAllBubbleIds = (db: Database, composerId: string): string[] => {
1091
1402
  const prefix = `bubbleId:${composerId}:`;
1092
1403
  const rows = db.query('SELECT key FROM cursorDiskKV WHERE key LIKE ? ORDER BY key ASC').all(`${prefix}%`) as Array<{
@@ -4,7 +4,7 @@ import {
4
4
  listCursorThreadsForGroup,
5
5
  listCursorWorkspaceGroups,
6
6
  readCursorThreadHead,
7
- readCursorThreadTranscript,
7
+ readCursorThreadTranscriptWithAgentFiles,
8
8
  } from './cursor-db';
9
9
  import {
10
10
  type CursorCliOptions,
@@ -115,7 +115,7 @@ const exportSingleThread = async (
115
115
  outputDir: string,
116
116
  options: CursorCliOptions,
117
117
  ): Promise<CursorExportedFile | null> => {
118
- const transcript = readCursorThreadTranscript(globalDbPath, thread.composerId);
118
+ const transcript = await readCursorThreadTranscriptWithAgentFiles(globalDbPath, thread.composerId, options.userDir);
119
119
  if (!transcript) {
120
120
  return null;
121
121
  }
@@ -24,6 +24,18 @@ export const hashCacheKeyParts = (...parts: string[]) => {
24
24
  return createHash('sha1').update(parts.join('|')).digest('hex');
25
25
  };
26
26
 
27
+ export const hashCacheKeyPartsIterable = (parts: Iterable<string>) => {
28
+ const hash = createHash('sha1');
29
+ for (const part of parts) {
30
+ hash.update(String(part.length));
31
+ hash.update(':');
32
+ hash.update(part);
33
+ hash.update(';');
34
+ }
35
+
36
+ return hash.digest('hex');
37
+ };
38
+
27
39
  export const getFileFingerprint = async (filePath: string) => {
28
40
  const metadata = await stat(filePath);
29
41
  return `${filePath}:${metadata.size}:${metadata.mtimeMs}`;
@@ -55,7 +67,8 @@ export const getCachedJson = async <T>(key: string): Promise<T | null> => {
55
67
  return (parsed as CacheEnvelope<T>).value;
56
68
  }
57
69
 
58
- return parsed as T;
70
+ await rm(filePath, { force: true });
71
+ return null;
59
72
  };
60
73
 
61
74
  export const setCachedJson = async <T>(key: string, value: T) => {
package/src/spiracha.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- type SpirachaCommandKind = 'codex' | 'claude' | 'cursor' | 'help' | 'ui';
3
+ type SpirachaCommandKind = 'codex' | 'claude' | 'cursor' | 'help' | 'ui' | 'version';
4
4
 
5
5
  type SpirachaInvocation = {
6
6
  kind: SpirachaCommandKind;
@@ -30,9 +30,22 @@ export const resolveSpirachaInvocation = (argv: string[]): SpirachaInvocation =>
30
30
  return { argv: [], kind: 'help' };
31
31
  }
32
32
 
33
+ if (firstArg === '--version' || firstArg === '-v' || firstArg === 'version') {
34
+ return { argv: [], kind: 'version' };
35
+ }
36
+
33
37
  return { argv, kind: 'codex' };
34
38
  };
35
39
 
40
+ export const readSpirachaPackageVersion = async (): Promise<string> => {
41
+ const manifest = (await Bun.file(new URL('../package.json', import.meta.url)).json()) as { version?: unknown };
42
+ if (typeof manifest.version !== 'string' || !manifest.version) {
43
+ throw new Error('Unable to read Spiracha version from package.json.');
44
+ }
45
+
46
+ return manifest.version;
47
+ };
48
+
36
49
  export const getSpirachaHelpText = (): string => {
37
50
  return [
38
51
  'spiracha - export local assistant transcripts and browse local history',
@@ -59,6 +72,7 @@ export const getSpirachaHelpText = (): string => {
59
72
  ' spiracha claude --help',
60
73
  ' spiracha cursor --help',
61
74
  ' spiracha ui --help',
75
+ ' spiracha --version',
62
76
  ].join('\n');
63
77
  };
64
78
 
@@ -70,6 +84,11 @@ export const runSpirachaCli = async (argv = process.argv.slice(2)): Promise<void
70
84
  return;
71
85
  }
72
86
 
87
+ if (invocation.kind === 'version') {
88
+ console.log(await readSpirachaPackageVersion());
89
+ return;
90
+ }
91
+
73
92
  if (invocation.kind === 'claude') {
74
93
  const { runExportClaudeCli } = await import('./export-claude');
75
94
  await runExportClaudeCli(invocation.argv);
@@ -1 +0,0 @@
1
- import{c as e,o as t,s as n}from"./formatters-BdnWuM1z.js";import{n as r,t as i}from"./data-table-Cj-v-uyB.js";import{a,i as o,n as s,r as c,t as l}from"./select-DbnpwqL6.js";import{Bt as u,Lt as d,M as f,k as p,zt as m}from"./index-BVFnfS78.js";import{t as h}from"./metric-card-CBZuWLzQ.js";import{t as g}from"./page-header-CnD21cPn.js";var _=u(m()),v=d(),y=r(),b=[y.accessor(`name`,{cell:e=>(0,v.jsx)(`span`,{className:`font-mono text-sm`,children:e.getValue()}),header:`Tool`}),y.accessor(`count`,{cell:e=>(0,v.jsx)(`span`,{className:`font-mono text-sm`,children:t(e.getValue())}),header:`Calls`})],x=r(),S=[x.accessor(`model`,{cell:e=>(0,v.jsx)(`span`,{className:`font-mono text-sm`,children:e.getValue()}),header:`Model`}),x.accessor(`threadCount`,{cell:e=>(0,v.jsx)(`span`,{className:`font-mono text-sm`,children:t(e.getValue())}),header:`Threads`}),x.accessor(`totalTokens`,{cell:e=>(0,v.jsx)(`span`,{className:`font-mono text-sm`,children:n(e.getValue())}),header:`Tokens`})];function C(){let r=e(f()).data,[u,d]=(0,_.useState)(null),m=e(p(u)).data;return(0,v.jsxs)(`div`,{className:`space-y-6`,children:[(0,v.jsx)(g,{actions:(0,v.jsxs)(l,{value:u??`__all__`,onValueChange:e=>{(0,_.startTransition)(()=>{d(e===`__all__`?null:e)})},children:[(0,v.jsx)(o,{className:`h-10 w-full rounded-full border-[var(--border)] bg-[var(--panel)] sm:w-[15rem]`,children:(0,v.jsx)(a,{placeholder:`Filter by project`})}),(0,v.jsxs)(s,{children:[(0,v.jsx)(c,{value:`__all__`,children:`All projects`}),r.map(e=>(0,v.jsx)(c,{value:e.name,children:e.name},e.name))]})]}),eyebrow:`Analytics`,subtitle:`Inspect token load, tool-call distribution, and project-scoped usage patterns to understand where Codex is spending effort.`,title:`Analytics`}),(0,v.jsxs)(`div`,{className:`grid gap-4 md:grid-cols-2 xl:grid-cols-5`,children:[(0,v.jsx)(h,{label:`Threads`,value:t(m.summary.totalThreads)}),(0,v.jsx)(h,{label:`Projects`,value:t(m.summary.totalProjects)}),(0,v.jsx)(h,{label:`Tokens`,value:n(m.summary.totalTokens)}),(0,v.jsx)(h,{label:`Average per thread`,value:n(Math.round(m.summary.averageTokensPerThread))}),(0,v.jsx)(h,{label:`Web search threads`,value:t(m.summary.threadsWithWebSearch)})]}),(0,v.jsxs)(`div`,{className:`grid gap-4 xl:grid-cols-2`,children:[(0,v.jsxs)(`section`,{className:`space-y-4`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`h3`,{className:`font-semibold text-sm`,children:`Most frequent tool calls`}),(0,v.jsx)(`p`,{className:`mt-1 text-[var(--muted-foreground)] text-sm`,children:`Useful for future prompt and tool optimization work.`})]}),(0,v.jsx)(i,{columns:b,data:m.toolUsage,emptyMessage:`No tool calls recorded.`})]}),(0,v.jsxs)(`section`,{className:`space-y-4`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`h3`,{className:`font-semibold text-sm`,children:`Model token breakdown`}),(0,v.jsx)(`p`,{className:`mt-1 text-[var(--muted-foreground)] text-sm`,children:`Compare model usage and token concentration within the current project scope.`})]}),(0,v.jsx)(i,{columns:S,data:m.modelsByTokens,emptyMessage:`No model usage recorded.`})]})]})]})}export{C as component};
@@ -1 +0,0 @@
1
- import{Lt as e}from"./index-BVFnfS78.js";import{t}from"./reload-error-panel-DLAg0AW2.js";var n=e();function r({error:e}){return(0,n.jsx)(t,{description:e.message,title:`Failed to load Antigravity conversation`})}export{r as errorComponent};
@@ -1 +0,0 @@
1
- import{c as e,n as t,o as n,r}from"./formatters-BdnWuM1z.js";import{t as i}from"./useMutation-MZ3Hr9h9.js";import{n as a,t as o}from"./metadata-section-jnIkB7dB.js";import{r as s,t as c}from"./download-P3Rp23Ad.js";import{t as l}from"./antigravity-keychain-panel-dYuRWtCf.js";import{t as u}from"./scroll-text-CqaFm9by.js";import{Ft as d,H as f,Lt as p,d as m,g as h,l as g,m as _,p as v,t as y}from"./index-BVFnfS78.js";import{t as b}from"./metric-card-CBZuWLzQ.js";import{t as x}from"./page-header-CnD21cPn.js";import{t as S}from"./text-document-panel-DPleOmmq.js";var C=p(),w=e=>[{label:`Conversation ID`,value:(0,C.jsx)(`span`,{"data-mono":`true`,children:e.conversation.conversationId})},{label:`Workspace`,value:(0,C.jsx)(d,{className:`text-[var(--accent)]`,params:{workspaceKey:e.conversation.workspaceKey},to:`/antigravity/$workspaceKey`,children:e.conversation.workspaceLabel})},{label:`Created`,value:(0,C.jsx)(`span`,{suppressHydrationWarning:!0,children:r(e.conversation.createdAtMs)})},{label:`Updated`,value:(0,C.jsx)(`span`,{suppressHydrationWarning:!0,children:r(e.conversation.lastUpdatedAtMs)})},{label:`Transcript source`,value:e.conversation.transcriptSource??`n/a`},{label:`Conversation path`,value:e.conversation.conversationPath??`n/a`},{label:`Transcript path`,value:e.conversation.transcriptPath??`n/a`},{label:`Summary path`,value:e.conversation.summaryPath??`n/a`},{label:`Source root`,value:e.conversation.sourceRoot??`n/a`}];function T({canExportArtifacts:e,canExportConversation:t,exportArtifactsPending:n,exportConversationPending:r,showConversationExport:i,onExportArtifacts:a,onExportConversation:o}){return(0,C.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[i?(0,C.jsxs)(f,{className:`rounded-full`,disabled:!t||r,type:`button`,variant:`outline`,onClick:o,children:[(0,C.jsx)(s,{className:`mr-2 size-4`}),`Export conversation`]}):null,e?(0,C.jsxs)(f,{className:`rounded-full`,disabled:n,type:`button`,variant:`outline`,onClick:a,children:[(0,C.jsx)(u,{className:`mr-2 size-4`}),`Export artifacts`]}):null]})}function E({detail:e}){return(0,C.jsxs)(`div`,{className:`space-y-4`,children:[e.transcriptLocked?(0,C.jsxs)(`section`,{className:`rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]`,children:[(0,C.jsx)(`h3`,{className:`font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]`,children:`Transcript`}),(0,C.jsx)(`p`,{className:`mt-4 text-[var(--muted-foreground)] text-sm`,children:`Unlock Antigravity transcript export to inspect the rendered conversation content here.`})]}):e.conversationMarkdown?(0,C.jsx)(S,{content:e.conversationMarkdown,description:`Rendered from the available Antigravity transcript or decrypted safe-storage payload.`,title:`Conversation transcript`}):(0,C.jsxs)(`section`,{className:`rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]`,children:[(0,C.jsx)(`h3`,{className:`font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]`,children:`Transcript`}),(0,C.jsx)(`p`,{className:`mt-4 text-[var(--muted-foreground)] text-sm`,children:e.artifactsMarkdown?`No transcript preview is available for this conversation. Generated artifacts are available below.`:`No transcript preview is available for this conversation.`})]}),e.artifactsMarkdown?(0,C.jsx)(S,{content:e.artifactsMarkdown,description:`Markdown artifacts generated for this conversation.`,title:`Artifacts`}):null]})}function D(){let r=e(m()).data,s=e(g(y.useParams().conversationId)).data,u=h(s.conversation,!!r?.isUnlocked),d=s.artifactsMarkdown!==null,f=u||s.transcriptLocked,p=i({mutationFn:()=>_({data:{conversationId:s.conversation.conversationId}}),onSuccess:e=>{c(e.filename,e.content,`text/markdown; charset=utf-8`)}}),S=i({mutationFn:()=>v({data:{conversationId:s.conversation.conversationId}}),onSuccess:e=>{c(e.filename,e.content,`text/markdown; charset=utf-8`)}});return(0,C.jsxs)(`div`,{className:`space-y-6`,children:[(0,C.jsx)(x,{actions:(0,C.jsx)(T,{canExportArtifacts:d,canExportConversation:u,exportArtifactsPending:S.isPending,exportConversationPending:p.isPending,showConversationExport:f,onExportArtifacts:()=>S.mutate(),onExportConversation:()=>p.mutate()}),breadcrumb:(0,C.jsx)(a,{items:[{label:`Antigravity`,to:`/antigravity`},{label:s.conversation.workspaceLabel,params:{workspaceKey:s.conversation.workspaceKey},to:`/antigravity/$workspaceKey`},{label:s.conversation.title}]}),eyebrow:`Antigravity conversation`,subtitle:`Conversation detail for the selected Antigravity workspace session.`,title:s.conversation.title}),(0,C.jsx)(l,{}),(0,C.jsxs)(`div`,{className:`grid gap-3 md:grid-cols-2 xl:grid-cols-4`,children:[(0,C.jsx)(b,{label:`Transcript entries`,value:n(s.conversation.transcriptEntryCount)}),(0,C.jsx)(b,{label:`Artifacts`,value:n(s.conversation.artifactCount)}),(0,C.jsx)(b,{label:`Size`,value:t(s.conversation.conversationBytes)}),(0,C.jsx)(b,{helper:s.conversation.transcriptSource??`summary`,label:`Indexed items`,value:n(s.conversation.indexedItemCount??0)})]}),(0,C.jsxs)(`div`,{className:`grid gap-4 xl:grid-cols-[0.95fr_1.05fr]`,children:[(0,C.jsx)(o,{items:w(s),title:`Conversation metadata`}),(0,C.jsx)(E,{detail:s})]}),p.isError?(0,C.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:p.error instanceof Error?p.error.message:`Conversation export failed`}):null,S.isError?(0,C.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:S.error instanceof Error?S.error.message:`Artifact export failed`}):null]})}export{D as component};
@@ -1 +0,0 @@
1
- import{t as e}from"./useMutation-MZ3Hr9h9.js";import{t}from"./useQuery-Cb4V0AT0.js";import{H as n,Lt as r,_t as i,d as a,gt as o,h as s,mt as c}from"./index-BVFnfS78.js";var l=o(`key-round`,[[`path`,{d:`M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z`,key:`1s6t7t`}],[`circle`,{cx:`16.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`w0ekpg`}]]),u=o(`lock-keyhole`,[[`circle`,{cx:`12`,cy:`16`,r:`1`,key:`1au0dj`}],[`rect`,{x:`3`,y:`10`,width:`18`,height:`12`,rx:`2`,key:`6s8ecr`}],[`path`,{d:`M7 10V7a5 5 0 0 1 10 0v3`,key:`1pqi11`}]]),d=o(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),f=o(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),p=r();function m(){let r=i(),o=t(a()).data??null,m=e({mutationFn:()=>s(),onSuccess:e=>{r.setQueryData(a().queryKey,e),Promise.all([r.invalidateQueries({queryKey:a().queryKey}),r.invalidateQueries({queryKey:[`antigravity-conversation`]})])}});if(!o||o.status===`unsupported`)return null;let h=o.isUnlocked,g=m.error?.message??o.error;return(0,p.jsxs)(`div`,{className:c(`flex flex-col gap-3 rounded-xl border px-4 py-3 sm:flex-row sm:items-center sm:justify-between`,h?`border-emerald-500/30 bg-emerald-500/10`:`border-[var(--border)] bg-[var(--panel)]`),children:[(0,p.jsxs)(`div`,{className:`flex min-w-0 items-start gap-3`,children:[(0,p.jsx)(`div`,{className:c(`mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-md`,h?`bg-emerald-500/15 text-emerald-600`:`bg-[var(--panel-secondary)]`),children:h?(0,p.jsx)(d,{className:`size-4`}):(0,p.jsx)(u,{className:`size-4`})}),(0,p.jsxs)(`div`,{className:`min-w-0`,children:[(0,p.jsx)(`p`,{className:`font-medium text-sm`,children:h?`Keychain access enabled`:`Unlock Antigravity transcript export`}),(0,p.jsx)(`p`,{className:`mt-1 text-[var(--muted-foreground)] text-xs`,children:h?`The Antigravity key is cached in this server process only. Transcript exports are available for local logs and safe-storage payloads.`:`Spiracha needs one-time access to ${o.keychainService} to decrypt Antigravity transcript data. macOS will ask for approval after you click unlock.`}),g?(0,p.jsxs)(`p`,{className:`mt-2 flex items-center gap-1 text-[var(--destructive)] text-xs`,children:[(0,p.jsx)(f,{className:`size-3`}),g]}):null]})]}),h?null:(0,p.jsxs)(n,{className:`shrink-0`,disabled:!o.canRequestAccess||m.isPending,type:`button`,onClick:()=>m.mutate(),children:[(0,p.jsx)(l,{className:`size-4`}),m.isPending?`Waiting...`:`Unlock`]})]})}export{u as n,m as t};
@@ -1 +0,0 @@
1
- import{c as e,n as t,o as n,r}from"./formatters-BdnWuM1z.js";import{t as i}from"./useMutation-MZ3Hr9h9.js";import{n as a,t as o}from"./data-table-Cj-v-uyB.js";import{r as s,t as c}from"./download-P3Rp23Ad.js";import{a as l,i as u,n as d,r as f,t as p}from"./dropdown-menu-3qB5j9nt.js";import{n as m,t as h}from"./antigravity-keychain-panel-dYuRWtCf.js";import{t as g}from"./scroll-text-CqaFm9by.js";import{Bt as _,Ft as v,H as y,Lt as b,_ as x,d as S,f as C,g as w,m as T,n as E,p as D,r as O,u as k,v as A,y as j,zt as M}from"./index-BVFnfS78.js";import{t as N}from"./page-header-CnD21cPn.js";import{t as P}from"./badge-aHE9ETVe.js";import{n as F,t as I}from"./text-filter-7M6wRo-t.js";var L=_(M(),1),R=b(),z=a(),B=(e,t)=>{let n=e.artifactCount>0,r=!!t?.isUnlocked,i=A(e)||x(e),a=w(e,r),o=j(e,r),s=a||o;return{canExportConversation:a,hasArtifacts:n,hasTranscript:i,lockedTranscript:o,showActions:s||n,showConversationAction:s}},V=(e,t)=>t.hasTranscript?`${e.transcriptSource?e.transcriptSource.replace(/-/gu,` `):`transcript`} · ${t.lockedTranscript?`locked`:`available`}`:`Summary only`,H=(e,i,a)=>[z.accessor(`title`,{cell:t=>{let n=B(t.row.original,e);return(0,R.jsxs)(v,{className:`block w-[16rem] max-w-[22rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto`,params:{conversationId:t.row.original.conversationId},to:`/antigravity-conversations/$conversationId`,children:[(0,R.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,R.jsx)(`p`,{className:`truncate font-medium underline-offset-2 hover:underline`,children:t.getValue()}),n.hasTranscript?(0,R.jsx)(P,{variant:`secondary`,children:`transcript`}):null,n.hasArtifacts?(0,R.jsx)(P,{variant:`outline`,children:`artifact`}):null]}),(0,R.jsx)(`p`,{className:`truncate text-[var(--muted-foreground)] text-xs`,children:t.row.original.conversationId})]})},header:`Conversation`}),z.accessor(`lastUpdatedAtMs`,{cell:e=>(0,R.jsx)(`span`,{className:`whitespace-nowrap text-sm`,suppressHydrationWarning:!0,children:r(e.getValue())}),header:`Updated`}),z.display({cell:t=>{let r=B(t.row.original,e);return(0,R.jsxs)(`span`,{className:`text-sm`,children:[V(t.row.original,r),t.row.original.transcriptEntryCount>0?` · ${n(t.row.original.transcriptEntryCount)} entries`:``]})},header:`Transcript`,id:`transcript`}),z.accessor(`artifactCount`,{cell:e=>(0,R.jsx)(`span`,{className:`font-mono text-sm`,children:n(e.getValue())}),header:`Artifacts`}),z.accessor(`conversationBytes`,{cell:e=>(0,R.jsx)(`span`,{className:`font-mono text-sm`,children:t(e.getValue())}),header:`Size`}),z.display({cell:t=>{let n=B(t.row.original,e);return n.showActions?(0,R.jsxs)(p,{children:[(0,R.jsx)(u,{asChild:!0,children:(0,R.jsx)(y,{className:`rounded-full`,size:`icon`,type:`button`,variant:`ghost`,onClick:e=>e.stopPropagation(),children:(0,R.jsx)(l,{className:`size-4`})})}),(0,R.jsxs)(d,{align:`end`,children:[n.showConversationAction?(0,R.jsxs)(f,{disabled:!n.canExportConversation,onClick:()=>i(t.row.original),children:[n.lockedTranscript?(0,R.jsx)(m,{className:`mr-2 size-4`}):(0,R.jsx)(s,{className:`mr-2 size-4`}),n.lockedTranscript?`Unlock conversation export first`:`Export conversation`]}):null,n.hasArtifacts?(0,R.jsxs)(f,{onClick:()=>a(t.row.original),children:[(0,R.jsx)(g,{className:`mr-2 size-4`}),`Export artifacts`]}):null]})]}):(0,R.jsx)(`span`,{className:`text-[var(--muted-foreground)] text-sm`,children:`No export`})},header:``,id:`actions`})];function U({conversations:e,decryptionState:t,onExportArtifacts:n,onExportConversation:r}){return(0,R.jsx)(o,{columns:(0,L.useMemo)(()=>H(t,r,n),[t,n,r]),data:e,emptyMessage:`No Antigravity conversations match the current workspace filter.`})}function W(){let t=E.useParams(),n=e(C()).data,r=O(n,t.workspaceKey),a=e(k(r.key)).data,o=e(S()).data??null,[s,l]=(0,L.useState)(``),u=(0,L.useDeferredValue)(s),d=i({mutationFn:e=>T({data:{conversationId:e.conversationId}}),onSuccess:e=>{c(e.filename,e.content,`text/markdown; charset=utf-8`)}}),f=i({mutationFn:e=>D({data:{conversationId:e.conversationId}}),onSuccess:e=>{c(e.filename,e.content,`text/markdown; charset=utf-8`)}}),p=a.filter(e=>I(u,[e.title,e.conversationId,e.transcriptSource,e.workspaceLabel]));return(0,R.jsxs)(`div`,{className:`space-y-6`,children:[(0,R.jsx)(N,{actions:(0,R.jsx)(F,{placeholder:`Search title, id, or transcript source`,value:s,onValueChange:l}),eyebrow:`Antigravity workspace`,subtitle:`Inspect conversation coverage across Antigravity transcripts, raw payloads, and generated artifacts.`,title:r.label}),(0,R.jsx)(h,{}),(0,R.jsx)(U,{conversations:p,decryptionState:o,onExportArtifacts:e=>f.mutate(e),onExportConversation:e=>d.mutate(e)}),d.isError?(0,R.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:d.error instanceof Error?d.error.message:`Conversation export failed`}):null,f.isError?(0,R.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:f.error instanceof Error?f.error.message:`Artifact export failed`}):null]})}export{W as component};
@@ -1 +0,0 @@
1
- import{Lt as e}from"./index-BVFnfS78.js";import{t}from"./reload-error-panel-DLAg0AW2.js";var n=e();function r({error:e}){return(0,n.jsx)(t,{description:e.message,title:`Failed to load Antigravity workspace`})}export{r as errorComponent};
@@ -1 +0,0 @@
1
- import{c as e,n as t,o as n,r}from"./formatters-BdnWuM1z.js";import{n as i,t as a}from"./data-table-Cj-v-uyB.js";import{t as o}from"./antigravity-keychain-panel-dYuRWtCf.js";import{Bt as s,Ft as c,Lt as l,f as u,zt as d}from"./index-BVFnfS78.js";import{t as f}from"./page-header-CnD21cPn.js";import{n as p,t as m}from"./text-filter-7M6wRo-t.js";var h=s(d()),g=l(),_=i(),v=[_.accessor(`label`,{cell:e=>(0,g.jsxs)(c,{className:`block w-[16rem] max-w-[22rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto`,params:{workspaceKey:e.row.original.key},to:`/antigravity/$workspaceKey`,children:[(0,g.jsx)(`p`,{className:`font-medium underline-offset-2 hover:underline`,children:e.getValue()}),(0,g.jsx)(`p`,{className:`truncate text-[var(--muted-foreground)] text-xs`,children:e.row.original.uri??`Unknown root`})]}),header:`Workspace`}),_.accessor(`conversationCount`,{cell:e=>(0,g.jsx)(`span`,{className:`font-mono text-sm`,children:n(e.getValue())}),header:`Conversations`}),_.accessor(`transcriptCount`,{cell:e=>(0,g.jsx)(`span`,{className:`font-mono text-sm`,children:n(e.getValue())}),header:`Transcripts`}),_.accessor(`artifactCount`,{cell:e=>(0,g.jsx)(`span`,{className:`font-mono text-sm`,children:n(e.getValue())}),header:`Artifacts`}),_.accessor(`conversationBytes`,{cell:e=>(0,g.jsx)(`span`,{className:`font-mono text-sm`,children:t(e.getValue())}),header:`Size`}),_.accessor(`lastActiveMs`,{cell:e=>(0,g.jsx)(`span`,{className:`whitespace-nowrap text-sm`,suppressHydrationWarning:!0,children:r(e.getValue())}),header:`Last updated`})];function y({workspaces:e}){return(0,g.jsx)(a,{columns:v,data:e,emptyMessage:`No Antigravity workspaces match the current search.`})}function b(){let t=e(u()).data,[n,r]=(0,h.useState)(``),i=(0,h.useDeferredValue)(n),a=t.filter(e=>m(i,[e.label,e.uri,e.key]));return(0,g.jsxs)(`div`,{className:`space-y-6`,children:[(0,g.jsx)(f,{actions:(0,g.jsx)(p,{placeholder:`Search workspace name or path`,value:n,onValueChange:r}),eyebrow:`Inventory`,subtitle:`Workspace groups are derived from Antigravity summary indexes, raw conversation files, local logs, and brain artifacts.`,title:`Antigravity`}),(0,g.jsx)(o,{}),(0,g.jsx)(y,{workspaces:a})]})}export{b as component};
@@ -1 +0,0 @@
1
- import{Lt as e}from"./index-BVFnfS78.js";import{t}from"./reload-error-panel-DLAg0AW2.js";var n=e();function r({error:e}){return(0,n.jsx)(t,{description:e.message,title:`Failed to load Antigravity workspaces`})}export{r as errorComponent};
@@ -1 +0,0 @@
1
- import{Lt as e,ct as t,mt as n,pt as r}from"./index-BVFnfS78.js";var i=e(),a=r(`inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3`,{defaultVariants:{variant:`default`},variants:{variant:{default:`bg-primary text-primary-foreground [a&]:hover:bg-primary/90`,destructive:`bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90`,ghost:`[a&]:hover:bg-accent [a&]:hover:text-accent-foreground`,link:`text-primary underline-offset-4 [a&]:hover:underline`,outline:`border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground`,secondary:`bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90`}}});function o({className:e,variant:r=`default`,asChild:o=!1,...s}){return(0,i.jsx)(o?t:`span`,{"data-slot":`badge`,"data-variant":r,className:n(a({variant:r}),e),...s})}export{o as t};
@@ -1 +0,0 @@
1
- import{$ as e,Bt as t,J as n,Lt as r,et as i,ft as a,gt as o,it as s,mt as c,nt as l,ot as u,zt as d}from"./index-BVFnfS78.js";var f=o(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),p=t(d(),1);function m(e){let t=p.useRef({value:e,previous:e});return p.useMemo(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}var h=r(),g=`Checkbox`,[_,v]=s(g),[y,b]=_(g);function x(e){let{__scopeCheckbox:t,checked:n,children:r,defaultChecked:a,disabled:o,form:s,name:c,onCheckedChange:l,required:u,value:d=`on`,internal_do_not_use_render:f}=e,[m,_]=i({prop:n,defaultProp:a??!1,onChange:l,caller:g}),[v,b]=p.useState(null),[x,S]=p.useState(null),C=p.useRef(!1),w=v?!!s||!!v.closest(`form`):!0,T={checked:m,disabled:o,setChecked:_,control:v,setControl:b,name:c,form:s,value:d,hasConsumerStoppedPropagationRef:C,required:u,defaultChecked:A(a)?!1:a,isFormControl:w,bubbleInput:x,setBubbleInput:S};return(0,h.jsx)(y,{scope:t,...T,children:k(f)?f(T):r})}var S=`CheckboxTrigger`,C=p.forwardRef(({__scopeCheckbox:e,onKeyDown:t,onClick:n,...r},i)=>{let{control:o,value:s,disabled:c,checked:d,required:f,setControl:m,setChecked:g,hasConsumerStoppedPropagationRef:_,isFormControl:v,bubbleInput:y}=b(S,e),x=a(i,m),C=p.useRef(d);return p.useEffect(()=>{let e=o?.form;if(e){let t=()=>g(C.current);return e.addEventListener(`reset`,t),()=>e.removeEventListener(`reset`,t)}},[o,g]),(0,h.jsx)(u.button,{type:`button`,role:`checkbox`,"aria-checked":A(d)?`mixed`:d,"aria-required":f,"data-state":j(d),"data-disabled":c?``:void 0,disabled:c,value:s,...r,ref:x,onKeyDown:l(t,e=>{e.key===`Enter`&&e.preventDefault()}),onClick:l(n,e=>{g(e=>A(e)?!0:!e),y&&v&&(_.current=e.isPropagationStopped(),_.current||e.stopPropagation())})})});C.displayName=S;var w=p.forwardRef((e,t)=>{let{__scopeCheckbox:n,name:r,checked:i,defaultChecked:a,required:o,disabled:s,value:c,onCheckedChange:l,form:u,...d}=e;return(0,h.jsx)(x,{__scopeCheckbox:n,checked:i,defaultChecked:a,disabled:s,required:o,onCheckedChange:l,name:r,form:u,value:c,internal_do_not_use_render:({isFormControl:e})=>(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(C,{...d,ref:t,__scopeCheckbox:n}),e&&(0,h.jsx)(O,{__scopeCheckbox:n})]})})});w.displayName=g;var T=`CheckboxIndicator`,E=p.forwardRef((t,n)=>{let{__scopeCheckbox:r,forceMount:i,...a}=t,o=b(T,r);return(0,h.jsx)(e,{present:i||A(o.checked)||o.checked===!0,children:(0,h.jsx)(u.span,{"data-state":j(o.checked),"data-disabled":o.disabled?``:void 0,...a,ref:n,style:{pointerEvents:`none`,...t.style}})})});E.displayName=T;var D=`CheckboxBubbleInput`,O=p.forwardRef(({__scopeCheckbox:e,...t},r)=>{let{control:i,hasConsumerStoppedPropagationRef:o,checked:s,defaultChecked:c,required:l,disabled:d,name:f,value:g,form:_,bubbleInput:v,setBubbleInput:y}=b(D,e),x=a(r,y),S=m(s),C=n(i);p.useEffect(()=>{let e=v;if(!e)return;let t=window.HTMLInputElement.prototype,n=Object.getOwnPropertyDescriptor(t,`checked`).set,r=!o.current;if(S!==s&&n){let t=new Event(`click`,{bubbles:r});e.indeterminate=A(s),n.call(e,A(s)?!1:s),e.dispatchEvent(t)}},[v,S,s,o]);let w=p.useRef(A(s)?!1:s);return(0,h.jsx)(u.input,{type:`checkbox`,"aria-hidden":!0,defaultChecked:c??w.current,required:l,disabled:d,name:f,value:g,form:_,...t,tabIndex:-1,ref:x,style:{...t.style,...C,position:`absolute`,pointerEvents:`none`,opacity:0,margin:0,transform:`translateX(-100%)`}})});O.displayName=D;function k(e){return typeof e==`function`}function A(e){return e===`indeterminate`}function j(e){return A(e)?`indeterminate`:e?`checked`:`unchecked`}function M({className:e,...t}){return(0,h.jsx)(w,{"data-slot":`checkbox`,className:c(`peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs outline-none transition-shadow focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-white dark:bg-input/30 dark:data-[state=checked]:bg-primary dark:data-[state=checked]:text-primary-foreground dark:aria-invalid:ring-destructive/40`,e),...t,children:(0,h.jsx)(E,{"data-slot":`checkbox-indicator`,className:`grid place-content-center text-current transition-none`,children:(0,h.jsx)(f,{className:`size-3.5`})})})}export{m as n,f as r,M as t};
@@ -1 +0,0 @@
1
- import{c as e,n as t,o as n,r}from"./formatters-BdnWuM1z.js";import{t as i}from"./useMutation-MZ3Hr9h9.js";import{n as a,t as o}from"./metadata-section-jnIkB7dB.js";import{r as s,t as c}from"./download-P3Rp23Ad.js";import{l,t as u}from"./delete-confirm-dialog-DTpzBiNK.js";import{t as d}from"./export-dialog-CazdrASq.js";import{Bt as f,C as p,Ft as m,H as h,It as g,Lt as _,T as v,_t as y,b,i as x,zt as S}from"./index-BVFnfS78.js";import{t as C}from"./metric-card-CBZuWLzQ.js";import{t as w}from"./page-header-CnD21cPn.js";import{t as T}from"./text-document-panel-DPleOmmq.js";import{t as E}from"./json-panel-DLkS30sQ.js";var D=f(S()),O=_(),k=e=>[{label:`Composer ID`,value:(0,O.jsx)(`span`,{"data-mono":`true`,children:e.thread.composerId})},{label:`Workspace`,value:(0,O.jsx)(m,{className:`text-[var(--accent)]`,params:{workspaceKey:e.thread.workspaceKey},to:`/cursor/$workspaceKey`,children:e.thread.workspaceLabel})},{label:`Workspace key`,value:(0,O.jsx)(`span`,{"data-mono":`true`,children:e.thread.workspaceKey})},{label:`Mode`,value:e.thread.mode??`unknown`},{label:`Created`,value:(0,O.jsx)(`span`,{suppressHydrationWarning:!0,children:r(e.thread.createdAtMs)})},{label:`Updated`,value:(0,O.jsx)(`span`,{suppressHydrationWarning:!0,children:r(e.thread.lastUpdatedAtMs)})},{label:`Transcript dirs`,value:e.thread.transcriptDirs.length>0?(0,O.jsx)(`div`,{className:`space-y-1`,children:e.thread.transcriptDirs.map(e=>(0,O.jsx)(`div`,{"data-mono":`true`,children:e},e))}):`n/a`}],A=e=>e===`md`?`text/markdown; charset=utf-8`:`text/plain; charset=utf-8`,j=()=>{let r=g(),f=y(),m=e(b(x.useParams().composerId)).data,[_,S]=(0,D.useState)(!1),[j,M]=(0,D.useState)(!1),N=i({mutationFn:()=>p({data:{composerIds:[m.thread.composerId]}}),onSuccess:async()=>{await r({params:{workspaceKey:m.thread.workspaceKey},to:`/cursor/$workspaceKey`}),await Promise.all([f.invalidateQueries({queryKey:[`cursor-thread`,m.thread.composerId]}),f.invalidateQueries({queryKey:[`cursor-threads`,m.thread.workspaceKey]}),f.invalidateQueries({queryKey:[`cursor-workspaces`]})])}}),P=i({mutationFn:async e=>{let t=await v({data:{...e,composerId:m.thread.composerId}});c(t.filename,t.content,A(e.outputFormat))},onSuccess:()=>{M(!1)}});return(0,O.jsxs)(`div`,{className:`space-y-6`,children:[(0,O.jsx)(w,{actions:(0,O.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,O.jsxs)(h,{className:`rounded-full`,type:`button`,variant:`outline`,onClick:()=>M(!0),children:[(0,O.jsx)(s,{className:`mr-2 size-4`}),`Export`]}),(0,O.jsxs)(h,{className:`rounded-full border-[var(--destructive)]/20 text-[var(--destructive)]`,type:`button`,variant:`outline`,onClick:()=>S(!0),children:[(0,O.jsx)(l,{className:`mr-2 size-4`}),`Delete`]})]}),breadcrumb:(0,O.jsx)(a,{items:[{label:`Cursor`,to:`/cursor`},{label:m.thread.workspaceLabel,params:{workspaceKey:m.thread.workspaceKey},to:`/cursor/$workspaceKey`},{label:m.thread.name}]}),eyebrow:`Cursor thread`,subtitle:`Thread detail for the selected Cursor workspace conversation.`,title:m.thread.name}),(0,O.jsxs)(`div`,{className:`grid gap-3 md:grid-cols-2 xl:grid-cols-4`,children:[(0,O.jsx)(C,{label:`Messages`,value:n(m.thread.bubbleCount)}),(0,O.jsx)(C,{label:`Size`,value:t(m.thread.bubbleBytes)}),(0,O.jsx)(C,{label:`Mode`,value:m.thread.mode??`unknown`}),(0,O.jsx)(C,{helper:`${n(m.transcript?.renderableBubbleCount??0)} renderable`,label:`Omitted`,value:n(m.transcript?.omittedBubbleCount??0)})]}),(0,O.jsxs)(`div`,{className:`grid gap-4 xl:grid-cols-[0.95fr_1.05fr]`,children:[(0,O.jsx)(o,{items:k(m),title:`Thread metadata`}),m.renderedTranscript?(0,O.jsx)(T,{content:m.renderedTranscript,description:`Rendered with commentary and tool calls enabled.`,title:`Transcript`}):(0,O.jsxs)(`section`,{className:`rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]`,children:[(0,O.jsx)(`h3`,{className:`font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]`,children:`Transcript`}),(0,O.jsx)(`p`,{className:`mt-4 text-[var(--muted-foreground)] text-sm`,children:`No renderable Cursor transcript content was found for this thread.`})]})]}),m.transcript?(0,O.jsx)(E,{title:`Raw transcript`,value:m.transcript}):null,N.isError?(0,O.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:N.error instanceof Error?N.error.message:`Delete failed`}):null,P.isError?(0,O.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:P.error instanceof Error?P.error.message:`Export failed`}):null,(0,O.jsx)(u,{confirmLabel:N.isPending?`Deleting...`:`Delete thread`,description:`Permanently delete "${m.thread.name}" from Cursor's database and remove any on-disk transcript directories. Quit Cursor first. This cannot be undone.`,open:_,title:`Delete Cursor thread?`,onConfirm:()=>N.mutate(),onOpenChange:S}),(0,O.jsx)(d,{open:j,pending:P.isPending,title:`Export ${m.thread.name}`,onExport:e=>P.mutate(e),onOpenChange:M})]})};export{j as component};
@@ -1 +0,0 @@
1
- import{Lt as e}from"./index-BVFnfS78.js";import{t}from"./reload-error-panel-DLAg0AW2.js";var n=e(),r=({error:e})=>(0,n.jsx)(t,{description:e.message,title:`Failed to load Cursor thread`});export{r as errorComponent};
@@ -1 +0,0 @@
1
- import{Lt as e}from"./index-BVFnfS78.js";import{t}from"./reload-error-panel-DLAg0AW2.js";var n=e(),r=({error:e})=>(0,n.jsx)(t,{description:e.message,title:`Failed to load Cursor workspace`});export{r as errorComponent};
@@ -1 +0,0 @@
1
- import{c as e,n as t,o as n,r}from"./formatters-BdnWuM1z.js";import{t as i}from"./useMutation-MZ3Hr9h9.js";import{n as a,t as o}from"./data-table-Cj-v-uyB.js";import{r as s,t as c}from"./download-P3Rp23Ad.js";import{a as l,i as u,n as d,r as f,t as p}from"./dropdown-menu-3qB5j9nt.js";import{t as m}from"./refresh-ccw-BDrYXjtD.js";import{l as h,t as g}from"./delete-confirm-dialog-DTpzBiNK.js";import{n as _,t as v}from"./export-dialog-CazdrASq.js";import{Bt as y,C as b,D as x,E as S,Ft as C,H as w,It as T,Lt as E,S as D,T as O,_t as k,a as A,o as j,w as M,x as N,zt as P}from"./index-BVFnfS78.js";import{t as F}from"./page-header-CnD21cPn.js";import{n as I,t as L}from"./text-filter-7M6wRo-t.js";var R=y(P(),1),z=E(),B=a(),V=[{desc:!0,id:`updatedAt`}],H=(e,i)=>[B.accessor(`name`,{cell:e=>(0,z.jsxs)(C,{className:`block w-[16rem] max-w-[20rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto`,params:{composerId:e.row.original.composerId},to:`/cursor-threads/$composerId`,children:[(0,z.jsx)(`p`,{className:`truncate font-medium underline-offset-2 hover:underline`,children:e.getValue()}),(0,z.jsxs)(`p`,{className:`truncate text-[var(--muted-foreground)] text-xs`,children:[e.row.original.mode?`${e.row.original.mode} · `:``,e.row.original.composerId]})]}),header:`Thread`}),B.accessor(`lastUpdatedAtMs`,{cell:e=>(0,z.jsx)(`span`,{className:`whitespace-nowrap text-sm`,suppressHydrationWarning:!0,children:r(e.getValue())}),header:`Updated`,id:`updatedAt`}),B.accessor(`createdAtMs`,{cell:e=>(0,z.jsx)(`span`,{className:`whitespace-nowrap text-sm`,suppressHydrationWarning:!0,children:r(e.getValue())}),header:`Created`,id:`createdAt`}),B.accessor(`mode`,{cell:e=>(0,z.jsx)(`span`,{className:`font-mono text-sm`,children:e.getValue()??`unknown`}),header:`Mode`}),B.accessor(`bubbleCount`,{cell:e=>(0,z.jsx)(`span`,{className:`font-mono text-sm`,children:n(e.getValue())}),header:`Messages`}),B.accessor(`bubbleBytes`,{cell:e=>(0,z.jsx)(`span`,{className:`font-mono text-sm`,children:t(e.getValue())}),header:`Size`}),B.display({cell:t=>(0,z.jsxs)(p,{children:[(0,z.jsx)(u,{asChild:!0,children:(0,z.jsx)(w,{"aria-label":`Actions for ${t.row.original.name}`,className:`rounded-full`,size:`icon`,type:`button`,variant:`ghost`,onClick:e=>e.stopPropagation(),children:(0,z.jsx)(l,{className:`size-4`})})}),(0,z.jsxs)(d,{align:`end`,children:[(0,z.jsxs)(f,{disabled:t.row.original.bubbleCount===0,onClick:()=>i(t.row.original),children:[(0,z.jsx)(s,{className:`mr-2 size-4`}),`Export thread`]}),(0,z.jsxs)(f,{className:`text-[var(--destructive)]`,onClick:()=>e(t.row.original),children:[(0,z.jsx)(h,{className:`mr-2 size-4`}),`Delete thread`]})]})]}),enableSorting:!1,header:``,id:`actions`})],U=({onDeleteThread:e,onDeleteThreads:t,onExportThread:n,onExportThreads:r,threads:i})=>(0,z.jsx)(o,{columns:(0,R.useMemo)(()=>H(e,n),[e,n]),data:i,emptyMessage:`No Cursor threads match the current workspace filter.`,enableRowSelection:!0,getRowId:e=>e.composerId,initialSorting:V,renderToolbar:({clearSelection:e,selectedRows:n})=>{if(n.length===0)return(0,z.jsx)(`p`,{className:`text-[var(--muted-foreground)] text-sm`,children:`Select threads to export or delete them in a batch.`});let i=n.map(e=>e.composerId),a=n.some(e=>e.bubbleCount===0);return(0,z.jsxs)(`div`,{className:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[(0,z.jsxs)(`p`,{className:`text-sm`,children:[n.length,` thread`,n.length===1?``:`s`,` selected`]}),(0,z.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,z.jsxs)(w,{className:`rounded-full`,disabled:a,size:`sm`,type:`button`,variant:`outline`,onClick:()=>r(i),children:[(0,z.jsx)(s,{className:`mr-2 size-4`}),`Export selected threads`]}),(0,z.jsxs)(w,{className:`rounded-full border-[var(--destructive)]/20 text-[var(--destructive)]`,size:`sm`,type:`button`,variant:`outline`,onClick:()=>t(i),children:[(0,z.jsx)(h,{className:`mr-2 size-4`}),`Delete selected threads`]}),(0,z.jsxs)(w,{className:`rounded-full`,size:`sm`,type:`button`,variant:`ghost`,onClick:e,children:[(0,z.jsx)(_,{className:`mr-2 size-4`}),`Clear selection`]})]})]})}}),W=(e,t)=>{let n=new Set(t);return e.filter(e=>n.has(e.composerId))},G=e=>e.length===0?null:{kind:`threads`,threads:e},K=e=>e.length===0?null:{composerIds:e.map(e=>e.composerId),label:e.length===1?e[0].name:`${e.length} selected threads`},q=(e,t)=>t?`Deleting...`:e?.kind===`workspace`?`Delete workspace`:e&&e.threads.length>1?`Delete threads`:`Delete thread`,J=e=>e?e.kind===`workspace`?`Permanently delete every thread for "${e.workspace.label}" from Cursor's database and remove any on-disk transcript directories. Quit Cursor first. This cannot be undone.`:e.threads.length===1?`Permanently delete "${e.threads[0].name}" from Cursor's database and remove any on-disk transcript directories. Quit Cursor first. This cannot be undone.`:`Permanently delete ${e.threads.length} selected Cursor threads and remove any on-disk transcript directories. Quit Cursor first. This cannot be undone.`:``,Y=e=>e?.kind===`workspace`?`Delete Cursor workspace?`:e&&e.threads.length>1?`Delete ${e.threads.length} Cursor threads?`:`Delete Cursor thread?`,X=e=>e===`md`?`text/markdown; charset=utf-8`:`text/plain; charset=utf-8`,Z=()=>{let t=T(),n=A.useParams(),r=k(),a=e(D()).data,o=j(a,n.workspaceKey),s=e(N(o.key)).data,[l,u]=(0,R.useState)(``),[d,f]=(0,R.useState)(null),[p,m]=(0,R.useState)(null),h=(0,R.useDeferredValue)(l),g=async()=>{await Promise.all([r.invalidateQueries({queryKey:[`cursor-workspaces`]}),r.invalidateQueries({queryKey:[`cursor-threads`,o.key]})])},_=i({mutationFn:()=>x({data:{apply:!0,workspaceKey:o.key}}),onSuccess:g}),y=i({mutationFn:e=>e.kind===`workspace`?M({data:{workspaceKey:e.workspace.key}}):b({data:{composerIds:e.threads.map(e=>e.composerId)}}),onSuccess:async(e,n)=>{if(n.kind===`workspace`){await t({to:`/cursor`}),await r.invalidateQueries({queryKey:[`cursor-workspaces`]}),f(null);return}await g(),f(null)}}),C=i({mutationFn:async e=>{if(!p)throw Error(`No thread selected for export`);let t=p.composerIds.length===1?await O({data:{...e,composerId:p.composerIds[0]}}):await S({data:{...e,composerIds:p.composerIds}});c(t.filename,t.content,X(e.outputFormat))},onSuccess:()=>{m(null)}}),w=s.filter(e=>L(h,[e.name,e.composerId,e.mode,e.workspaceLabel]));return(0,z.jsxs)(`div`,{className:`space-y-6`,children:[(0,z.jsx)(F,{actions:(0,z.jsx)(Q,{deletePending:y.isPending,recoverPending:_.isPending,searchInput:l,workspace:o,onDeleteWorkspace:()=>f({kind:`workspace`,workspace:o}),onRecoverWorkspace:()=>_.mutate(),onSearchInputChange:u}),eyebrow:`Cursor workspace`,subtitle:`Sort by any column, export thread transcripts, delete stale records, or repair split storage buckets for this workspace.`,title:o.label}),(0,z.jsx)($,{workspace:o}),(0,z.jsx)(U,{onDeleteThread:e=>f({kind:`threads`,threads:[e]}),onDeleteThreads:e=>{let t=G(W(w,e));t&&f(t)},onExportThread:e=>m({composerIds:[e.composerId],label:e.name}),onExportThreads:e=>{let t=K(W(w,e));t&&m(t)},threads:w}),(0,z.jsx)(ee,{deleteError:y.isError?y.error:null,exportError:C.isError?C.error:null,recoverError:_.isError?_.error:null}),(0,z.jsx)(te,{pending:y.isPending,pendingDelete:d,onConfirm:()=>{d&&y.mutate(d)},onOpenChange:e=>{e||f(null)}}),(0,z.jsx)(v,{open:p!==null,pending:C.isPending,title:p?`Export ${p.label}`:`Export thread`,onExport:e=>C.mutate(e),onOpenChange:e=>{e||m(null)}})]})},Q=({deletePending:e,recoverPending:t,searchInput:n,workspace:r,onDeleteWorkspace:i,onRecoverWorkspace:a,onSearchInputChange:o})=>(0,z.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row`,children:[r.needsRecovery?(0,z.jsxs)(w,{className:`rounded-full`,disabled:t,type:`button`,variant:`outline`,onClick:a,children:[(0,z.jsx)(m,{className:`mr-2 size-4`}),t?`Recovering...`:`Recover`]}):null,(0,z.jsxs)(w,{className:`rounded-full`,disabled:e,type:`button`,variant:`outline`,onClick:i,children:[(0,z.jsx)(h,{className:`mr-2 size-4`}),`Delete workspace`]}),(0,z.jsx)(I,{placeholder:`Search thread name, id, or mode`,value:n,onValueChange:o})]}),$=({workspace:e})=>e.needsRecovery?(0,z.jsxs)(`div`,{className:`rounded-xl border border-[var(--border)] bg-[var(--panel)] px-4 py-4`,children:[(0,z.jsx)(`p`,{className:`font-medium text-sm`,children:`This workspace has threads in an older storage bucket.`}),(0,z.jsx)(`p`,{className:`mt-1 text-[var(--muted-foreground)] text-xs`,children:`Recover merges those threads into the active bucket Cursor reads now so they reappear in Chat History. Quit Cursor before running it.`})]}):null,ee=({deleteError:e,exportError:t,recoverError:n})=>{let r=[n?n.message:null,e?e.message:null,t?t.message:null].filter(Boolean);return r.length===0?null:(0,z.jsx)(`div`,{className:`space-y-1`,children:r.map(e=>(0,z.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:e},e))})},te=({pending:e,pendingDelete:t,onConfirm:n,onOpenChange:r})=>(0,z.jsx)(g,{confirmLabel:q(t,e),description:J(t),open:t!==null,title:Y(t),onConfirm:n,onOpenChange:r});export{Z as component};
@@ -1 +0,0 @@
1
- import{Lt as e}from"./index-BVFnfS78.js";import{t}from"./reload-error-panel-DLAg0AW2.js";var n=e(),r=({error:e})=>(0,n.jsx)(t,{description:e.message,title:`Failed to load Cursor workspaces`});export{r as errorComponent};
@@ -1,2 +0,0 @@
1
- import{c as e,o as t,r as n}from"./formatters-BdnWuM1z.js";import{t as r}from"./useMutation-MZ3Hr9h9.js";import{n as i,t as a}from"./data-table-Cj-v-uyB.js";import{a as o,i as s,n as c,r as l,t as u}from"./dropdown-menu-3qB5j9nt.js";import{t as d}from"./refresh-ccw-BDrYXjtD.js";import{l as f,t as p}from"./delete-confirm-dialog-DTpzBiNK.js";import{Bt as m,D as h,Ft as g,H as _,Lt as v,S as y,_t as b,w as x,zt as S}from"./index-BVFnfS78.js";import{t as C}from"./page-header-CnD21cPn.js";import{t as w}from"./badge-aHE9ETVe.js";import{n as T,t as E}from"./text-filter-7M6wRo-t.js";var D=m(S()),O=v(),k=i(),A=e=>e.folders[0]??e.uri,j=e=>e.buckets.length===0?`File history`:`${t(e.buckets.length)} bucket${e.buckets.length===1?``:`s`}`,M=e=>e.needsRecovery?`Recovery available`:e.buckets.length===0?`Activity only`:`Current`,N=(e,r)=>[k.accessor(`label`,{cell:e=>(0,O.jsxs)(g,{className:`block w-[16rem] max-w-[22rem] space-y-1 rounded-md outline-none transition hover:opacity-80 focus-visible:ring-2 focus-visible:ring-[var(--accent)] lg:w-auto`,params:{workspaceKey:e.row.original.key},to:`/cursor/$workspaceKey`,children:[(0,O.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,O.jsx)(`p`,{className:`truncate font-medium underline-offset-2 hover:underline`,children:e.getValue()}),e.row.original.needsRecovery?(0,O.jsx)(w,{variant:`outline`,children:`recover`}):null]}),(0,O.jsx)(`p`,{className:`truncate text-[var(--muted-foreground)] text-xs`,children:A(e.row.original)})]}),header:`Workspace`}),k.accessor(`threadCount`,{cell:e=>(0,O.jsx)(`span`,{className:`font-mono text-sm`,children:t(e.getValue())}),header:`Threads`}),k.display({cell:e=>(0,O.jsx)(`span`,{className:`text-sm`,children:j(e.row.original)}),header:`Storage`,id:`storage`}),k.display({cell:e=>(0,O.jsx)(`span`,{className:`text-sm`,children:M(e.row.original)}),header:`State`,id:`state`}),k.accessor(`lastActiveMs`,{cell:e=>(0,O.jsx)(`span`,{className:`whitespace-nowrap text-sm`,suppressHydrationWarning:!0,children:n(e.getValue())}),header:`Last updated`}),k.display({cell:t=>(0,O.jsxs)(u,{children:[(0,O.jsx)(s,{asChild:!0,children:(0,O.jsx)(_,{"aria-label":`Actions for ${t.row.original.label}`,className:`rounded-full`,size:`icon`,type:`button`,variant:`ghost`,onClick:e=>e.stopPropagation(),children:(0,O.jsx)(o,{className:`size-4`})})}),(0,O.jsxs)(c,{align:`end`,children:[t.row.original.needsRecovery?(0,O.jsxs)(l,{onClick:()=>r(t.row.original),children:[(0,O.jsx)(d,{className:`mr-2 size-4`}),`Recover workspace`]}):null,(0,O.jsxs)(l,{className:`text-[var(--destructive)]`,onClick:()=>e(t.row.original),children:[(0,O.jsx)(f,{className:`mr-2 size-4`}),`Delete workspace`]})]})]}),header:``,id:`actions`})],P=({onDeleteWorkspace:e,onRecoverWorkspace:t,workspaces:n})=>(0,O.jsx)(a,{columns:N(e,t),data:n,emptyMessage:`No Cursor workspaces match the current search.`}),F=()=>{let t=b(),n=e(y()).data,[i,a]=(0,D.useState)(``),[o,s]=(0,D.useState)(null),c=(0,D.useDeferredValue)(i),l=async()=>{await t.invalidateQueries({queryKey:[`cursor-workspaces`]})},u=r({mutationFn:e=>h({data:{apply:!0,workspaceKey:e.key}}),onSuccess:l}),d=r({mutationFn:e=>x({data:{workspaceKey:e.key}}),onSuccess:async()=>{await l(),s(null)}}),f=n.filter(e=>E(c,[e.label,e.uri,e.folders.join(`
2
- `),e.kind]));return(0,O.jsxs)(`div`,{className:`space-y-6`,children:[(0,O.jsx)(C,{actions:(0,O.jsx)(T,{placeholder:`Search workspace name or path`,value:i,onValueChange:a}),eyebrow:`Inventory`,subtitle:`Workspace groups are derived from Cursor storage buckets and activity metadata. Open a workspace to inspect, export, recover, or delete its threads.`,title:`Cursor`}),(0,O.jsx)(P,{onDeleteWorkspace:s,onRecoverWorkspace:e=>u.mutate(e),workspaces:f}),u.isError?(0,O.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:u.error instanceof Error?u.error.message:`Workspace recovery failed`}):null,d.isError?(0,O.jsx)(`p`,{className:`text-[var(--destructive)] text-sm`,children:d.error instanceof Error?d.error.message:`Workspace deletion failed`}):null,(0,O.jsx)(p,{confirmLabel:d.isPending?`Deleting...`:`Delete workspace`,description:o?`Permanently delete every thread for "${o.label}" from Cursor's database and remove any on-disk transcript directories. Quit Cursor first. This cannot be undone.`:``,open:o!==null,title:`Delete Cursor workspace?`,onConfirm:()=>{o&&d.mutate(o)},onOpenChange:e=>{e||s(null)}})]})};export{F as component};