claude-code-tools 1.29.4__tar.gz → 1.29.6__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.29.4 → claude_code_tools-1.29.6}/PKG-INFO +10 -1
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/README.md +9 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/__init__.py +1 -1
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/github_wake.py +82 -20
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/github_watch_daemon.py +7 -1
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/github_watch_store.py +38 -5
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/issue_reply_delivery.py +123 -50
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_claude_to_codex.py +5 -15
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/pyproject.toml +2 -2
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/.gitignore +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/LICENSE +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/action_rpc.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/__init__.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/backends.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/cli.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/config.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/convert.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/discord_bot.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/http_frontend.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/locking.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/mattermost_bot.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/paths.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/readonly.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/registry.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/relay.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/serve.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/store.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/tmux.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/trust.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/turn_log.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/aichat.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/__init__.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/__main__.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/activity.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/cache.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/cli.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/detect.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/filters.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/model.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/render.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/amux/scan.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/claude_continue.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/cli_passthrough.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_app_server_rpc.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_continue.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_callback.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_cli.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_fingerprint.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_generation.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_legacy.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_models.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_process.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_reservation.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_resume.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_retry.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_reuse.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_state.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_supervisor.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_wait.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_worker.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/config.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/csv2gsheet.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/delete_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/dotenv_vault.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/env_safe.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/export_all.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/export_claude_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/export_codex_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/export_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_claude_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_codex_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_original_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_trimmed_sessions.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/fix_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/gdoc2docx.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/gdoc2md.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/gsheet2csv.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/helper_sessions.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/legacy_session_wrappers.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/md2gdoc.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/move_account.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/__init__.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/cli.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/hooks.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/migrations.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/models.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/prompt_detect.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/store.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/watcher.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/node_menu_ui.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_claude_noise.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_codex_flatten.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_codex_to_claude.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_render.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_service.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/resolve_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/resolve_session_names.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/resolve_session_render.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/search_index.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_cli_resolution.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_lineage.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_menu.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_menu_cli.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_resolution.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_selection.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_utils.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/smart_trim.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/smart_trim_core.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/tmux_cli_controller.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/tmux_execution_helpers.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/tmux_remote_controller.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/trim_in_place.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/trim_session.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/trim_session_claude.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/trim_session_codex.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_contract.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_formatting.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_identity_policy.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_manifest.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_projection.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_rendering.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_snapshots.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_store_backends.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_processes.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_runs.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_store_io.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_validation.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/agent-tunnel-spec.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/claude-code-tmux-tutorials.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/dot-zshrc.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/find-claude-session.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/intercom-spec.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/lmsh.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/local-llm-setup.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/tmux-cli-instructions.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/docs/vault-documentation.md +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/hatch_build.py +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/node_ui/action_config.js +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/node_ui/menu.js +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/node_ui/package-lock.json +0 -0
- {claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/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.29.
|
|
3
|
+
Version: 1.29.6
|
|
4
4
|
Summary: Collection of tools for working with Claude Code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -47,6 +47,15 @@ Everything — installation, every tool, plugins, and guides — lives in the
|
|
|
47
47
|
docs. Click a card below to jump to a feature, or
|
|
48
48
|
**[read the full docs](https://pchalasani.github.io/claude-code-tools/)**.
|
|
49
49
|
|
|
50
|
+
Skills install with one command (`-s` picks one skill, `-g` installs for
|
|
51
|
+
every project). Most work in any agent; a few are Claude Code and Codex
|
|
52
|
+
only (see the [Plugins page](https://pchalasani.github.io/claude-code-tools/getting-started/plugins/)).
|
|
53
|
+
Needs Node.js 22.20 or newer for `npx`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx skills add pchalasani/claude-code-tools -s voxtype-install
|
|
57
|
+
```
|
|
58
|
+
|
|
50
59
|
<div align="center">
|
|
51
60
|
|
|
52
61
|
<table>
|
|
@@ -21,6 +21,15 @@ Everything — installation, every tool, plugins, and guides — lives in the
|
|
|
21
21
|
docs. Click a card below to jump to a feature, or
|
|
22
22
|
**[read the full docs](https://pchalasani.github.io/claude-code-tools/)**.
|
|
23
23
|
|
|
24
|
+
Skills install with one command (`-s` picks one skill, `-g` installs for
|
|
25
|
+
every project). Most work in any agent; a few are Claude Code and Codex
|
|
26
|
+
only (see the [Plugins page](https://pchalasani.github.io/claude-code-tools/getting-started/plugins/)).
|
|
27
|
+
Needs Node.js 22.20 or newer for `npx`:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx skills add pchalasani/claude-code-tools -s voxtype-install
|
|
31
|
+
```
|
|
32
|
+
|
|
24
33
|
<div align="center">
|
|
25
34
|
|
|
26
35
|
<table>
|
|
@@ -19,8 +19,13 @@ from urllib.parse import urlparse
|
|
|
19
19
|
|
|
20
20
|
import click
|
|
21
21
|
|
|
22
|
+
from claude_code_tools import __version__
|
|
22
23
|
from claude_code_tools.codex_server_process import process_identity
|
|
23
|
-
from claude_code_tools.github_watch_store import
|
|
24
|
+
from claude_code_tools.github_watch_store import (
|
|
25
|
+
IssueWatch,
|
|
26
|
+
WatcherStatus,
|
|
27
|
+
WatchStore,
|
|
28
|
+
)
|
|
24
29
|
from claude_code_tools.issue_reply_delivery import (
|
|
25
30
|
_MAX_MONITOR_MESSAGE_BYTES,
|
|
26
31
|
_MONITOR_ACK,
|
|
@@ -31,6 +36,7 @@ from claude_code_tools.issue_reply_delivery import (
|
|
|
31
36
|
|
|
32
37
|
_DAEMON_START_SECONDS = 5.0
|
|
33
38
|
_DAEMON_STOP_SECONDS = 5.0
|
|
39
|
+
_DAEMON_REPLACE_SECONDS = 30.0
|
|
34
40
|
_MAX_GH_OUTPUT_BYTES = 1024 * 1024
|
|
35
41
|
|
|
36
42
|
|
|
@@ -41,6 +47,11 @@ _MAX_GH_OUTPUT_BYTES = 1024 * 1024
|
|
|
41
47
|
@click.option("--start", is_flag=True, help="Start the shared watcher.")
|
|
42
48
|
@click.option("--stop", is_flag=True, help="Stop the shared watcher.")
|
|
43
49
|
@click.option("--cancel", metavar="WATCH_ID", help="Cancel one pending wakeup.")
|
|
50
|
+
@click.option(
|
|
51
|
+
"--codex-home",
|
|
52
|
+
type=click.Path(path_type=Path, exists=True, file_okay=False),
|
|
53
|
+
help="Codex account directory of this session (default: $CODEX_HOME).",
|
|
54
|
+
)
|
|
44
55
|
@click.option(
|
|
45
56
|
"--claude-monitor",
|
|
46
57
|
is_flag=True,
|
|
@@ -53,9 +64,15 @@ def cli(
|
|
|
53
64
|
start: bool,
|
|
54
65
|
stop: bool,
|
|
55
66
|
cancel: str | None,
|
|
67
|
+
codex_home: Path | None,
|
|
56
68
|
claude_monitor: bool,
|
|
57
69
|
) -> None:
|
|
58
|
-
"""Wake this session when ISSUE_URL receives its first future comment.
|
|
70
|
+
"""Wake this session when ISSUE_URL receives its first future comment.
|
|
71
|
+
|
|
72
|
+
In Codex, the reply is delivered with `codex queue` to the session named
|
|
73
|
+
by CODEX_THREAD_ID. In Claude Code, run with --claude-monitor under the
|
|
74
|
+
Monitor tool.
|
|
75
|
+
"""
|
|
59
76
|
modes = sum((show_status, start, stop, cancel is not None))
|
|
60
77
|
if modes > 1 or (modes and issue_url is not None) or (modes and claude_monitor):
|
|
61
78
|
raise click.UsageError(
|
|
@@ -63,6 +80,8 @@ def cli(
|
|
|
63
80
|
)
|
|
64
81
|
if as_json and not show_status:
|
|
65
82
|
raise click.UsageError("--json requires --status")
|
|
83
|
+
if codex_home is not None and (modes or claude_monitor):
|
|
84
|
+
raise click.UsageError("--codex-home applies only to Codex reply wakeups")
|
|
66
85
|
if show_status:
|
|
67
86
|
_show_status(as_json)
|
|
68
87
|
return
|
|
@@ -83,21 +102,20 @@ def cli(
|
|
|
83
102
|
if claude_monitor:
|
|
84
103
|
_run_claude_monitor(issue_url)
|
|
85
104
|
return
|
|
86
|
-
|
|
105
|
+
try:
|
|
106
|
+
target = codex_target_from_environment(codex_home)
|
|
107
|
+
except DeliveryConfigurationError as exc:
|
|
108
|
+
raise click.ClickException(str(exc)) from exc
|
|
109
|
+
_register(issue_url, target)
|
|
87
110
|
|
|
88
111
|
|
|
89
112
|
def _register(
|
|
90
113
|
issue_url: str,
|
|
91
|
-
target: DeliveryTarget
|
|
114
|
+
target: DeliveryTarget,
|
|
92
115
|
announce: bool = True,
|
|
93
116
|
preserve_on_start_failure: bool = True,
|
|
94
117
|
) -> IssueWatch:
|
|
95
118
|
"""Validate and durably register one existing issue URL."""
|
|
96
|
-
if target is None:
|
|
97
|
-
try:
|
|
98
|
-
target = codex_target_from_environment()
|
|
99
|
-
except DeliveryConfigurationError as exc:
|
|
100
|
-
raise click.ClickException(str(exc)) from exc
|
|
101
119
|
repository, issue_number, host = _parse_issue_url(issue_url)
|
|
102
120
|
canonical_url = _verify_issue(repository, issue_number, host)
|
|
103
121
|
store = WatchStore()
|
|
@@ -325,19 +343,46 @@ def _stop_watcher(store: WatchStore) -> None:
|
|
|
325
343
|
return
|
|
326
344
|
assert status is not None
|
|
327
345
|
os.kill(status.pid, signal.SIGTERM)
|
|
328
|
-
|
|
346
|
+
if not _wait_for_exit(status, _DAEMON_STOP_SECONDS):
|
|
347
|
+
raise click.ClickException("watcher did not stop after SIGTERM")
|
|
348
|
+
click.echo("Watcher stopped.")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _replace_watcher(status: WatcherStatus) -> None:
|
|
352
|
+
"""End a watcher of another version, forcing it if a poll keeps it busy.
|
|
353
|
+
|
|
354
|
+
Its state is durable, so a forced stop can at most repeat a delivery.
|
|
355
|
+
"""
|
|
356
|
+
if process_identity(status.pid) != status.process_identity:
|
|
357
|
+
return
|
|
358
|
+
os.kill(status.pid, signal.SIGTERM)
|
|
359
|
+
if _wait_for_exit(status, _DAEMON_REPLACE_SECONDS):
|
|
360
|
+
return
|
|
361
|
+
if process_identity(status.pid) == status.process_identity:
|
|
362
|
+
os.kill(status.pid, signal.SIGKILL)
|
|
363
|
+
if not _wait_for_exit(status, _DAEMON_STOP_SECONDS):
|
|
364
|
+
raise RuntimeError("watcher of another version did not exit")
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _wait_for_exit(status: WatcherStatus, seconds: float) -> bool:
|
|
368
|
+
"""Wait until the exact recorded watcher process is gone."""
|
|
369
|
+
deadline = time.monotonic() + seconds
|
|
329
370
|
while time.monotonic() < deadline:
|
|
330
371
|
if process_identity(status.pid) != status.process_identity:
|
|
331
|
-
|
|
332
|
-
return
|
|
372
|
+
return True
|
|
333
373
|
time.sleep(0.05)
|
|
334
|
-
|
|
374
|
+
return False
|
|
335
375
|
|
|
336
376
|
|
|
337
|
-
def _ensure_watcher(store: WatchStore) -> bool:
|
|
338
|
-
"""Start one detached watcher and wait for its certified heartbeat.
|
|
339
|
-
|
|
340
|
-
|
|
377
|
+
def _ensure_watcher(store: WatchStore, replace_other_version: bool = True) -> bool:
|
|
378
|
+
"""Start one detached watcher and wait for its certified heartbeat.
|
|
379
|
+
|
|
380
|
+
A healthy watcher left running by another installed version is replaced
|
|
381
|
+
once, since it may not know the delivery adapter this version records.
|
|
382
|
+
"""
|
|
383
|
+
status = store.watcher_status()
|
|
384
|
+
if _watcher_is_healthy(status):
|
|
385
|
+
return _adopt_watcher(store, status, replace_other_version)
|
|
341
386
|
log = open(store.log_path, "ab", buffering=0)
|
|
342
387
|
try:
|
|
343
388
|
process = subprocess.Popen(
|
|
@@ -353,8 +398,9 @@ def _ensure_watcher(store: WatchStore) -> bool:
|
|
|
353
398
|
deadline = time.monotonic() + _DAEMON_START_SECONDS
|
|
354
399
|
while time.monotonic() < deadline:
|
|
355
400
|
if process.poll() is not None:
|
|
356
|
-
|
|
357
|
-
|
|
401
|
+
status = store.watcher_status()
|
|
402
|
+
if _watcher_is_healthy(status):
|
|
403
|
+
return _adopt_watcher(store, status, replace_other_version)
|
|
358
404
|
raise RuntimeError(f"watcher exited with status {process.returncode}")
|
|
359
405
|
status = store.watcher_status()
|
|
360
406
|
if (
|
|
@@ -364,13 +410,29 @@ def _ensure_watcher(store: WatchStore) -> bool:
|
|
|
364
410
|
):
|
|
365
411
|
return True
|
|
366
412
|
if _watcher_is_healthy(status):
|
|
367
|
-
return
|
|
413
|
+
return _adopt_watcher(store, status, replace_other_version)
|
|
368
414
|
time.sleep(0.05)
|
|
369
415
|
process.terminate()
|
|
370
416
|
process.wait(timeout=2)
|
|
371
417
|
raise RuntimeError("watcher did not publish a healthy heartbeat")
|
|
372
418
|
|
|
373
419
|
|
|
420
|
+
def _adopt_watcher(
|
|
421
|
+
store: WatchStore,
|
|
422
|
+
status: WatcherStatus | None,
|
|
423
|
+
replace_other_version: bool,
|
|
424
|
+
) -> bool:
|
|
425
|
+
"""Reuse a healthy watcher of this version, or replace it once."""
|
|
426
|
+
if status is not None and status.code_version == __version__:
|
|
427
|
+
return False
|
|
428
|
+
if status is None or not replace_other_version:
|
|
429
|
+
raise RuntimeError(
|
|
430
|
+
"a watcher from another claude-code-tools version is still running"
|
|
431
|
+
)
|
|
432
|
+
_replace_watcher(status)
|
|
433
|
+
return _ensure_watcher(store, replace_other_version=False)
|
|
434
|
+
|
|
435
|
+
|
|
374
436
|
def _watcher_is_healthy(status: object) -> bool:
|
|
375
437
|
"""Return whether durable status still identifies its exact process."""
|
|
376
438
|
if status is None:
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/github_watch_daemon.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""One shared daemon that wakes
|
|
1
|
+
"""One shared daemon that wakes agent sessions when watched issues get replies."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -17,6 +17,7 @@ from datetime import UTC, datetime, timedelta
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from types import FrameType
|
|
19
19
|
|
|
20
|
+
from claude_code_tools import __version__
|
|
20
21
|
from claude_code_tools.codex_server_process import process_identity
|
|
21
22
|
from claude_code_tools.github_watch_store import (
|
|
22
23
|
IssueWatch,
|
|
@@ -25,6 +26,7 @@ from claude_code_tools.github_watch_store import (
|
|
|
25
26
|
)
|
|
26
27
|
from claude_code_tools.issue_reply_delivery import (
|
|
27
28
|
IssueReply,
|
|
29
|
+
UnsupportedDeliveryTarget,
|
|
28
30
|
deliver_issue_reply,
|
|
29
31
|
)
|
|
30
32
|
|
|
@@ -114,6 +116,7 @@ class GitHubWatchDaemon:
|
|
|
114
116
|
self.pid,
|
|
115
117
|
self.process_identity,
|
|
116
118
|
self.started_at,
|
|
119
|
+
__version__,
|
|
117
120
|
)
|
|
118
121
|
|
|
119
122
|
def _poll_once(self) -> None:
|
|
@@ -294,6 +297,9 @@ class GitHubWatchDaemon:
|
|
|
294
297
|
"""Deliver one idempotent reply notification into the origin thread."""
|
|
295
298
|
try:
|
|
296
299
|
deliver_issue_reply(watch, comment)
|
|
300
|
+
except UnsupportedDeliveryTarget as exc:
|
|
301
|
+
self.store.mark_failed(watch.watch_id, _diagnostic(exc))
|
|
302
|
+
return False
|
|
297
303
|
except Exception as exc:
|
|
298
304
|
self.store.mark_retry(watch.watch_id, _diagnostic(exc))
|
|
299
305
|
return False
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/github_watch_store.py
RENAMED
|
@@ -50,6 +50,7 @@ class WatcherStatus:
|
|
|
50
50
|
process_identity: str
|
|
51
51
|
started_at: str
|
|
52
52
|
heartbeat_at: str
|
|
53
|
+
code_version: str | None = None
|
|
53
54
|
|
|
54
55
|
|
|
55
56
|
@dataclass(frozen=True)
|
|
@@ -114,6 +115,7 @@ class WatchStore:
|
|
|
114
115
|
with self._connect() as connection:
|
|
115
116
|
connection.executescript(_SCHEMA)
|
|
116
117
|
_migrate_repository_cursors(connection)
|
|
118
|
+
_migrate_watcher_status(connection)
|
|
117
119
|
|
|
118
120
|
@property
|
|
119
121
|
def lock_path(self) -> Path:
|
|
@@ -242,6 +244,18 @@ class WatchStore:
|
|
|
242
244
|
[(watch_id,) for watch_id in watch_ids],
|
|
243
245
|
)
|
|
244
246
|
|
|
247
|
+
def mark_failed(self, watch_id: str, error: str) -> None:
|
|
248
|
+
"""Stop retrying one watch whose delivery can never succeed."""
|
|
249
|
+
with self._connect() as connection:
|
|
250
|
+
connection.execute(
|
|
251
|
+
"""
|
|
252
|
+
UPDATE issue_watches
|
|
253
|
+
SET status = 'failed', last_error = ?
|
|
254
|
+
WHERE watch_id = ? AND status IN ('pending', 'retrying')
|
|
255
|
+
""",
|
|
256
|
+
(error[:4096], watch_id),
|
|
257
|
+
)
|
|
258
|
+
|
|
245
259
|
def cancel(self, watch_id: str) -> bool:
|
|
246
260
|
"""Cancel one pending watch by full ID or unambiguous prefix."""
|
|
247
261
|
with self._connect() as connection:
|
|
@@ -300,23 +314,32 @@ class WatchStore:
|
|
|
300
314
|
pid: int,
|
|
301
315
|
process_identity: str,
|
|
302
316
|
started_at: str,
|
|
317
|
+
code_version: str,
|
|
303
318
|
) -> None:
|
|
304
|
-
"""Publish ownership and
|
|
319
|
+
"""Publish ownership, liveness, and code version for the daemon."""
|
|
305
320
|
with self._connect() as connection:
|
|
306
321
|
connection.execute(
|
|
307
322
|
"""
|
|
308
323
|
INSERT INTO watcher_status(
|
|
309
324
|
singleton, instance_id, pid, process_identity,
|
|
310
|
-
started_at, heartbeat_at
|
|
311
|
-
) VALUES (1, ?, ?, ?, ?, ?)
|
|
325
|
+
started_at, heartbeat_at, code_version
|
|
326
|
+
) VALUES (1, ?, ?, ?, ?, ?, ?)
|
|
312
327
|
ON CONFLICT(singleton) DO UPDATE SET
|
|
313
328
|
instance_id = excluded.instance_id,
|
|
314
329
|
pid = excluded.pid,
|
|
315
330
|
process_identity = excluded.process_identity,
|
|
316
331
|
started_at = excluded.started_at,
|
|
317
|
-
heartbeat_at = excluded.heartbeat_at
|
|
332
|
+
heartbeat_at = excluded.heartbeat_at,
|
|
333
|
+
code_version = excluded.code_version
|
|
318
334
|
""",
|
|
319
|
-
(
|
|
335
|
+
(
|
|
336
|
+
instance_id,
|
|
337
|
+
pid,
|
|
338
|
+
process_identity,
|
|
339
|
+
started_at,
|
|
340
|
+
_now(),
|
|
341
|
+
code_version,
|
|
342
|
+
),
|
|
320
343
|
)
|
|
321
344
|
|
|
322
345
|
def watcher_status(self) -> WatcherStatus | None:
|
|
@@ -333,6 +356,7 @@ class WatchStore:
|
|
|
333
356
|
process_identity=row["process_identity"],
|
|
334
357
|
started_at=row["started_at"],
|
|
335
358
|
heartbeat_at=row["heartbeat_at"],
|
|
359
|
+
code_version=row["code_version"],
|
|
336
360
|
)
|
|
337
361
|
|
|
338
362
|
@contextmanager
|
|
@@ -385,6 +409,15 @@ def _migrate_repository_cursors(connection: sqlite3.Connection) -> None:
|
|
|
385
409
|
connection.execute("ALTER TABLE repository_cursors ADD COLUMN etag TEXT")
|
|
386
410
|
|
|
387
411
|
|
|
412
|
+
def _migrate_watcher_status(connection: sqlite3.Connection) -> None:
|
|
413
|
+
"""Record which code version a watcher runs; older watchers leave NULL."""
|
|
414
|
+
columns = {
|
|
415
|
+
row["name"] for row in connection.execute("PRAGMA table_info(watcher_status)")
|
|
416
|
+
}
|
|
417
|
+
if "code_version" not in columns:
|
|
418
|
+
connection.execute("ALTER TABLE watcher_status ADD COLUMN code_version TEXT")
|
|
419
|
+
|
|
420
|
+
|
|
388
421
|
def _now() -> str:
|
|
389
422
|
"""Return a stable UTC timestamp."""
|
|
390
423
|
return datetime.now(UTC).isoformat(timespec="microseconds")
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/issue_reply_delivery.py
RENAMED
|
@@ -5,14 +5,12 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
|
+
import shutil
|
|
8
9
|
import socket
|
|
10
|
+
import subprocess
|
|
9
11
|
from dataclasses import dataclass
|
|
12
|
+
from pathlib import Path
|
|
10
13
|
|
|
11
|
-
from claude_code_tools.codex_app_server_rpc import (
|
|
12
|
-
deliver_thread_message,
|
|
13
|
-
socket_path_from_endpoint,
|
|
14
|
-
verify_thread_loaded,
|
|
15
|
-
)
|
|
16
14
|
from claude_code_tools.github_watch_store import IssueWatch
|
|
17
15
|
|
|
18
16
|
_THREAD_ID = re.compile(
|
|
@@ -47,45 +45,100 @@ class DeliveryConfigurationError(RuntimeError):
|
|
|
47
45
|
"""The current agent session cannot receive durable notifications."""
|
|
48
46
|
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
class UnsupportedDeliveryTarget(DeliveryConfigurationError):
|
|
49
|
+
"""A stored watch names an adapter this version can never deliver to."""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def codex_target_from_environment(codex_home: Path | None = None) -> DeliveryTarget:
|
|
53
|
+
"""Preflight and capture the current native Codex session as a queue target.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
codex_home: Codex account directory; defaults to ``$CODEX_HOME`` and
|
|
57
|
+
then to the one ``~/.codex*`` directory holding this thread.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
A ``codex-queue-v1`` target naming the thread, account, and executable.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
DeliveryConfigurationError: This shell is not a usable Codex session.
|
|
64
|
+
"""
|
|
65
|
+
thread_id = os.environ.get("CODEX_THREAD_ID", "").strip().lower()
|
|
54
66
|
if not _THREAD_ID.fullmatch(thread_id):
|
|
55
67
|
raise DeliveryConfigurationError(
|
|
56
|
-
"github-wake requires a Codex
|
|
57
|
-
"
|
|
68
|
+
"github-wake requires a Codex session shell (CODEX_THREAD_ID is "
|
|
69
|
+
"unset); in Claude Code use --claude-monitor"
|
|
58
70
|
)
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
home = _codex_home_for_thread(thread_id, codex_home)
|
|
72
|
+
found = shutil.which("codex")
|
|
73
|
+
if found is None:
|
|
74
|
+
raise DeliveryConfigurationError("cannot find the codex executable")
|
|
75
|
+
# The shared watcher runs elsewhere, so a relative PATH hit must not leak.
|
|
76
|
+
executable = os.path.abspath(found)
|
|
61
77
|
try:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
completed = subprocess.run(
|
|
79
|
+
[executable, "queue", "--help"],
|
|
80
|
+
stdin=subprocess.DEVNULL,
|
|
81
|
+
capture_output=True,
|
|
82
|
+
text=True,
|
|
83
|
+
timeout=30,
|
|
84
|
+
check=False,
|
|
85
|
+
env={**os.environ, "CODEX_HOME": str(home)},
|
|
86
|
+
)
|
|
87
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
88
|
+
raise DeliveryConfigurationError(f"cannot run codex: {exc}") from exc
|
|
89
|
+
if (
|
|
90
|
+
completed.returncode != 0
|
|
91
|
+
or "--thread" not in completed.stdout
|
|
92
|
+
or "--message" not in completed.stdout
|
|
93
|
+
):
|
|
65
94
|
raise DeliveryConfigurationError(
|
|
66
|
-
|
|
67
|
-
|
|
95
|
+
"the installed codex does not support 'codex queue --thread "
|
|
96
|
+
"--message'; upgrade Codex"
|
|
97
|
+
)
|
|
68
98
|
return DeliveryTarget(
|
|
69
|
-
kind="codex-
|
|
70
|
-
payload={
|
|
99
|
+
kind="codex-queue-v1",
|
|
100
|
+
payload={
|
|
101
|
+
"codexHome": str(home),
|
|
102
|
+
"executable": executable,
|
|
103
|
+
"threadId": thread_id,
|
|
104
|
+
},
|
|
71
105
|
)
|
|
72
106
|
|
|
73
107
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
|
|
108
|
+
def _codex_home_for_thread(
|
|
109
|
+
thread_id: str,
|
|
110
|
+
codex_home: Path | None,
|
|
111
|
+
search_root: Path | None = None,
|
|
112
|
+
) -> Path:
|
|
113
|
+
"""Resolve the Codex account directory that owns one session thread.
|
|
77
114
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
|
|
115
|
+
Codex does not export CODEX_HOME to tool shells, so without an explicit
|
|
116
|
+
directory the unique ``.codex*`` account under ``search_root`` (default:
|
|
117
|
+
the home directory) holding the rollout is used.
|
|
118
|
+
"""
|
|
119
|
+
root = search_root or Path.home()
|
|
120
|
+
explicit = codex_home or (
|
|
121
|
+
Path(os.environ["CODEX_HOME"]) if os.environ.get("CODEX_HOME") else None
|
|
122
|
+
)
|
|
123
|
+
candidates = (
|
|
124
|
+
[explicit.expanduser()]
|
|
125
|
+
if explicit is not None
|
|
126
|
+
else sorted(root.glob(".codex*"))
|
|
127
|
+
)
|
|
128
|
+
pattern = f"sessions/*/*/*/rollout-*-{thread_id}.jsonl"
|
|
129
|
+
owners = {
|
|
130
|
+
path.resolve()
|
|
131
|
+
for path in candidates
|
|
132
|
+
if path.is_dir() and any(path.glob(pattern))
|
|
133
|
+
}
|
|
134
|
+
if len(owners) == 1:
|
|
135
|
+
return owners.pop()
|
|
136
|
+
searched = ", ".join(str(path) for path in candidates) or str(root)
|
|
137
|
+
problem = "several account directories" if owners else "no account directory"
|
|
138
|
+
raise DeliveryConfigurationError(
|
|
139
|
+
f"{problem} among {searched} holds Codex thread {thread_id}; pass "
|
|
140
|
+
"--codex-home with this session's account directory"
|
|
141
|
+
)
|
|
89
142
|
|
|
90
143
|
|
|
91
144
|
def deliver_issue_reply(watch: IssueWatch, reply: IssueReply) -> None:
|
|
@@ -93,27 +146,47 @@ def deliver_issue_reply(watch: IssueWatch, reply: IssueReply) -> None:
|
|
|
93
146
|
if watch.target_kind == "claude-monitor-v1":
|
|
94
147
|
_deliver_claude_monitor(watch, reply)
|
|
95
148
|
return
|
|
96
|
-
if watch.target_kind == "codex-
|
|
97
|
-
|
|
149
|
+
if watch.target_kind == "codex-queue-v1":
|
|
150
|
+
_deliver_codex_queue(watch, reply)
|
|
98
151
|
return
|
|
99
|
-
raise
|
|
100
|
-
f"unsupported issue-reply delivery adapter: {watch.target_kind}"
|
|
152
|
+
raise UnsupportedDeliveryTarget(
|
|
153
|
+
f"unsupported issue-reply delivery adapter: {watch.target_kind}; "
|
|
154
|
+
"re-arm the wakeup with github-wake"
|
|
101
155
|
)
|
|
102
156
|
|
|
103
157
|
|
|
104
|
-
def
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
158
|
+
def _deliver_codex_queue(watch: IssueWatch, reply: IssueReply) -> None:
|
|
159
|
+
"""Queue one reply on the native Codex session and require confirmation."""
|
|
160
|
+
target = watch.target
|
|
161
|
+
codex_home = target.get("codexHome")
|
|
162
|
+
executable = target.get("executable")
|
|
163
|
+
thread_id = target.get("threadId")
|
|
164
|
+
if not codex_home or not executable or not thread_id:
|
|
165
|
+
raise DeliveryConfigurationError("Codex queue target is incomplete")
|
|
166
|
+
completed = subprocess.run(
|
|
167
|
+
[
|
|
168
|
+
executable,
|
|
169
|
+
"queue",
|
|
170
|
+
"--thread",
|
|
171
|
+
thread_id,
|
|
172
|
+
"--message",
|
|
173
|
+
_codex_notification_message(watch, reply),
|
|
174
|
+
],
|
|
175
|
+
stdin=subprocess.DEVNULL,
|
|
176
|
+
capture_output=True,
|
|
177
|
+
text=True,
|
|
178
|
+
timeout=60,
|
|
179
|
+
check=False,
|
|
180
|
+
env={**os.environ, "CODEX_HOME": codex_home},
|
|
181
|
+
)
|
|
182
|
+
if completed.returncode != 0:
|
|
183
|
+
diagnostic = " ".join(completed.stderr.split())[:1000]
|
|
184
|
+
raise RuntimeError(f"codex queue failed: {diagnostic}")
|
|
185
|
+
confirmation = re.compile(
|
|
186
|
+
r"Queued message [0-9a-f-]{36} for thread " + re.escape(thread_id) + r"\."
|
|
116
187
|
)
|
|
188
|
+
if not confirmation.search(completed.stdout):
|
|
189
|
+
raise RuntimeError("codex queue printed no acceptance confirmation")
|
|
117
190
|
|
|
118
191
|
|
|
119
192
|
def _deliver_claude_monitor(watch: IssueWatch, reply: IssueReply) -> None:
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_claude_to_codex.py
RENAMED
|
@@ -514,18 +514,6 @@ def _ensure_leading_user(
|
|
|
514
514
|
yield msg
|
|
515
515
|
|
|
516
516
|
|
|
517
|
-
def _new_assistant_message_id() -> str:
|
|
518
|
-
"""Generate an assistant message id in the modern Codex format.
|
|
519
|
-
|
|
520
|
-
Real rollout assistant items carry ids like ``msg_<50 hex chars>``
|
|
521
|
-
(25 random bytes, hex-encoded, behind a ``msg_`` prefix).
|
|
522
|
-
|
|
523
|
-
Returns:
|
|
524
|
-
A fresh id string matching ``msg_[0-9a-f]{50}``.
|
|
525
|
-
"""
|
|
526
|
-
return "msg_" + secrets.token_hex(25)
|
|
527
|
-
|
|
528
|
-
|
|
529
517
|
def _make_codex_item(
|
|
530
518
|
msg: dict[str, Any], fallback_timestamp: str, turn_id: str
|
|
531
519
|
) -> dict[str, Any]:
|
|
@@ -534,8 +522,11 @@ def _make_codex_item(
|
|
|
534
522
|
Shapes match real modern rollouts exactly: user messages carry
|
|
535
523
|
``input_text`` content blocks plus the turn-id metadata
|
|
536
524
|
passthrough; assistant messages carry ``output_text`` content
|
|
537
|
-
plus the modern assistant payload fields (``
|
|
538
|
-
|
|
525
|
+
plus the modern assistant payload fields (``phase`` and the same
|
|
526
|
+
turn-id passthrough) that Codex itself writes. Assistant items
|
|
527
|
+
deliberately omit ``id``: Codex replays item ids to the Responses
|
|
528
|
+
API, which then looks them up as server-persisted items, and an
|
|
529
|
+
id that the server never issued fails the turn with HTTP 400.
|
|
539
530
|
|
|
540
531
|
Args:
|
|
541
532
|
msg: Flattened message dict (role/text/timestamp).
|
|
@@ -558,7 +549,6 @@ def _make_codex_item(
|
|
|
558
549
|
else:
|
|
559
550
|
payload = {
|
|
560
551
|
"type": "message",
|
|
561
|
-
"id": _new_assistant_message_id(),
|
|
562
552
|
"role": role,
|
|
563
553
|
"content": [
|
|
564
554
|
{"type": "output_text", "text": msg["text"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "claude-code-tools"
|
|
3
|
-
version = "1.29.
|
|
3
|
+
version = "1.29.6"
|
|
4
4
|
description = "Collection of tools for working with Claude Code"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -113,7 +113,7 @@ markers = [
|
|
|
113
113
|
|
|
114
114
|
[tool.commitizen]
|
|
115
115
|
name = "cz_conventional_commits"
|
|
116
|
-
version = "1.29.
|
|
116
|
+
version = "1.29.6"
|
|
117
117
|
tag_format = "v$version"
|
|
118
118
|
version_files = [
|
|
119
119
|
"pyproject.toml:version",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/__init__.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/backends.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/config.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/convert.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/discord_bot.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/locking.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/paths.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/readonly.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/registry.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/relay.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/serve.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/store.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/tmux.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/trust.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/agent_tunnel/turn_log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_app_server_rpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_callback.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_fingerprint.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_generation.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_legacy.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_models.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_process.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_reservation.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_resume.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_retry.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_reuse.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_state.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_supervisor.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_wait.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/codex_server_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/export_claude_session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/export_codex_session.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_claude_session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_codex_session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_original_session.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/find_trimmed_sessions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/legacy_session_wrappers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/msg/prompt_detect.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_claude_noise.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_codex_flatten.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/port_codex_to_claude.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/resolve_session_names.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/resolve_session_render.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_cli_resolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_resolution.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/session_selection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/tmux_cli_controller.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/tmux_execution_helpers.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/tmux_remote_controller.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/trim_session_claude.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/trim_session_codex.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_contract.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_formatting.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_manifest.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_projection.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_rendering.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_cli_snapshots.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_processes.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_store_io.py
RENAMED
|
File without changes
|
{claude_code_tools-1.29.4 → claude_code_tools-1.29.6}/claude_code_tools/workflow_validation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|