codex2claude 0.1.2__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.
- codex2claude-0.1.2/LICENSE +21 -0
- codex2claude-0.1.2/PKG-INFO +300 -0
- codex2claude-0.1.2/README.md +271 -0
- codex2claude-0.1.2/bridge/codex2claude/__init__.py +3 -0
- codex2claude-0.1.2/bridge/codex2claude/__main__.py +5 -0
- codex2claude-0.1.2/bridge/codex2claude/claude_cli.py +96 -0
- codex2claude-0.1.2/bridge/codex2claude/cli.py +239 -0
- codex2claude-0.1.2/bridge/codex2claude/errors.py +30 -0
- codex2claude-0.1.2/bridge/codex2claude/locking.py +22 -0
- codex2claude-0.1.2/bridge/codex2claude/logging_utils.py +19 -0
- codex2claude-0.1.2/bridge/codex2claude/models.py +43 -0
- codex2claude-0.1.2/bridge/codex2claude/paths.py +35 -0
- codex2claude-0.1.2/bridge/codex2claude/state.py +32 -0
- codex2claude-0.1.2/bridge/codex2claude/threading.py +14 -0
- codex2claude-0.1.2/bridge/codex2claude/version.py +1 -0
- codex2claude-0.1.2/bridge/codex2claude.egg-info/PKG-INFO +300 -0
- codex2claude-0.1.2/bridge/codex2claude.egg-info/SOURCES.txt +33 -0
- codex2claude-0.1.2/bridge/codex2claude.egg-info/dependency_links.txt +1 -0
- codex2claude-0.1.2/bridge/codex2claude.egg-info/entry_points.txt +2 -0
- codex2claude-0.1.2/bridge/codex2claude.egg-info/top_level.txt +1 -0
- codex2claude-0.1.2/pyproject.toml +44 -0
- codex2claude-0.1.2/setup.cfg +4 -0
- codex2claude-0.1.2/tests/test_admin_commands.py +135 -0
- codex2claude-0.1.2/tests/test_ask_flow_mock.py +286 -0
- codex2claude-0.1.2/tests/test_claude_cli_mock.py +68 -0
- codex2claude-0.1.2/tests/test_cli_smoke.py +13 -0
- codex2claude-0.1.2/tests/test_concurrency.py +63 -0
- codex2claude-0.1.2/tests/test_locking.py +19 -0
- codex2claude-0.1.2/tests/test_package_metadata.py +11 -0
- codex2claude-0.1.2/tests/test_paths.py +26 -0
- codex2claude-0.1.2/tests/test_real_claude_cli.py +52 -0
- codex2claude-0.1.2/tests/test_recovery.py +64 -0
- codex2claude-0.1.2/tests/test_skill_docs.py +11 -0
- codex2claude-0.1.2/tests/test_state.py +71 -0
- codex2claude-0.1.2/tests/test_threading.py +14 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codex2claude
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Local Codex-to-Claude bridge CLI
|
|
5
|
+
Author: OpenAI Codex
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Housetan218/codex2claude
|
|
8
|
+
Project-URL: Repository, https://github.com/Housetan218/codex2claude
|
|
9
|
+
Project-URL: Issues, https://github.com/Housetan218/codex2claude/issues
|
|
10
|
+
Project-URL: Releases, https://github.com/Housetan218/codex2claude/releases
|
|
11
|
+
Keywords: codex,claude,cli,bridge,agent
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
24
|
+
Classifier: Topic :: Utilities
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# codex2claude
|
|
31
|
+
|
|
32
|
+
`codex2claude` is a local one-way bridge from Codex to Claude.
|
|
33
|
+
|
|
34
|
+
It provides:
|
|
35
|
+
|
|
36
|
+
- a reusable Python CLI bridge
|
|
37
|
+
- Claude session persistence via native `session_id`
|
|
38
|
+
- deterministic per-thread locking
|
|
39
|
+
- a thin Codex skill wrapper surface
|
|
40
|
+
- no non-stdlib Python runtime dependency inside the bridge
|
|
41
|
+
|
|
42
|
+
Current implementation target:
|
|
43
|
+
|
|
44
|
+
- macOS / POSIX environments with Python 3 and local Claude CLI access
|
|
45
|
+
- not designed for Windows in its current `fcntl`-based form
|
|
46
|
+
|
|
47
|
+
## Current Status
|
|
48
|
+
|
|
49
|
+
Implemented and locally verified on this machine:
|
|
50
|
+
|
|
51
|
+
- `ask`
|
|
52
|
+
- `status`
|
|
53
|
+
- `forget`
|
|
54
|
+
- `gc`
|
|
55
|
+
- automatic resume via stored Claude `session_id`
|
|
56
|
+
- same-thread lock conflict handling
|
|
57
|
+
|
|
58
|
+
Fresh verification completed during implementation:
|
|
59
|
+
|
|
60
|
+
- `PYTHONPATH=bridge python3 -m unittest discover -s tests -p 'test_*.py' -v`
|
|
61
|
+
- real Claude new-session smoke
|
|
62
|
+
- real Claude resume smoke
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python3 -m venv .venv
|
|
68
|
+
source .venv/bin/activate
|
|
69
|
+
python3 -m pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
When a PyPI release is available, install with:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python3 -m pip install codex2claude
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If you do not want to install it yet, you can run it directly:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
PYTHONPATH=bridge python3 -m codex2claude ask --prompt "Reply with ok only"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
After editable install, both of these work:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
python -m codex2claude --help
|
|
88
|
+
codex2claude --help
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Usage
|
|
92
|
+
|
|
93
|
+
Ask Claude in the current workspace thread:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
codex2claude ask --prompt "Review this design" --workspace "$PWD"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use a named thread:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
codex2claude ask --prompt "Continue the design review" --workspace "$PWD" --thread design
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Force a fresh Claude session:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
codex2claude ask --prompt "Start over" --workspace "$PWD" --new
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Inspect stored state:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
codex2claude status --workspace "$PWD"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Forget the current thread:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
codex2claude forget --workspace "$PWD"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Remove stale thread files:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
codex2claude gc --max-age-days 7
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Codex Skill
|
|
130
|
+
|
|
131
|
+
The Codex-facing wrapper lives at:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
skills/codex-to-claude/SKILL.md
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The skill should stay thin. It should only:
|
|
138
|
+
|
|
139
|
+
- collect the user prompt
|
|
140
|
+
- choose default thread, named thread, or `--new`
|
|
141
|
+
- invoke `codex2claude`
|
|
142
|
+
- return stdout or surface stderr
|
|
143
|
+
|
|
144
|
+
It should not own Claude JSON parsing, session files, or retries.
|
|
145
|
+
|
|
146
|
+
Common trigger phrases that should explicitly steer Codex toward this skill:
|
|
147
|
+
|
|
148
|
+
- `Use the codex-to-claude skill`
|
|
149
|
+
- `ask Claude about this`
|
|
150
|
+
- `send this to Claude`
|
|
151
|
+
- `let Claude review this`
|
|
152
|
+
- `ask cc`
|
|
153
|
+
- `ask cc about this`
|
|
154
|
+
- `cc review this`
|
|
155
|
+
- `cc 怎么看`
|
|
156
|
+
- `cc 觉得呢`
|
|
157
|
+
- `cc 能帮忙看下吗`
|
|
158
|
+
- `问问cc`
|
|
159
|
+
- `问下Claude`
|
|
160
|
+
- `Claude 怎么看`
|
|
161
|
+
- `Claude 能看下吗`
|
|
162
|
+
- `让cc帮忙看看`
|
|
163
|
+
- `cc review下`
|
|
164
|
+
- `cc check一下`
|
|
165
|
+
- `give this to cc`
|
|
166
|
+
- `给 Claude 看看`
|
|
167
|
+
- `让 Claude 看一下`
|
|
168
|
+
- `发给 Claude`
|
|
169
|
+
- `给 cc 看看`
|
|
170
|
+
- `让 cc review 一下`
|
|
171
|
+
- `发给 cc`
|
|
172
|
+
|
|
173
|
+
For stability, prefer phrases where `cc` appears with an action like ask, review, check, or look. Avoid relying on bare `cc` by itself.
|
|
174
|
+
|
|
175
|
+
## Triggering
|
|
176
|
+
|
|
177
|
+
Recommended everyday trigger phrases:
|
|
178
|
+
|
|
179
|
+
- `给cc看看这个`
|
|
180
|
+
- `问问cc`
|
|
181
|
+
- `cc怎么看`
|
|
182
|
+
- `让cc review一下`
|
|
183
|
+
- `给Claude看看`
|
|
184
|
+
|
|
185
|
+
More explicit variants:
|
|
186
|
+
|
|
187
|
+
- `Use the codex-to-claude skill`
|
|
188
|
+
- `ask Claude about this`
|
|
189
|
+
- `ask cc`
|
|
190
|
+
- `cc review this`
|
|
191
|
+
- `给 cc 看看`
|
|
192
|
+
- `问下Claude`
|
|
193
|
+
|
|
194
|
+
## Activation Scope
|
|
195
|
+
|
|
196
|
+
These paths are confirmed to work:
|
|
197
|
+
|
|
198
|
+
- new Codex sessions started after the skill was installed
|
|
199
|
+
- `codex exec` runs started after the skill was installed
|
|
200
|
+
- direct `codex2claude` CLI usage
|
|
201
|
+
|
|
202
|
+
Do not assume already-open Codex sessions will hot-reload newly installed or updated skills.
|
|
203
|
+
|
|
204
|
+
If you changed trigger phrases or installed the skill during an existing session, restart Codex or open a fresh session before testing.
|
|
205
|
+
|
|
206
|
+
## Troubleshooting
|
|
207
|
+
|
|
208
|
+
If a trigger phrase does not route to Claude:
|
|
209
|
+
|
|
210
|
+
1. Start a new Codex session.
|
|
211
|
+
2. Test with a high-signal phrase such as `问问cc:请只回复 ok`.
|
|
212
|
+
3. If needed, use the most explicit form: `Use the codex-to-claude skill. Ask Claude: ...`
|
|
213
|
+
4. Verify the bridge directly with `codex2claude ask --prompt "Reply with ok only" --workspace "$PWD"`.
|
|
214
|
+
|
|
215
|
+
If direct CLI usage works but a natural-language trigger does not, the issue is skill discovery in that session, not the bridge itself.
|
|
216
|
+
|
|
217
|
+
## Threading Model
|
|
218
|
+
|
|
219
|
+
By default, one workspace maps to one Claude thread.
|
|
220
|
+
|
|
221
|
+
Use `--thread <name>` to split conversations inside the same repo:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
codex2claude ask --prompt "Review API design" --workspace "$PWD" --thread api
|
|
225
|
+
codex2claude ask --prompt "Review docs tone" --workspace "$PWD" --thread docs
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Use `--new` when you want a fresh Claude conversation for the selected thread key.
|
|
229
|
+
|
|
230
|
+
## Environment
|
|
231
|
+
|
|
232
|
+
- `CODEX2CLAUDE_CLAUDE_BIN`: override the Claude executable path
|
|
233
|
+
- `CODEX2CLAUDE_HOME`: override the bridge state root without changing your real shell `HOME`
|
|
234
|
+
- `CODEX2CLAUDE_RUN_REAL=1`: enable opt-in real Claude integration tests
|
|
235
|
+
|
|
236
|
+
## Test
|
|
237
|
+
|
|
238
|
+
Run the main test suite:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
PYTHONPATH=bridge python3 -m unittest discover -s tests -p 'test_*.py' -v
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Run the opt-in real Claude smoke test:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
PYTHONPATH=bridge CODEX2CLAUDE_RUN_REAL=1 python3 -m unittest tests.test_real_claude_cli -v
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
The real-Claude test is opt-in because it spends actual Claude usage and requires local auth.
|
|
251
|
+
|
|
252
|
+
## Exit Codes
|
|
253
|
+
|
|
254
|
+
- `0`: success
|
|
255
|
+
- `1`: Claude invocation failure or generic bridge failure
|
|
256
|
+
- `2`: Claude timeout
|
|
257
|
+
- `3`: same-thread lock conflict
|
|
258
|
+
- `4`: invalid arguments
|
|
259
|
+
- `5`: corrupted state or persistence failure
|
|
260
|
+
|
|
261
|
+
## State Layout
|
|
262
|
+
|
|
263
|
+
```text
|
|
264
|
+
~/.codex/codex2claude/
|
|
265
|
+
threads/
|
|
266
|
+
runs/
|
|
267
|
+
logs/
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Important files:
|
|
271
|
+
|
|
272
|
+
- `threads/<thread_key>.json`: current thread state and stored Claude `session_id`
|
|
273
|
+
- `runs/<thread_key>/...json`: per-run artifacts
|
|
274
|
+
- `logs/bridge.log`: append-only bridge events
|
|
275
|
+
|
|
276
|
+
## Current Scope
|
|
277
|
+
|
|
278
|
+
This version is intentionally one-way only:
|
|
279
|
+
|
|
280
|
+
- Codex initiates
|
|
281
|
+
- Claude replies
|
|
282
|
+
- bridge stores Claude `session_id`
|
|
283
|
+
- follow-up turns use native `claude --resume`
|
|
284
|
+
|
|
285
|
+
Bidirectional agent protocols are out of scope for v1.
|
|
286
|
+
|
|
287
|
+
## References
|
|
288
|
+
|
|
289
|
+
- Design: `docs/superpowers/specs/2026-03-28-codex-to-claude-design.md`
|
|
290
|
+
- Plan: `docs/superpowers/plans/2026-03-28-codex-to-claude-v1.md`
|
|
291
|
+
|
|
292
|
+
## Contributing
|
|
293
|
+
|
|
294
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development, verification, and release steps.
|
|
295
|
+
|
|
296
|
+
## CI And Releases
|
|
297
|
+
|
|
298
|
+
GitHub Actions runs unit tests and packaging checks on pushes and pull requests.
|
|
299
|
+
|
|
300
|
+
PyPI publishing is wired through `.github/workflows/release.yml` and is intended to use PyPI Trusted Publishing from GitHub Actions. Repository maintainers still need to configure the matching trusted publisher entry on PyPI before tag-based publishing can succeed.
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# codex2claude
|
|
2
|
+
|
|
3
|
+
`codex2claude` is a local one-way bridge from Codex to Claude.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
6
|
+
|
|
7
|
+
- a reusable Python CLI bridge
|
|
8
|
+
- Claude session persistence via native `session_id`
|
|
9
|
+
- deterministic per-thread locking
|
|
10
|
+
- a thin Codex skill wrapper surface
|
|
11
|
+
- no non-stdlib Python runtime dependency inside the bridge
|
|
12
|
+
|
|
13
|
+
Current implementation target:
|
|
14
|
+
|
|
15
|
+
- macOS / POSIX environments with Python 3 and local Claude CLI access
|
|
16
|
+
- not designed for Windows in its current `fcntl`-based form
|
|
17
|
+
|
|
18
|
+
## Current Status
|
|
19
|
+
|
|
20
|
+
Implemented and locally verified on this machine:
|
|
21
|
+
|
|
22
|
+
- `ask`
|
|
23
|
+
- `status`
|
|
24
|
+
- `forget`
|
|
25
|
+
- `gc`
|
|
26
|
+
- automatic resume via stored Claude `session_id`
|
|
27
|
+
- same-thread lock conflict handling
|
|
28
|
+
|
|
29
|
+
Fresh verification completed during implementation:
|
|
30
|
+
|
|
31
|
+
- `PYTHONPATH=bridge python3 -m unittest discover -s tests -p 'test_*.py' -v`
|
|
32
|
+
- real Claude new-session smoke
|
|
33
|
+
- real Claude resume smoke
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python3 -m venv .venv
|
|
39
|
+
source .venv/bin/activate
|
|
40
|
+
python3 -m pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
When a PyPI release is available, install with:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python3 -m pip install codex2claude
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If you do not want to install it yet, you can run it directly:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
PYTHONPATH=bridge python3 -m codex2claude ask --prompt "Reply with ok only"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
After editable install, both of these work:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python -m codex2claude --help
|
|
59
|
+
codex2claude --help
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
Ask Claude in the current workspace thread:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
codex2claude ask --prompt "Review this design" --workspace "$PWD"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use a named thread:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
codex2claude ask --prompt "Continue the design review" --workspace "$PWD" --thread design
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Force a fresh Claude session:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
codex2claude ask --prompt "Start over" --workspace "$PWD" --new
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Inspect stored state:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
codex2claude status --workspace "$PWD"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Forget the current thread:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
codex2claude forget --workspace "$PWD"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Remove stale thread files:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
codex2claude gc --max-age-days 7
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Codex Skill
|
|
101
|
+
|
|
102
|
+
The Codex-facing wrapper lives at:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
skills/codex-to-claude/SKILL.md
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The skill should stay thin. It should only:
|
|
109
|
+
|
|
110
|
+
- collect the user prompt
|
|
111
|
+
- choose default thread, named thread, or `--new`
|
|
112
|
+
- invoke `codex2claude`
|
|
113
|
+
- return stdout or surface stderr
|
|
114
|
+
|
|
115
|
+
It should not own Claude JSON parsing, session files, or retries.
|
|
116
|
+
|
|
117
|
+
Common trigger phrases that should explicitly steer Codex toward this skill:
|
|
118
|
+
|
|
119
|
+
- `Use the codex-to-claude skill`
|
|
120
|
+
- `ask Claude about this`
|
|
121
|
+
- `send this to Claude`
|
|
122
|
+
- `let Claude review this`
|
|
123
|
+
- `ask cc`
|
|
124
|
+
- `ask cc about this`
|
|
125
|
+
- `cc review this`
|
|
126
|
+
- `cc 怎么看`
|
|
127
|
+
- `cc 觉得呢`
|
|
128
|
+
- `cc 能帮忙看下吗`
|
|
129
|
+
- `问问cc`
|
|
130
|
+
- `问下Claude`
|
|
131
|
+
- `Claude 怎么看`
|
|
132
|
+
- `Claude 能看下吗`
|
|
133
|
+
- `让cc帮忙看看`
|
|
134
|
+
- `cc review下`
|
|
135
|
+
- `cc check一下`
|
|
136
|
+
- `give this to cc`
|
|
137
|
+
- `给 Claude 看看`
|
|
138
|
+
- `让 Claude 看一下`
|
|
139
|
+
- `发给 Claude`
|
|
140
|
+
- `给 cc 看看`
|
|
141
|
+
- `让 cc review 一下`
|
|
142
|
+
- `发给 cc`
|
|
143
|
+
|
|
144
|
+
For stability, prefer phrases where `cc` appears with an action like ask, review, check, or look. Avoid relying on bare `cc` by itself.
|
|
145
|
+
|
|
146
|
+
## Triggering
|
|
147
|
+
|
|
148
|
+
Recommended everyday trigger phrases:
|
|
149
|
+
|
|
150
|
+
- `给cc看看这个`
|
|
151
|
+
- `问问cc`
|
|
152
|
+
- `cc怎么看`
|
|
153
|
+
- `让cc review一下`
|
|
154
|
+
- `给Claude看看`
|
|
155
|
+
|
|
156
|
+
More explicit variants:
|
|
157
|
+
|
|
158
|
+
- `Use the codex-to-claude skill`
|
|
159
|
+
- `ask Claude about this`
|
|
160
|
+
- `ask cc`
|
|
161
|
+
- `cc review this`
|
|
162
|
+
- `给 cc 看看`
|
|
163
|
+
- `问下Claude`
|
|
164
|
+
|
|
165
|
+
## Activation Scope
|
|
166
|
+
|
|
167
|
+
These paths are confirmed to work:
|
|
168
|
+
|
|
169
|
+
- new Codex sessions started after the skill was installed
|
|
170
|
+
- `codex exec` runs started after the skill was installed
|
|
171
|
+
- direct `codex2claude` CLI usage
|
|
172
|
+
|
|
173
|
+
Do not assume already-open Codex sessions will hot-reload newly installed or updated skills.
|
|
174
|
+
|
|
175
|
+
If you changed trigger phrases or installed the skill during an existing session, restart Codex or open a fresh session before testing.
|
|
176
|
+
|
|
177
|
+
## Troubleshooting
|
|
178
|
+
|
|
179
|
+
If a trigger phrase does not route to Claude:
|
|
180
|
+
|
|
181
|
+
1. Start a new Codex session.
|
|
182
|
+
2. Test with a high-signal phrase such as `问问cc:请只回复 ok`.
|
|
183
|
+
3. If needed, use the most explicit form: `Use the codex-to-claude skill. Ask Claude: ...`
|
|
184
|
+
4. Verify the bridge directly with `codex2claude ask --prompt "Reply with ok only" --workspace "$PWD"`.
|
|
185
|
+
|
|
186
|
+
If direct CLI usage works but a natural-language trigger does not, the issue is skill discovery in that session, not the bridge itself.
|
|
187
|
+
|
|
188
|
+
## Threading Model
|
|
189
|
+
|
|
190
|
+
By default, one workspace maps to one Claude thread.
|
|
191
|
+
|
|
192
|
+
Use `--thread <name>` to split conversations inside the same repo:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
codex2claude ask --prompt "Review API design" --workspace "$PWD" --thread api
|
|
196
|
+
codex2claude ask --prompt "Review docs tone" --workspace "$PWD" --thread docs
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Use `--new` when you want a fresh Claude conversation for the selected thread key.
|
|
200
|
+
|
|
201
|
+
## Environment
|
|
202
|
+
|
|
203
|
+
- `CODEX2CLAUDE_CLAUDE_BIN`: override the Claude executable path
|
|
204
|
+
- `CODEX2CLAUDE_HOME`: override the bridge state root without changing your real shell `HOME`
|
|
205
|
+
- `CODEX2CLAUDE_RUN_REAL=1`: enable opt-in real Claude integration tests
|
|
206
|
+
|
|
207
|
+
## Test
|
|
208
|
+
|
|
209
|
+
Run the main test suite:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
PYTHONPATH=bridge python3 -m unittest discover -s tests -p 'test_*.py' -v
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Run the opt-in real Claude smoke test:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
PYTHONPATH=bridge CODEX2CLAUDE_RUN_REAL=1 python3 -m unittest tests.test_real_claude_cli -v
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The real-Claude test is opt-in because it spends actual Claude usage and requires local auth.
|
|
222
|
+
|
|
223
|
+
## Exit Codes
|
|
224
|
+
|
|
225
|
+
- `0`: success
|
|
226
|
+
- `1`: Claude invocation failure or generic bridge failure
|
|
227
|
+
- `2`: Claude timeout
|
|
228
|
+
- `3`: same-thread lock conflict
|
|
229
|
+
- `4`: invalid arguments
|
|
230
|
+
- `5`: corrupted state or persistence failure
|
|
231
|
+
|
|
232
|
+
## State Layout
|
|
233
|
+
|
|
234
|
+
```text
|
|
235
|
+
~/.codex/codex2claude/
|
|
236
|
+
threads/
|
|
237
|
+
runs/
|
|
238
|
+
logs/
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Important files:
|
|
242
|
+
|
|
243
|
+
- `threads/<thread_key>.json`: current thread state and stored Claude `session_id`
|
|
244
|
+
- `runs/<thread_key>/...json`: per-run artifacts
|
|
245
|
+
- `logs/bridge.log`: append-only bridge events
|
|
246
|
+
|
|
247
|
+
## Current Scope
|
|
248
|
+
|
|
249
|
+
This version is intentionally one-way only:
|
|
250
|
+
|
|
251
|
+
- Codex initiates
|
|
252
|
+
- Claude replies
|
|
253
|
+
- bridge stores Claude `session_id`
|
|
254
|
+
- follow-up turns use native `claude --resume`
|
|
255
|
+
|
|
256
|
+
Bidirectional agent protocols are out of scope for v1.
|
|
257
|
+
|
|
258
|
+
## References
|
|
259
|
+
|
|
260
|
+
- Design: `docs/superpowers/specs/2026-03-28-codex-to-claude-design.md`
|
|
261
|
+
- Plan: `docs/superpowers/plans/2026-03-28-codex-to-claude-v1.md`
|
|
262
|
+
|
|
263
|
+
## Contributing
|
|
264
|
+
|
|
265
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development, verification, and release steps.
|
|
266
|
+
|
|
267
|
+
## CI And Releases
|
|
268
|
+
|
|
269
|
+
GitHub Actions runs unit tests and packaging checks on pushes and pull requests.
|
|
270
|
+
|
|
271
|
+
PyPI publishing is wired through `.github/workflows/release.yml` and is intended to use PyPI Trusted Publishing from GitHub Actions. Repository maintainers still need to configure the matching trusted publisher entry on PyPI before tag-based publishing can succeed.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from .errors import ClaudeInvocationError, ClaudeTimeoutError, StateCorruptionError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(slots=True)
|
|
11
|
+
class ClaudeResult:
|
|
12
|
+
session_id: str | None
|
|
13
|
+
result_text: str
|
|
14
|
+
raw_payload: dict[str, object]
|
|
15
|
+
stderr_text: str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_claude_command(prompt: str, session_id: str | None, claude_bin: str = "claude") -> list[str]:
|
|
19
|
+
command = [claude_bin]
|
|
20
|
+
if session_id:
|
|
21
|
+
command.extend(["--resume", session_id])
|
|
22
|
+
command.extend(["-p", prompt, "--output-format", "json"])
|
|
23
|
+
return command
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def parse_claude_response(stdout: str, stderr: str = "") -> ClaudeResult:
|
|
27
|
+
try:
|
|
28
|
+
payload = json.loads(stdout)
|
|
29
|
+
except json.JSONDecodeError as exc:
|
|
30
|
+
raise StateCorruptionError("Claude returned malformed JSON") from exc
|
|
31
|
+
|
|
32
|
+
result_text = payload.get("result")
|
|
33
|
+
if not isinstance(result_text, str):
|
|
34
|
+
raise StateCorruptionError("Claude JSON missing string result")
|
|
35
|
+
|
|
36
|
+
if payload.get("is_error") is True:
|
|
37
|
+
raise ClaudeInvocationError(result_text)
|
|
38
|
+
|
|
39
|
+
session_id = payload.get("session_id")
|
|
40
|
+
if session_id is not None and not isinstance(session_id, str):
|
|
41
|
+
raise StateCorruptionError("Claude JSON returned invalid session_id")
|
|
42
|
+
if isinstance(session_id, str) and not session_id:
|
|
43
|
+
raise StateCorruptionError("Claude JSON returned empty session_id")
|
|
44
|
+
|
|
45
|
+
return ClaudeResult(
|
|
46
|
+
session_id=session_id,
|
|
47
|
+
result_text=result_text,
|
|
48
|
+
raw_payload=payload,
|
|
49
|
+
stderr_text=stderr,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def invoke_claude(
|
|
54
|
+
prompt: str,
|
|
55
|
+
session_id: str | None,
|
|
56
|
+
timeout_seconds: int,
|
|
57
|
+
cwd: str | None = None,
|
|
58
|
+
claude_bin: str = "claude",
|
|
59
|
+
) -> ClaudeResult:
|
|
60
|
+
command = build_claude_command(prompt=prompt, session_id=session_id, claude_bin=claude_bin)
|
|
61
|
+
try:
|
|
62
|
+
completed = subprocess.run(
|
|
63
|
+
command,
|
|
64
|
+
check=False,
|
|
65
|
+
capture_output=True,
|
|
66
|
+
text=True,
|
|
67
|
+
timeout=timeout_seconds,
|
|
68
|
+
cwd=cwd,
|
|
69
|
+
)
|
|
70
|
+
except FileNotFoundError as exc:
|
|
71
|
+
raise ClaudeInvocationError(f"Claude CLI not found: {claude_bin}") from exc
|
|
72
|
+
except subprocess.TimeoutExpired as exc:
|
|
73
|
+
raise ClaudeTimeoutError(f"Claude timed out after {timeout_seconds}s") from exc
|
|
74
|
+
|
|
75
|
+
if completed.returncode != 0:
|
|
76
|
+
stderr = completed.stderr.strip() or completed.stdout.strip() or "unknown Claude failure"
|
|
77
|
+
raise ClaudeInvocationError(stderr)
|
|
78
|
+
|
|
79
|
+
return parse_claude_response(completed.stdout, completed.stderr)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def read_claude_version(claude_bin: str = "claude") -> str | None:
|
|
83
|
+
try:
|
|
84
|
+
completed = subprocess.run(
|
|
85
|
+
[claude_bin, "--version"],
|
|
86
|
+
check=False,
|
|
87
|
+
capture_output=True,
|
|
88
|
+
text=True,
|
|
89
|
+
timeout=20,
|
|
90
|
+
)
|
|
91
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
92
|
+
return None
|
|
93
|
+
if completed.returncode != 0:
|
|
94
|
+
return None
|
|
95
|
+
version = completed.stdout.strip() or completed.stderr.strip()
|
|
96
|
+
return version or None
|