claude-code-tools 1.22.0__tar.gz → 1.23.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.22.0 → claude_code_tools-1.23.0}/PKG-INFO +1 -1
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/__init__.py +1 -1
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/aichat.py +35 -35
- claude_code_tools-1.23.0/claude_code_tools/port_render.py +363 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_service.py +46 -4
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/session_resolution.py +34 -9
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/pyproject.toml +2 -2
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/.gitignore +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/LICENSE +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/README.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/action_rpc.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/__init__.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/backends.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/cli.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/config.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/convert.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/discord_bot.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/locking.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/paths.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/registry.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/store.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/tmux.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/trust.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/claude_continue.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_continue.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_cli.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_fingerprint.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_generation.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_legacy.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_models.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_process.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_reservation.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_retry.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_reuse.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_state.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_supervisor.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_worker.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/config.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/csv2gsheet.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/delete_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/dotenv_vault.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/env_safe.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/export_all.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/export_claude_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/export_codex_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/export_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_claude_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_codex_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_original_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_trimmed_sessions.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/fix_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/gdoc2docx.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/gdoc2md.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/gsheet2csv.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/md2gdoc.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/__init__.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/cli.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/hooks.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/models.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/prompt_detect.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/store.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/watcher.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/node_menu_ui.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_claude_noise.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_claude_to_codex.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_codex_flatten.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_codex_to_claude.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/resolve_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/resolve_session_names.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/resolve_session_render.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/search_index.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/session_lineage.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/session_menu.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/session_menu_cli.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/session_utils.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/smart_trim.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/smart_trim_core.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/tmux_cli_controller.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/tmux_execution_helpers.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/tmux_remote_controller.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/trim_in_place.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/trim_session.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/trim_session_claude.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/trim_session_codex.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_contract.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_formatting.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_identity_policy.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_manifest.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_projection.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_rendering.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_snapshots.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_store_backends.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_processes.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_runs.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_store_io.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_validation.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/agent-tunnel-spec.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/claude-code-tmux-tutorials.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/dot-zshrc.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/find-claude-session.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/intercom-spec.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/lmsh.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/local-llm-setup.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/msg-phase-a-plan.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/msg-plan-v2.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/tmux-cli-instructions.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/docs/vault-documentation.md +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/hatch_build.py +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/node_ui/action_config.js +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/node_ui/menu.js +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/node_ui/package-lock.json +0 -0
- {claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/node_ui/package.json +0 -0
|
@@ -1768,7 +1768,9 @@ def port_session(
|
|
|
1768
1768
|
"""
|
|
1769
1769
|
import sys
|
|
1770
1770
|
|
|
1771
|
+
from claude_code_tools.port_render import PortDisplay
|
|
1771
1772
|
from claude_code_tools.port_service import (
|
|
1773
|
+
PortAmbiguityError,
|
|
1772
1774
|
PortSessionError,
|
|
1773
1775
|
port_claude_session,
|
|
1774
1776
|
port_codex_session,
|
|
@@ -1782,56 +1784,54 @@ def port_session(
|
|
|
1782
1784
|
claude_home = claude_home or root_obj.get("claude_home")
|
|
1783
1785
|
codex_home = codex_home or root_obj.get("codex_home")
|
|
1784
1786
|
|
|
1787
|
+
display = PortDisplay(session)
|
|
1788
|
+
display.start()
|
|
1785
1789
|
try:
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1790
|
+
with display.resolving():
|
|
1791
|
+
resolved = resolve_port_session(
|
|
1792
|
+
session,
|
|
1793
|
+
claude_home=claude_home,
|
|
1794
|
+
codex_home=codex_home,
|
|
1795
|
+
)
|
|
1796
|
+
except PortAmbiguityError as error:
|
|
1797
|
+
try:
|
|
1798
|
+
resolved = display.choose_candidate(error)
|
|
1799
|
+
except (EOFError, KeyboardInterrupt):
|
|
1800
|
+
display.error(str(error))
|
|
1801
|
+
sys.exit(1)
|
|
1802
|
+
if resolved is None:
|
|
1803
|
+
display.cancelled()
|
|
1804
|
+
sys.exit(1)
|
|
1789
1805
|
except PortSessionError as e:
|
|
1790
|
-
|
|
1806
|
+
display.error(str(e))
|
|
1791
1807
|
sys.exit(1)
|
|
1792
1808
|
|
|
1809
|
+
display.resolved(resolved)
|
|
1793
1810
|
if resolved.agent == "claude":
|
|
1794
|
-
print("Detected source agent: claude — porting to Codex")
|
|
1795
1811
|
try:
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1812
|
+
with display.porting(resolved.agent):
|
|
1813
|
+
result = port_claude_session(
|
|
1814
|
+
resolved.session_file,
|
|
1815
|
+
codex_home=codex_home,
|
|
1816
|
+
)
|
|
1799
1817
|
except PortSessionError as e:
|
|
1800
|
-
|
|
1818
|
+
display.error(str(e))
|
|
1801
1819
|
sys.exit(1)
|
|
1802
|
-
|
|
1803
|
-
print()
|
|
1804
|
-
print(f"New Codex session id: {result.new_session_id}")
|
|
1805
|
-
print(f"Output file: {result.output_file}")
|
|
1806
|
-
print(f"Session cwd: {result.cwd}")
|
|
1807
|
-
print()
|
|
1808
|
-
print("To resume:")
|
|
1809
|
-
print(f" {result.resume_hint}")
|
|
1810
|
-
print()
|
|
1811
|
-
print(
|
|
1812
|
-
"Tip: codex's /import can also import Claude sessions "
|
|
1813
|
-
"natively (interactive alternative)."
|
|
1814
|
-
)
|
|
1820
|
+
display.complete(result, resolved.agent)
|
|
1815
1821
|
return
|
|
1816
1822
|
|
|
1817
|
-
print("Detected source agent: codex — porting to Claude Code")
|
|
1818
1823
|
try:
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1824
|
+
with display.porting(resolved.agent):
|
|
1825
|
+
result = port_codex_session(
|
|
1826
|
+
resolved.session_file,
|
|
1827
|
+
claude_home=claude_home,
|
|
1828
|
+
)
|
|
1822
1829
|
except PortSessionError as e:
|
|
1823
1830
|
# Expected failure modes (missing/empty session, filesystem
|
|
1824
1831
|
# or encoding errors) become a clean error, not a traceback.
|
|
1825
|
-
|
|
1832
|
+
display.error(str(e))
|
|
1826
1833
|
sys.exit(1)
|
|
1827
|
-
|
|
1828
|
-
print()
|
|
1829
|
-
print(f"New Claude session id: {result.new_session_id}")
|
|
1830
|
-
print(f"Output file: {result.output_file}")
|
|
1831
|
-
print(f"Session cwd: {result.cwd}")
|
|
1832
|
-
print()
|
|
1833
|
-
print("To resume:")
|
|
1834
|
-
print(f" {result.resume_hint}")
|
|
1834
|
+
display.complete(result, resolved.agent)
|
|
1835
1835
|
|
|
1836
1836
|
|
|
1837
1837
|
@main.command("query")
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""Rich terminal rendering for the ``aichat port`` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from rich import box
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
from rich.prompt import Confirm, Prompt
|
|
16
|
+
from rich.status import Status
|
|
17
|
+
from rich.table import Table
|
|
18
|
+
from rich.text import Text
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from claude_code_tools.port_service import (
|
|
22
|
+
PortAmbiguityError,
|
|
23
|
+
PortResult,
|
|
24
|
+
ResolvedSession,
|
|
25
|
+
)
|
|
26
|
+
from claude_code_tools.resolve_session import SessionRecord
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_AGENT_LABELS = {"claude": "Claude", "codex": "Codex"}
|
|
30
|
+
_MAX_CANDIDATE_TITLE_CHARS = 120
|
|
31
|
+
_MACOS_CLIPBOARD_COMMANDS = (("pbcopy",),)
|
|
32
|
+
_LINUX_CLIPBOARD_COMMANDS = (
|
|
33
|
+
("wl-copy",),
|
|
34
|
+
("xclip", "-selection", "clipboard"),
|
|
35
|
+
("xsel", "--clipboard", "--input"),
|
|
36
|
+
)
|
|
37
|
+
_POWERSHELL_CLIPBOARD_ARGS = (
|
|
38
|
+
"-NoProfile",
|
|
39
|
+
"-NonInteractive",
|
|
40
|
+
"-Command",
|
|
41
|
+
"Set-Clipboard -Value ([Console]::In.ReadToEnd())",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _clipboard_commands(
|
|
46
|
+
platform_name: str,
|
|
47
|
+
) -> tuple[tuple[str, ...], ...]:
|
|
48
|
+
"""Return platform-specific clipboard commands in preference order."""
|
|
49
|
+
if platform_name == "darwin":
|
|
50
|
+
return _MACOS_CLIPBOARD_COMMANDS
|
|
51
|
+
if platform_name == "win32":
|
|
52
|
+
system_root = Path(os.environ.get("SystemRoot", r"C:\Windows"))
|
|
53
|
+
system32 = system_root / "System32"
|
|
54
|
+
powershell = (
|
|
55
|
+
system32
|
|
56
|
+
/ "WindowsPowerShell"
|
|
57
|
+
/ "v1.0"
|
|
58
|
+
/ "powershell.exe"
|
|
59
|
+
)
|
|
60
|
+
return (
|
|
61
|
+
(str(system32 / "clip.exe"),),
|
|
62
|
+
(
|
|
63
|
+
str(powershell),
|
|
64
|
+
*_POWERSHELL_CLIPBOARD_ARGS,
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
return _LINUX_CLIPBOARD_COMMANDS
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def copy_to_clipboard(
|
|
71
|
+
text: str,
|
|
72
|
+
*,
|
|
73
|
+
platform_name: str | None = None,
|
|
74
|
+
) -> bool:
|
|
75
|
+
"""Copy text with the first available working clipboard command.
|
|
76
|
+
|
|
77
|
+
Windows commands use explicit system paths so the current directory is
|
|
78
|
+
never searched for a clipboard executable.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
text: Text to place on the clipboard.
|
|
82
|
+
platform_name: Platform identifier. Defaults to ``sys.platform``.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
True when a clipboard command succeeds, otherwise False.
|
|
86
|
+
"""
|
|
87
|
+
commands = _clipboard_commands(platform_name or sys.platform)
|
|
88
|
+
for command in commands:
|
|
89
|
+
executable = shutil.which(command[0])
|
|
90
|
+
if executable is None:
|
|
91
|
+
continue
|
|
92
|
+
try:
|
|
93
|
+
completed = subprocess.run(
|
|
94
|
+
[executable, *command[1:]],
|
|
95
|
+
input=text,
|
|
96
|
+
text=True,
|
|
97
|
+
stdout=subprocess.DEVNULL,
|
|
98
|
+
stderr=subprocess.DEVNULL,
|
|
99
|
+
timeout=5,
|
|
100
|
+
check=False,
|
|
101
|
+
)
|
|
102
|
+
except (OSError, subprocess.SubprocessError):
|
|
103
|
+
continue
|
|
104
|
+
if completed.returncode == 0:
|
|
105
|
+
return True
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _agent_label(agent: str) -> str:
|
|
110
|
+
"""Return the human-facing label for an agent identifier."""
|
|
111
|
+
return _AGENT_LABELS.get(agent, agent.title())
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _target_agent(source_agent: str) -> str:
|
|
115
|
+
"""Return the destination agent identifier for a source agent."""
|
|
116
|
+
return "codex" if source_agent == "claude" else "claude"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _target_label(source_agent: str) -> str:
|
|
120
|
+
"""Return the product label for a destination agent."""
|
|
121
|
+
target = _target_agent(source_agent)
|
|
122
|
+
return "Claude Code" if target == "claude" else "Codex"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _match_reason(record: "SessionRecord", query: str) -> str:
|
|
126
|
+
"""Explain why one resolver candidate matched the query."""
|
|
127
|
+
matched_by = record.matched_by
|
|
128
|
+
if matched_by == "id":
|
|
129
|
+
return "exact session ID"
|
|
130
|
+
if matched_by == "partial-id":
|
|
131
|
+
return "session ID prefix"
|
|
132
|
+
if matched_by == "id-substring":
|
|
133
|
+
return "session ID contains query"
|
|
134
|
+
if matched_by == "filename":
|
|
135
|
+
return "rollout filename contains query"
|
|
136
|
+
if matched_by == "name":
|
|
137
|
+
name = record.name or ""
|
|
138
|
+
if name.casefold() == query.casefold():
|
|
139
|
+
return "exact name/title"
|
|
140
|
+
return "name/title contains query"
|
|
141
|
+
return "resolver match"
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _candidate_details(record: "SessionRecord") -> Text:
|
|
145
|
+
"""Build a readable multi-line summary for one candidate."""
|
|
146
|
+
details = Text()
|
|
147
|
+
title = " ".join((record.name or "Untitled session").split())
|
|
148
|
+
if len(title) > _MAX_CANDIDATE_TITLE_CHARS:
|
|
149
|
+
title = title[: _MAX_CANDIDATE_TITLE_CHARS - 3] + "..."
|
|
150
|
+
details.append(title, style="bold")
|
|
151
|
+
details.append("\nProject ", style="dim")
|
|
152
|
+
details.append(record.directory or "Unknown")
|
|
153
|
+
details.append("\nID ", style="dim")
|
|
154
|
+
details.append(record.session_id)
|
|
155
|
+
details.append("\nModified ", style="dim")
|
|
156
|
+
details.append(record.modified)
|
|
157
|
+
return details
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class PortDisplay:
|
|
161
|
+
"""Render progress, ambiguity choices, errors, and final results."""
|
|
162
|
+
|
|
163
|
+
def __init__(self, query: str) -> None:
|
|
164
|
+
"""Create a display for one port query."""
|
|
165
|
+
self.query = query
|
|
166
|
+
self.console = Console()
|
|
167
|
+
self.error_console = Console(stderr=True)
|
|
168
|
+
|
|
169
|
+
def start(self) -> None:
|
|
170
|
+
"""Print the command header and requested session query."""
|
|
171
|
+
content = Text()
|
|
172
|
+
content.append("Query ", style="dim")
|
|
173
|
+
content.append(self.query, style="bold")
|
|
174
|
+
self.console.print(
|
|
175
|
+
Panel.fit(
|
|
176
|
+
content,
|
|
177
|
+
title="[bold cyan]Session port[/bold cyan]",
|
|
178
|
+
border_style="cyan",
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def resolving(self) -> Status:
|
|
183
|
+
"""Start the visible resolution phase and return its spinner."""
|
|
184
|
+
self.console.print("[bold cyan]1/3[/] Resolving session")
|
|
185
|
+
return self.console.status(
|
|
186
|
+
"[dim]Searching Claude and Codex histories...[/dim]",
|
|
187
|
+
spinner="dots",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def choose_candidate(
|
|
191
|
+
self, error: "PortAmbiguityError"
|
|
192
|
+
) -> "ResolvedSession | None":
|
|
193
|
+
"""Present matching sessions and return the user's selection."""
|
|
194
|
+
from claude_code_tools.port_service import ResolvedSession
|
|
195
|
+
|
|
196
|
+
heading = Text()
|
|
197
|
+
heading.append(f"{error.match_count} sessions matched ", style="yellow")
|
|
198
|
+
heading.append(repr(error.query), style="bold yellow")
|
|
199
|
+
heading.append(". Choose the source session to port.", style="yellow")
|
|
200
|
+
self.console.print(heading)
|
|
201
|
+
|
|
202
|
+
table = Table(
|
|
203
|
+
box=box.ROUNDED,
|
|
204
|
+
header_style="bold cyan",
|
|
205
|
+
show_lines=True,
|
|
206
|
+
expand=True,
|
|
207
|
+
)
|
|
208
|
+
table.add_column("#", justify="right", no_wrap=True)
|
|
209
|
+
table.add_column("Agent", no_wrap=True)
|
|
210
|
+
table.add_column("Why it matched")
|
|
211
|
+
table.add_column("Session")
|
|
212
|
+
for index, record in enumerate(error.records, start=1):
|
|
213
|
+
table.add_row(
|
|
214
|
+
str(index),
|
|
215
|
+
_agent_label(record.agent),
|
|
216
|
+
_match_reason(record, error.query),
|
|
217
|
+
_candidate_details(record),
|
|
218
|
+
)
|
|
219
|
+
self.console.print(table)
|
|
220
|
+
|
|
221
|
+
if error.match_count > len(error.records):
|
|
222
|
+
hidden = error.match_count - len(error.records)
|
|
223
|
+
self.console.print(
|
|
224
|
+
f"[dim]{hidden} older matches are not shown. "
|
|
225
|
+
"Rerun with a full session ID to select one of them.[/dim]"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if not sys.stdin.isatty():
|
|
229
|
+
raise EOFError
|
|
230
|
+
|
|
231
|
+
choices = [str(index) for index in range(1, len(error.records) + 1)]
|
|
232
|
+
choices.append("q")
|
|
233
|
+
choice = Prompt.ask(
|
|
234
|
+
"[bold]Which source session do you want to port?[/bold] "
|
|
235
|
+
"[dim](number or q to cancel)[/dim]",
|
|
236
|
+
choices=choices,
|
|
237
|
+
show_choices=False,
|
|
238
|
+
console=self.console,
|
|
239
|
+
)
|
|
240
|
+
if choice == "q":
|
|
241
|
+
return None
|
|
242
|
+
record = error.records[int(choice) - 1]
|
|
243
|
+
return ResolvedSession(
|
|
244
|
+
agent=record.agent,
|
|
245
|
+
session_file=Path(record.session_file),
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
def resolved(self, session: "ResolvedSession") -> None:
|
|
249
|
+
"""Print the chosen source and detected direction."""
|
|
250
|
+
source = _agent_label(session.agent)
|
|
251
|
+
target = _target_label(session.agent)
|
|
252
|
+
self.console.print(
|
|
253
|
+
Text.assemble(
|
|
254
|
+
(" ✓ ", "bold green"),
|
|
255
|
+
(f"Detected source agent: {session.agent}", "bold"),
|
|
256
|
+
f" — porting to {target}",
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
self.console.print(
|
|
260
|
+
Text.assemble((" Source file ", "dim"), str(session.session_file))
|
|
261
|
+
)
|
|
262
|
+
self.console.print(f"[bold cyan]2/3[/] Porting {source} → {target}")
|
|
263
|
+
|
|
264
|
+
def porting(self, source_agent: str) -> Status:
|
|
265
|
+
"""Return the conversion-phase spinner."""
|
|
266
|
+
target = _target_label(source_agent)
|
|
267
|
+
return self.console.status(
|
|
268
|
+
f"[dim]Converting transcript and creating {target} session...[/dim]",
|
|
269
|
+
spinner="dots",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def complete(self, result: "PortResult", source_agent: str) -> None:
|
|
273
|
+
"""Render the final destination details and resume command."""
|
|
274
|
+
target_agent = _target_agent(source_agent)
|
|
275
|
+
target = _agent_label(target_agent)
|
|
276
|
+
self.console.print("[bold cyan]3/3[/] Ready")
|
|
277
|
+
|
|
278
|
+
table = Table.grid(padding=(0, 2))
|
|
279
|
+
table.add_column(style="bold cyan", no_wrap=True)
|
|
280
|
+
table.add_column()
|
|
281
|
+
table.add_row(
|
|
282
|
+
f"New {target} session id:",
|
|
283
|
+
Text(result.new_session_id),
|
|
284
|
+
)
|
|
285
|
+
table.add_row("Session cwd:", Text(result.cwd or "Unknown"))
|
|
286
|
+
table.add_row("Output file:", Text(str(result.output_file)))
|
|
287
|
+
self.console.print(
|
|
288
|
+
Panel(
|
|
289
|
+
table,
|
|
290
|
+
title="[bold green]Port complete[/bold green]",
|
|
291
|
+
border_style="green",
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
self.console.print("[bold]To resume:[/bold]")
|
|
295
|
+
command = Text(
|
|
296
|
+
f" {result.resume_hint}",
|
|
297
|
+
style="bold cyan",
|
|
298
|
+
no_wrap=True,
|
|
299
|
+
overflow="ignore",
|
|
300
|
+
)
|
|
301
|
+
self.console.print(command, soft_wrap=True)
|
|
302
|
+
if target_agent == "codex":
|
|
303
|
+
self.console.print()
|
|
304
|
+
self.console.print(
|
|
305
|
+
"[dim]Tip: Codex's /import is also available as an "
|
|
306
|
+
"interactive alternative.[/dim]"
|
|
307
|
+
)
|
|
308
|
+
self.offer_session_id_copy(result.new_session_id, target_agent)
|
|
309
|
+
|
|
310
|
+
def offer_session_id_copy(
|
|
311
|
+
self,
|
|
312
|
+
session_id: str,
|
|
313
|
+
target_agent: str,
|
|
314
|
+
) -> None:
|
|
315
|
+
"""Offer to copy a newly created session ID in interactive terminals."""
|
|
316
|
+
if not self.console.is_terminal or not sys.stdin.isatty():
|
|
317
|
+
return
|
|
318
|
+
self.console.print()
|
|
319
|
+
try:
|
|
320
|
+
should_copy = Confirm.ask(
|
|
321
|
+
"[bold]Copy the new session ID to your clipboard?[/bold] "
|
|
322
|
+
"[dim](Y/n)[/dim]",
|
|
323
|
+
default=True,
|
|
324
|
+
show_choices=False,
|
|
325
|
+
show_default=False,
|
|
326
|
+
console=self.console,
|
|
327
|
+
)
|
|
328
|
+
except (EOFError, KeyboardInterrupt):
|
|
329
|
+
should_copy = False
|
|
330
|
+
if not should_copy:
|
|
331
|
+
self.console.print("[dim]Clipboard left unchanged.[/dim]")
|
|
332
|
+
return
|
|
333
|
+
if copy_to_clipboard(session_id):
|
|
334
|
+
self.console.print(
|
|
335
|
+
"[bold green]✓ Session ID copied to clipboard.[/bold green]"
|
|
336
|
+
)
|
|
337
|
+
command = (
|
|
338
|
+
"codex resume"
|
|
339
|
+
if target_agent == "codex"
|
|
340
|
+
else "claude --resume"
|
|
341
|
+
)
|
|
342
|
+
self.console.print("[bold]Now you can run:[/bold]")
|
|
343
|
+
self.console.print(
|
|
344
|
+
Text(
|
|
345
|
+
f" {command} <paste session ID>",
|
|
346
|
+
style="bold cyan",
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
return
|
|
350
|
+
self.console.print(
|
|
351
|
+
"[yellow]Could not find a working system clipboard command. "
|
|
352
|
+
"The session ID is still shown above.[/yellow]"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
def error(self, message: str) -> None:
|
|
356
|
+
"""Render an expected failure without a traceback."""
|
|
357
|
+
self.error_console.print(
|
|
358
|
+
Text.assemble(("Error: ", "bold red"), message)
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
def cancelled(self) -> None:
|
|
362
|
+
"""Render a user-requested cancellation."""
|
|
363
|
+
self.console.print("[yellow]Port cancelled.[/yellow]")
|
|
@@ -25,11 +25,12 @@ from pathlib import Path
|
|
|
25
25
|
from typing import TYPE_CHECKING, Optional, Union
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
|
-
from claude_code_tools.resolve_session import ResolveResult
|
|
28
|
+
from claude_code_tools.resolve_session import ResolveResult, SessionRecord
|
|
29
29
|
|
|
30
30
|
from claude_code_tools.session_resolution import (
|
|
31
31
|
_detect_direct_path_agent,
|
|
32
32
|
_resolve_query_for_agent,
|
|
33
|
+
ambiguity_candidates,
|
|
33
34
|
build_ambiguity_message,
|
|
34
35
|
)
|
|
35
36
|
|
|
@@ -42,6 +43,39 @@ class PortSessionError(Exception):
|
|
|
42
43
|
"""
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
class PortAmbiguityError(PortSessionError):
|
|
47
|
+
"""A port query with more than one valid source session.
|
|
48
|
+
|
|
49
|
+
Carries the candidates alongside the printable message so the CLI
|
|
50
|
+
can offer them interactively instead of only reporting them.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
query: Original user query.
|
|
54
|
+
records: Newest matching records available for selection.
|
|
55
|
+
match_count: Total matches, including any omitted by the cap.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
query: str,
|
|
61
|
+
records: "tuple[SessionRecord, ...]",
|
|
62
|
+
match_count: int,
|
|
63
|
+
message: str,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Initialize an ambiguity with structured candidate data.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
query: Original user query.
|
|
69
|
+
records: Newest matching records available for selection.
|
|
70
|
+
match_count: Total matches before the display cap.
|
|
71
|
+
message: User-facing message listing the candidates.
|
|
72
|
+
"""
|
|
73
|
+
super().__init__(message)
|
|
74
|
+
self.query = query
|
|
75
|
+
self.records = records
|
|
76
|
+
self.match_count = match_count
|
|
77
|
+
|
|
78
|
+
|
|
45
79
|
@dataclass
|
|
46
80
|
class ResolvedSession:
|
|
47
81
|
"""A session file together with its detected source agent.
|
|
@@ -99,8 +133,10 @@ def resolve_port_session(
|
|
|
99
133
|
The resolved session file with its detected agent.
|
|
100
134
|
|
|
101
135
|
Raises:
|
|
102
|
-
|
|
103
|
-
|
|
136
|
+
PortAmbiguityError: When the query matches more than one
|
|
137
|
+
session; carries the candidates for interactive choice.
|
|
138
|
+
PortSessionError: When the session cannot be found or its
|
|
139
|
+
agent cannot be detected.
|
|
104
140
|
"""
|
|
105
141
|
# Arbitrary session names are legitimate queries here, and some
|
|
106
142
|
# (e.g. "~nonexistent-user" or names with NUL bytes) make path
|
|
@@ -137,7 +173,13 @@ def resolve_port_session(
|
|
|
137
173
|
return ResolvedSession(
|
|
138
174
|
agent=record.agent, session_file=Path(record.session_file)
|
|
139
175
|
)
|
|
140
|
-
|
|
176
|
+
records, match_count = ambiguity_candidates(results)
|
|
177
|
+
raise PortAmbiguityError(
|
|
178
|
+
session,
|
|
179
|
+
records,
|
|
180
|
+
match_count,
|
|
181
|
+
build_ambiguity_message(session, results),
|
|
182
|
+
)
|
|
141
183
|
|
|
142
184
|
|
|
143
185
|
def _read_output_cwd(output_file: Path) -> str:
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/session_resolution.py
RENAMED
|
@@ -22,7 +22,11 @@ from pathlib import Path
|
|
|
22
22
|
from typing import TYPE_CHECKING, Optional, cast
|
|
23
23
|
|
|
24
24
|
if TYPE_CHECKING:
|
|
25
|
-
from claude_code_tools.resolve_session import
|
|
25
|
+
from claude_code_tools.resolve_session import (
|
|
26
|
+
ResolveResult,
|
|
27
|
+
ResolverError,
|
|
28
|
+
SessionRecord,
|
|
29
|
+
)
|
|
26
30
|
|
|
27
31
|
from claude_code_tools.session_utils import (
|
|
28
32
|
_iter_named_session_files,
|
|
@@ -164,17 +168,22 @@ def _resolve_query_for_agent(
|
|
|
164
168
|
return None
|
|
165
169
|
|
|
166
170
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
) ->
|
|
170
|
-
"""
|
|
171
|
+
def ambiguity_candidates(
|
|
172
|
+
results: "list[ResolveResult]",
|
|
173
|
+
) -> "tuple[tuple[SessionRecord, ...], int]":
|
|
174
|
+
"""Return the displayable candidates for an ambiguous query.
|
|
175
|
+
|
|
176
|
+
Single source of truth for how ambiguous matches are ordered and
|
|
177
|
+
capped, so a caller that RENDERS the candidates (an interactive
|
|
178
|
+
picker) and one that only formats the message agree on which
|
|
179
|
+
sessions they are talking about.
|
|
171
180
|
|
|
172
181
|
Args:
|
|
173
|
-
session: Original session query.
|
|
174
182
|
results: Non-empty resolver results from one or both agents.
|
|
175
183
|
|
|
176
184
|
Returns:
|
|
177
|
-
|
|
185
|
+
Newest-first candidates capped at ``_MAX_AMBIGUITY_LINES``,
|
|
186
|
+
paired with the total match count before capping.
|
|
178
187
|
"""
|
|
179
188
|
records = [record for result in results for record in result.records]
|
|
180
189
|
records.sort(key=lambda record: record._modified_timestamp, reverse=True)
|
|
@@ -182,8 +191,24 @@ def build_ambiguity_message(
|
|
|
182
191
|
1 if result.kind == "single" else result.match_count
|
|
183
192
|
for result in results
|
|
184
193
|
)
|
|
194
|
+
return tuple(records[:_MAX_AMBIGUITY_LINES]), total
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def build_ambiguity_message(
|
|
198
|
+
session: str, results: "list[ResolveResult]"
|
|
199
|
+
) -> str:
|
|
200
|
+
"""Build the shared multi-candidate ambiguity message.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
session: Original session query.
|
|
204
|
+
results: Non-empty resolver results from one or both agents.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
A user-facing message listing the matching candidates.
|
|
208
|
+
"""
|
|
209
|
+
records, total = ambiguity_candidates(results)
|
|
185
210
|
lines = []
|
|
186
|
-
for record in records
|
|
211
|
+
for record in records:
|
|
187
212
|
# Codex auto-titles can contain entire prompts. Normalize whitespace
|
|
188
213
|
# and truncate them to keep every candidate on a readable line.
|
|
189
214
|
name = " ".join((record.name or "").split())
|
|
@@ -194,7 +219,7 @@ def build_ambiguity_message(
|
|
|
194
219
|
f" [{record.agent}] {record.session_id}{name_part}"
|
|
195
220
|
f" modified {record.modified}"
|
|
196
221
|
)
|
|
197
|
-
remaining = total - len(records
|
|
222
|
+
remaining = total - len(records)
|
|
198
223
|
if remaining > 0:
|
|
199
224
|
lines.append(f" ... and {remaining} more")
|
|
200
225
|
listing = "\n".join(lines)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "claude-code-tools"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.23.0"
|
|
4
4
|
description = "Collection of tools for working with Claude Code"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -111,7 +111,7 @@ markers = [
|
|
|
111
111
|
|
|
112
112
|
[tool.commitizen]
|
|
113
113
|
name = "cz_conventional_commits"
|
|
114
|
-
version = "1.
|
|
114
|
+
version = "1.23.0"
|
|
115
115
|
tag_format = "v$version"
|
|
116
116
|
version_files = [
|
|
117
117
|
"pyproject.toml:version",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/__init__.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/backends.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/config.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/convert.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/discord_bot.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/locking.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/paths.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/registry.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/store.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/tmux.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/agent_tunnel/trust.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_fingerprint.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_generation.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_legacy.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_models.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_process.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_reservation.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_retry.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_reuse.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_state.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/codex_server_supervisor.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/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.22.0 → claude_code_tools-1.23.0}/claude_code_tools/export_claude_session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/export_codex_session.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_claude_session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_codex_session.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/find_original_session.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/msg/prompt_detect.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_claude_noise.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_claude_to_codex.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_codex_flatten.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/port_codex_to_claude.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/resolve_session_names.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/resolve_session_render.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.22.0 → claude_code_tools-1.23.0}/claude_code_tools/tmux_cli_controller.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/tmux_execution_helpers.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/tmux_remote_controller.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/trim_session_claude.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/trim_session_codex.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_contract.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_formatting.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_manifest.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_projection.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_rendering.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_cli_snapshots.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_processes.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/claude_code_tools/workflow_store_io.py
RENAMED
|
File without changes
|
{claude_code_tools-1.22.0 → claude_code_tools-1.23.0}/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
|
|
File without changes
|
|
File without changes
|