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/LICENSE +21 -0
- package/README.md +534 -0
- package/README.zh.md +522 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +304 -0
- package/dist/git.d.ts +11 -0
- package/dist/git.js +58 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +10 -0
- package/dist/launch.d.ts +29 -0
- package/dist/launch.js +66 -0
- package/dist/parse/jsonl.d.ts +21 -0
- package/dist/parse/jsonl.js +75 -0
- package/dist/providers/claude.d.ts +36 -0
- package/dist/providers/claude.js +401 -0
- package/dist/providers/codex.d.ts +11 -0
- package/dist/providers/codex.js +310 -0
- package/dist/redact.d.ts +10 -0
- package/dist/redact.js +84 -0
- package/dist/summarize.d.ts +13 -0
- package/dist/summarize.js +241 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +9 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 context-relay contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
# codex-claude-relay
|
|
2
|
+
|
|
3
|
+
Hand off context between **OpenAI Codex CLI** and **Anthropic Claude Code** by reading their native session transcripts. Pick the right past session for the current repo, condense it into one handoff prompt, and launch the other CLI with that prompt as its first message.
|
|
4
|
+
|
|
5
|
+
No database. No daemon. No writes to either tool's session files.
|
|
6
|
+
|
|
7
|
+
🌐 中文文档: [README.zh.md](./README.zh.md)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
codex # work for a while in Codex
|
|
11
|
+
relay claude # condense the Codex session, launch `claude` pre-loaded with it
|
|
12
|
+
relay codex # later: condense the Claude session, launch `codex` pre-loaded with it
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## How it works
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌──────────────────────────────────┐
|
|
19
|
+
│ ~/.codex/sessions/**/ │ ──┐
|
|
20
|
+
│ rollout-*.jsonl │ │
|
|
21
|
+
└──────────────────────────────────┘ │ pick best session for $PWD
|
|
22
|
+
│ (cwd match + recency)
|
|
23
|
+
▼
|
|
24
|
+
┌──────────────────────┐
|
|
25
|
+
│ stream-parse JSONL │
|
|
26
|
+
│ normalize events │
|
|
27
|
+
│ redact secrets │ ─→ handoff (markdown, ~6–12 KB)
|
|
28
|
+
│ render template │
|
|
29
|
+
└──────────────────────┘
|
|
30
|
+
▲
|
|
31
|
+
┌──────────────────────────────────┐ │
|
|
32
|
+
│ ~/.claude/projects/<encoded>/ │ ──┘
|
|
33
|
+
│ <session-uuid>.jsonl │
|
|
34
|
+
│ memory/MEMORY.md │
|
|
35
|
+
└──────────────────────────────────┘
|
|
36
|
+
│
|
|
37
|
+
▼
|
|
38
|
+
spawn `claude` or `codex`
|
|
39
|
+
with the handoff as the
|
|
40
|
+
initial user prompt
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Why
|
|
44
|
+
|
|
45
|
+
Both major coding agents already write rich transcripts to disk. They just don't read each other's. So when you switch tools mid-task — Codex → Claude or Claude → Codex — you spend five minutes pasting back what you were doing, which files were touched, which commands failed.
|
|
46
|
+
|
|
47
|
+
The usual response is a "memory sync" tool. Those almost always introduce a *third* store (vector DB, JSON cache, `.ai/handoff.md`) that neither native tool consults. Now you have three sources of truth and the new one drifts out of date.
|
|
48
|
+
|
|
49
|
+
codex-claude-relay never stores anything. Every invocation re-reads the native transcripts. The two tools remain the only sources of truth.
|
|
50
|
+
|
|
51
|
+
| Approach | Persistent store | Mutates native files | Reads native transcripts |
|
|
52
|
+
| ------------------------------------------- | :--------------: | :------------------: | :----------------------: |
|
|
53
|
+
| Manual copy-paste | — | no | — |
|
|
54
|
+
| Vector-DB memory layer | yes | no | sometimes |
|
|
55
|
+
| `.ai/handoff.md` checked into repo | yes | no | no |
|
|
56
|
+
| **codex-claude-relay** | no | no | yes |
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
Requires Node.js 20+ (check with `node --version`).
|
|
61
|
+
|
|
62
|
+
### Option A — clone, build, link (recommended)
|
|
63
|
+
|
|
64
|
+
`npm install` only fetches the two devDependencies (`typescript`, `tsx`). `npm run build` produces `dist/cli.js`. **`npm link` is the separate step that puts the `relay` command on your `PATH`.** Skipping it is the most common reason for `command not found: relay`.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git clone https://github.com/Picrew/codex-claude-relay
|
|
68
|
+
cd codex-claude-relay
|
|
69
|
+
npm install # fetch devDeps (typescript, tsx)
|
|
70
|
+
npm run build # compile src/ → dist/
|
|
71
|
+
npm link # register `relay` and `codex-claude-relay` globally
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Verify:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
which relay # should print a path under your npm global prefix
|
|
78
|
+
relay --version # should print 0.1.0
|
|
79
|
+
relay inspect # should list discovered sessions
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
If `npm link` reports a permission error, your global `npm` prefix isn't user-writable. Either fix the prefix (`npm config set prefix "$HOME/.npm-global"` and add `$HOME/.npm-global/bin` to `PATH`) or use Option B.
|
|
83
|
+
|
|
84
|
+
### Option B — no link, run via node
|
|
85
|
+
|
|
86
|
+
If you don't want to link globally:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
node /absolute/path/to/codex-claude-relay/dist/cli.js inspect
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
A shell alias is the lightest workaround:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
echo 'alias relay="node $HOME/path/to/codex-claude-relay/dist/cli.js"' >> ~/.zshrc
|
|
96
|
+
source ~/.zshrc
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Option C — install globally from a tarball
|
|
100
|
+
|
|
101
|
+
If the package is published (it currently isn't, but the layout supports it):
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install -g codex-claude-relay
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Requirements for actually launching agents
|
|
108
|
+
|
|
109
|
+
`claude` and `codex` must be on `PATH` for `relay claude` and `relay codex` to spawn them:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
which claude codex
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If either is missing, install it from its vendor — `relay preview`, `relay inspect`, and `--dry-run` still work without them.
|
|
116
|
+
|
|
117
|
+
### Uninstall
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cd codex-claude-relay
|
|
121
|
+
npm unlink -g # remove the global symlink
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Commands
|
|
125
|
+
|
|
126
|
+
| Command | Effect |
|
|
127
|
+
| ------------------------ | --------------------------------------------------------------------- |
|
|
128
|
+
| `relay claude` | Build handoff from latest relevant Codex session → launch `claude` |
|
|
129
|
+
| `relay codex` | Build handoff from latest relevant Claude session → launch `codex` |
|
|
130
|
+
| `relay preview <target>` | Print the handoff that would be sent; don't launch |
|
|
131
|
+
| `relay inspect` | Show what would be picked, with scores and reasons |
|
|
132
|
+
|
|
133
|
+
### Flags
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
--last Use the most recently modified session, skipping the
|
|
137
|
+
cwd-based ranking. Useful when cwd doesn't match.
|
|
138
|
+
--with-diff Append the current `git diff HEAD` to the handoff.
|
|
139
|
+
--max-chars N Cap the rendered handoff length (default 12000).
|
|
140
|
+
--dry-run Build the handoff and print it; do not launch.
|
|
141
|
+
--no-redact Disable secret redaction. Default is ON.
|
|
142
|
+
--debug Verbose discovery / parsing info on stderr.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Walkthrough
|
|
146
|
+
|
|
147
|
+
The two main flows are *Claude Code → Codex* and *Codex → Claude Code*. They are symmetric; pick the one that matches the direction you're switching.
|
|
148
|
+
|
|
149
|
+
### Scenario A: Claude Code → Codex
|
|
150
|
+
|
|
151
|
+
You've been working in Claude Code, you want Codex to continue.
|
|
152
|
+
|
|
153
|
+
**Step 1 — Leave Claude Code (or just open a new terminal).**
|
|
154
|
+
|
|
155
|
+
You don't need to exit cleanly. Claude Code flushes its JSONL transcript as you go, so even mid-conversation the latest events are on disk. Either type `/exit` inside Claude Code, or open a new terminal tab (`Cmd+T` in iTerm / Terminal) and keep Claude running in the background — both work.
|
|
156
|
+
|
|
157
|
+
> ⚠ Do **not** type `relay codex` into the Claude Code prompt itself. The Claude REPL will treat it as a message to the model, not a shell command. Always run `relay` from a regular shell.
|
|
158
|
+
|
|
159
|
+
**Step 2 — `cd` into the repo.**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cd /path/to/your/repo
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The current directory matters: context-relay uses it (plus `git rev-parse --show-toplevel`) to pick the right past session.
|
|
166
|
+
|
|
167
|
+
**Step 3 — Sanity check what would be sent.**
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
relay preview codex --max-chars 8000 | less
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Scroll through and verify:
|
|
174
|
+
|
|
175
|
+
- **Original task** — does the first paragraph match what you originally asked Claude to do?
|
|
176
|
+
- **Subsequent user instructions** — are your follow-up messages there?
|
|
177
|
+
- **Files touched or inspected** — is the list reasonable?
|
|
178
|
+
- **Recent conversation tail** — does it cover the last few exchanges?
|
|
179
|
+
|
|
180
|
+
If the original task is truncated and you want more of it, raise `--max-chars` (e.g. `--max-chars 16000`).
|
|
181
|
+
|
|
182
|
+
If `relay inspect` was already showing the right session at score ≥ 90, you can usually skip this step. Press `q` to exit `less`.
|
|
183
|
+
|
|
184
|
+
**Step 4 — Launch Codex with the handoff.**
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
relay codex
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
You'll see stderr print `codex-claude-relay: launching \`codex\` with handoff (N chars)`, then Codex's TUI opens. The handoff arrives as Codex's first user message:
|
|
191
|
+
|
|
192
|
+
- If the handoff is ≤ 8 KB, it's passed inline as the first message.
|
|
193
|
+
- If it's > 8 KB, context-relay writes it to a `0600` temp file (under `$TMPDIR`) and passes a short reference prompt: *"Read the handoff context file at … and continue."* Codex calls its file-read tool, reads the handoff, and proceeds. The temp file is deleted when Codex exits.
|
|
194
|
+
|
|
195
|
+
Either way, Codex's first response will acknowledge the context (~10 s round-trip). After that you continue typing as usual.
|
|
196
|
+
|
|
197
|
+
**Step 5 — Verify the handoff actually landed.**
|
|
198
|
+
|
|
199
|
+
Once Codex finishes its first reply, ask it something only the prior session would know:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
我之前在 Claude 里问的第一个问题是什么?逐字告诉我。
|
|
203
|
+
列一下 Claude 都动过哪些文件,跑过哪些命令。
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Or, in English:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
Quote my original first question to the previous agent verbatim.
|
|
210
|
+
List every file the previous agent touched and the commands it ran.
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
If Codex can answer accurately, the handoff is working. Continue your work.
|
|
214
|
+
|
|
215
|
+
### Scenario B: Codex → Claude Code
|
|
216
|
+
|
|
217
|
+
Symmetric to Scenario A. You've been working in Codex, you want Claude Code to continue.
|
|
218
|
+
|
|
219
|
+
**Step 1 — Leave Codex (or open a new terminal).** Codex also flushes its rollout JSONL continuously.
|
|
220
|
+
|
|
221
|
+
**Step 2 — `cd` into the repo.**
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
cd /path/to/your/repo
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Step 3 — Preview.**
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
relay preview claude --max-chars 8000 | less
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
If Codex worked in a worktree or under a subdir, the cwd recorded in the session might not match your current git root. `relay inspect` will show the score; if it's low (< 60), try `--last`:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
relay preview claude --last
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Step 4 — Launch.**
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
relay claude
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Claude Code's TUI opens. The handoff becomes its first user message (or temp-file ref for handoffs > 8 KB).
|
|
246
|
+
|
|
247
|
+
**Step 5 — Verify.**
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
Quote my original first question to the previous Codex session verbatim.
|
|
251
|
+
Summarize what Codex got working and what's still open.
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Side-by-side mode (don't actually "switch", run both)
|
|
255
|
+
|
|
256
|
+
You don't have to close one to use the other. Open two terminal tabs:
|
|
257
|
+
|
|
258
|
+
| Tab 1 | Tab 2 |
|
|
259
|
+
| ---------------------------- | ------------------------------ |
|
|
260
|
+
| `codex` (or `claude`) keeps running | `cd repo && relay claude` (or `relay codex`) |
|
|
261
|
+
|
|
262
|
+
Both agents now have the same repo state plus the same prior context. Useful when you want a second opinion on the same problem without losing your original session.
|
|
263
|
+
|
|
264
|
+
### Useful flags in practice
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# Cwd doesn't match the recorded one (worktree, moved repo, symlink, etc.)
|
|
268
|
+
relay codex --last
|
|
269
|
+
|
|
270
|
+
# Receiving agent should see your uncommitted work too
|
|
271
|
+
relay codex --with-diff
|
|
272
|
+
|
|
273
|
+
# Bigger handoff — more original task + more conversation tail
|
|
274
|
+
relay codex --max-chars 20000
|
|
275
|
+
|
|
276
|
+
# See what would happen without launching
|
|
277
|
+
relay codex --dry-run
|
|
278
|
+
|
|
279
|
+
# Diagnose which session was picked and why
|
|
280
|
+
relay codex --debug --dry-run
|
|
281
|
+
|
|
282
|
+
# Trust the transcript, skip redaction (rare)
|
|
283
|
+
relay codex --no-redact
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### What does *not* work
|
|
287
|
+
|
|
288
|
+
- Running `relay` inside the source agent's REPL itself. Always run from a regular shell.
|
|
289
|
+
- Switching between repos in one command. `relay` operates on the **current** git root only.
|
|
290
|
+
- Cross-machine handoff. Transcripts are local. Copy the JSONL across by hand if you really need to.
|
|
291
|
+
- Forging a fake "previous session" so the receiving agent thinks it's literally resuming. The handoff is structured context, not a session import — see [FAQ](#faq).
|
|
292
|
+
|
|
293
|
+
## Example: `relay inspect`
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
$ cd ~/work/my-project
|
|
297
|
+
$ relay inspect
|
|
298
|
+
codex-claude-relay v0.1.0 inspect
|
|
299
|
+
|
|
300
|
+
Git context:
|
|
301
|
+
cwd: /Users/alice/work/my-project
|
|
302
|
+
inRepo: true
|
|
303
|
+
root: /Users/alice/work/my-project
|
|
304
|
+
branch: main
|
|
305
|
+
|
|
306
|
+
Codex sessions (~/.codex/sessions):
|
|
307
|
+
dir exists: true
|
|
308
|
+
count: 137
|
|
309
|
+
best: ~/.codex/sessions/2026/05/14/rollout-2026-05-14T09-15-22-…jsonl
|
|
310
|
+
score=88.7 mtime=2026-05-14T01:22:08.142Z
|
|
311
|
+
cwd=/Users/alice/work/my-project
|
|
312
|
+
reasons: cwd matches git root exactly | recency +28.7 (age 0.4d)
|
|
313
|
+
|
|
314
|
+
Claude Code sessions (~/.claude/projects):
|
|
315
|
+
dir exists: true
|
|
316
|
+
count: 42
|
|
317
|
+
best: ~/.claude/projects/-Users-alice-work-my-project/8c3f….jsonl
|
|
318
|
+
score=130.0 mtime=2026-05-19T14:10:01.000Z
|
|
319
|
+
cwd=/Users/alice/work/my-project
|
|
320
|
+
reasons: inside encoded project dir | cwd matches git root exactly | recency +30.0 (age 0.0d)
|
|
321
|
+
|
|
322
|
+
Claude memory for this project:
|
|
323
|
+
exists: false
|
|
324
|
+
bytes: 0
|
|
325
|
+
|
|
326
|
+
Binaries on PATH:
|
|
327
|
+
claude: yes
|
|
328
|
+
codex: yes
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Example: what's actually in the handoff
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
You are continuing work in this repository after a context handoff from
|
|
335
|
+
OpenAI Codex CLI to Anthropic Claude Code.
|
|
336
|
+
|
|
337
|
+
Repository:
|
|
338
|
+
- Path: /Users/alice/work/my-project
|
|
339
|
+
- Branch: main
|
|
340
|
+
- Git status summary:
|
|
341
|
+
M src/server.ts
|
|
342
|
+
|
|
343
|
+
Original task:
|
|
344
|
+
Add rate limiting to the /api/upload endpoint.
|
|
345
|
+
|
|
346
|
+
Subsequent user instructions:
|
|
347
|
+
- use redis, not in-memory
|
|
348
|
+
- ignore /health pings
|
|
349
|
+
|
|
350
|
+
What has already been done / key decisions:
|
|
351
|
+
- Implemented sliding-window limiter in src/middleware/rate.ts
|
|
352
|
+
- Chose redis pipelining over a Lua script for simpler ops
|
|
353
|
+
|
|
354
|
+
Files touched or inspected:
|
|
355
|
+
- src/middleware/rate.ts
|
|
356
|
+
- src/server.ts
|
|
357
|
+
- test/rate.test.ts
|
|
358
|
+
|
|
359
|
+
Commands run (★ = errored):
|
|
360
|
+
- `npm test -- rate`
|
|
361
|
+
- ★ `redis-cli ping`
|
|
362
|
+
|
|
363
|
+
Errors observed:
|
|
364
|
+
- redis-cli ping: Could not connect to Redis at 127.0.0.1:6379
|
|
365
|
+
|
|
366
|
+
Recent conversation tail:
|
|
367
|
+
- User: it should also rate-limit anonymous IPs
|
|
368
|
+
- Assistant: Added a fallback bucket keyed by IP for unauthenticated calls.
|
|
369
|
+
|
|
370
|
+
Safety notes for you, the receiving agent:
|
|
371
|
+
- Do not assume the prior agent's conclusions are still correct.
|
|
372
|
+
- Re-check current files and `git status` / `git diff` before editing.
|
|
373
|
+
- Prefer the live repository state over anything implied by this transcript.
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Session discovery & ranking
|
|
377
|
+
|
|
378
|
+
Both providers walk the canonical directory recursively, peek each file's metadata cheaply, then rank:
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
score = cwd_match_signal + recency_decay
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
| Signal | Codex weight | Claude weight |
|
|
385
|
+
| ------------------------------------- | :----------: | :-----------: |
|
|
386
|
+
| Recorded cwd equals git root | +60 | +60 |
|
|
387
|
+
| Recorded cwd inside git root | +50 | +50 |
|
|
388
|
+
| Recorded cwd mentions repo name | +25 | +20 |
|
|
389
|
+
| Inside Claude's encoded project dir | — | +40 |
|
|
390
|
+
| Recency: linear decay 0 → 14 days | +0 … +30 | +0 … +30 |
|
|
391
|
+
|
|
392
|
+
Pass `--last` to skip ranking and force the most-recent-by-mtime file. Pass `--debug` to see the reasons string for the chosen candidate.
|
|
393
|
+
|
|
394
|
+
## Native transcript formats
|
|
395
|
+
|
|
396
|
+
codex-claude-relay parses the formats the official CLIs already write. It does not invent any file shape.
|
|
397
|
+
|
|
398
|
+
**Codex CLI** — one JSONL per session, one line per event:
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Each line is `{ "type", "payload", "timestamp" }`. Useful payload types:
|
|
405
|
+
|
|
406
|
+
| `payload.type` | What it carries |
|
|
407
|
+
| ------------------------ | ---------------------------------------------- |
|
|
408
|
+
| `session_meta` | `cwd`, `id`, originator, model |
|
|
409
|
+
| `message` (role=user) | The user's message turn (`content[].input_text`) |
|
|
410
|
+
| `message` (role=assistant) | The model's message turn (`content[].output_text`) |
|
|
411
|
+
| `function_call` | Tool invocation (`name`, JSON-string `arguments`) |
|
|
412
|
+
| `function_call_output` | Tool result (`output` is the captured text) |
|
|
413
|
+
|
|
414
|
+
**Claude Code** — one JSONL per session, grouped per project:
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonl
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
`<encoded-cwd>` is the absolute project path with `/` and `.` replaced by `-`. Each useful line:
|
|
421
|
+
|
|
422
|
+
| `type` | Carries |
|
|
423
|
+
| ---------- | -------------------------------------------------------------------- |
|
|
424
|
+
| `user` | User turn or `tool_result` block(s); also `cwd`, `gitBranch` |
|
|
425
|
+
| `assistant`| Model turn: text + `tool_use` blocks (`name`, `input`) |
|
|
426
|
+
|
|
427
|
+
Tool calls live inside `message.content[]` as `{ type: "tool_use" }`. Their results land inside the next `user` line as `{ type: "tool_result" }`.
|
|
428
|
+
|
|
429
|
+
If `~/.claude/projects/<encoded-cwd>/memory/` exists, its `MEMORY.md` and the `.md` files it links are included when generating the Claude → Codex handoff.
|
|
430
|
+
|
|
431
|
+
## What gets filtered out
|
|
432
|
+
|
|
433
|
+
To keep the handoff readable, the parser drops:
|
|
434
|
+
|
|
435
|
+
- Codex `reasoning` events (the model's private chain-of-thought)
|
|
436
|
+
- Codex `event_msg` / `turn_context` / `token_count` framing
|
|
437
|
+
- Codex environment-context user messages (`<environment_context>…`)
|
|
438
|
+
- Claude `<task-notification>`, `<system-reminder>`, lone `[Image: source: …]` markers, slash-command framing
|
|
439
|
+
- Claude sidechain messages (`isSidechain: true`)
|
|
440
|
+
- Anything matching the secret-redaction rules (see below)
|
|
441
|
+
|
|
442
|
+
Tool outputs are clipped to ~400 chars per event so a single noisy `cat large-file` doesn't crowd out everything else.
|
|
443
|
+
|
|
444
|
+
## Safety
|
|
445
|
+
|
|
446
|
+
| Concern | What codex-claude-relay does |
|
|
447
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
448
|
+
| Native files | Opened read-only. Never written to `~/.codex/sessions/` or `~/.claude/projects/`. |
|
|
449
|
+
| Secrets in transcripts | Redacted by default. See list below. |
|
|
450
|
+
| Process-list / argv leakage | Handoffs > 8 KB are written to a `0600` temp file in a per-invocation `0700` dir; only a short ref prompt goes in `argv`. The temp file is unlinked when the child exits. |
|
|
451
|
+
| Shell interpolation | `spawn(..., { shell: false })`. The handoff is never interpreted by a shell. |
|
|
452
|
+
| Stale data | If the source session is > 24 h old, the handoff includes a `⚠ stale` notice. |
|
|
453
|
+
|
|
454
|
+
Redaction covers (case-insensitive where applicable):
|
|
455
|
+
|
|
456
|
+
- OpenAI keys: `sk-…`, `sk-proj-…`, `sk-ant-…`
|
|
457
|
+
- GitHub tokens: `ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_`
|
|
458
|
+
- AWS access key IDs (`AKIA…`)
|
|
459
|
+
- Google API keys (`AIza…`)
|
|
460
|
+
- JWTs (three base64url segments separated by dots)
|
|
461
|
+
- PEM private key blocks (`-----BEGIN … PRIVATE KEY-----` … `-----END …-----`)
|
|
462
|
+
- `Authorization:` and `Set-Cookie:` headers
|
|
463
|
+
- Env-var style `*SECRET*=`, `*TOKEN*=`, `*PASSWORD*=`, `*API_KEY*=`, `*CREDENTIAL*=` with ≥ 6-char values
|
|
464
|
+
|
|
465
|
+
Pass `--no-redact` only when you trust the transcript.
|
|
466
|
+
|
|
467
|
+
## Limitations
|
|
468
|
+
|
|
469
|
+
| Situation | What happens |
|
|
470
|
+
| ---------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
471
|
+
| Transcripts deleted or disabled | Nothing to read; `relay inspect` reports `count=0`. |
|
|
472
|
+
| Two repos with the same basename | Cwd-exact-match (+60) still wins; otherwise check `--debug`. |
|
|
473
|
+
| Same project across multiple Claude sessions | Highest cwd-score + most recent wins; pin with `--last` if you need to. |
|
|
474
|
+
| Codex/Claude transcript schema changes upstream | Parser skips malformed lines and reports the count in `--debug`. |
|
|
475
|
+
| Cross-machine handoff | Not supported. Transcripts are local-only. |
|
|
476
|
+
| Huge tool outputs | Clipped to ~400 chars per event in the handoff. |
|
|
477
|
+
| Codex/Claude rotate or compact session files | Once the file is gone, so is the context source. |
|
|
478
|
+
|
|
479
|
+
## FAQ
|
|
480
|
+
|
|
481
|
+
**Does Claude actually resume a Codex session?**
|
|
482
|
+
No. Claude starts a fresh native session and reads the handoff as its first user message. The handoff is structured for an agent to consume but it is not a literal session import.
|
|
483
|
+
|
|
484
|
+
**Does codex-claude-relay write anywhere on disk?**
|
|
485
|
+
Only the optional temp file used when the prompt exceeds the inline-argv limit (~8 KB). It lives under `$TMPDIR` and is unlinked when the child exits.
|
|
486
|
+
|
|
487
|
+
**Can I save the handoff into my repo if I want to?**
|
|
488
|
+
Sure — pipe it: `relay preview codex > .ai/handoff.md`. codex-claude-relay won't do this by default because the point is to stay stateless.
|
|
489
|
+
|
|
490
|
+
**Why not always include `git diff`?**
|
|
491
|
+
Most sessions already touched files you've committed; the diff would be noise. Pass `--with-diff` when uncommitted work actually matters.
|
|
492
|
+
|
|
493
|
+
**Why one big prompt, not a folder of fragments?**
|
|
494
|
+
The receiving agent reads one prompt at the start of its turn. Splitting just forces it to re-concatenate.
|
|
495
|
+
|
|
496
|
+
**Can I run `relay` from inside Codex or Claude?**
|
|
497
|
+
You can, but it'll generate a handoff for the *current* session it's reading — usually not what you want. The intended use is in a separate shell, after you step out of the agent.
|
|
498
|
+
|
|
499
|
+
**Why does ranking depend on cwd, not on content?**
|
|
500
|
+
Cheap and accurate enough in practice. Content-based ranking would need to read every transcript fully on every invocation. cwd-match + recency picks the right session for the current repo in milliseconds.
|
|
501
|
+
|
|
502
|
+
## Development
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
npm install
|
|
506
|
+
npm run typecheck # strict TS, zero suppressions
|
|
507
|
+
npm test # node --test on the 17 unit tests
|
|
508
|
+
npm run build # tsc → dist/
|
|
509
|
+
node dist/cli.js inspect
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Project layout:
|
|
513
|
+
|
|
514
|
+
```
|
|
515
|
+
src/
|
|
516
|
+
cli.ts # arg parsing, command dispatch
|
|
517
|
+
index.ts # programmatic exports
|
|
518
|
+
types.ts # shared types only — no runtime
|
|
519
|
+
git.ts # git rev-parse / branch / diff
|
|
520
|
+
parse/jsonl.ts # streaming JSONL reader + helpers
|
|
521
|
+
providers/
|
|
522
|
+
codex.ts # ~/.codex/sessions discovery + parsing
|
|
523
|
+
claude.ts # ~/.claude/projects discovery + parsing + memory
|
|
524
|
+
redact.ts # secret patterns
|
|
525
|
+
summarize.ts # event digest + handoff template
|
|
526
|
+
launch.ts # child_process spawn + temp-file fallback
|
|
527
|
+
test/ # node:test unit tests
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
Dependencies: only `typescript` and `tsx` as devDependencies. Zero runtime dependencies — the CLI uses Node built-ins exclusively.
|
|
531
|
+
|
|
532
|
+
## License
|
|
533
|
+
|
|
534
|
+
MIT. See [LICENSE](./LICENSE).
|