claude-team-mcp 0.5.0__py3-none-any.whl → 0.6.0__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/cli_backends/codex.py +2 -3
- claude_team_mcp/issue_tracker/__init__.py +132 -0
- claude_team_mcp/tools/__init__.py +2 -2
- claude_team_mcp/tools/issue_tracker_help.py +50 -0
- claude_team_mcp/tools/message_workers.py +10 -3
- claude_team_mcp/tools/spawn_workers.py +137 -62
- claude_team_mcp/utils/__init__.py +13 -5
- claude_team_mcp/utils/constants.py +151 -29
- claude_team_mcp/utils/worktree_detection.py +37 -15
- claude_team_mcp/worker_prompt.py +119 -33
- {claude_team_mcp-0.5.0.dist-info → claude_team_mcp-0.6.0.dist-info}/METADATA +16 -3
- {claude_team_mcp-0.5.0.dist-info → claude_team_mcp-0.6.0.dist-info}/RECORD +14 -13
- claude_team_mcp/tools/bd_help.py +0 -42
- {claude_team_mcp-0.5.0.dist-info → claude_team_mcp-0.6.0.dist-info}/WHEEL +0 -0
- {claude_team_mcp-0.5.0.dist-info → claude_team_mcp-0.6.0.dist-info}/entry_points.txt +0 -0
|
@@ -58,10 +58,9 @@ class CodexCLI(AgentCLI):
|
|
|
58
58
|
"""
|
|
59
59
|
args: list[str] = []
|
|
60
60
|
|
|
61
|
-
# Codex uses --
|
|
62
|
-
# (--full-auto doesn't work through happy wrapper)
|
|
61
|
+
# Codex uses --full-auto for autonomous operation.
|
|
63
62
|
if dangerously_skip_permissions:
|
|
64
|
-
args.append("--
|
|
63
|
+
args.append("--full-auto")
|
|
65
64
|
|
|
66
65
|
# Note: settings_file is ignored - Codex doesn't support this
|
|
67
66
|
# Idle detection uses session file polling instead
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Issue tracker abstraction module.
|
|
3
|
+
|
|
4
|
+
Defines a protocol and backend implementations for issue tracker commands.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Protocol, runtime_checkable
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("claude-team-mcp")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class IssueTrackerBackend(Protocol):
|
|
19
|
+
"""
|
|
20
|
+
Protocol defining the issue tracker backend interface.
|
|
21
|
+
|
|
22
|
+
Backends provide a name, CLI command, marker directory, and command templates.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
name: str
|
|
26
|
+
cli: str
|
|
27
|
+
marker_dir: str
|
|
28
|
+
commands: dict[str, str]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class BeadsBackend:
|
|
33
|
+
"""Beads issue tracker backend."""
|
|
34
|
+
|
|
35
|
+
name: str = "beads"
|
|
36
|
+
cli: str = "bd"
|
|
37
|
+
marker_dir: str = ".beads"
|
|
38
|
+
commands: dict[str, str] = field(
|
|
39
|
+
default_factory=lambda: {
|
|
40
|
+
"list": "bd --no-db list",
|
|
41
|
+
"ready": "bd --no-db ready",
|
|
42
|
+
"show": "bd --no-db show {issue_id}",
|
|
43
|
+
"update": "bd --no-db update {issue_id} --status {status}",
|
|
44
|
+
"close": "bd --no-db close {issue_id}",
|
|
45
|
+
"create": (
|
|
46
|
+
"bd --no-db create --title \"{title}\" --type {type} "
|
|
47
|
+
"--priority {priority} --description \"{description}\""
|
|
48
|
+
),
|
|
49
|
+
"comment": "bd --no-db comment {issue_id} \"{comment}\"",
|
|
50
|
+
"dep_add": "bd --no-db dep add {issue_id} {dependency_id}",
|
|
51
|
+
"dep_tree": "bd --no-db dep tree {issue_id}",
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class PebblesBackend:
|
|
58
|
+
"""Pebbles issue tracker backend."""
|
|
59
|
+
|
|
60
|
+
name: str = "pebbles"
|
|
61
|
+
cli: str = "pb"
|
|
62
|
+
marker_dir: str = ".pebbles"
|
|
63
|
+
commands: dict[str, str] = field(
|
|
64
|
+
default_factory=lambda: {
|
|
65
|
+
"list": "pb list",
|
|
66
|
+
"ready": "pb ready",
|
|
67
|
+
"show": "pb show {issue_id}",
|
|
68
|
+
"update": "pb update {issue_id} -status {status}",
|
|
69
|
+
"close": "pb close {issue_id}",
|
|
70
|
+
"create": (
|
|
71
|
+
"pb create -title \"{title}\" -type {type} -priority {priority} "
|
|
72
|
+
"-description \"{description}\""
|
|
73
|
+
),
|
|
74
|
+
"comment": "pb comment {issue_id} -body \"{comment}\"",
|
|
75
|
+
"dep_add": "pb dep add {issue_id} {dependency_id}",
|
|
76
|
+
"dep_tree": "pb dep tree {issue_id}",
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
BEADS_BACKEND = BeadsBackend()
|
|
82
|
+
PEBBLES_BACKEND = PebblesBackend()
|
|
83
|
+
BACKEND_REGISTRY: dict[str, IssueTrackerBackend] = {
|
|
84
|
+
BEADS_BACKEND.name: BEADS_BACKEND,
|
|
85
|
+
PEBBLES_BACKEND.name: PEBBLES_BACKEND,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def detect_issue_tracker(project_path: str) -> IssueTrackerBackend | None:
|
|
90
|
+
"""
|
|
91
|
+
Detect the issue tracker backend for the given project path.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
project_path: Absolute or relative path to the project root.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
The detected IssueTrackerBackend, or None if no markers are present.
|
|
98
|
+
"""
|
|
99
|
+
beads_marker = os.path.join(project_path, BEADS_BACKEND.marker_dir)
|
|
100
|
+
pebbles_marker = os.path.join(project_path, PEBBLES_BACKEND.marker_dir)
|
|
101
|
+
|
|
102
|
+
# Check marker directories in the project root.
|
|
103
|
+
beads_present = os.path.isdir(beads_marker)
|
|
104
|
+
pebbles_present = os.path.isdir(pebbles_marker)
|
|
105
|
+
|
|
106
|
+
# Resolve the deterministic backend when both markers exist.
|
|
107
|
+
if beads_present and pebbles_present:
|
|
108
|
+
logger.warning(
|
|
109
|
+
"Both .beads and .pebbles found in %s; defaulting to pebbles",
|
|
110
|
+
project_path,
|
|
111
|
+
)
|
|
112
|
+
return PEBBLES_BACKEND
|
|
113
|
+
|
|
114
|
+
# Return the matching backend if only one marker exists.
|
|
115
|
+
if pebbles_present:
|
|
116
|
+
return PEBBLES_BACKEND
|
|
117
|
+
|
|
118
|
+
if beads_present:
|
|
119
|
+
return BEADS_BACKEND
|
|
120
|
+
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
__all__ = [
|
|
125
|
+
"IssueTrackerBackend",
|
|
126
|
+
"BeadsBackend",
|
|
127
|
+
"PebblesBackend",
|
|
128
|
+
"BEADS_BACKEND",
|
|
129
|
+
"PEBBLES_BACKEND",
|
|
130
|
+
"BACKEND_REGISTRY",
|
|
131
|
+
"detect_issue_tracker",
|
|
132
|
+
]
|
|
@@ -8,7 +8,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
8
8
|
|
|
9
9
|
from . import adopt_worker
|
|
10
10
|
from . import annotate_worker
|
|
11
|
-
from . import
|
|
11
|
+
from . import issue_tracker_help
|
|
12
12
|
from . import check_idle_workers
|
|
13
13
|
from . import close_workers
|
|
14
14
|
from . import discover_workers
|
|
@@ -31,7 +31,7 @@ def register_all_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
31
31
|
"""
|
|
32
32
|
# Tools that don't need ensure_connection
|
|
33
33
|
annotate_worker.register_tools(mcp)
|
|
34
|
-
|
|
34
|
+
issue_tracker_help.register_tools(mcp)
|
|
35
35
|
check_idle_workers.register_tools(mcp)
|
|
36
36
|
close_workers.register_tools(mcp)
|
|
37
37
|
examine_worker.register_tools(mcp)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Issue tracker help tool.
|
|
3
|
+
|
|
4
|
+
Provides issue_tracker_help for quick reference on issue tracking commands.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from mcp.server.fastmcp import FastMCP
|
|
10
|
+
|
|
11
|
+
from ..issue_tracker import BACKEND_REGISTRY, detect_issue_tracker
|
|
12
|
+
from ..utils import build_issue_tracker_help_text, build_issue_tracker_quick_commands
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_tools(mcp: FastMCP) -> None:
|
|
16
|
+
"""Register issue_tracker_help tool on the MCP server."""
|
|
17
|
+
|
|
18
|
+
@mcp.tool()
|
|
19
|
+
async def issue_tracker_help() -> dict:
|
|
20
|
+
"""
|
|
21
|
+
Get a quick reference guide for using issue tracking.
|
|
22
|
+
|
|
23
|
+
Returns condensed documentation on tracker commands, workflow patterns,
|
|
24
|
+
and best practices for worker sessions. Call this tool when you need
|
|
25
|
+
guidance on tracking progress, adding comments, or managing issues.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Dict with help text and key command examples
|
|
29
|
+
"""
|
|
30
|
+
project_path = str(Path.cwd())
|
|
31
|
+
backend = detect_issue_tracker(project_path)
|
|
32
|
+
help_text = build_issue_tracker_help_text(backend)
|
|
33
|
+
quick_commands = build_issue_tracker_quick_commands(backend)
|
|
34
|
+
|
|
35
|
+
response = {
|
|
36
|
+
"help": help_text,
|
|
37
|
+
"quick_commands": quick_commands,
|
|
38
|
+
"worker_tip": (
|
|
39
|
+
"As a worker, add comments to track progress rather than closing issues. "
|
|
40
|
+
"The coordinator will close issues after reviewing your work."
|
|
41
|
+
),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if backend is None:
|
|
45
|
+
response["supported_trackers"] = sorted(BACKEND_REGISTRY.keys())
|
|
46
|
+
else:
|
|
47
|
+
response["tracker"] = backend.name
|
|
48
|
+
response["cli"] = backend.cli
|
|
49
|
+
|
|
50
|
+
return response
|
|
@@ -19,9 +19,10 @@ from ..idle_detection import (
|
|
|
19
19
|
wait_for_any_idle as wait_for_any_idle_impl,
|
|
20
20
|
SessionInfo,
|
|
21
21
|
)
|
|
22
|
+
from ..issue_tracker import detect_issue_tracker
|
|
22
23
|
from ..iterm_utils import send_prompt_for_agent
|
|
23
24
|
from ..registry import SessionStatus
|
|
24
|
-
from ..utils import error_response, HINTS
|
|
25
|
+
from ..utils import build_worker_message_hint, error_response, HINTS
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger("claude-team-mcp")
|
|
27
28
|
|
|
@@ -187,8 +188,14 @@ def register_tools(mcp: FastMCP) -> None:
|
|
|
187
188
|
# Update status to busy
|
|
188
189
|
registry.update_status(sid, SessionStatus.BUSY)
|
|
189
190
|
|
|
190
|
-
# Append hint
|
|
191
|
-
|
|
191
|
+
# Append tracker-specific hint so workers know how to log progress.
|
|
192
|
+
tracker_path = (
|
|
193
|
+
str(session.main_repo_path)
|
|
194
|
+
if session.main_repo_path is not None
|
|
195
|
+
else session.project_path
|
|
196
|
+
)
|
|
197
|
+
tracker_backend = detect_issue_tracker(tracker_path)
|
|
198
|
+
message_with_hint = message + build_worker_message_hint(tracker_backend)
|
|
192
199
|
|
|
193
200
|
# Send the message using agent-specific input handling.
|
|
194
201
|
# Codex needs a longer pre-Enter delay than Claude.
|
|
@@ -23,6 +23,7 @@ from ..iterm_utils import (
|
|
|
23
23
|
MAX_PANES_PER_TAB,
|
|
24
24
|
create_multi_pane_layout,
|
|
25
25
|
find_available_window,
|
|
26
|
+
get_window_for_session,
|
|
26
27
|
send_prompt,
|
|
27
28
|
send_prompt_for_agent,
|
|
28
29
|
split_pane,
|
|
@@ -32,7 +33,7 @@ from ..iterm_utils import (
|
|
|
32
33
|
from ..names import pick_names_for_count
|
|
33
34
|
from ..profile import apply_appearance_colors
|
|
34
35
|
from ..registry import SessionStatus
|
|
35
|
-
from ..utils import HINTS, error_response,
|
|
36
|
+
from ..utils import HINTS, error_response, get_worktree_tracker_dir
|
|
36
37
|
from ..worker_prompt import generate_worker_prompt, get_coordinator_guidance
|
|
37
38
|
from ..worktree import WorktreeError, create_local_worktree
|
|
38
39
|
|
|
@@ -324,76 +325,140 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
324
325
|
if layout == "auto":
|
|
325
326
|
# Try to find an existing window where the ENTIRE batch fits.
|
|
326
327
|
# This keeps spawn batches together rather than spreading across windows.
|
|
327
|
-
managed_iterm_ids: set[str] = {
|
|
328
|
-
s.iterm_session.session_id
|
|
329
|
-
for s in registry.list_all()
|
|
330
|
-
if s.iterm_session is not None
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
# Find a window with enough space for ALL workers
|
|
334
|
-
result = await find_available_window(
|
|
335
|
-
app,
|
|
336
|
-
max_panes=MAX_PANES_PER_TAB,
|
|
337
|
-
managed_session_ids=managed_iterm_ids,
|
|
338
|
-
)
|
|
339
|
-
|
|
340
328
|
target_tab = None
|
|
341
329
|
initial_pane_count = 0
|
|
342
330
|
first_session = None # Session to split from
|
|
343
331
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
332
|
+
# Prefer the coordinator's window when running inside iTerm2.
|
|
333
|
+
# ITERM_SESSION_ID format is "wXtYpZ:UUID" - extract just the UUID.
|
|
334
|
+
iterm_session_env = os.environ.get("ITERM_SESSION_ID")
|
|
335
|
+
coordinator_session_id = None
|
|
336
|
+
if iterm_session_env and ":" in iterm_session_env:
|
|
337
|
+
coordinator_session_id = iterm_session_env.split(":", 1)[1]
|
|
338
|
+
if coordinator_session_id:
|
|
339
|
+
coordinator_session = None
|
|
340
|
+
coordinator_tab = None
|
|
341
|
+
|
|
342
|
+
for window in app.terminal_windows:
|
|
343
|
+
for tab in window.tabs:
|
|
344
|
+
for session in tab.sessions:
|
|
345
|
+
if session.session_id == coordinator_session_id:
|
|
346
|
+
coordinator_session = session
|
|
347
|
+
coordinator_tab = tab
|
|
348
|
+
break
|
|
349
|
+
if coordinator_session:
|
|
350
|
+
break
|
|
351
|
+
if coordinator_session:
|
|
352
|
+
break
|
|
353
|
+
|
|
354
|
+
if coordinator_session and coordinator_tab:
|
|
355
|
+
coordinator_window = await get_window_for_session(
|
|
356
|
+
app, coordinator_session
|
|
356
357
|
)
|
|
358
|
+
if coordinator_window is not None:
|
|
359
|
+
initial_pane_count = len(coordinator_tab.sessions)
|
|
360
|
+
available_slots = MAX_PANES_PER_TAB - initial_pane_count
|
|
361
|
+
if worker_count <= available_slots:
|
|
362
|
+
target_tab = coordinator_tab
|
|
363
|
+
first_session = coordinator_session
|
|
364
|
+
logger.debug(
|
|
365
|
+
"Using coordinator window "
|
|
366
|
+
f"({initial_pane_count} panes, {available_slots} slots)"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
if target_tab is None:
|
|
370
|
+
managed_iterm_ids: set[str] = {
|
|
371
|
+
s.iterm_session.session_id
|
|
372
|
+
for s in registry.list_all()
|
|
373
|
+
if s.iterm_session is not None
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
# Find a window with enough space for ALL workers
|
|
377
|
+
result = await find_available_window(
|
|
378
|
+
app,
|
|
379
|
+
max_panes=MAX_PANES_PER_TAB,
|
|
380
|
+
managed_session_ids=managed_iterm_ids,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
if result:
|
|
384
|
+
window, tab, existing_session = result
|
|
385
|
+
initial_pane_count = len(tab.sessions)
|
|
386
|
+
available_slots = MAX_PANES_PER_TAB - initial_pane_count
|
|
387
|
+
|
|
388
|
+
if worker_count <= available_slots:
|
|
389
|
+
# Entire batch fits in this window
|
|
390
|
+
target_tab = tab
|
|
391
|
+
first_session = existing_session
|
|
392
|
+
logger.debug(
|
|
393
|
+
f"Batch of {worker_count} fits in existing window "
|
|
394
|
+
f"({initial_pane_count} panes, {available_slots} slots)"
|
|
395
|
+
)
|
|
357
396
|
|
|
358
397
|
if target_tab:
|
|
359
398
|
# Reuse existing window - track pane count locally (iTerm objects stale)
|
|
360
399
|
local_pane_count = initial_pane_count
|
|
361
|
-
|
|
400
|
+
final_pane_count = initial_pane_count + worker_count
|
|
401
|
+
# Track created sessions for splitting
|
|
362
402
|
created_sessions: list = []
|
|
363
403
|
|
|
364
404
|
for i in range(worker_count):
|
|
365
|
-
#
|
|
366
|
-
#
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
405
|
+
# Choose layout strategy based on final pane count:
|
|
406
|
+
# - 3 panes: coordinator full left, workers stacked right
|
|
407
|
+
# - 4 panes: quad (TL→TR→BL→BR)
|
|
408
|
+
if final_pane_count == 3:
|
|
409
|
+
# Layout: coordinator | worker1
|
|
410
|
+
# |--------
|
|
411
|
+
# | worker2
|
|
412
|
+
if local_pane_count == 1:
|
|
413
|
+
# First split: vertical from coordinator
|
|
414
|
+
new_session = await split_pane(
|
|
415
|
+
first_session,
|
|
416
|
+
vertical=True,
|
|
417
|
+
before=False,
|
|
418
|
+
profile=None,
|
|
419
|
+
profile_customizations=profile_customizations[i],
|
|
420
|
+
)
|
|
421
|
+
else:
|
|
422
|
+
# Second split: horizontal from first worker (stack on right)
|
|
423
|
+
new_session = await split_pane(
|
|
424
|
+
created_sessions[0],
|
|
425
|
+
vertical=False,
|
|
426
|
+
before=False,
|
|
427
|
+
profile=None,
|
|
428
|
+
profile_customizations=profile_customizations[i],
|
|
429
|
+
)
|
|
430
|
+
else:
|
|
431
|
+
# Quad pattern: TL→TR(vsplit)→BL(hsplit)→BR(hsplit)
|
|
432
|
+
if local_pane_count == 1:
|
|
433
|
+
# First split: vertical (left/right)
|
|
434
|
+
new_session = await split_pane(
|
|
435
|
+
first_session,
|
|
436
|
+
vertical=True,
|
|
437
|
+
before=False,
|
|
438
|
+
profile=None,
|
|
439
|
+
profile_customizations=profile_customizations[i],
|
|
440
|
+
)
|
|
441
|
+
elif local_pane_count == 2:
|
|
442
|
+
# Second split: horizontal from left pane (bottom-left)
|
|
443
|
+
new_session = await split_pane(
|
|
444
|
+
first_session,
|
|
445
|
+
vertical=False,
|
|
446
|
+
before=False,
|
|
447
|
+
profile=None,
|
|
448
|
+
profile_customizations=profile_customizations[i],
|
|
449
|
+
)
|
|
450
|
+
else: # local_pane_count == 3
|
|
451
|
+
# Third split: horizontal from right pane (bottom-right)
|
|
452
|
+
tr_session = (
|
|
453
|
+
created_sessions[0] if created_sessions else first_session
|
|
454
|
+
)
|
|
455
|
+
new_session = await split_pane(
|
|
456
|
+
tr_session,
|
|
457
|
+
vertical=False,
|
|
458
|
+
before=False,
|
|
459
|
+
profile=None,
|
|
460
|
+
profile_customizations=profile_customizations[i],
|
|
461
|
+
)
|
|
397
462
|
|
|
398
463
|
pane_sessions.append(new_session)
|
|
399
464
|
created_sessions.append(new_session)
|
|
@@ -476,9 +541,13 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
476
541
|
marker_id = session_ids[index]
|
|
477
542
|
agent_type = agent_types[index]
|
|
478
543
|
|
|
479
|
-
# Check for worktree and set
|
|
480
|
-
|
|
481
|
-
|
|
544
|
+
# Check for worktree and set tracker env var if needed.
|
|
545
|
+
tracker_info = get_worktree_tracker_dir(project_path)
|
|
546
|
+
if tracker_info:
|
|
547
|
+
env_var, tracker_dir = tracker_info
|
|
548
|
+
env = {env_var: tracker_dir}
|
|
549
|
+
else:
|
|
550
|
+
env = None
|
|
482
551
|
|
|
483
552
|
if agent_type == "codex":
|
|
484
553
|
# Start Codex in interactive mode using start_agent_in_session
|
|
@@ -560,12 +629,18 @@ def register_tools(mcp: FastMCP, ensure_connection) -> None:
|
|
|
560
629
|
if not bead and not custom_prompt:
|
|
561
630
|
workers_awaiting_task.append(managed.name)
|
|
562
631
|
|
|
632
|
+
tracker_path = (
|
|
633
|
+
str(managed.main_repo_path)
|
|
634
|
+
if managed.main_repo_path is not None
|
|
635
|
+
else managed.project_path
|
|
636
|
+
)
|
|
563
637
|
worker_prompt = generate_worker_prompt(
|
|
564
638
|
managed.session_id,
|
|
565
639
|
resolved_names[i],
|
|
566
640
|
agent_type=managed.agent_type,
|
|
567
641
|
use_worktree=use_worktree,
|
|
568
642
|
bead=bead,
|
|
643
|
+
project_path=tracker_path,
|
|
569
644
|
custom_prompt=custom_prompt,
|
|
570
645
|
)
|
|
571
646
|
|
|
@@ -2,16 +2,24 @@
|
|
|
2
2
|
Shared utilities for Claude Team MCP tools.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from .constants import
|
|
5
|
+
from .constants import (
|
|
6
|
+
CONVERSATION_PAGE_SIZE,
|
|
7
|
+
ISSUE_TRACKER_HELP_TOOL,
|
|
8
|
+
build_issue_tracker_help_text,
|
|
9
|
+
build_issue_tracker_quick_commands,
|
|
10
|
+
build_worker_message_hint,
|
|
11
|
+
)
|
|
6
12
|
from .errors import error_response, HINTS, get_session_or_error
|
|
7
|
-
from .worktree_detection import
|
|
13
|
+
from .worktree_detection import get_worktree_tracker_dir
|
|
8
14
|
|
|
9
15
|
__all__ = [
|
|
10
|
-
"BEADS_HELP_TEXT",
|
|
11
16
|
"CONVERSATION_PAGE_SIZE",
|
|
12
|
-
"
|
|
17
|
+
"ISSUE_TRACKER_HELP_TOOL",
|
|
18
|
+
"build_issue_tracker_help_text",
|
|
19
|
+
"build_issue_tracker_quick_commands",
|
|
20
|
+
"build_worker_message_hint",
|
|
13
21
|
"error_response",
|
|
14
22
|
"HINTS",
|
|
15
23
|
"get_session_or_error",
|
|
16
|
-
"
|
|
24
|
+
"get_worktree_tracker_dir",
|
|
17
25
|
]
|
|
@@ -4,6 +4,8 @@ Shared constants for Claude Team MCP tools.
|
|
|
4
4
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from ..issue_tracker import BACKEND_REGISTRY, IssueTrackerBackend
|
|
8
|
+
|
|
7
9
|
# Default page size for conversation history pagination
|
|
8
10
|
CONVERSATION_PAGE_SIZE = 5
|
|
9
11
|
|
|
@@ -11,23 +13,109 @@ CONVERSATION_PAGE_SIZE = 5
|
|
|
11
13
|
# Codex streams JSONL to stdout; we pipe it through tee to this directory
|
|
12
14
|
CODEX_JSONL_DIR = Path.home() / ".claude-team" / "codex"
|
|
13
15
|
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
# Tool name used for issue tracker help
|
|
17
|
+
ISSUE_TRACKER_HELP_TOOL = "issue_tracker_help"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _format_tracker_command(
|
|
21
|
+
backend: IssueTrackerBackend,
|
|
22
|
+
command: str,
|
|
23
|
+
**kwargs: str,
|
|
24
|
+
) -> str | None:
|
|
25
|
+
"""Format a tracker command template with the provided arguments."""
|
|
26
|
+
template = backend.commands.get(command)
|
|
27
|
+
if not template:
|
|
28
|
+
return None
|
|
29
|
+
return template.format(**kwargs)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _supported_trackers_summary() -> str:
|
|
33
|
+
"""Return a comma-separated summary of known issue trackers."""
|
|
34
|
+
return ", ".join(sorted(BACKEND_REGISTRY.keys()))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build_issue_tracker_help_text(backend: IssueTrackerBackend | None) -> str:
|
|
38
|
+
"""Build issue tracker help text for the detected backend."""
|
|
39
|
+
if backend is None:
|
|
40
|
+
supported = _supported_trackers_summary()
|
|
41
|
+
return (
|
|
42
|
+
"# Issue Tracker Quick Reference\n\n"
|
|
43
|
+
"No issue tracker detected for this project. "
|
|
44
|
+
f"Supported trackers: {supported}.\n"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Prepare command examples from the backend templates.
|
|
48
|
+
list_cmd = _format_tracker_command(backend, "list") or ""
|
|
49
|
+
ready_cmd = _format_tracker_command(backend, "ready") or ""
|
|
50
|
+
show_cmd = _format_tracker_command(backend, "show", issue_id="<issue-id>") or ""
|
|
51
|
+
update_cmd = (
|
|
52
|
+
_format_tracker_command(
|
|
53
|
+
backend,
|
|
54
|
+
"update",
|
|
55
|
+
issue_id="<issue-id>",
|
|
56
|
+
status="in_progress",
|
|
57
|
+
)
|
|
58
|
+
or ""
|
|
59
|
+
)
|
|
60
|
+
# Worker-focused examples reuse the comment command template.
|
|
61
|
+
comment_cmd = (
|
|
62
|
+
_format_tracker_command(
|
|
63
|
+
backend,
|
|
64
|
+
"comment",
|
|
65
|
+
issue_id="<issue-id>",
|
|
66
|
+
comment="progress message",
|
|
67
|
+
)
|
|
68
|
+
or ""
|
|
69
|
+
)
|
|
70
|
+
comment_progress_cmd = (
|
|
71
|
+
_format_tracker_command(
|
|
72
|
+
backend,
|
|
73
|
+
"comment",
|
|
74
|
+
issue_id="<issue-id>",
|
|
75
|
+
comment="Completed the API endpoint, now working on tests",
|
|
76
|
+
)
|
|
77
|
+
or ""
|
|
78
|
+
)
|
|
79
|
+
comment_final_cmd = (
|
|
80
|
+
_format_tracker_command(
|
|
81
|
+
backend,
|
|
82
|
+
"comment",
|
|
83
|
+
issue_id="<issue-id>",
|
|
84
|
+
comment=(
|
|
85
|
+
"COMPLETE: Implemented feature X. Changes in src/foo.py and "
|
|
86
|
+
"tests/test_foo.py. Ready for review."
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
or ""
|
|
90
|
+
)
|
|
91
|
+
# Closing and creation examples round out the workflow guidance.
|
|
92
|
+
close_cmd = _format_tracker_command(backend, "close", issue_id="<issue-id>") or ""
|
|
93
|
+
create_cmd = (
|
|
94
|
+
_format_tracker_command(
|
|
95
|
+
backend,
|
|
96
|
+
"create",
|
|
97
|
+
title="Bug: X doesn't work",
|
|
98
|
+
type="bug",
|
|
99
|
+
priority="P1",
|
|
100
|
+
description="Details...",
|
|
101
|
+
)
|
|
102
|
+
or ""
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Compose the full help text with backend-specific examples.
|
|
106
|
+
return f"""# Issue Tracker Quick Reference
|
|
107
|
+
|
|
108
|
+
Your project uses the `{backend.name}` issue tracker. Use it to track progress and communicate with the coordinator.
|
|
21
109
|
|
|
22
110
|
## Essential Commands
|
|
23
111
|
|
|
24
112
|
```bash
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
113
|
+
{list_cmd}
|
|
114
|
+
{ready_cmd}
|
|
115
|
+
{show_cmd}
|
|
116
|
+
{update_cmd}
|
|
117
|
+
{comment_cmd}
|
|
118
|
+
{close_cmd}
|
|
31
119
|
```
|
|
32
120
|
|
|
33
121
|
## Status Values
|
|
@@ -50,38 +138,72 @@ bd close <id> # Close when complete
|
|
|
50
138
|
|
|
51
139
|
## As a Worker
|
|
52
140
|
|
|
53
|
-
**IMPORTANT**: You should NOT close beads unless explicitly told to. Instead:
|
|
54
|
-
|
|
55
141
|
1. Mark your issue as in-progress when starting:
|
|
56
142
|
```bash
|
|
57
|
-
|
|
143
|
+
{update_cmd}
|
|
58
144
|
```
|
|
59
145
|
|
|
60
146
|
2. Add comments to document your progress:
|
|
61
147
|
```bash
|
|
62
|
-
|
|
63
|
-
bd comment <issue-id> "Found edge case - handling null values in response"
|
|
148
|
+
{comment_progress_cmd}
|
|
64
149
|
```
|
|
65
150
|
|
|
66
151
|
3. When finished, add a final summary comment:
|
|
67
152
|
```bash
|
|
68
|
-
|
|
153
|
+
{comment_final_cmd}
|
|
69
154
|
```
|
|
70
155
|
|
|
71
|
-
4. The coordinator will review and close the
|
|
156
|
+
4. The coordinator will review and close the issue.
|
|
72
157
|
|
|
73
158
|
## Creating New Issues (if needed)
|
|
74
159
|
|
|
75
160
|
```bash
|
|
76
|
-
|
|
161
|
+
{create_cmd}
|
|
77
162
|
```
|
|
163
|
+
"""
|
|
78
164
|
|
|
79
|
-
## Searching
|
|
80
165
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
166
|
+
def build_issue_tracker_quick_commands(
|
|
167
|
+
backend: IssueTrackerBackend | None,
|
|
168
|
+
) -> dict[str, str]:
|
|
169
|
+
"""Return quick command examples for the detected backend."""
|
|
170
|
+
if backend is None:
|
|
171
|
+
return {}
|
|
172
|
+
|
|
173
|
+
# Map common actions to backend templates for quick reference.
|
|
174
|
+
commands = {
|
|
175
|
+
"list_issues": _format_tracker_command(backend, "list"),
|
|
176
|
+
"show_ready": _format_tracker_command(backend, "ready"),
|
|
177
|
+
"show_issue": _format_tracker_command(backend, "show", issue_id="<issue-id>"),
|
|
178
|
+
"start_work": _format_tracker_command(
|
|
179
|
+
backend,
|
|
180
|
+
"update",
|
|
181
|
+
issue_id="<issue-id>",
|
|
182
|
+
status="in_progress",
|
|
183
|
+
),
|
|
184
|
+
"add_comment": _format_tracker_command(
|
|
185
|
+
backend,
|
|
186
|
+
"comment",
|
|
187
|
+
issue_id="<issue-id>",
|
|
188
|
+
comment="progress message",
|
|
189
|
+
),
|
|
190
|
+
"close_issue": _format_tracker_command(backend, "close", issue_id="<issue-id>"),
|
|
191
|
+
}
|
|
192
|
+
return {key: value for key, value in commands.items() if value}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def build_worker_message_hint(backend: IssueTrackerBackend | None) -> str:
|
|
196
|
+
"""Return the issue tracker hint appended to worker messages."""
|
|
197
|
+
if backend is None:
|
|
198
|
+
supported = _supported_trackers_summary()
|
|
199
|
+
return (
|
|
200
|
+
"\n\n---\n"
|
|
201
|
+
f"(Note: Use the `{ISSUE_TRACKER_HELP_TOOL}` tool for guidance on the "
|
|
202
|
+
f"configured issue tracker. Supported trackers: {supported}.)"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
"\n\n---\n"
|
|
207
|
+
f"(Note: Use the `{ISSUE_TRACKER_HELP_TOOL}` tool for guidance on "
|
|
208
|
+
f"{backend.name} commands (CLI: `{backend.cli}`).)"
|
|
209
|
+
)
|
|
@@ -2,19 +2,25 @@
|
|
|
2
2
|
Git worktree detection utilities.
|
|
3
3
|
|
|
4
4
|
Detects if a project path is a git worktree and locates the main repo's
|
|
5
|
-
|
|
5
|
+
issue tracker marker directory for proper issue tracking.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
9
|
import os
|
|
10
10
|
import subprocess
|
|
11
11
|
|
|
12
|
+
from claude_team_mcp.issue_tracker import (
|
|
13
|
+
BEADS_BACKEND,
|
|
14
|
+
PEBBLES_BACKEND,
|
|
15
|
+
detect_issue_tracker,
|
|
16
|
+
)
|
|
17
|
+
|
|
12
18
|
logger = logging.getLogger("claude-team-mcp")
|
|
13
19
|
|
|
14
20
|
|
|
15
|
-
def
|
|
21
|
+
def get_worktree_tracker_dir(project_path: str) -> tuple[str, str] | None:
|
|
16
22
|
"""
|
|
17
|
-
Detect if project_path is a git worktree and return
|
|
23
|
+
Detect if project_path is a git worktree and return tracker env var + dir.
|
|
18
24
|
|
|
19
25
|
Git worktrees have .git as a file (not a directory) pointing to the main repo.
|
|
20
26
|
The `git rev-parse --git-common-dir` command returns the path to the shared
|
|
@@ -24,9 +30,9 @@ def get_worktree_beads_dir(project_path: str) -> str | None:
|
|
|
24
30
|
project_path: Absolute path to the project directory
|
|
25
31
|
|
|
26
32
|
Returns:
|
|
27
|
-
|
|
33
|
+
(env_var, tracker_dir) if:
|
|
28
34
|
- project_path is a git worktree
|
|
29
|
-
- The main repo has a .beads
|
|
35
|
+
- The main repo has a tracker marker (.beads or .pebbles)
|
|
30
36
|
Otherwise returns None.
|
|
31
37
|
"""
|
|
32
38
|
try:
|
|
@@ -45,7 +51,7 @@ def get_worktree_beads_dir(project_path: str) -> str | None:
|
|
|
45
51
|
|
|
46
52
|
git_common_dir = result.stdout.strip()
|
|
47
53
|
|
|
48
|
-
# If the result is just ".git", this is the main repo (not a worktree)
|
|
54
|
+
# If the result is just ".git", this is the main repo (not a worktree).
|
|
49
55
|
if git_common_dir == ".git":
|
|
50
56
|
return None
|
|
51
57
|
|
|
@@ -57,19 +63,35 @@ def get_worktree_beads_dir(project_path: str) -> str | None:
|
|
|
57
63
|
|
|
58
64
|
git_common_dir = os.path.normpath(git_common_dir)
|
|
59
65
|
|
|
60
|
-
# Main repo is the parent directory of .git
|
|
66
|
+
# Main repo is the parent directory of .git.
|
|
61
67
|
main_repo = os.path.dirname(git_common_dir)
|
|
62
68
|
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
if
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
# Identify which tracker is present in the main repo.
|
|
70
|
+
tracker_backend = detect_issue_tracker(main_repo)
|
|
71
|
+
if tracker_backend is None:
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
# Map the detected tracker to its env var and marker directory.
|
|
75
|
+
if tracker_backend is BEADS_BACKEND:
|
|
76
|
+
env_var = "BEADS_DIR"
|
|
77
|
+
elif tracker_backend is PEBBLES_BACKEND:
|
|
78
|
+
env_var = "PEBBLES_DIR"
|
|
79
|
+
else:
|
|
80
|
+
logger.warning(
|
|
81
|
+
"Unknown issue tracker backend %s for %s",
|
|
82
|
+
tracker_backend.name,
|
|
83
|
+
main_repo,
|
|
69
84
|
)
|
|
70
|
-
return
|
|
85
|
+
return None
|
|
71
86
|
|
|
72
|
-
|
|
87
|
+
tracker_dir = os.path.join(main_repo, tracker_backend.marker_dir)
|
|
88
|
+
logger.info(
|
|
89
|
+
"Detected git worktree. Setting %s=%s for project %s",
|
|
90
|
+
env_var,
|
|
91
|
+
tracker_dir,
|
|
92
|
+
project_path,
|
|
93
|
+
)
|
|
94
|
+
return env_var, tracker_dir
|
|
73
95
|
|
|
74
96
|
except subprocess.TimeoutExpired:
|
|
75
97
|
logger.warning(f"Timeout checking git worktree status for {project_path}")
|
claude_team_mcp/worker_prompt.py
CHANGED
|
@@ -2,10 +2,84 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Literal, Optional
|
|
4
4
|
|
|
5
|
+
from .issue_tracker import BACKEND_REGISTRY, IssueTrackerBackend, detect_issue_tracker
|
|
6
|
+
from .utils.constants import ISSUE_TRACKER_HELP_TOOL
|
|
7
|
+
|
|
5
8
|
# Valid agent types for prompt generation
|
|
6
9
|
AgentType = Literal["claude", "codex"]
|
|
7
10
|
|
|
8
11
|
|
|
12
|
+
def _resolve_issue_tracker_backend(
|
|
13
|
+
project_path: Optional[str],
|
|
14
|
+
) -> IssueTrackerBackend | None:
|
|
15
|
+
"""Detect the issue tracker backend for a project path."""
|
|
16
|
+
if not project_path:
|
|
17
|
+
return None
|
|
18
|
+
return detect_issue_tracker(project_path)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _format_tracker_command(
|
|
22
|
+
backend: IssueTrackerBackend,
|
|
23
|
+
command: str,
|
|
24
|
+
**kwargs: str,
|
|
25
|
+
) -> str:
|
|
26
|
+
"""Format a tracker command template with the provided arguments."""
|
|
27
|
+
template = backend.commands.get(command)
|
|
28
|
+
if not template:
|
|
29
|
+
return ""
|
|
30
|
+
return template.format(**kwargs)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _supported_tracker_list() -> str:
|
|
34
|
+
"""Return a readable list of supported issue trackers."""
|
|
35
|
+
return ", ".join(sorted(BACKEND_REGISTRY.keys()))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _build_tracker_workflow_section(
|
|
39
|
+
issue_id: str,
|
|
40
|
+
backend: IssueTrackerBackend | None,
|
|
41
|
+
step_number: int,
|
|
42
|
+
) -> tuple[str, str | None]:
|
|
43
|
+
"""Build the issue tracker workflow section and show command hint."""
|
|
44
|
+
if backend:
|
|
45
|
+
update_cmd = _format_tracker_command(
|
|
46
|
+
backend,
|
|
47
|
+
"update",
|
|
48
|
+
issue_id=issue_id,
|
|
49
|
+
status="in_progress",
|
|
50
|
+
)
|
|
51
|
+
close_cmd = _format_tracker_command(
|
|
52
|
+
backend,
|
|
53
|
+
"close",
|
|
54
|
+
issue_id=issue_id,
|
|
55
|
+
)
|
|
56
|
+
show_cmd = _format_tracker_command(backend, "show", issue_id=issue_id) or None
|
|
57
|
+
# Provide backend-specific commands when a tracker is detected.
|
|
58
|
+
section = f"""
|
|
59
|
+
{step_number}. **Issue tracker workflow.** You're working on `{issue_id}`. Follow this workflow:
|
|
60
|
+
- Mark in progress: `{update_cmd}`
|
|
61
|
+
- Implement the changes
|
|
62
|
+
- Close issue: `{close_cmd}`
|
|
63
|
+
- Commit with issue reference: `git add -A && git commit -m "{issue_id}: <summary>"`
|
|
64
|
+
|
|
65
|
+
Use the {backend.name} CLI (`{backend.cli}`) for issue tracker commands.
|
|
66
|
+
"""
|
|
67
|
+
return section, show_cmd
|
|
68
|
+
|
|
69
|
+
supported = _supported_tracker_list()
|
|
70
|
+
# Fall back to generic instructions when no tracker is detected.
|
|
71
|
+
section = f"""
|
|
72
|
+
{step_number}. **Issue tracker workflow.** You're working on `{issue_id}`. Follow this workflow:
|
|
73
|
+
- Mark in progress in the issue tracker
|
|
74
|
+
- Implement the changes
|
|
75
|
+
- Close the issue when done
|
|
76
|
+
- Commit with issue reference: `git add -A && git commit -m "{issue_id}: <summary>"`
|
|
77
|
+
|
|
78
|
+
No issue tracker detected. Supported trackers: {supported}. Use `{ISSUE_TRACKER_HELP_TOOL}` for guidance.
|
|
79
|
+
"""
|
|
80
|
+
return section, None
|
|
81
|
+
|
|
82
|
+
|
|
9
83
|
def generate_worker_prompt(
|
|
10
84
|
session_id: str,
|
|
11
85
|
name: str,
|
|
@@ -13,6 +87,7 @@ def generate_worker_prompt(
|
|
|
13
87
|
agent_type: AgentType = "claude",
|
|
14
88
|
use_worktree: bool = False,
|
|
15
89
|
bead: Optional[str] = None,
|
|
90
|
+
project_path: Optional[str] = None,
|
|
16
91
|
custom_prompt: Optional[str] = None,
|
|
17
92
|
) -> str:
|
|
18
93
|
"""Generate the pre-prompt text for a worker session.
|
|
@@ -22,7 +97,8 @@ def generate_worker_prompt(
|
|
|
22
97
|
name: The friendly name assigned to this worker
|
|
23
98
|
agent_type: The type of agent CLI ("claude" or "codex")
|
|
24
99
|
use_worktree: Whether this worker is in an isolated worktree
|
|
25
|
-
bead: Optional
|
|
100
|
+
bead: Optional issue tracker ID (if provided, this is the assignment)
|
|
101
|
+
project_path: Optional project path for issue tracker detection
|
|
26
102
|
custom_prompt: Optional additional instructions from the coordinator
|
|
27
103
|
|
|
28
104
|
Returns:
|
|
@@ -40,6 +116,7 @@ def generate_worker_prompt(
|
|
|
40
116
|
name=name,
|
|
41
117
|
use_worktree=use_worktree,
|
|
42
118
|
bead=bead,
|
|
119
|
+
project_path=project_path,
|
|
43
120
|
custom_prompt=custom_prompt,
|
|
44
121
|
)
|
|
45
122
|
# Default to Claude prompt for unknown agent types to maintain backward compatibility
|
|
@@ -48,6 +125,7 @@ def generate_worker_prompt(
|
|
|
48
125
|
name=name,
|
|
49
126
|
use_worktree=use_worktree,
|
|
50
127
|
bead=bead,
|
|
128
|
+
project_path=project_path,
|
|
51
129
|
custom_prompt=custom_prompt,
|
|
52
130
|
)
|
|
53
131
|
|
|
@@ -57,6 +135,7 @@ def _generate_claude_worker_prompt(
|
|
|
57
135
|
name: str,
|
|
58
136
|
use_worktree: bool = False,
|
|
59
137
|
bead: Optional[str] = None,
|
|
138
|
+
project_path: Optional[str] = None,
|
|
60
139
|
custom_prompt: Optional[str] = None,
|
|
61
140
|
) -> str:
|
|
62
141
|
"""Generate the pre-prompt for a Claude Code worker session.
|
|
@@ -70,31 +149,31 @@ def _generate_claude_worker_prompt(
|
|
|
70
149
|
session_id: The unique identifier for this worker session
|
|
71
150
|
name: The friendly name assigned to this worker
|
|
72
151
|
use_worktree: Whether this worker is in an isolated worktree
|
|
73
|
-
bead: Optional
|
|
152
|
+
bead: Optional issue tracker ID
|
|
153
|
+
project_path: Optional project path for issue tracker detection
|
|
74
154
|
custom_prompt: Optional additional instructions
|
|
75
155
|
|
|
76
156
|
Returns:
|
|
77
157
|
Formatted pre-prompt for Claude worker
|
|
78
158
|
"""
|
|
159
|
+
# Detect issue tracker backend so we can use the right commands.
|
|
160
|
+
tracker_backend = _resolve_issue_tracker_backend(project_path)
|
|
161
|
+
|
|
79
162
|
# Build optional sections with dynamic numbering
|
|
80
163
|
next_step = 4
|
|
81
164
|
extra_sections = ""
|
|
82
165
|
|
|
83
|
-
#
|
|
166
|
+
# Issue tracker section (if issue ID provided)
|
|
84
167
|
if bead:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Use `bd --no-db` for all beads commands (required in worktrees).
|
|
93
|
-
"""
|
|
94
|
-
extra_sections += beads_section
|
|
168
|
+
tracker_section, show_cmd = _build_tracker_workflow_section(
|
|
169
|
+
issue_id=bead,
|
|
170
|
+
backend=tracker_backend,
|
|
171
|
+
step_number=next_step,
|
|
172
|
+
)
|
|
173
|
+
extra_sections += tracker_section
|
|
95
174
|
next_step += 1
|
|
96
175
|
|
|
97
|
-
# Commit section (if worktree but
|
|
176
|
+
# Commit section (if worktree but issue tracker section didn't already cover commit)
|
|
98
177
|
if use_worktree and not bead:
|
|
99
178
|
commit_section = f"""
|
|
100
179
|
{next_step}. **Commit when done.** You're in an isolated worktree branch — commit your
|
|
@@ -107,9 +186,12 @@ def _generate_claude_worker_prompt(
|
|
|
107
186
|
# Closing/assignment section - 4 cases based on bead and custom_prompt
|
|
108
187
|
if bead and custom_prompt:
|
|
109
188
|
# Case 2: bead + custom instructions
|
|
189
|
+
show_hint = (
|
|
190
|
+
f"Use `{show_cmd}` for details." if show_cmd else "Use your issue tracker for details."
|
|
191
|
+
)
|
|
110
192
|
closing = f"""=== YOUR ASSIGNMENT ===
|
|
111
193
|
|
|
112
|
-
The coordinator assigned you `{bead}` (
|
|
194
|
+
The coordinator assigned you `{bead}` ({show_hint}) and included
|
|
113
195
|
the following instructions:
|
|
114
196
|
|
|
115
197
|
{custom_prompt}
|
|
@@ -117,9 +199,12 @@ the following instructions:
|
|
|
117
199
|
Get to work!"""
|
|
118
200
|
elif bead:
|
|
119
201
|
# Case 1: bead only
|
|
202
|
+
show_hint = (
|
|
203
|
+
f"Use `{show_cmd}` for details." if show_cmd else "Use your issue tracker for details."
|
|
204
|
+
)
|
|
120
205
|
closing = f"""=== YOUR ASSIGNMENT ===
|
|
121
206
|
|
|
122
|
-
Your assignment is `{bead}`.
|
|
207
|
+
Your assignment is `{bead}`. {show_hint} Get to work!"""
|
|
123
208
|
elif custom_prompt:
|
|
124
209
|
# Case 3: custom instructions only
|
|
125
210
|
closing = f"""=== YOUR ASSIGNMENT ===
|
|
@@ -161,6 +246,7 @@ def _generate_codex_worker_prompt(
|
|
|
161
246
|
name: str,
|
|
162
247
|
use_worktree: bool = False,
|
|
163
248
|
bead: Optional[str] = None,
|
|
249
|
+
project_path: Optional[str] = None,
|
|
164
250
|
custom_prompt: Optional[str] = None,
|
|
165
251
|
) -> str:
|
|
166
252
|
"""Generate the pre-prompt for an OpenAI Codex worker session.
|
|
@@ -174,31 +260,31 @@ def _generate_codex_worker_prompt(
|
|
|
174
260
|
session_id: The unique identifier for this worker session
|
|
175
261
|
name: The friendly name assigned to this worker
|
|
176
262
|
use_worktree: Whether this worker is in an isolated worktree
|
|
177
|
-
bead: Optional
|
|
263
|
+
bead: Optional issue tracker ID
|
|
264
|
+
project_path: Optional project path for issue tracker detection
|
|
178
265
|
custom_prompt: Optional additional instructions
|
|
179
266
|
|
|
180
267
|
Returns:
|
|
181
268
|
Formatted pre-prompt for Codex worker
|
|
182
269
|
"""
|
|
270
|
+
# Detect issue tracker backend so we can use the right commands.
|
|
271
|
+
tracker_backend = _resolve_issue_tracker_backend(project_path)
|
|
272
|
+
|
|
183
273
|
# Build optional sections with dynamic numbering
|
|
184
274
|
next_step = 4
|
|
185
275
|
extra_sections = ""
|
|
186
276
|
|
|
187
|
-
#
|
|
277
|
+
# Issue tracker section (if issue ID provided) - same workflow as Claude
|
|
188
278
|
if bead:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
Use `bd --no-db` for all beads commands (required in worktrees).
|
|
197
|
-
"""
|
|
198
|
-
extra_sections += beads_section
|
|
279
|
+
tracker_section, show_cmd = _build_tracker_workflow_section(
|
|
280
|
+
issue_id=bead,
|
|
281
|
+
backend=tracker_backend,
|
|
282
|
+
step_number=next_step,
|
|
283
|
+
)
|
|
284
|
+
extra_sections += tracker_section
|
|
199
285
|
next_step += 1
|
|
200
286
|
|
|
201
|
-
# Commit section (if worktree but
|
|
287
|
+
# Commit section (if worktree but issue tracker section didn't already cover commit)
|
|
202
288
|
if use_worktree and not bead:
|
|
203
289
|
commit_section = f"""
|
|
204
290
|
{next_step}. **Commit when done.** You're in an isolated worktree branch — commit your
|
|
@@ -212,7 +298,7 @@ def _generate_codex_worker_prompt(
|
|
|
212
298
|
if bead and custom_prompt:
|
|
213
299
|
closing = f"""=== YOUR ASSIGNMENT ===
|
|
214
300
|
|
|
215
|
-
The coordinator assigned you `{bead}` (
|
|
301
|
+
The coordinator assigned you `{bead}` ({f"Use `{show_cmd}` for details." if show_cmd else "Use your issue tracker for details."}) and included
|
|
216
302
|
the following instructions:
|
|
217
303
|
|
|
218
304
|
{custom_prompt}
|
|
@@ -221,7 +307,7 @@ Get to work!"""
|
|
|
221
307
|
elif bead:
|
|
222
308
|
closing = f"""=== YOUR ASSIGNMENT ===
|
|
223
309
|
|
|
224
|
-
Your assignment is `{bead}`. Use `
|
|
310
|
+
Your assignment is `{bead}`. {f"Use `{show_cmd}` for details." if show_cmd else "Use your issue tracker for details."} Get to work!"""
|
|
225
311
|
elif custom_prompt:
|
|
226
312
|
closing = f"""=== YOUR ASSIGNMENT ===
|
|
227
313
|
|
|
@@ -270,7 +356,7 @@ def get_coordinator_guidance(
|
|
|
270
356
|
worker_summaries: List of dicts with keys:
|
|
271
357
|
- name: Worker name
|
|
272
358
|
- agent_type: Agent type ("claude" or "codex")
|
|
273
|
-
- bead: Optional
|
|
359
|
+
- bead: Optional issue tracker ID
|
|
274
360
|
- custom_prompt: Optional custom instructions (truncated for display)
|
|
275
361
|
- awaiting_task: True if worker has no bead and no prompt
|
|
276
362
|
|
|
@@ -309,7 +395,7 @@ def get_coordinator_guidance(
|
|
|
309
395
|
elif bead:
|
|
310
396
|
worker_lines.append(
|
|
311
397
|
f"- **{name}**{type_indicator}: `{bead}` "
|
|
312
|
-
"(
|
|
398
|
+
"(issue tracker workflow: mark in_progress -> implement -> close -> commit)"
|
|
313
399
|
)
|
|
314
400
|
elif custom_prompt:
|
|
315
401
|
short_prompt = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-team-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
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
|
|
@@ -192,7 +192,7 @@ export CLAUDE_TEAM_CODEX_COMMAND="happy codex"
|
|
|
192
192
|
| Tool | Description |
|
|
193
193
|
|------|-------------|
|
|
194
194
|
| `list_worktrees` | List git worktrees created by claude-team for a repository |
|
|
195
|
-
| `bd_help` |
|
|
195
|
+
| `bd_help` | Beads quick reference (use `pb help` for Pebbles projects) |
|
|
196
196
|
|
|
197
197
|
### Worker Identification
|
|
198
198
|
|
|
@@ -214,7 +214,6 @@ Arguments:
|
|
|
214
214
|
skip_permissions: bool - If True, start Claude with --dangerously-skip-permissions
|
|
215
215
|
custom_names: list[str] - Override automatic themed name selection
|
|
216
216
|
custom_prompt: str - Custom prompt instead of standard worker pre-prompt
|
|
217
|
-
include_beads_instructions: bool - Append beads guidance to custom prompt (default: True)
|
|
218
217
|
use_worktrees: bool - Create isolated git worktree for each worker
|
|
219
218
|
|
|
220
219
|
Returns:
|
|
@@ -269,6 +268,20 @@ make install-commands
|
|
|
269
268
|
| `/merge-worker` | Directly merge a worker's branch back to parent (for internal changes) |
|
|
270
269
|
| `/pr-worker` | Create a pull request from a worker's branch |
|
|
271
270
|
| `/team-summary` | Generate end-of-session summary of all worker activity |
|
|
271
|
+
|
|
272
|
+
## Issue Tracker Support
|
|
273
|
+
|
|
274
|
+
`claude-team` supports both Pebbles (`pb`) and Beads (`bd --no-db`).
|
|
275
|
+
The tracker is auto-detected by marker directories in the project root:
|
|
276
|
+
|
|
277
|
+
- `.pebbles` → Pebbles
|
|
278
|
+
- `.beads` → Beads
|
|
279
|
+
|
|
280
|
+
If both markers exist, Pebbles is selected by default. Worker prompts and
|
|
281
|
+
coordination guidance use the detected tracker commands. For quick references:
|
|
282
|
+
|
|
283
|
+
- Pebbles: `pb help`
|
|
284
|
+
- Beads: `bd_help` tool
|
|
272
285
|
| `/cleanup-worktrees` | Remove worktrees for merged branches |
|
|
273
286
|
|
|
274
287
|
## Usage Patterns
|
|
@@ -10,33 +10,34 @@ claude_team_mcp/registry.py,sha256=y14ggF7qCia-xYYWJZhTsUAnBn5fFEkS7LspfEwhGQI,1
|
|
|
10
10
|
claude_team_mcp/server.py,sha256=oOYWVJo0-P0izs1wyxeouK1MJP16-P5djRZH8trSrqM,12726
|
|
11
11
|
claude_team_mcp/session_state.py,sha256=OcViByS05afdiCrBNRLR9qKrAn77H-2ALTe7yHfUUDs,34837
|
|
12
12
|
claude_team_mcp/subprocess_cache.py,sha256=6Q1NIMn2X5_75S4s_a9iPBka-UCz6RW0EMxPSPEaLxo,3612
|
|
13
|
-
claude_team_mcp/worker_prompt.py,sha256=
|
|
13
|
+
claude_team_mcp/worker_prompt.py,sha256=z69Dxo1kF6_JpJnxCL72T69J-J48fglOIevCiayRlmI,15740
|
|
14
14
|
claude_team_mcp/worktree.py,sha256=s5X4mePVb4AlPzYsTklLGKgLD-4GJX1iokpsF6WwrdU,17015
|
|
15
15
|
claude_team_mcp/cli_backends/__init__.py,sha256=9lpklc5lGWn-AdHoVqFMHau3UgzsN276grC1istHI2k,1038
|
|
16
16
|
claude_team_mcp/cli_backends/base.py,sha256=dArm2Yqjzs3smN0JpSGR79e9I_kQYJFgQ8gYpNrmS-o,3923
|
|
17
17
|
claude_team_mcp/cli_backends/claude.py,sha256=5axMph_IX-wX-DMulq0B_xQ3mVbhlwZjIi_WqjQwyL4,3268
|
|
18
|
-
claude_team_mcp/cli_backends/codex.py,sha256=
|
|
18
|
+
claude_team_mcp/cli_backends/codex.py,sha256=NaBCCj1ed076cHTG44Ob1TpscwcdM3WATEqnq42kBIU,3284
|
|
19
|
+
claude_team_mcp/issue_tracker/__init__.py,sha256=JYcVEdXkqDKXd9BAUoz3emYyZ9bvVRsuTB7Vxfw3jhU,3792
|
|
19
20
|
claude_team_mcp/schemas/__init__.py,sha256=IingbdgdQx9C0ZLBOhZ-doqIJRbO7aeAQjuCHMuVA84,99
|
|
20
21
|
claude_team_mcp/schemas/codex.py,sha256=KwUkTmD70kQLZvzl366tHAsuwKbtsk86ySTOW6b9wDk,5949
|
|
21
|
-
claude_team_mcp/tools/__init__.py,sha256=
|
|
22
|
+
claude_team_mcp/tools/__init__.py,sha256=qILDACyNju45dX19QOmtCnVlRWCfGx7y96h5aJ_J5OU,1506
|
|
22
23
|
claude_team_mcp/tools/adopt_worker.py,sha256=oWSamOzOqnLPWADWb4tk7qSPHroxIhQXkFDnhy_EOkw,4560
|
|
23
24
|
claude_team_mcp/tools/annotate_worker.py,sha256=mBo4YMaaaNQxQBduYK1DPA-Ioift-ZQ9tYlBM6S15t8,1639
|
|
24
|
-
claude_team_mcp/tools/bd_help.py,sha256=kA3DnMGG48ruIt7L-tcUPfevy_l-Yy22pa3I20DZU2A,1402
|
|
25
25
|
claude_team_mcp/tools/check_idle_workers.py,sha256=IjDNDeal9M7lTONWjKZrt8bXu7O277ossHz_2oyLRcY,3374
|
|
26
26
|
claude_team_mcp/tools/close_workers.py,sha256=glVUNM5Q32z8DyzvNKpxExN9pDR16rzjuyzEjI-eInM,6633
|
|
27
27
|
claude_team_mcp/tools/discover_workers.py,sha256=PkdnURZBex_y0kFx_pv-EgWrEbtQtsg5TjqycNs7BJI,5437
|
|
28
28
|
claude_team_mcp/tools/examine_worker.py,sha256=7Nd3EOdsGVDjMJ8ov9NhmcrjN8K9IE4i_noXHOP1uI8,1620
|
|
29
|
+
claude_team_mcp/tools/issue_tracker_help.py,sha256=KxgjFhXp3NCUDjqTl3UnIiFV-Y5DM80C1EoZXfA82QM,1668
|
|
29
30
|
claude_team_mcp/tools/list_workers.py,sha256=6cvsgHOYtakkSgoZdYez4VEQ3I8aWAKaGWhfiZyPf2c,2390
|
|
30
31
|
claude_team_mcp/tools/list_worktrees.py,sha256=_EIwpwoCMLaQh4dHws-jO53MS-E-L7hC7oPT7xC26lw,3665
|
|
31
|
-
claude_team_mcp/tools/message_workers.py,sha256=
|
|
32
|
+
claude_team_mcp/tools/message_workers.py,sha256=02XAE2EsJkkcZdmjaxQRiyUhAFTN4uB8kYzSkOwbxk0,12220
|
|
32
33
|
claude_team_mcp/tools/read_worker_logs.py,sha256=9gccM6MSaoxu97Xjsk7nUalLEU8XHV7r3SKggBRpNww,5913
|
|
33
|
-
claude_team_mcp/tools/spawn_workers.py,sha256=
|
|
34
|
+
claude_team_mcp/tools/spawn_workers.py,sha256=A08erciMM-q0TCPaFeVn6KvtVerIWdlbm6bH7P_E5G8,32366
|
|
34
35
|
claude_team_mcp/tools/wait_idle_workers.py,sha256=ITnhBiJYCgkz-DGJaYouAyTDh6cPIL_9RrptqC_xHu0,5232
|
|
35
|
-
claude_team_mcp/utils/__init__.py,sha256=
|
|
36
|
-
claude_team_mcp/utils/constants.py,sha256=
|
|
36
|
+
claude_team_mcp/utils/__init__.py,sha256=Jyc-N1RXZnMdXLjSgRBB2ih04v-h0woUzZcVYYfc7wQ,647
|
|
37
|
+
claude_team_mcp/utils/constants.py,sha256=FGDHeo0reZ89365fuXJGIl2Y5MFQAoKfWtmYr7UQzhQ,5815
|
|
37
38
|
claude_team_mcp/utils/errors.py,sha256=9uPFHp7UJYB5t3p7P_5Sh3PVpYh4_vj_RLzX5GcJPEU,2738
|
|
38
|
-
claude_team_mcp/utils/worktree_detection.py,sha256=
|
|
39
|
-
claude_team_mcp-0.
|
|
40
|
-
claude_team_mcp-0.
|
|
41
|
-
claude_team_mcp-0.
|
|
42
|
-
claude_team_mcp-0.
|
|
39
|
+
claude_team_mcp/utils/worktree_detection.py,sha256=oMGcb7p1jvr7qWs06sxUMTAV8jRialcVqziCTCdW7XU,3251
|
|
40
|
+
claude_team_mcp-0.6.0.dist-info/METADATA,sha256=PQ457DQFuTdb_Ua5NGvIEVl0cYKStLobxAM8DHxzidw,16020
|
|
41
|
+
claude_team_mcp-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
42
|
+
claude_team_mcp-0.6.0.dist-info/entry_points.txt,sha256=cmk5dmK50RVquExT-k_J72akl3qKIerPpVqfit7kQtQ,53
|
|
43
|
+
claude_team_mcp-0.6.0.dist-info/RECORD,,
|
claude_team_mcp/tools/bd_help.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Beads help tool.
|
|
3
|
-
|
|
4
|
-
Provides bd_help for quick reference on beads issue tracking.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from mcp.server.fastmcp import FastMCP
|
|
8
|
-
|
|
9
|
-
from ..utils import BEADS_HELP_TEXT
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def register_tools(mcp: FastMCP) -> None:
|
|
13
|
-
"""Register bd_help tool on the MCP server."""
|
|
14
|
-
|
|
15
|
-
@mcp.tool()
|
|
16
|
-
async def bd_help() -> dict:
|
|
17
|
-
"""
|
|
18
|
-
Get a quick reference guide for using Beads issue tracking.
|
|
19
|
-
|
|
20
|
-
Returns condensed documentation on beads commands, workflow patterns,
|
|
21
|
-
and best practices for worker sessions. Call this tool when you need
|
|
22
|
-
guidance on tracking progress, adding comments, or managing issues.
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
Dict with help text and key command examples
|
|
26
|
-
"""
|
|
27
|
-
return {
|
|
28
|
-
"help": BEADS_HELP_TEXT,
|
|
29
|
-
"quick_commands": {
|
|
30
|
-
"list_issues": "bd list",
|
|
31
|
-
"show_ready": "bd ready",
|
|
32
|
-
"show_issue": "bd show <issue-id>",
|
|
33
|
-
"start_work": "bd update <id> --status in_progress",
|
|
34
|
-
"add_comment": 'bd comment <id> "progress message"',
|
|
35
|
-
"close_issue": "bd close <id>",
|
|
36
|
-
"search": "bd search <query>",
|
|
37
|
-
},
|
|
38
|
-
"worker_tip": (
|
|
39
|
-
"As a worker, add comments to track progress rather than closing issues. "
|
|
40
|
-
"The coordinator will close issues after reviewing your work."
|
|
41
|
-
),
|
|
42
|
-
}
|
|
File without changes
|
|
File without changes
|