@team-agent/installer 0.1.10 → 0.2.0
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.
- package/crates/team-agent-core/src/lib.rs +50 -5
- package/package.json +1 -1
- package/schemas/team.schema.json +1 -0
- package/skills/team-agent/SKILL.md +1 -1
- package/src/team_agent/approvals/__init__.py +65 -0
- package/src/team_agent/approvals/constants.py +6 -0
- package/src/team_agent/approvals/parsing.py +176 -0
- package/src/team_agent/approvals/runtime_prompts.py +171 -0
- package/src/team_agent/approvals/status.py +165 -0
- package/src/team_agent/cli/__init__.py +135 -0
- package/src/team_agent/cli/commands.py +335 -0
- package/src/team_agent/cli/e2e.py +202 -0
- package/src/team_agent/cli/helpers.py +137 -0
- package/src/team_agent/cli/parser.py +470 -0
- package/src/team_agent/compiler.py +98 -33
- package/src/team_agent/coordinator/__init__.py +53 -0
- package/src/team_agent/{coordinator.py → coordinator/__main__.py} +3 -1
- package/src/team_agent/coordinator/lifecycle.py +319 -0
- package/src/team_agent/coordinator/metadata.py +61 -0
- package/src/team_agent/coordinator/paths.py +17 -0
- package/src/team_agent/diagnose/__init__.py +48 -0
- package/src/team_agent/diagnose/checks.py +101 -0
- package/src/team_agent/diagnose/health.py +241 -0
- package/src/team_agent/diagnose/preflight.py +194 -0
- package/src/team_agent/diagnose/quick_start.py +233 -0
- package/src/team_agent/display/__init__.py +61 -0
- package/src/team_agent/display/close.py +147 -0
- package/src/team_agent/display/ghostty.py +77 -0
- package/src/team_agent/display/worker_window.py +110 -0
- package/src/team_agent/display/workspace.py +473 -0
- package/src/team_agent/launch/__init__.py +41 -0
- package/src/team_agent/launch/bootstrap.py +85 -0
- package/src/team_agent/launch/config.py +106 -0
- package/src/team_agent/launch/core.py +291 -0
- package/src/team_agent/launch/requirements.py +57 -0
- package/src/team_agent/leader/__init__.py +320 -0
- package/src/team_agent/lifecycle/__init__.py +5 -0
- package/src/team_agent/lifecycle/agents.py +226 -0
- package/src/team_agent/lifecycle/operations.py +321 -0
- package/src/team_agent/lifecycle/start.py +360 -0
- package/src/team_agent/mcp_server/__init__.py +42 -0
- package/src/team_agent/mcp_server/__main__.py +7 -0
- package/src/team_agent/mcp_server/contracts.py +148 -0
- package/src/team_agent/mcp_server/normalize.py +257 -0
- package/src/team_agent/mcp_server/server.py +150 -0
- package/src/team_agent/mcp_server/tools.py +205 -0
- package/src/team_agent/message_store/__init__.py +23 -0
- package/src/team_agent/message_store/agent_health.py +109 -0
- package/src/team_agent/{message_store.py → message_store/core.py} +188 -245
- package/src/team_agent/message_store/result_watchers.py +102 -0
- package/src/team_agent/message_store/schema.py +266 -0
- package/src/team_agent/messaging/__init__.py +1 -0
- package/src/team_agent/messaging/activity_detector.py +190 -0
- package/src/team_agent/messaging/delivery.py +128 -0
- package/src/team_agent/messaging/deps.py +263 -0
- package/src/team_agent/messaging/idle_alerts.py +217 -0
- package/src/team_agent/messaging/internal_delivery.py +46 -0
- package/src/team_agent/messaging/leader.py +317 -0
- package/src/team_agent/messaging/leader_panes.py +343 -0
- package/src/team_agent/messaging/result_delivery.py +300 -0
- package/src/team_agent/messaging/results.py +456 -0
- package/src/team_agent/messaging/scheduler.py +418 -0
- package/src/team_agent/messaging/send.py +493 -0
- package/src/team_agent/messaging/tmux_io.py +337 -0
- package/src/team_agent/messaging/tmux_prompt.py +229 -0
- package/src/team_agent/orchestrator/__init__.py +376 -0
- package/src/team_agent/orchestrator/plan.py +122 -0
- package/src/team_agent/orchestrator/state.py +128 -0
- package/src/team_agent/profiles/__init__.py +82 -0
- package/src/team_agent/profiles/constants.py +19 -0
- package/src/team_agent/profiles/core.py +407 -0
- package/src/team_agent/profiles/helpers.py +69 -0
- package/src/team_agent/profiles/provider_env.py +188 -0
- package/src/team_agent/profiles/smoke.py +201 -0
- package/src/team_agent/provider_cli/__init__.py +43 -0
- package/src/team_agent/provider_cli/adapter.py +167 -0
- package/src/team_agent/provider_cli/base.py +48 -0
- package/src/team_agent/provider_cli/claude.py +457 -0
- package/src/team_agent/provider_cli/codex.py +319 -0
- package/src/team_agent/provider_cli/copilot.py +8 -0
- package/src/team_agent/provider_cli/fake.py +39 -0
- package/src/team_agent/provider_cli/gemini.py +95 -0
- package/src/team_agent/provider_cli/opencode.py +8 -0
- package/src/team_agent/provider_cli/prompt.py +62 -0
- package/src/team_agent/provider_cli/registry.py +18 -0
- package/src/team_agent/provider_cli/unsupported.py +32 -0
- package/src/team_agent/providers.py +67 -949
- package/src/team_agent/quality_gates.py +104 -0
- package/src/team_agent/restart/__init__.py +34 -0
- package/src/team_agent/restart/orchestration.py +328 -0
- package/src/team_agent/restart/selection.py +89 -0
- package/src/team_agent/restart/snapshot.py +70 -0
- package/src/team_agent/runtime.py +802 -5740
- package/src/team_agent/rust_core.py +22 -5
- package/src/team_agent/sessions/__init__.py +25 -0
- package/src/team_agent/sessions/capture.py +93 -0
- package/src/team_agent/sessions/inventory.py +44 -0
- package/src/team_agent/sessions/resume.py +135 -0
- package/src/team_agent/spec.py +3 -1
- package/src/team_agent/state.py +204 -4
- package/src/team_agent/status/__init__.py +63 -0
- package/src/team_agent/status/approvals.py +52 -0
- package/src/team_agent/status/compact.py +158 -0
- package/src/team_agent/status/constants.py +18 -0
- package/src/team_agent/status/inbox.py +28 -0
- package/src/team_agent/status/peek.py +117 -0
- package/src/team_agent/status/queries.py +168 -0
- package/src/team_agent/terminal.py +57 -0
- package/src/team_agent/cli.py +0 -857
- package/src/team_agent/mcp_server.py +0 -579
- package/src/team_agent/profiles.py +0 -882
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import tempfile
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from team_agent import runtime
|
|
9
|
+
from team_agent.simple_yaml import dumps
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cmd_e2e(args: argparse.Namespace) -> dict[str, Any]:
|
|
13
|
+
providers = [item.strip() for item in args.providers.split(",") if item.strip()]
|
|
14
|
+
workspace = Path(args.workspace).resolve() if args.workspace else Path(tempfile.mkdtemp(prefix="team-agent-e2e-"))
|
|
15
|
+
workspace.mkdir(parents=True, exist_ok=True)
|
|
16
|
+
results: dict[str, Any] = {"workspace": str(workspace), "providers": {}, "ok": True}
|
|
17
|
+
if "fake" in providers:
|
|
18
|
+
spec_path = workspace / "team.spec.yaml"
|
|
19
|
+
spec_path.write_text(dumps(_fake_spec(workspace)), encoding="utf-8")
|
|
20
|
+
results["providers"]["fake"] = _run_fake_e2e(spec_path, workspace)
|
|
21
|
+
results["ok"] = results["ok"] and results["providers"]["fake"]["ok"]
|
|
22
|
+
for provider in [p for p in providers if p != "fake"]:
|
|
23
|
+
from team_agent.providers import get_adapter
|
|
24
|
+
|
|
25
|
+
adapter = get_adapter(provider)
|
|
26
|
+
installed = adapter.is_installed()
|
|
27
|
+
if not installed:
|
|
28
|
+
provider_result = {
|
|
29
|
+
"ok": False,
|
|
30
|
+
"skipped": True,
|
|
31
|
+
"reason": f"{adapter.command_name} not installed",
|
|
32
|
+
"version": None,
|
|
33
|
+
}
|
|
34
|
+
elif not args.real:
|
|
35
|
+
provider_result = {
|
|
36
|
+
"ok": False,
|
|
37
|
+
"skipped": True,
|
|
38
|
+
"reason": "real provider launch disabled; rerun with --real on an authenticated machine",
|
|
39
|
+
"version": adapter.version(),
|
|
40
|
+
}
|
|
41
|
+
else:
|
|
42
|
+
provider_result = _run_real_launch_smoke(provider, workspace)
|
|
43
|
+
results["providers"][provider] = provider_result
|
|
44
|
+
results["ok"] = results["ok"] and provider_result["ok"]
|
|
45
|
+
return results
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _run_fake_e2e(spec_path: Path, workspace: Path) -> dict[str, Any]:
|
|
49
|
+
launched = runtime.launch(spec_path, auto_approve=True)
|
|
50
|
+
sent = runtime.send_message(workspace, None, "implement fake task", task_id="task_impl", requires_ack=True)
|
|
51
|
+
import time
|
|
52
|
+
|
|
53
|
+
time.sleep(1.0)
|
|
54
|
+
collected = runtime.collect(workspace)
|
|
55
|
+
stopped = runtime.shutdown(workspace)
|
|
56
|
+
return {"ok": bool(launched["ok"] and sent["ok"] and collected["collected"] and stopped["ok"]), "launch": launched, "send": sent, "collect": collected, "shutdown": stopped}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _run_real_launch_smoke(provider: str, workspace: Path) -> dict[str, Any]:
|
|
60
|
+
spec_path = workspace / f"team.{provider}.spec.yaml"
|
|
61
|
+
spec = _fake_spec(workspace)
|
|
62
|
+
spec["team"]["name"] = f"real-{provider}-smoke"
|
|
63
|
+
spec["leader"]["provider"] = provider
|
|
64
|
+
spec["agents"][0]["provider"] = provider
|
|
65
|
+
spec["agents"][0]["id"] = f"{provider}_smoke"
|
|
66
|
+
spec["agents"][0]["tools"] = ["fs_read", "fs_list", "git_diff", "mcp_team", "provider_builtin"]
|
|
67
|
+
spec["agents"][0]["role"] = "reviewer"
|
|
68
|
+
spec["agents"][0]["system_prompt"]["inline"] = (
|
|
69
|
+
"Real provider smoke. Do not edit files or run shell. "
|
|
70
|
+
"Do not call team-agent launch and do not create a nested Team Agent team. "
|
|
71
|
+
"When asked, call team_orchestrator.report_result exactly once with result_envelope_v1."
|
|
72
|
+
)
|
|
73
|
+
spec["routing"]["rules"][0]["assign_to"] = spec["agents"][0]["id"]
|
|
74
|
+
spec["runtime"]["session_name"] = f"team-agent-real-{provider}"
|
|
75
|
+
spec["runtime"]["startup_order"] = [spec["agents"][0]["id"]]
|
|
76
|
+
spec["tasks"][0]["id"] = f"task_real_{provider}_callback"
|
|
77
|
+
spec["tasks"][0]["title"] = f"Real {provider} callback smoke"
|
|
78
|
+
spec["tasks"][0]["assignee"] = spec["agents"][0]["id"]
|
|
79
|
+
spec["tasks"][0]["requires_tools"] = ["fs_read", "git_diff"]
|
|
80
|
+
spec["tasks"][0]["type"] = "review"
|
|
81
|
+
spec_path.write_text(dumps(spec), encoding="utf-8")
|
|
82
|
+
launched = runtime.launch(spec_path, auto_approve=True)
|
|
83
|
+
import time
|
|
84
|
+
|
|
85
|
+
time.sleep(10.0 if provider == "codex" else 3.0)
|
|
86
|
+
collected = None
|
|
87
|
+
sent = None
|
|
88
|
+
if provider == "codex":
|
|
89
|
+
task_id = spec["tasks"][0]["id"]
|
|
90
|
+
agent_id = spec["agents"][0]["id"]
|
|
91
|
+
message = (
|
|
92
|
+
"Do not call team-agent launch and do not create a nested Team Agent team. "
|
|
93
|
+
"Do not edit files or run shell. "
|
|
94
|
+
"Call team_orchestrator.report_result with envelope "
|
|
95
|
+
f'{{"schema_version":"result_envelope_v1","task_id":"{task_id}",'
|
|
96
|
+
f'"agent_id":"{agent_id}","status":"success","summary":"ok",'
|
|
97
|
+
'"changes":[],"tests":[{"command":"real-codex-callback-smoke","status":"passed"}],'
|
|
98
|
+
'"risks":[],"artifacts":[],"next_actions":[]}. Do not edit files or run shell.'
|
|
99
|
+
)
|
|
100
|
+
sent = runtime.send_message(workspace, agent_id, message, task_id=task_id, requires_ack=True)
|
|
101
|
+
for _ in range(24):
|
|
102
|
+
time.sleep(5.0)
|
|
103
|
+
result = runtime.collect(workspace)
|
|
104
|
+
if result["collected"]:
|
|
105
|
+
collected = result
|
|
106
|
+
break
|
|
107
|
+
status = runtime.status(workspace, as_json=True)
|
|
108
|
+
stopped = runtime.shutdown(workspace)
|
|
109
|
+
agent_id = spec["agents"][0]["id"]
|
|
110
|
+
agent_status = status["agents"].get(agent_id, {})
|
|
111
|
+
callback_ok = provider != "codex" or bool(collected and collected["collected"])
|
|
112
|
+
return {
|
|
113
|
+
"ok": bool(launched["ok"] and stopped["ok"] and agent_status.get("tmux_window_present") and callback_ok),
|
|
114
|
+
"launch": launched,
|
|
115
|
+
"send": sent,
|
|
116
|
+
"collect": collected,
|
|
117
|
+
"status": status,
|
|
118
|
+
"shutdown": stopped,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _fake_spec(workspace: Path) -> dict[str, Any]:
|
|
123
|
+
return {
|
|
124
|
+
"version": 1,
|
|
125
|
+
"team": {
|
|
126
|
+
"name": "fake-e2e",
|
|
127
|
+
"mode": "supervisor_worker",
|
|
128
|
+
"objective": "Exercise fake provider orchestration.",
|
|
129
|
+
"workspace": str(workspace),
|
|
130
|
+
},
|
|
131
|
+
"leader": {
|
|
132
|
+
"id": "leader",
|
|
133
|
+
"role": "leader",
|
|
134
|
+
"provider": "fake",
|
|
135
|
+
"model": None,
|
|
136
|
+
"tools": ["fs_read", "fs_list", "mcp_team"],
|
|
137
|
+
"context_policy": {
|
|
138
|
+
"keep_user_thread": True,
|
|
139
|
+
"receive_worker_outputs": "structured_only",
|
|
140
|
+
"max_worker_result_tokens": 2000,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
"agents": [
|
|
144
|
+
{
|
|
145
|
+
"id": "fake_impl",
|
|
146
|
+
"role": "implementation_engineer",
|
|
147
|
+
"provider": "fake",
|
|
148
|
+
"model": None,
|
|
149
|
+
"working_directory": str(workspace),
|
|
150
|
+
"system_prompt": {"inline": "Handle fake implementation tasks.", "file": None},
|
|
151
|
+
"tools": ["fs_read", "fs_write", "fs_list", "execute_bash", "git_diff", "mcp_team", "provider_builtin"],
|
|
152
|
+
"permission_mode": "restricted",
|
|
153
|
+
"preferred_for": ["implementation"],
|
|
154
|
+
"avoid_for": [],
|
|
155
|
+
"output_contract": {"format": "result_envelope_v1", "required_fields": ["task_id", "status", "summary", "artifacts"]},
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"routing": {
|
|
159
|
+
"default_assignee": "leader",
|
|
160
|
+
"rules": [{"id": "implementation-to-fake", "match": {"type": ["implementation"]}, "assign_to": "fake_impl", "priority": 10}],
|
|
161
|
+
},
|
|
162
|
+
"communication": {
|
|
163
|
+
"protocol": "mcp_inbox",
|
|
164
|
+
"topology": "leader_centered",
|
|
165
|
+
"worker_to_worker": True,
|
|
166
|
+
"ack_timeout_sec": 2,
|
|
167
|
+
"result_format": "result_envelope_v1",
|
|
168
|
+
"message_store": {"sqlite": ".team/runtime/team.db", "mirror_files": ".team/messages"},
|
|
169
|
+
},
|
|
170
|
+
"runtime": {
|
|
171
|
+
"backend": "tmux",
|
|
172
|
+
"display_backend": "none",
|
|
173
|
+
"session_name": "team-agent-fake-e2e",
|
|
174
|
+
"auto_launch": True,
|
|
175
|
+
"require_user_approval_before_launch": False,
|
|
176
|
+
"max_active_agents": 1,
|
|
177
|
+
"startup_order": ["fake_impl"],
|
|
178
|
+
},
|
|
179
|
+
"context": {
|
|
180
|
+
"state_file": "team_state.md",
|
|
181
|
+
"artifact_dir": ".team/artifacts",
|
|
182
|
+
"log_dir": ".team/logs",
|
|
183
|
+
"summarization": {
|
|
184
|
+
"worker_full_logs": "retain_outside_leader_context",
|
|
185
|
+
"state_update": "after_each_result",
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
"tasks": [
|
|
189
|
+
{
|
|
190
|
+
"id": "task_impl",
|
|
191
|
+
"title": "Fake implementation",
|
|
192
|
+
"type": "implementation",
|
|
193
|
+
"assignee": None,
|
|
194
|
+
"deps": [],
|
|
195
|
+
"acceptance": ["fake result collected"],
|
|
196
|
+
"status": "pending",
|
|
197
|
+
"requires_tools": ["fs_write", "execute_bash"],
|
|
198
|
+
"files": ["src/example.py"],
|
|
199
|
+
"risk": "low",
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def emit(result: Any, as_json: bool) -> None:
|
|
13
|
+
if as_json:
|
|
14
|
+
print(json.dumps(result, indent=2, ensure_ascii=False, sort_keys=True))
|
|
15
|
+
return
|
|
16
|
+
if isinstance(result, dict):
|
|
17
|
+
for key, value in result.items():
|
|
18
|
+
if isinstance(value, (dict, list)):
|
|
19
|
+
print(f"{key}: {json.dumps(value, ensure_ascii=False)}")
|
|
20
|
+
else:
|
|
21
|
+
print(f"{key}: {value}")
|
|
22
|
+
else:
|
|
23
|
+
print(result)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _workspace_from_args(args: argparse.Namespace) -> Path:
|
|
27
|
+
return Path(getattr(args, "workspace", ".")).resolve()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _emit_cli_error(exc: Exception, args: argparse.Namespace) -> None:
|
|
31
|
+
workspace = _workspace_from_args(args)
|
|
32
|
+
log_dir = workspace / ".team" / "logs"
|
|
33
|
+
try:
|
|
34
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
except OSError:
|
|
36
|
+
log_dir = Path.cwd()
|
|
37
|
+
log_path = log_dir / f"cli-error-{int(time.time())}.log"
|
|
38
|
+
log_path.write_text("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)), encoding="utf-8")
|
|
39
|
+
payload = _cli_error_payload(exc, args, log_path)
|
|
40
|
+
if getattr(args, "json", False):
|
|
41
|
+
print(json.dumps(payload, ensure_ascii=False))
|
|
42
|
+
return
|
|
43
|
+
print(f"error: {payload['error']}", file=sys.stderr)
|
|
44
|
+
print(f"action: {payload['action']}", file=sys.stderr)
|
|
45
|
+
print(f"log: {payload['log']}", file=sys.stderr)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _cli_error_payload(exc: Exception, args: argparse.Namespace, log_path: Path) -> dict[str, Any]:
|
|
49
|
+
error = str(exc)
|
|
50
|
+
payload = {
|
|
51
|
+
"ok": False,
|
|
52
|
+
"error": error,
|
|
53
|
+
"action": "run `team-agent doctor` or inspect the log path shown here",
|
|
54
|
+
"log": str(log_path),
|
|
55
|
+
}
|
|
56
|
+
session_name = _tmux_session_conflict_name(error)
|
|
57
|
+
if session_name:
|
|
58
|
+
payload.update(
|
|
59
|
+
{
|
|
60
|
+
"reason": "tmux_session_name_conflict",
|
|
61
|
+
"session_name": session_name,
|
|
62
|
+
"action": _tmux_session_conflict_action(session_name, getattr(args, "command", "")),
|
|
63
|
+
"next_actions": [_tmux_session_conflict_next_action(getattr(args, "command", ""))],
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
return payload
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _tmux_session_conflict_name(error: str) -> str | None:
|
|
70
|
+
marker = "tmux session already exists:"
|
|
71
|
+
if marker not in error:
|
|
72
|
+
return None
|
|
73
|
+
name = error.split(marker, 1)[1].strip()
|
|
74
|
+
name = name.split(";", 1)[0].splitlines()[0].strip()
|
|
75
|
+
if ". Startup" in name:
|
|
76
|
+
name = name.split(". Startup", 1)[0].strip()
|
|
77
|
+
name = name.rstrip(".").strip()
|
|
78
|
+
return name or None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _tmux_session_conflict_next_action(command: str) -> str:
|
|
82
|
+
if command == "quick-start":
|
|
83
|
+
return "Change `name:` in TEAM.md and run `team-agent quick-start` again."
|
|
84
|
+
return "Use a different team name or runtime.session_name before starting again."
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _tmux_session_conflict_action(session_name: str, command: str) -> str:
|
|
88
|
+
if command == "quick-start":
|
|
89
|
+
return (
|
|
90
|
+
f"tmux session `{session_name}` already exists. It may be an active team. "
|
|
91
|
+
"Do not terminate existing tmux sessions from quick-start; "
|
|
92
|
+
"change `name:` in TEAM.md and run quick-start again."
|
|
93
|
+
)
|
|
94
|
+
return (
|
|
95
|
+
f"tmux session `{session_name}` already exists. It may be an active team. "
|
|
96
|
+
"Do not terminate existing tmux sessions from startup; "
|
|
97
|
+
"use a different team name or runtime.session_name and start again."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _provider_args(values: list[str]) -> list[str]:
|
|
102
|
+
if values and values[0] == "--":
|
|
103
|
+
return values[1:]
|
|
104
|
+
return values
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _leader_launcher_args(values: list[str]) -> dict[str, Any]:
|
|
108
|
+
provider_args: list[str] = []
|
|
109
|
+
attach_existing = False
|
|
110
|
+
confirm_attach = False
|
|
111
|
+
attach_session: str | None = None
|
|
112
|
+
index = 0
|
|
113
|
+
while index < len(values):
|
|
114
|
+
value = values[index]
|
|
115
|
+
if value == "--":
|
|
116
|
+
provider_args.extend(values[index:])
|
|
117
|
+
break
|
|
118
|
+
if value in {"--attach", "--attach-existing"}:
|
|
119
|
+
attach_existing = True
|
|
120
|
+
elif value == "--confirm":
|
|
121
|
+
confirm_attach = True
|
|
122
|
+
elif value == "--attach-session":
|
|
123
|
+
index += 1
|
|
124
|
+
if index >= len(values):
|
|
125
|
+
raise RuntimeError("--attach-session requires a tmux session name")
|
|
126
|
+
attach_session = values[index]
|
|
127
|
+
elif value.startswith("--attach-session="):
|
|
128
|
+
attach_session = value.split("=", 1)[1]
|
|
129
|
+
else:
|
|
130
|
+
provider_args.append(value)
|
|
131
|
+
index += 1
|
|
132
|
+
return {
|
|
133
|
+
"provider_args": provider_args,
|
|
134
|
+
"attach_existing": attach_existing,
|
|
135
|
+
"confirm_attach": confirm_attach,
|
|
136
|
+
"attach_session": attach_session,
|
|
137
|
+
}
|