@team-agent/installer 0.5.99 → 0.5.100

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 CHANGED
@@ -581,7 +581,7 @@ dependencies = [
581
581
 
582
582
  [[package]]
583
583
  name = "team-agent"
584
- version = "0.5.99"
584
+ version = "0.5.100"
585
585
  dependencies = [
586
586
  "anyhow",
587
587
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-trans
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.5.99"
12
+ version = "0.5.100"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -6,100 +6,6 @@
6
6
  use super::*;
7
7
  use crate::transport::Transport;
8
8
 
9
- const INIT_SPEC_TEMPLATE: &str = include_str!("../model/testdata/team.spec.yaml");
10
- const INIT_STATE_TEMPLATE: &str = r#"# Team State
11
-
12
- Updated: not launched
13
-
14
- ## Objective
15
-
16
- Pending.
17
-
18
- ## Team
19
-
20
- - Name: pending
21
- - Runtime session: pending
22
-
23
- ## Agents
24
-
25
- - Pending launch.
26
-
27
- ## Task Graph
28
-
29
- - Pending task graph.
30
-
31
- ## Latest Results
32
-
33
- - None.
34
-
35
- ## Blockers
36
-
37
- - None.
38
-
39
- ## Next Step
40
-
41
- - Run `team-agent validate team.spec.yaml`, review permissions, then run `team-agent launch team.spec.yaml --yes`.
42
- "#;
43
-
44
- pub fn cmd_init(args: &InitArgs) -> Result<CmdResult, CliError> {
45
- let team_root = args.workspace.join(".team");
46
- let spec_path = team_root.join("current").join("team.spec.yaml");
47
- let state_path = args.workspace.join("team_state.md");
48
- let team_md_path = args.workspace.join("TEAM.md");
49
- let agents_dir = args.workspace.join("agents");
50
- let default_agent_path = agents_dir.join("worker.md");
51
- if spec_path.exists() && !args.force {
52
- return Err(CliError::Runtime(format!(
53
- "{} already exists; pass --force to overwrite",
54
- spec_path.display()
55
- )));
56
- }
57
- for dir in [
58
- team_root.clone(),
59
- team_root.join("current"),
60
- team_root.join("runtime"),
61
- team_root.join("logs"),
62
- team_root.join("messages"),
63
- team_root.join("artifacts"),
64
- agents_dir.clone(),
65
- ] {
66
- std::fs::create_dir_all(&dir)?;
67
- }
68
- std::fs::write(&spec_path, INIT_SPEC_TEMPLATE)?;
69
- if args.force || !team_md_path.exists() {
70
- std::fs::write(
71
- &team_md_path,
72
- "---\nname: current\nobjective: Pending.\nprovider: fake\n---\n\nPending.\n",
73
- )?;
74
- }
75
- if args.force || !default_agent_path.exists() {
76
- std::fs::write(
77
- &default_agent_path,
78
- "---\nname: worker\nrole: Worker\nprovider: fake\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWait for instructions.\n",
79
- )?;
80
- }
81
- if args.force || !state_path.exists() {
82
- std::fs::write(&state_path, INIT_STATE_TEMPLATE)?;
83
- }
84
- crate::event_log::EventLog::new(&args.workspace)
85
- .write(
86
- "init",
87
- json!({
88
- "spec_path": spec_path.to_string_lossy().to_string(),
89
- "state_path": state_path.to_string_lossy().to_string(),
90
- }),
91
- )
92
- .map_err(|e| CliError::Runtime(e.to_string()))?;
93
- Ok(CmdResult::from_json(
94
- json!({
95
- "ok": true,
96
- "spec": spec_path.to_string_lossy().to_string(),
97
- "state": state_path.to_string_lossy().to_string(),
98
- }),
99
- args.json,
100
- ))
101
- }
102
-
103
9
  /// `cmd_quick_start`(`commands.py:18`)。`--json` 或 `!ok` → 整 dict;否则 `result["summary"]`。
104
10
  pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
105
11
  let mut value = lifecycle_port::quick_start(
@@ -437,24 +343,6 @@ pub fn cmd_allow_peer_talk(args: &AllowPeerTalkArgs) -> Result<CmdResult, CliErr
437
343
  Ok(CmdResult::from_json(value, args.json))
438
344
  }
439
345
 
440
- /// `diagnose` is the permanent compatibility spelling for `doctor`.
441
- /// Keep this wrapper free of probing/reporting logic so direct Rust callers and
442
- /// the CLI both converge on the canonical handler.
443
- pub fn cmd_diagnose(args: &DiagnoseArgs) -> Result<CmdResult, CliError> {
444
- cmd_doctor(&DoctorArgs {
445
- spec: None,
446
- workspace: args.workspace.clone(),
447
- gate: None,
448
- comms: false,
449
- team: args.team.clone(),
450
- fix: false,
451
- fix_schema: false,
452
- cleanup_orphans: false,
453
- confirm: false,
454
- json: args.json,
455
- })
456
- }
457
-
458
346
  /// `cmd_preflight`(`parser.py:160`)。
459
347
  pub fn cmd_preflight(args: &PreflightArgs) -> Result<CmdResult, CliError> {
460
348
  let report = build_preflight_report(&args.team)?;
@@ -1309,50 +1197,7 @@ pub fn cmd_remove_agent(args: &RemoveAgentArgs) -> Result<CmdResult, CliError> {
1309
1197
  ))
1310
1198
  }
1311
1199
 
1312
- /// `cmd_stuck_list`(`commands.py:405`)。REUSE `messaging::stuck_list`。
1313
- pub fn cmd_stuck_list(args: &StuckListArgs) -> Result<CmdResult, CliError> {
1314
- let selected = crate::state::selector::resolve_active_team(
1315
- &args.workspace,
1316
- args.team.as_deref(),
1317
- crate::state::selector::SelectorMode::RuntimeOnly,
1318
- )
1319
- .map_err(|e| CliError::Runtime(e.to_string()))?;
1320
- let suppressed = selected
1321
- .state
1322
- .get("coordinator")
1323
- .and_then(|v| v.get("suppressed_idle_alerts"))
1324
- .and_then(|v| v.get(&selected.team_key))
1325
- .cloned()
1326
- .unwrap_or_else(|| json!({}));
1327
- Ok(CmdResult::from_json(
1328
- json!({"ok": true, "suppressed_idle_alerts": suppressed}),
1329
- args.json,
1330
- ))
1331
- }
1332
-
1333
- /// `cmd_stuck_cancel`(`commands.py:409`)。REUSE `messaging::stuck_cancel`(suppressed_by="leader")。
1334
- pub fn cmd_stuck_cancel(args: &StuckCancelArgs) -> Result<CmdResult, CliError> {
1335
- if args.team.is_some() {
1336
- return Err(CliError::Usage(
1337
- "stuck-cancel --team is not supported yet because stuck suppression storage is not team-scoped".to_string(),
1338
- ));
1339
- }
1340
- Ok(CmdResult::from_json(
1341
- messaging::stuck_cancel(&args.workspace, &args.agent, args.alert_type, "leader")?,
1342
- args.json,
1343
- ))
1344
- }
1345
-
1346
- /// `cmd_acknowledge_idle`(`commands.py:418`)。
1347
- pub fn cmd_acknowledge_idle(args: &AcknowledgeIdleArgs) -> Result<CmdResult, CliError> {
1348
- Ok(CmdResult::from_json(
1349
- lifecycle_port::acknowledge_idle(&args.workspace, args.team.as_deref())?,
1350
- args.json,
1351
- ))
1352
- }
1353
-
1354
- /// `doctor` is the single health/diagnostic pipeline. `diagnose` enters here
1355
- /// through the compatibility wrapper above; it never gets a second facts engine.
1200
+ /// `doctor` is the single health/diagnostic pipeline.
1356
1201
  pub fn cmd_doctor(args: &DoctorArgs) -> Result<CmdResult, CliError> {
1357
1202
  if let Some(DoctorGate::Unknown(raw)) = &args.gate {
1358
1203
  let value = json!({
@@ -144,7 +144,7 @@ pub(crate) fn diagnose_runtime(state: &Value, backend: &dyn Transport) -> (Value
144
144
  let error_str = error.to_string();
145
145
  let issue_id = classify_tmux_server_error(&error_str);
146
146
  issues.push(json!(issue_id));
147
- let mut hint = recovery_hint(session_name, issue_id, "team-agent diagnose");
147
+ let mut hint = recovery_hint(session_name, issue_id, "team-agent doctor");
148
148
  if let Some(obj) = hint.as_object_mut() {
149
149
  obj.insert("reason".to_string(), Value::String(error_str));
150
150
  }
@@ -947,7 +947,7 @@ fn topology_repair_hint(issue: &str) -> Value {
947
947
  "action_required": true,
948
948
  "advisory": true,
949
949
  "broken_class": issue,
950
- "hint_action": "team-agent diagnose --json",
950
+ "hint_action": "team-agent doctor --json",
951
951
  "dedupe_key": issue,
952
952
  "action": "repair the tmux topology mismatch, then rerun team-agent restart",
953
953
  })
@@ -1605,7 +1605,7 @@ pub(crate) fn build_wait_ready_report(
1605
1605
  "workers_not_ready",
1606
1606
  "workers not ready before timeout",
1607
1607
  vec![json!(
1608
- "inspect team-agent diagnose output and worker terminals"
1608
+ "inspect team-agent doctor output and worker terminals"
1609
1609
  )],
1610
1610
  )
1611
1611
  };
@@ -2,8 +2,10 @@
2
2
  //! cli · emit — `emit`(--json vs 人读 dict 逐键)+ 顶层 `run` 调度(parser.py `main`)+
3
3
  //! 人读标量/集合渲染(`human_value` / `json_dumps_like`)。
4
4
 
5
- use super::spec::{command_spec, CommandKind, CommandTier, ALL_DISPATCH_KINDS, COMMAND_SPECS};
6
5
  use super::models::cmd_models;
6
+ use super::spec::{command_spec, CommandKind, COMMAND_SPECS};
7
+ #[cfg(test)]
8
+ use super::spec::{CommandTier, ALL_DISPATCH_KINDS};
7
9
  use super::*;
8
10
  use std::io::{ErrorKind, Write as _};
9
11
 
@@ -171,16 +173,11 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
171
173
  };
172
174
  match spec.kind {
173
175
  CommandKind::Dispatch(_) => {}
174
- CommandKind::SpecOnlyAlias { .. } => {
175
- eprintln!("{}", command_help(Some(command)));
176
- return Ok(ExitCode::Usage);
177
- }
178
176
  CommandKind::LeaderPassthrough { .. } => {
179
177
  return Ok(emit_unknown_subcommand_usage(command));
180
178
  }
181
179
  }
182
180
  match command {
183
- "init" => cmd_init(&init_args(args, cwd)).map(emit_result),
184
181
  "quick-start" => cmd_quick_start(&quick_start_args(args, cwd)?).map(emit_result),
185
182
  "send" => cmd_send(&send_args(args, cwd)?).map(emit_result),
186
183
  "allow-peer-talk" => {
@@ -188,10 +185,8 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
188
185
  }
189
186
  "status" => cmd_status_for_team(&status_args(args, cwd), parse_args(args).team.as_deref())
190
187
  .map(emit_result),
191
- "stop" => cmd_shutdown(&shutdown_args(args, cwd)?).map(emit_result),
192
188
  "shutdown" => cmd_shutdown(&shutdown_args(args, cwd)?).map(emit_result),
193
189
  "restart" => cmd_restart(&restart_args(args, cwd)?).map(emit_result),
194
- "restart-agent" => cmd_reset_agent(&reset_agent_args(args, cwd)?).map(emit_result),
195
190
  "start-agent" => cmd_start_agent(&start_agent_args(args, cwd)?).map(emit_result),
196
191
  "stop-agent" => cmd_stop_agent(&stop_agent_args(args, cwd)?).map(emit_result),
197
192
  "reset-agent" => cmd_reset_agent(&reset_agent_args(args, cwd)?).map(emit_result),
@@ -199,11 +194,6 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
199
194
  "clone-agent" => cmd_clone_agent(&clone_agent_args(args, cwd)?).map(emit_result),
200
195
  "fork-agent" => cmd_fork_agent(&fork_agent_args(args, cwd)?).map(emit_result),
201
196
  "remove-agent" => cmd_remove_agent(&remove_agent_args(args, cwd)?).map(emit_result),
202
- "stuck-list" => cmd_stuck_list(&stuck_list_args(args, cwd)).map(emit_result),
203
- "stuck-cancel" => cmd_stuck_cancel(&stuck_cancel_args(args, cwd)?).map(emit_result),
204
- "acknowledge-idle" => {
205
- cmd_acknowledge_idle(&acknowledge_idle_args(args, cwd)).map(emit_result)
206
- }
207
197
  "takeover" => cmd_takeover(&takeover_args(args, cwd)).map(emit_result),
208
198
  "claim-leader" => cmd_claim_leader(&claim_leader_args(args, cwd)).map(emit_result),
209
199
  // Real dispatch: `cmd_attach_leader` writes the `leader_receiver` binding.
@@ -232,8 +222,6 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
232
222
  "profile" => cmd_profile(&profile_args(args, cwd)?).map(emit_result),
233
223
  "results" => cmd_results(&results_args(args, cwd)?).map(emit_result),
234
224
  "wait" => cmd_wait(&wait_args(args, cwd)?).map(emit_result),
235
- // Compatibility spelling: parse and execute the exact doctor contract.
236
- "diagnose" => cmd_doctor(&doctor_args(args, cwd)).map(emit_result),
237
225
  "preflight" => cmd_preflight(&preflight_args(args, cwd)).map(emit_result),
238
226
  "wait-ready" => cmd_wait_ready(&wait_ready_args(args, cwd)).map(emit_result),
239
227
  "e2e" => cmd_e2e(&e2e_args(args, cwd)).map(emit_result),
@@ -243,58 +231,6 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
243
231
  }
244
232
  }
245
233
 
246
- const DISPATCH_COMMANDS: &[&str] = &[
247
- "init",
248
- "quick-start",
249
- "send",
250
- "allow-peer-talk",
251
- "status",
252
- "stop",
253
- "shutdown",
254
- "restart",
255
- "restart-agent",
256
- "start-agent",
257
- "stop-agent",
258
- "reset-agent",
259
- "add-agent",
260
- "fork-agent",
261
- "remove-agent",
262
- "stuck-list",
263
- "stuck-cancel",
264
- "acknowledge-idle",
265
- "takeover",
266
- "claim-leader",
267
- "attach-leader",
268
- "attach-app-server-leader",
269
- "identity",
270
- "approvals",
271
- "inbox",
272
- "doctor",
273
- "watch",
274
- "sessions",
275
- // 0.5.9 E7: host leader discovery command surface.
276
- "leaders",
277
- "models",
278
- "validate",
279
- "install-skill",
280
- "profile",
281
- "results",
282
- "wait",
283
- "diagnose",
284
- "preflight",
285
- "wait-ready",
286
- "e2e",
287
- "peek",
288
- "coordinator",
289
- ];
290
-
291
- // 0.5.26 (`.team/artifacts/stale-team-saveconflict-locate.md` §7.6):
292
- // `purge-agent` was previously listed in help but had no dispatch arm,
293
- // so it read as a supported recovery command while actually failing with
294
- // "invalid choice". The dispatch registration remains out of scope for
295
- // 0.5.26 (destructive semantics deserve their own CR); keep the help
296
- // consistent with the dispatch table so it is no longer advertised.
297
- const SPEC_ONLY_HELP_COMMANDS: &[&str] = &["start"];
298
234
  // Command grammar, not provider identity parsing: these are top-level CLI
299
235
  // passthrough verbs for starting a leader under a provider executable.
300
236
  const LEADER_PASSTHROUGH_COMMANDS: &[&str] =
@@ -370,18 +306,6 @@ fn append_help_section(out: &mut String, title: &str, names: &[&str]) {
370
306
  }
371
307
  }
372
308
 
373
- fn compat_hidden_help(command: &str, usage: &str) -> String {
374
- let Some(spec) = command_spec(command) else {
375
- return usage.to_string();
376
- };
377
- if spec.tier != CommandTier::CompatHidden {
378
- return usage.to_string();
379
- }
380
- let sunset = spec.sunset.unwrap_or("C2");
381
- let action = spec.action.unwrap_or("use a supported command");
382
- format!("{usage}\n\nstatus: hidden compatibility command\nsunset: {sunset}\naction: {action}")
383
- }
384
-
385
309
  ///
386
310
  /// Test-only public accessor for `command_help` — allows integration
387
311
  /// tests to grep the help copy without depending on internal parser
@@ -404,9 +328,7 @@ pub fn __test_quick_start_args(
404
328
  fn command_help(command: Option<&str>) -> String {
405
329
  match command {
406
330
  None => default_help(),
407
- Some("init") => compat_hidden_help("init", "usage: team-agent init [--workspace WORKSPACE] [--force] [--json]"),
408
331
  Some("quick-start") => "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--backend tmux|conpty] [--json] [--detail]\n\n--backend selects the worker transport: tmux (default on POSIX) or conpty (Windows-native ConPTY worker transport; requires the shim binary and Windows host).\n\n--detail includes internal receiver/topology diagnostics in JSON output.\n\nAfter a successful start, use the returned `send_commands` (or choose an agent explicitly) with `team-agent send AGENT MESSAGE`.".to_string(),
409
- Some("start") => compat_hidden_help("start", "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]"),
410
332
  Some("send") => concat!(
411
333
  "usage: team-agent send TO MESSAGE... ",
412
334
  "[--workspace WORKSPACE] [--team TEAM] ",
@@ -420,10 +342,8 @@ fn command_help(command: Option<&str>) -> String {
420
342
  Some("status") => "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]\n\n输出七字段:name/provider/runtime_status/activity/health/session_name/tmux_command;人读与 --json 使用同一投影。缺少可靠定位或原生 tmux/process 采样时显示 unknown;tmux_command 可复制到对应目标。--summary/--detail 仅保留兼容性,不增加诊断字段。".to_string(),
421
343
  Some("models") => "usage: team-agent models [--provider pi|cursor_agent] [QUERY|--search TEXT] [--json]\n\nLists exact provider model ids with case-insensitive multi-word search across provider, vendor, id, and display name. Cursor uses the local `agent --list-models` catalog.".to_string(),
422
344
  Some("leaders") => "usage: team-agent leaders [QUERY|--search TEXT] [--all|--stale] [--json] | --prune [--dry-run] [--json]\n\nLists LIVE leaders by default. Use --all to include retained STALE entries, --stale to inspect only STALE entries, QUERY or --search TEXT to match workspace/team/name fields, and --prune to remove only entries proven terminal by canonical state. --dry-run is valid only with --prune.".to_string(),
423
- Some("stop") => compat_hidden_help("stop", "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]"),
424
345
  Some("shutdown") => "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
425
346
  Some("restart") => "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json] [--detail]".to_string(),
426
- Some("restart-agent") => compat_hidden_help("restart-agent", "usage: team-agent restart-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--json]"),
427
347
  Some("reset-agent") => "usage: team-agent reset-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--json]".to_string(),
428
348
  Some("start-agent") => "usage: team-agent start-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--allow-fresh] [--json]\n\nAfter a successful start, use the returned `send_commands` with `team-agent send AGENT MESSAGE`.".to_string(),
429
349
  Some("stop-agent") => "usage: team-agent stop-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
@@ -432,9 +352,6 @@ fn command_help(command: Option<&str>) -> String {
432
352
  Some("fork-agent") => "usage: team-agent fork-agent SOURCE_AGENT --as AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
433
353
  Some("remove-agent") => "usage: team-agent remove-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--from-spec] [--confirm] [--force] [--json]".to_string(),
434
354
  // 0.5.26 (§7.6): removed from help; dispatch was never wired.
435
- Some("stuck-list") => "usage: team-agent stuck-list [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
436
- Some("stuck-cancel") => "usage: team-agent stuck-cancel AGENT [--workspace WORKSPACE] [--alert-type stuck|idle_fallback|cross_worker_deadlock|all] [--json]".to_string(),
437
- Some("acknowledge-idle") => "usage: team-agent acknowledge-idle [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
438
355
  Some("takeover") => "usage: team-agent takeover [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
439
356
  Some("claim-leader") => "usage: team-agent claim-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json] [--detail]".to_string(),
440
357
  Some("attach-leader") => "usage: team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] [--pane PANE] [--provider PROVIDER] [--confirm] [--json]".to_string(),
@@ -450,7 +367,6 @@ fn command_help(command: Option<&str>) -> String {
450
367
  Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--proxy-mode direct|inherit] [--json]".to_string(),
451
368
  Some("results") => "usage: team-agent results --case CASE_ID [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
452
369
  Some("wait") => "usage: team-agent wait --task TASK [--workspace WORKSPACE] [--json]".to_string(),
453
- Some("diagnose") => "usage: team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--gate orphans|comms] [--comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]".to_string(),
454
370
  Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
455
371
  Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--team TEAM] [--timeout SECONDS] [--json]".to_string(),
456
372
  Some("e2e") => "usage: team-agent e2e [--workspace WORKSPACE] [--providers LIST] [--real] [--json]".to_string(),
@@ -838,7 +754,6 @@ struct ParsedArgs {
838
754
  label: Option<String>,
839
755
  from_spec: bool,
840
756
  confirm: bool,
841
- alert_type: Option<String>,
842
757
  limit: Option<usize>,
843
758
  gate: Option<String>,
844
759
  comms: bool,
@@ -929,7 +844,6 @@ fn parse_args(args: &[String]) -> ParsedArgs {
929
844
  "--label" => parsed.label = next_arg(args, &mut i),
930
845
  "--from-spec" => parsed.from_spec = true,
931
846
  "--confirm" => parsed.confirm = true,
932
- "--alert-type" => parsed.alert_type = next_arg(args, &mut i),
933
847
  "-n" | "--limit" => {
934
848
  parsed.limit = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok())
935
849
  }
@@ -1136,15 +1050,6 @@ fn models_args(args: &[String]) -> Result<ModelsArgs, CliError> {
1136
1050
  })
1137
1051
  }
1138
1052
 
1139
- fn init_args(args: &[String], cwd: &Path) -> InitArgs {
1140
- let parsed = parse_args(args);
1141
- InitArgs {
1142
- workspace: workspace(&parsed, cwd),
1143
- force: parsed.force,
1144
- json: parsed.json,
1145
- }
1146
- }
1147
-
1148
1053
  fn resolve_cli_path(cwd: &Path, path: &Path) -> PathBuf {
1149
1054
  if path.is_absolute() {
1150
1055
  path.to_path_buf()
@@ -1616,48 +1521,6 @@ fn remove_agent_args(args: &[String], cwd: &Path) -> Result<RemoveAgentArgs, Cli
1616
1521
  })
1617
1522
  }
1618
1523
 
1619
- fn stuck_list_args(args: &[String], cwd: &Path) -> StuckListArgs {
1620
- let parsed = parse_args(args);
1621
- StuckListArgs {
1622
- workspace: workspace(&parsed, cwd),
1623
- json: parsed.json,
1624
- team: parsed.team,
1625
- }
1626
- }
1627
-
1628
- fn stuck_cancel_args(args: &[String], cwd: &Path) -> Result<StuckCancelArgs, CliError> {
1629
- let parsed = parse_args(args);
1630
- let workspace = workspace(&parsed, cwd);
1631
- refuse_if_multi_alive_team_missing_scope("stuck-cancel", &workspace, parsed.team.as_deref())?;
1632
- Ok(StuckCancelArgs {
1633
- agent: required_pos(&parsed, 0, "agent")?,
1634
- workspace,
1635
- alert_type: alert_type(parsed.alert_type.as_deref())?,
1636
- json: parsed.json,
1637
- team: parsed.team,
1638
- })
1639
- }
1640
-
1641
- fn alert_type(raw: Option<&str>) -> Result<Option<AlertType>, CliError> {
1642
- match raw {
1643
- Some("stuck") => Ok(Some(AlertType::Stuck)),
1644
- Some("idle_fallback") => Ok(Some(AlertType::IdleFallback)),
1645
- Some("cross_worker_deadlock") => Ok(Some(AlertType::CrossWorkerDeadlock)),
1646
- Some("all") | None => Ok(None),
1647
- Some(other) => Err(CliError::Usage(format!("invalid --alert-type: {other}"))),
1648
- }
1649
- }
1650
-
1651
- fn acknowledge_idle_args(args: &[String], cwd: &Path) -> AcknowledgeIdleArgs {
1652
- let parsed = parse_args(args);
1653
- let workspace = workspace(&parsed, cwd);
1654
- AcknowledgeIdleArgs {
1655
- team: parsed.team,
1656
- workspace,
1657
- json: parsed.json,
1658
- }
1659
- }
1660
-
1661
1524
  fn doctor_args(args: &[String], cwd: &Path) -> DoctorArgs {
1662
1525
  let parsed = parse_args(args);
1663
1526
  DoctorArgs {
@@ -1863,15 +1726,6 @@ fn option_value(args: &[String], flag: &str) -> Option<String> {
1863
1726
  None
1864
1727
  }
1865
1728
 
1866
- fn diagnose_args(args: &[String], cwd: &Path) -> DiagnoseArgs {
1867
- let parsed = parse_args(args);
1868
- DiagnoseArgs {
1869
- workspace: workspace(&parsed, cwd),
1870
- json: parsed.json,
1871
- team: parsed.team,
1872
- }
1873
- }
1874
-
1875
1729
  fn preflight_args(args: &[String], cwd: &Path) -> PreflightArgs {
1876
1730
  let parsed = parse_args(args);
1877
1731
  let team = parsed
@@ -2064,7 +1918,7 @@ mod tests {
2064
1918
  }
2065
1919
 
2066
1920
  #[test]
2067
- fn command_specs_have_unique_names_and_valid_aliases() {
1921
+ fn command_specs_have_unique_names() {
2068
1922
  let mut names = std::collections::BTreeSet::new();
2069
1923
  for spec in COMMAND_SPECS {
2070
1924
  assert!(
@@ -2073,15 +1927,6 @@ mod tests {
2073
1927
  spec.name
2074
1928
  );
2075
1929
  }
2076
- for spec in COMMAND_SPECS {
2077
- if let Some(alias_of) = spec.alias_of {
2078
- assert!(
2079
- names.contains(alias_of),
2080
- "`{}` aliases missing command `{alias_of}`",
2081
- spec.name
2082
- );
2083
- }
2084
- }
2085
1930
  }
2086
1931
 
2087
1932
  #[test]
@@ -2152,7 +1997,7 @@ mod tests {
2152
1997
  let top_help = command_help(None);
2153
1998
  let visible = visible_help_commands(&top_help);
2154
1999
  assert!(visible.iter().any(|command| command == "doctor"));
2155
- for command in ["leaders", "diagnose", "e2e", "peek", "coordinator"] {
2000
+ for command in ["leaders", "e2e", "peek", "coordinator"] {
2156
2001
  assert!(
2157
2002
  !visible.iter().any(|visible| visible == command),
2158
2003
  "`{command}` must stay hidden from default help"
@@ -2170,16 +2015,6 @@ mod tests {
2170
2015
  assert!(help.contains("--dry-run"));
2171
2016
  }
2172
2017
 
2173
- #[test]
2174
- fn compat_hidden_help_has_sunset_action() {
2175
- for command in ["stop", "restart-agent", "start", "init"] {
2176
- let help = command_help(Some(command)).to_lowercase();
2177
- assert!(help.contains("status: hidden compatibility command"));
2178
- assert!(help.contains("sunset: c2"));
2179
- assert!(help.contains("action:"));
2180
- }
2181
- }
2182
-
2183
2018
  #[test]
2184
2019
  fn observation_a_commands_have_terminal_tiers() {
2185
2020
  for (command, tier) in [
@@ -2187,7 +2022,6 @@ mod tests {
2187
2022
  ("approvals", CommandTier::Secondary),
2188
2023
  ("profile", CommandTier::Secondary),
2189
2024
  ("install-skill", CommandTier::Secondary),
2190
- ("init", CommandTier::CompatHidden),
2191
2025
  ] {
2192
2026
  assert_eq!(command_spec(command).map(|spec| spec.tier), Some(tier));
2193
2027
  }
@@ -2309,14 +2143,13 @@ mod tests {
2309
2143
  "--json",
2310
2144
  ][..],
2311
2145
  ),
2312
- ("stuck-list", &["--workspace", "--team", "--json"][..]),
2146
+ ("stop-agent", &["--workspace", "--team", "--json"][..]),
2313
2147
  ("approvals", &["--workspace", "--team", "--json"][..]),
2314
2148
  (
2315
2149
  "inbox",
2316
2150
  &["--workspace", "--team", "-n", "--limit", "--json"][..],
2317
2151
  ),
2318
2152
  ("sessions", &["--workspace", "--team", "--json"][..]),
2319
- ("diagnose", &["--workspace", "--team", "--json"][..]),
2320
2153
  (
2321
2154
  "wait-ready",
2322
2155
  &["--workspace", "--team", "--timeout", "--json"][..],
@@ -2621,18 +2454,6 @@ mod tests {
2621
2454
  );
2622
2455
  }
2623
2456
 
2624
- #[test]
2625
- fn stuck_cancel_args_builder_refuses_on_multi_alive_team() {
2626
- let ws = tmp_workspace();
2627
- seed_two_alive_teams_in(&ws);
2628
- let argv = cli_argv(&["worker_a", "--workspace", &ws.to_string_lossy()]);
2629
- let err = stuck_cancel_args(&argv, &ws).expect_err("must refuse");
2630
- assert!(
2631
- err.to_string().contains("multiple alive teams"),
2632
- "stuck-cancel args builder must surface the refusal; got: {err}"
2633
- );
2634
- }
2635
-
2636
2457
  #[test]
2637
2458
  fn shutdown_args_builder_refuses_on_multi_alive_team() {
2638
2459
  let ws = tmp_workspace();