spiracha 1.1.1 → 1.2.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 (51) hide show
  1. package/AGENTS.md +13 -7
  2. package/README.md +6 -2
  3. package/apps/ui/AGENTS.md +2 -0
  4. package/apps/ui/README.md +3 -1
  5. package/apps/ui/dist/client/assets/{analytics-CqWZmyV6.js → analytics-Cv0JMDN2.js} +1 -1
  6. package/apps/ui/dist/client/assets/{checkbox-DXM4lkJq.js → checkbox-DjHij7DJ.js} +1 -1
  7. package/apps/ui/dist/client/assets/{data-table-DnPYMPCD.js → data-table-Bgnh7phF.js} +1 -1
  8. package/apps/ui/dist/client/assets/{delete-confirm-dialog-CcZaRX33.js → delete-confirm-dialog-CIZy_LXD.js} +1 -1
  9. package/apps/ui/dist/client/assets/download-DQtfva4z.js +1 -0
  10. package/apps/ui/dist/client/assets/{es2015-Bm0kEzx2.js → es2015-DsDKdYCE.js} +1 -1
  11. package/apps/ui/dist/client/assets/{formatters-C12LmYaa.js → formatters-CWFrMKSn.js} +1 -1
  12. package/apps/ui/dist/client/assets/{index-DdJ7ahIt.js → index-C_-e0lDI.js} +2 -2
  13. package/apps/ui/dist/client/assets/{input-CEsI7EpI.js → input-BbgApiqZ.js} +1 -1
  14. package/apps/ui/dist/client/assets/{metric-card-9jwBF7rG.js → metric-card-BJX5rkHK.js} +1 -1
  15. package/apps/ui/dist/client/assets/{page-header-Dr_h1CVv.js → page-header-ODLuGLAB.js} +1 -1
  16. package/apps/ui/dist/client/assets/{projects._project-uyNGnpjH.js → projects._project-C2Pys_bB.js} +1 -1
  17. package/apps/ui/dist/client/assets/{projects._project-zoM8d2nH.js → projects._project-CHvAKvlu.js} +1 -1
  18. package/apps/ui/dist/client/assets/{projects.index-D1CWVN-O.js → projects.index-BmwtS1x-.js} +1 -1
  19. package/apps/ui/dist/client/assets/{projects.index-DukMuny6.js → projects.index-CuLw73mt.js} +1 -1
  20. package/apps/ui/dist/client/assets/{routes-Gr2Wwh83.js → routes-CfnaTOlj.js} +1 -1
  21. package/apps/ui/dist/client/assets/{select-CFim44gT.js → select-B1kH_5lx.js} +1 -1
  22. package/apps/ui/dist/client/assets/{settings-DqhyDxo2.js → settings-mYTB3sso.js} +1 -1
  23. package/apps/ui/dist/client/assets/{threads._threadId-DT75NiBa.js → threads._threadId-CUiCZSwo.js} +1 -1
  24. package/apps/ui/dist/client/assets/{threads._threadId-Df5VXIuZ.js → threads._threadId-C_47okme.js} +1 -1
  25. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-kj_QB_26.js +99 -0
  26. package/apps/ui/dist/server/assets/{analytics-BMxW_bZL.js → analytics-2QpLKjlG.js} +1 -1
  27. package/apps/ui/dist/server/assets/{codex-queries-CAF6HYiG.js → codex-queries-BH4Cb0v3.js} +2 -2
  28. package/apps/ui/dist/server/assets/{codex-server-BFZq2Y2O.js → codex-server-DqzruLmg.js} +35 -87
  29. package/apps/ui/dist/server/assets/{download-C5rkk_Bo.js → download-Drctxary.js} +7 -7
  30. package/apps/ui/dist/server/assets/{projects._project-CcJLp_A8.js → projects._project-DreIU5b0.js} +3 -3
  31. package/apps/ui/dist/server/assets/{projects._project-CJ7l0ynC.js → projects._project-gT01HBqH.js} +2 -2
  32. package/apps/ui/dist/server/assets/{projects.index-srtogpuF.js → projects.index-BYmgSGAj.js} +1 -1
  33. package/apps/ui/dist/server/assets/{router-C_w-haH6.js → router-Qj5Kn7bl.js} +6 -6
  34. package/apps/ui/dist/server/assets/{routes-BhbxvJE7.js → routes-BtcXuK0x.js} +2 -2
  35. package/apps/ui/dist/server/assets/{routes-CPe-ppmC.js → routes-_LbCIjtJ.js} +2 -2
  36. package/apps/ui/dist/server/assets/{threads._threadId-euyNckhj.js → threads._threadId-D5m6ypGw.js} +3 -3
  37. package/apps/ui/dist/server/assets/{threads._threadId-Ba7vv6-K.js → threads._threadId-DcbAJkwf.js} +2 -2
  38. package/apps/ui/dist/server/server.js +19 -36
  39. package/bin/codex-chats-claude.js +5 -0
  40. package/bin/codex-chats.js +5 -0
  41. package/bin/spiracha.js +5 -0
  42. package/package.json +9 -6
  43. package/src/lib/codex-browser-export.ts +28 -3
  44. package/src/lib/codex-exporter-cli.ts +9 -9
  45. package/src/lib/codex-exporter-transcript.ts +16 -190
  46. package/src/lib/codex-exporter-types.ts +1 -1
  47. package/src/lib/codex-exporter.ts +0 -1
  48. package/src/lib/interactive-cli.ts +2 -2
  49. package/src/mcp-server.ts +2 -2
  50. package/apps/ui/dist/client/assets/download-DOwxk-cG.js +0 -1
  51. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-C0V305Nt.js +0 -99
