@team-agent/installer 0.5.112 → 0.5.114
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 +252 -84
- package/Cargo.toml +3 -1
- package/crates/team-agent/Cargo.toml +3 -0
- package/crates/team-agent/src/cli/adapters.rs +119 -50
- package/crates/team-agent/src/cli/diagnose.rs +36 -13
- package/crates/team-agent/src/cli/emit.rs +173 -109
- package/crates/team-agent/src/cli/leader.rs +16 -0
- package/crates/team-agent/src/cli/leader_prompt.rs +261 -0
- package/crates/team-agent/src/cli/leaders.rs +32 -32
- package/crates/team-agent/src/cli/mod.rs +158 -14
- package/crates/team-agent/src/cli/models.rs +68 -18
- package/crates/team-agent/src/cli/named_address.rs +17 -16
- package/crates/team-agent/src/cli/route.rs +4 -4
- package/crates/team-agent/src/cli/send/presentation.rs +19 -18
- package/crates/team-agent/src/cli/send/resolve.rs +11 -11
- package/crates/team-agent/src/cli/send.rs +4 -4
- package/crates/team-agent/src/cli/spec.rs +35 -29
- package/crates/team-agent/src/cli/status.rs +7 -7
- package/crates/team-agent/src/cli/status_port/brief.rs +268 -101
- package/crates/team-agent/src/cli/tests/base.rs +94 -6
- package/crates/team-agent/src/cli/tests/divergence.rs +3 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
- package/crates/team-agent/src/cli/tests/named_address.rs +3 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +9 -7
- package/crates/team-agent/src/cli/triage.rs +53 -2
- package/crates/team-agent/src/cli/types.rs +8 -8
- package/crates/team-agent/src/compiler/tests.rs +12 -0
- package/crates/team-agent/src/compiler.rs +65 -26
- package/crates/team-agent/src/contract_runtime/backend.rs +646 -0
- package/crates/team-agent/src/contract_runtime/config.rs +309 -0
- package/crates/team-agent/src/contract_runtime/discovery.rs +237 -0
- package/crates/team-agent/src/contract_runtime/forward.rs +229 -0
- package/crates/team-agent/src/contract_runtime/framework.rs +261 -0
- package/crates/team-agent/src/contract_runtime/mcp.rs +396 -0
- package/crates/team-agent/src/contract_runtime/mod.rs +14 -0
- package/crates/team-agent/src/contract_runtime/prompt.rs +216 -0
- package/crates/team-agent/src/contract_runtime/pump.rs +207 -0
- package/crates/team-agent/src/contract_runtime/registry.rs +30 -0
- package/crates/team-agent/src/coordinator/tick.rs +26 -8
- package/crates/team-agent/src/leader/mod.rs +1 -0
- package/crates/team-agent/src/leader/prompt.rs +906 -0
- package/crates/team-agent/src/leader/start.rs +265 -16
- package/crates/team-agent/src/leader/tests/identity.rs +21 -0
- package/crates/team-agent/src/leader/tests/mod.rs +2 -0
- package/crates/team-agent/src/leader/tests/prompt_contract.rs +268 -0
- package/crates/team-agent/src/leader/types.rs +6 -0
- package/crates/team-agent/src/lib.rs +4 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +760 -365
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +38 -9
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +17 -0
- package/crates/team-agent/src/lifecycle/launch.rs +39 -28
- package/crates/team-agent/src/lifecycle/profile_launch.rs +58 -46
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +97 -52
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -2
- package/crates/team-agent/src/lifecycle/types.rs +4 -1
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +37 -27
- package/crates/team-agent/src/main.rs +6 -0
- package/crates/team-agent/src/messaging/delivery.rs +50 -1
- package/crates/team-agent/src/messaging/send.rs +10 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +131 -0
- package/crates/team-agent/src/model/spec.rs +24 -12
- package/crates/team-agent/src/provider/adapter.rs +8 -59
- package/crates/team-agent/src/provider/adapters/claude.rs +6 -14
- package/crates/team-agent/src/provider/adapters/codex.rs +8 -18
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -22
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +8 -45
- package/crates/team-agent/src/provider/adapters/grok.rs +7 -31
- package/crates/team-agent/src/provider/adapters/pi.rs +7 -18
- package/crates/team-agent/src/provider/command_helpers.rs +45 -0
- package/crates/team-agent/src/provider/mod.rs +1 -0
- package/crates/team-agent/src/provider/model_catalog.rs +21 -28
- package/crates/team-agent/src/provider/tests/adapter.rs +70 -0
- package/crates/team-agent/src/provider/tests/command_helpers.rs +55 -0
- package/crates/team-agent/src/state/persist.rs +73 -0
- package/package.json +4 -4
|
@@ -68,6 +68,9 @@ pub fn run(argv: &[String], cwd: &Path) -> ExitCode {
|
|
|
68
68
|
if command == "route" {
|
|
69
69
|
return super::route::run(&argv[1..]);
|
|
70
70
|
}
|
|
71
|
+
if command == "leader-prompt" {
|
|
72
|
+
return super::leader_prompt::run(&argv[1..]);
|
|
73
|
+
}
|
|
71
74
|
if is_leader_passthrough_command(command) {
|
|
72
75
|
return match cmd_leader_passthrough(command, &argv[1..], cwd) {
|
|
73
76
|
Ok(result) => emit_result(result),
|
|
@@ -110,25 +113,25 @@ fn emit_cli_error_for_command(
|
|
|
110
113
|
cwd: &Path,
|
|
111
114
|
error: &CliError,
|
|
112
115
|
) -> ExitCode {
|
|
113
|
-
let missing_input = matches!(error, CliError::Usage(message) if message.starts_with("missing ") || message.starts_with("
|
|
116
|
+
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
117
|
if command_spec(command).is_some() && missing_input {
|
|
115
118
|
let help = command_help(Some(command));
|
|
116
119
|
let explanation = match error {
|
|
117
120
|
CliError::Usage(message) => match message.as_str() {
|
|
118
|
-
"missing agent" | "missing source_agent" => "
|
|
121
|
+
"missing agent" | "missing source_agent" => "Provide an agent name.".to_string(),
|
|
119
122
|
"missing profile command" => {
|
|
120
|
-
"
|
|
123
|
+
"Provide a profile operation: init, doctor or show.".to_string()
|
|
121
124
|
}
|
|
122
|
-
"missing profile name" => "
|
|
125
|
+
"missing profile name" => "Provide an authentication/proxy profile name.".to_string(),
|
|
123
126
|
"add-agent requires --provider <name>" => {
|
|
124
|
-
"
|
|
127
|
+
"Select a provider with --provider, or supply a role file with --role-file.".to_string()
|
|
125
128
|
}
|
|
126
129
|
"add-agent requires --bypass <true|false>" => {
|
|
127
|
-
"
|
|
130
|
+
"Explicitly retain permission prompts with --bypass false, or declare bypass in the role file.".to_string()
|
|
128
131
|
}
|
|
129
132
|
other => other
|
|
130
133
|
.strip_prefix("missing ")
|
|
131
|
-
.map(|field| format!("
|
|
134
|
+
.map(|field| format!("Provide the required argument: {field}."))
|
|
132
135
|
.unwrap_or_else(|| other.to_string()),
|
|
133
136
|
},
|
|
134
137
|
_ => error.to_string(),
|
|
@@ -140,7 +143,7 @@ fn emit_cli_error_for_command(
|
|
|
140
143
|
value["error"] = serde_json::json!(explanation);
|
|
141
144
|
value["action"] = serde_json::json!(format!("team-agent {command} --help"));
|
|
142
145
|
value["next_actions"] = serde_json::json!([format!(
|
|
143
|
-
"
|
|
146
|
+
"Complete the required arguments using the Examples in team-agent {command} --help"
|
|
144
147
|
)]);
|
|
145
148
|
println!("{}", python_compact_json(&value));
|
|
146
149
|
} else {
|
|
@@ -179,7 +182,7 @@ pub(super) fn emit_result(r: CmdResult) -> ExitCode {
|
|
|
179
182
|
let mut stderr = stderr.lock();
|
|
180
183
|
let _ = writeln!(
|
|
181
184
|
stderr,
|
|
182
|
-
"
|
|
185
|
+
"Output unavailable; the message was persisted with message_id={message_id}. Use team-agent inbox leader -n 3 for replies; do not resend."
|
|
183
186
|
);
|
|
184
187
|
}
|
|
185
188
|
return if error.kind() == ErrorKind::BrokenPipe {
|
|
@@ -212,6 +215,9 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
212
215
|
if command == "route" {
|
|
213
216
|
return Ok(super::route::run(args));
|
|
214
217
|
}
|
|
218
|
+
if command == "leader-prompt" {
|
|
219
|
+
return Ok(super::leader_prompt::run(args));
|
|
220
|
+
}
|
|
215
221
|
// Keep the removed flag harmless for older scripts and persisted command
|
|
216
222
|
// lines; the default tmux backend is already the only runtime path.
|
|
217
223
|
let filtered_args: Vec<String> = args
|
|
@@ -255,7 +261,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
255
261
|
)
|
|
256
262
|
{
|
|
257
263
|
return Err(CliError::Usage(
|
|
258
|
-
"
|
|
264
|
+
"Provide the agent name, task message or required command settings; see Usage and Examples below".to_string(),
|
|
259
265
|
));
|
|
260
266
|
}
|
|
261
267
|
match command {
|
|
@@ -290,6 +296,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
290
296
|
// delivery path — no separate route authority.
|
|
291
297
|
"leaders" => cmd_leaders(&leaders_args(args, cwd)?).map(emit_result),
|
|
292
298
|
"models" => cmd_models(&models_args(args)?).map(emit_result),
|
|
299
|
+
"kiro" => Ok(emit_result(cmd_kiro_leader(args))),
|
|
293
300
|
|
|
294
301
|
"install-skill" => cmd_install_skill(&install_skill_args(args)?).map(emit_result),
|
|
295
302
|
"profile" => cmd_profile(&profile_args(args, cwd)?).map(emit_result),
|
|
@@ -300,10 +307,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
300
307
|
|
|
301
308
|
// Script compatibility is deliberately outside the human catalog and suggestions.
|
|
302
309
|
fn is_machine_command(command: &str) -> bool {
|
|
303
|
-
matches!(
|
|
304
|
-
command,
|
|
305
|
-
"wait" | "coordinator" | "attach-app-server-leader"
|
|
306
|
-
)
|
|
310
|
+
matches!(command, "wait" | "coordinator" | "attach-app-server-leader")
|
|
307
311
|
}
|
|
308
312
|
|
|
309
313
|
fn dispatch_machine(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliError> {
|
|
@@ -340,16 +344,16 @@ fn is_known_subcommand(command: &str) -> bool {
|
|
|
340
344
|
}
|
|
341
345
|
|
|
342
346
|
pub(crate) fn default_help() -> String {
|
|
343
|
-
let mut out = String::from("Team Agent
|
|
347
|
+
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");
|
|
344
348
|
append_help_section(
|
|
345
349
|
&mut out,
|
|
346
|
-
"
|
|
350
|
+
"Getting started",
|
|
347
351
|
&["quick-start", "send", "status", "models", "inbox"],
|
|
348
352
|
);
|
|
349
|
-
append_help_section(&mut out, "
|
|
353
|
+
append_help_section(&mut out, "Team lifecycle", &["restart", "shutdown"]);
|
|
350
354
|
append_help_section(
|
|
351
355
|
&mut out,
|
|
352
|
-
"
|
|
356
|
+
"Agent lifecycle",
|
|
353
357
|
&[
|
|
354
358
|
"add-agent",
|
|
355
359
|
"start-agent",
|
|
@@ -362,21 +366,25 @@ pub(crate) fn default_help() -> String {
|
|
|
362
366
|
);
|
|
363
367
|
append_help_section(
|
|
364
368
|
&mut out,
|
|
365
|
-
"
|
|
369
|
+
"Observation and collaboration",
|
|
366
370
|
&["leaders", "doctor", "approvals"],
|
|
367
371
|
);
|
|
368
|
-
append_help_section(&mut out, "设置", &["route", "profile", "install-skill"]);
|
|
369
372
|
append_help_section(
|
|
370
373
|
&mut out,
|
|
371
|
-
"
|
|
374
|
+
"Configuration",
|
|
375
|
+
&["route", "leader-prompt", "profile", "install-skill"],
|
|
376
|
+
);
|
|
377
|
+
append_help_section(
|
|
378
|
+
&mut out,
|
|
379
|
+
"Guided recovery",
|
|
372
380
|
&["claim-leader", "takeover", "attach-leader"],
|
|
373
381
|
);
|
|
374
382
|
append_help_section(
|
|
375
383
|
&mut out,
|
|
376
|
-
"
|
|
377
|
-
&["pi", "codex", "claude", "copilot", "grok", "cursor"],
|
|
384
|
+
"Leader launch",
|
|
385
|
+
&["pi", "codex", "claude", "copilot", "grok", "cursor", "kiro"],
|
|
378
386
|
);
|
|
379
|
-
out.push_str("\
|
|
387
|
+
out.push_str("\nCommand arguments and examples: team-agent <command> --help");
|
|
380
388
|
out
|
|
381
389
|
}
|
|
382
390
|
|
|
@@ -411,9 +419,9 @@ pub fn __test_quick_start_args(
|
|
|
411
419
|
}
|
|
412
420
|
|
|
413
421
|
pub(super) const TEAM_TEMPLATE: &str =
|
|
414
|
-
"---\nname:
|
|
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---\
|
|
416
|
-
const HUMAN_NAVIGATION: &str = "
|
|
422
|
+
"---\nname: current\n---\nA small team for a command-line example.\n";
|
|
423
|
+
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";
|
|
424
|
+
const HUMAN_NAVIGATION: &str = "Use team-agent --help to list available commands.";
|
|
417
425
|
|
|
418
426
|
pub(super) fn command_help(command: Option<&str>) -> String {
|
|
419
427
|
let Some(name) = command else {
|
|
@@ -425,56 +433,61 @@ pub(super) fn command_help(command: Option<&str>) -> String {
|
|
|
425
433
|
if name == "route" {
|
|
426
434
|
return super::route::HELP.to_string();
|
|
427
435
|
}
|
|
436
|
+
if name == "leader-prompt" {
|
|
437
|
+
return super::leader_prompt::HELP.to_string();
|
|
438
|
+
}
|
|
428
439
|
let (details, examples, next) = match name {
|
|
429
440
|
"quick-start" => (
|
|
430
|
-
"TEAMDIR
|
|
441
|
+
"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.",
|
|
431
442
|
"team-agent quick-start\nteam-agent quick-start ./roles --workspace ./project\nteam-agent quick-start . --json",
|
|
432
|
-
"
|
|
433
|
-
"send" => ("<agent>
|
|
434
|
-
"team-agent send worker '
|
|
435
|
-
"
|
|
436
|
-
"inbox" => ("<agent>
|
|
437
|
-
"team-agent inbox leader -n 3\nteam-agent inbox worker --limit 5\nteam-agent inbox leader --workspace . --team help-demo --json", "
|
|
438
|
-
"status" => ("
|
|
439
|
-
"team-agent status\nteam-agent status worker\nteam-agent status --workspace . --team help-demo --json", "
|
|
440
|
-
"models" => ("--provider
|
|
441
|
-
"team-agent models --provider pi\nteam-agent models --provider pi --search luna\nteam-agent models --provider cursor_agent --json", "
|
|
442
|
-
"restart" => ("WORKSPACE
|
|
443
|
-
"team-agent restart .\nteam-agent restart . --team help-demo --json", "
|
|
444
|
-
"shutdown" => ("
|
|
445
|
-
"team-agent shutdown --workspace .\nteam-agent shutdown --workspace . --team help-demo --json", "
|
|
446
|
-
"add-agent" => ("<agent>
|
|
447
|
-
"team-agent add-agent reviewer --provider pi --model openai-codex/gpt-6-luna --bypass false --prompt '
|
|
448
|
-
"start-agent" => ("
|
|
449
|
-
"team-agent start-agent worker\nteam-agent start-agent worker --model openai-codex/gpt-6-luna", "
|
|
450
|
-
"stop-agent" => ("<agent>
|
|
451
|
-
"team-agent stop-agent worker\nteam-agent stop-agent worker --workspace . --team help-demo --json", "
|
|
452
|
-
"reset-agent" => ("
|
|
453
|
-
"team-agent reset-agent worker --discard-session\nteam-agent reset-agent worker --discard-session --workspace . --team help-demo --json", "
|
|
454
|
-
"clone-agent" => ("<agent>
|
|
455
|
-
"team-agent clone-agent worker --as reviewer\nteam-agent clone-agent worker --as reviewer --label '
|
|
456
|
-
"fork-agent" => ("<agent>
|
|
457
|
-
"team-agent fork-agent worker --as experiment\nteam-agent fork-agent worker --as experiment --workspace . --team help-demo --json", "
|
|
458
|
-
"remove-agent" => ("
|
|
459
|
-
"team-agent remove-agent reviewer --confirm\nteam-agent remove-agent reviewer --from-spec --confirm --workspace . --team help-demo --json", "
|
|
460
|
-
"leaders" => ("
|
|
461
|
-
"team-agent leaders\nteam-agent leaders --all --json\nteam-agent leaders --prune --dry-run", "
|
|
462
|
-
"doctor" => ("
|
|
463
|
-
"team-agent doctor --workspace .\nteam-agent doctor --workspace . --team help-demo --json", "
|
|
464
|
-
"approvals" => ("
|
|
465
|
-
"team-agent approvals\nteam-agent approvals worker --json", "
|
|
466
|
-
"profile" => ("init
|
|
467
|
-
"team-agent profile init local --auth-mode subscription\nteam-agent profile doctor local\nteam-agent profile show local", "
|
|
468
|
-
"install-skill" => ("--source
|
|
469
|
-
"team-agent install-skill --source \"$SKILL_DIR\" --target codex --dry-run\nteam-agent install-skill --source \"$SKILL_DIR\" --target codex", "
|
|
470
|
-
"claim-leader" => ("
|
|
471
|
-
"team-agent claim-leader --workspace . --json\nteam-agent claim-leader --workspace . --team help-demo --confirm --json", "
|
|
472
|
-
"takeover" => ("
|
|
473
|
-
"team-agent takeover --workspace . --team help-demo --confirm\nteam-agent takeover --workspace . --team help-demo --confirm --json", "
|
|
474
|
-
"attach-leader" => ("PANE
|
|
475
|
-
"team-agent attach-leader --pane \"$PANE\" --provider pi --workspace .\nteam-agent attach-leader --pane \"$PANE\" --provider pi --workspace . --team help-demo --confirm --json", "
|
|
476
|
-
|
|
477
|
-
"", "
|
|
443
|
+
"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."),
|
|
444
|
+
"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.",
|
|
445
|
+
"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",
|
|
446
|
+
"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."),
|
|
447
|
+
"inbox" => ("<agent> is the recipient name; -n/--limit bounds the message count. Reading replies does not resend messages.",
|
|
448
|
+
"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."),
|
|
449
|
+
"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.",
|
|
450
|
+
"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 ."),
|
|
451
|
+
"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.",
|
|
452
|
+
"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."),
|
|
453
|
+
"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.",
|
|
454
|
+
"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."),
|
|
455
|
+
"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.",
|
|
456
|
+
"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."),
|
|
457
|
+
"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.",
|
|
458
|
+
"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'."),
|
|
459
|
+
"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.",
|
|
460
|
+
"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."),
|
|
461
|
+
"stop-agent" => ("<agent> names the agent to stop. This is not removal; configuration and session history are retained.",
|
|
462
|
+
"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."),
|
|
463
|
+
"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.",
|
|
464
|
+
"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."),
|
|
465
|
+
"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.",
|
|
466
|
+
"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."),
|
|
467
|
+
"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.",
|
|
468
|
+
"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."),
|
|
469
|
+
"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.",
|
|
470
|
+
"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."),
|
|
471
|
+
"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.",
|
|
472
|
+
"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."),
|
|
473
|
+
"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.",
|
|
474
|
+
"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."),
|
|
475
|
+
"approvals" => ("Optional <agent> selects one agent. Observes permission prompts without granting approval.",
|
|
476
|
+
"team-agent approvals\nteam-agent approvals worker --json", "Verify the requested permission, handle it in the actual provider prompt, then check status."),
|
|
477
|
+
"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.",
|
|
478
|
+
"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."),
|
|
479
|
+
"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.",
|
|
480
|
+
"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."),
|
|
481
|
+
"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.",
|
|
482
|
+
"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."),
|
|
483
|
+
"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.",
|
|
484
|
+
"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."),
|
|
485
|
+
"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.",
|
|
486
|
+
"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."),
|
|
487
|
+
"kiro" => ("Kiro leader launching is not admitted by this candidate. This entry returns the stable capability refusal kiro_leader_not_admitted (exit 1).\nNo native process, leader binding, attach or provider fallback is attempted. --help/-h is read-only and exits 0.",
|
|
488
|
+
"team-agent kiro\nteam-agent kiro --json", "Use provider: kiro in a worker role with team-agent quick-start; worker support does not grant leader capability."),
|
|
489
|
+
_ => ("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.",
|
|
490
|
+
"", "Run team-agent quick-start in the opened leader's tool context, not in an ordinary worker conversation."),
|
|
478
491
|
};
|
|
479
492
|
let examples = if matches!(spec.kind, CommandKind::LeaderPassthrough { .. }) {
|
|
480
493
|
let native = if name == "cursor" { "agent" } else { name };
|
|
@@ -486,31 +499,38 @@ pub(super) fn command_help(command: Option<&str>) -> String {
|
|
|
486
499
|
} else {
|
|
487
500
|
examples.to_string()
|
|
488
501
|
};
|
|
489
|
-
let scope = if
|
|
490
|
-
"--json
|
|
502
|
+
let scope = if name == "kiro" {
|
|
503
|
+
"Use --json for the structured capability refusal. Native arguments and attach options cannot enable this capability."
|
|
504
|
+
} else if matches!(spec.kind, CommandKind::LeaderPassthrough { .. }) {
|
|
505
|
+
"Use --json for structured output. --workspace/--team are not launcher options here; they pass to the native provider."
|
|
491
506
|
} else {
|
|
492
|
-
"--json
|
|
507
|
+
"Use --json for structured output. --workspace selects the project; supported commands use --team to select the team."
|
|
493
508
|
};
|
|
494
|
-
let mut out = format!(
|
|
509
|
+
let mut out = format!(
|
|
510
|
+
"Purpose:\n{}.\nUsage: {}\n\nOptions:\n{}\n{}\nExamples:\n{}\n\nNext Action:\n{}",
|
|
511
|
+
spec.summary, spec.usage, details, scope, examples, next
|
|
512
|
+
);
|
|
495
513
|
if name == "quick-start" {
|
|
496
|
-
out.push_str(&format!("\n\
|
|
514
|
+
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}"));
|
|
497
515
|
}
|
|
498
516
|
if name == "cursor" {
|
|
499
|
-
out.push_str("\
|
|
517
|
+
out.push_str("\nDiscover models with team-agent models --provider cursor_agent.");
|
|
500
518
|
}
|
|
501
519
|
if name == "copilot" {
|
|
502
|
-
out.push_str(
|
|
520
|
+
out.push_str(
|
|
521
|
+
"\nCopilot has no team-agent models entrypoint; consult native provider help.",
|
|
522
|
+
);
|
|
503
523
|
}
|
|
504
524
|
out
|
|
505
525
|
}
|
|
506
526
|
|
|
507
527
|
fn emit_unknown_subcommand_usage(command: &str) -> ExitCode {
|
|
508
528
|
emit_usage_error(&format!(
|
|
509
|
-
"
|
|
529
|
+
"Unknown command: '{command}'. Use team-agent --help to list available commands."
|
|
510
530
|
));
|
|
511
531
|
// E8 (N38): 错路引导 —— 拼写近似时建议最接近的真子命令(additive,不改既有 golden 行)。
|
|
512
532
|
if let Some(suggestion) = nearest_subcommand(command) {
|
|
513
|
-
eprintln!("
|
|
533
|
+
eprintln!("Did you mean `{suggestion}`? See team-agent {suggestion} --help for examples.");
|
|
514
534
|
}
|
|
515
535
|
// 0.5.45 naming-addressing (RED-6): unknown subcommand exits 1
|
|
516
536
|
// (Error), aligned with the family of typo refusals throughout
|
|
@@ -542,8 +562,8 @@ fn nearest_subcommand(input: &str) -> Option<&'static str> {
|
|
|
542
562
|
}
|
|
543
563
|
|
|
544
564
|
fn emit_usage_error(message: &str) {
|
|
545
|
-
eprintln!("
|
|
546
|
-
eprintln!("
|
|
565
|
+
eprintln!("Usage: team-agent <command>; list commands with team-agent --help");
|
|
566
|
+
eprintln!("Error: {message}");
|
|
547
567
|
}
|
|
548
568
|
|
|
549
569
|
/// `install-skill` 参数(RED-1 根治:把 skill 安装单源收敛到二进制,install.mjs 调它)。
|
|
@@ -692,12 +712,12 @@ fn emit_cli_error(command: &str, args: &[String], cwd: &Path, error: &CliError)
|
|
|
692
712
|
{
|
|
693
713
|
if let Some(message) = super::named_address::human_address_reason(reason) {
|
|
694
714
|
payload.error = message.to_string();
|
|
695
|
-
payload.action = "
|
|
715
|
+
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();
|
|
696
716
|
}
|
|
697
717
|
}
|
|
698
718
|
if command_spec(command).is_some()
|
|
699
719
|
&& payload.action
|
|
700
|
-
== "
|
|
720
|
+
== "Run team-agent doctor --workspace . for the selected team, or inspect the error log shown here."
|
|
701
721
|
{
|
|
702
722
|
let mut doctor = format!(
|
|
703
723
|
"team-agent doctor --workspace {}",
|
|
@@ -706,7 +726,7 @@ fn emit_cli_error(command: &str, args: &[String], cwd: &Path, error: &CliError)
|
|
|
706
726
|
if let Some(team) = parsed.team.as_deref() {
|
|
707
727
|
doctor.push_str(&format!(" --team {}", super::adapters::shell_quote(team)));
|
|
708
728
|
}
|
|
709
|
-
payload.action = format!("
|
|
729
|
+
payload.action = format!("Run {doctor} for the selected team, or inspect the error log shown here.");
|
|
710
730
|
}
|
|
711
731
|
payload.action = crate::redaction::redact_external_text(&payload.action);
|
|
712
732
|
payload.log = crate::redaction::redact_external_text(&payload.log);
|
|
@@ -1083,7 +1103,7 @@ fn quick_start_args(args: &[String], cwd: &Path) -> Result<QuickStartArgs, CliEr
|
|
|
1083
1103
|
let normalized = literal.trim().to_ascii_lowercase();
|
|
1084
1104
|
if normalized != "tmux" && normalized != "conpty" {
|
|
1085
1105
|
return Err(CliError::Usage(format!(
|
|
1086
|
-
"--backend
|
|
1106
|
+
"--backend must be tmux or conpty; received {literal:?}. Use tmux on POSIX. ConPTY requires a Windows host and an installed shim."
|
|
1087
1107
|
)));
|
|
1088
1108
|
}
|
|
1089
1109
|
}
|
|
@@ -1137,7 +1157,7 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
|
|
|
1137
1157
|
&& parsed.positionals.len() < 2
|
|
1138
1158
|
{
|
|
1139
1159
|
return Err(CliError::Usage(
|
|
1140
|
-
"
|
|
1160
|
+
"Provide a recipient agent name and task message, such as team-agent send worker 'Review the changes'".to_string(),
|
|
1141
1161
|
));
|
|
1142
1162
|
}
|
|
1143
1163
|
let target = if parsed.targets.is_some()
|
|
@@ -1171,7 +1191,7 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
|
|
|
1171
1191
|
)
|
|
1172
1192
|
.map_err(|error| CliError::Usage(format!("invalid send routing: {error}")))?;
|
|
1173
1193
|
if normalized.deprecation.is_some() {
|
|
1174
|
-
eprintln!("
|
|
1194
|
+
eprintln!("Warning: legacy send settings are deprecated. Use team-agent send <agent> 'task message'; add --mailbox for storage without injection.");
|
|
1175
1195
|
}
|
|
1176
1196
|
Ok(SendArgs {
|
|
1177
1197
|
target,
|
|
@@ -1250,16 +1270,20 @@ fn warn_send_legacy_delivery_flags(args: &[String]) {
|
|
|
1250
1270
|
"--message-id",
|
|
1251
1271
|
];
|
|
1252
1272
|
let spec = command_spec("send");
|
|
1253
|
-
let sunset = spec
|
|
1254
|
-
|
|
1255
|
-
.
|
|
1256
|
-
|
|
1273
|
+
let sunset = spec
|
|
1274
|
+
.and_then(|spec| spec.sunset)
|
|
1275
|
+
.unwrap_or("a future compatibility release");
|
|
1276
|
+
let action = spec.and_then(|spec| spec.action).unwrap_or(
|
|
1277
|
+
"Use team-agent send <agent> 'task message'; select scope with --workspace/--team",
|
|
1278
|
+
);
|
|
1257
1279
|
for flag in FLAGS {
|
|
1258
1280
|
if args
|
|
1259
1281
|
.iter()
|
|
1260
1282
|
.any(|arg| arg == flag || arg.starts_with(&format!("{flag}=")))
|
|
1261
1283
|
{
|
|
1262
|
-
eprintln!(
|
|
1284
|
+
eprintln!(
|
|
1285
|
+
"Warning: {flag} is deprecated and will be removed in {sunset}. Next: {action}"
|
|
1286
|
+
);
|
|
1263
1287
|
}
|
|
1264
1288
|
}
|
|
1265
1289
|
}
|
|
@@ -1673,7 +1697,7 @@ fn reset_agent_args(args: &[String], cwd: &Path) -> Result<ResetAgentArgs, CliEr
|
|
|
1673
1697
|
let agent = required_pos(&parsed, 0, "agent")?;
|
|
1674
1698
|
if !parsed.discard_session {
|
|
1675
1699
|
return Err(CliError::Usage(
|
|
1676
|
-
"missing --discard-session
|
|
1700
|
+
"missing --discard-session; add this option only after explicit authorization to discard the saved session association".to_string(),
|
|
1677
1701
|
));
|
|
1678
1702
|
}
|
|
1679
1703
|
let workspace = workspace(&parsed, cwd);
|
|
@@ -2074,7 +2098,11 @@ mod tests {
|
|
|
2074
2098
|
.filter(|spec| spec.default_help)
|
|
2075
2099
|
.map(|spec| spec.name)
|
|
2076
2100
|
.collect();
|
|
2077
|
-
assert_eq!(
|
|
2101
|
+
assert_eq!(
|
|
2102
|
+
expected.len(),
|
|
2103
|
+
31,
|
|
2104
|
+
"the public catalog includes Leader prompt management and the Kiro capability gate"
|
|
2105
|
+
);
|
|
2078
2106
|
for spec_name in &expected {
|
|
2079
2107
|
assert!(
|
|
2080
2108
|
visible.iter().any(|command| command == spec_name),
|
|
@@ -2088,7 +2116,7 @@ mod tests {
|
|
|
2088
2116
|
expected_sorted.sort();
|
|
2089
2117
|
assert_eq!(
|
|
2090
2118
|
actual, expected_sorted,
|
|
2091
|
-
"default help must match the exact
|
|
2119
|
+
"default help must match the exact public spec set, not a slack threshold; got {visible:?}"
|
|
2092
2120
|
);
|
|
2093
2121
|
assert!(
|
|
2094
2122
|
top_help.contains("copilot"),
|
|
@@ -2145,7 +2173,10 @@ mod tests {
|
|
|
2145
2173
|
!is_machine_command(command),
|
|
2146
2174
|
"retired Machine route: {command}"
|
|
2147
2175
|
);
|
|
2148
|
-
assert!(
|
|
2176
|
+
assert!(
|
|
2177
|
+
!is_known_subcommand(command),
|
|
2178
|
+
"retired help gate: {command}"
|
|
2179
|
+
);
|
|
2149
2180
|
}
|
|
2150
2181
|
}
|
|
2151
2182
|
|
|
@@ -2317,13 +2348,46 @@ mod tests {
|
|
|
2317
2348
|
);
|
|
2318
2349
|
}
|
|
2319
2350
|
|
|
2351
|
+
#[test]
|
|
2352
|
+
fn public_help_templates_and_cli_guidance_are_english() {
|
|
2353
|
+
fn assert_english(text: &str) {
|
|
2354
|
+
assert!(
|
|
2355
|
+
!text.chars().any(|ch| matches!(ch, '\u{3400}'..='\u{9fff}')),
|
|
2356
|
+
"unauthorized CJK CLI guidance: {text}"
|
|
2357
|
+
);
|
|
2358
|
+
}
|
|
2359
|
+
assert_english(&default_help());
|
|
2360
|
+
assert_eq!(TEAM_TEMPLATE.lines().nth(1), Some("name: current"));
|
|
2361
|
+
for spec in COMMAND_SPECS {
|
|
2362
|
+
assert_english(spec.summary);
|
|
2363
|
+
assert_english(&command_help(Some(spec.name)));
|
|
2364
|
+
}
|
|
2365
|
+
for text in [
|
|
2366
|
+
TEAM_TEMPLATE,
|
|
2367
|
+
WORKER_TEMPLATE,
|
|
2368
|
+
HUMAN_NAVIGATION,
|
|
2369
|
+
crate::cli::route::HELP,
|
|
2370
|
+
crate::cli::COMMS_BOUNDARY_TEXT,
|
|
2371
|
+
crate::cli::QUICK_START_REMINDER,
|
|
2372
|
+
crate::cli::SEND_REMINDER,
|
|
2373
|
+
crate::cli::STATUS_REMINDER,
|
|
2374
|
+
] {
|
|
2375
|
+
assert_english(text);
|
|
2376
|
+
}
|
|
2377
|
+
for command in ["quick-start", "restart", "send"] {
|
|
2378
|
+
let payload = CliError::Runtime("fixture failure".to_string())
|
|
2379
|
+
.to_payload(Path::new("/tmp/fixture.log"), command);
|
|
2380
|
+
assert_english(&payload.action);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2320
2384
|
#[test]
|
|
2321
2385
|
fn worker_lifecycle_help_uses_authoritative_spec_usage_and_summary() {
|
|
2322
2386
|
for name in ["start-agent", "add-agent"] {
|
|
2323
2387
|
let spec = command_spec(name).expect("registered worker lifecycle command");
|
|
2324
2388
|
let help = command_help(Some(name));
|
|
2325
2389
|
assert!(help.contains(spec.usage));
|
|
2326
|
-
assert!(help.contains("
|
|
2390
|
+
assert!(help.contains("Purpose:"));
|
|
2327
2391
|
for flag in [
|
|
2328
2392
|
"--model MODEL",
|
|
2329
2393
|
"--effort LEVEL",
|
|
@@ -2334,13 +2398,13 @@ mod tests {
|
|
|
2334
2398
|
] {
|
|
2335
2399
|
assert!(help.contains(flag), "{name} help missing {flag}: {help}");
|
|
2336
2400
|
}
|
|
2337
|
-
assert!(help.contains("
|
|
2401
|
+
assert!(help.contains("Next Action") && help.contains("send"));
|
|
2338
2402
|
}
|
|
2339
2403
|
assert!(!command_help(Some("start-agent")).contains("--force"));
|
|
2340
2404
|
let add = command_help(Some("add-agent"));
|
|
2341
2405
|
assert!(add.contains("[--role-file FILE]"));
|
|
2342
2406
|
assert!(add.contains("provider") && add.contains("bypass") && add.contains("--role-file"));
|
|
2343
|
-
assert!(add.contains("
|
|
2407
|
+
assert!(add.contains("Conflicting") && add.contains("refused"));
|
|
2344
2408
|
}
|
|
2345
2409
|
|
|
2346
2410
|
#[test]
|
|
@@ -2348,17 +2412,17 @@ mod tests {
|
|
|
2348
2412
|
let help = command_help(Some("status"));
|
|
2349
2413
|
for marker in [
|
|
2350
2414
|
"name/provider/model/effort/runtime_status/activity/health/session_name/tmux_command",
|
|
2351
|
-
"--json
|
|
2352
|
-
"unknown
|
|
2415
|
+
"Use --json for structured output",
|
|
2416
|
+
"unknown means insufficient evidence",
|
|
2353
2417
|
"tmux_command",
|
|
2354
|
-
"
|
|
2418
|
+
"do not add fields",
|
|
2355
2419
|
] {
|
|
2356
2420
|
assert!(
|
|
2357
2421
|
help.contains(marker),
|
|
2358
2422
|
"status help missing {marker}: {help}"
|
|
2359
2423
|
);
|
|
2360
2424
|
}
|
|
2361
|
-
assert!(!help.contains("
|
|
2425
|
+
assert!(!help.contains("error breakdown via status --summary"));
|
|
2362
2426
|
}
|
|
2363
2427
|
|
|
2364
2428
|
#[test]
|
|
@@ -2497,14 +2561,14 @@ mod tests {
|
|
|
2497
2561
|
.unwrap();
|
|
2498
2562
|
let err = cmd_send(&args).unwrap_err();
|
|
2499
2563
|
assert!(
|
|
2500
|
-
matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("--to") && message.contains("
|
|
2564
|
+
matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("--to") && message.contains("cannot") && message.contains("combined")),
|
|
2501
2565
|
"expected --pane/--to mutual-exclusion usage error, got {err:?}"
|
|
2502
2566
|
);
|
|
2503
2567
|
|
|
2504
2568
|
let args = send_args(&cli_argv(&["--pane", "%1596"]), &cwd).unwrap();
|
|
2505
2569
|
let err = cmd_send(&args).unwrap_err();
|
|
2506
2570
|
assert!(
|
|
2507
|
-
matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("
|
|
2571
|
+
matches!(err, CliError::Usage(ref message) if message.contains("--pane") && message.contains("cannot send an empty message")),
|
|
2508
2572
|
"expected empty-message usage error, got {err:?}"
|
|
2509
2573
|
);
|
|
2510
2574
|
|
|
@@ -116,6 +116,22 @@ pub fn cmd_leader_passthrough(
|
|
|
116
116
|
Ok(CmdResult::from_json(value, as_json))
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/// Public capability gate, not a legacy passthrough or a native launch attempt.
|
|
120
|
+
pub(crate) fn cmd_kiro_leader(args: &[String]) -> CmdResult {
|
|
121
|
+
CmdResult::from_json(
|
|
122
|
+
json!({
|
|
123
|
+
"schema_version": "leader_launch.v1",
|
|
124
|
+
"ok": false,
|
|
125
|
+
"provider": "kiro",
|
|
126
|
+
"capability": "leader_launch",
|
|
127
|
+
"reason": "kiro_leader_not_admitted",
|
|
128
|
+
"error": "Kiro leader launching is not admitted by this candidate; no native process was started.",
|
|
129
|
+
"action": "Use provider: kiro in a worker role with team-agent quick-start. Worker support does not grant leader capability.",
|
|
130
|
+
}),
|
|
131
|
+
leader_launcher_json(args),
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
119
135
|
/// Closed-set verb → Provider. `None` = not a passthrough verb (must not become ClaudeCode).
|
|
120
136
|
pub(crate) fn leader_passthrough_provider(command: &str) -> Option<crate::model::enums::Provider> {
|
|
121
137
|
use crate::model::enums::Provider;
|