@team-agent/installer 0.5.82 → 0.5.84
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/emit.rs +193 -14
- package/crates/team-agent/src/cli/leaders.rs +304 -167
- package/crates/team-agent/src/cli/models.rs +231 -24
- package/crates/team-agent/src/cli/spec.rs +2 -2
- package/crates/team-agent/src/cli/types.rs +12 -1
- package/crates/team-agent/src/leader/registry.rs +295 -64
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +16 -18
- package/crates/team-agent/src/mcp_server/tests/tools.rs +31 -20
- package/crates/team-agent/src/mcp_server/tests/wire.rs +30 -0
- package/crates/team-agent/src/mcp_server/tools.rs +113 -11
- package/crates/team-agent/src/mcp_server/wire.rs +20 -1
- package/crates/team-agent/src/state/persist.rs +50 -1
- package/package.json +4 -4
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -218,7 +218,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
218
218
|
// resolve short/qualified/hash-qualified names to a canonical
|
|
219
219
|
// (workspace, team_key) tuple and delegates to the E6 named-leader
|
|
220
220
|
// delivery path — no separate route authority.
|
|
221
|
-
"leaders" => cmd_leaders(&leaders_args(args, cwd)).map(emit_result),
|
|
221
|
+
"leaders" => cmd_leaders(&leaders_args(args, cwd)?).map(emit_result),
|
|
222
222
|
"models" => cmd_models(&models_args(args)?).map(emit_result),
|
|
223
223
|
"validate" => cmd_validate(&validate_args(args, cwd)).map(emit_result),
|
|
224
224
|
"install-skill" => cmd_install_skill(&install_skill_args(args)?).map(emit_result),
|
|
@@ -324,7 +324,15 @@ pub(crate) fn default_help() -> String {
|
|
|
324
324
|
append_help_section(
|
|
325
325
|
&mut out,
|
|
326
326
|
"Core",
|
|
327
|
-
&[
|
|
327
|
+
&[
|
|
328
|
+
"quick-start",
|
|
329
|
+
"send",
|
|
330
|
+
"status",
|
|
331
|
+
"collect",
|
|
332
|
+
"results",
|
|
333
|
+
"models",
|
|
334
|
+
"leaders",
|
|
335
|
+
],
|
|
328
336
|
);
|
|
329
337
|
append_help_section(
|
|
330
338
|
&mut out,
|
|
@@ -410,7 +418,8 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
410
418
|
.to_string(),
|
|
411
419
|
Some("allow-peer-talk") => "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]".to_string(),
|
|
412
420
|
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 使用同一投影。缺少可靠定位或 nodeprobe 证据时显示 unknown;tmux_command 可复制到对应目标。--summary/--detail 仅保留兼容性,不增加诊断字段。".to_string(),
|
|
413
|
-
Some("models") => "usage: team-agent models --provider pi [
|
|
421
|
+
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
|
+
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(),
|
|
414
423
|
Some("stop") => compat_hidden_help("stop", "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]"),
|
|
415
424
|
Some("shutdown") => "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]".to_string(),
|
|
416
425
|
Some("restart") => "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json] [--detail]".to_string(),
|
|
@@ -998,6 +1007,9 @@ fn parse_args(args: &[String]) -> ParsedArgs {
|
|
|
998
1007
|
other if other.starts_with("--provider=") => {
|
|
999
1008
|
parsed.provider = Some(other.trim_start_matches("--provider=").to_string());
|
|
1000
1009
|
}
|
|
1010
|
+
other if other.starts_with("--search=") => {
|
|
1011
|
+
parsed.search = Some(other.trim_start_matches("--search=").to_string());
|
|
1012
|
+
}
|
|
1001
1013
|
other if other.starts_with("--socket=") => {
|
|
1002
1014
|
parsed.socket = Some(other.trim_start_matches("--socket=").to_string());
|
|
1003
1015
|
}
|
|
@@ -1097,17 +1109,19 @@ fn quick_start_args(args: &[String], cwd: &Path) -> Result<QuickStartArgs, CliEr
|
|
|
1097
1109
|
|
|
1098
1110
|
fn models_args(args: &[String]) -> Result<ModelsArgs, CliError> {
|
|
1099
1111
|
let parsed = parse_args(args);
|
|
1100
|
-
let provider = parsed
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
"models supports only --provider pi, got {provider:?}"
|
|
1106
|
-
)));
|
|
1112
|
+
let provider = parsed.provider.unwrap_or_else(|| "pi".to_string());
|
|
1113
|
+
if parsed.search.is_some() && !parsed.positionals.is_empty() {
|
|
1114
|
+
return Err(CliError::Usage(
|
|
1115
|
+
"models accepts either QUERY or --search TEXT, not both".to_string(),
|
|
1116
|
+
));
|
|
1107
1117
|
}
|
|
1118
|
+
let search = parsed
|
|
1119
|
+
.search
|
|
1120
|
+
.or_else(|| (!parsed.positionals.is_empty()).then(|| parsed.positionals.join(" ")))
|
|
1121
|
+
.filter(|query| !query.trim().is_empty());
|
|
1108
1122
|
Ok(ModelsArgs {
|
|
1109
1123
|
provider,
|
|
1110
|
-
search
|
|
1124
|
+
search,
|
|
1111
1125
|
json: parsed.json,
|
|
1112
1126
|
})
|
|
1113
1127
|
}
|
|
@@ -1683,9 +1697,109 @@ fn sessions_args(args: &[String], cwd: &Path) -> SessionsArgs {
|
|
|
1683
1697
|
}
|
|
1684
1698
|
}
|
|
1685
1699
|
|
|
1686
|
-
fn leaders_args(args: &[String], _cwd: &Path) -> LeadersArgs {
|
|
1687
|
-
let
|
|
1688
|
-
|
|
1700
|
+
fn leaders_args(args: &[String], _cwd: &Path) -> Result<LeadersArgs, CliError> {
|
|
1701
|
+
let mut view = None;
|
|
1702
|
+
let mut query = None;
|
|
1703
|
+
let mut prune = false;
|
|
1704
|
+
let mut dry_run = false;
|
|
1705
|
+
let mut json = false;
|
|
1706
|
+
let mut i = 0usize;
|
|
1707
|
+
while i < args.len() {
|
|
1708
|
+
let arg = &args[i];
|
|
1709
|
+
match arg.as_str() {
|
|
1710
|
+
"--json" => json = true,
|
|
1711
|
+
"--all" => {
|
|
1712
|
+
if view.replace(LeadersView::All).is_some() {
|
|
1713
|
+
return Err(CliError::Usage(
|
|
1714
|
+
"leaders accepts only one of --all or --stale".to_string(),
|
|
1715
|
+
));
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
"--stale" => {
|
|
1719
|
+
if view.replace(LeadersView::Stale).is_some() {
|
|
1720
|
+
return Err(CliError::Usage(
|
|
1721
|
+
"leaders accepts only one of --all or --stale".to_string(),
|
|
1722
|
+
));
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
"--prune" => {
|
|
1726
|
+
if prune {
|
|
1727
|
+
return Err(CliError::Usage(
|
|
1728
|
+
"leaders accepts --prune at most once".to_string(),
|
|
1729
|
+
));
|
|
1730
|
+
}
|
|
1731
|
+
prune = true;
|
|
1732
|
+
}
|
|
1733
|
+
"--dry-run" => dry_run = true,
|
|
1734
|
+
"--search" => {
|
|
1735
|
+
let Some(value) = args.get(i.saturating_add(1)) else {
|
|
1736
|
+
return Err(CliError::Usage(
|
|
1737
|
+
"leaders --search requires TEXT".to_string(),
|
|
1738
|
+
));
|
|
1739
|
+
};
|
|
1740
|
+
if value.starts_with('-') {
|
|
1741
|
+
return Err(CliError::Usage(
|
|
1742
|
+
"leaders --search requires TEXT".to_string(),
|
|
1743
|
+
));
|
|
1744
|
+
}
|
|
1745
|
+
i = i.saturating_add(1);
|
|
1746
|
+
if query.replace(value.clone()).is_some() {
|
|
1747
|
+
return Err(CliError::Usage(
|
|
1748
|
+
"leaders accepts only one QUERY or --search TEXT".to_string(),
|
|
1749
|
+
));
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
value if value.starts_with("--search=") => {
|
|
1753
|
+
let value = value.trim_start_matches("--search=").to_string();
|
|
1754
|
+
if query.replace(value).is_some() {
|
|
1755
|
+
return Err(CliError::Usage(
|
|
1756
|
+
"leaders accepts only one QUERY or --search TEXT".to_string(),
|
|
1757
|
+
));
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
value if value.starts_with('-') => {
|
|
1761
|
+
return Err(CliError::Usage(format!(
|
|
1762
|
+
"unknown leaders argument: {value}"
|
|
1763
|
+
)))
|
|
1764
|
+
}
|
|
1765
|
+
value => {
|
|
1766
|
+
if query.replace(value.to_string()).is_some() {
|
|
1767
|
+
return Err(CliError::Usage(
|
|
1768
|
+
"leaders accepts only one QUERY or --search TEXT".to_string(),
|
|
1769
|
+
));
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
i = i.saturating_add(1);
|
|
1774
|
+
}
|
|
1775
|
+
if query
|
|
1776
|
+
.as_deref()
|
|
1777
|
+
.is_some_and(|value| value.trim().is_empty())
|
|
1778
|
+
{
|
|
1779
|
+
return Err(CliError::Usage("leaders QUERY cannot be blank".to_string()));
|
|
1780
|
+
}
|
|
1781
|
+
if prune && view.is_some() {
|
|
1782
|
+
return Err(CliError::Usage(
|
|
1783
|
+
"leaders --prune cannot be combined with --all or --stale".to_string(),
|
|
1784
|
+
));
|
|
1785
|
+
}
|
|
1786
|
+
if prune && query.is_some() {
|
|
1787
|
+
return Err(CliError::Usage(
|
|
1788
|
+
"leaders --prune cannot be combined with QUERY or --search".to_string(),
|
|
1789
|
+
));
|
|
1790
|
+
}
|
|
1791
|
+
if dry_run && !prune {
|
|
1792
|
+
return Err(CliError::Usage(
|
|
1793
|
+
"leaders --dry-run requires --prune".to_string(),
|
|
1794
|
+
));
|
|
1795
|
+
}
|
|
1796
|
+
Ok(LeadersArgs {
|
|
1797
|
+
view: view.unwrap_or(LeadersView::Live),
|
|
1798
|
+
query,
|
|
1799
|
+
prune,
|
|
1800
|
+
dry_run,
|
|
1801
|
+
json,
|
|
1802
|
+
})
|
|
1689
1803
|
}
|
|
1690
1804
|
|
|
1691
1805
|
fn validate_args(args: &[String], cwd: &Path) -> ValidateArgs {
|
|
@@ -1935,6 +2049,37 @@ mod tests {
|
|
|
1935
2049
|
.collect()
|
|
1936
2050
|
}
|
|
1937
2051
|
|
|
2052
|
+
#[test]
|
|
2053
|
+
fn leaders_parser_supports_views_and_search() {
|
|
2054
|
+
let parsed = leaders_args(
|
|
2055
|
+
&cli_argv(&["--all", "--search", "Wiki", "--json"]),
|
|
2056
|
+
Path::new("."),
|
|
2057
|
+
)
|
|
2058
|
+
.unwrap();
|
|
2059
|
+
assert_eq!(parsed.view, LeadersView::All);
|
|
2060
|
+
assert_eq!(parsed.query.as_deref(), Some("Wiki"));
|
|
2061
|
+
assert!(!parsed.prune);
|
|
2062
|
+
assert!(!parsed.dry_run);
|
|
2063
|
+
assert!(parsed.json);
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
#[test]
|
|
2067
|
+
fn leaders_parser_rejects_invalid_search_and_prune_combinations() {
|
|
2068
|
+
for args in [
|
|
2069
|
+
cli_argv(&["one", "two"]),
|
|
2070
|
+
cli_argv(&["one", "--search", "two"]),
|
|
2071
|
+
cli_argv(&["--search", " "]),
|
|
2072
|
+
cli_argv(&["--dry-run"]),
|
|
2073
|
+
cli_argv(&["--prune", "--stale"]),
|
|
2074
|
+
cli_argv(&["--prune", "--search", "x"]),
|
|
2075
|
+
] {
|
|
2076
|
+
assert!(
|
|
2077
|
+
leaders_args(&args, Path::new(".")).is_err(),
|
|
2078
|
+
"args={args:?}"
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
1938
2083
|
#[test]
|
|
1939
2084
|
fn command_specs_have_unique_names_and_valid_aliases() {
|
|
1940
2085
|
let mut names = std::collections::BTreeSet::new();
|
|
@@ -2031,6 +2176,16 @@ mod tests {
|
|
|
2031
2176
|
}
|
|
2032
2177
|
}
|
|
2033
2178
|
|
|
2179
|
+
#[test]
|
|
2180
|
+
fn leaders_help_publishes_selectors_and_prune() {
|
|
2181
|
+
let help = command_help(Some("leaders"));
|
|
2182
|
+
assert!(help.contains("--all"));
|
|
2183
|
+
assert!(help.contains("--stale"));
|
|
2184
|
+
assert!(help.contains("--search TEXT"));
|
|
2185
|
+
assert!(help.contains("--prune"));
|
|
2186
|
+
assert!(help.contains("--dry-run"));
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2034
2189
|
#[test]
|
|
2035
2190
|
fn compat_hidden_help_has_sunset_action() {
|
|
2036
2191
|
for command in ["stop", "restart-agent", "start", "init"] {
|
|
@@ -2235,6 +2390,30 @@ mod tests {
|
|
|
2235
2390
|
assert!(!help.contains("错误细分走 status --summary"));
|
|
2236
2391
|
}
|
|
2237
2392
|
|
|
2393
|
+
#[test]
|
|
2394
|
+
fn models_defaults_provider_and_accepts_one_query_form() {
|
|
2395
|
+
let defaulted = models_args(&cli_argv(&["gpt", "5.6", "luna"])).unwrap();
|
|
2396
|
+
assert_eq!(defaulted.provider, "pi");
|
|
2397
|
+
assert_eq!(defaulted.search.as_deref(), Some("gpt 5.6 luna"));
|
|
2398
|
+
|
|
2399
|
+
let cursor = models_args(&cli_argv(&[
|
|
2400
|
+
"--provider",
|
|
2401
|
+
"cursor_agent",
|
|
2402
|
+
"--search",
|
|
2403
|
+
"GPT Luna",
|
|
2404
|
+
]))
|
|
2405
|
+
.unwrap();
|
|
2406
|
+
assert_eq!(cursor.provider, "cursor_agent");
|
|
2407
|
+
assert_eq!(cursor.search.as_deref(), Some("GPT Luna"));
|
|
2408
|
+
|
|
2409
|
+
let both = models_args(&cli_argv(&["luna", "--search", "gpt"])).unwrap_err();
|
|
2410
|
+
assert!(
|
|
2411
|
+
matches!(both, CliError::Usage(message) if message.contains("either QUERY or --search TEXT"))
|
|
2412
|
+
);
|
|
2413
|
+
let blank = models_args(&cli_argv(&["--search", " "])).unwrap();
|
|
2414
|
+
assert_eq!(blank.search, None);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2238
2417
|
#[test]
|
|
2239
2418
|
fn send_pane_positionals_are_message_not_target() {
|
|
2240
2419
|
let cwd = tmp_workspace();
|