tandem-cli 0.2.0__tar.gz → 0.3.0__tar.gz
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.
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/.claude/settings.local.json +6 -1
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/.gitignore +3 -0
- tandem_cli-0.3.0/PKG-INFO +183 -0
- tandem_cli-0.3.0/README.md +157 -0
- tandem_cli-0.3.0/docs/configuration.md +77 -0
- tandem_cli-0.3.0/docs/development.md +29 -0
- tandem_cli-0.3.0/docs/how-it-works.md +106 -0
- tandem_cli-0.3.0/docs/plans/2026-08-09-meta-harness-frame.md +1860 -0
- tandem_cli-0.3.0/docs/plans/2026-08-09-readme-restructure.md +367 -0
- tandem_cli-0.3.0/docs/plans/2026-08-10-idle-flip-status-probe.md +595 -0
- tandem_cli-0.3.0/docs/plans/2026-08-10-retire-interstitial-prompt.md +857 -0
- tandem_cli-0.3.0/docs/plans/2026-08-13-pr1-nharness-core.md +1813 -0
- tandem_cli-0.3.0/docs/plans/2026-08-13-pr2-opencode-adapter.md +1907 -0
- tandem_cli-0.3.0/docs/specs/2026-08-09-meta-harness-frame-design.md +186 -0
- tandem_cli-0.3.0/docs/specs/2026-08-09-model-pin-recovery-design.md +140 -0
- tandem_cli-0.3.0/docs/specs/2026-08-09-readme-restructure-design.md +117 -0
- tandem_cli-0.3.0/docs/specs/2026-08-10-remove-interstitial-prompt-design.md +102 -0
- tandem_cli-0.3.0/docs/specs/2026-08-13-opencode-harness-design.md +442 -0
- tandem_cli-0.3.0/docs/subagents.md +132 -0
- tandem_cli-0.3.0/docs/superpowers/plans/2026-08-11-process-warmup-live-validation.md +97 -0
- tandem_cli-0.3.0/docs/superpowers/plans/2026-08-11-process-warmup.md +1706 -0
- tandem_cli-0.3.0/docs/superpowers/plans/2026-08-12-process-warmup-pipelined.md +286 -0
- tandem_cli-0.3.0/docs/superpowers/specs/2026-08-10-idle-flip-status-probe.md +101 -0
- tandem_cli-0.3.0/docs/superpowers/specs/2026-08-11-process-warmup-design.md +221 -0
- tandem_cli-0.3.0/docs/superpowers/specs/2026-08-12-process-warmup-pipelined-design.md +125 -0
- tandem_cli-0.3.0/docs/why.md +69 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/plugin/.claude-plugin/plugin.json +1 -1
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/plugin/README.md +18 -11
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/plugin/agents/codex-worker.md +8 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/plugin/agents/gpt.md +8 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/pyproject.toml +1 -1
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/cli.py +171 -114
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/compat.py +7 -2
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/config.py +66 -1
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/constants.py +1 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/converter.py +5 -8
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/doctor.py +50 -129
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/events.py +30 -15
- tandem_cli-0.3.0/src/tandem/flip.py +325 -0
- tandem_cli-0.3.0/src/tandem/frame.py +278 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/harness/__init__.py +0 -4
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/harness/base.py +55 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/harness/claude_code.py +111 -11
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/harness/codex.py +41 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/hookroute.py +36 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/ops.py +121 -52
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/paths.py +8 -0
- tandem_cli-0.3.0/src/tandem/pinstash.py +97 -0
- tandem_cli-0.3.0/src/tandem/ptyrun.py +433 -0
- tandem_cli-0.3.0/src/tandem/runner.py +828 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/state.py +81 -59
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/sync.py +16 -25
- tandem_cli-0.3.0/src/tandem/warm.py +171 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/conftest.py +119 -9
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_cli.py +44 -27
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_config.py +73 -1
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_converter.py +11 -5
- tandem_cli-0.3.0/tests/test_flip.py +586 -0
- tandem_cli-0.3.0/tests/test_frame.py +559 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_hookroute.py +65 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_memory_doctor.py +37 -1
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_ops.py +94 -12
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_parse.py +41 -2
- tandem_cli-0.3.0/tests/test_participants.py +109 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_paths_compat.py +16 -0
- tandem_cli-0.3.0/tests/test_pinstash.py +94 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_plugin.py +8 -1
- tandem_cli-0.3.0/tests/test_ptyrun.py +560 -0
- tandem_cli-0.3.0/tests/test_runner.py +1704 -0
- tandem_cli-0.3.0/tests/test_state.py +185 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_sub.py +120 -28
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_sync.py +3 -3
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_tail.py +10 -6
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_toolmap.py +16 -10
- tandem_cli-0.3.0/tests/test_warm.py +223 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/uv.lock +1 -1
- tandem_cli-0.2.0/.superpowers/sdd/.gitignore +0 -1
- tandem_cli-0.2.0/PKG-INFO +0 -389
- tandem_cli-0.2.0/README.md +0 -363
- tandem_cli-0.2.0/src/tandem/ptyrun.py +0 -99
- tandem_cli-0.2.0/src/tandem/runner.py +0 -261
- tandem_cli-0.2.0/src/tandem/shell.py +0 -207
- tandem_cli-0.2.0/tests/test_ptyrun.py +0 -14
- tandem_cli-0.2.0/tests/test_runner.py +0 -104
- tandem_cli-0.2.0/tests/test_shell.py +0 -402
- tandem_cli-0.2.0/tests/test_state.py +0 -147
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/.claude-plugin/marketplace.json +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/LICENSE +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/formats.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/gpt-subagent.gif +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/plans/2026-07-29-native-tool-call-translation.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/plans/2026-07-31-codex-subagents.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/plans/2026-08-01-plugin-marketplace-install.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/plans/2026-08-01-sandbox-consent-and-manual-routing.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/plans/2026-08-02-plugin-auto-install.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/plans/2026-08-03-manual-default-model-passthrough.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/plans/2026-08-06-harness-startup-args.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/specs/2026-07-29-native-tool-call-translation-design.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/specs/2026-07-31-codex-subagents-design.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/specs/2026-08-02-plugin-auto-install-design.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/specs/2026-08-03-manual-default-model-passthrough-design.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/specs/2026-08-06-harness-startup-args-design.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/superpowers/plans/2026-07-29-tandem-shell.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/docs/superpowers/specs/2026-07-29-tandem-shell-design.md +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/plugin/hooks/hooks.json +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/__init__.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/memory_sync.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/modelcat.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/plugin_setup.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/summarize.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/tailer.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/toolmap.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/src/tandem/util.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/golden/claude-probe.jsonl +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/golden/codex-probe.jsonl +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_modelcat.py +0 -0
- {tandem_cli-0.2.0 → tandem_cli-0.3.0}/tests/test_plugin_setup.py +0 -0
|
@@ -7,7 +7,12 @@
|
|
|
7
7
|
"Bash(curl -sL --max-time 30 -o bl_books_full.csv \"https://raw.githubusercontent.com/realpython/python-data-cleaning/master/Datasets/BL-Flickr-Images-Book.csv\")",
|
|
8
8
|
"Bash(curl -sL --max-time 30 -o chipotle.tsv \"https://raw.githubusercontent.com/justmarkham/DAT8/master/data/chipotle.tsv\")",
|
|
9
9
|
"Bash(git reset *)",
|
|
10
|
-
"Bash(/Users/bhavya/.claude/plugins/cache/claude-plugins-official/superpowers/6.2.0/skills/subagent-driven-development/scripts/task-brief *)"
|
|
10
|
+
"Bash(/Users/bhavya/.claude/plugins/cache/claude-plugins-official/superpowers/6.2.0/skills/subagent-driven-development/scripts/task-brief *)",
|
|
11
|
+
"WebFetch(domain:subreddittraffic.live)",
|
|
12
|
+
"Bash(grep -n *)",
|
|
13
|
+
"Read(//Users/bhavya/.tandem/**)",
|
|
14
|
+
"Read(//Users/bhavya/.config/**)",
|
|
15
|
+
"Bash(uv run *)"
|
|
11
16
|
]
|
|
12
17
|
}
|
|
13
18
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tandem-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Meta-harness that runs Claude Code and Codex CLI with live trace sync.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Bhavya6187/tandem
|
|
6
|
+
Project-URL: Repository, https://github.com/Bhavya6187/tandem
|
|
7
|
+
Project-URL: Issues, https://github.com/Bhavya6187/tandem/issues
|
|
8
|
+
Author-email: Bhavya Agarwal <bhavya.6187@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,claude-code,cli,codex,session-sync
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: POSIX
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: click>=8.1
|
|
22
|
+
Requires-Dist: pexpect>=4.9
|
|
23
|
+
Requires-Dist: pydantic>=2.7
|
|
24
|
+
Requires-Dist: watchdog>=4.0
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
# 🤝 tandem
|
|
30
|
+
|
|
31
|
+
**One coding session. Two AI agents. Zero lost context.**
|
|
32
|
+
|
|
33
|
+
Run [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and
|
|
34
|
+
[OpenAI Codex CLI](https://github.com/openai/codex) as a single paired
|
|
35
|
+
session — each model in its own native harness. Work in either one,
|
|
36
|
+
flip to the other with **Ctrl-]**, and pick up exactly where you left
|
|
37
|
+
off. Only one model runs per turn; the other stays in sync through pure
|
|
38
|
+
local file translation.
|
|
39
|
+
|
|
40
|
+
[](https://github.com/Bhavya6187/tandem/actions/workflows/ci.yml)
|
|
41
|
+
[](https://pypi.org/project/tandem-cli/)
|
|
42
|
+
[](https://github.com/Bhavya6187/tandem/blob/main/pyproject.toml)
|
|
43
|
+
[](https://github.com/Bhavya6187/tandem/blob/main/LICENSE)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv tool install tandem-cli
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
You'll need Python 3.11+ and the `claude` and `codex` CLIs on your PATH.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv tool install tandem-cli # or: pip install tandem-cli
|
|
61
|
+
cd your-project
|
|
62
|
+
tandem # fresh paired session; drops you into claude
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Work normally — that's the real Claude Code TUI. When you want the other
|
|
66
|
+
model, press **Ctrl-]**: tandem closes out the harness at the turn
|
|
67
|
+
boundary and reopens the same conversation in Codex a couple of seconds
|
|
68
|
+
later. Press it again to come back. The bottom row tracks who you're
|
|
69
|
+
facing:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
claude ● │ codex ○ ^] flips
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Pressed mid-turn, the flip arms and fires the moment the model finishes
|
|
76
|
+
(the bar says so; press again to cancel).
|
|
77
|
+
|
|
78
|
+
Exit the harness the usual way and you're back at your shell, with the
|
|
79
|
+
session saved and a hint for picking it up again:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
to continue this session: tandem resume a1b2c3d4e5f6
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Come back anytime:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
tandem resume # most recent session in this directory
|
|
89
|
+
tandem resume a1b2c3d4e5f6 # a specific one (id from the exit hint)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Why tandem?
|
|
93
|
+
|
|
94
|
+
### 🖥️ One CLI, two harnesses, zero ceremony.
|
|
95
|
+
|
|
96
|
+
`tandem` is the terminal you live in. It fronts the real Claude Code or
|
|
97
|
+
Codex TUI — pixel-for-pixel native — and **Ctrl-]** flips to the other
|
|
98
|
+
one in a couple of seconds, same conversation, same files, same history.
|
|
99
|
+
A one-line tab bar on the bottom row shows which model you're facing;
|
|
100
|
+
everything above it is the untouched native UI.
|
|
101
|
+
|
|
102
|
+
### 🆕 0.2 — GPT subagents inside Claude Code
|
|
103
|
+
|
|
104
|
+
Type "get the code reviewed by gpt" and tandem's plugin hands the task
|
|
105
|
+
to a local codex worker; the verdict lands back in your Claude session,
|
|
106
|
+
and your Claude quota stays on the main thread. Pin any model your
|
|
107
|
+
codex account offers ("ask sol to review it") or set a cheap default
|
|
108
|
+
once. Setup and routing:
|
|
109
|
+
[GPT subagents guide](https://github.com/Bhavya6187/tandem/blob/main/docs/subagents.md).
|
|
110
|
+
|
|
111
|
+

|
|
112
|
+
|
|
113
|
+
### ⏳ Hit a usage limit? Just keep going.
|
|
114
|
+
|
|
115
|
+
Claude runs out of its usage window mid-refactor? Press **Ctrl-]** and
|
|
116
|
+
Codex continues the **same conversation** a second later — same files,
|
|
117
|
+
same history, same plan. Two subscriptions become one long runway.
|
|
118
|
+
|
|
119
|
+
### 🌩️ Immune to outages.
|
|
120
|
+
|
|
121
|
+
An Anthropic or OpenAI outage doesn't stop your work: **Ctrl-]**, keep
|
|
122
|
+
going in the other harness, and flip back whenever it clears.
|
|
123
|
+
|
|
124
|
+
Five more reasons — subscriptions not API bills, two model families on
|
|
125
|
+
one problem, native harnesses, instant switching, privacy:
|
|
126
|
+
[Why tandem?](https://github.com/Bhavya6187/tandem/blob/main/docs/why.md)
|
|
127
|
+
|
|
128
|
+
## How it works
|
|
129
|
+
|
|
130
|
+
- Only one model runs per turn — the other side is never invoked to
|
|
131
|
+
"catch up".
|
|
132
|
+
- As you work, tandem translates the growing transcript into the other
|
|
133
|
+
CLI's native session format — pure local file I/O, no model calls —
|
|
134
|
+
so the other side is always resume-ready.
|
|
135
|
+
- Each CLI is the real thing running on a PTY: your keybindings, slash
|
|
136
|
+
commands, and MCP servers all work exactly as they do today — tandem
|
|
137
|
+
reserves exactly one key (Ctrl-]) and one terminal row (the tab bar).
|
|
138
|
+
- A flip waits for the turn boundary, exits the fronted CLI gracefully,
|
|
139
|
+
lets the sync settle, and resumes the other side — no stop in
|
|
140
|
+
between.
|
|
141
|
+
- Everything stays local: the CLIs' own session files plus a small
|
|
142
|
+
SQLite database in `~/.tandem`. No cloud sync, no telemetry.
|
|
143
|
+
- Uninstall tandem tomorrow and both sessions still resume natively
|
|
144
|
+
with `claude --resume` and `codex resume`.
|
|
145
|
+
|
|
146
|
+
Full mechanics — sync engine, crash safety, compatibility ranges:
|
|
147
|
+
[How tandem works](https://github.com/Bhavya6187/tandem/blob/main/docs/how-it-works.md).
|
|
148
|
+
|
|
149
|
+
## Command cheat sheet
|
|
150
|
+
|
|
151
|
+
| Command | What it does |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| `tandem` | Start a fresh paired session (Claude active; `--active codex` to flip) |
|
|
154
|
+
| `Ctrl-]` | Flip to the other harness from inside a running session (rebindable in `[frame]`) |
|
|
155
|
+
| `tandem resume [id]` | Continue the most recent (or a specific) session |
|
|
156
|
+
| `tandem run --on codex "…"` | One-off prompt to the *other* agent, with full context |
|
|
157
|
+
| `tandem sub "…"` | Run one delegated task on a codex model (what GPT subagents use under the hood) |
|
|
158
|
+
| `tandem status` | Show pairing, roles, and sync position |
|
|
159
|
+
| `tandem plugin install` | Install the Claude Code plugin (also offered on first `tandem` launch) |
|
|
160
|
+
|
|
161
|
+
Three maintenance commands round it out: `tandem doctor` (health
|
|
162
|
+
check), `tandem sync` (manual catch-up translation), and `tandem
|
|
163
|
+
sync-mcp` (share MCP server configs between the tools).
|
|
164
|
+
|
|
165
|
+
## Docs
|
|
166
|
+
|
|
167
|
+
- [GPT subagents](https://github.com/Bhavya6187/tandem/blob/main/docs/subagents.md) —
|
|
168
|
+
plugin install, worker model, routing modes, sandbox & trust boundary
|
|
169
|
+
- [Why tandem?](https://github.com/Bhavya6187/tandem/blob/main/docs/why.md) —
|
|
170
|
+
the full pitch, all eight reasons
|
|
171
|
+
- [Configuration](https://github.com/Bhavya6187/tandem/blob/main/docs/configuration.md) —
|
|
172
|
+
the optional `~/.tandem/config.toml`: subagent workers, per-harness
|
|
173
|
+
startup args, the flip key and tab bar
|
|
174
|
+
- [How tandem works](https://github.com/Bhavya6187/tandem/blob/main/docs/how-it-works.md) —
|
|
175
|
+
sync engine, PTY passthrough, compatibility, where your data lives
|
|
176
|
+
- [Developing tandem](https://github.com/Bhavya6187/tandem/blob/main/docs/development.md) —
|
|
177
|
+
dev setup and the converter adapter interface
|
|
178
|
+
- [Observed session formats](https://github.com/Bhavya6187/tandem/blob/main/docs/formats.md) —
|
|
179
|
+
the claude/codex transcript details tandem is pinned against
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
[MIT](https://github.com/Bhavya6187/tandem/blob/main/LICENSE)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🤝 tandem
|
|
4
|
+
|
|
5
|
+
**One coding session. Two AI agents. Zero lost context.**
|
|
6
|
+
|
|
7
|
+
Run [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and
|
|
8
|
+
[OpenAI Codex CLI](https://github.com/openai/codex) as a single paired
|
|
9
|
+
session — each model in its own native harness. Work in either one,
|
|
10
|
+
flip to the other with **Ctrl-]**, and pick up exactly where you left
|
|
11
|
+
off. Only one model runs per turn; the other stays in sync through pure
|
|
12
|
+
local file translation.
|
|
13
|
+
|
|
14
|
+
[](https://github.com/Bhavya6187/tandem/actions/workflows/ci.yml)
|
|
15
|
+
[](https://pypi.org/project/tandem-cli/)
|
|
16
|
+
[](https://github.com/Bhavya6187/tandem/blob/main/pyproject.toml)
|
|
17
|
+
[](https://github.com/Bhavya6187/tandem/blob/main/LICENSE)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv tool install tandem-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
You'll need Python 3.11+ and the `claude` and `codex` CLIs on your PATH.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv tool install tandem-cli # or: pip install tandem-cli
|
|
35
|
+
cd your-project
|
|
36
|
+
tandem # fresh paired session; drops you into claude
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Work normally — that's the real Claude Code TUI. When you want the other
|
|
40
|
+
model, press **Ctrl-]**: tandem closes out the harness at the turn
|
|
41
|
+
boundary and reopens the same conversation in Codex a couple of seconds
|
|
42
|
+
later. Press it again to come back. The bottom row tracks who you're
|
|
43
|
+
facing:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
claude ● │ codex ○ ^] flips
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Pressed mid-turn, the flip arms and fires the moment the model finishes
|
|
50
|
+
(the bar says so; press again to cancel).
|
|
51
|
+
|
|
52
|
+
Exit the harness the usual way and you're back at your shell, with the
|
|
53
|
+
session saved and a hint for picking it up again:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
to continue this session: tandem resume a1b2c3d4e5f6
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Come back anytime:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
tandem resume # most recent session in this directory
|
|
63
|
+
tandem resume a1b2c3d4e5f6 # a specific one (id from the exit hint)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Why tandem?
|
|
67
|
+
|
|
68
|
+
### 🖥️ One CLI, two harnesses, zero ceremony.
|
|
69
|
+
|
|
70
|
+
`tandem` is the terminal you live in. It fronts the real Claude Code or
|
|
71
|
+
Codex TUI — pixel-for-pixel native — and **Ctrl-]** flips to the other
|
|
72
|
+
one in a couple of seconds, same conversation, same files, same history.
|
|
73
|
+
A one-line tab bar on the bottom row shows which model you're facing;
|
|
74
|
+
everything above it is the untouched native UI.
|
|
75
|
+
|
|
76
|
+
### 🆕 0.2 — GPT subagents inside Claude Code
|
|
77
|
+
|
|
78
|
+
Type "get the code reviewed by gpt" and tandem's plugin hands the task
|
|
79
|
+
to a local codex worker; the verdict lands back in your Claude session,
|
|
80
|
+
and your Claude quota stays on the main thread. Pin any model your
|
|
81
|
+
codex account offers ("ask sol to review it") or set a cheap default
|
|
82
|
+
once. Setup and routing:
|
|
83
|
+
[GPT subagents guide](https://github.com/Bhavya6187/tandem/blob/main/docs/subagents.md).
|
|
84
|
+
|
|
85
|
+

|
|
86
|
+
|
|
87
|
+
### ⏳ Hit a usage limit? Just keep going.
|
|
88
|
+
|
|
89
|
+
Claude runs out of its usage window mid-refactor? Press **Ctrl-]** and
|
|
90
|
+
Codex continues the **same conversation** a second later — same files,
|
|
91
|
+
same history, same plan. Two subscriptions become one long runway.
|
|
92
|
+
|
|
93
|
+
### 🌩️ Immune to outages.
|
|
94
|
+
|
|
95
|
+
An Anthropic or OpenAI outage doesn't stop your work: **Ctrl-]**, keep
|
|
96
|
+
going in the other harness, and flip back whenever it clears.
|
|
97
|
+
|
|
98
|
+
Five more reasons — subscriptions not API bills, two model families on
|
|
99
|
+
one problem, native harnesses, instant switching, privacy:
|
|
100
|
+
[Why tandem?](https://github.com/Bhavya6187/tandem/blob/main/docs/why.md)
|
|
101
|
+
|
|
102
|
+
## How it works
|
|
103
|
+
|
|
104
|
+
- Only one model runs per turn — the other side is never invoked to
|
|
105
|
+
"catch up".
|
|
106
|
+
- As you work, tandem translates the growing transcript into the other
|
|
107
|
+
CLI's native session format — pure local file I/O, no model calls —
|
|
108
|
+
so the other side is always resume-ready.
|
|
109
|
+
- Each CLI is the real thing running on a PTY: your keybindings, slash
|
|
110
|
+
commands, and MCP servers all work exactly as they do today — tandem
|
|
111
|
+
reserves exactly one key (Ctrl-]) and one terminal row (the tab bar).
|
|
112
|
+
- A flip waits for the turn boundary, exits the fronted CLI gracefully,
|
|
113
|
+
lets the sync settle, and resumes the other side — no stop in
|
|
114
|
+
between.
|
|
115
|
+
- Everything stays local: the CLIs' own session files plus a small
|
|
116
|
+
SQLite database in `~/.tandem`. No cloud sync, no telemetry.
|
|
117
|
+
- Uninstall tandem tomorrow and both sessions still resume natively
|
|
118
|
+
with `claude --resume` and `codex resume`.
|
|
119
|
+
|
|
120
|
+
Full mechanics — sync engine, crash safety, compatibility ranges:
|
|
121
|
+
[How tandem works](https://github.com/Bhavya6187/tandem/blob/main/docs/how-it-works.md).
|
|
122
|
+
|
|
123
|
+
## Command cheat sheet
|
|
124
|
+
|
|
125
|
+
| Command | What it does |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `tandem` | Start a fresh paired session (Claude active; `--active codex` to flip) |
|
|
128
|
+
| `Ctrl-]` | Flip to the other harness from inside a running session (rebindable in `[frame]`) |
|
|
129
|
+
| `tandem resume [id]` | Continue the most recent (or a specific) session |
|
|
130
|
+
| `tandem run --on codex "…"` | One-off prompt to the *other* agent, with full context |
|
|
131
|
+
| `tandem sub "…"` | Run one delegated task on a codex model (what GPT subagents use under the hood) |
|
|
132
|
+
| `tandem status` | Show pairing, roles, and sync position |
|
|
133
|
+
| `tandem plugin install` | Install the Claude Code plugin (also offered on first `tandem` launch) |
|
|
134
|
+
|
|
135
|
+
Three maintenance commands round it out: `tandem doctor` (health
|
|
136
|
+
check), `tandem sync` (manual catch-up translation), and `tandem
|
|
137
|
+
sync-mcp` (share MCP server configs between the tools).
|
|
138
|
+
|
|
139
|
+
## Docs
|
|
140
|
+
|
|
141
|
+
- [GPT subagents](https://github.com/Bhavya6187/tandem/blob/main/docs/subagents.md) —
|
|
142
|
+
plugin install, worker model, routing modes, sandbox & trust boundary
|
|
143
|
+
- [Why tandem?](https://github.com/Bhavya6187/tandem/blob/main/docs/why.md) —
|
|
144
|
+
the full pitch, all eight reasons
|
|
145
|
+
- [Configuration](https://github.com/Bhavya6187/tandem/blob/main/docs/configuration.md) —
|
|
146
|
+
the optional `~/.tandem/config.toml`: subagent workers, per-harness
|
|
147
|
+
startup args, the flip key and tab bar
|
|
148
|
+
- [How tandem works](https://github.com/Bhavya6187/tandem/blob/main/docs/how-it-works.md) —
|
|
149
|
+
sync engine, PTY passthrough, compatibility, where your data lives
|
|
150
|
+
- [Developing tandem](https://github.com/Bhavya6187/tandem/blob/main/docs/development.md) —
|
|
151
|
+
dev setup and the converter adapter interface
|
|
152
|
+
- [Observed session formats](https://github.com/Bhavya6187/tandem/blob/main/docs/formats.md) —
|
|
153
|
+
the claude/codex transcript details tandem is pinned against
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
[MIT](https://github.com/Bhavya6187/tandem/blob/main/LICENSE)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
tandem reads one optional file: `~/.tandem/config.toml`. No file is
|
|
4
|
+
required; every key has a working default. (Back to the
|
|
5
|
+
[README](../README.md).)
|
|
6
|
+
|
|
7
|
+
## [subagents] — GPT subagent workers
|
|
8
|
+
|
|
9
|
+
Worker model, routing mode, and context handling for GPT subagent
|
|
10
|
+
dispatches. Key semantics and the full routing story live in the
|
|
11
|
+
[GPT subagents guide](subagents.md):
|
|
12
|
+
|
|
13
|
+
```toml
|
|
14
|
+
[subagents]
|
|
15
|
+
model = "gpt-5.6-luna" # worker default; unset = your codex account's default
|
|
16
|
+
route = "manual" # manual | all | off
|
|
17
|
+
context = "match" # match | task | full
|
|
18
|
+
keep_forks = false # keep each worker's rollout for debugging
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## [claude] / [codex] — per-harness startup args
|
|
22
|
+
|
|
23
|
+
Optional per-harness tables add flags to every interactive session tandem
|
|
24
|
+
opens (`tandem`, `tandem resume`) — one-off relays (`tandem run`),
|
|
25
|
+
subagent dispatch, and doctor probes are unaffected:
|
|
26
|
+
|
|
27
|
+
```toml
|
|
28
|
+
[claude]
|
|
29
|
+
args = ["--dangerously-skip-permissions"]
|
|
30
|
+
|
|
31
|
+
[codex]
|
|
32
|
+
args = ["--dangerously-bypass-approvals-and-sandbox"]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The flags shown disable the harnesses' own permission prompts for
|
|
36
|
+
sessions tandem launches — set them only if that is what you want.
|
|
37
|
+
The list is passed to the harness raw: a flag that expects a value can
|
|
38
|
+
swallow the settings tandem appends after it and break turn tracking.
|
|
39
|
+
Malformed values (a non-list, empty or non-string elements) are
|
|
40
|
+
silently ignored rather than failing the launch.
|
|
41
|
+
|
|
42
|
+
## [frame] — the flip key, the tab bar, and warm flips
|
|
43
|
+
|
|
44
|
+
The frame is tandem's own surface inside a running session: one reserved
|
|
45
|
+
keybind that flips to the other harness, the one-line tab bar on the
|
|
46
|
+
bottom terminal row, and the pipelined boot behind the flip.
|
|
47
|
+
|
|
48
|
+
```toml
|
|
49
|
+
[frame]
|
|
50
|
+
flip_key = "ctrl-]"
|
|
51
|
+
bar = true
|
|
52
|
+
warm = true # boot the incoming harness while the outgoing one shuts down
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| key | default | meaning |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `flip_key` | `"ctrl-]"` | The flip keybind, consumed by tandem (never forwarded). Accepts `ctrl-<char>` or a hex byte like `"0x1d"`; printable keys are rejected (they would swallow typing). The bar relabels itself to match (`ctrl-t` shows `^T flips`). |
|
|
58
|
+
| `bar` | `true` | The one-line tab bar on the bottom terminal row. `false` hides it; the flip still works. |
|
|
59
|
+
| `warm` | `true` | Overlap the two halves of a flip: the incoming harness starts booting the moment the flip fires (a mid-turn press waits for the turn boundary first), while the outgoing one is still shutting down. `false` gives fully serial flips — the boot only begins once the old harness is gone. |
|
|
60
|
+
|
|
61
|
+
An unparseable value falls back to the default rather than failing the
|
|
62
|
+
launch. If a terminal can't sustain the bar, tandem drops it for the rest
|
|
63
|
+
of that session — the flip is unaffected — and `tandem doctor` warns
|
|
64
|
+
about it until you delete the marker file it names; set `bar = false` if
|
|
65
|
+
you'd rather keep the bar off for good. Shrinking the window below the
|
|
66
|
+
bar's row floor also drops it for the session, but that is tandem's own
|
|
67
|
+
policy rather than a conflict, so nothing is recorded and `doctor` stays
|
|
68
|
+
quiet.
|
|
69
|
+
|
|
70
|
+
Warming is pipelining, not a background service: the moment the flip
|
|
71
|
+
fires — a mid-turn `Ctrl-]` waits for the turn boundary first — the
|
|
72
|
+
incoming harness starts and the outgoing one is torn down at the same
|
|
73
|
+
time, so the flip lands at about the incoming harness's own start-up
|
|
74
|
+
speed instead of that plus the shutdown. Nothing exists before you press
|
|
75
|
+
the key and nothing survives the flip — between flips a tandem session is
|
|
76
|
+
exactly one harness process. `warm = false` restores the fully serial
|
|
77
|
+
flip, which is slower but does the same thing.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Developing tandem
|
|
2
|
+
|
|
3
|
+
Dev setup and the extension surface. (Back to the
|
|
4
|
+
[README](../README.md).)
|
|
5
|
+
|
|
6
|
+
## Extending tandem
|
|
7
|
+
|
|
8
|
+
The sync engine talks to a small adapter interface
|
|
9
|
+
(`tandem.converter.TraceConverter`):
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
class TraceConverter(Protocol):
|
|
13
|
+
def translate_entry(entry, direction, ctx) -> list[TargetEntry] | TranslationError
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`ReferenceConverter` implements it via a normalized event model
|
|
17
|
+
(`tandem/events.py`) derived from the observed formats. Pass your own
|
|
18
|
+
converter to `SyncEngine(store, session, source, converter=...)`.
|
|
19
|
+
|
|
20
|
+
## Development
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv sync && uv run pytest
|
|
24
|
+
pipx install . # or: uv tool install .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Dependencies are deliberately small: `click` (CLI), `pydantic` v2 (event
|
|
28
|
+
schema), `watchdog` (transcript tailing), `pexpect`/ptyprocess (PTY
|
|
29
|
+
passthrough); state is stdlib `sqlite3`.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# How tandem works
|
|
2
|
+
|
|
3
|
+
The mechanics behind the pairing: sync engine, PTY passthrough,
|
|
4
|
+
compatibility ranges, and where your data lives. (Back to the
|
|
5
|
+
[README](../README.md).)
|
|
6
|
+
|
|
7
|
+
- **One model per command — always.** Only the active harness's model is
|
|
8
|
+
ever invoked (or, for `run --on`, the target's). The shadow side is pure
|
|
9
|
+
local file I/O: tandem tails the active transcript, translates each
|
|
10
|
+
entry, and appends it to the shadow's session file. The shadow's model
|
|
11
|
+
is never called to "catch up".
|
|
12
|
+
- **Exit means exit.** Leaving the harness — as opposed to flipping out
|
|
13
|
+
of it — prints the resume hint and returns you to your OS shell; the
|
|
14
|
+
paired session is saved and `tandem resume` re-enters it.
|
|
15
|
+
`status` / `sync` / `doctor` / `run --on` / `sync-mcp` run one-shot
|
|
16
|
+
from your shell, targeting the directory's most recently used session.
|
|
17
|
+
- **The frame: flip without leaving.** Ctrl-] (configurable, consumed at
|
|
18
|
+
the PTY layer, ignored inside bracketed paste) flips the screen to the
|
|
19
|
+
other harness: pressed mid-turn it arms and fires at the turn boundary
|
|
20
|
+
(press again to cancel — the bar shows the armed state), then
|
|
21
|
+
tandem exits the fronted CLI gracefully (quit keystrokes, then SIGTERM,
|
|
22
|
+
then a bounded SIGKILL), lets the incremental sync settle, flips roles,
|
|
23
|
+
and resumes the other side — with no stop in between.
|
|
24
|
+
With claude fronted, the boundary comes from claude's own session
|
|
25
|
+
registry (`~/.claude/sessions/<pid>.json`, the data `claude agents
|
|
26
|
+
--json` prints): `busy` holds the flip, anything else fires it — so an
|
|
27
|
+
idle prompt flips instantly even though claude keeps appending
|
|
28
|
+
housekeeping to its transcript between turns.
|
|
29
|
+
(The registry appeared in claude 2.1.226; on older claudes the probe
|
|
30
|
+
finds nothing and every armed flip fires at once, mid-turn included —
|
|
31
|
+
the deliberate single-tier trade: no silent valve, breakage is loud.)
|
|
32
|
+
With codex fronted the
|
|
33
|
+
transcript-marker rules stand: where no marker could be wired (codex
|
|
34
|
+
with a `notify` handler of your own, which tandem won't clobber) ~2s of
|
|
35
|
+
transcript quiescence stands in; where one was wired, a 120s valve
|
|
36
|
+
covers a marker that never arrives — far above any plausible tool-call
|
|
37
|
+
silence, because firing early kills a live turn while firing late only
|
|
38
|
+
costs a wait (and Ctrl-] cancels). The
|
|
39
|
+
bottom terminal row is tandem's one drawn pixel: the child is told the
|
|
40
|
+
terminal is a row shorter, a scroll region keeps output above the bar,
|
|
41
|
+
and a targeted watcher reasserts it after child screen resets. If a
|
|
42
|
+
terminal can't sustain the bar it drops for the session (the flip keeps
|
|
43
|
+
working) and `tandem doctor` says so until you delete the marker file it
|
|
44
|
+
names; shrinking the window below the bar's row floor drops it just as
|
|
45
|
+
permanently, but silently — that one isn't a conflict to fix.
|
|
46
|
+
- **PTY passthrough.** tandem launches the real CLI on a pty (raw mode,
|
|
47
|
+
resize forwarding, signals through the line discipline) and never
|
|
48
|
+
scrapes terminal output — the transcript files are the source of truth.
|
|
49
|
+
Turn-complete hooks (`claude --settings` Stop hook, `codex -c
|
|
50
|
+
notify=[…]`) are wired per-invocation as wake-up signals, with
|
|
51
|
+
fs-watching as the data path and fallback. If your codex config already
|
|
52
|
+
sets `notify`, tandem leaves it alone.
|
|
53
|
+
- **Append-only, crash-safe sync.** Each transcript entry is translated as
|
|
54
|
+
it lands — no bulk re-export at switch time. Appends are whole-line +
|
|
55
|
+
fsync, and a write-ahead intent in the sync cursor makes translation
|
|
56
|
+
exactly-once across crashes; on restart, sync resumes from the last
|
|
57
|
+
confirmed entry.
|
|
58
|
+
- **Tool calls translate natively.** The harnesses speak different tool
|
|
59
|
+
vocabularies, so each completed call+result pair is re-expressed in the
|
|
60
|
+
shadow's own terms — `Bash` ↔ `exec_command`, `Edit`/`Write` ↔
|
|
61
|
+
`apply_patch`, `TodoWrite` ↔ `update_plan` — and lands as a real
|
|
62
|
+
tool-call record, so shadow history reads as the shadow's own work.
|
|
63
|
+
Anything that wouldn't map truthfully passes through verbatim; a call
|
|
64
|
+
whose result never arrived is closed with a `(tool result not recorded)`
|
|
65
|
+
placeholder at handoff, since both replay APIs reject dangling calls.
|
|
66
|
+
- **Attribution stays legible.** Every synced *text* message is tagged
|
|
67
|
+
`[via claude-code]` / `[via codex]` (tandem's own notes use `[tandem]`),
|
|
68
|
+
so interleaved histories make sense to you and to the models. Tool
|
|
69
|
+
activity is untagged — it's mirrored as native records, not prose.
|
|
70
|
+
- **Errors are contained.** An entry that fails translation becomes a
|
|
71
|
+
single per-turn placeholder in the shadow, with the raw entry
|
|
72
|
+
quarantined under `~/.tandem/quarantine/…` — and sync continues. The
|
|
73
|
+
shadow is never corrupted or truncated.
|
|
74
|
+
- **Memory files stay in step.** Fresh launches and every switch sync
|
|
75
|
+
CLAUDE.md ↔ AGENTS.md: shared content lives in a
|
|
76
|
+
`<!-- tandem:shared:begin/end -->` block (newer file wins),
|
|
77
|
+
tool-specific text outside the block is preserved, and a file without
|
|
78
|
+
markers is read from but never rewritten. Git state is never touched.
|
|
79
|
+
|
|
80
|
+
## Compatibility
|
|
81
|
+
|
|
82
|
+
Session formats are internal to the CLIs and drift between releases.
|
|
83
|
+
tandem pins what it was built against (observed formats documented in
|
|
84
|
+
[docs/formats.md](https://github.com/Bhavya6187/tandem/blob/main/docs/formats.md)):
|
|
85
|
+
|
|
86
|
+
| CLI | Tested | Accepted range |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| Claude Code | 2.1.220 | ≥ 2.0, < 3 |
|
|
89
|
+
| Codex CLI | 0.145.0 | ≥ 0.140, < 0.150 |
|
|
90
|
+
|
|
91
|
+
Outside the range, tandem warns and asks you to run `tandem doctor`.
|
|
92
|
+
Format knowledge is isolated per tool in
|
|
93
|
+
`src/tandem/harness/claude_code.py` and `src/tandem/harness/codex.py`.
|
|
94
|
+
|
|
95
|
+
## Where your data lives
|
|
96
|
+
|
|
97
|
+
- `~/.tandem/state.db` — SQLite: session pairing + per-source sync cursors
|
|
98
|
+
(override the directory with `TANDEM_HOME`)
|
|
99
|
+
- `~/.tandem/quarantine/<session>/` — raw entries that failed translation
|
|
100
|
+
- `~/.claude/projects/<munged-cwd>/<session-id>.jsonl` — claude transcript
|
|
101
|
+
- `~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<session-id>.jsonl` — codex
|
|
102
|
+
rollout (`CLAUDE_CONFIG_DIR` / `CODEX_HOME` honored)
|
|
103
|
+
|
|
104
|
+
Claude session ids are minted by tandem (`claude --session-id`); codex
|
|
105
|
+
mints its own on first run and tandem captures it from the new rollout
|
|
106
|
+
file.
|