spiracha 1.1.2 → 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-C01sv0JJ.js → codex-server-DqzruLmg.js} +17 -86
  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 +2 -2
  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
@@ -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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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-C01sv0JJ.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.2",
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;
@@ -101,9 +101,9 @@ const toDownloadOptions = (input: RenderCodexThreadDownloadInput): CodexCliOptio
101
101
  dbPath: input.dbPath,
102
102
  flat: false,
103
103
  includeCommentary: input.includeCommentary,
104
+ includeMetadata: input.includeMetadata,
104
105
  includeTools: input.includeTools,
105
106
  inputDir: '',
106
- optimized: input.optimized,
107
107
  outputDir: '',
108
108
  outputFormat: input.outputFormat,
109
109
  projectFilter: 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)',
@@ -40,16 +40,16 @@ export const convertSessionFile = async (target: ExportTarget, options: CodexCli
40
40
  return null;
41
41
  }
42
42
 
43
- if (options.optimized) {
44
- return transcriptState.sections.join('\n\n').trimEnd() + '\n';
45
- }
46
-
47
43
  const title = getTitle(target, transcriptState.sessionMeta);
48
- const metadata = buildMetadataEntries(target, transcriptState.sessionMeta, options);
49
44
  const parts = [
50
45
  renderDocumentTitle(title, options.outputFormat),
51
46
  '',
52
- renderMetadataBlock(metadata, options.outputFormat),
47
+ options.includeMetadata
48
+ ? renderMetadataBlock(
49
+ buildMetadataEntries(target, transcriptState.sessionMeta, options),
50
+ options.outputFormat,
51
+ )
52
+ : '',
53
53
  ...transcriptState.sections,
54
54
  ].filter(Boolean);
55
55
  return parts.join('\n').trimEnd() + '\n';
@@ -69,7 +69,6 @@ export const writeSessionFileExport = async (
69
69
  assistantModel: target.thread?.model ?? null,
70
70
  sections: [],
71
71
  sessionMeta: {},
72
- startedTranscript: false,
73
72
  };
74
73
  let wroteSection = false;
75
74
 
@@ -82,7 +81,7 @@ export const writeSessionFileExport = async (
82
81
  continue;
83
82
  }
84
83
 
85
- transcriptStream.write(transform(wroteSection ? `${getSectionSeparator(options)}${block}` : block));
84
+ transcriptStream.write(transform(wroteSection ? `${getSectionSeparator()}${block}` : block));
86
85
  wroteSection = true;
87
86
  }
88
87
  await finalizeExportWriteStream(transcriptStream);
@@ -124,7 +123,6 @@ type CodexTranscriptState = {
124
123
  assistantModel: string | null;
125
124
  sessionMeta: SessionMeta;
126
125
  sections: string[];
127
- startedTranscript: boolean;
128
126
  };
129
127
 
