@team-agent/installer 0.3.0 → 0.3.2
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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +38 -7
- package/crates/team-agent/src/cli/emit.rs +182 -54
- package/crates/team-agent/src/cli/mod.rs +703 -35
- package/crates/team-agent/src/cli/status_port.rs +170 -44
- package/crates/team-agent/src/cli/tests/run_delegation.rs +2 -0
- package/crates/team-agent/src/cli/types.rs +1 -0
- package/crates/team-agent/src/coordinator/health.rs +130 -0
- package/crates/team-agent/src/leader/lease.rs +23 -2
- package/crates/team-agent/src/leader/rediscover/tests.rs +1 -0
- package/crates/team-agent/src/leader/rediscover.rs +2 -0
- package/crates/team-agent/src/leader/tests/byte_findings.rs +9 -6
- package/crates/team-agent/src/leader/tests/idle.rs +1 -0
- package/crates/team-agent/src/leader/tests/lease_claim.rs +157 -0
- package/crates/team-agent/src/leader/types.rs +2 -0
- package/crates/team-agent/src/lifecycle/launch.rs +554 -65
- package/crates/team-agent/src/lifecycle/restart/common.rs +65 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +57 -15
- package/crates/team-agent/src/lifecycle/restart/remove.rs +5 -1
- package/crates/team-agent/src/lifecycle/restart.rs +20 -0
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +52 -0
- package/crates/team-agent/src/lifecycle/types.rs +25 -0
- package/crates/team-agent/src/mcp_server/tests/wire.rs +28 -0
- package/crates/team-agent/src/mcp_server/wire.rs +81 -1
- package/crates/team-agent/src/messaging/delivery.rs +574 -12
- package/crates/team-agent/src/messaging/leader_receiver.rs +26 -37
- package/crates/team-agent/src/messaging/mod.rs +1 -1
- package/crates/team-agent/src/messaging/results.rs +218 -49
- package/crates/team-agent/src/messaging/send.rs +15 -19
- package/crates/team-agent/src/provider/adapter.rs +95 -10
- package/crates/team-agent/src/provider/helpers.rs +10 -1
- package/crates/team-agent/src/state/identity.rs +3 -0
- package/crates/team-agent/src/state/persist.rs +113 -1
- package/crates/team-agent/src/state/projection.rs +127 -3
- package/crates/team-agent/src/tmux_backend/tests.rs +179 -0
- package/crates/team-agent/src/tmux_backend.rs +124 -12
- package/npm/install.mjs +29 -7
- package/package.json +4 -4
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -85,7 +85,7 @@ pub fn cmd_init(args: &InitArgs) -> Result<CmdResult, CliError> {
|
|
|
85
85
|
/// `cmd_quick_start`(`commands.py:18`)。`--json` 或 `!ok` → 整 dict;否则 `result["summary"]`。
|
|
86
86
|
pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
87
87
|
let value = lifecycle_port::quick_start(
|
|
88
|
-
&args.
|
|
88
|
+
&args.workspace,
|
|
89
89
|
&args.agents_dir,
|
|
90
90
|
args.name.as_deref(),
|
|
91
91
|
args.team_id.as_deref(),
|
|
@@ -123,6 +123,10 @@ pub fn cmd_compile(args: &CompileArgs) -> Result<CmdResult, CliError> {
|
|
|
123
123
|
/// `cmd_status`(`commands.py:90`)。三态:`--summary`(xor json,xor agent)→五行文本;
|
|
124
124
|
/// `--json`→`status_port::status(compact=!detail)`;else→`status_port::format_status(agent)`。
|
|
125
125
|
pub fn cmd_status(args: &StatusArgs) -> Result<CmdResult, CliError> {
|
|
126
|
+
cmd_status_for_team(args, None)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub fn cmd_status_for_team(args: &StatusArgs, team: Option<&str>) -> Result<CmdResult, CliError> {
|
|
126
130
|
if args.summary && args.json {
|
|
127
131
|
return Err(CliError::Runtime(
|
|
128
132
|
"--summary and --json are mutually exclusive".to_string(),
|
|
@@ -135,7 +139,7 @@ pub fn cmd_status(args: &StatusArgs) -> Result<CmdResult, CliError> {
|
|
|
135
139
|
}
|
|
136
140
|
let selected = match crate::state::selector::resolve_active_team(
|
|
137
141
|
&args.workspace,
|
|
138
|
-
|
|
142
|
+
team,
|
|
139
143
|
crate::state::selector::SelectorMode::RuntimeOnly,
|
|
140
144
|
) {
|
|
141
145
|
Ok(selected) => selected,
|
|
@@ -147,15 +151,29 @@ pub fn cmd_status(args: &StatusArgs) -> Result<CmdResult, CliError> {
|
|
|
147
151
|
}
|
|
148
152
|
};
|
|
149
153
|
if args.summary {
|
|
150
|
-
let value = status_port::
|
|
154
|
+
let value = status_port::status_scoped(
|
|
155
|
+
&selected.run_workspace,
|
|
156
|
+
&selected.state,
|
|
157
|
+
Some(&selected.team_key),
|
|
158
|
+
true,
|
|
159
|
+
false,
|
|
160
|
+
)?;
|
|
151
161
|
return Ok(CmdResult::human(format_status_summary(&value)));
|
|
152
162
|
}
|
|
153
163
|
if args.json {
|
|
154
|
-
let value = status_port::
|
|
164
|
+
let value = status_port::status_scoped(
|
|
165
|
+
&selected.run_workspace,
|
|
166
|
+
&selected.state,
|
|
167
|
+
Some(&selected.team_key),
|
|
168
|
+
status_compact_flag(args.detail),
|
|
169
|
+
args.detail,
|
|
170
|
+
)?;
|
|
155
171
|
return Ok(CmdResult::from_json(value, true));
|
|
156
172
|
}
|
|
157
|
-
Ok(CmdResult::human(status_port::
|
|
173
|
+
Ok(CmdResult::human(status_port::format_status_scoped(
|
|
158
174
|
&selected.run_workspace,
|
|
175
|
+
&selected.state,
|
|
176
|
+
Some(&selected.team_key),
|
|
159
177
|
args.agent.as_deref(),
|
|
160
178
|
)?))
|
|
161
179
|
}
|
|
@@ -252,9 +270,13 @@ pub fn cmd_validate_result(args: &ValidateResultArgs) -> Result<CmdResult, CliEr
|
|
|
252
270
|
|
|
253
271
|
/// `cmd_collect`(`parser.py:292`)。
|
|
254
272
|
pub fn cmd_collect(args: &CollectArgs) -> Result<CmdResult, CliError> {
|
|
273
|
+
cmd_collect_for_team(args, None)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
pub fn cmd_collect_for_team(args: &CollectArgs, team: Option<&str>) -> Result<CmdResult, CliError> {
|
|
255
277
|
let selected = match crate::state::selector::resolve_active_team(
|
|
256
278
|
&args.workspace,
|
|
257
|
-
|
|
279
|
+
team,
|
|
258
280
|
crate::state::selector::SelectorMode::RuntimeOnly,
|
|
259
281
|
) {
|
|
260
282
|
Ok(selected) => selected,
|
|
@@ -269,7 +291,16 @@ pub fn cmd_collect(args: &CollectArgs) -> Result<CmdResult, CliError> {
|
|
|
269
291
|
));
|
|
270
292
|
}
|
|
271
293
|
};
|
|
272
|
-
let value = match
|
|
294
|
+
let value = match if team.is_some() {
|
|
295
|
+
messaging::collect_for_team(
|
|
296
|
+
&selected.run_workspace,
|
|
297
|
+
args.result_file.as_deref(),
|
|
298
|
+
false,
|
|
299
|
+
Some(&selected.team_key),
|
|
300
|
+
)
|
|
301
|
+
} else {
|
|
302
|
+
messaging::collect(&selected.run_workspace, args.result_file.as_deref(), false)
|
|
303
|
+
} {
|
|
273
304
|
Ok(value) => value,
|
|
274
305
|
Err(error) => {
|
|
275
306
|
return Ok(CmdResult::from_json(
|
|
@@ -79,7 +79,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
79
79
|
"compile" => cmd_compile(&compile_args(args, cwd)?).map(emit_result),
|
|
80
80
|
"send" => cmd_send(&send_args(args, cwd)?).map(emit_result),
|
|
81
81
|
"allow-peer-talk" => cmd_allow_peer_talk(&allow_peer_talk_args(args, cwd)?).map(emit_result),
|
|
82
|
-
"status" =>
|
|
82
|
+
"status" => cmd_status_for_team(&status_args(args, cwd), parse_args(args).team.as_deref()).map(emit_result),
|
|
83
83
|
"stop" => cmd_shutdown(&shutdown_args(args, cwd)).map(emit_result),
|
|
84
84
|
"shutdown" => cmd_shutdown(&shutdown_args(args, cwd)).map(emit_result),
|
|
85
85
|
"restart" => cmd_restart(&restart_args(args, cwd)).map(emit_result),
|
|
@@ -108,7 +108,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
108
108
|
Ok(ExitCode::Usage)
|
|
109
109
|
}
|
|
110
110
|
"validate-result" => cmd_validate_result(&validate_result_args(args)?).map(emit_result),
|
|
111
|
-
"collect" =>
|
|
111
|
+
"collect" => cmd_collect_for_team(&collect_args(args, cwd), parse_args(args).team.as_deref()).map(emit_result),
|
|
112
112
|
"settle" => cmd_settle(&settle_args(args, cwd)).map(emit_result),
|
|
113
113
|
"repair-state" => cmd_repair_state(&repair_state_args(args, cwd)?).map(emit_result),
|
|
114
114
|
"diagnose" => cmd_diagnose(&diagnose_args(args, cwd)).map(emit_result),
|
|
@@ -121,6 +121,50 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
const DISPATCH_COMMANDS: &[&str] = &[
|
|
125
|
+
"init",
|
|
126
|
+
"quick-start",
|
|
127
|
+
"compile",
|
|
128
|
+
"send",
|
|
129
|
+
"allow-peer-talk",
|
|
130
|
+
"status",
|
|
131
|
+
"stop",
|
|
132
|
+
"shutdown",
|
|
133
|
+
"restart",
|
|
134
|
+
"restart-agent",
|
|
135
|
+
"start-agent",
|
|
136
|
+
"stop-agent",
|
|
137
|
+
"reset-agent",
|
|
138
|
+
"add-agent",
|
|
139
|
+
"fork-agent",
|
|
140
|
+
"remove-agent",
|
|
141
|
+
"stuck-list",
|
|
142
|
+
"stuck-cancel",
|
|
143
|
+
"acknowledge-idle",
|
|
144
|
+
"takeover",
|
|
145
|
+
"claim-leader",
|
|
146
|
+
"identity",
|
|
147
|
+
"approvals",
|
|
148
|
+
"inbox",
|
|
149
|
+
"doctor",
|
|
150
|
+
"watch",
|
|
151
|
+
"sessions",
|
|
152
|
+
"validate",
|
|
153
|
+
"profile",
|
|
154
|
+
"validate-result",
|
|
155
|
+
"collect",
|
|
156
|
+
"settle",
|
|
157
|
+
"repair-state",
|
|
158
|
+
"diagnose",
|
|
159
|
+
"preflight",
|
|
160
|
+
"wait-ready",
|
|
161
|
+
"e2e",
|
|
162
|
+
"peek",
|
|
163
|
+
"coordinator",
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
const SPEC_ONLY_HELP_COMMANDS: &[&str] = &["start", "purge-agent", "attach-leader"];
|
|
167
|
+
|
|
124
168
|
fn emit_missing_subcommand_usage() -> ExitCode {
|
|
125
169
|
emit_usage_error("the following arguments are required: {codex,claude,...,doctor}");
|
|
126
170
|
ExitCode::Usage
|
|
@@ -131,65 +175,62 @@ fn emit_missing_subcommand_usage() -> ExitCode {
|
|
|
131
175
|
/// Used by the `--help` short-circuit gate so unknown commands keep falling through
|
|
132
176
|
/// to the argparse invalid-choice path.
|
|
133
177
|
fn is_known_subcommand(command: &str) -> bool {
|
|
134
|
-
|
|
135
|
-
command,
|
|
136
|
-
"init"
|
|
137
|
-
| "quick-start"
|
|
138
|
-
| "compile"
|
|
139
|
-
| "send"
|
|
140
|
-
| "allow-peer-talk"
|
|
141
|
-
| "status"
|
|
142
|
-
| "start"
|
|
143
|
-
| "stop"
|
|
144
|
-
| "shutdown"
|
|
145
|
-
| "restart"
|
|
146
|
-
| "restart-agent"
|
|
147
|
-
| "start-agent"
|
|
148
|
-
| "stop-agent"
|
|
149
|
-
| "reset-agent"
|
|
150
|
-
| "add-agent"
|
|
151
|
-
| "fork-agent"
|
|
152
|
-
| "remove-agent"
|
|
153
|
-
| "purge-agent"
|
|
154
|
-
| "stuck-list"
|
|
155
|
-
| "stuck-cancel"
|
|
156
|
-
| "acknowledge-idle"
|
|
157
|
-
| "takeover"
|
|
158
|
-
| "claim-leader"
|
|
159
|
-
| "attach-leader"
|
|
160
|
-
| "identity"
|
|
161
|
-
| "approvals"
|
|
162
|
-
| "inbox"
|
|
163
|
-
| "doctor"
|
|
164
|
-
| "watch"
|
|
165
|
-
| "sessions"
|
|
166
|
-
| "validate"
|
|
167
|
-
| "profile"
|
|
168
|
-
| "validate-result"
|
|
169
|
-
| "collect"
|
|
170
|
-
| "settle"
|
|
171
|
-
| "repair-state"
|
|
172
|
-
| "diagnose"
|
|
173
|
-
| "preflight"
|
|
174
|
-
| "wait-ready"
|
|
175
|
-
| "e2e"
|
|
176
|
-
| "peek"
|
|
177
|
-
| "coordinator"
|
|
178
|
-
)
|
|
178
|
+
DISPATCH_COMMANDS.contains(&command) || SPEC_ONLY_HELP_COMMANDS.contains(&command)
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
fn command_help(command: Option<&str>) -> String {
|
|
182
182
|
match command {
|
|
183
|
-
None =>
|
|
184
|
-
|
|
183
|
+
None => {
|
|
184
|
+
let mut commands = vec!["codex", "claude"];
|
|
185
|
+
commands.extend_from_slice(DISPATCH_COMMANDS);
|
|
186
|
+
commands.extend_from_slice(SPEC_ONLY_HELP_COMMANDS);
|
|
187
|
+
format!(
|
|
188
|
+
"usage: team-agent <command> [options]\n\nCommands: {}\n\nRun `team-agent <command> --help` for command flags.",
|
|
189
|
+
commands.join(", ")
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
Some("init") => "usage: team-agent init [--workspace WORKSPACE] [--force] [--json]".to_string(),
|
|
193
|
+
Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--fresh] [--json]".to_string(),
|
|
185
194
|
Some("start") => "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]".to_string(),
|
|
186
|
-
Some("
|
|
195
|
+
Some("compile") => "usage: team-agent compile --team TEAM [--out FILE] [--json]".to_string(),
|
|
196
|
+
Some("send") => "usage: team-agent send TARGET MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--targets AGENTS] [--task TASK] [--sender SENDER] [--watch-result] [--requires-ack|--no-ack] [--no-wait] [--timeout SECONDS] [--confirm-human] [--message-id ID] [--json]".to_string(),
|
|
197
|
+
Some("allow-peer-talk") => "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]".to_string(),
|
|
198
|
+
Some("status") => "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]".to_string(),
|
|
199
|
+
Some("stop") => "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
|
|
200
|
+
Some("shutdown") => "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
|
|
201
|
+
Some("restart") => "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--json]".to_string(),
|
|
187
202
|
Some("restart-agent") => "usage: team-agent restart-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]".to_string(),
|
|
203
|
+
Some("reset-agent") => "usage: team-agent reset-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]".to_string(),
|
|
204
|
+
Some("start-agent") => "usage: team-agent start-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--allow-fresh] [--no-display] [--json]".to_string(),
|
|
205
|
+
Some("stop-agent") => "usage: team-agent stop-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
206
|
+
Some("add-agent") => "usage: team-agent add-agent AGENT --role-file FILE [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]".to_string(),
|
|
207
|
+
Some("fork-agent") => "usage: team-agent fork-agent SOURCE_AGENT --as AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]".to_string(),
|
|
208
|
+
Some("remove-agent") => "usage: team-agent remove-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--from-spec] [--confirm] [--force] [--json]".to_string(),
|
|
188
209
|
Some("purge-agent") => "usage: team-agent purge-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--json]".to_string(),
|
|
189
|
-
Some("
|
|
190
|
-
Some("
|
|
191
|
-
Some("
|
|
192
|
-
Some("
|
|
210
|
+
Some("stuck-list") => "usage: team-agent stuck-list [--workspace WORKSPACE] [--json]".to_string(),
|
|
211
|
+
Some("stuck-cancel") => "usage: team-agent stuck-cancel AGENT [--workspace WORKSPACE] [--alert-type stuck|idle_fallback|cross_worker_deadlock|all] [--json]".to_string(),
|
|
212
|
+
Some("acknowledge-idle") => "usage: team-agent acknowledge-idle [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
213
|
+
Some("takeover") => "usage: team-agent takeover [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
214
|
+
Some("claim-leader") => "usage: team-agent claim-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
215
|
+
Some("attach-leader") => "usage: team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
216
|
+
Some("identity") => "usage: team-agent identity [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
217
|
+
Some("approvals") => "usage: team-agent approvals [AGENT] [--workspace WORKSPACE] [--json]".to_string(),
|
|
218
|
+
Some("inbox") => "usage: team-agent inbox AGENT [--workspace WORKSPACE] [--limit N] [--since CURSOR] [--json]".to_string(),
|
|
219
|
+
Some("doctor") => "usage: team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--gate orphans|comms] [--comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]".to_string(),
|
|
220
|
+
Some("watch") => "usage: team-agent watch [--workspace WORKSPACE] [--team TEAM]".to_string(),
|
|
221
|
+
Some("sessions") => "usage: team-agent sessions [--workspace WORKSPACE] [--json]".to_string(),
|
|
222
|
+
Some("validate") => "usage: team-agent validate [SPEC] [--json]".to_string(),
|
|
223
|
+
Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--json]".to_string(),
|
|
224
|
+
Some("validate-result") => "usage: team-agent validate-result [ENVELOPE] [--file FILE|--result JSON] [--json]".to_string(),
|
|
225
|
+
Some("collect") => "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]".to_string(),
|
|
226
|
+
Some("settle") => "usage: team-agent settle [--workspace WORKSPACE] [--json]".to_string(),
|
|
227
|
+
Some("repair-state") => "usage: team-agent repair-state --task TASK --status STATUS [SUMMARY] [--assignee AGENT] [--workspace WORKSPACE] [--json]".to_string(),
|
|
228
|
+
Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--json]".to_string(),
|
|
229
|
+
Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
|
|
230
|
+
Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--timeout SECONDS] [--json]".to_string(),
|
|
231
|
+
Some("e2e") => "usage: team-agent e2e [--workspace WORKSPACE] [--providers LIST] [--real] [--json]".to_string(),
|
|
232
|
+
Some("peek") => "usage: team-agent peek AGENT [--workspace WORKSPACE] [--tail N] [--allow-raw-screen] [--json]".to_string(),
|
|
233
|
+
Some("coordinator") => "usage: team-agent coordinator [--workspace WORKSPACE] [--once] [--tick-interval SECONDS]".to_string(),
|
|
193
234
|
Some(other) => format!("usage: team-agent {other} [options]"),
|
|
194
235
|
}
|
|
195
236
|
}
|
|
@@ -524,6 +565,7 @@ fn quick_start_args(args: &[String], cwd: &Path) -> Result<QuickStartArgs, CliEr
|
|
|
524
565
|
workspace.join(agents_dir)
|
|
525
566
|
};
|
|
526
567
|
Ok(QuickStartArgs {
|
|
568
|
+
workspace,
|
|
527
569
|
agents_dir,
|
|
528
570
|
name: parsed.name,
|
|
529
571
|
team_id: parsed.team_id.or(parsed.team),
|
|
@@ -1056,6 +1098,92 @@ mod tests {
|
|
|
1056
1098
|
items.iter().map(|s| (*s).to_string()).collect()
|
|
1057
1099
|
}
|
|
1058
1100
|
|
|
1101
|
+
fn source_dispatch_commands() -> Vec<&'static str> {
|
|
1102
|
+
let source = include_str!("emit.rs");
|
|
1103
|
+
let after_start = source.split_once("fn dispatch(").unwrap().1;
|
|
1104
|
+
let dispatch_source = after_start.split_once("const DISPATCH_COMMANDS").unwrap().0;
|
|
1105
|
+
let mut commands = Vec::new();
|
|
1106
|
+
for line in dispatch_source.lines() {
|
|
1107
|
+
let line = line.trim_start();
|
|
1108
|
+
let Some(rest) = line.strip_prefix('"') else {
|
|
1109
|
+
continue;
|
|
1110
|
+
};
|
|
1111
|
+
let Some((command, after_command)) = rest.split_once('"') else {
|
|
1112
|
+
continue;
|
|
1113
|
+
};
|
|
1114
|
+
let after_command = after_command.trim_start();
|
|
1115
|
+
if (after_command.starts_with("=>") || after_command.starts_with("if "))
|
|
1116
|
+
&& !commands.contains(&command)
|
|
1117
|
+
{
|
|
1118
|
+
commands.push(command);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
commands
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
#[test]
|
|
1125
|
+
fn t0_help_catalog_tracks_dispatch_commands() {
|
|
1126
|
+
let source_commands = source_dispatch_commands();
|
|
1127
|
+
for command in &source_commands {
|
|
1128
|
+
assert!(
|
|
1129
|
+
DISPATCH_COMMANDS.contains(command),
|
|
1130
|
+
"dispatch command `{command}` is missing from DISPATCH_COMMANDS"
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
for command in DISPATCH_COMMANDS {
|
|
1134
|
+
assert!(
|
|
1135
|
+
source_commands.contains(command),
|
|
1136
|
+
"DISPATCH_COMMANDS contains `{command}` but dispatch has no matching arm"
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
let top_help = command_help(None);
|
|
1141
|
+
for command in DISPATCH_COMMANDS {
|
|
1142
|
+
assert!(
|
|
1143
|
+
top_help.contains(command),
|
|
1144
|
+
"top-level --help is missing dispatch command `{command}`"
|
|
1145
|
+
);
|
|
1146
|
+
let command_help = command_help(Some(command));
|
|
1147
|
+
assert!(
|
|
1148
|
+
command_help.contains("usage: team-agent") && command_help.contains(command),
|
|
1149
|
+
"`team-agent {command} --help` must show command-specific usage, got {command_help:?}"
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
for command in SPEC_ONLY_HELP_COMMANDS {
|
|
1153
|
+
assert!(
|
|
1154
|
+
top_help.contains(command),
|
|
1155
|
+
"top-level --help is missing spec-only help command `{command}`"
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
#[test]
|
|
1161
|
+
fn t0_help_catalog_lists_command_flags() {
|
|
1162
|
+
for (command, flags) in [
|
|
1163
|
+
("quick-start", &["--workspace", "--team-id", "--yes", "--fresh", "--json"][..]),
|
|
1164
|
+
("send", &["--workspace", "--team", "--targets", "--watch-result", "--timeout", "--json"][..]),
|
|
1165
|
+
("status", &["--workspace", "--team", "--summary", "--json", "--detail"][..]),
|
|
1166
|
+
("shutdown", &["--workspace", "--team", "--keep-logs", "--json"][..]),
|
|
1167
|
+
("restart", &["--team", "--allow-fresh", "--json"][..]),
|
|
1168
|
+
("start-agent", &["--workspace", "--team", "--force", "--allow-fresh", "--no-display", "--json"][..]),
|
|
1169
|
+
("reset-agent", &["--workspace", "--team", "--discard-session", "--no-display", "--json"][..]),
|
|
1170
|
+
("add-agent", &["--role-file", "--workspace", "--team", "--no-display", "--json"][..]),
|
|
1171
|
+
("fork-agent", &["--as", "--label", "--workspace", "--team", "--no-display", "--json"][..]),
|
|
1172
|
+
("remove-agent", &["--workspace", "--team", "--from-spec", "--confirm", "--force", "--json"][..]),
|
|
1173
|
+
("doctor", &["--workspace", "--team", "--gate", "--fix-schema", "--cleanup-orphans", "--json"][..]),
|
|
1174
|
+
("collect", &["--workspace", "--team", "--result-file", "--json"][..]),
|
|
1175
|
+
("repair-state", &["--task", "--status", "--assignee", "--workspace", "--json"][..]),
|
|
1176
|
+
("wait-ready", &["--workspace", "--timeout", "--json"][..]),
|
|
1177
|
+
("peek", &["--workspace", "--tail", "--allow-raw-screen", "--json"][..]),
|
|
1178
|
+
("coordinator", &["--workspace", "--once", "--tick-interval"][..]),
|
|
1179
|
+
] {
|
|
1180
|
+
let help = command_help(Some(command));
|
|
1181
|
+
for flag in flags {
|
|
1182
|
+
assert!(help.contains(flag), "`team-agent {command} --help` is missing {flag}");
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1059
1187
|
#[test]
|
|
1060
1188
|
fn ux_quick_start_workspace_resolves_relative_agents_dir_inside_workspace() {
|
|
1061
1189
|
let cwd = tmp_workspace();
|