@team-agent/installer 0.5.112 → 0.5.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +100 -44
- package/crates/team-agent/src/cli/emit.rs +127 -98
- package/crates/team-agent/src/cli/leaders.rs +32 -32
- package/crates/team-agent/src/cli/mod.rs +6 -6
- 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 +29 -29
- package/crates/team-agent/src/cli/status.rs +7 -7
- package/crates/team-agent/src/cli/tests/base.rs +6 -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 +7 -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 +0 -9
- package/crates/team-agent/src/leader/start.rs +5 -0
- package/crates/team-agent/src/leader/tests/identity.rs +21 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +325 -14
- package/crates/team-agent/src/lifecycle/launch.rs +2 -2
- 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/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/package.json +4 -4
|
@@ -124,7 +124,7 @@ fn send_hint(entry: &LeaderRegistryEntry) -> String {
|
|
|
124
124
|
format!(
|
|
125
125
|
"team-agent send {} {}",
|
|
126
126
|
quote_cli_arg(&to),
|
|
127
|
-
quote_cli_arg("
|
|
127
|
+
quote_cli_arg("message")
|
|
128
128
|
)
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -183,19 +183,19 @@ fn format_leaders_human(rows: &[LeaderRow], view: LeadersView) -> String {
|
|
|
183
183
|
if rows.is_empty() {
|
|
184
184
|
return match view {
|
|
185
185
|
LeadersView::Live => {
|
|
186
|
-
"
|
|
186
|
+
"No live leaders. Run team-agent doctor --workspace . for the current team.\n".to_string()
|
|
187
187
|
}
|
|
188
|
-
LeadersView::All => "
|
|
189
|
-
LeadersView::Stale => "
|
|
188
|
+
LeadersView::All => "No registered leaders. Open a leader with team-agent pi for first-time collaboration.\n".to_string(),
|
|
189
|
+
LeadersView::Stale => "No stale leader registrations.\n".to_string(),
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
|
-
let mut out = String::from("
|
|
192
|
+
let mut out = String::from("leader\tstatus\tworkspace\tteam\tsend command\n");
|
|
193
193
|
for row in rows {
|
|
194
194
|
out.push_str("leader\t");
|
|
195
195
|
out.push_str(match row.status {
|
|
196
|
-
"LIVE" => "
|
|
197
|
-
"STALE" => "
|
|
198
|
-
"AMBIGUOUS" => "
|
|
196
|
+
"LIVE" => "LIVE",
|
|
197
|
+
"STALE" => "STALE",
|
|
198
|
+
"AMBIGUOUS" => "AMBIGUOUS",
|
|
199
199
|
other => other,
|
|
200
200
|
});
|
|
201
201
|
out.push('\t');
|
|
@@ -212,7 +212,7 @@ fn format_leaders_human(rows: &[LeaderRow], view: LeadersView) -> String {
|
|
|
212
212
|
&row.entry.workspace,
|
|
213
213
|
Some(&row.entry.team_key),
|
|
214
214
|
)
|
|
215
|
-
.unwrap_or_else(|| "
|
|
215
|
+
.unwrap_or_else(|| "Verify the workspace path".to_string()),
|
|
216
216
|
);
|
|
217
217
|
}
|
|
218
218
|
out.push('\n');
|
|
@@ -240,33 +240,33 @@ fn prune_json(report: &RegistryPruneReport) -> Value {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
fn format_prune_human(report: &RegistryPruneReport) -> String {
|
|
243
|
-
let mut out = format!("
|
|
243
|
+
let mut out = format!("dry_run: {}\n", report.dry_run);
|
|
244
244
|
for (label, items) in [
|
|
245
|
-
("
|
|
246
|
-
("
|
|
247
|
-
("
|
|
248
|
-
("
|
|
249
|
-
("
|
|
245
|
+
("candidates", &report.candidates),
|
|
246
|
+
("removed", &report.removed),
|
|
247
|
+
("kept", &report.kept),
|
|
248
|
+
("skipped", &report.skipped),
|
|
249
|
+
("errors", &report.errors),
|
|
250
250
|
] {
|
|
251
251
|
out.push_str(&format!("{label}: {}\n", items.len()));
|
|
252
252
|
for item in items {
|
|
253
253
|
out.push_str(" ");
|
|
254
254
|
out.push_str(&item.workspace.display().to_string());
|
|
255
|
-
out.push_str("
|
|
255
|
+
out.push_str(", team: ");
|
|
256
256
|
out.push_str(&item.team_key);
|
|
257
257
|
out.push_str(" [");
|
|
258
258
|
out.push_str(match item.reason.as_str() {
|
|
259
|
-
"LIVE" => "
|
|
260
|
-
"team_key_not_found" => "
|
|
261
|
-
"team_not_alive" => "
|
|
262
|
-
"registry_entry_missing" => "
|
|
259
|
+
"LIVE" => "still live; registration retained",
|
|
260
|
+
"team_key_not_found" => "team no longer exists in the workspace",
|
|
261
|
+
"team_not_alive" => "team is stopped",
|
|
262
|
+
"registry_entry_missing" => "registration no longer exists",
|
|
263
263
|
"registry_entry_changed" | "canonical_state_changed" => {
|
|
264
|
-
"
|
|
264
|
+
"registration or team changed; cleanup refused"
|
|
265
265
|
}
|
|
266
|
-
"canonical_state_unavailable" => "
|
|
267
|
-
"registry_lock_unavailable" => "
|
|
268
|
-
"remove_failed" => "
|
|
269
|
-
_ => "
|
|
266
|
+
"canonical_state_unavailable" => "team state unavailable; registration retained",
|
|
267
|
+
"registry_lock_unavailable" => "registry lock unavailable; cleanup refused",
|
|
268
|
+
"remove_failed" => "registration removal failed; inspect file permissions",
|
|
269
|
+
_ => "registration could not be verified safely; diagnose the selected workspace",
|
|
270
270
|
});
|
|
271
271
|
out.push_str("]\n");
|
|
272
272
|
}
|
|
@@ -337,13 +337,13 @@ mod tests {
|
|
|
337
337
|
assert_eq!(lines.len(), 3, "one header and both selected rows: {out}");
|
|
338
338
|
let header = lines[0];
|
|
339
339
|
assert_eq!(header.split('\t').count(), 5);
|
|
340
|
-
assert!(header.contains("
|
|
340
|
+
assert!(header.contains("status") && header.contains("workspace") && header.contains("team"));
|
|
341
341
|
let live: Vec<_> = lines[1].split('\t').collect();
|
|
342
342
|
assert_eq!(
|
|
343
343
|
&live[..4],
|
|
344
344
|
&[
|
|
345
345
|
"leader",
|
|
346
|
-
"
|
|
346
|
+
"LIVE",
|
|
347
347
|
"/Volumes/nvme/Projects/讨论team-agent",
|
|
348
348
|
"wiki-team"
|
|
349
349
|
]
|
|
@@ -354,7 +354,7 @@ mod tests {
|
|
|
354
354
|
"team-agent",
|
|
355
355
|
"send",
|
|
356
356
|
"leader",
|
|
357
|
-
"
|
|
357
|
+
"Complete the task and reply to the leader.",
|
|
358
358
|
"--workspace",
|
|
359
359
|
"/Volumes/nvme/Projects/讨论team-agent",
|
|
360
360
|
"--team",
|
|
@@ -364,7 +364,7 @@ mod tests {
|
|
|
364
364
|
let stale: Vec<_> = lines[2].split('\t').collect();
|
|
365
365
|
assert_eq!(
|
|
366
366
|
&stale[..4],
|
|
367
|
-
&["leader", "
|
|
367
|
+
&["leader", "STALE", "/Users/alauda/stale", "old-team"]
|
|
368
368
|
);
|
|
369
369
|
assert_eq!(stale[4], "-", "stale rows must never suggest a send: {out}");
|
|
370
370
|
}
|
|
@@ -404,9 +404,9 @@ mod tests {
|
|
|
404
404
|
#[test]
|
|
405
405
|
fn empty_view_names_are_explicit() {
|
|
406
406
|
for (view, empty, next) in [
|
|
407
|
-
(LeadersView::Live, "
|
|
408
|
-
(LeadersView::Stale, "
|
|
409
|
-
(LeadersView::All, "
|
|
407
|
+
(LeadersView::Live, "No live leaders", Some("team-agent doctor")),
|
|
408
|
+
(LeadersView::Stale, "No stale leader registrations", None),
|
|
409
|
+
(LeadersView::All, "No registered leaders", Some("team-agent pi")),
|
|
410
410
|
] {
|
|
411
411
|
let out = format_leaders_human(&[], view);
|
|
412
412
|
assert!(out.contains(empty) && out.ends_with('\n'), "{out}");
|
|
@@ -49,12 +49,12 @@ use crate::messaging::{self, MessageTarget, SendOptions, TrustedSender};
|
|
|
49
49
|
use crate::model::ids::{TaskId, TeamKey};
|
|
50
50
|
|
|
51
51
|
pub(crate) const COMMS_BOUNDARY_TEXT: &str =
|
|
52
|
-
"
|
|
53
|
-
pub(crate) const QUICK_START_REMINDER: &str = "
|
|
52
|
+
"This checks terminal connection consistency only; it does not send a message or prove that a worker replied.";
|
|
53
|
+
pub(crate) const QUICK_START_REMINDER: &str = "Next: send a task. Use team-agent status for status and team-agent inbox leader -n 3 for replies; do not inspect worker terminal content.";
|
|
54
54
|
pub(crate) const SEND_REMINDER: &str =
|
|
55
|
-
"
|
|
55
|
+
"Wait for an actual reply; use team-agent inbox leader -n 3. Do not inspect worker terminal content.";
|
|
56
56
|
pub(crate) const STATUS_REMINDER: &str =
|
|
57
|
-
"
|
|
57
|
+
"Next: use team-agent inbox leader -n 3 for replies. Status is not proof of task completion; do not inspect worker terminal content.";
|
|
58
58
|
|
|
59
59
|
pub mod adapters;
|
|
60
60
|
pub mod attach_app_server_leader;
|
|
@@ -4176,7 +4176,7 @@ pub mod lifecycle_port {
|
|
|
4176
4176
|
"team-agent".to_string(),
|
|
4177
4177
|
"send".to_string(),
|
|
4178
4178
|
"implementer".to_string(),
|
|
4179
|
-
"
|
|
4179
|
+
"Complete the task and reply to the leader.".to_string(),
|
|
4180
4180
|
"--workspace".to_string(),
|
|
4181
4181
|
team_dir.to_string_lossy().into_owned(),
|
|
4182
4182
|
"--team".to_string(),
|
|
@@ -4213,7 +4213,7 @@ pub mod lifecycle_port {
|
|
|
4213
4213
|
"team-agent".to_string(),
|
|
4214
4214
|
"send".to_string(),
|
|
4215
4215
|
"implementer".to_string(),
|
|
4216
|
-
"
|
|
4216
|
+
"Complete the task and reply to the leader.".to_string(),
|
|
4217
4217
|
"--workspace".to_string(),
|
|
4218
4218
|
team_dir.to_string_lossy().into_owned(),
|
|
4219
4219
|
"--team".to_string(),
|
|
@@ -78,16 +78,16 @@ pub(crate) struct NamedAddressError {
|
|
|
78
78
|
|
|
79
79
|
pub(crate) fn human_address_reason(reason: &str) -> Option<&'static str> {
|
|
80
80
|
Some(match reason {
|
|
81
|
-
"name_invalid" => "
|
|
82
|
-
"workspace_not_found" => "
|
|
81
|
+
"name_invalid" => "Invalid recipient name; use a name from the agent list.",
|
|
82
|
+
"workspace_not_found" => "The selected workspace directory does not exist; verify the path.",
|
|
83
83
|
"state_not_found" | "workspace_no_state" => {
|
|
84
|
-
"
|
|
84
|
+
"No usable team exists in the selected workspace; use quick-start for initial creation or restart for an initialized team."
|
|
85
85
|
}
|
|
86
|
-
"team_key_not_found" => "
|
|
87
|
-
"leader_not_attached" => "
|
|
88
|
-
"name_not_resolvable" => "
|
|
89
|
-
"name_not_live" => "
|
|
90
|
-
"name_ambiguous" => "
|
|
86
|
+
"team_key_not_found" => "The selected workspace has no such team; verify --workspace and --team.",
|
|
87
|
+
"leader_not_attached" => "No leader is attached to this team; diagnose it and follow the recovery action.",
|
|
88
|
+
"name_not_resolvable" => "This team has no such agent; check the agent list.",
|
|
89
|
+
"name_not_live" => "The agent is not running or its channel is stale; check status before recovery.",
|
|
90
|
+
"name_ambiguous" => "Multiple targets share this name; select an explicit workspace and team.",
|
|
91
91
|
_ => return None,
|
|
92
92
|
})
|
|
93
93
|
}
|
|
@@ -121,23 +121,24 @@ impl NamedAddressError {
|
|
|
121
121
|
pub(crate) fn human_guidance(&self, requested: &str) -> (String, String) {
|
|
122
122
|
let workspace = self.human_scope.as_deref();
|
|
123
123
|
let mut message = format!(
|
|
124
|
-
"{}\
|
|
125
|
-
human_address_reason(self.kind.as_str()).unwrap_or("
|
|
124
|
+
"{}\nRequested recipient: {requested:?}\nReason: {}",
|
|
125
|
+
human_address_reason(self.kind.as_str()).unwrap_or("No usable recipient could be resolved."),
|
|
126
|
+
self.message
|
|
126
127
|
);
|
|
127
128
|
if let Some(workspace) = workspace {
|
|
128
|
-
message.push_str(&format!("\
|
|
129
|
+
message.push_str(&format!("\nSelected workspace: {:?}", workspace.to_string_lossy()));
|
|
129
130
|
}
|
|
130
131
|
if let Some(team) = self.human_team.as_deref() {
|
|
131
|
-
message.push_str(&format!("\
|
|
132
|
+
message.push_str(&format!("\nSelected team: {team:?}"));
|
|
132
133
|
}
|
|
133
|
-
let mut action = "
|
|
134
|
+
let mut action = "No reliable spelling suggestion is available; verify the workspace and team before choosing a target.".to_string();
|
|
134
135
|
for candidate in &self.candidates {
|
|
135
136
|
if let (Some(agent), Some(team)) = (
|
|
136
137
|
candidate.get("agent_id").and_then(Value::as_str),
|
|
137
138
|
candidate.get("team_key").and_then(Value::as_str),
|
|
138
139
|
) {
|
|
139
140
|
message.push_str(&format!(
|
|
140
|
-
"\
|
|
141
|
+
"\nCandidate team {team:?}, agent {agent:?}; confirm the intended target."
|
|
141
142
|
));
|
|
142
143
|
}
|
|
143
144
|
}
|
|
@@ -162,7 +163,7 @@ impl NamedAddressError {
|
|
|
162
163
|
if let Some(command) =
|
|
163
164
|
super::adapters::send_command(agent, workspace, Some(team))
|
|
164
165
|
{
|
|
165
|
-
action = format!("
|
|
166
|
+
action = format!("Did you mean agent {agent:?} in team {team:?}? After confirmation, replace the example message below. Nothing was sent:\n{command}");
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -171,7 +172,7 @@ impl NamedAddressError {
|
|
|
171
172
|
workspace.filter(|path| !path.to_string_lossy().chars().any(char::is_control))
|
|
172
173
|
{
|
|
173
174
|
action.push_str(&format!(
|
|
174
|
-
"\
|
|
175
|
+
"\nList available agents with:\nteam-agent status --workspace {}",
|
|
175
176
|
super::adapters::shell_quote(&workspace.to_string_lossy())
|
|
176
177
|
));
|
|
177
178
|
}
|
|
@@ -5,7 +5,7 @@ use crate::provider::argv_route::{self, Config, Mutation, Override, RouteError};
|
|
|
5
5
|
use crate::provider::Provider;
|
|
6
6
|
use serde_json::{json, Value};
|
|
7
7
|
|
|
8
|
-
pub(crate) const HELP: &str = "
|
|
8
|
+
pub(crate) const HELP: &str = "Purpose:\nConfigure additional provider launch arguments. Disabled by default.\nUsage: team-agent route [status|enable|disable|show [TOOL]|set TOOL -- ARG...|add TOOL -- ARG...|clear TOOL] [--json]\n\nOptions:\nstatus reports the switch; enable/disable change it; show lists all or one provider's arguments.\nset replaces arguments; add appends; clear removes a provider's arguments. Use --json for structured output.\nTOOL is a supported provider name, such as pi/codex/claude/copilot/grok/cursor_agent.\nEverything after the first -- is a literal provider argument, including --help and --json.\nConfiguration: ~/.team-agent/argv-routing.json. This is host-global, not isolated by workspace or team.\nTEAM_AGENT_CLI_ARGV_ROUTING overrides the persisted switch.\nExamples:\nteam-agent route status\nteam-agent route set pi -- --mode text\nteam-agent route enable\n\nNext Action:\nVerify arguments with route show pi. Changes apply to the next launch, not an existing session.\nWithout an enabled configuration, providers retain their original arguments. Leave routing disabled unless the arguments are understood.";
|
|
9
9
|
|
|
10
10
|
struct Request {
|
|
11
11
|
operation: String,
|
|
@@ -16,9 +16,9 @@ struct Request {
|
|
|
16
16
|
|
|
17
17
|
fn usage() -> RouteError {
|
|
18
18
|
RouteError {
|
|
19
|
-
error: "
|
|
19
|
+
error: "Invalid or incomplete arguments; set/add require a provider name, -- and at least one provider argument".to_string(),
|
|
20
20
|
reason: "argv_route_usage",
|
|
21
|
-
action: "
|
|
21
|
+
action: "Follow the Examples in team-agent route --help, such as team-agent route set pi -- --mode text".to_string(),
|
|
22
22
|
config_path: None,
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -118,7 +118,7 @@ fn execute(request: &Request) -> Result<Value, RouteError> {
|
|
|
118
118
|
}
|
|
119
119
|
if override_state != Override::Unset {
|
|
120
120
|
value["notice"] = json!(format!(
|
|
121
|
-
"
|
|
121
|
+
"Switch overridden by environment variable {} ({})",
|
|
122
122
|
argv_route::ENV_NAME,
|
|
123
123
|
override_state.status()
|
|
124
124
|
));
|
|
@@ -20,7 +20,7 @@ pub(super) fn watch_notice_json(target: &MessageTarget, opts: &SendOptions) -> V
|
|
|
20
20
|
"watcher_id": format!("watch-{agent_id}"),
|
|
21
21
|
"task_id": opts.task_id.as_ref().map(|t| t.as_str().to_string()),
|
|
22
22
|
"agent_id": agent_id,
|
|
23
|
-
"notice": "
|
|
23
|
+
"notice": "The leader is notified when the agent replies; use team-agent inbox leader -n 3."
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -188,50 +188,51 @@ pub(super) fn send_human_output(value: &Value) -> String {
|
|
|
188
188
|
let reason = value.get("reason").and_then(Value::as_str).unwrap_or("");
|
|
189
189
|
let explanation =
|
|
190
190
|
crate::cli::named_address::human_address_reason(reason).unwrap_or(match reason {
|
|
191
|
-
"target_not_in_team" | "unknown_recipient" => "
|
|
191
|
+
"target_not_in_team" | "unknown_recipient" => "The selected team has no such agent.",
|
|
192
192
|
"missing_permissions" | "human_confirmation_required" => {
|
|
193
|
-
"
|
|
193
|
+
"Send refused: communication permission or human confirmation is required."
|
|
194
194
|
}
|
|
195
195
|
"recipient_busy" | "recipient_pane_in_non_input_mode" => {
|
|
196
|
-
"
|
|
196
|
+
"The agent cannot receive a task now; check its status."
|
|
197
197
|
}
|
|
198
198
|
"routing_ambiguous" | "empty_target_list" | "ambiguous" => {
|
|
199
|
-
"
|
|
199
|
+
"No recipient was selected unambiguously; provide an agent name and task message."
|
|
200
200
|
}
|
|
201
201
|
"team_owner_mismatch" | "PaneWorkspaceMismatch" | "session_drift" => {
|
|
202
|
-
"
|
|
202
|
+
"Team or pane ownership mismatch; sending was refused."
|
|
203
203
|
}
|
|
204
204
|
"coordinator_unavailable" | "tmux_target_missing" => {
|
|
205
|
-
"
|
|
205
|
+
"The team channel is unavailable; delivery was not confirmed."
|
|
206
206
|
}
|
|
207
207
|
"message_already_claimed" | "duplicate" => {
|
|
208
|
-
"
|
|
208
|
+
"This message is already claimed or duplicated; do not resend."
|
|
209
209
|
}
|
|
210
|
-
"no_caller_pane" => "
|
|
210
|
+
"no_caller_pane" => "The current terminal has no usable team channel; send from the leader's tool context.",
|
|
211
211
|
_ => value
|
|
212
212
|
.get("error")
|
|
213
213
|
.and_then(Value::as_str)
|
|
214
|
-
.unwrap_or("
|
|
214
|
+
.unwrap_or("Send was unsuccessful; task delivery was not confirmed."),
|
|
215
215
|
});
|
|
216
|
-
let
|
|
216
|
+
let details = crate::cli::triage::diagnostic_details(value);
|
|
217
|
+
let mut out = format!("{explanation}\nReport: {details}\nAdditional diagnosis: use team-agent status for agent names or team-agent doctor --workspace . for channel problems. Do not resend repeatedly.");
|
|
217
218
|
if let Some(suggested) = value
|
|
218
219
|
.get("suggested_name")
|
|
219
220
|
.and_then(Value::as_str)
|
|
220
221
|
.filter(|name| !name.is_empty() && !name.contains(['/', ':', ',', '\n', '\r']))
|
|
221
222
|
{
|
|
222
223
|
out.push_str(&format!(
|
|
223
|
-
"\
|
|
224
|
+
"\nDid you mean {suggested}? Confirm before using that agent name."
|
|
224
225
|
));
|
|
225
226
|
}
|
|
226
227
|
return out;
|
|
227
228
|
}
|
|
228
229
|
if value.get("status").and_then(Value::as_str) == Some("stored_only") {
|
|
229
|
-
return format!("
|
|
230
|
+
return format!("Message stored for {agent}; it was not injected into the current conversation.\nNext: use team-agent inbox leader -n 3 for replies. Mailbox storage does not guarantee execution.");
|
|
230
231
|
}
|
|
231
232
|
if value.get("delivered").and_then(Value::as_bool) == Some(true) {
|
|
232
|
-
format!("
|
|
233
|
+
format!("Delivered to {agent}. {}", crate::cli::SEND_REMINDER)
|
|
233
234
|
} else {
|
|
234
|
-
"
|
|
235
|
+
"Task accepted, but delivery to the agent's conversation is still pending.\nNext: check team-agent status or team-agent doctor --workspace .; wait for an actual reply without repeated sends.".to_string()
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
|
|
@@ -251,7 +252,7 @@ pub(super) fn send_human_target(value: &Value) -> String {
|
|
|
251
252
|
!name.is_empty() && *name != "*" && !name.contains(['/', ':', ',', '\n', '\r'])
|
|
252
253
|
})
|
|
253
254
|
.map(str::to_string)
|
|
254
|
-
.unwrap_or_else(|| "
|
|
255
|
+
.unwrap_or_else(|| "the recipient".to_string())
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
pub(super) fn send_human_status(value: &Value) -> String {
|
|
@@ -286,11 +287,11 @@ pub(super) fn send_reminder_for_value(value: &Value) -> &'static str {
|
|
|
286
287
|
let status = value.get("status").and_then(Value::as_str);
|
|
287
288
|
let delivery_status = value.get("delivery_status").and_then(Value::as_str);
|
|
288
289
|
if status == Some("stored_only") {
|
|
289
|
-
"
|
|
290
|
+
"Mailbox-only: not injected into the current conversation; storage does not guarantee execution."
|
|
290
291
|
} else if delivered == Some(true) && delivery_status != Some("pending") {
|
|
291
292
|
crate::cli::SEND_REMINDER
|
|
292
293
|
} else {
|
|
293
|
-
"
|
|
294
|
+
"Task accepted, but delivery is unconfirmed. Check team-agent status/doctor; wait for an actual reply without repeated sends."
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
@@ -7,11 +7,11 @@ use serde_json::{json, Value};
|
|
|
7
7
|
|
|
8
8
|
pub(super) fn warn_send_alias(flag: &str) {
|
|
9
9
|
let spec = crate::cli::spec::command_spec("send");
|
|
10
|
-
let sunset = spec.and_then(|spec| spec.sunset).unwrap_or("
|
|
10
|
+
let sunset = spec.and_then(|spec| spec.sunset).unwrap_or("a future compatibility release");
|
|
11
11
|
let action = spec.and_then(|spec| spec.action).unwrap_or(
|
|
12
|
-
"
|
|
12
|
+
"Use team-agent send <agent> 'task message'; select the workspace and team with --workspace/--team",
|
|
13
13
|
);
|
|
14
|
-
eprintln!("
|
|
14
|
+
eprintln!("Warning: {flag} is deprecated and will be removed in {sunset}. Next: {action}");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
pub(super) fn logical_to_from_args(
|
|
@@ -22,7 +22,7 @@ pub(super) fn logical_to_from_args(
|
|
|
22
22
|
&& (args.target.is_some() || args.targets.is_some() || args.to_leader.is_some())
|
|
23
23
|
{
|
|
24
24
|
return Err(CliError::Usage(
|
|
25
|
-
"
|
|
25
|
+
"An agent name cannot be combined with --to-name/--pane/--to; choose one addressing mode".to_string(),
|
|
26
26
|
));
|
|
27
27
|
}
|
|
28
28
|
let supplied = [
|
|
@@ -36,7 +36,7 @@ pub(super) fn logical_to_from_args(
|
|
|
36
36
|
.count();
|
|
37
37
|
if supplied > 1 {
|
|
38
38
|
return Err(CliError::Usage(
|
|
39
|
-
"
|
|
39
|
+
"An agent name, --targets, --to-name and --to-leader are mutually exclusive; choose one addressing mode"
|
|
40
40
|
.to_string(),
|
|
41
41
|
));
|
|
42
42
|
}
|
|
@@ -52,11 +52,11 @@ pub(super) fn logical_to_from_args(
|
|
|
52
52
|
if args.target.is_none() && supplied > 0 && args.message.is_empty() {
|
|
53
53
|
if args.to_name.is_some() {
|
|
54
54
|
return Err(CliError::Usage(
|
|
55
|
-
"
|
|
55
|
+
"Provide a task message; --to-name cannot send an empty message".to_string(),
|
|
56
56
|
));
|
|
57
57
|
}
|
|
58
58
|
return Err(CliError::Usage(
|
|
59
|
-
"
|
|
59
|
+
"Provide a nonempty task message after the agent name, such as team-agent send worker 'Review the changes'".to_string(),
|
|
60
60
|
));
|
|
61
61
|
}
|
|
62
62
|
Ok(logical_to)
|
|
@@ -182,7 +182,7 @@ pub(super) fn send_to_logical_to(
|
|
|
182
182
|
.collect::<Vec<_>>();
|
|
183
183
|
if names.is_empty() || names.len() != logical_to.split(',').count() {
|
|
184
184
|
return Err(CliError::Usage(
|
|
185
|
-
"
|
|
185
|
+
"The recipient list contains an empty agent name; use names returned by status".to_string(),
|
|
186
186
|
));
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -322,7 +322,7 @@ pub(super) fn adapt_positional_bare_error(
|
|
|
322
322
|
.and_then(|suggested| suggested.rsplit('/').next())
|
|
323
323
|
.map(str::to_string);
|
|
324
324
|
if let Some(suggested) = error.suggested_name.as_deref() {
|
|
325
|
-
error.action = format!("
|
|
325
|
+
error.action = format!("Did you mean {suggested}? Confirm the agent name; select the workspace and team with --workspace/--team.");
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
@@ -336,7 +336,7 @@ pub(super) fn logical_recipient_id(
|
|
|
336
336
|
.ok_or_else(|| CliError::Runtime("resolved worker is missing agent id".to_string())),
|
|
337
337
|
crate::cli::named_address::NamedTargetKind::Leader => Ok("leader".to_string()),
|
|
338
338
|
crate::cli::named_address::NamedTargetKind::SessionWindow => Err(CliError::Usage(
|
|
339
|
-
"
|
|
339
|
+
"Terminal-window addressing is no longer supported; use an agent name or leader, with --workspace/--team to select scope"
|
|
340
340
|
.to_string(),
|
|
341
341
|
)),
|
|
342
342
|
}
|
|
@@ -350,7 +350,7 @@ pub(super) fn send_to_resolved_name(
|
|
|
350
350
|
let recipient = logical_recipient_id(resolved)?;
|
|
351
351
|
if resolved.warning.is_some() {
|
|
352
352
|
eprintln!(
|
|
353
|
-
"
|
|
353
|
+
"Warning: legacy addressing is deprecated; send by agent name and select scope with --workspace/--team."
|
|
354
354
|
);
|
|
355
355
|
}
|
|
356
356
|
let target = MessageTarget::Single(recipient);
|
|
@@ -41,20 +41,20 @@ pub fn cmd_send(args: &SendArgs) -> Result<CmdResult, CliError> {
|
|
|
41
41
|
|| args.to_leader.is_some()
|
|
42
42
|
{
|
|
43
43
|
let message = if args.to_name.is_some() {
|
|
44
|
-
"
|
|
44
|
+
"An agent name cannot be combined with --to-name/--pane/--to; choose one addressing mode"
|
|
45
45
|
} else {
|
|
46
|
-
"--pane
|
|
46
|
+
"--pane cannot be combined with an agent name, --to or --to-leader"
|
|
47
47
|
};
|
|
48
48
|
return Err(CliError::Usage(message.to_string()));
|
|
49
49
|
}
|
|
50
50
|
let content = args.message.join(" ");
|
|
51
51
|
if content.is_empty() {
|
|
52
52
|
return Err(CliError::Usage(
|
|
53
|
-
"
|
|
53
|
+
"Provide a task message; --pane cannot send an empty message".to_string(),
|
|
54
54
|
));
|
|
55
55
|
}
|
|
56
56
|
return Err(CliError::Usage(format!(
|
|
57
|
-
"--pane {pane_id}
|
|
57
|
+
"--pane {pane_id} is no longer supported; use team-agent send <agent> 'task message' with --workspace/--team to select scope"
|
|
58
58
|
)));
|
|
59
59
|
}
|
|
60
60
|
if args.targets.is_some() {
|
|
@@ -106,35 +106,35 @@ pub(crate) struct CommandSpec {
|
|
|
106
106
|
// Only human operations belong here. Compatibility commands are private in emit.rs.
|
|
107
107
|
#[rustfmt::skip]
|
|
108
108
|
pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
|
|
109
|
-
CommandSpec { name: "quick-start", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::QuickStart), summary: "
|
|
110
|
-
CommandSpec { name: "send", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Send), summary: "
|
|
111
|
-
CommandSpec { name: "status", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Status), summary: "
|
|
112
|
-
CommandSpec { name: "models", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Models), summary: "
|
|
113
|
-
CommandSpec { name: "inbox", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Inbox), summary: "
|
|
114
|
-
CommandSpec { name: "restart", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Restart), summary: "
|
|
115
|
-
CommandSpec { name: "shutdown", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Shutdown), summary: "
|
|
116
|
-
CommandSpec { name: "add-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::AddAgent), summary: "
|
|
117
|
-
CommandSpec { name: "start-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::StartAgent), summary: "
|
|
118
|
-
CommandSpec { name: "stop-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::StopAgent), summary: "
|
|
119
|
-
CommandSpec { name: "reset-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::ResetAgent), summary: "
|
|
120
|
-
CommandSpec { name: "clone-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::CloneAgent), summary: "
|
|
121
|
-
CommandSpec { name: "fork-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::ForkAgent), summary: "
|
|
122
|
-
CommandSpec { name: "remove-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::RemoveAgent), summary: "
|
|
123
|
-
CommandSpec { name: "leaders", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Leaders), summary: "
|
|
124
|
-
CommandSpec { name: "doctor", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Doctor), summary: "
|
|
125
|
-
CommandSpec { name: "approvals", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Approvals), summary: "
|
|
126
|
-
CommandSpec { name: "route", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Route), summary: "
|
|
127
|
-
CommandSpec { name: "profile", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Profile), summary: "
|
|
128
|
-
CommandSpec { name: "install-skill", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::InstallSkill), summary: "
|
|
129
|
-
CommandSpec { name: "claim-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::ClaimLeader), summary: "
|
|
130
|
-
CommandSpec { name: "takeover", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::Takeover), summary: "
|
|
131
|
-
CommandSpec { name: "attach-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::AttachLeader), summary: "
|
|
132
|
-
CommandSpec { name: "pi", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "pi" }, summary: "
|
|
133
|
-
CommandSpec { name: "codex", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "codex" }, summary: "
|
|
134
|
-
CommandSpec { name: "claude", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "claude" }, summary: "
|
|
135
|
-
CommandSpec { name: "copilot", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "copilot" }, summary: "
|
|
136
|
-
CommandSpec { name: "grok", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "grok" }, summary: "
|
|
137
|
-
CommandSpec { name: "cursor", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "cursor_agent" }, summary: "
|
|
109
|
+
CommandSpec { name: "quick-start", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::QuickStart), summary: "Create a team from role files; use restart for an initialized team", usage: "team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--team TEAM] [--name NAME] [--team-id TEAM] [--yes] [--backend tmux|conpty] [--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
110
|
+
CommandSpec { name: "send", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Send), summary: "Send a task to a specified agent", usage: "team-agent send <agent> MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--mailbox] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: Some("a future compatibility release"), action: Some("Use team-agent send <agent> 'task message'; select scope with --workspace and --team"), governance: None },
|
|
111
|
+
CommandSpec { name: "status", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Status), summary: "Check agent status without reading conversation content", usage: "team-agent status [<agent>] [--workspace WORKSPACE] [--team TEAM] [--json] [--summary|--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
112
|
+
CommandSpec { name: "models", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Models), summary: "Discover model names supported by the native provider", usage: "team-agent models [--provider pi|cursor_agent|codex|claude|claude_code|grok] [QUERY|--search TEXT] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
113
|
+
CommandSpec { name: "inbox", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::Inbox), summary: "Read agent replies", usage: "team-agent inbox <agent> [-n N|--limit N] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
114
|
+
CommandSpec { name: "restart", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Restart), summary: "Restart an existing team using saved sessions when available", usage: "team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
115
|
+
CommandSpec { name: "shutdown", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Shutdown), summary: "Shut down the selected team and report residual resources", usage: "team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
116
|
+
CommandSpec { name: "add-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::AddAgent), summary: "Add and start an agent", usage: "team-agent add-agent <agent> [--role-file FILE] [--provider TOOL] [--model MODEL] [--effort LEVEL] [--bypass true|false] [--prompt TEXT] [--profile NAME] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
117
|
+
CommandSpec { name: "start-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::StartAgent), summary: "Start an existing team agent", usage: "team-agent start-agent <agent> [--provider TOOL] [--model MODEL] [--effort LEVEL] [--bypass true|false] [--prompt TEXT] [--profile NAME] [--workspace WORKSPACE] [--team TEAM] [--allow-fresh] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
118
|
+
CommandSpec { name: "stop-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::StopAgent), summary: "Stop an agent while retaining configuration and session history", usage: "team-agent stop-agent <agent> [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
119
|
+
CommandSpec { name: "reset-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::ResetAgent), summary: "Discard an agent's saved session association", usage: "team-agent reset-agent <agent> --discard-session [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
120
|
+
CommandSpec { name: "clone-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::CloneAgent), summary: "Clone agent configuration into a new agent", usage: "team-agent clone-agent <agent> --as NEW_AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
121
|
+
CommandSpec { name: "fork-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::ForkAgent), summary: "Fork an agent session where supported by the provider", usage: "team-agent fork-agent <agent> --as NEW_AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
122
|
+
CommandSpec { name: "remove-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::RemoveAgent), summary: "Remove an agent after confirmation", usage: "team-agent remove-agent <agent> --confirm [--workspace WORKSPACE] [--team TEAM] [--from-spec] [--force] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
123
|
+
CommandSpec { name: "leaders", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Leaders), summary: "List local leaders and their team scopes", usage: "team-agent leaders [QUERY|--search TEXT] [--all|--stale] [--json] | --prune [--dry-run] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
124
|
+
CommandSpec { name: "doctor", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Doctor), summary: "Diagnose a team and suggest recovery; read-only by default", usage: "team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--comms] [--gate orphans|comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
125
|
+
CommandSpec { name: "approvals", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Approvals), summary: "List pending agent approvals", usage: "team-agent approvals [<agent>] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
126
|
+
CommandSpec { name: "route", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Route), summary: "Configure additional provider launch arguments", usage: "team-agent route [status|enable|disable|show [TOOL]|set TOOL -- ARG...|add TOOL -- ARG...|clear TOOL] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
127
|
+
CommandSpec { name: "profile", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Profile), summary: "Manage authentication and proxy profiles", usage: "team-agent profile init|doctor|show NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--proxy-mode direct|inherit] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
128
|
+
CommandSpec { name: "install-skill", tier: CommandTier::Core, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::InstallSkill), summary: "Optionally install an existing public operation skill", usage: "team-agent install-skill (--source DIR|--uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
129
|
+
CommandSpec { name: "claim-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::ClaimLeader), summary: "Register the current terminal as leader only when diagnosis recommends it", usage: "team-agent claim-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
130
|
+
CommandSpec { name: "takeover", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::Takeover), summary: "Take over an existing team only after diagnosis and confirmation", usage: "team-agent takeover [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
131
|
+
CommandSpec { name: "attach-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::AttachLeader), summary: "Attach a verified leader terminal only when diagnosis recommends it", usage: "team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] --pane PANE --provider TOOL [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, sunset: None, action: None, governance: None },
|
|
132
|
+
CommandSpec { name: "pi", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "pi" }, summary: "Open a leader session with Pi", usage: "team-agent pi [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
133
|
+
CommandSpec { name: "codex", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "codex" }, summary: "Open a leader session with Codex", usage: "team-agent codex [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
134
|
+
CommandSpec { name: "claude", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "claude" }, summary: "Open a leader session with Claude Code", usage: "team-agent claude [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
135
|
+
CommandSpec { name: "copilot", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "copilot" }, summary: "Open a leader session with Copilot", usage: "team-agent copilot [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
136
|
+
CommandSpec { name: "grok", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "grok" }, summary: "Open a leader session with Grok", usage: "team-agent grok [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
137
|
+
CommandSpec { name: "cursor", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "cursor_agent" }, summary: "Open a leader session with Cursor agent", usage: "team-agent cursor [--attach-existing] [--confirm] [--attach-session SESSION] [--external-leader] [--allow-nested-attach] [--json] [-- TOOL_ARGS...]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, sunset: None, action: None, governance: None },
|
|
138
138
|
];
|
|
139
139
|
|
|
140
140
|
pub(crate) fn command_spec(name: &str) -> Option<&'static CommandSpec> {
|