130
128
  const collectCodexTranscript = async (
@@ -136,7 +134,6 @@ const collectCodexTranscript = async (
136
134
  assistantModel,
137
135
  sections: [],
138
136
  sessionMeta: {},
139
- startedTranscript: false,
140
137
  };
141
138
 
142
139
  for await (const parsed of readJsonlObjects(sessionFile)) {
@@ -146,9 +143,7 @@ const collectCodexTranscript = async (
146
143
  return state;
147
144
  };
148
145
 
149
- const getSectionSeparator = (options: CodexCliOptions) => {
150
- return options.optimized ? '\n\n' : '\n';
151
- };
146
+ const getSectionSeparator = () => '\n';
152
147
 
153
148
  const processCodexTranscriptRecord = (
154
149
  parsed: Record<string, JsonValue>,
@@ -181,58 +176,23 @@ const renderCodexTranscriptRecord = (
181
176
  return '';
182
177
  }
183
178
 
184
- return options.optimized
185
- ? renderCompactToolBlock(tool, options.outputFormat)
186
- : renderToolBlock(tool, options.outputFormat);
179
+ return renderToolBlock(tool, options.outputFormat);
187
180
  };
188
181
 
189
182
  const processCodexMessageRecord = (message: MessageRecord, options: CodexCliOptions, state: CodexTranscriptState) => {
190
- if (options.optimized) {
191
- return processOptimizedCodexMessageRecord(message, options, state);
192
- }
193
-
194
183
  return renderMessageBlock(message, options.outputFormat, state.assistantModel, options.includeCommentary);
195
184
  };
196
185
 
197
- const processOptimizedCodexMessageRecord = (
198
- message: MessageRecord,
199
- options: CodexCliOptions,
200
- state: CodexTranscriptState,
201
- ) => {
202
- if (message.role !== 'user' && message.role !== 'assistant') {
203
- return '';
204
- }
205
-
206
- if (message.role === 'assistant' && message.phase === 'commentary' && !options.includeCommentary) {
207
- return '';
208
- }
209
-
210
- const compact = compactMessageText(message, true);
211
- if (!compact) {
212
- return '';
213
- }
214
-
215
- if (!state.startedTranscript) {
216
- if (shouldSkipOptimizedPrelude(message.role, compact)) {
217
- return '';
218
- }
219
- state.startedTranscript = true;
220
- }
221
-
222
- return renderCompactBlock(message, compact, options.outputFormat, state.assistantModel);
223
- };
224
-
225
186
  const buildStreamExportPrefix = (target: ExportTarget, sessionMeta: SessionMeta, options: CodexCliOptions) => {
226
- if (options.optimized) {
187
+ if (!options.includeMetadata) {
227
188
  return '';
228
189
  }
229
190
 
230
191
  const title = getTitle(target, sessionMeta);
231
- const metadata = buildMetadataEntries(target, sessionMeta, options);
232
192
  const parts = [
233
193
  renderDocumentTitle(title, options.outputFormat),
234
194
  '',
235
- renderMetadataBlock(metadata, options.outputFormat),
195
+ renderMetadataBlock(buildMetadataEntries(target, sessionMeta, options), options.outputFormat),
236
196
  ]
237
197
  .filter(Boolean)
238
198
  .join('\n');
@@ -240,13 +200,6 @@ const buildStreamExportPrefix = (target: ExportTarget, sessionMeta: SessionMeta,
240
200
  return `${parts}\n`;
241
201
  };
242
202
 
243
- export const compactMessageText = (message: MessageRecord, optimized: boolean): string => {
244
- const rawText = extractText(message.content);
245
- const cleaned = stripPreviewBlock(rawText);
246
-
247
- return optimized ? optimizePlainText(optimizeRenderedText(cleaned)) : cleaned.trim();
248
- };
249
-
250
203
  export const formatToolOutputSummary = (outputText: string, outputFormat: ExportFormat): string => {
251
204
  if (!outputText) {
252
205
  return '';
@@ -308,27 +261,6 @@ const getTitle = (target: ExportTarget, sessionMeta: SessionMeta): string => {
308
261
  return sessionMeta.id ?? path.basename(target.sessionFile, '.jsonl');
309
262
  };
310
263
 
311
- const shouldSkipOptimizedPrelude = (role: string, text: string): boolean => {
312
- if (role !== 'user') {
313
- return true;
314
- }
315
-
316
- return (
317
- text.startsWith('AGENTS.md instructions for ') ||
318
- text.startsWith('# AGENTS.md instructions for ') ||
319
- text.startsWith('<permissions instructions>') ||
320
- text.startsWith('<environment_context>') ||
321
- text.startsWith('<app-context>') ||
322
- text.startsWith('<collaboration_mode>') ||
323
- text.startsWith('<skills_instructions>') ||
324
- text.startsWith('You are Codex, a coding agent based on GPT-5.') ||
325
- text.startsWith('Read this before making changes.') ||
326
- text.includes('Filesystem sandboxing defines which files can be read or written.') ||
327
- text.includes('approval_policy') ||
328
- text.includes('base_instructions')
329
- );
330
- };
331
-
332
264
  const buildMetadataEntries = (
333
265
  target: ExportTarget,
334
266
  sessionMeta: SessionMeta,
@@ -585,7 +517,7 @@ const renderMessageBlock = (
585
517
  return '';
586
518
  }
587
519
 
588
- const text = cleanExtractedText(extractText(message.content)).trim();
520
+ const text = cleanExtractedText(stripPreviewBlock(extractText(message.content))).trim();
589
521
  if (!text || shouldSkipMessage(message.role, text)) {
590
522
  return '';
591
523
  }
@@ -606,36 +538,6 @@ const renderToolBlock = (tool: ToolRecord, outputFormat: ExportFormat): string =
606
538
  return summary ? renderSection('Tool Output', summary, outputFormat) : '';
607
539
  };
608
540
 
609
- const renderCompactBlock = (
610
- message: MessageRecord,
611
- text: string,
612
- outputFormat: ExportFormat,
613
- assistantModel: string | null,
614
- ): string => {
615
- const prefix = message.role === 'user' ? 'U:' : `${formatModelLabel(message.model ?? assistantModel)}:`;
616
- const lines = text.split('\n');
617
- const [firstLine, ...rest] = lines;
618
-
619
- if (rest.length === 0) {
620
- return `${prefix} ${normalizeCompactLiteral(firstLine, outputFormat)}`;
621
- }
622
-
623
- return [
624
- `${prefix} ${normalizeCompactLiteral(firstLine, outputFormat)}`,
625
- ...rest.map((line) => normalizeCompactLiteral(line, outputFormat)),
626
- ].join('\n');
627
- };
628
-
629
- const renderCompactToolBlock = (tool: ToolRecord, outputFormat: ExportFormat): string => {
630
- if (tool.kind === 'call') {
631
- const details = formatCompactToolCall(tool, outputFormat);
632
- return details ? `T: ${details}` : '';
633
- }
634
-
635
- const summary = formatCompactToolOutput(tool.outputText ?? '');
636
- return summary ? `R: ${summary}` : '';
637
- };
638
-
639
541
  const stripPreviewBlock = (text: string): string => {
640
542
  const parts = text
641
543
  .split(/\n{2,}/)
@@ -662,49 +564,15 @@ const stripPreviewBlock = (text: string): string => {
662
564
  return parts.slice(1).join('\n\n');
663
565
  };
664
566
 
665
- const optimizeRenderedText = (text: string): string => {
666
- return text
667
- .replace(/^\*Phase:\s+`[^`]+`\*\s*\n*/gm, '')
668
- .replace(/^\s*<image\b[^>]*>\s*$/gim, '')
669
- .replace(/^\s*<\/image>\s*$/gim, '')
670
- .replace(/^\s*\[Image attached\]\s*$/gim, '')
671
- .replace(/^#{1,6}\s+/gm, '')
672
- .replace(/^```[^\n]*\n?/gm, '')
673
- .replace(/\n```$/gm, '')
674
- .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
675
- .replace(/^##\s+User\s*$/gm, 'User:')
676
- .replace(/^##\s+Assistant\s*$/gm, 'Assistant:')
677
- .replace(/`([^`]+)`/g, '$1')
678
- .replace(/\*\*([^*]+)\*\*/g, '$1')
679
- .replace(/\*([^*\n]+)\*/g, '$1')
680
- .replace(/\n{3,}/g, '\n\n')
681
- .trim();
682
- };
683
-
684
- const optimizePlainText = (text: string): string => {
685
- const normalized = text
686
- .replace(/\r/g, '')
687
- .replace(/^\s*<image\b[^>]*>\s*$/gim, '')
688
- .replace(/^\s*<\/image>\s*$/gim, '')
689
- .replace(/^\s*\[Image attached\]\s*$/gim, '')
690
- .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
691
- .replace(/`([^`]+)`/g, '$1')
692
- .replace(/\*\*([^*]+)\*\*/g, '$1')
693
- .replace(/\*([^*\n]+)\*/g, '$1');
694
-
695
- return normalized
696
- .split('\n')
697
- .map((line) => line.replace(/[ \t]+$/g, ''))
698
- .join('\n')
699
- .replace(/\n{3,}/g, '\n\n')
700
- .trim();
701
- };
702
-
703
567
  const shouldSkipMessage = (role: string, text: string): boolean => {
704
568
  if (text.startsWith('<environment_context>')) {
705
569
  return true;
706
570
  }
707
571
 
572
+ if (text.startsWith('AGENTS.md instructions for ')) {
573
+ return true;
574
+ }
575
+
708
576
  if (text.startsWith('# AGENTS.md instructions for ')) {
709
577
  return true;
710
578
  }
@@ -725,44 +593,6 @@ const formatToolCallDetails = (tool: ToolRecord, outputFormat: ExportFormat): st
725
593
  return details.cmd ? `Command: ${formatInlineLiteral(details.cmd, outputFormat)}` : '';
726
594
  };
727
595
 
728
- const formatCompactToolCall = (tool: ToolRecord, outputFormat: ExportFormat): string => {
729
- if (tool.name === 'exec_command') {
730
- const details = parseExecCommandArguments(tool.argumentsText);
731
- if (!details.cmd) {
732
- return 'exec_command';
733
- }
734
-
735
- const command = formatInlineLiteral(details.cmd, outputFormat);
736
- return details.workdir ? `exec_command ${command} @ ${details.workdir}` : `exec_command ${command}`;
737
- }
738
-
739
- return tool.callId ? `${tool.name} (${tool.callId})` : tool.name;
740
- };
741
-
742
- const formatCompactToolOutput = (outputText: string): string => {
743
- if (!outputText) {
744
- return '';
745
- }
746
-
747
- const lines = outputText
748
- .split('\n')
749
- .map((line) => line.trim())
750
- .filter(Boolean);
751
-
752
- const exit = lines.find((line) => line.startsWith('Process exited with code '));
753
- const wall = lines.find((line) => line.startsWith('Wall time: '));
754
-
755
- if (exit && wall) {
756
- return `${exit.replace('Process ', '')}; ${wall.toLowerCase()}`;
757
- }
758
-
759
- if (exit) {
760
- return exit.replace('Process ', '');
761
- }
762
-
763
- return '';
764
- };
765
-
766
596
  const extractText = (content: JsonValue): string => {
767
597
  if (typeof content === 'string') {
768
598
  return content;
@@ -802,7 +632,3 @@ const extractContentPart = (value: JsonValue): string => {
802
632
 
803
633
  return text ?? '';
804
634
  };
805
-
806
- const normalizeCompactLiteral = (value: string, outputFormat: ExportFormat): string => {
807
- return outputFormat === 'md' ? value : value.replace(/`([^`]+)`/g, '$1');
808
- };