@team-agent/installer 0.5.111 → 0.5.113

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 (73) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +93 -779
  4. package/crates/team-agent/src/cli/diagnose.rs +2 -424
  5. package/crates/team-agent/src/cli/emit.rs +158 -373
  6. package/crates/team-agent/src/cli/leaders.rs +32 -32
  7. package/crates/team-agent/src/cli/mod.rs +8 -15
  8. package/crates/team-agent/src/cli/named_address.rs +17 -16
  9. package/crates/team-agent/src/cli/route.rs +4 -4
  10. package/crates/team-agent/src/cli/send/presentation.rs +19 -18
  11. package/crates/team-agent/src/cli/send/resolve.rs +11 -11
  12. package/crates/team-agent/src/cli/send.rs +4 -4
  13. package/crates/team-agent/src/cli/spec.rs +29 -32
  14. package/crates/team-agent/src/cli/status.rs +7 -7
  15. package/crates/team-agent/src/cli/tests/base.rs +6 -6
  16. package/crates/team-agent/src/cli/tests/divergence.rs +3 -2
  17. package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
  18. package/crates/team-agent/src/cli/tests/leader_watch.rs +2 -86
  19. package/crates/team-agent/src/cli/tests/main_preserved.rs +15 -140
  20. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +1 -258
  21. package/crates/team-agent/src/cli/tests/mod.rs +0 -2
  22. package/crates/team-agent/src/cli/tests/named_address.rs +3 -3
  23. package/crates/team-agent/src/cli/tests/status_send.rs +7 -7
  24. package/crates/team-agent/src/cli/triage.rs +53 -2
  25. package/crates/team-agent/src/cli/types.rs +12 -116
  26. package/crates/team-agent/src/compiler/tests.rs +12 -0
  27. package/crates/team-agent/src/compiler.rs +0 -9
  28. package/crates/team-agent/src/coordinator/health.rs +3 -283
  29. package/crates/team-agent/src/coordinator/mod.rs +0 -5
  30. package/crates/team-agent/src/coordinator/tests/basics.rs +0 -9
  31. package/crates/team-agent/src/coordinator/tests/mod.rs +0 -1
  32. package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -17
  33. package/crates/team-agent/src/coordinator/types.rs +1 -21
  34. package/crates/team-agent/src/db/message_store.rs +0 -42
  35. package/crates/team-agent/src/db/migration.rs +49 -9
  36. package/crates/team-agent/src/db/schema.rs +27 -20
  37. package/crates/team-agent/src/leader/owner_bind.rs +0 -24
  38. package/crates/team-agent/src/leader/start.rs +5 -0
  39. package/crates/team-agent/src/leader/tests/byte_findings.rs +0 -20
  40. package/crates/team-agent/src/leader/tests/identity.rs +21 -32
  41. package/crates/team-agent/src/leader/tests/lease_claim.rs +0 -55
  42. package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
  43. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +325 -14
  44. package/crates/team-agent/src/lifecycle/launch.rs +2 -2
  45. package/crates/team-agent/src/lifecycle/profile_launch.rs +58 -46
  46. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +97 -52
  47. package/crates/team-agent/src/lifecycle/restart.rs +1 -1
  48. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -51
  49. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -2
  50. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +23 -23
  51. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +29 -55
  52. package/crates/team-agent/src/messaging/mod.rs +0 -2
  53. package/crates/team-agent/src/messaging/results.rs +0 -58
  54. package/crates/team-agent/src/messaging/tests/runtime.rs +0 -46
  55. package/crates/team-agent/src/provider/adapter.rs +8 -59
  56. package/crates/team-agent/src/provider/adapters/claude.rs +6 -14
  57. package/crates/team-agent/src/provider/adapters/codex.rs +8 -18
  58. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -22
  59. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +8 -45
  60. package/crates/team-agent/src/provider/adapters/grok.rs +7 -31
  61. package/crates/team-agent/src/provider/adapters/pi.rs +7 -18
  62. package/crates/team-agent/src/provider/command_helpers.rs +45 -0
  63. package/crates/team-agent/src/provider/mod.rs +1 -0
  64. package/crates/team-agent/src/provider/model_catalog.rs +21 -28
  65. package/crates/team-agent/src/provider/tests/adapter.rs +70 -0
  66. package/crates/team-agent/src/provider/tests/command_helpers.rs +55 -0
  67. package/crates/team-agent/src/state/repository.rs +1 -5
  68. package/package.json +4 -4
  69. package/templates/team_state.md +1 -1
  70. package/crates/team-agent/src/cli/tests/peer_allow.rs +0 -164
  71. package/crates/team-agent/src/cli/tests/verb_validate.rs +0 -195
  72. package/crates/team-agent/src/coordinator/tests/watch.rs +0 -223
  73. package/crates/team-agent/src/messaging/peers.rs +0 -34
