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