@team-agent/installer 0.3.2 → 0.3.3
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 +34 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +196 -19
- package/crates/team-agent/src/cli/diagnose.rs +144 -10
- package/crates/team-agent/src/cli/emit.rs +286 -52
- package/crates/team-agent/src/cli/leader.rs +37 -8
- package/crates/team-agent/src/cli/mod.rs +799 -316
- package/crates/team-agent/src/cli/status_port.rs +25 -2
- package/crates/team-agent/src/cli/tests/divergence.rs +1 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +23 -13
- package/crates/team-agent/src/cli/tests/main_preserved.rs +2 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +57 -3
- package/crates/team-agent/src/cli/types.rs +17 -0
- package/crates/team-agent/src/compiler.rs +15 -5
- package/crates/team-agent/src/coordinator/health.rs +89 -20
- package/crates/team-agent/src/coordinator/mod.rs +4 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +500 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +58 -0
- package/crates/team-agent/src/coordinator/tick.rs +222 -69
- package/crates/team-agent/src/coordinator/types.rs +15 -3
- package/crates/team-agent/src/db/schema.rs +37 -2
- package/crates/team-agent/src/diagnose/comms.rs +226 -0
- package/crates/team-agent/src/diagnose/mod.rs +45 -0
- package/crates/team-agent/src/diagnose/orphans.rs +658 -0
- package/crates/team-agent/src/fake_worker.rs +146 -3
- package/crates/team-agent/src/leader/start.rs +121 -23
- package/crates/team-agent/src/leader/types.rs +44 -1
- package/crates/team-agent/src/lib.rs +3 -0
- package/crates/team-agent/src/lifecycle/display.rs +645 -47
- package/crates/team-agent/src/lifecycle/launch.rs +818 -116
- package/crates/team-agent/src/lifecycle/mod.rs +2 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +810 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +522 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +99 -23
- package/crates/team-agent/src/lifecycle/restart/common.rs +177 -83
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +443 -9
- package/crates/team-agent/src/lifecycle/restart/remove.rs +22 -6
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +19 -0
- package/crates/team-agent/src/lifecycle/restart.rs +4 -1
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +5 -5
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +37 -7
- package/crates/team-agent/src/lifecycle/types.rs +19 -0
- package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +341 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +10 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +158 -0
- package/crates/team-agent/src/mcp_server/mod.rs +3 -74
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +1 -1
- package/crates/team-agent/src/mcp_server/tests/send.rs +6 -5
- package/crates/team-agent/src/mcp_server/tools.rs +312 -111
- package/crates/team-agent/src/mcp_server/types.rs +6 -4
- package/crates/team-agent/src/mcp_server/wire.rs +19 -7
- package/crates/team-agent/src/message_store.rs +21 -4
- package/crates/team-agent/src/messaging/delivery.rs +87 -37
- package/crates/team-agent/src/messaging/mod.rs +9 -6
- package/crates/team-agent/src/messaging/results.rs +153 -16
- package/crates/team-agent/src/messaging/selftest.rs +199 -12
- package/crates/team-agent/src/messaging/send.rs +35 -3
- package/crates/team-agent/src/messaging/tests/runtime.rs +19 -4
- package/crates/team-agent/src/messaging/types.rs +11 -3
- package/crates/team-agent/src/os_probe.rs +119 -0
- package/crates/team-agent/src/packaging/migrate.rs +10 -2
- package/crates/team-agent/src/packaging/tests.rs +23 -0
- package/crates/team-agent/src/provider/adapter.rs +483 -67
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +1 -7
- package/crates/team-agent/src/provider/classify.rs +51 -4
- package/crates/team-agent/src/provider/startup_prompt.rs +94 -0
- package/crates/team-agent/src/provider/types.rs +47 -0
- package/crates/team-agent/src/session_capture.rs +616 -0
- package/crates/team-agent/src/state/persist.rs +57 -0
- package/crates/team-agent/src/state/projection.rs +32 -23
- package/crates/team-agent/src/state/selector.rs +5 -2
- package/crates/team-agent/src/tmux_backend.rs +97 -60
- package/crates/team-agent/src/transport/test_support.rs +9 -0
- package/crates/team-agent/src/transport/tests/wire.rs +4 -0
- package/crates/team-agent/src/transport.rs +13 -2
- package/package.json +4 -4
|
@@ -40,19 +40,18 @@ pub fn run(argv: &[String], cwd: &Path) -> ExitCode {
|
|
|
40
40
|
println!("{}", command_help(None));
|
|
41
41
|
return ExitCode::Ok;
|
|
42
42
|
}
|
|
43
|
-
// CR-063/G4: every
|
|
44
|
-
// before argument validation, leader-pane checks, or runtime-state writes.
|
|
45
|
-
// `command_has_help` whitelist (quick-start/start/stop/...) silently dropped `--help`
|
|
46
|
-
// for add-agent / stop-agent / reset-agent / claim-leader / attach-leader, so those
|
|
47
|
-
// fell into handlers that emitted "missing agent" or "caller_not_leader_shaped" and
|
|
48
|
-
// created `.team/logs/cli-error-*.log` under cwd.
|
|
43
|
+
// CR-063/G4: every registered subcommand's `--help` must short-circuit before dispatch,
|
|
44
|
+
// before argument validation, leader-pane checks, or runtime-state writes.
|
|
49
45
|
//
|
|
50
46
|
// The gate stays on KNOWN subcommands so an unknown command still falls through to
|
|
51
47
|
// the argparse-style invalid-choice path (golden parser.py:84; covered by
|
|
52
48
|
// `cli_unknown_command_red` and the `claude_code` divergence guard which would
|
|
53
49
|
// otherwise be silently passthrough-shaped).
|
|
54
50
|
if is_known_subcommand(command)
|
|
55
|
-
&& argv
|
|
51
|
+
&& argv
|
|
52
|
+
.iter()
|
|
53
|
+
.skip(1)
|
|
54
|
+
.any(|arg| matches!(arg.as_str(), "-h" | "--help"))
|
|
56
55
|
{
|
|
57
56
|
println!("{}", command_help(Some(command)));
|
|
58
57
|
return ExitCode::Ok;
|
|
@@ -78,8 +77,11 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
78
77
|
"quick-start" => cmd_quick_start(&quick_start_args(args, cwd)?).map(emit_result),
|
|
79
78
|
"compile" => cmd_compile(&compile_args(args, cwd)?).map(emit_result),
|
|
80
79
|
"send" => cmd_send(&send_args(args, cwd)?).map(emit_result),
|
|
81
|
-
"allow-peer-talk" =>
|
|
82
|
-
|
|
80
|
+
"allow-peer-talk" => {
|
|
81
|
+
cmd_allow_peer_talk(&allow_peer_talk_args(args, cwd)?).map(emit_result)
|
|
82
|
+
}
|
|
83
|
+
"status" => cmd_status_for_team(&status_args(args, cwd), parse_args(args).team.as_deref())
|
|
84
|
+
.map(emit_result),
|
|
83
85
|
"stop" => cmd_shutdown(&shutdown_args(args, cwd)).map(emit_result),
|
|
84
86
|
"shutdown" => cmd_shutdown(&shutdown_args(args, cwd)).map(emit_result),
|
|
85
87
|
"restart" => cmd_restart(&restart_args(args, cwd)).map(emit_result),
|
|
@@ -92,9 +94,13 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
92
94
|
"remove-agent" => cmd_remove_agent(&remove_agent_args(args, cwd)?).map(emit_result),
|
|
93
95
|
"stuck-list" => cmd_stuck_list(&stuck_list_args(args, cwd)).map(emit_result),
|
|
94
96
|
"stuck-cancel" => cmd_stuck_cancel(&stuck_cancel_args(args, cwd)?).map(emit_result),
|
|
95
|
-
"acknowledge-idle" =>
|
|
97
|
+
"acknowledge-idle" => {
|
|
98
|
+
cmd_acknowledge_idle(&acknowledge_idle_args(args, cwd)).map(emit_result)
|
|
99
|
+
}
|
|
96
100
|
"takeover" => cmd_takeover(&takeover_args(args, cwd)).map(emit_result),
|
|
97
101
|
"claim-leader" => cmd_claim_leader(&claim_leader_args(args, cwd)).map(emit_result),
|
|
102
|
+
// Real dispatch: `cmd_attach_leader` writes the `leader_receiver` binding.
|
|
103
|
+
"attach-leader" => cmd_attach_leader(&attach_leader_args(args, cwd)?).map(emit_result),
|
|
98
104
|
"identity" => cmd_identity(&identity_args(args, cwd)).map(emit_result),
|
|
99
105
|
"approvals" => cmd_approvals(&approvals_args(args, cwd)).map(emit_result),
|
|
100
106
|
"inbox" => cmd_inbox(&inbox_args(args, cwd)?).map(emit_result),
|
|
@@ -108,7 +114,10 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
108
114
|
Ok(ExitCode::Usage)
|
|
109
115
|
}
|
|
110
116
|
"validate-result" => cmd_validate_result(&validate_result_args(args)?).map(emit_result),
|
|
111
|
-
"collect" =>
|
|
117
|
+
"collect" => {
|
|
118
|
+
cmd_collect_for_team(&collect_args(args, cwd), parse_args(args).team.as_deref())
|
|
119
|
+
.map(emit_result)
|
|
120
|
+
}
|
|
112
121
|
"settle" => cmd_settle(&settle_args(args, cwd)).map(emit_result),
|
|
113
122
|
"repair-state" => cmd_repair_state(&repair_state_args(args, cwd)?).map(emit_result),
|
|
114
123
|
"diagnose" => cmd_diagnose(&diagnose_args(args, cwd)).map(emit_result),
|
|
@@ -143,6 +152,7 @@ const DISPATCH_COMMANDS: &[&str] = &[
|
|
|
143
152
|
"acknowledge-idle",
|
|
144
153
|
"takeover",
|
|
145
154
|
"claim-leader",
|
|
155
|
+
"attach-leader",
|
|
146
156
|
"identity",
|
|
147
157
|
"approvals",
|
|
148
158
|
"inbox",
|
|
@@ -163,15 +173,15 @@ const DISPATCH_COMMANDS: &[&str] = &[
|
|
|
163
173
|
"coordinator",
|
|
164
174
|
];
|
|
165
175
|
|
|
166
|
-
const SPEC_ONLY_HELP_COMMANDS: &[&str] = &["start", "purge-agent"
|
|
176
|
+
const SPEC_ONLY_HELP_COMMANDS: &[&str] = &["start", "purge-agent"];
|
|
167
177
|
|
|
168
178
|
fn emit_missing_subcommand_usage() -> ExitCode {
|
|
169
179
|
emit_usage_error("the following arguments are required: {codex,claude,...,doctor}");
|
|
170
180
|
ExitCode::Usage
|
|
171
181
|
}
|
|
172
182
|
|
|
173
|
-
/// Registered subcommands (the dispatch table)
|
|
174
|
-
/// dispatch arm yet but must still respond to `--help` per CR-063/G4
|
|
183
|
+
/// Registered subcommands (the dispatch table) plus spec-only verbs that have no
|
|
184
|
+
/// dispatch arm yet but must still respond to `--help` per CR-063/G4.
|
|
175
185
|
/// Used by the `--help` short-circuit gate so unknown commands keep falling through
|
|
176
186
|
/// to the argparse invalid-choice path.
|
|
177
187
|
fn is_known_subcommand(command: &str) -> bool {
|
|
@@ -198,7 +208,7 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
198
208
|
Some("status") => "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]".to_string(),
|
|
199
209
|
Some("stop") => "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
|
|
200
210
|
Some("shutdown") => "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
|
|
201
|
-
Some("restart") => "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--json]".to_string(),
|
|
211
|
+
Some("restart") => "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json]".to_string(),
|
|
202
212
|
Some("restart-agent") => "usage: team-agent restart-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]".to_string(),
|
|
203
213
|
Some("reset-agent") => "usage: team-agent reset-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]".to_string(),
|
|
204
214
|
Some("start-agent") => "usage: team-agent start-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--allow-fresh] [--no-display] [--json]".to_string(),
|
|
@@ -212,7 +222,7 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
212
222
|
Some("acknowledge-idle") => "usage: team-agent acknowledge-idle [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
213
223
|
Some("takeover") => "usage: team-agent takeover [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
214
224
|
Some("claim-leader") => "usage: team-agent claim-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
215
|
-
Some("attach-leader") => "usage: team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]".to_string(),
|
|
225
|
+
Some("attach-leader") => "usage: team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] [--pane PANE] [--provider PROVIDER] [--confirm] [--json]".to_string(),
|
|
216
226
|
Some("identity") => "usage: team-agent identity [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
217
227
|
Some("approvals") => "usage: team-agent approvals [AGENT] [--workspace WORKSPACE] [--json]".to_string(),
|
|
218
228
|
Some("inbox") => "usage: team-agent inbox AGENT [--workspace WORKSPACE] [--limit N] [--since CURSOR] [--json]".to_string(),
|
|
@@ -223,13 +233,13 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
223
233
|
Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--json]".to_string(),
|
|
224
234
|
Some("validate-result") => "usage: team-agent validate-result [ENVELOPE] [--file FILE|--result JSON] [--json]".to_string(),
|
|
225
235
|
Some("collect") => "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]".to_string(),
|
|
226
|
-
Some("settle") => "usage: team-agent settle [--workspace WORKSPACE] [--json]".to_string(),
|
|
236
|
+
Some("settle") => "usage: team-agent settle [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
227
237
|
Some("repair-state") => "usage: team-agent repair-state --task TASK --status STATUS [SUMMARY] [--assignee AGENT] [--workspace WORKSPACE] [--json]".to_string(),
|
|
228
238
|
Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--json]".to_string(),
|
|
229
239
|
Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
|
|
230
240
|
Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--timeout SECONDS] [--json]".to_string(),
|
|
231
241
|
Some("e2e") => "usage: team-agent e2e [--workspace WORKSPACE] [--providers LIST] [--real] [--json]".to_string(),
|
|
232
|
-
Some("peek") => "usage: team-agent peek AGENT [--workspace WORKSPACE] [--tail N] [--allow-raw-screen] [--json]".to_string(),
|
|
242
|
+
Some("peek") => "usage: team-agent peek AGENT [--workspace WORKSPACE] [--tail N|--head N] [--search TEXT] [--allow-raw-screen] [--json]".to_string(),
|
|
233
243
|
Some("coordinator") => "usage: team-agent coordinator [--workspace WORKSPACE] [--once] [--tick-interval SECONDS]".to_string(),
|
|
234
244
|
Some(other) => format!("usage: team-agent {other} [options]"),
|
|
235
245
|
}
|
|
@@ -261,8 +271,8 @@ pub fn cmd_validate(args: &ValidateArgs) -> Result<CmdResult, CliError> {
|
|
|
261
271
|
fn validate_spec_file(spec_path: &Path) -> Result<Value, CliError> {
|
|
262
272
|
let text = std::fs::read_to_string(spec_path)?;
|
|
263
273
|
let base_dir = spec_path.parent().unwrap_or_else(|| Path::new("."));
|
|
264
|
-
let spec =
|
|
265
|
-
.map_err(model_error_to_cli)?;
|
|
274
|
+
let spec =
|
|
275
|
+
crate::model::spec::load_and_validate_spec(&text, base_dir).map_err(model_error_to_cli)?;
|
|
266
276
|
let team = spec
|
|
267
277
|
.get("team")
|
|
268
278
|
.and_then(|team| team.get("name"))
|
|
@@ -381,12 +391,28 @@ fn cli_error_log_path(workspace: &Path) -> PathBuf {
|
|
|
381
391
|
struct PythonCompactFormatter;
|
|
382
392
|
|
|
383
393
|
impl serde_json::ser::Formatter for PythonCompactFormatter {
|
|
384
|
-
fn begin_array_value<W: ?Sized + std::io::Write>(
|
|
385
|
-
|
|
394
|
+
fn begin_array_value<W: ?Sized + std::io::Write>(
|
|
395
|
+
&mut self,
|
|
396
|
+
w: &mut W,
|
|
397
|
+
first: bool,
|
|
398
|
+
) -> std::io::Result<()> {
|
|
399
|
+
if first {
|
|
400
|
+
Ok(())
|
|
401
|
+
} else {
|
|
402
|
+
w.write_all(b", ")
|
|
403
|
+
}
|
|
386
404
|
}
|
|
387
405
|
|
|
388
|
-
fn begin_object_key<W: ?Sized + std::io::Write>(
|
|
389
|
-
|
|
406
|
+
fn begin_object_key<W: ?Sized + std::io::Write>(
|
|
407
|
+
&mut self,
|
|
408
|
+
w: &mut W,
|
|
409
|
+
first: bool,
|
|
410
|
+
) -> std::io::Result<()> {
|
|
411
|
+
if first {
|
|
412
|
+
Ok(())
|
|
413
|
+
} else {
|
|
414
|
+
w.write_all(b", ")
|
|
415
|
+
}
|
|
390
416
|
}
|
|
391
417
|
|
|
392
418
|
fn begin_object_value<W: ?Sized + std::io::Write>(&mut self, w: &mut W) -> std::io::Result<()> {
|
|
@@ -430,6 +456,7 @@ struct ParsedArgs {
|
|
|
430
456
|
summary: bool,
|
|
431
457
|
keep_logs: bool,
|
|
432
458
|
allow_fresh: bool,
|
|
459
|
+
session_converge_deadline_ms: Option<u64>,
|
|
433
460
|
force: bool,
|
|
434
461
|
no_display: bool,
|
|
435
462
|
discard_session: bool,
|
|
@@ -452,6 +479,8 @@ struct ParsedArgs {
|
|
|
452
479
|
providers: Option<String>,
|
|
453
480
|
allow_raw_screen: bool,
|
|
454
481
|
tail: Option<usize>,
|
|
482
|
+
head: Option<usize>,
|
|
483
|
+
search: Option<String>,
|
|
455
484
|
result_file: Option<PathBuf>,
|
|
456
485
|
file: Option<PathBuf>,
|
|
457
486
|
result: Option<String>,
|
|
@@ -459,6 +488,8 @@ struct ParsedArgs {
|
|
|
459
488
|
assignee: Option<String>,
|
|
460
489
|
out: Option<PathBuf>,
|
|
461
490
|
auth_mode: Option<String>,
|
|
491
|
+
pane: Option<String>,
|
|
492
|
+
provider: Option<String>,
|
|
462
493
|
message_id: Option<String>,
|
|
463
494
|
}
|
|
464
495
|
|
|
@@ -486,12 +517,18 @@ fn parse_args(args: &[String]) -> ParsedArgs {
|
|
|
486
517
|
"--requires-ack" => parsed.requires_ack = true,
|
|
487
518
|
"--no-ack" => parsed.no_ack = true,
|
|
488
519
|
"--no-wait" => parsed.no_wait = true,
|
|
489
|
-
"--timeout" =>
|
|
520
|
+
"--timeout" => {
|
|
521
|
+
parsed.timeout = next_arg(args, &mut i).and_then(|v| v.parse::<f64>().ok())
|
|
522
|
+
}
|
|
490
523
|
"--confirm-human" => parsed.confirm_human = true,
|
|
491
524
|
"--detail" => parsed.detail = true,
|
|
492
525
|
"--summary" => parsed.summary = true,
|
|
493
526
|
"--keep-logs" => parsed.keep_logs = true,
|
|
494
527
|
"--allow-fresh" => parsed.allow_fresh = true,
|
|
528
|
+
"--session-converge-deadline" => {
|
|
529
|
+
parsed.session_converge_deadline_ms =
|
|
530
|
+
next_arg(args, &mut i).and_then(|v| parse_seconds_ms(&v));
|
|
531
|
+
}
|
|
495
532
|
"--force" => parsed.force = true,
|
|
496
533
|
"--no-display" => parsed.no_display = true,
|
|
497
534
|
"--discard-session" => parsed.discard_session = true,
|
|
@@ -501,7 +538,9 @@ fn parse_args(args: &[String]) -> ParsedArgs {
|
|
|
501
538
|
"--from-spec" => parsed.from_spec = true,
|
|
502
539
|
"--confirm" => parsed.confirm = true,
|
|
503
540
|
"--alert-type" => parsed.alert_type = next_arg(args, &mut i),
|
|
504
|
-
"--limit" =>
|
|
541
|
+
"--limit" => {
|
|
542
|
+
parsed.limit = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok())
|
|
543
|
+
}
|
|
505
544
|
"--since" => parsed.since = next_arg(args, &mut i),
|
|
506
545
|
"--gate" => parsed.gate = next_arg(args, &mut i),
|
|
507
546
|
"--comms" => parsed.comms = true,
|
|
@@ -509,11 +548,15 @@ fn parse_args(args: &[String]) -> ParsedArgs {
|
|
|
509
548
|
"--fix-schema" => parsed.fix_schema = true,
|
|
510
549
|
"--cleanup-orphans" => parsed.cleanup_orphans = true,
|
|
511
550
|
"--once" => parsed.once = true,
|
|
512
|
-
"--tick-interval" =>
|
|
551
|
+
"--tick-interval" => {
|
|
552
|
+
parsed.tick_interval = next_arg(args, &mut i).and_then(|v| v.parse::<f64>().ok())
|
|
553
|
+
}
|
|
513
554
|
"--status" => parsed.status_value = next_arg(args, &mut i),
|
|
514
555
|
"--providers" => parsed.providers = next_arg(args, &mut i),
|
|
515
556
|
"--allow-raw-screen" => parsed.allow_raw_screen = true,
|
|
516
557
|
"--tail" => parsed.tail = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok()),
|
|
558
|
+
"--head" => parsed.head = next_arg(args, &mut i).and_then(|v| v.parse::<usize>().ok()),
|
|
559
|
+
"--search" => parsed.search = next_arg(args, &mut i),
|
|
517
560
|
"--result-file" => parsed.result_file = next_arg(args, &mut i).map(PathBuf::from),
|
|
518
561
|
"--file" => parsed.file = next_arg(args, &mut i).map(PathBuf::from),
|
|
519
562
|
"--result" => parsed.result = next_arg(args, &mut i),
|
|
@@ -521,11 +564,19 @@ fn parse_args(args: &[String]) -> ParsedArgs {
|
|
|
521
564
|
"--assignee" => parsed.assignee = next_arg(args, &mut i),
|
|
522
565
|
"--out" => parsed.out = next_arg(args, &mut i).map(PathBuf::from),
|
|
523
566
|
"--auth-mode" => parsed.auth_mode = next_arg(args, &mut i),
|
|
567
|
+
"--pane" => parsed.pane = next_arg(args, &mut i),
|
|
568
|
+
"--provider" => parsed.provider = next_arg(args, &mut i),
|
|
524
569
|
"--message-id" => parsed.message_id = next_arg(args, &mut i),
|
|
525
570
|
"-h" | "--help" => {}
|
|
526
571
|
other if other.starts_with("--team=") => {
|
|
527
572
|
parsed.team = Some(other.trim_start_matches("--team=").to_string());
|
|
528
573
|
}
|
|
574
|
+
other if other.starts_with("--pane=") => {
|
|
575
|
+
parsed.pane = Some(other.trim_start_matches("--pane=").to_string());
|
|
576
|
+
}
|
|
577
|
+
other if other.starts_with("--provider=") => {
|
|
578
|
+
parsed.provider = Some(other.trim_start_matches("--provider=").to_string());
|
|
579
|
+
}
|
|
529
580
|
other if other.starts_with('-') => {}
|
|
530
581
|
other => parsed.positionals.push(other.to_string()),
|
|
531
582
|
}
|
|
@@ -539,8 +590,26 @@ fn next_arg(args: &[String], index: &mut usize) -> Option<String> {
|
|
|
539
590
|
args.get(*index).cloned()
|
|
540
591
|
}
|
|
541
592
|
|
|
593
|
+
fn parse_seconds_ms(raw: &str) -> Option<u64> {
|
|
594
|
+
let seconds = raw.parse::<f64>().ok()?;
|
|
595
|
+
if seconds.is_finite() && seconds >= 0.0 {
|
|
596
|
+
Some((seconds * 1000.0).round() as u64)
|
|
597
|
+
} else {
|
|
598
|
+
None
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
fn parse_cli_provider(raw: Option<&str>) -> Result<crate::provider::Provider, CliError> {
|
|
603
|
+
let raw = raw.unwrap_or("codex");
|
|
604
|
+
serde_json::from_value::<crate::provider::Provider>(serde_json::json!(raw))
|
|
605
|
+
.map_err(|_| CliError::Runtime(format!("unknown provider: {raw}")))
|
|
606
|
+
}
|
|
607
|
+
|
|
542
608
|
fn workspace(parsed: &ParsedArgs, cwd: &Path) -> PathBuf {
|
|
543
|
-
parsed
|
|
609
|
+
parsed
|
|
610
|
+
.workspace
|
|
611
|
+
.clone()
|
|
612
|
+
.unwrap_or_else(|| cwd.to_path_buf())
|
|
544
613
|
}
|
|
545
614
|
|
|
546
615
|
fn required_pos(parsed: &ParsedArgs, index: usize, name: &str) -> Result<String, CliError> {
|
|
@@ -591,7 +660,9 @@ fn compile_args(args: &[String], cwd: &Path) -> Result<CompileArgs, CliError> {
|
|
|
591
660
|
.as_deref()
|
|
592
661
|
.map(PathBuf::from)
|
|
593
662
|
.ok_or_else(|| CliError::Usage("missing --team".to_string()))?;
|
|
594
|
-
let out = parsed
|
|
663
|
+
let out = parsed
|
|
664
|
+
.out
|
|
665
|
+
.unwrap_or_else(|| PathBuf::from("team.spec.yaml"));
|
|
595
666
|
Ok(CompileArgs {
|
|
596
667
|
team: resolve_cli_path(cwd, &team),
|
|
597
668
|
out: resolve_cli_path(cwd, &out),
|
|
@@ -618,7 +689,12 @@ fn send_args(args: &[String], cwd: &Path) -> Result<SendArgs, CliError> {
|
|
|
618
689
|
let workspace = workspace(&parsed, cwd);
|
|
619
690
|
Ok(SendArgs {
|
|
620
691
|
target,
|
|
621
|
-
message: parsed
|
|
692
|
+
message: parsed
|
|
693
|
+
.positionals
|
|
694
|
+
.iter()
|
|
695
|
+
.skip(message_start)
|
|
696
|
+
.cloned()
|
|
697
|
+
.collect(),
|
|
622
698
|
targets: parsed.targets,
|
|
623
699
|
workspace,
|
|
624
700
|
team: parsed.team,
|
|
@@ -703,6 +779,21 @@ fn claim_leader_args(args: &[String], cwd: &Path) -> ClaimLeaderArgs {
|
|
|
703
779
|
}
|
|
704
780
|
}
|
|
705
781
|
|
|
782
|
+
fn attach_leader_args(args: &[String], cwd: &Path) -> Result<AttachLeaderArgs, CliError> {
|
|
783
|
+
let parsed = parse_args(args);
|
|
784
|
+
Ok(AttachLeaderArgs {
|
|
785
|
+
workspace: workspace(&parsed, cwd),
|
|
786
|
+
team: parsed.team,
|
|
787
|
+
pane: parsed
|
|
788
|
+
.pane
|
|
789
|
+
.filter(|pane| !pane.is_empty())
|
|
790
|
+
.map(crate::transport::PaneId::new),
|
|
791
|
+
provider: parse_cli_provider(parsed.provider.as_deref())?,
|
|
792
|
+
confirm: parsed.confirm,
|
|
793
|
+
json: parsed.json,
|
|
794
|
+
})
|
|
795
|
+
}
|
|
796
|
+
|
|
706
797
|
fn identity_args(args: &[String], cwd: &Path) -> IdentityArgs {
|
|
707
798
|
let parsed = parse_args(args);
|
|
708
799
|
IdentityArgs {
|
|
@@ -732,6 +823,7 @@ fn restart_args(args: &[String], cwd: &Path) -> RestartArgs {
|
|
|
732
823
|
.unwrap_or_else(|| workspace(&parsed, cwd)),
|
|
733
824
|
team: parsed.team,
|
|
734
825
|
allow_fresh: parsed.allow_fresh,
|
|
826
|
+
session_converge_deadline_ms: parsed.session_converge_deadline_ms,
|
|
735
827
|
json: parsed.json,
|
|
736
828
|
}
|
|
737
829
|
}
|
|
@@ -777,7 +869,9 @@ fn add_agent_args(args: &[String], cwd: &Path) -> Result<AddAgentArgs, CliError>
|
|
|
777
869
|
agent: required_pos(&parsed, 0, "agent")?,
|
|
778
870
|
workspace: workspace(&parsed, cwd),
|
|
779
871
|
team: parsed.team,
|
|
780
|
-
role_file: parsed
|
|
872
|
+
role_file: parsed
|
|
873
|
+
.role_file
|
|
874
|
+
.ok_or_else(|| CliError::Usage("missing --role-file".to_string()))?,
|
|
781
875
|
no_display: parsed.no_display,
|
|
782
876
|
json: parsed.json,
|
|
783
877
|
})
|
|
@@ -789,7 +883,9 @@ fn fork_agent_args(args: &[String], cwd: &Path) -> Result<ForkAgentArgs, CliErro
|
|
|
789
883
|
source_agent: required_pos(&parsed, 0, "source_agent")?,
|
|
790
884
|
workspace: workspace(&parsed, cwd),
|
|
791
885
|
team: parsed.team,
|
|
792
|
-
as_agent: parsed
|
|
886
|
+
as_agent: parsed
|
|
887
|
+
.as_agent
|
|
888
|
+
.ok_or_else(|| CliError::Usage("missing --as".to_string()))?,
|
|
793
889
|
label: parsed.label,
|
|
794
890
|
no_display: parsed.no_display,
|
|
795
891
|
json: parsed.json,
|
|
@@ -935,7 +1031,9 @@ fn repair_state_args(args: &[String], cwd: &Path) -> Result<RepairStateArgs, Cli
|
|
|
935
1031
|
let parsed = parse_args(args);
|
|
936
1032
|
Ok(RepairStateArgs {
|
|
937
1033
|
workspace: workspace(&parsed, cwd),
|
|
938
|
-
task_id: parsed
|
|
1034
|
+
task_id: parsed
|
|
1035
|
+
.task
|
|
1036
|
+
.ok_or_else(|| CliError::Usage("missing --task".to_string()))?,
|
|
939
1037
|
assignee: parsed.assignee,
|
|
940
1038
|
status: parsed
|
|
941
1039
|
.status_value
|
|
@@ -1025,6 +1123,8 @@ fn peek_args(args: &[String], cwd: &Path) -> Result<PeekArgs, CliError> {
|
|
|
1025
1123
|
agent: required_pos(&parsed, 0, "agent")?,
|
|
1026
1124
|
workspace: workspace(&parsed, cwd),
|
|
1027
1125
|
tail: parsed.tail.unwrap_or(80),
|
|
1126
|
+
head: parsed.head,
|
|
1127
|
+
search: parsed.search,
|
|
1028
1128
|
allow_raw_screen: parsed.allow_raw_screen,
|
|
1029
1129
|
json: parsed.json,
|
|
1030
1130
|
})
|
|
@@ -1062,13 +1162,23 @@ fn json_dumps_like(value: &Value) -> String {
|
|
|
1062
1162
|
Err(_) => "\"\"".to_string(),
|
|
1063
1163
|
},
|
|
1064
1164
|
Value::Array(arr) => {
|
|
1065
|
-
let inner = arr
|
|
1165
|
+
let inner = arr
|
|
1166
|
+
.iter()
|
|
1167
|
+
.map(json_dumps_like)
|
|
1168
|
+
.collect::<Vec<_>>()
|
|
1169
|
+
.join(", ");
|
|
1066
1170
|
format!("[{inner}]")
|
|
1067
1171
|
}
|
|
1068
1172
|
Value::Object(obj) => {
|
|
1069
1173
|
let inner = obj
|
|
1070
1174
|
.iter()
|
|
1071
|
-
.map(|(k, v)|
|
|
1175
|
+
.map(|(k, v)| {
|
|
1176
|
+
format!(
|
|
1177
|
+
"{}: {}",
|
|
1178
|
+
json_dumps_like(&Value::String(k.clone())),
|
|
1179
|
+
json_dumps_like(v)
|
|
1180
|
+
)
|
|
1181
|
+
})
|
|
1072
1182
|
.collect::<Vec<_>>()
|
|
1073
1183
|
.join(", ");
|
|
1074
1184
|
format!("{{{inner}}}")
|
|
@@ -1160,26 +1270,144 @@ mod tests {
|
|
|
1160
1270
|
#[test]
|
|
1161
1271
|
fn t0_help_catalog_lists_command_flags() {
|
|
1162
1272
|
for (command, flags) in [
|
|
1163
|
-
(
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
(
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1273
|
+
(
|
|
1274
|
+
"quick-start",
|
|
1275
|
+
&["--workspace", "--team-id", "--yes", "--fresh", "--json"][..],
|
|
1276
|
+
),
|
|
1277
|
+
(
|
|
1278
|
+
"send",
|
|
1279
|
+
&[
|
|
1280
|
+
"--workspace",
|
|
1281
|
+
"--team",
|
|
1282
|
+
"--targets",
|
|
1283
|
+
"--watch-result",
|
|
1284
|
+
"--timeout",
|
|
1285
|
+
"--json",
|
|
1286
|
+
][..],
|
|
1287
|
+
),
|
|
1288
|
+
(
|
|
1289
|
+
"status",
|
|
1290
|
+
&["--workspace", "--team", "--summary", "--json", "--detail"][..],
|
|
1291
|
+
),
|
|
1292
|
+
(
|
|
1293
|
+
"shutdown",
|
|
1294
|
+
&["--workspace", "--team", "--keep-logs", "--json"][..],
|
|
1295
|
+
),
|
|
1296
|
+
(
|
|
1297
|
+
"restart",
|
|
1298
|
+
&[
|
|
1299
|
+
"--team",
|
|
1300
|
+
"--allow-fresh",
|
|
1301
|
+
"--session-converge-deadline",
|
|
1302
|
+
"--json",
|
|
1303
|
+
][..],
|
|
1304
|
+
),
|
|
1305
|
+
(
|
|
1306
|
+
"start-agent",
|
|
1307
|
+
&[
|
|
1308
|
+
"--workspace",
|
|
1309
|
+
"--team",
|
|
1310
|
+
"--force",
|
|
1311
|
+
"--allow-fresh",
|
|
1312
|
+
"--no-display",
|
|
1313
|
+
"--json",
|
|
1314
|
+
][..],
|
|
1315
|
+
),
|
|
1316
|
+
(
|
|
1317
|
+
"reset-agent",
|
|
1318
|
+
&[
|
|
1319
|
+
"--workspace",
|
|
1320
|
+
"--team",
|
|
1321
|
+
"--discard-session",
|
|
1322
|
+
"--no-display",
|
|
1323
|
+
"--json",
|
|
1324
|
+
][..],
|
|
1325
|
+
),
|
|
1326
|
+
(
|
|
1327
|
+
"add-agent",
|
|
1328
|
+
&[
|
|
1329
|
+
"--role-file",
|
|
1330
|
+
"--workspace",
|
|
1331
|
+
"--team",
|
|
1332
|
+
"--no-display",
|
|
1333
|
+
"--json",
|
|
1334
|
+
][..],
|
|
1335
|
+
),
|
|
1336
|
+
(
|
|
1337
|
+
"fork-agent",
|
|
1338
|
+
&[
|
|
1339
|
+
"--as",
|
|
1340
|
+
"--label",
|
|
1341
|
+
"--workspace",
|
|
1342
|
+
"--team",
|
|
1343
|
+
"--no-display",
|
|
1344
|
+
"--json",
|
|
1345
|
+
][..],
|
|
1346
|
+
),
|
|
1347
|
+
(
|
|
1348
|
+
"remove-agent",
|
|
1349
|
+
&[
|
|
1350
|
+
"--workspace",
|
|
1351
|
+
"--team",
|
|
1352
|
+
"--from-spec",
|
|
1353
|
+
"--confirm",
|
|
1354
|
+
"--force",
|
|
1355
|
+
"--json",
|
|
1356
|
+
][..],
|
|
1357
|
+
),
|
|
1358
|
+
(
|
|
1359
|
+
"doctor",
|
|
1360
|
+
&[
|
|
1361
|
+
"--workspace",
|
|
1362
|
+
"--team",
|
|
1363
|
+
"--gate",
|
|
1364
|
+
"--fix-schema",
|
|
1365
|
+
"--cleanup-orphans",
|
|
1366
|
+
"--json",
|
|
1367
|
+
][..],
|
|
1368
|
+
),
|
|
1369
|
+
(
|
|
1370
|
+
"attach-leader",
|
|
1371
|
+
&[
|
|
1372
|
+
"--workspace",
|
|
1373
|
+
"--team",
|
|
1374
|
+
"--pane",
|
|
1375
|
+
"--provider",
|
|
1376
|
+
"--confirm",
|
|
1377
|
+
"--json",
|
|
1378
|
+
][..],
|
|
1379
|
+
),
|
|
1380
|
+
(
|
|
1381
|
+
"collect",
|
|
1382
|
+
&["--workspace", "--team", "--result-file", "--json"][..],
|
|
1383
|
+
),
|
|
1384
|
+
(
|
|
1385
|
+
"repair-state",
|
|
1386
|
+
&["--task", "--status", "--assignee", "--workspace", "--json"][..],
|
|
1387
|
+
),
|
|
1176
1388
|
("wait-ready", &["--workspace", "--timeout", "--json"][..]),
|
|
1177
|
-
(
|
|
1178
|
-
|
|
1389
|
+
(
|
|
1390
|
+
"peek",
|
|
1391
|
+
&[
|
|
1392
|
+
"--workspace",
|
|
1393
|
+
"--tail",
|
|
1394
|
+
"--head",
|
|
1395
|
+
"--search",
|
|
1396
|
+
"--allow-raw-screen",
|
|
1397
|
+
"--json",
|
|
1398
|
+
][..],
|
|
1399
|
+
),
|
|
1400
|
+
(
|
|
1401
|
+
"coordinator",
|
|
1402
|
+
&["--workspace", "--once", "--tick-interval"][..],
|
|
1403
|
+
),
|
|
1179
1404
|
] {
|
|
1180
1405
|
let help = command_help(Some(command));
|
|
1181
1406
|
for flag in flags {
|
|
1182
|
-
assert!(
|
|
1407
|
+
assert!(
|
|
1408
|
+
help.contains(flag),
|
|
1409
|
+
"`team-agent {command} --help` is missing {flag}"
|
|
1410
|
+
);
|
|
1183
1411
|
}
|
|
1184
1412
|
}
|
|
1185
1413
|
}
|
|
@@ -1189,7 +1417,13 @@ mod tests {
|
|
|
1189
1417
|
let cwd = tmp_workspace();
|
|
1190
1418
|
let ws = tmp_workspace();
|
|
1191
1419
|
let args = quick_start_args(
|
|
1192
|
-
&cli_argv(&[
|
|
1420
|
+
&cli_argv(&[
|
|
1421
|
+
"--workspace",
|
|
1422
|
+
&ws.to_string_lossy(),
|
|
1423
|
+
"agents",
|
|
1424
|
+
"--yes",
|
|
1425
|
+
"--json",
|
|
1426
|
+
]),
|
|
1193
1427
|
&cwd,
|
|
1194
1428
|
)
|
|
1195
1429
|
.unwrap();
|
|
@@ -47,21 +47,48 @@ pub fn leader_launcher_args(values: &[String]) -> Result<LeaderLauncherArgs, Cli
|
|
|
47
47
|
Ok(out)
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
fn leader_launcher_json(values: &[String]) -> bool {
|
|
51
|
+
values
|
|
52
|
+
.iter()
|
|
53
|
+
.take_while(|arg| arg.as_str() != "--")
|
|
54
|
+
.any(|arg| arg == "--json")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn without_leader_json(values: &[String]) -> Vec<String> {
|
|
58
|
+
let mut out = Vec::with_capacity(values.len());
|
|
59
|
+
let mut provider_remainder = false;
|
|
60
|
+
for value in values {
|
|
61
|
+
if provider_remainder || value != "--json" {
|
|
62
|
+
out.push(value.clone());
|
|
63
|
+
}
|
|
64
|
+
if value == "--" {
|
|
65
|
+
provider_remainder = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
out
|
|
69
|
+
}
|
|
70
|
+
|
|
50
71
|
/// `codex`/`claude` passthrough(`parser.py:86`/`_run_leader_passthrough`):leader 早返回,
|
|
51
72
|
/// **不**进 subparser。`-h`/`--help` 打 usage 直接返回 [`CmdResult::none`]。否则解析 attach
|
|
52
|
-
/// 旗标 + `lifecycle_port::start_leader`。`command` ∈ {codex,
|
|
53
|
-
pub fn cmd_leader_passthrough(
|
|
73
|
+
/// 旗标 + `lifecycle_port::start_leader`。`command` ∈ {codex, claude}。
|
|
74
|
+
pub fn cmd_leader_passthrough(
|
|
75
|
+
command: &str,
|
|
76
|
+
provider_args: &[String],
|
|
77
|
+
cwd: &Path,
|
|
78
|
+
) -> Result<CmdResult, CliError> {
|
|
54
79
|
if provider_args == ["-h"] || provider_args == ["--help"] {
|
|
55
80
|
return Ok(CmdResult::none());
|
|
56
81
|
}
|
|
57
|
-
let
|
|
82
|
+
let as_json = leader_launcher_json(provider_args);
|
|
83
|
+
let launcher_args = without_leader_json(provider_args);
|
|
84
|
+
let attach = leader_launcher_args(&launcher_args)?;
|
|
58
85
|
let provider = if command == "codex" {
|
|
59
86
|
crate::model::enums::Provider::Codex
|
|
60
87
|
} else {
|
|
61
88
|
crate::model::enums::Provider::ClaudeCode
|
|
62
89
|
};
|
|
63
90
|
let value = lifecycle_port::start_leader(provider, &attach.provider_args, cwd, &attach)?;
|
|
64
|
-
Ok(CmdResult::from_json(value,
|
|
91
|
+
Ok(CmdResult::from_json(value, as_json))
|
|
65
92
|
}
|
|
66
93
|
|
|
67
94
|
// =============================================================================
|
|
@@ -143,7 +170,11 @@ fn parse_inbox_entries(text: impl AsRef<str>) -> Vec<String> {
|
|
|
143
170
|
}
|
|
144
171
|
|
|
145
172
|
fn render_inbox_summary(entries: &[String], budget: usize) -> String {
|
|
146
|
-
let noun = if entries.len() == 1 {
|
|
173
|
+
let noun = if entries.len() == 1 {
|
|
174
|
+
"entry"
|
|
175
|
+
} else {
|
|
176
|
+
"entries"
|
|
177
|
+
};
|
|
147
178
|
let header = format!("Leader inbox: {} new fallback {noun}", entries.len());
|
|
148
179
|
let hint = "Hint: team-agent inbox leader";
|
|
149
180
|
let footer = "Truncated: more fallback entries available; run team-agent inbox leader";
|
|
@@ -173,9 +204,7 @@ fn render_inbox_summary(entries: &[String], budget: usize) -> String {
|
|
|
173
204
|
};
|
|
174
205
|
if char_len(&summary) > budget {
|
|
175
206
|
let keep = budget.saturating_sub(char_len(footer)).saturating_sub(6);
|
|
176
|
-
let body = prefix_chars(&lines.join("\n"), keep)
|
|
177
|
-
.trim_end()
|
|
178
|
-
.to_string();
|
|
207
|
+
let body = prefix_chars(&lines.join("\n"), keep).trim_end().to_string();
|
|
179
208
|
summary = format!("{body} ...\n{footer}");
|
|
180
209
|
}
|
|
181
210
|
summary
|