@@ -110,25 +110,25 @@ fn emit_cli_error_for_command(
110
110
  cwd: &Path,
111
111
  error: &CliError,
112
112
  ) -> ExitCode {
113
- let missing_input = matches!(error, CliError::Usage(message) if message.starts_with("missing ") || message.starts_with("请填写") || matches!(message.as_str(), "add-agent requires --provider <name>" | "add-agent requires --bypass <true|false>" | "--source is required unless --uninstall"));
113
+ let missing_input = matches!(error, CliError::Usage(message) if message.starts_with("missing ") || message.starts_with("Provide ") || matches!(message.as_str(), "add-agent requires --provider <name>" | "add-agent requires --bypass <true|false>" | "--source is required unless --uninstall"));
114
114
  if command_spec(command).is_some() && missing_input {
115
115
  let help = command_help(Some(command));
116
116
  let explanation = match error {
117
117
  CliError::Usage(message) => match message.as_str() {
118
- "missing agent" | "missing source_agent" => "请填写队友名。".to_string(),
118
+ "missing agent" | "missing source_agent" => "Provide an agent name.".to_string(),
119
119
  "missing profile command" => {
120
- "请填写 profile 操作:init、doctor 或 show。".to_string()
120
+ "Provide a profile operation: init, doctor or show.".to_string()
121
121
  }
122
- "missing profile name" => "请填写登录/代理配置的名称。".to_string(),
122
+ "missing profile name" => "Provide an authentication/proxy profile name.".to_string(),
123
123
  "add-agent requires --provider <name>" => {
124
- "请用 --provider 明确选择工具,或用 --role-file 提供角色文件。".to_string()
124
+ "Select a provider with --provider, or supply a role file with --role-file.".to_string()
125
125
  }
126
126
  "add-agent requires --bypass <true|false>" => {
127
- "请用 --bypass false 明确保留权限询问,或在角色文件中明确此设置。".to_string()
127
+ "Explicitly retain permission prompts with --bypass false, or declare bypass in the role file.".to_string()
128
128
  }
129
129
  other => other
130
130
  .strip_prefix("missing ")
131
- .map(|field| format!("请补齐必需参数:{field}。"))
131
+ .map(|field| format!("Provide the required argument: {field}."))
132
132
  .unwrap_or_else(|| other.to_string()),
133
133
  },
134
134
  _ => error.to_string(),
@@ -140,7 +140,7 @@ fn emit_cli_error_for_command(
140
140
  value["error"] = serde_json::json!(explanation);
141
141
  value["action"] = serde_json::json!(format!("team-agent {command} --help"));
142
142
  value["next_actions"] = serde_json::json!([format!(
143
- "请按 team-agent {command} --help 的 Examples 补齐参数"
143
+ "Complete the required arguments using the Examples in team-agent {command} --help"
144
144
  )]);
145
145
  println!("{}", python_compact_json(&value));
146
146
  } else {
@@ -179,7 +179,7 @@ pub(super) fn emit_result(r: CmdResult) -> ExitCode {
179
179
  let mut stderr = stderr.lock();
180
180
  let _ = writeln!(
181
181
  stderr,
182
- "输出暂不可用;消息已保存,消息编号={message_id}。用 team-agent inbox leader -n 3 查看回复,不要重发。"
182
+ "Output unavailable; the message was persisted with message_id={message_id}. Use team-agent inbox leader -n 3 for replies; do not resend."
183
183
  );
184
184
  }
185
185
  return if error.kind() == ErrorKind::BrokenPipe {
@@ -251,20 +251,16 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
251
251
  | "clone-agent"
252
252
  | "fork-agent"
253
253
  | "remove-agent"
254
- | "allow-peer-talk"
255
254
  | "profile"
256
255
  )
257
256
  {
258
257
  return Err(CliError::Usage(
259
- "请填写队友名、任务内容或命令所需设置;见下方用法与 Examples".to_string(),
258
+ "Provide the agent name, task message or required command settings; see Usage and Examples below".to_string(),
260
259
  ));
261
260
  }
262
261
  match command {
263
262
  "quick-start" => cmd_quick_start(&quick_start_args(args, cwd)?).map(emit_result),
264
263
  "send" => cmd_send(&send_args(args, cwd)?).map(emit_result),
265
- "allow-peer-talk" => {
266
- cmd_allow_peer_talk(&allow_peer_talk_args(args, cwd)?).map(emit_result)
267
- }
268
264
  "status" => cmd_status_for_team(&status_args(args, cwd), parse_args(args).team.as_deref())
269
265
  .map(emit_result),
270
266
  "shutdown" => cmd_shutdown(&shutdown_args(args, cwd)?).map(emit_result),
@@ -306,33 +302,13 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
306
302
  fn is_machine_command(command: &str) -> bool {
307
303
  matches!(
308
304
  command,
309
- "results"
310
- | "wait"
311
- | "wait-ready"
312
- | "preflight"
313
- | "validate"
314
- | "identity"
315
- | "sessions"
316
- | "watch"
317
- | "e2e"
318
- | "peek"
319
- | "coordinator"
320
- | "attach-app-server-leader"
305
+ "wait" | "coordinator" | "attach-app-server-leader"
321
306
  )
322
307
  }
323
308
 
324
309
  fn dispatch_machine(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
325
310
  match command {
326
- "results" => cmd_results(&results_args(args, cwd)?).map(emit_result),
327
311
  "wait" => cmd_wait(&wait_args(args, cwd)?).map(emit_result),
328
- "wait-ready" => cmd_wait_ready(&wait_ready_args(args, cwd)).map(emit_result),
329
- "preflight" => cmd_preflight(&preflight_args(args, cwd)).map(emit_result),
330
- "validate" => cmd_validate(&validate_args(args, cwd)).map(emit_result),
331
- "identity" => cmd_identity(&identity_args(args, cwd)).map(emit_result),
332
- "sessions" => cmd_sessions(&sessions_args(args, cwd)).map(emit_result),
333
- "watch" => cmd_watch(&watch_args(args, cwd)).map(emit_result),
334
- "e2e" => cmd_e2e(&e2e_args(args, cwd)).map(emit_result),
335
- "peek" => cmd_peek(&peek_args(args, cwd)?).map(emit_result),
336
312
  "coordinator" => run_coordinator(args, cwd),
337
313
  "attach-app-server-leader" => {
338
314
  cmd_attach_app_server_leader(&attach_app_server_leader_args(args, cwd)?)
@@ -364,16 +340,16 @@ fn is_known_subcommand(command: &str) -> bool {
364
340
  }
365
341
 
366
342
  pub(crate) fn default_help() -> String {
367
- let mut out = String::from("Team Agent:起一支队伍,给队友发任务,看回复,安全关队。\n\n1. 准备队友\n 在项目目录运行 team-agent quick-start;空目录会给出 TEAM.md 和 agents/worker.md 两文件模板,请自行创建。\n2. 打开主控并起队\n 已安装且已登录 Pi?运行 team-agent pi。\n 在该主控的命令行/工具上下文运行 team-agent quick-start。\n 已有队伍要恢复?看 team-agent restart --help,不要接管或删除它。\n3. 发任务、看回复\n team-agent send worker '计算 245 × 37,把答案回复给 leader。'\n team-agent inbox leader -n 3\n team-agent status\n 发出任务不等于完成;等队友真正回复。\n4. 体检、关队\n team-agent doctor --workspace .\n team-agent shutdown --workspace . --json\n 检查本队残留为空,再用 doctor 确认所选项目没有待处理问题。\n\n多个项目/队伍用 --workspace/--team 选择,不猜对象;模型名称先用 models 查询。\nWindows 使用 ConPTY 需要 Windows 主机和已安装的 shim;tmux 指令不是通用 Windows 路径。\n");
343
+ let mut out = String::from("Team Agent: create a team, send tasks, read replies and shut down safely.\n\n1. Prepare agents\n Run team-agent quick-start in the project directory. An empty directory receives TEAM.md and agents/worker.md templates to create manually.\n2. Open a leader and create the team\n With Pi installed and signed in, run team-agent pi.\n Run team-agent quick-start in that leader's tool context.\n To recover an existing team, see team-agent restart --help; do not take over or delete it.\n3. Send tasks and read replies\n team-agent send worker 'Calculate 245 * 37 and reply to the leader.'\n team-agent inbox leader -n 3\n team-agent status\n Sending is not completion; wait for an actual reply.\n4. Diagnose and shut down\n team-agent doctor --workspace .\n team-agent shutdown --workspace . --json\n Verify this team's residual resources are empty, then diagnose the selected workspace.\n\nSelect workspace/team scope with --workspace/--team. Discover model names with models.\nWindows ConPTY requires a Windows host and an installed shim; tmux commands are not a generic Windows path.\n");
368
344
  append_help_section(
369
345
  &mut out,
370
- "开始协作",
346
+ "Getting started",
371
347
  &["quick-start", "send", "status", "models", "inbox"],
372
348
  );
373
- append_help_section(&mut out, "队伍管理", &["restart", "shutdown"]);
349
+ append_help_section(&mut out, "Team lifecycle", &["restart", "shutdown"]);
374
350
  append_help_section(
375
351
  &mut out,
376
- "队友管理",
352
+ "Agent lifecycle",
377
353
  &[
378
354
  "add-agent",
379
355
  "start-agent",
@@ -386,21 +362,21 @@ pub(crate) fn default_help() -> String {
386
362
  );
387
363
  append_help_section(
388
364
  &mut out,
389
- "观察与协作",
390
- &["leaders", "doctor", "approvals", "allow-peer-talk"],
365
+ "Observation and collaboration",
366
+ &["leaders", "doctor", "approvals"],
391
367
  );
392
- append_help_section(&mut out, "设置", &["route", "profile", "install-skill"]);
368
+ append_help_section(&mut out, "Configuration", &["route", "profile", "install-skill"]);
393
369
  append_help_section(
394
370
  &mut out,
395
- "按体检提示恢复",
371
+ "Guided recovery",
396
372
  &["claim-leader", "takeover", "attach-leader"],
397
373
  );
398
374
  append_help_section(
399
375
  &mut out,
400
- "启动主控",
376
+ "Leader launch",
401
377
  &["pi", "codex", "claude", "copilot", "grok", "cursor"],
402
378
  );
403
- out.push_str("\n每个操作的参数和例子:team-agent <command> --help");
379
+ out.push_str("\nCommand arguments and examples: team-agent <command> --help");
404
380
  out
405
381
  }
406
382
 
@@ -435,9 +411,9 @@ pub fn __test_quick_start_args(
435
411
  }
436
412
 
437
413
  pub(super) const TEAM_TEMPLATE: &str =
438
- "---\nname: help-demo\n---\nA small team for a command-line example.\n";
439
- pub(super) const WORKER_TEMPLATE: &str = "---\nname: worker\nrole: assistant\nprovider: pi\nmodel: openai-codex/gpt-6-luna\nauth_mode: subscription\ndangerously_skip_permissions: false\n---\n完成任务后,把简明答案回复给 leader。\n";
440
- const HUMAN_NAVIGATION: &str = "请用 team-agent --help 查看可用操作。";
414
+ "---\nname: current\n---\nA small team for a command-line example.\n";
415
+ pub(super) const WORKER_TEMPLATE: &str = "---\nname: worker\nrole: assistant\nprovider: pi\nmodel: openai-codex/gpt-6-luna\nauth_mode: subscription\ndangerously_skip_permissions: false\n---\nComplete the task and send a concise reply to the leader.\n";
416
+ const HUMAN_NAVIGATION: &str = "Use team-agent --help to list available commands.";
441
417
 
442
418
  pub(super) fn command_help(command: Option<&str>) -> String {
443
419
  let Some(name) = command else {
@@ -451,56 +427,54 @@ pub(super) fn command_help(command: Option<&str>) -> String {
451
427
  }
452
428
  let (details, examples, next) = match name {
453
429
  "quick-start" => (
454
- "TEAMDIR 是含 TEAM.md 和 agents/ 的角色目录;--workspace 选择运行项目。\n--name 设置队伍名称;--team/--team-id 选择队伍;--yes 确认已有提示;--detail 查看详细返回。\n工具须已安装并登录;工具/模型/思考强度写在角色文件中;跳过权限询问默认 false。\n--backend tmux 用于 POSIX;Windows 的 conpty 需要 Windows 主机和已安装的 ConPTY shim。",
430
+ "TEAMDIR contains TEAM.md and agents/; --workspace selects the runtime project.\n--name sets the team name; --team/--team-id selects the team; --yes retains compatibility; --detail includes full diagnostics.\nProviders must be installed and signed in. Declare provider/model/effort in role files; permission bypass defaults to false.\n--backend tmux is for POSIX; conpty requires a Windows host and an installed ConPTY shim.",
455
431
  "team-agent quick-start\nteam-agent quick-start ./roles --workspace ./project\nteam-agent quick-start . --json",
456
- "确认 Pi 已安装并登录,用 team-agent models --provider pi 核对模型。\n运行 team-agent pi,在该主控的命令行/工具上下文再运行 quick-start;已有队伍看 restart --help。"),
457
- "send" => ("<agent> 是 status 中的队友名;MESSAGE 是任务内容。\n--mailbox 只留言,不发送到当前对话;默认发送到队友对话。",
458
- "team-agent send worker '计算 245 × 37,把答案回复给 leader。'\nteam-agent send worker '检查改动' --workspace . --team help-demo\nteam-agent send worker '下次上线时查看' --mailbox",
459
- "已收下任务不等于送达或完成。等真实回复,用 team-agent inbox leader -n 3;有疑问看 status/doctor,不反复重发。"),
460
- "inbox" => ("<agent> 是收信队友名;-n/--limit 限制条数。查看回复不触发重发。",
461
- "team-agent inbox leader -n 3\nteam-agent inbox worker --limit 5\nteam-agent inbox leader --workspace . --team help-demo --json", "没有回复时先等队友自然返回,再看 team-agent status;不要读取队友终端内容。"),
462
- "status" => ("可选 <agent> 只查看一位队友;--summary/--detail 保留兼容,不增加字段。\n九字段:name/provider/model/effort/runtime_status/activity/health/session_name/tmux_command。\n运行状态、忙闲、健康各有含义;unknown 表示证据不足,null 表示未设置,不猜默认模型。",
463
- "team-agent status\nteam-agent status worker\nteam-agent status --workspace . --team help-demo --json", "看回复用 team-agent inbox leader -n 3;查问题用 team-agent doctor --workspace .。"),
464
- "models" => ("--provider 选择工具,默认 pi;QUERY 或 --search 按关键词搜索,不能同时用。\nCursor 的工具名是 cursor_agent;Copilot 暂无此模型查询入口。返回真实目录,不猜模型。",
465
- "team-agent models --provider pi\nteam-agent models --provider pi --search luna\nteam-agent models --provider cursor_agent --json", "把返回的完整模型名称填入角色文件或 add-agent --model,再起队或加人。"),
466
- "restart" => ("WORKSPACE 默认当前项目;优先恢复已保存会话。\n只有用户明确允许丢弃旧对话时才加 --allow-fresh;--session-converge-deadline 设置等待秒数。",
467
- "team-agent restart .\nteam-agent restart . --team help-demo --json", "用 status 查看恢复结果;需要新会话先征得用户同意,不用 takeover 代替恢复。"),
468
- "shutdown" => ("只关闭所选项目/队伍;日志默认保留,--keep-logs 保留兼容。\n--json 查看关闭范围、降级情况及本队残留,返回成功不代表其他队伍已关闭。",
469
- "team-agent shutdown --workspace .\nteam-agent shutdown --workspace . --team help-demo --json", "核对本队残留为空,再用 team-agent doctor --workspace .;不要广域清理其他队伍。"),
470
- "add-agent" => ("<agent> 是新队友名;--provider 选择工具;--model 模型名称;--effort 思考强度。\n--bypass 是否跳过权限询问,示例 false;--prompt 任务职责;--profile 登录/代理设置。\n工具和 bypass 必须由参数或 --role-file 明确提供;冲突会拒绝,不猜设置。--force 替换已有队友,仅在用户明确授权后用。",
471
- "team-agent add-agent reviewer --provider pi --model openai-codex/gpt-6-luna --bypass false --prompt '检查任务'\nteam-agent add-agent reviewer --role-file ./agents/reviewer.md", "用 status 确认队友,再运行 team-agent send reviewer '检查改动'。"),
472
- "start-agent" => ("只启动已有队友;仍在运行时先 stop-agent。\n--provider 工具;--model 模型名称;--effort 思考强度;--bypass 是否跳过权限询问。\n--prompt/--profile 更换职责/登录设置;只有明确获准丢弃旧对话才用 --allow-fresh。",
473
- "team-agent start-agent worker\nteam-agent start-agent worker --model openai-codex/gpt-6-luna", "新增队友用 add-agent;启动后看 status,再 send 分派任务。"),
474
- "stop-agent" => ("<agent> 指定要暂停的队友;暂停不是删除,配置与会话记录保留。",
475
- "team-agent stop-agent worker\nteam-agent stop-agent worker --workspace . --team help-demo --json", "用 status 确认停止;恢复用 team-agent start-agent worker。"),
476
- "reset-agent" => ("必须明确加 --discard-session;此操作会停止旧进程、清除保存的会话关联,并尝试以新会话重新启动。已暂停的队友不会自动启动;不承诺删除工具的全部历史记录。",
477
- "team-agent reset-agent worker --discard-session\nteam-agent reset-agent worker --discard-session --workspace . --team help-demo --json", "用 team-agent status 核对重置结果;正常重置后直接 send 分派新任务,无需再执行 start-agent。"),
478
- "clone-agent" => ("<agent> 是源队友;--as 新队友名;--label 可读标签。\n复制配置创建新队友,不复制完整对话。",
479
- "team-agent clone-agent worker --as reviewer\nteam-agent clone-agent worker --as reviewer --label '审阅队友' --workspace .", "用 status 查看新队友,再 send reviewer 分派独立任务。"),
480
- "fork-agent" => ("<agent> 是源队友;--as 新队友名;--label 可读标签。\n需要源会话已保存且工具支持分支;不满足条件会拒绝,不保证所有工具都可分支。",
481
- "team-agent fork-agent worker --as experiment\nteam-agent fork-agent worker --as experiment --workspace . --team help-demo --json", "分支成功会占用新资源;看 status,再向 experiment 派发任务,用完停止或移除。"),
482
- "remove-agent" => ("必须 --confirm;配置定义的队友还需 --from-spec,动态队友可以不加。\n只删除托管角色副本,不删除外部用户角色文件;运行中的队友先 stop-agent。\n--force 可停止并移除运行中的队友,仅在理解风险且明确授权后使用。",
483
- "team-agent remove-agent reviewer --confirm\nteam-agent remove-agent reviewer --from-spec --confirm --workspace . --team help-demo --json", "先保存所需回复;移除后核对 status 和实际配置范围,不对其他队友使用强制清理。"),
484
- "leaders" => ("默认列出可用主控;--all 包含已失效登记,--stale 只看失效项。\nQUERY/--search 按项目/队伍/名称筛选;--prune 仅清理已确认退役的登记,--dry-run 先预览。",
485
- "team-agent leaders\nteam-agent leaders --all --json\nteam-agent leaders --prune --dry-run", "根据项目和队伍选择 --workspace/--team;清理登记不等于关闭进程。"),
486
- "doctor" => ("默认只检查,不修复;SPEC 可选配置路径。\n--comms/--gate comms 检查连接,不证明队友实际回复;--gate orphans 检查残留。\n--fix/--fix-schema/--cleanup-orphans 仅在获准的范围内使用;危险修复需要 --confirm。",
487
- "team-agent doctor --workspace .\nteam-agent doctor --workspace . --team help-demo --json", "按实际问题与所选队伍范围处理;不要把体检建议当成已执行的修复。"),
488
- "approvals" => ("可选 <agent> 查看一位队友;仅观察权限询问,不自动批准。",
489
- "team-agent approvals\nteam-agent approvals worker --json", "确认权限用途后,在工具的实际权限询问处处理,再看 status。"),
490
- "allow-peer-talk" => ("指定两位现有队友;--workspace 选择项目。此命令不支持 --team。",
491
- "team-agent allow-peer-talk worker reviewer\nteam-agent allow-peer-talk worker reviewer --workspace . --json", "再向队友发送交流任务;允许交流不等于已经互发消息。"),
492
- "profile" => ("init 创建配置,doctor 检查配置,show 显示设置;NAME 是配置名。\n--auth-mode 登录方式;--proxy-mode direct 不走代理、inherit 沿用环境代理。\n配置存放在所选工作区,不因 --team 自动变成队伍独立配置;不要输出凭证。",
493
- "team-agent profile init local --auth-mode subscription\nteam-agent profile doctor local\nteam-agent profile show local", "检查通过后用 add-agent/start-agent --profile local;需要登录时使用工具原生登录入口。"),
494
- "install-skill" => ("--source 必须是已核实的指南目录;--target 选择 codex/claude/copilot/all。\n--dest 覆盖安装位置;--dry-run 只预览;--uninstall 移除目标指南。此项不是起队前提。",
495
- "team-agent install-skill --source \"$SKILL_DIR\" --target codex --dry-run\nteam-agent install-skill --source \"$SKILL_DIR\" --target codex", "先把 SKILL_DIR 设为实际已核验目录;核对预览后再安装,不覆盖未知用户文件。"),
496
- "claim-leader" => ("仅在 doctor 明确提示登记主控、且当前终端确为主控时使用。\n--confirm 表示授权登记;不会自动打开新工具。",
497
- "team-agent claim-leader --workspace . --json\nteam-agent claim-leader --workspace . --team help-demo --confirm --json", "先核对 doctor 与当前终端归属;未确认前的拒绝不是一次成功登记。"),
498
- "takeover" => ("仅在 doctor 提示接管且用户已授权时使用;接管会修改主控归属。\n有可用的队伍终端时,即使省略 --confirm 也可能执行接管;不能用作只读预演。正常恢复用 restart。",
499
- "team-agent takeover --workspace . --team help-demo --confirm\nteam-agent takeover --workspace . --team help-demo --confirm --json", "核实主控和队伍归属再确认;完成后查看 status/doctor,不接管其他人的队伍。"),
500
- "attach-leader" => ("PANE 必须来自实际核验的主控终端,不能猜终端编号。\n--provider 选择实际工具;--confirm 表示授权连接已有主控,仅按 doctor 提示使用。",
501
- "team-agent attach-leader --pane \"$PANE\" --provider pi --workspace .\nteam-agent attach-leader --pane \"$PANE\" --provider pi --workspace . --team help-demo --confirm --json", "先将 PANE 设为已核验的终端;连接后用 doctor 确认,再派发任务。"),
502
- _ => ("先确保工具已安装并完成原生登录;当前目录作为项目。\n--attach-existing 与 --confirm 只在已核验主控归属并明确授权后使用;--attach-session 选择已核验的会话。\n--external-leader 选择外部主控;--allow-nested-attach 仅在明确授权使用嵌套终端时添加。\n-- 后面的参数原样交给工具;纯 --help/-h 只显示这里的帮助,不启动工具。",
503
- "", "在打开的主控命令行/工具上下文运行 team-agent quick-start;不是在普通队友对话中起队。"),
432
+ "Check Pi installation and sign-in; verify models with team-agent models --provider pi.\nRun team-agent pi, then quick-start in that leader's tool context. For an existing team, see restart --help."),
433
+ "send" => ("<agent> is an agent name from status; MESSAGE is the task text.\n--mailbox stores a message without injecting it into the current conversation; default delivery targets the agent conversation.",
434
+ "team-agent send worker 'Calculate 245 * 37 and reply to the leader.'\nteam-agent send worker 'Review the changes' --workspace . --team help-demo\nteam-agent send worker 'Read this when next available' --mailbox",
435
+ "Acceptance is not delivery or completion. Wait for an actual reply; use team-agent inbox leader -n 3. Diagnose with status/doctor rather than repeated sends."),
436
+ "inbox" => ("<agent> is the recipient name; -n/--limit bounds the message count. Reading replies does not resend messages.",
437
+ "team-agent inbox leader -n 3\nteam-agent inbox worker --limit 5\nteam-agent inbox leader --workspace . --team help-demo --json", "Wait for a natural reply, then check team-agent status if needed. Do not inspect worker terminal content."),
438
+ "status" => ("Optional <agent> selects one agent. --summary/--detail retain compatibility and do not add fields.\nNine fields: name/provider/model/effort/runtime_status/activity/health/session_name/tmux_command.\nRuntime, activity and health are distinct. unknown means insufficient evidence; null means unset. No default model is guessed.",
439
+ "team-agent status\nteam-agent status worker\nteam-agent status --workspace . --team help-demo --json", "Read replies with team-agent inbox leader -n 3; diagnose problems with team-agent doctor --workspace ."),
440
+ "models" => ("--provider selects the provider, default pi. QUERY or --search performs keyword search; they are mutually exclusive.\nUse cursor_agent for Cursor. Copilot has no model discovery entrypoint here. Results come from the native catalog, not guesses.",
441
+ "team-agent models --provider pi\nteam-agent models --provider pi --search luna\nteam-agent models --provider cursor_agent --json", "Use the exact returned model name in a role file or add-agent --model before launch."),
442
+ "restart" => ("WORKSPACE defaults to the current project; saved sessions are preferred.\nUse --allow-fresh only after explicit authorization to lose saved context. --session-converge-deadline sets the wait in seconds.",
443
+ "team-agent restart .\nteam-agent restart . --team help-demo --json", "Check status after restart. Obtain authorization before creating a fresh session; takeover is not a substitute for restart."),
444
+ "shutdown" => ("Shuts down only the selected workspace/team. Logs are retained by default; --keep-logs remains compatible.\n--json reports scope, degradation and owned residuals. Success does not mean other teams were shut down.",
445
+ "team-agent shutdown --workspace .\nteam-agent shutdown --workspace . --team help-demo --json", "Verify this team's owned residuals are empty, then run team-agent doctor --workspace . Do not clean up other teams."),
446
+ "add-agent" => ("<agent> names the new agent. --provider selects the provider; --model sets the model; --effort sets reasoning effort.\n--bypass controls permission bypass (example: false); --prompt sets responsibilities; --profile selects authentication/proxy settings.\nProvider and bypass must be explicit in arguments or --role-file. Conflicting settings are refused, not guessed. Use --force to replace an agent only after explicit authorization.",
447
+ "team-agent add-agent reviewer --provider pi --model openai-codex/gpt-6-luna --bypass false --prompt 'Review tasks'\nteam-agent add-agent reviewer --role-file ./agents/reviewer.md", "Verify the agent with status, then run team-agent send reviewer 'Review the changes'."),
448
+ "start-agent" => ("Starts an existing agent only; stop-agent first if it is running.\n--provider selects the provider; --model sets the model; --effort sets reasoning effort; --bypass controls permission bypass.\n--prompt/--profile changes responsibilities/authentication. Use --allow-fresh only after explicit authorization to lose saved context.",
449
+ "team-agent start-agent worker\nteam-agent start-agent worker --model openai-codex/gpt-6-luna", "Use add-agent for a new agent. Check status after starting, then send a task."),
450
+ "stop-agent" => ("<agent> names the agent to stop. This is not removal; configuration and session history are retained.",
451
+ "team-agent stop-agent worker\nteam-agent stop-agent worker --workspace . --team help-demo --json", "Verify stopped status; resume with team-agent start-agent worker."),
452
+ "reset-agent" => ("Requires explicit --discard-session. Stops the old process, clears the saved session association and attempts a fresh restart. Paused agents stay paused. This does not delete all native provider history.",
453
+ "team-agent reset-agent worker --discard-session\nteam-agent reset-agent worker --discard-session --workspace . --team help-demo --json", "Verify with team-agent status. After a normal reset, send a new task; another start-agent is unnecessary."),
454
+ "clone-agent" => ("<agent> is the source; --as names the new agent; --label sets a readable label.\nCreates an agent from copied configuration, not a complete conversation copy.",
455
+ "team-agent clone-agent worker --as reviewer\nteam-agent clone-agent worker --as reviewer --label 'Review agent' --workspace .", "Check the new agent with status, then send reviewer an independent task."),
456
+ "fork-agent" => ("<agent> is the source; --as names the new agent; --label sets a readable label.\nRequires a captured source session and supported fork behavior. Unsupported cases are refused; not all providers support the same kind of fork.",
457
+ "team-agent fork-agent worker --as experiment\nteam-agent fork-agent worker --as experiment --workspace . --team help-demo --json", "Inspect status after success, then send a task to the resulting agent. Stop or remove a new seat when finished."),
458
+ "remove-agent" => ("Requires --confirm. Spec-defined agents also require --from-spec; dynamic agents do not.\nDeletes only the managed role copy, not an external user role file. Stop a running agent first.\n--force stops and removes a running agent; use only after understanding the risk and receiving explicit authorization.",
459
+ "team-agent remove-agent reviewer --confirm\nteam-agent remove-agent reviewer --from-spec --confirm --workspace . --team help-demo --json", "Save required replies first; verify status and configuration scope after removal. Do not force-clean other agents."),
460
+ "leaders" => ("Lists live leaders by default; --all includes stale registrations and --stale selects stale entries.\nQUERY/--search filters workspace/team/name. --prune removes verified retired registrations; preview with --dry-run.",
461
+ "team-agent leaders\nteam-agent leaders --all --json\nteam-agent leaders --prune --dry-run", "Select --workspace/--team from the reported scope. Registry cleanup is not process shutdown."),
462
+ "doctor" => ("Read-only diagnosis by default; SPEC is an optional configuration path.\n--comms/--gate comms checks channels, not actual replies; --gate orphans checks residual resources.\nUse --fix/--fix-schema/--cleanup-orphans only within authorized scope. Dangerous repairs require --confirm.",
463
+ "team-agent doctor --workspace .\nteam-agent doctor --workspace . --team help-demo --json", "Follow the actual issue and selected-team scope. A suggested repair is not an executed repair."),
464
+ "approvals" => ("Optional <agent> selects one agent. Observes permission prompts without granting approval.",
465
+ "team-agent approvals\nteam-agent approvals worker --json", "Verify the requested permission, handle it in the actual provider prompt, then check status."),
466
+ "profile" => ("init creates a profile; doctor checks it; show displays settings. NAME is the profile name.\n--auth-mode selects authentication; --proxy-mode direct bypasses proxies and inherit uses the environment proxy.\nProfiles belong to the selected workspace, not automatically to an isolated --team. Do not expose credentials.",
467
+ "team-agent profile init local --auth-mode subscription\nteam-agent profile doctor local\nteam-agent profile show local", "After validation, select --profile local with add-agent/start-agent. Use native provider sign-in when needed."),
468
+ "install-skill" => ("--source must be a verified skill directory; --target selects codex/claude/copilot/all.\n--dest overrides the destination; --dry-run previews; --uninstall removes the target skill. Installation is not required to create a team.",
469
+ "team-agent install-skill --source \"$SKILL_DIR\" --target codex --dry-run\nteam-agent install-skill --source \"$SKILL_DIR\" --target codex", "Set SKILL_DIR to a verified directory and inspect the preview before installation. Do not overwrite unknown user files."),
470
+ "claim-leader" => ("Use only when doctor recommends leader registration and this terminal is the actual leader.\n--confirm authorizes registration; it does not open a new provider.",
471
+ "team-agent claim-leader --workspace . --json\nteam-agent claim-leader --workspace . --team help-demo --confirm --json", "Verify doctor findings and terminal ownership first. A refusal without confirmation is not successful registration."),
472
+ "takeover" => ("Use only when doctor recommends takeover and the user has authorized it. This changes leader ownership.\nWith a usable team terminal, takeover may execute even without --confirm; it is not a read-only preview. Use restart for normal recovery.",
473
+ "team-agent takeover --workspace . --team help-demo --confirm\nteam-agent takeover --workspace . --team help-demo --confirm --json", "Verify leader/team ownership before confirmation. Check status/doctor afterwards; do not take over someone else's team."),
474
+ "attach-leader" => ("PANE must be a verified leader terminal, not a guessed pane id.\n--provider selects the actual provider; --confirm authorizes attachment to an existing leader. Use only when doctor recommends it.",
475
+ "team-agent attach-leader --pane \"$PANE\" --provider pi --workspace .\nteam-agent attach-leader --pane \"$PANE\" --provider pi --workspace . --team help-demo --confirm --json", "Set PANE to the verified terminal. Confirm attachment with doctor before sending a task."),
476
+ _ => ("Install the provider and complete native sign-in first. The current directory is the workspace.\nUse --attach-existing/--confirm only after verifying leader ownership and receiving authorization; --attach-session selects a verified session.\n--external-leader selects an external leader; --allow-nested-attach requires explicit authorization for nested terminals.\nArguments after -- pass through unchanged. Bare --help/-h displays this help without starting the provider.",
477
+ "", "Run team-agent quick-start in the opened leader's tool context, not in an ordinary worker conversation."),
504
478
  };
505
479
  let examples = if matches!(spec.kind, CommandKind::LeaderPassthrough { .. }) {
506
480
  let native = if name == "cursor" { "agent" } else { name };
@@ -513,30 +487,30 @@ pub(super) fn command_help(command: Option<&str>) -> String {
513
487
  examples.to_string()
514
488
  };
515
489
  let scope = if matches!(spec.kind, CommandKind::LeaderPassthrough { .. }) {
516
- "--json 给程序读取;不把 --workspace/--team 当作主控选项传入,它们会交给原生工具。"
490
+ "Use --json for structured output. --workspace/--team are not launcher options here; they pass to the native provider."
517
491
  } else {
518
- "--json 给程序读取;--workspace 选项目,支持 --team 的操作用它选队伍,不猜对象。"
492
+ "Use --json for structured output. --workspace selects the project; supported commands use --team to select the team."
519
493
  };
520
- let mut out = format!("做什么:\n{}。\n用法:{}\n\n怎么用:\n{}\n{}\nExamples(可复制):\n{}\n\n下一步(Next Action):\n{}", spec.summary, spec.usage, details, scope, examples, next);
494
+ let mut out = format!("Purpose:\n{}.\nUsage: {}\n\nOptions:\n{}\n{}\nExamples:\n{}\n\nNext Action:\n{}", spec.summary, spec.usage, details, scope, examples, next);
521
495
  if name == "quick-start" {
522
- out.push_str(&format!("\n\n最简两文件(请自行创建,不会自动写入):\nTEAM.md:\n{TEAM_TEMPLATE}\nagents/worker.md:\n{WORKER_TEMPLATE}"));
496
+ out.push_str(&format!("\n\nMinimal two-file configuration (create manually; not written automatically):\nTEAM.md:\n{TEAM_TEMPLATE}\nagents/worker.md:\n{WORKER_TEMPLATE}"));
523
497
  }
524
498
  if name == "cursor" {
525
- out.push_str("\n查询模型用 team-agent models --provider cursor_agent。");
499
+ out.push_str("\nDiscover models with team-agent models --provider cursor_agent.");
526
500
  }
527
501
  if name == "copilot" {
528
- out.push_str("\nCopilot 暂无 team-agent models 查询入口,请看工具原生帮助。");
502
+ out.push_str("\nCopilot has no team-agent models entrypoint; consult native provider help.");
529
503
  }
530
504
  out
531
505
  }
532
506
 
533
507
  fn emit_unknown_subcommand_usage(command: &str) -> ExitCode {
534
508
  emit_usage_error(&format!(
535
- "没有这个操作:'{command}'。请用 team-agent --help 查看可用操作。"
509
+ "Unknown command: '{command}'. Use team-agent --help to list available commands."
536
510
  ));
537
511
  // E8 (N38): 错路引导 —— 拼写近似时建议最接近的真子命令(additive,不改既有 golden 行)。
538
512
  if let Some(suggestion) = nearest_subcommand(command) {
539
- eprintln!("你是否想使用 `{suggestion}`?运行 team-agent {suggestion} --help 查看例子。");
513
+ eprintln!("Did you mean `{suggestion}`? See team-agent {suggestion} --help for examples.");
540
514
  }
541
515
  // 0.5.45 naming-addressing (RED-6): unknown subcommand exits 1
542
516
  // (Error), aligned with the family of typo refusals throughout
@@ -568,11 +542,10 @@ fn nearest_subcommand(input: &str) -> Option<&'static str> {
568
542
  }
569
543
 
570
544
  fn emit_usage_error(message: &str) {
571
- eprintln!("用法:team-agent <command>;查看所有操作:team-agent --help");
572
- eprintln!("错误:{message}");
545
+ eprintln!("Usage: team-agent <command>; list commands with team-agent --help");
546
+ eprintln!("Error: {message}");
573
547
  }
574
548
 
575
- /// `cmd_validate` delegates to runtime validate_file.
576
549
  /// `install-skill` 参数(RED-1 根治:把 skill 安装单源收敛到二进制,install.mjs 调它)。
577
550
  struct InstallSkillArgs {
578
551
  target: crate::packaging::SkillTarget,
@@ -688,93 +661,10 @@ fn cmd_install_skill(args: &InstallSkillArgs) -> Result<CmdResult, CliError> {
688
661
  ))
689
662
  }
690
663
 
691
- pub fn cmd_validate(args: &ValidateArgs) -> Result<CmdResult, CliError> {
692
- let spec = resolve_path(&args.spec);
693
- let value = if spec.is_dir() {
694
- validate_team_dir(&spec)?
695
- } else {
696
- validate_spec_file(&spec)?
697
- };
698
- Ok(CmdResult::from_json(value, args.json))
699
- }
700
-
701
- fn validate_spec_file(spec_path: &Path) -> Result<Value, CliError> {
702
- let text = std::fs::read_to_string(spec_path)?;
703
- let base_dir = spec_path.parent().unwrap_or_else(|| Path::new("."));
704
- let spec =
705
- crate::model::spec::load_and_validate_spec(&text, base_dir).map_err(model_error_to_cli)?;
706
- let team = spec
707
- .get("team")
708
- .and_then(|team| team.get("name"))
709
- .and_then(crate::model::yaml::Value::as_str)
710
- .unwrap_or("");
711
- let workspace = spec
712
- .get("team")
713
- .and_then(|team| team.get("workspace"))
714
- .and_then(crate::model::yaml::Value::as_str)
715
- .map(PathBuf::from)
716
- .unwrap_or_else(|| base_dir.to_path_buf());
717
- let mut obj = Map::new();
718
- obj.insert("ok".to_string(), Value::Bool(true));
719
- obj.insert("team".to_string(), Value::String(team.to_string()));
720
- obj.insert(
721
- "workspace".to_string(),
722
- Value::String(workspace.to_string_lossy().to_string()),
723
- );
724
- Ok(Value::Object(obj))
725
- }
726
-
727
- fn validate_team_dir(team_dir: &Path) -> Result<Value, CliError> {
728
- let spec = crate::compiler::compile_team(team_dir).map_err(model_error_to_cli)?;
729
- let team = spec
730
- .get("team")
731
- .and_then(|team| team.get("name"))
732
- .and_then(crate::model::yaml::Value::as_str)
733
- .unwrap_or("");
734
- let workspace = spec
735
- .get("team")
736
- .and_then(|team| team.get("workspace"))
737
- .and_then(crate::model::yaml::Value::as_str)
738
- .map(PathBuf::from)
739
- .unwrap_or_else(|| team_dir.to_path_buf());
740
- let agents = spec
741
- .get("agents")
742
- .and_then(crate::model::yaml::Value::as_list)
743
- .map(|agents| {
744
- agents
745
- .iter()
746
- .filter_map(|agent| {
747
- agent
748
- .get("id")
749
- .and_then(crate::model::yaml::Value::as_str)
750
- .map(|id| Value::String(id.to_string()))
751
- })
752
- .collect::<Vec<_>>()
753
- })
754
- .unwrap_or_default();
755
- let mut obj = Map::new();
756
- obj.insert("ok".to_string(), Value::Bool(true));
757
- obj.insert("type".to_string(), Value::String("team_dir".to_string()));
758
- obj.insert(
759
- "workspace".to_string(),
760
- Value::String(workspace.to_string_lossy().to_string()),
761
- );
762
- obj.insert("team".to_string(), Value::String(team.to_string()));
763
- obj.insert("agents".to_string(), Value::Array(agents));
764
- Ok(Value::Object(obj))
765
- }
766
-
767
664
  fn resolve_path(path: &Path) -> PathBuf {
768
665
  std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
769
666
  }
770
667
 
771
- fn model_error_to_cli(error: crate::model::errors::ModelError) -> CliError {
772
- match error {
773
- crate::model::errors::ModelError::Validation(message) => CliError::Runtime(message),
774
- other => CliError::Runtime(other.to_string()),
775
- }
776
- }
777
-
778
668
  fn emit_cli_error(command: &str, args: &[String], cwd: &Path, error: &CliError) -> ExitCode {
779
669
  let parsed = parse_args(args);
780
670
  let workspace = workspace(&parsed, cwd);
@@ -802,12 +692,12 @@ fn emit_cli_error(command: &str, args: &[String], cwd: &Path, error: &CliError)
802
692
  {
803
693
  if let Some(message) = super::named_address::human_address_reason(reason) {
804
694
  payload.error = message.to_string();
805
- payload.action = "先运行 team-agent status,再用列表中的队友名发送;用 --workspace/--team 选择项目和队伍,连接问题先 doctor。".to_string();
695
+ payload.action = "Run team-agent status and use an agent name from the list. Select scope with --workspace/--team; diagnose channel issues with doctor.".to_string();
806
696
  }
807
697
  }
808
698
  if command_spec(command).is_some()
809
699
  && payload.action
810
- == "先运行 team-agent doctor --workspace . 检查所选队伍,或查看此处列出的错误日志。"
700
+ == "Run team-agent doctor --workspace . for the selected team, or inspect the error log shown here."
811
701
  {
812
702
  let mut doctor = format!(
813
703
  "team-agent doctor --workspace {}",
@@ -816,7 +706,7 @@ fn emit_cli_error(command: &str, args: &[String], cwd: &Path, error: &CliError)
816
706
  if let Some(team) = parsed.team.as_deref() {
817
707
  doctor.push_str(&format!(" --team {}", super::adapters::shell_quote(team)));
818
708
  }
819
- payload.action = format!("先运行 {doctor} 检查所选队伍,或查看此处列出的错误日志。");
709
+ payload.action = format!("Run {doctor} for the selected team, or inspect the error log shown here.");
820
710
  }
821
711
  payload.action = crate::redaction::redact_external_text(&payload.action);
822
712
  payload.log = crate::redaction::redact_external_text(&payload.log);
@@ -951,14 +841,9 @@ struct ParsedArgs {
951
841
  once: bool,
952
842
  tick_interval: Option<f64>,
953
843
  status_value: Option<String>,
954
- providers: Option<String>,
955
- allow_raw_screen: bool,
956
- tail: Option<usize>,
957
- head: Option<usize>,
958
844
  search: Option<String>,
959
845
  file: Option<PathBuf>,
960
846
  result: Option<String>,
961
- real: bool,
962
847
  assignee: Option<String>,
963
848
  auth_mode: Option<String>,
964
849
  proxy_mode: Option<String>,
@@ -1045,14 +930,9 @@ fn parse_args(args: &[String]) -> ParsedArgs {
1045
930
  parsed.tick_interval = next_arg(args, &mut i).and_then(|v| v.parse::<f64>().ok())
1046
931
  }
1047
932
  "--status" => parsed.status_value = next_arg(args, &mut i),
1048
- "--providers" => parsed.providers = next_arg(args, &mut i),
1049
- "--allow-raw-screen" => parsed.allow_raw_screen = true,
1050
- "--tail" => parsed.tail = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok()),
1051
- "--head" => parsed.head = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok()),
1052
933
  "--search" => parsed.search = next_arg(args, &mut i),
1053
934
  "--file" => parsed.file = next_arg(args, &mut i).map(PathBuf::from),
1054
935
  "--result" => parsed.result = next_arg(args, &mut i),
1055
- "--real" => parsed.real = true,
1056
936
  "--assignee" => parsed.assignee = next_arg(args, &mut i),
1057
937
  "--auth-mode" => parsed.auth_mode = next_arg(args, &mut i),
1058
938
  "--proxy-mode" => parsed.proxy_mode = next_arg(args, &mut i),
@@ -1203,7 +1083,7 @@ fn quick_start_args(args: &[String], cwd: &Path) -> Result<QuickStartArgs, CliEr
1203
1083
  let normalized = literal.trim().to_ascii_lowercase();
1204
1084
  if normalized != "tmux" && normalized != "conpty" {
1205
1085
  return Err(CliError::Usage(format!(
1206
- "--backend 只能是 tmux 或 conpty,收到 {literal:?}。POSIX 使用 tmux;Windows ConPTY 需要 Windows 主机和已安装的 shim。"
1086
+ "--backend must be tmux or conpty; received {literal:?}. Use tmux on POSIX. ConPTY requires a Windows host and an installed shim."
1207
1087
  )));
1208
1088
  }
1209
1089
  }
@@ -1257,7 +1137,7 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
1257
1137
  && parsed.positionals.len() < 2
1258
1138
  {
1259
1139
  return Err(CliError::Usage(
1260
- "请填写收信队友名和任务内容,例如 team-agent send worker '检查改动'".to_string(),
1140
+ "Provide a recipient agent name and task message, such as team-agent send worker 'Review the changes'".to_string(),
1261
1141
  ));
1262
1142
  }
1263
1143
  let target = if parsed.targets.is_some()
@@ -1291,7 +1171,7 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
1291
1171
  )
1292
1172
  .map_err(|error| CliError::Usage(format!("invalid send routing: {error}")))?;
1293
1173
  if normalized.deprecation.is_some() {
1294
- eprintln!("提示:旧发送设置已弃用。直接用 team-agent send <agent> '任务内容';只留言请加 --mailbox。");
1174
+ eprintln!("Warning: legacy send settings are deprecated. Use team-agent send <agent> 'task message'; add --mailbox for storage without injection.");
1295
1175
  }
1296
1176
  Ok(SendArgs {
1297
1177
  target,
@@ -1370,16 +1250,16 @@ fn warn_send_legacy_delivery_flags(args: &[String]) {
1370
1250
  "--message-id",
1371
1251
  ];
1372
1252
  let spec = command_spec("send");
1373
- let sunset = spec.and_then(|spec| spec.sunset).unwrap_or("后续兼容版本");
1253
+ let sunset = spec.and_then(|spec| spec.sunset).unwrap_or("a future compatibility release");
1374
1254
  let action = spec
1375
1255
  .and_then(|spec| spec.action)
1376
- .unwrap_or("使用 team-agent send <agent> '任务内容',通过 --workspace/--team 选项目和队伍");
1256
+ .unwrap_or("Use team-agent send <agent> 'task message'; select scope with --workspace/--team");
1377
1257
  for flag in FLAGS {
1378
1258
  if args
1379
1259
  .iter()
1380
1260
  .any(|arg| arg == flag || arg.starts_with(&format!("{flag}=")))
1381
1261
  {
1382
- eprintln!("提示:{flag} 已弃用,将在{sunset}移除。下一步:{action}");
1262
+ eprintln!("Warning: {flag} is deprecated and will be removed in {sunset}. Next: {action}");
1383
1263
  }
1384
1264
  }
1385
1265
  }
@@ -1458,17 +1338,6 @@ fn refuse_if_multi_alive_team_missing_scope(
1458
1338
  Ok(())
1459
1339
  }
1460
1340
 
1461
- fn allow_peer_talk_args(args: &[String], cwd: &Path) -> Result<AllowPeerTalkArgs, CliError> {
1462
- let parsed = parse_args(args);
1463
- Ok(AllowPeerTalkArgs {
1464
- a: required_pos(&parsed, 0, "a")?,
1465
- b: required_pos(&parsed, 1, "b")?,
1466
- workspace: workspace(&parsed, cwd),
1467
- json: parsed.json,
1468
- team: parsed.team,
1469
- })
1470
- }
1471
-
1472
1341
  fn status_args(args: &[String], cwd: &Path) -> StatusArgs {
1473
1342
  let parsed = parse_args(args);
1474
1343
  StatusArgs {
@@ -1481,14 +1350,6 @@ fn status_args(args: &[String], cwd: &Path) -> StatusArgs {
1481
1350
  }
1482
1351
  }
1483
1352
 
1484
- fn watch_args(args: &[String], cwd: &Path) -> WatchArgs {
1485
- let parsed = parse_args(args);
1486
- WatchArgs {
1487
- workspace: workspace(&parsed, cwd),
1488
- team: parsed.team,
1489
- }
1490
- }
1491
-
1492
1353
  fn wait_args(args: &[String], cwd: &Path) -> Result<WaitArgs, CliError> {
1493
1354
  let parsed = parse_args(args);
1494
1355
  let workspace = workspace(&parsed, cwd);
@@ -1586,15 +1447,6 @@ fn attach_app_server_leader_args(
1586
1447
  })
1587
1448
  }
1588
1449
 
1589
- fn identity_args(args: &[String], cwd: &Path) -> IdentityArgs {
1590
- let parsed = parse_args(args);
1591
- IdentityArgs {
1592
- workspace: workspace(&parsed, cwd),
1593
- team: parsed.team,
1594
- json: parsed.json,
1595
- }
1596
- }
1597
-
1598
1450
  fn shutdown_args(args: &[String], cwd: &Path) -> Result<ShutdownArgs, CliError> {
1599
1451
  let parsed = parse_args(args);
1600
1452
  let workspace = workspace(&parsed, cwd);
@@ -1821,7 +1673,7 @@ fn reset_agent_args(args: &[String], cwd: &Path) -> Result<ResetAgentArgs, CliEr
1821
1673
  let agent = required_pos(&parsed, 0, "agent")?;
1822
1674
  if !parsed.discard_session {
1823
1675
  return Err(CliError::Usage(
1824
- "missing --discard-session;只有用户明确同意清除会话关联后才能添加此参数".to_string(),
1676
+ "missing --discard-session; add this option only after explicit authorization to discard the saved session association".to_string(),
1825
1677
  ));
1826
1678
  }
1827
1679
  let workspace = workspace(&parsed, cwd);
@@ -1917,15 +1769,6 @@ fn doctor_gate(raw: Option<&str>) -> Option<DoctorGate> {
1917
1769
  }
1918
1770
  }
1919
1771
 
1920
- fn sessions_args(args: &[String], cwd: &Path) -> SessionsArgs {
1921
- let parsed = parse_args(args);
1922
- SessionsArgs {
1923
- workspace: workspace(&parsed, cwd),
1924
- json: parsed.json,
1925
- team: parsed.team,
1926
- }
1927
- }
1928
-
1929
1772
  fn leaders_args(args: &[String], _cwd: &Path) -> Result<LeadersArgs, CliError> {
1930
1773
  let mut view = None;
1931
1774
  let mut query = None;
@@ -2031,18 +1874,6 @@ fn leaders_args(args: &[String], _cwd: &Path) -> Result<LeadersArgs, CliError> {
2031
1874
  })
2032
1875
  }
2033
1876
 
2034
- fn validate_args(args: &[String], cwd: &Path) -> ValidateArgs {
2035
- let parsed = parse_args(args);
2036
- ValidateArgs {
2037
- spec: parsed
2038
- .positionals
2039
- .first()
2040
- .map(PathBuf::from)
2041
- .unwrap_or_else(|| cwd.join("team.spec.yaml")),
2042
- json: parsed.json,
2043
- }
2044
- }
2045
-
2046
1877
  fn profile_args(args: &[String], cwd: &Path) -> Result<ProfileArgs, CliError> {
2047
1878
  let parsed = parse_args(args);
2048
1879
  let workspace = resolve_cli_path(cwd, &workspace(&parsed, cwd));
@@ -2057,106 +1888,6 @@ fn profile_args(args: &[String], cwd: &Path) -> Result<ProfileArgs, CliError> {
2057
1888
  })
2058
1889
  }
2059
1890
 
2060
- fn results_args(args: &[String], cwd: &Path) -> Result<ResultsArgs, CliError> {
2061
- if args
2062
- .iter()
2063
- .any(|arg| arg == "--to" || arg.starts_with("--to="))
2064
- {
2065
- return Err(CliError::Usage(
2066
- "results does not accept --to; it is a read-only command".to_string(),
2067
- ));
2068
- }
2069
- let parsed = parse_args(args);
2070
- let case_id = option_value(args, "--case")
2071
- .filter(|value| !value.is_empty())
2072
- .ok_or_else(|| CliError::Usage("missing --case".to_string()))?;
2073
- Ok(ResultsArgs {
2074
- case_id,
2075
- workspace: workspace(&parsed, cwd),
2076
- team: parsed.team,
2077
- json: parsed.json,
2078
- })
2079
- }
2080
-
2081
- fn option_value(args: &[String], flag: &str) -> Option<String> {
2082
- let prefix = format!("{flag}=");
2083
- let mut i = 0usize;
2084
- while i < args.len() {
2085
- let arg = args.get(i)?;
2086
- if let Some(value) = arg.strip_prefix(&prefix) {
2087
- return Some(value.to_string());
2088
- }
2089
- if arg == flag {
2090
- return args
2091
- .get(i.saturating_add(1))
2092
- .filter(|value| !value.starts_with('-'))
2093
- .cloned();
2094
- }
2095
- i = i.saturating_add(1);
2096
- }
2097
- None
2098
- }
2099
-
2100
- fn preflight_args(args: &[String], cwd: &Path) -> PreflightArgs {
2101
- let parsed = parse_args(args);
2102
- let team = parsed
2103
- .team
2104
- .as_deref()
2105
- .map(PathBuf::from)
2106
- .or_else(|| parsed.positionals.first().map(PathBuf::from))
2107
- .unwrap_or_else(|| cwd.to_path_buf());
2108
- PreflightArgs {
2109
- team,
2110
- json: parsed.json,
2111
- }
2112
- }
2113
-
2114
- fn wait_ready_args(args: &[String], cwd: &Path) -> WaitReadyArgs {
2115
- let parsed = parse_args(args);
2116
- WaitReadyArgs {
2117
- workspace: workspace(&parsed, cwd),
2118
- timeout: parsed.timeout.unwrap_or(60.0),
2119
- json: parsed.json,
2120
- team: parsed.team,
2121
- }
2122
- }
2123
-
2124
- fn e2e_args(args: &[String], cwd: &Path) -> E2eArgs {
2125
- let parsed = parse_args(args);
2126
- let providers = parsed
2127
- .providers
2128
- .as_deref()
2129
- .unwrap_or("fake")
2130
- .split(',')
2131
- .filter_map(|p| {
2132
- let trimmed = p.trim();
2133
- if trimmed.is_empty() {
2134
- None
2135
- } else {
2136
- Some(trimmed.to_string())
2137
- }
2138
- })
2139
- .collect();
2140
- E2eArgs {
2141
- workspace: workspace(&parsed, cwd),
2142
- providers,
2143
- real: parsed.real,
2144
- json: parsed.json,
2145
- }
2146
- }
2147
-
2148
- fn peek_args(args: &[String], cwd: &Path) -> Result<PeekArgs, CliError> {
2149
- let parsed = parse_args(args);
2150
- Ok(PeekArgs {
2151
- agent: required_pos(&parsed, 0, "agent")?,
2152
- workspace: workspace(&parsed, cwd),
2153
- tail: parsed.tail.unwrap_or(80),
2154
- head: parsed.head,
2155
- search: parsed.search,
2156
- allow_raw_screen: parsed.allow_raw_screen,
2157
- json: parsed.json,
2158
- })
2159
- }
2160
1891
  fn run_coordinator(args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
2161
1892
  let parsed = parse_args(args);
2162
1893
  let workspace = crate::coordinator::WorkspacePath::new(workspace(&parsed, cwd));
@@ -2343,7 +2074,7 @@ mod tests {
2343
2074
  .filter(|spec| spec.default_help)
2344
2075
  .map(|spec| spec.name)
2345
2076
  .collect();
2346
- assert_eq!(expected.len(), 30, "the public catalog is Human30");
2077
+ assert_eq!(expected.len(), 29, "the public catalog is Human29");
2347
2078
  for spec_name in &expected {
2348
2079
  assert!(
2349
2080
  visible.iter().any(|command| command == spec_name),
@@ -2357,7 +2088,7 @@ mod tests {
2357
2088
  expected_sorted.sort();
2358
2089
  assert_eq!(
2359
2090
  actual, expected_sorted,
2360
- "default help must match the exact Human30 public spec set, not a slack threshold; got {visible:?}"
2091
+ "default help must match the exact Human29 public spec set, not a slack threshold; got {visible:?}"
2361
2092
  );
2362
2093
  assert!(
2363
2094
  top_help.contains("copilot"),
@@ -2392,6 +2123,32 @@ mod tests {
2392
2123
  }
2393
2124
  }
2394
2125
 
2126
+ #[test]
2127
+ fn retired_commands_have_no_catalog_machine_or_help_gate() {
2128
+ for command in [
2129
+ "e2e",
2130
+ "allow-peer-talk",
2131
+ "results",
2132
+ "validate",
2133
+ "identity",
2134
+ "sessions",
2135
+ "watch",
2136
+ "peek",
2137
+ "wait-ready",
2138
+ "preflight",
2139
+ ] {
2140
+ assert!(
2141
+ command_spec(command).is_none(),
2142
+ "retired catalog entry: {command}"
2143
+ );
2144
+ assert!(
2145
+ !is_machine_command(command),
2146
+ "retired Machine route: {command}"
2147
+ );
2148
+ assert!(!is_known_subcommand(command), "retired help gate: {command}");
2149
+ }
2150
+ }
2151
+
2395
2152
  #[test]
2396
2153
  fn leaders_help_publishes_selectors_and_prune() {
2397
2154
  let help = command_help(Some("leaders"));
@@ -2405,7 +2162,6 @@ mod tests {
2405
2162
  #[test]
2406
2163
  fn observation_a_commands_have_terminal_tiers() {
2407
2164
  for (command, tier) in [
2408
- ("allow-peer-talk", CommandTier::Core),
2409
2165
  ("approvals", CommandTier::Core),
2410
2166
  ("profile", CommandTier::Core),
2411
2167
  ("install-skill", CommandTier::Core),
@@ -2547,7 +2303,7 @@ mod tests {
2547
2303
  }
2548
2304
  }
2549
2305
  let cwd = tmp_workspace();
2550
- for command in ["sessions", "wait-ready", "peek", "coordinator"] {
2306
+ for command in ["wait", "attach-app-server-leader", "coordinator"] {
2551
2307
  assert_eq!(
2552
2308
  run(&cli_argv(&[command, "--help"]), &cwd),
2553
2309
  ExitCode::Usage,
@@ -2561,13 +2317,42 @@ mod tests {
2561
2317
  );
2562
2318
  }
2563
2319
 
2320
+ #[test]
2321
+ fn public_help_templates_and_cli_guidance_are_english() {
2322
+ fn assert_english(text: &str) {
2323
+ assert!(
2324
+ !text.chars().any(|ch| matches!(ch, '\u{3400}'..='\u{9fff}')),
2325
+ "unauthorized CJK CLI guidance: {text}"
2326
+ );
2327
+ }
2328
+ assert_english(&default_help());
2329
+ assert_eq!(TEAM_TEMPLATE.lines().nth(1), Some("name: current"));
2330
+ for spec in COMMAND_SPECS {
2331
+ assert_english(spec.summary);
2332
+ assert_english(&command_help(Some(spec.name)));
2333
+ }
2334
+ for text in [
2335
+ TEAM_TEMPLATE, WORKER_TEMPLATE, HUMAN_NAVIGATION,
2336
+ crate::cli::route::HELP, crate::cli::COMMS_BOUNDARY_TEXT,
2337
+ crate::cli::QUICK_START_REMINDER, crate::cli::SEND_REMINDER,
2338
+ crate::cli::STATUS_REMINDER,
2339
+ ] {
2340
+ assert_english(text);
2341
+ }
2342
+ for command in ["quick-start", "restart", "send"] {
2343
+ let payload = CliError::Runtime("fixture failure".to_string())
2344
+ .to_payload(Path::new("/tmp/fixture.log"), command);
2345
+ assert_english(&payload.action);
2346
+ }
2347
+ }
2348
+
2564
2349
  #[test]
2565
2350
  fn worker_lifecycle_help_uses_authoritative_spec_usage_and_summary() {
2566
2351
  for name in ["start-agent", "add-agent"] {
2567
2352
  let spec = command_spec(name).expect("registered worker lifecycle command");
2568
2353
  let help = command_help(Some(name));
2569
2354
  assert!(help.contains(spec.usage));
2570
- assert!(help.contains("做什么:"));
2355
+ assert!(help.contains("Purpose:"));
2571
2356
  for flag in [
2572
2357
  "--model MODEL",
2573
2358
  "--effort LEVEL",
@@ -2578,13 +2363,13 @@ mod tests {
2578
2363
  ] {
2579
2364
  assert!(help.contains(flag), "{name} help missing {flag}: {help}");
2580
2365
  }
2581
- assert!(help.contains("下一步") && help.contains("send"));
2366
+ assert!(help.contains("Next Action") && help.contains("send"));
2582
2367
  }
2583
2368
  assert!(!command_help(Some("start-agent")).contains("--force"));
2584
2369
  let add = command_help(Some("add-agent"));
2585
2370
  assert!(add.contains("[--role-file FILE]"));
2586
2371
  assert!(add.contains("provider") && add.contains("bypass") && add.contains("--role-file"));
2587
- assert!(add.contains("冲突") && add.contains("拒绝"));
2372
+ assert!(add.contains("Conflicting") && add.contains("refused"));
2588
2373
  }
2589
2374
 
2590
2375
  #[test]
@@ -2592,17 +2377,17 @@ mod tests {
2592
2377
  let help = command_help(Some("status"));
2593
2378
  for marker in [
2594
2379
  "name/provider/model/effort/runtime_status/activity/health/session_name/tmux_command",
2595
- "--json 给程序读取",
2596
- "unknown 表示证据不足",
2380
+ "Use --json for structured output",
2381
+ "unknown means insufficient evidence",
2597
2382
  "tmux_command",
2598
- "不增加字段",
2383
+ "do not add fields",
2599
2384
  ] {
2600
2385
  assert!(
2601
2386
  help.contains(marker),
2602
2387
  "status help missing {marker}: {help}"
2603
2388
  );
2604
2389
  }
2605
- assert!(!help.contains("错误细分走 status --summary"));
2390
+ assert!(!help.contains("error breakdown via status --summary"));
2606
2391
  }
2607
2392
 
2608
2393
  #[test]
@@ -2741,14 +2526,14 @@ mod tests {
2741
2526
  .unwrap();
2742
2527
  let err = cmd_send(&args).unwrap_err();
2743
2528
  assert!(
2744
- matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("--to") && message.contains("不能") && message.contains("同时")),
2529
+ matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("--to") && message.contains("cannot") && message.contains("combined")),
2745
2530
  "expected --pane/--to mutual-exclusion usage error, got {err:?}"
2746
2531
  );
2747
2532
 
2748
2533
  let args = send_args(&cli_argv(&["--pane", "%1596"]), &cwd).unwrap();
2749
2534
  let err = cmd_send(&args).unwrap_err();
2750
2535
  assert!(
2751
- matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("不能发送空消息")),
2536
+ matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("cannot send an empty message")),
2752
2537
  "expected empty-message usage error, got {err:?}"
2753
2538
  );
2754
2539