claude-team-mcp 0.9.1__py3-none-any.whl → 0.9.2__py3-none-any.whl
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_team_mcp/registry.py +22 -14
- claude_team_mcp/session_state.py +36 -0
- claude_team_mcp/tools/adopt_worker.py +2 -1
- claude_team_mcp/tools/discover_workers.py +1 -1
- claude_team_mcp/tools/spawn_workers.py +25 -3
- {claude_team_mcp-0.9.1.dist-info → claude_team_mcp-0.9.2.dist-info}/METADATA +1 -1
- {claude_team_mcp-0.9.1.dist-info → claude_team_mcp-0.9.2.dist-info}/RECORD +9 -9
- {claude_team_mcp-0.9.1.dist-info → claude_team_mcp-0.9.2.dist-info}/WHEEL +0 -0
- {claude_team_mcp-0.9.1.dist-info → claude_team_mcp-0.9.2.dist-info}/entry_points.txt +0 -0
claude_team_mcp/registry.py
CHANGED
|
@@ -139,6 +139,7 @@ class RecoveredSession:
|
|
|
139
139
|
coordinator_annotation: Optional[str] = None
|
|
140
140
|
worktree_path: Optional[str] = None
|
|
141
141
|
main_repo_path: Optional[str] = None
|
|
142
|
+
codex_jsonl_path: Optional[str] = None
|
|
142
143
|
|
|
143
144
|
@staticmethod
|
|
144
145
|
def map_event_state_to_status(event_state: EventState) -> SessionStatus:
|
|
@@ -178,6 +179,7 @@ class RecoveredSession:
|
|
|
178
179
|
"worktree_path": self.worktree_path,
|
|
179
180
|
"main_repo_path": self.main_repo_path,
|
|
180
181
|
"agent_type": self.agent_type,
|
|
182
|
+
"codex_jsonl_path": self.codex_jsonl_path,
|
|
181
183
|
# Recovery-specific fields
|
|
182
184
|
"source": "event_log",
|
|
183
185
|
"event_state": self.event_state,
|
|
@@ -227,6 +229,9 @@ class ManagedSession:
|
|
|
227
229
|
# Agent type: "claude" (default) or "codex"
|
|
228
230
|
agent_type: AgentType = "claude"
|
|
229
231
|
|
|
232
|
+
# Cached Codex JSONL path (discovered at spawn time via marker polling)
|
|
233
|
+
codex_jsonl_path: Optional[Path] = None
|
|
234
|
+
|
|
230
235
|
def __post_init__(self):
|
|
231
236
|
"""Auto-populate terminal_id from terminal_session if not set."""
|
|
232
237
|
if self.terminal_id is None:
|
|
@@ -255,6 +260,7 @@ class ManagedSession:
|
|
|
255
260
|
"worktree_path": str(self.worktree_path) if self.worktree_path else None,
|
|
256
261
|
"main_repo_path": str(self.main_repo_path) if self.main_repo_path else None,
|
|
257
262
|
"agent_type": self.agent_type,
|
|
263
|
+
"codex_jsonl_path": str(self.codex_jsonl_path) if self.codex_jsonl_path else None,
|
|
258
264
|
# Source field for distinguishing live vs recovered sessions
|
|
259
265
|
"source": "registry",
|
|
260
266
|
}
|
|
@@ -292,22 +298,29 @@ class ManagedSession:
|
|
|
292
298
|
Get the path to this session's JSONL file.
|
|
293
299
|
|
|
294
300
|
For Claude workers: uses marker-based discovery in ~/.claude/projects/.
|
|
295
|
-
For Codex workers: uses marker-based discovery in
|
|
301
|
+
For Codex workers: uses cached path or marker-based discovery in
|
|
302
|
+
~/.codex/sessions/. Returns None (not a wrong file) when discovery fails.
|
|
296
303
|
|
|
297
304
|
Returns:
|
|
298
305
|
Path object, or None if session cannot be discovered
|
|
299
306
|
"""
|
|
300
307
|
if self.agent_type == "codex":
|
|
301
|
-
|
|
308
|
+
# Use cached path if available (set at spawn time)
|
|
309
|
+
if self.codex_jsonl_path and self.codex_jsonl_path.exists():
|
|
310
|
+
return self.codex_jsonl_path
|
|
302
311
|
|
|
303
|
-
#
|
|
312
|
+
# Try marker-based discovery with generous timeout (workers can run for hours)
|
|
304
313
|
match = find_codex_session_by_internal_id(
|
|
305
314
|
self.session_id,
|
|
306
|
-
max_age_seconds=
|
|
315
|
+
max_age_seconds=86400,
|
|
307
316
|
)
|
|
308
317
|
if match:
|
|
318
|
+
# Cache for future calls
|
|
319
|
+
self.codex_jsonl_path = match.jsonl_path
|
|
309
320
|
return match.jsonl_path
|
|
310
|
-
|
|
321
|
+
|
|
322
|
+
# No blind fallback - returning None is better than returning wrong data
|
|
323
|
+
return None
|
|
311
324
|
else:
|
|
312
325
|
# For Claude, use marker-based discovery
|
|
313
326
|
# Auto-discover if not already known
|
|
@@ -353,16 +366,10 @@ class ManagedSession:
|
|
|
353
366
|
True if idle, False if working or session file not available
|
|
354
367
|
"""
|
|
355
368
|
if self.agent_type == "codex":
|
|
356
|
-
from .idle_detection import
|
|
369
|
+
from .idle_detection import is_codex_idle
|
|
357
370
|
|
|
358
|
-
#
|
|
359
|
-
|
|
360
|
-
self.session_id,
|
|
361
|
-
max_age_seconds=600,
|
|
362
|
-
)
|
|
363
|
-
session_file = match.jsonl_path if match else None
|
|
364
|
-
if not session_file:
|
|
365
|
-
session_file = find_codex_session_file(max_age_seconds=600)
|
|
371
|
+
# Use the same path resolution as get_jsonl_path() (cached or marker-based)
|
|
372
|
+
session_file = self.get_jsonl_path()
|
|
366
373
|
if not session_file:
|
|
367
374
|
return False
|
|
368
375
|
return is_codex_idle(session_file)
|
|
@@ -795,6 +802,7 @@ class SessionRegistry:
|
|
|
795
802
|
coordinator_annotation=data.get("coordinator_annotation"),
|
|
796
803
|
worktree_path=data.get("worktree_path"),
|
|
797
804
|
main_repo_path=data.get("main_repo_path"),
|
|
805
|
+
codex_jsonl_path=data.get("codex_jsonl_path"),
|
|
798
806
|
)
|
|
799
807
|
|
|
800
808
|
def count(self) -> int:
|
claude_team_mcp/session_state.py
CHANGED
|
@@ -838,6 +838,42 @@ async def await_marker_in_jsonl(
|
|
|
838
838
|
return None
|
|
839
839
|
|
|
840
840
|
|
|
841
|
+
async def await_codex_marker_in_jsonl(
|
|
842
|
+
session_id: str,
|
|
843
|
+
timeout: float = 30.0,
|
|
844
|
+
poll_interval: float = 0.5,
|
|
845
|
+
) -> Optional[CodexSessionMatch]:
|
|
846
|
+
"""
|
|
847
|
+
Poll for a Codex session marker to appear in the JSONL.
|
|
848
|
+
|
|
849
|
+
Codex workers write markers into ~/.codex/sessions/ files. This function
|
|
850
|
+
polls until the marker for the given session_id is found.
|
|
851
|
+
|
|
852
|
+
Args:
|
|
853
|
+
session_id: The internal session ID to search for in markers
|
|
854
|
+
timeout: Maximum seconds to wait (default 30)
|
|
855
|
+
poll_interval: Seconds between polls (default 0.5, slower than Claude
|
|
856
|
+
because Codex takes longer to start)
|
|
857
|
+
|
|
858
|
+
Returns:
|
|
859
|
+
CodexSessionMatch if found, None on timeout
|
|
860
|
+
"""
|
|
861
|
+
import asyncio
|
|
862
|
+
|
|
863
|
+
start = time.time()
|
|
864
|
+
|
|
865
|
+
while time.time() - start < timeout:
|
|
866
|
+
match = find_codex_session_by_internal_id(
|
|
867
|
+
session_id,
|
|
868
|
+
max_age_seconds=300,
|
|
869
|
+
)
|
|
870
|
+
if match:
|
|
871
|
+
return match
|
|
872
|
+
await asyncio.sleep(poll_interval)
|
|
873
|
+
|
|
874
|
+
return None
|
|
875
|
+
|
|
876
|
+
|
|
841
877
|
# =============================================================================
|
|
842
878
|
# Session Discovery
|
|
843
879
|
# =============================================================================
|
|
@@ -78,7 +78,8 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
78
78
|
# Check if already managed
|
|
79
79
|
for managed in registry.list_all():
|
|
80
80
|
if (
|
|
81
|
-
managed
|
|
81
|
+
hasattr(managed, 'terminal_session')
|
|
82
|
+
and managed.terminal_session.backend_id == backend_id
|
|
82
83
|
and managed.terminal_session.native_id == target_id
|
|
83
84
|
):
|
|
84
85
|
return error_response(
|
|
@@ -90,7 +90,7 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
90
90
|
managed_ids = {
|
|
91
91
|
s.terminal_session.native_id
|
|
92
92
|
for s in registry.list_all()
|
|
93
|
-
if s.terminal_session.backend_id == backend_id
|
|
93
|
+
if hasattr(s, 'terminal_session') and s.terminal_session.backend_id == backend_id
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
try:
|
|
@@ -187,7 +187,11 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
187
187
|
# Then immediately:
|
|
188
188
|
message_workers(session_ids=["Groucho"], message="Your task is...")
|
|
189
189
|
"""
|
|
190
|
-
from ..session_state import
|
|
190
|
+
from ..session_state import (
|
|
191
|
+
await_codex_marker_in_jsonl,
|
|
192
|
+
await_marker_in_jsonl,
|
|
193
|
+
generate_marker_message,
|
|
194
|
+
)
|
|
191
195
|
|
|
192
196
|
app_ctx = ctx.request_context.lifespan_context
|
|
193
197
|
registry = app_ctx.registry
|
|
@@ -452,7 +456,7 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
452
456
|
managed_session_ids = {
|
|
453
457
|
s.terminal_session.native_id
|
|
454
458
|
for s in registry.list_all()
|
|
455
|
-
if s.terminal_session.backend_id == backend.backend_id
|
|
459
|
+
if hasattr(s, 'terminal_session') and s.terminal_session.backend_id == backend.backend_id
|
|
456
460
|
}
|
|
457
461
|
|
|
458
462
|
# Find a window with enough space for ALL workers
|
|
@@ -693,7 +697,7 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
693
697
|
submit=True,
|
|
694
698
|
)
|
|
695
699
|
|
|
696
|
-
# Wait for markers to appear in JSONL (Claude
|
|
700
|
+
# Wait for markers to appear in JSONL (Claude and Codex)
|
|
697
701
|
for i, managed in enumerate(managed_sessions):
|
|
698
702
|
if managed.agent_type == "claude":
|
|
699
703
|
claude_session_id = await await_marker_in_jsonl(
|
|
@@ -709,6 +713,24 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
709
713
|
f"Marker polling timed out for {managed.session_id}, "
|
|
710
714
|
"JSONL correlation unavailable"
|
|
711
715
|
)
|
|
716
|
+
elif managed.agent_type == "codex":
|
|
717
|
+
# Poll for Codex marker and cache the JSONL path
|
|
718
|
+
codex_match = await await_codex_marker_in_jsonl(
|
|
719
|
+
managed.session_id,
|
|
720
|
+
timeout=30.0,
|
|
721
|
+
poll_interval=0.5,
|
|
722
|
+
)
|
|
723
|
+
if codex_match:
|
|
724
|
+
managed.codex_jsonl_path = codex_match.jsonl_path
|
|
725
|
+
logger.info(
|
|
726
|
+
f"Codex JSONL path cached for {managed.session_id}: "
|
|
727
|
+
f"{codex_match.jsonl_path}"
|
|
728
|
+
)
|
|
729
|
+
else:
|
|
730
|
+
logger.warning(
|
|
731
|
+
f"Codex marker polling timed out for {managed.session_id}, "
|
|
732
|
+
"JSONL correlation unavailable"
|
|
733
|
+
)
|
|
712
734
|
|
|
713
735
|
# Send worker prompts - always use generate_worker_prompt with bead/custom_prompt
|
|
714
736
|
workers_awaiting_task: list[str] = [] # Workers with no bead and no prompt
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-team-mcp
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: MCP server for managing multiple Claude Code sessions via iTerm2
|
|
5
5
|
Project-URL: Homepage, https://github.com/Martian-Engineering/claude-team
|
|
6
6
|
Project-URL: Repository, https://github.com/Martian-Engineering/claude-team
|
|
@@ -12,9 +12,9 @@ claude_team_mcp/idle_detection.py,sha256=KRnhObbVM--S8HPowWq7UkjXxq5AMpGdVHY5UUv
|
|
|
12
12
|
claude_team_mcp/iterm_utils.py,sha256=Imp4yUwq06avwdEw3bOSaO168DpCUveazc71Q8AZOFw,37621
|
|
13
13
|
claude_team_mcp/names.py,sha256=qUJlO2WlFjxB4Q1vLLTcEjTvKA63fJ3i2QBz80ABgnQ,15978
|
|
14
14
|
claude_team_mcp/profile.py,sha256=yqytAE0dIe6NQ6FodldSepO-ZIeR7-C0UfK3FsO7Pn4,12326
|
|
15
|
-
claude_team_mcp/registry.py,sha256=
|
|
15
|
+
claude_team_mcp/registry.py,sha256=iJ1UApZ3zWzHWvMayyaNLoXBRE2mO9hbuD0KQ2XfmpQ,29985
|
|
16
16
|
claude_team_mcp/server.py,sha256=HmpGfckw6rgXPNcJCJzLbiqsFM9yoDr05MgwonUuQNU,18969
|
|
17
|
-
claude_team_mcp/session_state.py,sha256=
|
|
17
|
+
claude_team_mcp/session_state.py,sha256=juDC1xpBlQpLCvZgOUTjmAb-d-RCSHRaTyn-NS-b4Hs,47819
|
|
18
18
|
claude_team_mcp/subprocess_cache.py,sha256=6Q1NIMn2X5_75S4s_a9iPBka-UCz6RW0EMxPSPEaLxo,3612
|
|
19
19
|
claude_team_mcp/worker_prompt.py,sha256=NvzUrR6Zqjp8HuL-xOeAncdvACHbyN7O0nISWyySHq4,15771
|
|
20
20
|
claude_team_mcp/worktree.py,sha256=q05yhIqlpsG9CW-QDRq5SyqC7T3iYYxjLDkUiGX2M-g,19102
|
|
@@ -30,11 +30,11 @@ claude_team_mcp/terminal_backends/base.py,sha256=9GrFDm-Jah8y6DALQrq8dei9UgWeZkW
|
|
|
30
30
|
claude_team_mcp/terminal_backends/iterm.py,sha256=A5klhOpzDrZ-636aN1Hlnnfxk4OA-Rz_OuaQi1ABz0Y,9317
|
|
31
31
|
claude_team_mcp/terminal_backends/tmux.py,sha256=blMb669GlGjxedOLLu35NmuZpsN74Z4bq1EmMGqO7G0,24512
|
|
32
32
|
claude_team_mcp/tools/__init__.py,sha256=NrtUzCC5Df-0GDqdQKlFqD4yv69pf5qbKIsypRxuxRg,1659
|
|
33
|
-
claude_team_mcp/tools/adopt_worker.py,sha256=
|
|
33
|
+
claude_team_mcp/tools/adopt_worker.py,sha256=UFgg9P_wytO98lYQVfGgPRCqaYaJJGUdt5-rYVXnwlk,7110
|
|
34
34
|
claude_team_mcp/tools/annotate_worker.py,sha256=mBo4YMaaaNQxQBduYK1DPA-Ioift-ZQ9tYlBM6S15t8,1639
|
|
35
35
|
claude_team_mcp/tools/check_idle_workers.py,sha256=IjDNDeal9M7lTONWjKZrt8bXu7O277ossHz_2oyLRcY,3374
|
|
36
36
|
claude_team_mcp/tools/close_workers.py,sha256=A--QqoL5KBUFbsNNe4-SYxsKOn1plY5_IfV6wHT8gvE,8029
|
|
37
|
-
claude_team_mcp/tools/discover_workers.py,sha256=
|
|
37
|
+
claude_team_mcp/tools/discover_workers.py,sha256=_QVZRGfVP0k1-ZUNix1UlsfHrdVBuyM2ohtw_h1zIv0,12021
|
|
38
38
|
claude_team_mcp/tools/examine_worker.py,sha256=7Nd3EOdsGVDjMJ8ov9NhmcrjN8K9IE4i_noXHOP1uI8,1620
|
|
39
39
|
claude_team_mcp/tools/issue_tracker_help.py,sha256=KxgjFhXp3NCUDjqTl3UnIiFV-Y5DM80C1EoZXfA82QM,1668
|
|
40
40
|
claude_team_mcp/tools/list_workers.py,sha256=bD-i1kI_qCgFF1TJc_U5TpEc_Gk6baqkFzAInKLVJgs,4906
|
|
@@ -42,14 +42,14 @@ claude_team_mcp/tools/list_worktrees.py,sha256=EXPxzvHRX7a1c9CedBjZVn-lckiljdTH7
|
|
|
42
42
|
claude_team_mcp/tools/message_workers.py,sha256=xa-y88gb2ugi36eRNE0ChAAZ1tJ62HuXwSlGhp7qDlE,13464
|
|
43
43
|
claude_team_mcp/tools/poll_worker_changes.py,sha256=uv2-jhhJCZ4sTkuKqSBLK894GHof3Ue9roplgPwaTu0,8329
|
|
44
44
|
claude_team_mcp/tools/read_worker_logs.py,sha256=Xe1Ch1ZJShI11et_beOwFL9q0WM5yMorhgDpstOd4Dw,6120
|
|
45
|
-
claude_team_mcp/tools/spawn_workers.py,sha256=
|
|
45
|
+
claude_team_mcp/tools/spawn_workers.py,sha256=PGY4-LzRnJfnWVQ4kyhNNie1HNPhbioe6dXiRDohEoM,38730
|
|
46
46
|
claude_team_mcp/tools/wait_idle_workers.py,sha256=VvHeIK2BM1q6SoY0DODCeKAEkAj3-MsB6g87scu2cyM,5555
|
|
47
47
|
claude_team_mcp/tools/worker_events.py,sha256=s1_pFMFrH-Kppni6RT9yzQbsJ_ChhmF8ujM2lkdEl1s,9448
|
|
48
48
|
claude_team_mcp/utils/__init__.py,sha256=Jyc-N1RXZnMdXLjSgRBB2ih04v-h0woUzZcVYYfc7wQ,647
|
|
49
49
|
claude_team_mcp/utils/constants.py,sha256=FGDHeo0reZ89365fuXJGIl2Y5MFQAoKfWtmYr7UQzhQ,5815
|
|
50
50
|
claude_team_mcp/utils/errors.py,sha256=kP0MPjLIpEOZkbGAzDxonMFERbsjwfID5VVY5qAR2II,2949
|
|
51
51
|
claude_team_mcp/utils/worktree_detection.py,sha256=oMGcb7p1jvr7qWs06sxUMTAV8jRialcVqziCTCdW7XU,3251
|
|
52
|
-
claude_team_mcp-0.9.
|
|
53
|
-
claude_team_mcp-0.9.
|
|
54
|
-
claude_team_mcp-0.9.
|
|
55
|
-
claude_team_mcp-0.9.
|
|
52
|
+
claude_team_mcp-0.9.2.dist-info/METADATA,sha256=7NIO_cETJlIIft77OreT48npeIFtGXnV9NXQ0o31l5U,21211
|
|
53
|
+
claude_team_mcp-0.9.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
54
|
+
claude_team_mcp-0.9.2.dist-info/entry_points.txt,sha256=cmk5dmK50RVquExT-k_J72akl3qKIerPpVqfit7kQtQ,53
|
|
55
|
+
claude_team_mcp-0.9.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|