@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.
Files changed (39) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +38 -7
  4. package/crates/team-agent/src/cli/emit.rs +182 -54
  5. package/crates/team-agent/src/cli/mod.rs +703 -35
  6. package/crates/team-agent/src/cli/status_port.rs +170 -44
  7. package/crates/team-agent/src/cli/tests/run_delegation.rs +2 -0
  8. package/crates/team-agent/src/cli/types.rs +1 -0
  9. package/crates/team-agent/src/coordinator/health.rs +130 -0
  10. package/crates/team-agent/src/leader/lease.rs +23 -2
  11. package/crates/team-agent/src/leader/rediscover/tests.rs +1 -0
  12. package/crates/team-agent/src/leader/rediscover.rs +2 -0
  13. package/crates/team-agent/src/leader/tests/byte_findings.rs +9 -6
  14. package/crates/team-agent/src/leader/tests/idle.rs +1 -0
  15. package/crates/team-agent/src/leader/tests/lease_claim.rs +157 -0
  16. package/crates/team-agent/src/leader/types.rs +2 -0
  17. package/crates/team-agent/src/lifecycle/launch.rs +554 -65
  18. package/crates/team-agent/src/lifecycle/restart/common.rs +65 -0
  19. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +57 -15
  20. package/crates/team-agent/src/lifecycle/restart/remove.rs +5 -1
  21. package/crates/team-agent/src/lifecycle/restart.rs +20 -0
  22. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +52 -0
  23. package/crates/team-agent/src/lifecycle/types.rs +25 -0
  24. package/crates/team-agent/src/mcp_server/tests/wire.rs +28 -0
  25. package/crates/team-agent/src/mcp_server/wire.rs +81 -1
  26. package/crates/team-agent/src/messaging/delivery.rs +574 -12
  27. package/crates/team-agent/src/messaging/leader_receiver.rs +26 -37
  28. package/crates/team-agent/src/messaging/mod.rs +1 -1
  29. package/crates/team-agent/src/messaging/results.rs +218 -49
  30. package/crates/team-agent/src/messaging/send.rs +15 -19
  31. package/crates/team-agent/src/provider/adapter.rs +95 -10
  32. package/crates/team-agent/src/provider/helpers.rs +10 -1
  33. package/crates/team-agent/src/state/identity.rs +3 -0
  34. package/crates/team-agent/src/state/persist.rs +113 -1
  35. package/crates/team-agent/src/state/projection.rs +127 -3
  36. package/crates/team-agent/src/tmux_backend/tests.rs +179 -0
  37. package/crates/team-agent/src/tmux_backend.rs +124 -12
  38. package/npm/install.mjs +29 -7
  39. package/package.json +4 -4
package/Cargo.lock CHANGED
@@ -555,7 +555,7 @@ dependencies = [
555
555
 
556
556
  [[package]]
557
557
  name = "team-agent"
558
- version = "0.3.0"
558
+ version = "0.3.2"
559
559
  dependencies = [
560
560
  "anyhow",
561
561
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent"]
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.3.0"
12
+ version = "0.3.2"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -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.agents_dir,
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
- None,
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::status(&selected.run_workspace, true, false)?;
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::status(&selected.run_workspace, status_compact_flag(args.detail), args.detail)?;
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::format_status(
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
- None,
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 messaging::collect(&selected.run_workspace, args.result_file.as_deref(), false) {
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" => cmd_status(&status_args(args, cwd)).map(emit_result),
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" => cmd_collect(&collect_args(args, cwd)).map(emit_result),
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
- matches!(
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 => "usage: team-agent <command> [options]\n\nCommands: quick-start, status, send, collect, start, stop, shutdown, restart, restart-agent, start-agent, stop-agent, reset-agent, add-agent, remove-agent, purge-agent".to_string(),
184
- Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--name NAME] [--team-id TEAM] [--yes] [--fresh] [--json]".to_string(),
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("stop") | Some("shutdown") => "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
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("restart") => "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--json]".to_string(),
190
- Some("status") => "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--summary|--json] [--detail]".to_string(),
191
- Some("send") => "usage: team-agent send TARGET MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
192
- Some("collect") => "usage: team-agent collect [--workspace WORKSPACE] [--result-file FILE] [--json]".to_string(),
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();