codex-claude-relay 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.zh.md ADDED
@@ -0,0 +1,522 @@
1
+ # codex-claude-relay
2
+
3
+ 在 **OpenAI Codex CLI** 与 **Anthropic Claude Code** 之间做上下文接力:直接读两边写在本地的原生 session 文件,挑出和当前仓库相关的一次会话,压成一段 handoff prompt,把另一边的 CLI 用这段 prompt 当作首条用户输入启动。
4
+
5
+ 不开数据库。不跑后台进程。不改写两边任何原生文件。
6
+
7
+ 🌐 English: [README.md](./README.md)
8
+
9
+ ```bash
10
+ codex # 在 Codex 里干一会儿
11
+ relay claude # 把 Codex 这次会话压缩好,作为初始 prompt 启动 claude
12
+ relay codex # 反过来:把 Claude 这次会话压缩好,作为初始 prompt 启动 codex
13
+ ```
14
+
15
+ ## 工作流
16
+
17
+ ```
18
+ ┌──────────────────────────────────┐
19
+ │ ~/.codex/sessions/**/ │ ──┐
20
+ │ rollout-*.jsonl │ │
21
+ └──────────────────────────────────┘ │ 按当前目录挑最匹配的 session
22
+ │ (cwd 命中 + 新鲜度)
23
+
24
+ ┌──────────────────────┐
25
+ │ 流式解析 JSONL │
26
+ │ 归一化事件 │
27
+ │ 脱敏密钥 │ ─→ handoff(markdown,约 6–12 KB)
28
+ │ 套用模板渲染 │
29
+ └──────────────────────┘
30
+
31
+ ┌──────────────────────────────────┐ │
32
+ │ ~/.claude/projects/<encoded>/ │ ──┘
33
+ │ <session-uuid>.jsonl │
34
+ │ memory/MEMORY.md │
35
+ └──────────────────────────────────┘
36
+
37
+
38
+ 用 spawn 启动 claude / codex,
39
+ handoff 作为首条用户 prompt
40
+ ```
41
+
42
+ ## 为什么需要它
43
+
44
+ 两家主流 coding agent 都已经在本地各写各的 transcript,问题在于它们互相不读。任务切到一半换工具,就要花五分钟把上下文复制粘贴过去——改过哪些文件、哪些命令失败了、试过哪些方案。
45
+
46
+ 常见的「记忆同步」方案几乎都会引入**第三个**存储(向量库、JSON 缓存、`.ai/handoff.md`),而这个新存储恰恰是两边原生工具都不读的。结果就是三份「真相来源」,新加的那份永远跟不上。
47
+
48
+ codex-claude-relay 走另一条路:**什么都不存**。每次调用都重新读原生 transcript。两个工具仍然是仅有的真相来源,这个 CLI 只是无状态的读取器 + 进程启动器。
49
+
50
+ | 方案 | 持久存储 | 修改原生文件 | 直接读原生 transcript |
51
+ | ------------------------------------- | :------: | :----------: | :-------------------: |
52
+ | 手动复制粘贴 | — | 否 | — |
53
+ | 向量库 / 记忆同步工具 | 是 | 否 | 有时 |
54
+ | 在仓库里维护 `.ai/handoff.md` | 是 | 否 | 否 |
55
+ | **codex-claude-relay** | 否 | 否 | 是 |
56
+
57
+ ## 安装
58
+
59
+ 需要 Node.js 20+(`node --version` 检查)。
60
+
61
+ ### 方案 A —— clone + build + link(推荐)
62
+
63
+ `npm install` 只下载两个 devDependency(`typescript`、`tsx`)。`npm run build` 把 `src/` 编译到 `dist/`。**`npm link` 是单独的一步,它才会把 `relay` 命令暴露到全局 `PATH`。**跳过这步就是 `command not found: relay` 的最常见原因。
64
+
65
+ ```bash
66
+ git clone https://github.com/Picrew/codex-claude-relay
67
+ cd codex-claude-relay
68
+ npm install # 装 devDeps(typescript, tsx)
69
+ npm run build # 编译 src/ → dist/
70
+ npm link # 把 `relay` 和 `codex-claude-relay` 注册到全局
71
+ ```
72
+
73
+ 验证:
74
+
75
+ ```bash
76
+ which relay # 应该打印 npm 全局 prefix 下的路径
77
+ relay --version # 应该打印 0.1.0
78
+ relay inspect # 应该列出发现的 session
79
+ ```
80
+
81
+ 如果 `npm link` 报权限错误,说明你的 npm 全局 prefix 不可写。要么把 prefix 改到 HOME 下(`npm config set prefix "$HOME/.npm-global"`,然后把 `$HOME/.npm-global/bin` 加进 `PATH`),要么走方案 B。
82
+
83
+ ### 方案 B —— 不 link,直接 node 跑
84
+
85
+ 不想 link 全局:
86
+
87
+ ```bash
88
+ node /absolute/path/to/codex-claude-relay/dist/cli.js inspect
89
+ ```
90
+
91
+ 最轻的替代是加个 shell alias:
92
+
93
+ ```bash
94
+ echo 'alias relay="node $HOME/path/to/codex-claude-relay/dist/cli.js"' >> ~/.zshrc
95
+ source ~/.zshrc
96
+ ```
97
+
98
+ ### 方案 C —— 从 tarball 全局装
99
+
100
+ 如果这个包发布到 npm 了(当前还没,但结构支持):
101
+
102
+ ```bash
103
+ npm install -g codex-claude-relay
104
+ ```
105
+
106
+ ### 真正启动 agent 的前提
107
+
108
+ `claude` 和 `codex` 都需要在 `PATH` 上才能让 `relay claude` / `relay codex` spawn 起来:
109
+
110
+ ```bash
111
+ which claude codex
112
+ ```
113
+
114
+ 如果哪个没有,去对应官网装。`relay preview`、`relay inspect`、`--dry-run` 不依赖它们也能用。
115
+
116
+ ### 卸载
117
+
118
+ ```bash
119
+ cd codex-claude-relay
120
+ npm unlink -g # 拆掉全局符号链接
121
+ ```
122
+
123
+ ## 命令
124
+
125
+ | 命令 | 作用 |
126
+ | -------------------------- | --------------------------------------------------------------------- |
127
+ | `relay claude` | 从最相关的 Codex session 生成 handoff,启动 `claude` |
128
+ | `relay codex` | 从最相关的 Claude session 生成 handoff,启动 `codex` |
129
+ | `relay preview <target>` | 只打印 handoff,不启动任何东西 |
130
+ | `relay inspect` | 显示发现到的 session、打分与原因 |
131
+
132
+ ### 选项
133
+
134
+ ```
135
+ --last 直接用最新 mtime 的 session,跳过 cwd 排序
136
+ (当 cwd 匹配不上时有用)
137
+ --with-diff 把当前 `git diff HEAD` 拼到 handoff 里
138
+ --max-chars N handoff 字符上限(默认 12000)
139
+ --dry-run 只生成并打印 handoff,不启动目标 agent
140
+ --no-redact 关闭密钥脱敏(默认开启)
141
+ --debug stderr 输出详细发现 / 解析信息
142
+ ```
143
+
144
+ ## 完整 Usage 教程
145
+
146
+ 两种主流向:*Claude Code → Codex* 与 *Codex → Claude Code*。两边是对称的,按你要切的方向看对应那一段就行。
147
+
148
+ ### 场景 A:Claude Code → Codex
149
+
150
+ 你在 Claude Code 里干了一会儿,想让 Codex 接着干。
151
+
152
+ **Step 1 — 离开 Claude Code(或者直接开新终端窗口)。**
153
+
154
+ 不需要"干净退出"。Claude Code 的 JSONL transcript 是边干边落盘的,哪怕你不正式退出,最近的事件也都在硬盘上。要么在 Claude Code 里输入 `/exit`,要么直接开一个新终端 tab(iTerm / Terminal 里 `Cmd+T`),让 Claude 在后台保持运行 —— 两种都可以。
155
+
156
+ > ⚠ **不要**把 `relay codex` 输入到 Claude Code 自己的输入框里。Claude REPL 会把它当成发给模型的一句消息,不会当成 shell 命令执行。`relay` 必须在普通 shell 里跑。
157
+
158
+ **Step 2 — `cd` 到你的仓库。**
159
+
160
+ ```bash
161
+ cd /path/to/your/repo
162
+ ```
163
+
164
+ 当前目录很关键:context-relay 用 `git rev-parse --show-toplevel` 拿到 git root,再据此挑最相关的过往 session。
165
+
166
+ **Step 3 — 先 preview,看 handoff 会包含什么(零启动、零风险)。**
167
+
168
+ ```bash
169
+ relay preview codex --max-chars 8000 | less
170
+ ```
171
+
172
+ 翻一翻确认:
173
+
174
+ - **Original task** —— 第一段是不是你原本让 Claude 做的事?
175
+ - **Subsequent user instructions** —— 你的后续指令在不在?
176
+ - **Files touched or inspected** —— 文件列表是不是合理?
177
+ - **Recent conversation tail** —— 是否覆盖了最后几轮对话?
178
+
179
+ 如果原始任务被截断了想看完整版,加大 `--max-chars`(比如 `--max-chars 16000`)。
180
+
181
+ 如果 `relay inspect` 已经显示对应 session 的 score ≥ 90,这一步基本可以跳。按 `q` 退出 less。
182
+
183
+ **Step 4 — 真正启动 Codex,带 handoff。**
184
+
185
+ ```bash
186
+ relay codex
187
+ ```
188
+
189
+ 会看到 stderr 打 `codex-claude-relay: launching \`codex\` with handoff (N chars)`,然后 Codex 的 TUI 起来。handoff 作为 Codex 的首条 user message 进入:
190
+
191
+ - handoff ≤ 8 KB:直接 inline 当首条消息传入
192
+ - handoff > 8 KB:context-relay 把它写到 `$TMPDIR` 下一个 `0600` 临时文件,argv 里只放一段简短引用 prompt:「请读 handoff 文件 …,然后继续」。Codex 用文件读取工具读完后继续。Codex 退出时临时文件被删除。
193
+
194
+ 两种情况下,Codex 第一次回应都会"确认 + 概述下一步"(这就是上次讨论的那个 ~10s 回合)。之后你就照常打字。
195
+
196
+ **Step 5 — 验证 Codex 真的看到了上下文。**
197
+
198
+ 等 Codex 第一次回应完,问它只有上次 session 才知道的事:
199
+
200
+ ```
201
+ 我之前在 Claude 里问的第一个问题是什么?逐字告诉我。
202
+ 列一下 Claude 都动过哪些文件,跑过哪些命令。
203
+ ```
204
+
205
+ Codex 应该能准确答出来。能答 → handoff 起作用了,继续干活。
206
+
207
+ ### 场景 B:Codex → Claude Code
208
+
209
+ 与场景 A 完全对称。你在 Codex 里干了一会儿,想让 Claude Code 接着干。
210
+
211
+ **Step 1 — 离开 Codex(或开新终端)。** Codex 的 rollout JSONL 也是连续落盘的。
212
+
213
+ **Step 2 — `cd` 到仓库。**
214
+
215
+ ```bash
216
+ cd /path/to/your/repo
217
+ ```
218
+
219
+ **Step 3 — Preview。**
220
+
221
+ ```bash
222
+ relay preview claude --max-chars 8000 | less
223
+ ```
224
+
225
+ 如果 Codex 是在 worktree 或子目录里跑的,session 里记录的 cwd 可能跟你当前 git root 对不上。`relay inspect` 会显示 score;如果太低(< 60),用 `--last`:
226
+
227
+ ```bash
228
+ relay preview claude --last
229
+ ```
230
+
231
+ **Step 4 — 启动。**
232
+
233
+ ```bash
234
+ relay claude
235
+ ```
236
+
237
+ Claude Code 的 TUI 起来,handoff 作为首条 user message 进入(> 8 KB 时同样走临时文件路径)。
238
+
239
+ **Step 5 — 验证。**
240
+
241
+ ```
242
+ 逐字告诉我我在前一个 Codex session 里问的第一个问题。
243
+ 总结一下 Codex 已经搞定了什么,还有哪些没收尾。
244
+ ```
245
+
246
+ ### 并行模式(不切,两个一起开)
247
+
248
+ 不一定非要关掉一个再用另一个。开两个终端 tab:
249
+
250
+ | Tab 1 | Tab 2 |
251
+ | -------------------------------------- | ---------------------------------------------------- |
252
+ | `codex`(或 `claude`)继续跑 | `cd repo && relay claude`(或 `relay codex`) |
253
+
254
+ 两边都拿到同样的仓库状态 + 同样的过往上下文。需要让另一个 agent 给同一个问题第二个意见时很方便,不用丢掉原来的 session。
255
+
256
+ ### 实战常用 flag 组合
257
+
258
+ ```bash
259
+ # 当前 cwd 跟 session 记录的对不上(worktree / 仓库挪过位置 / 软链)
260
+ relay codex --last
261
+
262
+ # 让接收方也看到你当前未提交的改动
263
+ relay codex --with-diff
264
+
265
+ # 调大 handoff 上限,让原始任务和对话尾部都更完整
266
+ relay codex --max-chars 20000
267
+
268
+ # 只看不启动
269
+ relay codex --dry-run
270
+
271
+ # 排查到底选了哪个 session、为什么选它
272
+ relay codex --debug --dry-run
273
+
274
+ # 信任 transcript,跳过脱敏(少用)
275
+ relay codex --no-redact
276
+ ```
277
+
278
+ ### 不会工作的几种用法
279
+
280
+ - 在源 agent 自己的 REPL 里跑 `relay`。永远在普通 shell 里跑。
281
+ - 一条命令切换多个仓库。`relay` 只针对**当前** git root。
282
+ - 跨机器 handoff。transcript 在本地。真要跨机的话只能手动拷 JSONL。
283
+ - 期望接收方"真正续上"原 session ID。handoff 是结构化上下文摘要,不是 session 导入 —— 见 [FAQ](#faq)。
284
+
285
+ ## 示例:`relay inspect`
286
+
287
+ ```
288
+ $ cd ~/work/my-project
289
+ $ relay inspect
290
+ codex-claude-relay v0.1.0 inspect
291
+
292
+ Git context:
293
+ cwd: /Users/alice/work/my-project
294
+ inRepo: true
295
+ root: /Users/alice/work/my-project
296
+ branch: main
297
+
298
+ Codex sessions (~/.codex/sessions):
299
+ dir exists: true
300
+ count: 137
301
+ best: ~/.codex/sessions/2026/05/14/rollout-2026-05-14T09-15-22-…jsonl
302
+ score=88.7 mtime=2026-05-14T01:22:08.142Z
303
+ cwd=/Users/alice/work/my-project
304
+ reasons: cwd matches git root exactly | recency +28.7 (age 0.4d)
305
+
306
+ Claude Code sessions (~/.claude/projects):
307
+ dir exists: true
308
+ count: 42
309
+ best: ~/.claude/projects/-Users-alice-work-my-project/8c3f….jsonl
310
+ score=130.0 mtime=2026-05-19T14:10:01.000Z
311
+ cwd=/Users/alice/work/my-project
312
+ reasons: inside encoded project dir | cwd matches git root exactly | recency +30.0 (age 0.0d)
313
+
314
+ Binaries on PATH:
315
+ claude: yes
316
+ codex: yes
317
+ ```
318
+
319
+ ## 示例:handoff 实际长什么样
320
+
321
+ ```
322
+ You are continuing work in this repository after a context handoff from
323
+ OpenAI Codex CLI to Anthropic Claude Code.
324
+
325
+ Repository:
326
+ - Path: /Users/alice/work/my-project
327
+ - Branch: main
328
+ - Git status summary:
329
+ M src/server.ts
330
+
331
+ Original task:
332
+ Add rate limiting to the /api/upload endpoint.
333
+
334
+ Subsequent user instructions:
335
+ - use redis, not in-memory
336
+ - ignore /health pings
337
+
338
+ What has already been done / key decisions:
339
+ - Implemented sliding-window limiter in src/middleware/rate.ts
340
+ - Chose redis pipelining over a Lua script for simpler ops
341
+
342
+ Files touched or inspected:
343
+ - src/middleware/rate.ts
344
+ - src/server.ts
345
+ - test/rate.test.ts
346
+
347
+ Commands run (★ = errored):
348
+ - `npm test -- rate`
349
+ - ★ `redis-cli ping`
350
+
351
+ Errors observed:
352
+ - redis-cli ping: Could not connect to Redis at 127.0.0.1:6379
353
+
354
+ Recent conversation tail:
355
+ - User: it should also rate-limit anonymous IPs
356
+ - Assistant: Added a fallback bucket keyed by IP for unauthenticated calls.
357
+
358
+ Safety notes for you, the receiving agent:
359
+ - 不要假设上一个 agent 的结论仍然正确
360
+ - 动手前先看当前文件 / `git status` / `git diff`
361
+ - 优先以当前仓库状态为准,而非 transcript 里的回忆
362
+ ```
363
+
364
+ ## 发现与打分
365
+
366
+ 两边的 provider 都递归遍历对应目录,廉价读出 metadata 后排序:
367
+
368
+ ```
369
+ score = cwd 匹配信号 + 新鲜度衰减
370
+ ```
371
+
372
+ | 信号 | Codex 权重 | Claude 权重 |
373
+ | --------------------------------- | :---------: | :---------: |
374
+ | 记录的 cwd 等于 git root | +60 | +60 |
375
+ | 记录的 cwd 在 git root 之下 | +50 | +50 |
376
+ | 记录的 cwd 路径中包含仓库名 | +25 | +20 |
377
+ | 文件位于 Claude 编码后的项目目录 | — | +40 |
378
+ | 新鲜度:14 天内线性衰减 | +0 … +30 | +0 … +30 |
379
+
380
+ 加 `--last` 跳过打分,强制使用 mtime 最新的那个文件。加 `--debug` 看选中候选的 reasons 字符串。
381
+
382
+ ## 原生 transcript 格式
383
+
384
+ codex-claude-relay 不发明任何文件格式,直接解析两家 CLI 已经写好的内容。
385
+
386
+ **Codex CLI** — 每个 session 一份 JSONL,一行一个事件:
387
+
388
+ ```
389
+ ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl
390
+ ```
391
+
392
+ 每行结构 `{ "type", "payload", "timestamp" }`。重要的 payload 类型:
393
+
394
+ | `payload.type` | 含义 |
395
+ | ---------------------------- | --------------------------------------------------- |
396
+ | `session_meta` | `cwd`、`id`、originator、模型 |
397
+ | `message`(role=user) | 用户输入(`content[].input_text`) |
398
+ | `message`(role=assistant) | 模型回复(`content[].output_text`) |
399
+ | `function_call` | 工具调用(`name`,JSON 字符串 `arguments`) |
400
+ | `function_call_output` | 工具结果(`output` 是抓取到的输出文本) |
401
+
402
+ **Claude Code** — 每个 session 一份 JSONL,按项目目录归组:
403
+
404
+ ```
405
+ ~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonl
406
+ ```
407
+
408
+ `<encoded-cwd>` 是把绝对路径里的 `/` 和 `.` 都替换成 `-`。关键行:
409
+
410
+ | `type` | 含义 |
411
+ | ------------- | ------------------------------------------------------------------- |
412
+ | `user` | 用户消息或多个 `tool_result` 块;同时带 `cwd`、`gitBranch` |
413
+ | `assistant` | 模型回复:text + `tool_use` 块(`name`,`input`) |
414
+
415
+ 工具调用在 `message.content[]` 里以 `{ type: "tool_use" }` 出现;它们的结果落在下一条 `user` 行里以 `{ type: "tool_result" }` 出现。
416
+
417
+ 如果 `~/.claude/projects/<encoded-cwd>/memory/` 存在,里面的 `MEMORY.md` 和它链接的 `.md` 文件会一并拼进 Claude → Codex 方向的 handoff。
418
+
419
+ ## 会被过滤掉的内容
420
+
421
+ 为了让 handoff 可读,解析阶段会丢弃:
422
+
423
+ - Codex 的 `reasoning` 事件(模型的内部思考)
424
+ - Codex 的 `event_msg` / `turn_context` / `token_count` 等框架事件
425
+ - Codex 的 `<environment_context>…` 自动注入消息
426
+ - Claude 的 `<task-notification>`、`<system-reminder>`、单独的 `[Image: source: …]`、slash command 包装等
427
+ - Claude 的 sidechain 消息(`isSidechain: true`)
428
+ - 命中脱敏规则的所有字符串(见下)
429
+
430
+ 每条工具输出会被裁到约 400 字符,避免一个 `cat 大文件` 把别的全挤掉。
431
+
432
+ ## 安全模型
433
+
434
+ | 关注点 | 实现方式 |
435
+ | --------------------------------- | --------------------------------------------------------------------------------------------------------- |
436
+ | 原生文件 | 只读。不会向 `~/.codex/sessions/` 或 `~/.claude/projects/` 写入任何内容 |
437
+ | transcript 中的密钥 | 默认脱敏,规则见下 |
438
+ | 进程列表 / argv 泄漏 | handoff 超过 8 KB 时写入 `0600` 临时文件(外层目录 `0700`,按本次调用单独建),argv 里只放一段简短引用 prompt。子进程退出时临时文件被删除 |
439
+ | Shell 解释 | `spawn(..., { shell: false })`。handoff 永远不会被 shell 二次解释 |
440
+ | 数据陈旧 | 源 session 超过 24 小时未活动时,handoff 头部会自动加 `⚠ stale` 提示 |
441
+
442
+ 脱敏覆盖(含大小写不敏感处):
443
+
444
+ - OpenAI 密钥:`sk-…`、`sk-proj-…`、`sk-ant-…`
445
+ - GitHub token:`ghp_`、`gho_`、`ghu_`、`ghs_`、`ghr_`
446
+ - AWS access key id(`AKIA…`)
447
+ - Google API key(`AIza…`)
448
+ - JWT(三段 base64url + 点号)
449
+ - PEM 私钥块(`-----BEGIN … PRIVATE KEY-----` … `-----END …-----`)
450
+ - `Authorization:` 和 `Set-Cookie:` 头
451
+ - `*SECRET*=`、`*TOKEN*=`、`*PASSWORD*=`、`*API_KEY*=`、`*CREDENTIAL*=` 环境变量风格(值长度 ≥ 6)
452
+
453
+ 只在你信任 transcript 时再加 `--no-redact`。
454
+
455
+ ## 局限
456
+
457
+ | 情况 | 行为 |
458
+ | ------------------------------------------------- | ----------------------------------------------------------------- |
459
+ | transcript 被关闭或删除 | 没东西可读;`relay inspect` 会显示 `count=0` |
460
+ | 两个仓库 basename 相同 | cwd 精确匹配 +60 仍然会赢;可以 `--debug` 看具体原因 |
461
+ | 同一项目在 Claude 里跑过多次 session | cwd 分最高且最新的那次胜出;必要时用 `--last` 钉死 |
462
+ | Codex / Claude 上游 schema 变化 | 解析跳过坏行并在 `--debug` 中报告数量 |
463
+ | 跨机器 handoff | 不支持,transcript 在本地磁盘上 |
464
+ | 巨大的工具输出 | handoff 里每条事件裁到约 400 字符 |
465
+ | Codex / Claude 自己轮转或清理 session 文件 | 文件没了,对应的上下文源也就没了 |
466
+
467
+ ## FAQ
468
+
469
+ **这真能让 Claude「续上」一个 Codex session 吗?**
470
+ 不行。Claude 仍然是开新的原生 session,把 handoff 当作首条用户消息读进去。handoff 是给 agent 看的结构化摘要,不是字面意义上的 session 导入。
471
+
472
+ **它会往磁盘写东西吗?**
473
+ 仅当 prompt 超过 argv 内联上限(约 8 KB)时会落临时文件,位于 `$TMPDIR` 下,子进程退出时删除。
474
+
475
+ **我想自己把 handoff 留底,可以吗?**
476
+ 直接重定向就行:`relay preview codex > .ai/handoff.md`。工具默认不替你写,因为「无状态」是产品的核心定位。
477
+
478
+ **为什么不默认带上 `git diff`?**
479
+ 大多数会话改动的文件你都已经 commit 了,diff 反而是噪声。真有未提交工作时加 `--with-diff`。
480
+
481
+ **为什么是一整段,不切碎?**
482
+ 目标 agent 启动时只读一段初始 prompt。切碎以后它还得自己拼回去。
483
+
484
+ **能在 Codex / Claude 内部跑 `relay` 吗?**
485
+ 能跑,但它会生成「当前正在被读的 session」的 handoff,通常不是你想要的。推荐用法是退出 agent 后在另一个终端窗口跑。
486
+
487
+ **为什么排序看 cwd,不看内容?**
488
+ 便宜又够准。看内容意味着每次都得完整读所有 transcript。cwd 命中 + 新鲜度足够在毫秒级挑出当前仓库的会话。
489
+
490
+ ## 开发
491
+
492
+ ```bash
493
+ npm install
494
+ npm run typecheck # 严格 TS,无任何 suppress
495
+ npm test # node --test 跑 17 个单元测试
496
+ npm run build # tsc → dist/
497
+ node dist/cli.js inspect
498
+ ```
499
+
500
+ 代码结构:
501
+
502
+ ```
503
+ src/
504
+ cli.ts # 参数解析 + 子命令分发
505
+ index.ts # 编程接口导出
506
+ types.ts # 共享类型(仅类型,无运行时)
507
+ git.ts # git rev-parse / 分支 / diff
508
+ parse/jsonl.ts # 流式 JSONL 读取与工具函数
509
+ providers/
510
+ codex.ts # ~/.codex/sessions 的发现与解析
511
+ claude.ts # ~/.claude/projects 的发现 / 解析 / memory
512
+ redact.ts # 密钥模式
513
+ summarize.ts # 事件归纳 + handoff 模板
514
+ launch.ts # child_process spawn + 大 prompt 落临时文件
515
+ test/ # node:test 单元测试
516
+ ```
517
+
518
+ 依赖:仅 `typescript` 与 `tsx` 作为 devDependencies。**零运行时依赖**,CLI 完全跑在 Node 内置模块上。
519
+
520
+ ## License
521
+
522
+ MIT,见 [LICENSE](./LICENSE)。
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};