claude-code-tools 1.17.0__tar.gz → 1.18.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.
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/PKG-INFO +4 -71
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/README.md +3 -70
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/__init__.py +1 -1
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/aichat.py +111 -4
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server.py +32 -31
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_fingerprint.py +205 -15
- claude_code_tools-1.18.0/claude_code_tools/codex_server_legacy.py +278 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_models.py +10 -32
- claude_code_tools-1.18.0/claude_code_tools/codex_server_reservation.py +70 -0
- claude_code_tools-1.18.0/claude_code_tools/codex_server_retry.py +45 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_reuse.py +21 -0
- claude_code_tools-1.18.0/claude_code_tools/codex_server_state.py +65 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/export_session.py +249 -140
- claude_code_tools-1.18.0/claude_code_tools/port_claude_noise.py +405 -0
- claude_code_tools-1.18.0/claude_code_tools/port_claude_to_codex.py +760 -0
- claude_code_tools-1.18.0/claude_code_tools/port_codex_flatten.py +518 -0
- claude_code_tools-1.18.0/claude_code_tools/port_codex_to_claude.py +714 -0
- claude_code_tools-1.18.0/claude_code_tools/port_service.py +384 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/resolve_session.py +380 -217
- claude_code_tools-1.18.0/claude_code_tools/resolve_session_names.py +60 -0
- claude_code_tools-1.18.0/claude_code_tools/resolve_session_render.py +177 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/session_utils.py +281 -105
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/pyproject.toml +2 -2
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/.gitignore +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/LICENSE +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/action_rpc.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/__init__.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/backends.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/cli.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/config.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/convert.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/discord_bot.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/locking.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/paths.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/registry.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/store.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/tmux.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/agent_tunnel/trust.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/claude_continue.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_continue.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_cli.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_generation.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_process.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_supervisor.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/codex_server_worker.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/config.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/csv2gsheet.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/delete_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/dotenv_vault.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/env_safe.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/export_all.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/export_claude_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/export_codex_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/find_claude_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/find_codex_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/find_original_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/find_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/find_trimmed_sessions.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/fix_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/gdoc2docx.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/gdoc2md.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/gsheet2csv.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/md2gdoc.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/msg/__init__.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/msg/cli.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/msg/hooks.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/msg/models.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/msg/prompt_detect.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/msg/store.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/msg/watcher.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/node_menu_ui.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/search_index.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/session_lineage.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/session_menu.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/session_menu_cli.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/smart_trim.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/smart_trim_core.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/tmux_cli_controller.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/tmux_execution_helpers.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/tmux_remote_controller.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/trim_in_place.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/trim_session.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/trim_session_claude.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/trim_session_codex.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_contract.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_formatting.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_identity_policy.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_manifest.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_projection.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_rendering.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_snapshots.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_cli_store_backends.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_processes.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_runs.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_store_io.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/claude_code_tools/workflow_validation.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/agent-tunnel-spec.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/ai-agent-writing-skills.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/cc-codex-instructions.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/claude-code-chutes.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/claude-code-tmux-tutorials.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/dot-zshrc.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/ensemble-ideas.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/example-with-images.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/find-claude-session.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/intercom-spec.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/linked-in-20260102.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/lmsh.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/local-llm-setup.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/msg-phase-a-plan.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/msg-plan-v2.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/paper-writing-style-transfer-tools.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/reddit-aichat-resume-v2.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/reddit-aichat-resume.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/reddit-aichat.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/reddit-post.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/rollover-details.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/tmux-cli-instructions.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/docs/vault-documentation.md +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/hatch_build.py +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/node_ui/action_config.js +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/node_ui/menu.js +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/node_ui/package-lock.json +0 -0
- {claude_code_tools-1.17.0 → claude_code_tools-1.18.0}/node_ui/package.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.18.0
|
|
4
4
|
Summary: Collection of tools for working with Claude Code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -66,81 +66,14 @@ separate Rust binary:
|
|
|
66
66
|
[Releases](https://github.com/pchalasani/claude-code-tools/releases)
|
|
67
67
|
(look for `rust-v*`)
|
|
68
68
|
|
|
69
|
-
Install the Claude Code
|
|
69
|
+
Install the Claude Code
|
|
70
|
+
[plugins](https://pchalasani.github.io/claude-code-tools/getting-started/plugins/)
|
|
71
|
+
for hooks, skills, and agents:
|
|
70
72
|
|
|
71
73
|
```bash
|
|
72
74
|
claude plugin marketplace add pchalasani/claude-code-tools
|
|
73
|
-
claude plugin install workflow@cctools-plugins
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
Install the equivalent dynamic workflow plugin for Codex:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
codex plugin marketplace add pchalasani/claude-code-tools
|
|
80
|
-
codex plugin add dynamic-workflow@cctools-codex-plugins
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
The Codex plugin installs independently: it does not require the Python
|
|
84
|
-
`claude-code-tools` package or an npm install. Node.js 20 or newer and Codex
|
|
85
|
-
CLI 0.136.0 or newer are required for callbacks. Version 0.136.0 is the first
|
|
86
|
-
compatible CLI release for this callback protocol. Codex still marks the app
|
|
87
|
-
server and remote TUI interfaces as experimental, so those surfaces may evolve.
|
|
88
|
-
The plugin can notify the originating Codex thread through Codex's shared app
|
|
89
|
-
server after a detached workflow finishes. Callback preflight also rejects a
|
|
90
|
-
stale connected server.
|
|
91
|
-
|
|
92
|
-
The optional Python package makes that callback setup a single command:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
codex-dynamic
|
|
96
|
-
# Or continue the latest conversation:
|
|
97
|
-
codex-dynamic resume --last
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
`codex-dynamic` starts or reuses the local app server and then hands the
|
|
101
|
-
terminal directly to Codex. After a plugin or Codex upgrade, the next launch
|
|
102
|
-
automatically starts a fresh server generation; existing sessions and workflow
|
|
103
|
-
callbacks stay connected to their old generation. No manual restart is needed.
|
|
104
|
-
Use `codex-server status` or `logs` for nondisruptive inspection. A forced stop
|
|
105
|
-
or restart cleans up every retained generation and disconnects its attached
|
|
106
|
-
TUIs, so use those commands only after exiting the sessions normally.
|
|
107
|
-
|
|
108
|
-
The Python package also installs `codex-workflows`, an observational dashboard
|
|
109
|
-
for durable workflow runs:
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
codex-workflows
|
|
113
|
-
codex-workflows watch --limit 20
|
|
114
|
-
codex-workflows show RUN_ID
|
|
115
|
-
codex-workflows --all
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
The default list contains only active workflows and identifies the project
|
|
119
|
-
folder from which each workflow was launched. Use `show RUN_ID` for its full
|
|
120
|
-
working directory, or `--all` to include completed and diagnostic history. The
|
|
121
|
-
command reads durable workflow state and process metadata. It never changes a
|
|
122
|
-
run, sends a signal, repairs state, or launches an agent. See the
|
|
123
|
-
[codex-workflows reference][codex-workflows-reference] for filters, JSON output,
|
|
124
|
-
and the complete versioned schema.
|
|
125
|
-
|
|
126
|
-
[codex-workflows-reference]:
|
|
127
|
-
//pchalasani.github.io/claude-code-tools/tools/codex-dynamic/#observe-workflow-runs
|
|
128
|
-
|
|
129
|
-
Server management and passive supervision add no model calls. Completion
|
|
130
|
-
reporting starts a new turn only while the thread is idle; otherwise it steers
|
|
131
|
-
and extends the active turn. Either reporting path consumes model tokens.
|
|
132
|
-
|
|
133
|
-
Each workflow `run` or `resume` uses narrow host execution for that exact
|
|
134
|
-
reviewed supervisor command. The workflow JavaScript remains restricted, and
|
|
135
|
-
every headless Codex worker keeps its declared sandbox.
|
|
136
|
-
|
|
137
|
-
See the
|
|
138
|
-
[Claude → Codex migration guide][claude-to-codex-guide]
|
|
139
|
-
for usage and workflow-porting instructions.
|
|
140
|
-
|
|
141
|
-
[claude-to-codex-guide]:
|
|
142
|
-
https://pchalasani.github.io/claude-code-tools/guides/claude-to-codex/
|
|
143
|
-
|
|
144
77
|
---
|
|
145
78
|
|
|
146
79
|
Click a card to jump to that feature, or
|
|
@@ -40,81 +40,14 @@ separate Rust binary:
|
|
|
40
40
|
[Releases](https://github.com/pchalasani/claude-code-tools/releases)
|
|
41
41
|
(look for `rust-v*`)
|
|
42
42
|
|
|
43
|
-
Install the Claude Code
|
|
43
|
+
Install the Claude Code
|
|
44
|
+
[plugins](https://pchalasani.github.io/claude-code-tools/getting-started/plugins/)
|
|
45
|
+
for hooks, skills, and agents:
|
|
44
46
|
|
|
45
47
|
```bash
|
|
46
48
|
claude plugin marketplace add pchalasani/claude-code-tools
|
|
47
|
-
claude plugin install workflow@cctools-plugins
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
Install the equivalent dynamic workflow plugin for Codex:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
codex plugin marketplace add pchalasani/claude-code-tools
|
|
54
|
-
codex plugin add dynamic-workflow@cctools-codex-plugins
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
The Codex plugin installs independently: it does not require the Python
|
|
58
|
-
`claude-code-tools` package or an npm install. Node.js 20 or newer and Codex
|
|
59
|
-
CLI 0.136.0 or newer are required for callbacks. Version 0.136.0 is the first
|
|
60
|
-
compatible CLI release for this callback protocol. Codex still marks the app
|
|
61
|
-
server and remote TUI interfaces as experimental, so those surfaces may evolve.
|
|
62
|
-
The plugin can notify the originating Codex thread through Codex's shared app
|
|
63
|
-
server after a detached workflow finishes. Callback preflight also rejects a
|
|
64
|
-
stale connected server.
|
|
65
|
-
|
|
66
|
-
The optional Python package makes that callback setup a single command:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
codex-dynamic
|
|
70
|
-
# Or continue the latest conversation:
|
|
71
|
-
codex-dynamic resume --last
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
`codex-dynamic` starts or reuses the local app server and then hands the
|
|
75
|
-
terminal directly to Codex. After a plugin or Codex upgrade, the next launch
|
|
76
|
-
automatically starts a fresh server generation; existing sessions and workflow
|
|
77
|
-
callbacks stay connected to their old generation. No manual restart is needed.
|
|
78
|
-
Use `codex-server status` or `logs` for nondisruptive inspection. A forced stop
|
|
79
|
-
or restart cleans up every retained generation and disconnects its attached
|
|
80
|
-
TUIs, so use those commands only after exiting the sessions normally.
|
|
81
|
-
|
|
82
|
-
The Python package also installs `codex-workflows`, an observational dashboard
|
|
83
|
-
for durable workflow runs:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
codex-workflows
|
|
87
|
-
codex-workflows watch --limit 20
|
|
88
|
-
codex-workflows show RUN_ID
|
|
89
|
-
codex-workflows --all
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
The default list contains only active workflows and identifies the project
|
|
93
|
-
folder from which each workflow was launched. Use `show RUN_ID` for its full
|
|
94
|
-
working directory, or `--all` to include completed and diagnostic history. The
|
|
95
|
-
command reads durable workflow state and process metadata. It never changes a
|
|
96
|
-
run, sends a signal, repairs state, or launches an agent. See the
|
|
97
|
-
[codex-workflows reference][codex-workflows-reference] for filters, JSON output,
|
|
98
|
-
and the complete versioned schema.
|
|
99
|
-
|
|
100
|
-
[codex-workflows-reference]:
|
|
101
|
-
//pchalasani.github.io/claude-code-tools/tools/codex-dynamic/#observe-workflow-runs
|
|
102
|
-
|
|
103
|
-
Server management and passive supervision add no model calls. Completion
|
|
104
|
-
reporting starts a new turn only while the thread is idle; otherwise it steers
|
|
105
|
-
and extends the active turn. Either reporting path consumes model tokens.
|
|
106
|
-
|
|
107
|
-
Each workflow `run` or `resume` uses narrow host execution for that exact
|
|
108
|
-
reviewed supervisor command. The workflow JavaScript remains restricted, and
|
|
109
|
-
every headless Codex worker keeps its declared sandbox.
|
|
110
|
-
|
|
111
|
-
See the
|
|
112
|
-
[Claude → Codex migration guide][claude-to-codex-guide]
|
|
113
|
-
for usage and workflow-porting instructions.
|
|
114
|
-
|
|
115
|
-
[claude-to-codex-guide]:
|
|
116
|
-
https://pchalasani.github.io/claude-code-tools/guides/claude-to-codex/
|
|
117
|
-
|
|
118
51
|
---
|
|
119
52
|
|
|
120
53
|
Click a card to jump to that feature, or
|
|
@@ -37,7 +37,7 @@ class ResolveCommand(click.Command):
|
|
|
37
37
|
"""Parse arguments, converting Click usage failures to resolver errors."""
|
|
38
38
|
import sys
|
|
39
39
|
|
|
40
|
-
from claude_code_tools.
|
|
40
|
+
from claude_code_tools.resolve_session_render import _render_error
|
|
41
41
|
|
|
42
42
|
raw_args = tuple(args)
|
|
43
43
|
try:
|
|
@@ -115,9 +115,12 @@ def main(ctx, claude_home, codex_home):
|
|
|
115
115
|
# Auto-index sessions on every aichat command (incremental, fast if up-to-date)
|
|
116
116
|
# Skip for build-index/clear-index to avoid double-indexing or state conflicts
|
|
117
117
|
# In JSON mode (-j/--json), suppress all output for clean parsing
|
|
118
|
+
# 'port' is also skipped: its contract requires the detected
|
|
119
|
+
# direction line to be the FIRST output, so the indexer's progress
|
|
120
|
+
# bar must never print before it.
|
|
118
121
|
skip_auto_index_cmds = ['build-index', 'clear-index', 'index-stats']
|
|
119
122
|
should_skip = (
|
|
120
|
-
ctx.invoked_subcommand
|
|
123
|
+
ctx.invoked_subcommand in ('port', 'resolve')
|
|
121
124
|
or ctx.invoked_subcommand in skip_auto_index_cmds
|
|
122
125
|
or any(cmd in sys.argv for cmd in skip_auto_index_cmds)
|
|
123
126
|
)
|
|
@@ -182,10 +185,16 @@ def resolve_session_cmd(
|
|
|
182
185
|
json_output: bool,
|
|
183
186
|
pretty_output: bool,
|
|
184
187
|
) -> None:
|
|
185
|
-
"""Resolve a name, full
|
|
188
|
+
"""Resolve a session name, full ID, ID fragment, or filename fragment.
|
|
189
|
+
|
|
190
|
+
Ordered tiers (first non-empty wins): exact ID, exact name, ID
|
|
191
|
+
prefix, ID substring, name substring, session-file name
|
|
192
|
+
substring (e.g. a codex rollout timestamp like
|
|
193
|
+
2026-03-25T14-50).
|
|
194
|
+
"""
|
|
186
195
|
import sys
|
|
187
196
|
|
|
188
|
-
from claude_code_tools.
|
|
197
|
+
from claude_code_tools.resolve_session_render import run
|
|
189
198
|
|
|
190
199
|
if json_output and pretty_output:
|
|
191
200
|
click.echo(
|
|
@@ -1442,6 +1451,104 @@ def move_session(session, new_project, agent):
|
|
|
1442
1451
|
console.print(f" [cyan]{resume_cmd}[/cyan]")
|
|
1443
1452
|
|
|
1444
1453
|
|
|
1454
|
+
@main.command("port")
|
|
1455
|
+
@click.argument("session", required=True)
|
|
1456
|
+
@click.option("--claude-home", help="Path to Claude home directory")
|
|
1457
|
+
@click.option("--codex-home", help="Path to Codex home directory")
|
|
1458
|
+
@click.pass_context
|
|
1459
|
+
def port_session(
|
|
1460
|
+
ctx: click.Context,
|
|
1461
|
+
session: str,
|
|
1462
|
+
claude_home: "str | None",
|
|
1463
|
+
codex_home: "str | None",
|
|
1464
|
+
) -> None:
|
|
1465
|
+
"""Port a session to the OTHER agent (codex <-> claude).
|
|
1466
|
+
|
|
1467
|
+
Auto-detects which agent the session belongs to. Codex sessions
|
|
1468
|
+
are converted into a flattened, resumable Claude Code session;
|
|
1469
|
+
Claude sessions are converted into a flattened, resumable Codex
|
|
1470
|
+
rollout.
|
|
1471
|
+
|
|
1472
|
+
SESSION may be a full session id, a partial id (prefix, middle,
|
|
1473
|
+
or suffix fragment), a session name (set with /rename), a
|
|
1474
|
+
rollout filename fragment (e.g. 2026-03-25T14-50), or a session
|
|
1475
|
+
file path. Lookup is global across all projects in both homes.
|
|
1476
|
+
|
|
1477
|
+
\b
|
|
1478
|
+
Examples:
|
|
1479
|
+
aichat port 019f6d85-df3c-7c83-84f6-b97e73305fcf
|
|
1480
|
+
aichat port abc123 --claude-home ~/.claude-alt
|
|
1481
|
+
aichat port my-session-name
|
|
1482
|
+
aichat port 2026-03-25T14-50
|
|
1483
|
+
"""
|
|
1484
|
+
import sys
|
|
1485
|
+
|
|
1486
|
+
from claude_code_tools.port_service import (
|
|
1487
|
+
PortSessionError,
|
|
1488
|
+
port_claude_session,
|
|
1489
|
+
port_codex_session,
|
|
1490
|
+
resolve_port_session,
|
|
1491
|
+
)
|
|
1492
|
+
|
|
1493
|
+
# Group-level --claude-home/--codex-home (given BEFORE the
|
|
1494
|
+
# subcommand) are stored on the root context; command-level
|
|
1495
|
+
# options take precedence.
|
|
1496
|
+
root_obj = ctx.find_root().obj or {}
|
|
1497
|
+
claude_home = claude_home or root_obj.get("claude_home")
|
|
1498
|
+
codex_home = codex_home or root_obj.get("codex_home")
|
|
1499
|
+
|
|
1500
|
+
try:
|
|
1501
|
+
resolved = resolve_port_session(
|
|
1502
|
+
session, claude_home=claude_home, codex_home=codex_home
|
|
1503
|
+
)
|
|
1504
|
+
except PortSessionError as e:
|
|
1505
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
1506
|
+
sys.exit(1)
|
|
1507
|
+
|
|
1508
|
+
if resolved.agent == "claude":
|
|
1509
|
+
print("Detected source agent: claude — porting to Codex")
|
|
1510
|
+
try:
|
|
1511
|
+
result = port_claude_session(
|
|
1512
|
+
resolved.session_file, codex_home=codex_home
|
|
1513
|
+
)
|
|
1514
|
+
except PortSessionError as e:
|
|
1515
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
1516
|
+
sys.exit(1)
|
|
1517
|
+
|
|
1518
|
+
print()
|
|
1519
|
+
print(f"New Codex session id: {result.new_session_id}")
|
|
1520
|
+
print(f"Output file: {result.output_file}")
|
|
1521
|
+
print(f"Session cwd: {result.cwd}")
|
|
1522
|
+
print()
|
|
1523
|
+
print("To resume:")
|
|
1524
|
+
print(f" {result.resume_hint}")
|
|
1525
|
+
print()
|
|
1526
|
+
print(
|
|
1527
|
+
"Tip: codex's /import can also import Claude sessions "
|
|
1528
|
+
"natively (interactive alternative)."
|
|
1529
|
+
)
|
|
1530
|
+
return
|
|
1531
|
+
|
|
1532
|
+
print("Detected source agent: codex — porting to Claude Code")
|
|
1533
|
+
try:
|
|
1534
|
+
result = port_codex_session(
|
|
1535
|
+
resolved.session_file, claude_home=claude_home
|
|
1536
|
+
)
|
|
1537
|
+
except PortSessionError as e:
|
|
1538
|
+
# Expected failure modes (missing/empty session, filesystem
|
|
1539
|
+
# or encoding errors) become a clean error, not a traceback.
|
|
1540
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
1541
|
+
sys.exit(1)
|
|
1542
|
+
|
|
1543
|
+
print()
|
|
1544
|
+
print(f"New Claude session id: {result.new_session_id}")
|
|
1545
|
+
print(f"Output file: {result.output_file}")
|
|
1546
|
+
print(f"Session cwd: {result.cwd}")
|
|
1547
|
+
print()
|
|
1548
|
+
print("To resume:")
|
|
1549
|
+
print(f" {result.resume_hint}")
|
|
1550
|
+
|
|
1551
|
+
|
|
1445
1552
|
@main.command("query")
|
|
1446
1553
|
@click.argument("session", required=False)
|
|
1447
1554
|
@click.argument("question", required=False)
|
|
@@ -14,11 +14,13 @@ import struct
|
|
|
14
14
|
import subprocess
|
|
15
15
|
import sys
|
|
16
16
|
import time
|
|
17
|
-
from contextlib import contextmanager
|
|
17
|
+
from contextlib import ExitStack, contextmanager
|
|
18
18
|
from dataclasses import replace
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from typing import Iterator, Mapping, Sequence
|
|
21
21
|
|
|
22
|
+
from claude_code_tools import codex_server_retry
|
|
23
|
+
from claude_code_tools.codex_server_legacy import reauthenticate_legacy_state
|
|
22
24
|
from claude_code_tools.codex_server_fingerprint import (
|
|
23
25
|
PluginSnapshot as _PluginSnapshot,
|
|
24
26
|
hash_plugin_tree as _hash_plugin_tree, # noqa: F401
|
|
@@ -78,6 +80,7 @@ from claude_code_tools.codex_server_reuse import (
|
|
|
78
80
|
disconnect_refusal as _disconnect_refusal,
|
|
79
81
|
helper_restart_reason as _reuse_restart_reason,
|
|
80
82
|
require_external_compatible as _external_compatible,
|
|
83
|
+
same_server_launch as _same_server_launch,
|
|
81
84
|
)
|
|
82
85
|
|
|
83
86
|
|
|
@@ -150,6 +153,22 @@ def _lifecycle_lock(paths: ServerPaths) -> Iterator[None]:
|
|
|
150
153
|
os.close(fd)
|
|
151
154
|
|
|
152
155
|
|
|
156
|
+
@contextmanager
|
|
157
|
+
def _reserved_generation_lifecycle(
|
|
158
|
+
base: ServerPaths,
|
|
159
|
+
paths: ServerPaths,
|
|
160
|
+
generation: str,
|
|
161
|
+
) -> Iterator[None]:
|
|
162
|
+
"""Reserve capacity before releasing the base lifecycle lock."""
|
|
163
|
+
generation_lock = ExitStack()
|
|
164
|
+
with _lifecycle_lock(base):
|
|
165
|
+
require_generation_capacity(base, generation)
|
|
166
|
+
prepare_runtime(paths)
|
|
167
|
+
generation_lock.enter_context(_lifecycle_lock(paths))
|
|
168
|
+
with generation_lock:
|
|
169
|
+
yield
|
|
170
|
+
|
|
171
|
+
|
|
153
172
|
def _resolve_codex(env: Mapping[str, str]) -> str:
|
|
154
173
|
"""Resolve a canonical executable used by both server and TUI."""
|
|
155
174
|
configured = env.get("CCTOOLS_CODEX_BIN")
|
|
@@ -519,6 +538,7 @@ def get_status(env: Mapping[str, str] | None = None) -> ServerStatus:
|
|
|
519
538
|
except StateFileError as exc:
|
|
520
539
|
state = None
|
|
521
540
|
state_error = str(exc)
|
|
541
|
+
state = reauthenticate_legacy_state(paths, state, _socket_peer_pid)
|
|
522
542
|
probe = _probe_server(codex_path, child_env, paths)
|
|
523
543
|
return _status_from(paths, state, probe, state_error)
|
|
524
544
|
|
|
@@ -557,33 +577,12 @@ def _require_unchanged_plugin_snapshot(
|
|
|
557
577
|
) -> None:
|
|
558
578
|
"""Reject a lifecycle decision made across a plugin input change."""
|
|
559
579
|
if _plugin_configuration_snapshot(paths, codex_options) != expected:
|
|
560
|
-
raise
|
|
580
|
+
raise codex_server_retry.PluginSnapshotChangedError(
|
|
561
581
|
"the Codex plugin or marketplace snapshot changed during "
|
|
562
582
|
f"{operation}; retry after plugin updates finish"
|
|
563
583
|
)
|
|
564
584
|
|
|
565
585
|
|
|
566
|
-
def _same_server_launch(expected: OwnedServer, current: OwnedServer) -> bool:
|
|
567
|
-
"""Return whether two states name the same exact supervised launch."""
|
|
568
|
-
return (
|
|
569
|
-
current.pid,
|
|
570
|
-
current.pgid,
|
|
571
|
-
current.process_started_at,
|
|
572
|
-
current.launch_token,
|
|
573
|
-
current.worker_pid,
|
|
574
|
-
current.worker_pgid,
|
|
575
|
-
current.worker_started_at,
|
|
576
|
-
) == (
|
|
577
|
-
expected.pid,
|
|
578
|
-
expected.pgid,
|
|
579
|
-
expected.process_started_at,
|
|
580
|
-
expected.launch_token,
|
|
581
|
-
expected.worker_pid,
|
|
582
|
-
expected.worker_pgid,
|
|
583
|
-
expected.worker_started_at,
|
|
584
|
-
)
|
|
585
|
-
|
|
586
|
-
|
|
587
586
|
def _certify_helper_boundary(
|
|
588
587
|
expected: OwnedServer,
|
|
589
588
|
active_env: Mapping[str, str],
|
|
@@ -724,6 +723,7 @@ def _wait_for_process_group_exit(
|
|
|
724
723
|
)
|
|
725
724
|
|
|
726
725
|
|
|
726
|
+
@codex_server_retry.retry_plugin_snapshot_changes
|
|
727
727
|
def ensure_server(
|
|
728
728
|
env: Mapping[str, str] | None = None,
|
|
729
729
|
*,
|
|
@@ -745,16 +745,15 @@ def ensure_server(
|
|
|
745
745
|
codex_options,
|
|
746
746
|
)
|
|
747
747
|
paths = paths_for_generation(base_paths, generation)
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
with _lifecycle_lock(paths):
|
|
748
|
+
with _reserved_generation_lifecycle(base_paths, paths, generation):
|
|
749
|
+
child_env = _command_env(active_env, paths)
|
|
750
|
+
child_env[CODEX_SERVER_OPTIONS_ENV] = json.dumps(
|
|
751
|
+
list(codex_options),
|
|
752
|
+
separators=(",", ":"),
|
|
753
|
+
)
|
|
755
754
|
locked_snapshot = _plugin_configuration_snapshot(paths, codex_options)
|
|
756
755
|
if locked_snapshot.fingerprint != plugin_snapshot.fingerprint:
|
|
757
|
-
raise
|
|
756
|
+
raise codex_server_retry.PluginSnapshotChangedError(
|
|
758
757
|
"the Codex plugin or marketplace snapshot changed during "
|
|
759
758
|
"app-server generation selection; retry after updates finish"
|
|
760
759
|
)
|
|
@@ -766,6 +765,7 @@ def ensure_server(
|
|
|
766
765
|
except StateFileError as exc:
|
|
767
766
|
state = None
|
|
768
767
|
state_error = str(exc)
|
|
768
|
+
state = reauthenticate_legacy_state(paths, state, _socket_peer_pid)
|
|
769
769
|
|
|
770
770
|
probe = _probe_server(codex_path, child_env, paths)
|
|
771
771
|
status = _status_from(paths, state, probe, state_error)
|
|
@@ -938,6 +938,7 @@ def _stop_server_at(
|
|
|
938
938
|
) from exc
|
|
939
939
|
_quarantine_invalid_state(paths)
|
|
940
940
|
state = None
|
|
941
|
+
state = reauthenticate_legacy_state(paths, state, _socket_peer_pid)
|
|
941
942
|
|
|
942
943
|
if state is not None and _state_is_owned(state):
|
|
943
944
|
if not allow_disconnect:
|