@@ -1,10 +1,10 @@
1
1
  import { t as cn } from "./utils-C_uf36nf.js";
2
2
  import { t as Button } from "./button-CmTDnzOn.js";
3
3
  import { t as SettingsProvider } from "./settings-store-DpEJEQ7M.js";
4
- import { i as projectsQueryOptions, t as analyticsQueryOptions } from "./codex-queries-CAF6HYiG.js";
5
- import { t as Route$5 } from "./routes-BhbxvJE7.js";
6
- import { t as Route$6 } from "./threads._threadId-Ba7vv6-K.js";
7
- import { t as Route$7 } from "./projects._project-CJ7l0ynC.js";
4
+ import { i as projectsQueryOptions, t as analyticsQueryOptions } from "./codex-queries-BH4Cb0v3.js";
5
+ import { t as Route$5 } from "./routes-BtcXuK0x.js";
6
+ import { t as Route$6 } from "./threads._threadId-DcbAJkwf.js";
7
+ import { t as Route$7 } from "./projects._project-gT01HBqH.js";
8
8
  import { useEffect, useState } from "react";
9
9
  import { HeadContent, Link, Outlet, Scripts, createFileRoute, createRootRouteWithContext, createRouter, lazyRouteComponent, notFound, redirect, useRouterState } from "@tanstack/react-router";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -207,7 +207,7 @@ var $$splitComponentImporter$3 = () => import("./settings-MvWDgc1u.js");
207
207
  var Route$3 = createFileRoute("/settings")({ component: lazyRouteComponent($$splitComponentImporter$3, "component") });
208
208
  //#endregion
209
209
  //#region src/routes/analytics.tsx
210
- var $$splitComponentImporter$2 = () => import("./analytics-BMxW_bZL.js");
210
+ var $$splitComponentImporter$2 = () => import("./analytics-2QpLKjlG.js");
211
211
  var Route$2 = createFileRoute("/analytics")({
212
212
  component: lazyRouteComponent($$splitComponentImporter$2, "component"),
213
213
  loader: ({ context }) => {
@@ -234,7 +234,7 @@ var Route$1 = createFileRoute("/$threadId")({
234
234
  //#endregion
235
235
  //#region src/routes/projects.index.tsx
236
236
  var $$splitErrorComponentImporter = () => import("./projects.index-CaplpeMy.js");
237
- var $$splitComponentImporter = () => import("./projects.index-srtogpuF.js");
237
+ var $$splitComponentImporter = () => import("./projects.index-BYmgSGAj.js");
238
238
  var Route = createFileRoute("/projects/")({
239
239
  component: lazyRouteComponent($$splitComponentImporter, "component"),
240
240
  errorComponent: lazyRouteComponent($$splitErrorComponentImporter, "errorComponent"),
@@ -1,8 +1,8 @@
1
- import { n as dashboardQueryOptions } from "./codex-queries-CAF6HYiG.js";
1
+ import { n as dashboardQueryOptions } from "./codex-queries-BH4Cb0v3.js";
2
2
  import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  //#region src/routes/index.tsx
5
- var $$splitComponentImporter = () => import("./routes-CPe-ppmC.js");
5
+ var $$splitComponentImporter = () => import("./routes-_LbCIjtJ.js");
6
6
  var Route = createFileRoute("/")({
7
7
  component: lazyRouteComponent($$splitComponentImporter, "component"),
8
8
  loader: ({ context }) => context.queryClient.ensureQueryData(dashboardQueryOptions())
@@ -1,5 +1,5 @@
1
- import { n as dashboardQueryOptions } from "./codex-queries-CAF6HYiG.js";
2
- import { t as Route } from "./routes-BhbxvJE7.js";
1
+ import { n as dashboardQueryOptions } from "./codex-queries-BH4Cb0v3.js";
2
+ import { t as Route } from "./routes-BtcXuK0x.js";
3
3
  import { t as MetricCard } from "./metric-card-ByEeLu0r.js";
4
4
  import { t as PageHeader } from "./page-header-CxdZM86z.js";
5
5
  import { o as formatNumber, r as formatDateTime, s as formatTokens } from "./formatters-FJaGZgJk.js";
@@ -2,14 +2,14 @@ import { t as applyPathTransforms$1 } from "./path-transforms-DL2IwtYd.js";
2
2
  import { t as cn } from "./utils-C_uf36nf.js";
3
3
  import { t as Button } from "./button-CmTDnzOn.js";
4
4
  import { n as useSettings } from "./settings-store-DpEJEQ7M.js";
5
- import { a as threadSnapshotQueryOptions, c as deleteThreadFn, o as threadTranscriptQueryOptions, u as exportThreadFn } from "./codex-queries-CAF6HYiG.js";
6
- import { t as Route } from "./threads._threadId-Ba7vv6-K.js";
5
+ import { a as threadSnapshotQueryOptions, c as deleteThreadFn, o as threadTranscriptQueryOptions, u as exportThreadFn } from "./codex-queries-BH4Cb0v3.js";
6
+ import { t as Route } from "./threads._threadId-DcbAJkwf.js";
7
7
  import { t as Checkbox$1 } from "./checkbox-C0hovF41.js";
8
8
  import { t as MetricCard } from "./metric-card-ByEeLu0r.js";
9
9
  import { t as PageHeader } from "./page-header-CxdZM86z.js";
10
10
  import { a as formatModelLabel, i as formatList, n as formatBytes, r as formatDateTime, s as formatTokens, t as formatBooleanLabel } from "./formatters-FJaGZgJk.js";
11
11
  import { t as DeleteConfirmDialog } from "./delete-confirm-dialog-CWqcTXTF.js";
12
- import { n as downloadUrlFile, r as ExportDialog, t as downloadTextFile } from "./download-C5rkk_Bo.js";
12
+ import { n as downloadUrlFile, r as ExportDialog, t as downloadTextFile } from "./download-Drctxary.js";
13
13
  import { useEffect, useRef, useState } from "react";
14
14
  import { Link, useNavigate } from "@tanstack/react-router";
15
15
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1,10 +1,10 @@
1
- import { a as threadSnapshotQueryOptions } from "./codex-queries-CAF6HYiG.js";
1
+ import { a as threadSnapshotQueryOptions } from "./codex-queries-BH4Cb0v3.js";
2
2
  import { t as LoadingPanel } from "./loading-panel-DbLdvjtR.js";
3
3
  import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/routes/threads.$threadId.tsx
6
6
  var $$splitErrorComponentImporter = () => import("./threads._threadId-BSSK4nkI.js");
7
- var $$splitComponentImporter = () => import("./threads._threadId-euyNckhj.js");
7
+ var $$splitComponentImporter = () => import("./threads._threadId-D5m6ypGw.js");
8
8
  var Route = createFileRoute("/threads/$threadId")({
9
9
  component: lazyRouteComponent($$splitComponentImporter, "component"),
10
10
  errorComponent: lazyRouteComponent($$splitErrorComponentImporter, "errorComponent"),
@@ -23,7 +23,7 @@ var NullProtoObj = /* @__PURE__ */ (() => {
23
23
  return e.prototype = Object.create(null), Object.freeze(e.prototype), e;
24
24
  })();
25
25
  //#endregion
26
- //#region ../../node_modules/.bun/srvx@0.11.15/node_modules/srvx/dist/_chunks/_url.mjs
26
+ //#region ../../node_modules/.bun/srvx@0.11.16/node_modules/srvx/dist/_chunks/_url.mjs
27
27
  function lazyInherit(target, source, sourceKey) {
28
28
  for (const key of [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)]) {
29
29
  if (key === "constructor") continue;
@@ -52,20 +52,6 @@ function lazyInherit(target, source, sourceKey) {
52
52
  }
53
53
  }
54
54
  var _needsNormRE = /(?:(?:^|\/)(?:\.|\.\.|%2e|%2e\.|\.%2e|%2e%2e)(?:\/|$))|[\\^\x80-\uffff]/i;
55
- /**
56
- * URL wrapper with fast paths to access to the following props:
57
- *
58
- * - `url.pathname`
59
- * - `url.search`
60
- * - `url.searchParams`
61
- * - `url.protocol`
62
- *
63
- * **NOTES:**
64
- *
65
- * - It is assumed that the input URL is **already encoded** and formatted from an HTTP request and contains no hash.
66
- * - Triggering the setters or getters on other props will deoptimize to full URL parsing.
67
- * - Changes to `searchParams` will be discarded as we don't track them.
68
- */
69
55
  var FastURL = /* @__PURE__ */ (() => {
70
56
  const NativeURL = globalThis.URL;
71
57
  const FastURL = class URL {
@@ -113,10 +99,11 @@ var FastURL = /* @__PURE__ */ (() => {
113
99
  const url = this.href;
114
100
  const protoIndex = url.indexOf("://");
115
101
  const pathnameIndex = protoIndex === -1 ? -1 : url.indexOf("/", protoIndex + 4);
102
+ const qIndex = pathnameIndex === -1 ? -1 : url.indexOf("?", pathnameIndex);
116
103
  this.#pos = [
117
104
  protoIndex,
118
105
  pathnameIndex,
119
- pathnameIndex === -1 ? -1 : url.indexOf("?", pathnameIndex)
106
+ qIndex
120
107
  ];
121
108
  }
122
109
  return this.#pos;
@@ -150,7 +137,8 @@ var FastURL = /* @__PURE__ */ (() => {
150
137
  if (this.#protocol === void 0) {
151
138
  const [protocolIndex] = this.#getPos();
152
139
  if (protocolIndex === -1) return this._url.protocol;
153
- this.#protocol = this.href.slice(0, protocolIndex + 1);
140
+ const url = this.href;
141
+ this.#protocol = url.slice(0, protocolIndex + 1);
154
142
  }
155
143
  return this.#protocol;
156
144
  }
@@ -167,12 +155,7 @@ var FastURL = /* @__PURE__ */ (() => {
167
155
  return FastURL;
168
156
  })();
169
157
  //#endregion
170
- //#region ../../node_modules/.bun/srvx@0.11.15/node_modules/srvx/dist/adapters/node.mjs
171
- /**
172
- * Fast Response for Node.js runtime
173
- *
174
- * It is faster because in most cases it doesn't create a full Response instance.
175
- */
158
+ //#region ../../node_modules/.bun/srvx@0.11.16/node_modules/srvx/dist/adapters/node.mjs
176
159
  var NodeResponse = /* @__PURE__ */ (() => {
177
160
  const NativeResponse = globalThis.Response;
178
161
  const STATUS_CODES = globalThis.process?.getBuiltinModule?.("node:http")?.STATUS_CODES || {};
@@ -3471,7 +3454,7 @@ var defaultSerovalPlugins = [
3471
3454
  * the dev styles URL for route-scoped CSS collection.
3472
3455
  */
3473
3456
  async function getStartManifest(matchedRoutes) {
3474
- const { tsrStartManifest } = await import("./assets/_tanstack-start-manifest_v-C0V305Nt.js");
3457
+ const { tsrStartManifest } = await import("./assets/_tanstack-start-manifest_v-kj_QB_26.js");
3475
3458
  const startManifest = tsrStartManifest();
3476
3459
  let routes = startManifest.routes;
3477
3460
  routes[rootRouteId];
@@ -3498,47 +3481,47 @@ async function getStartManifest(matchedRoutes) {
3498
3481
  var manifest = {
3499
3482
  "0814663c3bdc58135f97d210d145ef0be5ca54ef9a5f1e3030be9b1bfc901e30": {
3500
3483
  functionName: "exportThreadFn_createServerFn_handler",
3501
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3484
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3502
3485
  },
3503
3486
  "164ee82cdd565ed96591a64312f0f7bd961040baf066a89d9f5636330d11360b": {
3504
3487
  functionName: "deleteProjectFn_createServerFn_handler",
3505
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3488
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3506
3489
  },
3507
3490
  "29727b7ad5b8fe42e83817376653e064d9fe8888799f056b2e59296b3396568b": {
3508
3491
  functionName: "deleteThreadFn_createServerFn_handler",
3509
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3492
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3510
3493
  },
3511
3494
  "4712520da0f07bbd1f0907e5a162fe518516ff4caca3fd23876cc65539d87d7a": {
3512
3495
  functionName: "getAnalyticsFn_createServerFn_handler",
3513
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3496
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3514
3497
  },
3515
3498
  "59fb2cb4d60c8e7d47e0afcc914ee6f9d9f4bf076c8e66eab1693066753655b3": {
3516
3499
  functionName: "listProjectThreadsFn_createServerFn_handler",
3517
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3500
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3518
3501
  },
3519
3502
  "5da27045f7e28ded6353bc16aace284af7ef1b4010ef04d0186a6feadb466497": {
3520
3503
  functionName: "getThreadTranscriptFn_createServerFn_handler",
3521
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3504
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3522
3505
  },
3523
3506
  "72991e2b6e0adbf8d63bb8b139dad88a00b77b7030ec28ceac36c3cce7846b4c": {
3524
3507
  functionName: "getThreadSnapshotFn_createServerFn_handler",
3525
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3508
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3526
3509
  },
3527
3510
  "792690638a3b10035a5b7368c3d98bdc70cbfe1e36a4aa5f45b1c49b8b1025b0": {
3528
3511
  functionName: "getDashboardSummaryFn_createServerFn_handler",
3529
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3512
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3530
3513
  },
3531
3514
  "96aa60bf7dd9b5bde415bcf3ad6f6955a975eecd9aa0516cf401cc39bebebe6c": {
3532
3515
  functionName: "deleteThreadsFn_createServerFn_handler",
3533
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3516
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3534
3517
  },
3535
3518
  "b4e15c006e9a277470958bb008f89b5b0acc7256109581de44cf17d587d174a5": {
3536
3519
  functionName: "exportThreadsFn_createServerFn_handler",
3537
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3520
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3538
3521
  },
3539
3522
  "ccefccb816ba13508f23db4e31067b3403e750225257592d3ae11071ffc3fd6f": {
3540
3523
  functionName: "listProjectsFn_createServerFn_handler",
3541
- importer: () => import("./assets/codex-server-BFZq2Y2O.js")
3524
+ importer: () => import("./assets/codex-server-DqzruLmg.js")
3542
3525
  }
3543
3526
  };
3544
3527
  async function getServerFnById(id, access) {
@@ -5328,7 +5311,7 @@ var getBaseManifest = getProdBaseManifest;
5328
5311
  var createEarlyHintsForRequest = createEarlyHintsCollector;
5329
5312
  async function loadEntries() {
5330
5313
  const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
5331
- import("./assets/router-C_w-haH6.js"),
5314
+ import("./assets/router-Qj5Kn7bl.js"),
5332
5315
  import("./assets/start-HeKLHD9b.js"),
5333
5316
  import("./assets/__23tanstack-start-plugin-adapters-BzCA6dXo.js")
5334
5317
  ]);
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { runExportClaudeCli } from '../src/export-claude.ts';
4
+
5
+ await runExportClaudeCli();
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { runExportChatsCli } from '../src/export-chats.ts';
4
+
5
+ await runExportChatsCli();
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { runSpirachaCli } from '../src/spiracha.ts';
4
+
5
+ await runSpirachaCli();
package/package.json CHANGED
@@ -4,21 +4,21 @@
4
4
  "url": "https://github.com/ragaeeb"
5
5
  },
6
6
  "bin": {
7
- "codex-chats": "./src/export-chats.ts",
8
- "codex-chats-claude": "./src/export-claude.ts",
9
- "spiracha": "./src/spiracha.ts"
7
+ "codex-chats": "./bin/codex-chats.js",
8
+ "codex-chats-claude": "./bin/codex-chats-claude.js",
9
+ "spiracha": "./bin/spiracha.js"
10
10
  },
11
11
  "bugs": {
12
12
  "url": "https://github.com/ragaeeb/spiracha/issues"
13
13
  },
14
14
  "dependencies": {
15
- "@inquirer/prompts": "^8.4.3",
15
+ "@inquirer/prompts": "^8.5.0",
16
16
  "@modelcontextprotocol/sdk": "^1.29.0",
17
17
  "@tanstack/react-query": "5.100.14",
18
18
  "@tanstack/react-router": "1.170.8",
19
19
  "@tanstack/react-router-ssr-query": "1.167.0",
20
20
  "@tanstack/react-table": "8.21.3",
21
- "@tanstack/react-virtual": "3.13.25",
21
+ "@tanstack/react-virtual": "3.13.26",
22
22
  "class-variance-authority": "0.7.1",
23
23
  "clsx": "2.1.1",
24
24
  "iconv-lite": "^0.7.2",
@@ -39,6 +39,9 @@
39
39
  "bun": ">=1.3.13"
40
40
  },
41
41
  "files": [
42
+ "bin/codex-chats.js",
43
+ "bin/codex-chats-claude.js",
44
+ "bin/spiracha.js",
42
45
  "src/export-chats.ts",
43
46
  "src/export-claude.ts",
44
47
  "src/mcp-server.ts",
@@ -109,7 +112,7 @@
109
112
  "ui:preview": "bun run --cwd apps/ui preview"
110
113
  },
111
114
  "type": "module",
112
- "version": "1.1.1",
115
+ "version": "1.2.0",
113
116
  "workspaces": [
114
117
  "apps/*"
115
118
  ]
@@ -11,9 +11,9 @@ import { buildUiExportDownloadUrl, ensureUiExportDir } from './ui-export-files';
11
11
  type RenderCodexThreadDownloadInput = {
12
12
  dbPath: string;
13
13
  includeCommentary: boolean;
14
+ includeMetadata: boolean;
14
15
  includeTools: boolean;
15
16
  largeExportThresholdBytes?: number;
16
- optimized: boolean;
17
17
  outputFormat: ExportFormat;
18
18
  pathDisplaySettings?: Pick<PathDisplaySettings, 'convertToProjectRoot' | 'redactUsername'>;
19
19
  publicExportDir?: string;
@@ -79,6 +79,17 @@ const buildUniqueArchivePath = (exportDir: string, exportBaseName: string) => {
79
79
  return path.join(exportDir, `${exportBaseName}-${randomUUID()}.zip`);
80
80
  };
81
81
 
82
+ const buildUniqueBatchEntryBaseName = (baseName: string, threadId: string, usedBaseNames: Set<string>): string => {
83
+ if (!usedBaseNames.has(baseName)) {
84
+ usedBaseNames.add(baseName);
85
+ return baseName;
86
+ }
87
+
88
+ const collisionSafeBaseName = `${baseName}-${threadId}`;
89
+ usedBaseNames.add(collisionSafeBaseName);
90
+ return collisionSafeBaseName;
91
+ };
92
+
82
93
  type RolloutSnapshot = {
83
94
  mtimeMs: number;
84
95
  sizeBytes: number;
@@ -90,9 +101,9 @@ const toDownloadOptions = (input: RenderCodexThreadDownloadInput): CodexCliOptio
90
101
  dbPath: input.dbPath,
91
102
  flat: false,
92
103
  includeCommentary: input.includeCommentary,
104
+ includeMetadata: input.includeMetadata,
93
105
  includeTools: input.includeTools,
94
106
  inputDir: '',
95
- optimized: input.optimized,
96
107
  outputDir: '',
97
108
  outputFormat: input.outputFormat,
98
109
  projectFilter: null,
@@ -340,6 +351,7 @@ export const renderCodexThreadsDownload = async (
340
351
  const exportBaseName = buildBatchExportBaseName(threads);
341
352
  const bundleDirectory = await createExportWorkspace(exportDir, exportBaseName);
342
353
  const zipPath = buildUniqueArchivePath(exportDir, exportBaseName);
354
+ const usedBatchEntryBaseNames = new Set<string>();
343
355
 
344
356
  logExportEvent('info', 'batch_start', {
345
357
  exportBaseName,
@@ -352,8 +364,13 @@ export const renderCodexThreadsDownload = async (
352
364
  for (const entry of browseEntries) {
353
365
  const rolloutSnapshotBefore = await getRolloutSnapshot(entry.thread.rollout_path);
354
366
  const singleBaseName = buildExportBaseName(entry.thread);
367
+ const uniqueBaseName = buildUniqueBatchEntryBaseName(
368
+ singleBaseName,
369
+ entry.thread.id,
370
+ usedBatchEntryBaseNames,
371
+ );
355
372
  const extension = input.outputFormat === 'md' ? 'md' : 'txt';
356
- const relativeFileName = `${singleBaseName}.${extension}`;
373
+ const relativeFileName = `${uniqueBaseName}.${extension}`;
357
374
  const savedPath = path.join(bundleDirectory, relativeFileName);
358
375
  const transform = (text: string) =>
359
376
  input.pathDisplaySettings
@@ -363,6 +380,14 @@ export const renderCodexThreadsDownload = async (
363
380
  })
364
381
  : text;
365
382
 
383
+ if (uniqueBaseName !== singleBaseName) {
384
+ logExportEvent('warn', 'batch_entry_name_collision', {
385
+ resolvedFileName: relativeFileName,
386
+ singleBaseName,
387
+ threadId: entry.thread.id,
388
+ });
389
+ }
390
+
366
391
  const saved = await writeSessionFileExport(
367
392
  {
368
393
  fallbackReason: null,
@@ -10,7 +10,7 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
10
10
  let projectFilter: string | null = null;
11
11
  let threadIds: string[] = [];
12
12
  let outputProvided = false;
13
- let optimized = false;
13
+ let includeMetadata = true;
14
14
  let includeCommentary = true;
15
15
  let includeTools = false;
16
16
  let outputFormat: ExportFormat = 'md';
@@ -22,9 +22,9 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
22
22
  dbPath,
23
23
  flat,
24
24
  includeCommentary,
25
+ includeMetadata,
25
26
  includeTools,
26
27
  inputDir,
27
- optimized,
28
28
  outputDir,
29
29
  outputFormat,
30
30
  outputProvided,
@@ -37,9 +37,9 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
37
37
  dbPath,
38
38
  flat,
39
39
  includeCommentary,
40
+ includeMetadata,
40
41
  includeTools,
41
42
  inputDir,
42
- optimized,
43
43
  outputDir,
44
44
  outputFormat,
45
45
  outputProvided,
@@ -58,9 +58,9 @@ export const parseCodexCliArgs = (argv: string[]): CodexCliOptions => {
58
58
  dbPath,
59
59
  flat,
60
60
  includeCommentary,
61
+ includeMetadata,
61
62
  includeTools,
62
63
  inputDir,
63
- optimized,
64
64
  outputDir: outputDir ?? DEFAULT_OUTPUT_DIR,
65
65
  outputFormat,
66
66
  projectFilter,
@@ -73,9 +73,9 @@ type CodexCliState = {
73
73
  dbPath: string;
74
74
  flat: boolean;
75
75
  includeCommentary: boolean;
76
+ includeMetadata: boolean;
76
77
  includeTools: boolean;
77
78
  inputDir: string;
78
- optimized: boolean;
79
79
  outputDir: string | null;
80
80
  outputFormat: ExportFormat;
81
81
  outputProvided: boolean;
@@ -142,12 +142,12 @@ const applyCodexCliArg = (argv: string[], index: number, state: CodexCliState):
142
142
  };
143
143
  }
144
144
 
145
- if (arg === '--optimized') {
145
+ if (arg === '--no-metadata') {
146
146
  return {
147
147
  index,
148
148
  state: {
149
149
  ...state,
150
- optimized: true,
150
+ includeMetadata: false,
151
151
  },
152
152
  };
153
153
  }
@@ -220,7 +220,7 @@ export const getCodexHelpText = (): string => {
220
220
  'Usage:',
221
221
  ' codex-chats',
222
222
  ' codex-chats --interactive',
223
- ' codex-chats [--db FILE] [--input DIR] [--output DIR] [--cwd DIR] [--project NAME] [--optimized] [--tools] [--flat] [--output-format md|txt] [codex://threads/<thread-id> ...]',
223
+ ' codex-chats [--db FILE] [--input DIR] [--output DIR] [--cwd DIR] [--project NAME] [--no-metadata] [--tools] [--flat] [--output-format md|txt] [codex://threads/<thread-id> ...]',
224
224
  '',
225
225
  'Options:',
226
226
  ` --db Thread database path (default: ${DEFAULT_DB_PATH})`,
@@ -230,7 +230,7 @@ export const getCodexHelpText = (): string => {
230
230
  ' --project Only export chats whose cwd basename matches this project name',
231
231
  ' codex://threads/<id>',
232
232
  ' Only export the exact threads referenced by these Codex deeplinks',
233
- ' --optimized Suppress metadata and apply token-saving text cleanup',
233
+ ' --no-metadata Omit the chat metadata section from the export header',
234
234
  ' --tools Include tool-call logs such as exec_command invocations',
235
235
  ' --flat Write all exports into one folder instead of nested subfolders',
236
236
  ' --output-format Output file format: md or txt (default: md)',