@team-agent/installer 0.5.28 → 0.5.31

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.
@@ -0,0 +1,222 @@
1
+ //! C1 command registry: one catalog for help visibility, known-command gates,
2
+ //! suggestion candidates, and command-tier governance.
3
+
4
+ #![allow(dead_code)]
5
+
6
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
7
+ pub(crate) enum CommandTier {
8
+ Core,
9
+ Guided,
10
+ Secondary,
11
+ DevInternal,
12
+ CompatHidden,
13
+ }
14
+
15
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
16
+ pub(crate) enum CommandCategory {
17
+ Start,
18
+ Daily,
19
+ TeamLifecycle,
20
+ WorkerLifecycle,
21
+ GuidedRecovery,
22
+ Setup,
23
+ Observe,
24
+ Dev,
25
+ Compat,
26
+ }
27
+
28
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
29
+ pub(crate) enum CommandKind {
30
+ Dispatch(DispatchKind),
31
+ LeaderPassthrough { provider: &'static str },
32
+ SpecOnlyAlias { alias_of: &'static str },
33
+ }
34
+
35
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
36
+ pub(crate) enum TokenUsage {
37
+ No,
38
+ Yes,
39
+ Conditional,
40
+ }
41
+
42
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
43
+ pub(crate) enum DispatchKind {
44
+ Init,
45
+ QuickStart,
46
+ Compile,
47
+ Send,
48
+ FallbackSendLeader,
49
+ FallbackReportResult,
50
+ AllowPeerTalk,
51
+ Status,
52
+ Stop,
53
+ Shutdown,
54
+ Restart,
55
+ RestartAgent,
56
+ StartAgent,
57
+ StopAgent,
58
+ ResetAgent,
59
+ AddAgent,
60
+ ForkAgent,
61
+ RemoveAgent,
62
+ StuckList,
63
+ StuckCancel,
64
+ AcknowledgeIdle,
65
+ Takeover,
66
+ ClaimLeader,
67
+ AttachLeader,
68
+ AttachAppServerLeader,
69
+ Identity,
70
+ Approvals,
71
+ Inbox,
72
+ Doctor,
73
+ Watch,
74
+ Sessions,
75
+ Leaders,
76
+ Validate,
77
+ InstallSkill,
78
+ Profile,
79
+ ValidateResult,
80
+ Collect,
81
+ Settle,
82
+ RepairState,
83
+ Diagnose,
84
+ Preflight,
85
+ WaitReady,
86
+ E2e,
87
+ Peek,
88
+ Coordinator,
89
+ }
90
+
91
+ pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
92
+ DispatchKind::Init,
93
+ DispatchKind::QuickStart,
94
+ DispatchKind::Compile,
95
+ DispatchKind::Send,
96
+ DispatchKind::FallbackSendLeader,
97
+ DispatchKind::FallbackReportResult,
98
+ DispatchKind::AllowPeerTalk,
99
+ DispatchKind::Status,
100
+ DispatchKind::Stop,
101
+ DispatchKind::Shutdown,
102
+ DispatchKind::Restart,
103
+ DispatchKind::RestartAgent,
104
+ DispatchKind::StartAgent,
105
+ DispatchKind::StopAgent,
106
+ DispatchKind::ResetAgent,
107
+ DispatchKind::AddAgent,
108
+ DispatchKind::ForkAgent,
109
+ DispatchKind::RemoveAgent,
110
+ DispatchKind::StuckList,
111
+ DispatchKind::StuckCancel,
112
+ DispatchKind::AcknowledgeIdle,
113
+ DispatchKind::Takeover,
114
+ DispatchKind::ClaimLeader,
115
+ DispatchKind::AttachLeader,
116
+ DispatchKind::AttachAppServerLeader,
117
+ DispatchKind::Identity,
118
+ DispatchKind::Approvals,
119
+ DispatchKind::Inbox,
120
+ DispatchKind::Doctor,
121
+ DispatchKind::Watch,
122
+ DispatchKind::Sessions,
123
+ DispatchKind::Leaders,
124
+ DispatchKind::Validate,
125
+ DispatchKind::InstallSkill,
126
+ DispatchKind::Profile,
127
+ DispatchKind::ValidateResult,
128
+ DispatchKind::Collect,
129
+ DispatchKind::Settle,
130
+ DispatchKind::RepairState,
131
+ DispatchKind::Diagnose,
132
+ DispatchKind::Preflight,
133
+ DispatchKind::WaitReady,
134
+ DispatchKind::E2e,
135
+ DispatchKind::Peek,
136
+ DispatchKind::Coordinator,
137
+ ];
138
+
139
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
140
+ pub(crate) struct GovernanceNote {
141
+ pub(crate) decision: &'static str,
142
+ pub(crate) reason: &'static str,
143
+ }
144
+
145
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
146
+ pub(crate) struct CommandSpec {
147
+ pub(crate) name: &'static str,
148
+ pub(crate) tier: CommandTier,
149
+ pub(crate) category: CommandCategory,
150
+ pub(crate) kind: CommandKind,
151
+ pub(crate) summary: &'static str,
152
+ pub(crate) usage: &'static str,
153
+ pub(crate) default_help: bool,
154
+ pub(crate) command_help: bool,
155
+ pub(crate) suggestion_index: bool,
156
+ pub(crate) token_usage: TokenUsage,
157
+ pub(crate) alias_of: Option<&'static str>,
158
+ pub(crate) sunset: Option<&'static str>,
159
+ pub(crate) action: Option<&'static str>,
160
+ pub(crate) governance: Option<GovernanceNote>,
161
+ }
162
+
163
+ #[rustfmt::skip]
164
+ pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
165
+ CommandSpec { name: "quick-start", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::QuickStart), summary: "start or attach a team from TEAM.md", usage: "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--no-display] [--backend tmux|conpty] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
166
+ CommandSpec { name: "send", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Send), summary: "send a message/task", usage: "usage: team-agent send TARGET MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--targets AGENTS] [--to-name NAME] [--pane PANE] [--task TASK] [--sender SENDER] [--watch-result] [--requires-ack|--no-ack] [--no-wait] [--timeout SECONDS] [--confirm-human] [--message-id ID] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
167
+ CommandSpec { name: "status", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Status), summary: "show current team status", usage: "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
168
+ CommandSpec { name: "collect", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Collect), summary: "collect reported results", usage: "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
169
+ CommandSpec { name: "restart", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Restart), summary: "restart the selected team", usage: "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
170
+ CommandSpec { name: "shutdown", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Shutdown), summary: "stop the selected team", usage: "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
171
+ CommandSpec { name: "add-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::AddAgent), summary: "add a worker", usage: "usage: team-agent add-agent AGENT --role-file FILE [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
172
+ CommandSpec { name: "start-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::StartAgent), summary: "start an existing worker", usage: "usage: team-agent start-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--force] [--allow-fresh] [--no-display] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
173
+ CommandSpec { name: "stop-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::StopAgent), summary: "stop a worker", usage: "usage: team-agent stop-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
174
+ CommandSpec { name: "reset-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::ResetAgent), summary: "reset a worker session", usage: "usage: team-agent reset-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
175
+ CommandSpec { name: "diagnose", tier: CommandTier::Core, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Diagnose), summary: "inspect this workspace runtime and suggested repairs", usage: "usage: team-agent diagnose [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
176
+ CommandSpec { name: "claim-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::ClaimLeader), summary: "bind this pane when diagnose reports rebind_required", usage: "usage: team-agent claim-leader [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
177
+ CommandSpec { name: "takeover", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::Takeover), summary: "replace a live leader binding with explicit consent", usage: "usage: team-agent takeover [--workspace WORKSPACE] [--team TEAM] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
178
+ CommandSpec { name: "attach-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::AttachLeader), summary: "bind an external leader pane", usage: "usage: team-agent attach-leader [--workspace WORKSPACE] [--team TEAM] [--pane PANE] [--provider PROVIDER] [--confirm] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
179
+
180
+ CommandSpec { name: "codex", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "codex" }, summary: "launch a Codex leader", usage: "usage: team-agent codex [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
181
+ CommandSpec { name: "claude", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "claude" }, summary: "launch a Claude leader", usage: "usage: team-agent claude [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
182
+ CommandSpec { name: "copilot", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "copilot" }, summary: "launch a Copilot leader", usage: "usage: team-agent copilot [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
183
+
184
+ CommandSpec { name: "init", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::Init), summary: "legacy bootstrap command", usage: "usage: team-agent init [--workspace WORKSPACE] [--force] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: Some("quick-start"), sunset: Some("C2"), action: Some("use `team-agent quick-start`"), governance: None },
185
+ CommandSpec { name: "start", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::SpecOnlyAlias { alias_of: "quick-start" }, summary: "legacy start alias", usage: "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::Conditional, alias_of: Some("quick-start"), sunset: Some("C2"), action: Some("use `team-agent quick-start` or `team-agent restart`"), governance: None },
186
+ CommandSpec { name: "stop", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::Stop), summary: "legacy shutdown alias", usage: "usage: team-agent stop [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: Some("shutdown"), sunset: Some("C2"), action: Some("use `team-agent shutdown`"), governance: None },
187
+ CommandSpec { name: "restart-agent", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::RestartAgent), summary: "legacy reset-agent alias", usage: "usage: team-agent restart-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--discard-session] [--no-display] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::Conditional, alias_of: Some("reset-agent"), sunset: Some("C2"), action: Some("use `team-agent reset-agent`"), governance: None },
188
+ CommandSpec { name: "fallback-send-leader", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::FallbackSendLeader), summary: "legacy leader send fallback", usage: "usage: team-agent fallback-send-leader --workspace WORKSPACE --team TEAM --sender AGENT --message-id ID --content TEXT --primary-error ERROR [--task TASK] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: Some("C2"), action: Some("use `team-agent send` and `team-agent diagnose`"), governance: None },
189
+ CommandSpec { name: "fallback-report-result", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::FallbackReportResult), summary: "legacy report_result fallback", usage: "usage: team-agent fallback-report-result --workspace WORKSPACE --team TEAM --agent-id AGENT --task-id TASK --result-json JSON --primary-error ERROR [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: Some("C2"), action: Some("use `team-agent collect` or MCP `report_result`"), governance: None },
190
+ CommandSpec { name: "settle", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::Settle), summary: "legacy settle helper", usage: "usage: team-agent settle [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: Some("C2"), action: Some("use `team-agent collect`"), governance: None },
191
+ CommandSpec { name: "validate-result", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::ValidateResult), summary: "legacy result validation helper", usage: "usage: team-agent validate-result [ENVELOPE] [--file FILE|--result JSON] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: Some("C2"), action: Some("use report_result validation through normal `team-agent collect` flow"), governance: None },
192
+ CommandSpec { name: "stuck-list", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::StuckList), summary: "legacy stuck alert listing", usage: "usage: team-agent stuck-list [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: Some("C2"), action: Some("use `team-agent diagnose`"), governance: None },
193
+ CommandSpec { name: "stuck-cancel", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::StuckCancel), summary: "legacy stuck alert suppression", usage: "usage: team-agent stuck-cancel AGENT [--workspace WORKSPACE] [--alert-type stuck|idle_fallback|cross_worker_deadlock|all] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: Some("C2"), action: Some("use the concrete notification action or `team-agent diagnose`"), governance: None },
194
+ CommandSpec { name: "acknowledge-idle", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::AcknowledgeIdle), summary: "legacy idle acknowledgement", usage: "usage: team-agent acknowledge-idle [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: Some("C2"), action: Some("use the concrete idle notification action or `team-agent diagnose`"), governance: None },
195
+
196
+ CommandSpec { name: "repair-state", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::RepairState), summary: "guided state repair helper", usage: "usage: team-agent repair-state --task TASK --status STATUS [SUMMARY] [--assignee AGENT] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
197
+ CommandSpec { name: "leaders", tier: CommandTier::Secondary, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Leaders), summary: "inspect registered host leaders", usage: "usage: team-agent leaders [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
198
+ CommandSpec { name: "doctor", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Doctor), summary: "run host maintenance checks", usage: "usage: team-agent doctor [SPEC] [--workspace WORKSPACE] [--team TEAM] [--gate orphans|comms] [--comms] [--fix] [--fix-schema] [--cleanup-orphans] [--confirm] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
199
+ CommandSpec { name: "attach-app-server-leader", tier: CommandTier::Guided, category: CommandCategory::GuidedRecovery, kind: CommandKind::Dispatch(DispatchKind::AttachAppServerLeader), summary: "bind an app-server leader receiver", usage: "usage: team-agent attach-app-server-leader [--workspace WORKSPACE] [--team TEAM] --socket unix:///path.sock --thread-id THREAD_ID [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
200
+ CommandSpec { name: "remove-agent", tier: CommandTier::Secondary, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::RemoveAgent), summary: "remove a worker", usage: "usage: team-agent remove-agent AGENT [--workspace WORKSPACE] [--team TEAM] [--from-spec] [--confirm] [--force] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
201
+ CommandSpec { name: "fork-agent", tier: CommandTier::Secondary, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::ForkAgent), summary: "fork a worker", usage: "usage: team-agent fork-agent SOURCE_AGENT --as AGENT [--label LABEL] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
202
+ CommandSpec { name: "allow-peer-talk", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::AllowPeerTalk), summary: "allow direct worker peer talk", usage: "usage: team-agent allow-peer-talk A B [--workspace WORKSPACE] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "capability/config surface" }) },
203
+ CommandSpec { name: "approvals", tier: CommandTier::Secondary, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Approvals), summary: "inspect provider approval state", usage: "usage: team-agent approvals [AGENT] [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "approval diagnostics" }) },
204
+ CommandSpec { name: "profile", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::Profile), summary: "manage provider profiles", usage: "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "provider/profile setup" }) },
205
+ CommandSpec { name: "install-skill", tier: CommandTier::Secondary, category: CommandCategory::Setup, kind: CommandKind::Dispatch(DispatchKind::InstallSkill), summary: "install or uninstall Team Agent skills", usage: "usage: team-agent install-skill (--source DIR | --uninstall) [--target codex|claude|copilot|all] [--dest DIR] [--dry-run] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: Some(GovernanceNote { decision: "secondary", reason: "capability setup" }) },
206
+ CommandSpec { name: "inbox", tier: CommandTier::Secondary, category: CommandCategory::Observe, kind: CommandKind::Dispatch(DispatchKind::Inbox), summary: "inspect queued messages", usage: "usage: team-agent inbox AGENT [--workspace WORKSPACE] [--team TEAM] [--limit N] [--since CURSOR] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
207
+
208
+ CommandSpec { name: "identity", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Identity), summary: "inspect identity metadata", usage: "usage: team-agent identity [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
209
+ CommandSpec { name: "watch", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Watch), summary: "watch event logs", usage: "usage: team-agent watch [--workspace WORKSPACE] [--team TEAM]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
210
+ CommandSpec { name: "sessions", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Sessions), summary: "inspect session inventory", usage: "usage: team-agent sessions [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
211
+ CommandSpec { name: "compile", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Compile), summary: "compile a team spec", usage: "usage: team-agent compile --team TEAM [--out FILE] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
212
+ CommandSpec { name: "validate", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Validate), summary: "validate a team spec", usage: "usage: team-agent validate [SPEC] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
213
+ CommandSpec { name: "preflight", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Preflight), summary: "run preflight checks", usage: "usage: team-agent preflight [TEAMDIR] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
214
+ CommandSpec { name: "wait-ready", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::WaitReady), summary: "wait for runtime readiness", usage: "usage: team-agent wait-ready [--workspace WORKSPACE] [--team TEAM] [--timeout SECONDS] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
215
+ CommandSpec { name: "e2e", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::E2e), summary: "run e2e harness", usage: "usage: team-agent e2e [--workspace WORKSPACE] [--providers LIST] [--real] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
216
+ CommandSpec { name: "peek", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Peek), summary: "capture low-level terminal output", usage: "usage: team-agent peek AGENT [--workspace WORKSPACE] [--tail N|--head N] [--search TEXT] [--allow-raw-screen] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
217
+ CommandSpec { name: "coordinator", tier: CommandTier::DevInternal, category: CommandCategory::Dev, kind: CommandKind::Dispatch(DispatchKind::Coordinator), summary: "run the coordinator daemon", usage: "usage: team-agent coordinator [--workspace WORKSPACE] [--once] [--tick-interval SECONDS]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
218
+ ];
219
+
220
+ pub(crate) fn command_spec(name: &str) -> Option<&'static CommandSpec> {
221
+ COMMAND_SPECS.iter().find(|spec| spec.name == name)
222
+ }
@@ -32,6 +32,17 @@ pub fn agent_summary_counts(agents: &Value, health: &Value) -> SummaryCounts {
32
32
  return counts;
33
33
  };
34
34
  for (agent_id, agent) in agent_map {
35
+ if let Some(bucket) = stale_agent_bucket(agent) {
36
+ match bucket {
37
+ SummaryBucket::Stopped => counts.stopped += 1,
38
+ SummaryBucket::Unknown => counts.unknown += 1,
39
+ SummaryBucket::Running
40
+ | SummaryBucket::Busy
41
+ | SummaryBucket::Idle
42
+ | SummaryBucket::Failed => {}
43
+ }
44
+ continue;
45
+ }
35
46
  let raw = agent
36
47
  .get("status")
37
48
  .and_then(Value::as_str)
@@ -53,6 +64,21 @@ pub fn agent_summary_counts(agents: &Value, health: &Value) -> SummaryCounts {
53
64
  counts
54
65
  }
55
66
 
67
+ fn stale_agent_bucket(agent: &Value) -> Option<SummaryBucket> {
68
+ if agent.get("stale").and_then(Value::as_bool) != Some(true) {
69
+ return None;
70
+ }
71
+ if agent
72
+ .get("stale_reason")
73
+ .and_then(Value::as_str)
74
+ .is_some_and(|reason| !reason.is_empty())
75
+ {
76
+ Some(SummaryBucket::Stopped)
77
+ } else {
78
+ Some(SummaryBucket::Unknown)
79
+ }
80
+ }
81
+
56
82
  /// `_interaction_counts`(`commands.py:292-306`):遍历 agents 的 `interacted` 字段。
57
83
  pub fn interaction_counts(agents: &Value) -> InteractionCounts {
58
84
  let mut counts = InteractionCounts::default();
@@ -165,6 +191,9 @@ fn csv_agent_status(
165
191
  agent: &Value,
166
192
  health: Option<&serde_json::Map<String, Value>>,
167
193
  ) -> &'static str {
194
+ if stale_agent_bucket(agent).is_some() {
195
+ return "错误";
196
+ }
168
197
  let raw = agent
169
198
  .get("status")
170
199
  .and_then(Value::as_str)
@@ -34,12 +34,13 @@ use rusqlite::params;
34
34
  // backlog>0 才挂 down-hint(anti-nag)。auto-recovery 不做(user 已裁)。
35
35
  let coordinator_running = coordinator_status_running(&health);
36
36
  let undelivered_backlog = count_undelivered_backlog(&conn, owner_team_id)?;
37
+ let session_name = state.get("session_name").cloned().unwrap_or(Value::Null);
38
+ let tmux_present = tmux_session_present(workspace, state, session_name.as_str());
37
39
  let runtime_block = build_runtime_status_block(
38
40
  coordinator_running,
39
41
  undelivered_backlog,
42
+ !tmux_present,
40
43
  );
41
- let session_name = state.get("session_name").cloned().unwrap_or(Value::Null);
42
- let tmux_present = tmux_session_present(workspace, state, session_name.as_str());
43
44
  let agents = enrich_agents(state.get("agents"), tmux_present);
44
45
  let tasks = state
45
46
  .get("tasks")
@@ -458,6 +459,9 @@ use rusqlite::params;
458
459
  "stale_reason".to_string(),
459
460
  Value::String(reason.to_string()),
460
461
  );
462
+ if !tmux_session_present {
463
+ downgrade_stale_agent(&mut enriched);
464
+ }
461
465
  }
462
466
  out.insert(agent_id.clone(), Value::Object(enriched));
463
467
  }
@@ -469,6 +473,25 @@ use rusqlite::params;
469
473
  Value::Object(out)
470
474
  }
471
475
 
476
+ fn downgrade_stale_agent(agent: &mut Map<String, Value>) {
477
+ let raw = agent
478
+ .get("status")
479
+ .and_then(Value::as_str)
480
+ .unwrap_or("")
481
+ .to_ascii_lowercase();
482
+ if matches!(raw.as_str(), "running" | "busy" | "working" | "idle") {
483
+ agent.insert("status".to_string(), Value::String("stopped".to_string()));
484
+ }
485
+ let worker_state = agent
486
+ .get("worker_state")
487
+ .and_then(Value::as_str)
488
+ .unwrap_or("")
489
+ .to_ascii_uppercase();
490
+ if matches!(worker_state.as_str(), "RUNNING" | "BUSY" | "PROBABLY_IDLE") {
491
+ agent.insert("worker_state".to_string(), Value::String("DEAD".to_string()));
492
+ }
493
+ }
494
+
472
495
  fn stale_reason_for_agent(agent: &Value, tmux_session_present: bool) -> Option<&'static str> {
473
496
  let pane_dead = !tmux_session_present && agent_has_pane_fact(agent);
474
497
  let process_dead =
@@ -1093,14 +1116,23 @@ use rusqlite::params;
1093
1116
  /// B-5 / 036b N38 — status 出口的 runtime 块:把 coordinator_health 与
1094
1117
  /// undelivered backlog 合体暴露。down-hint 只在【coordinator 不在跑 ∧ 有 backlog】
1095
1118
  /// 两条件同时满足才挂(anti-nag);健康状态下不挂提示。auto-recovery 不做。
1096
- fn build_runtime_status_block(coordinator_running: bool, undelivered: i64) -> Value {
1119
+ fn build_runtime_status_block(
1120
+ coordinator_running: bool,
1121
+ undelivered: i64,
1122
+ tmux_session_missing: bool,
1123
+ ) -> Value {
1097
1124
  let mut runtime = serde_json::Map::new();
1098
1125
  runtime.insert(
1099
1126
  "coordinator".to_string(),
1100
1127
  json!({"ok": coordinator_running}),
1101
1128
  );
1102
1129
  runtime.insert("undelivered".to_string(), json!(undelivered));
1103
- if !coordinator_running && undelivered > 0 {
1130
+ if tmux_session_missing {
1131
+ runtime.insert(
1132
+ "hint".to_string(),
1133
+ json!("tmux session missing — run team-agent restart"),
1134
+ );
1135
+ } else if !coordinator_running && undelivered > 0 {
1104
1136
  runtime.insert(
1105
1137
  "hint".to_string(),
1106
1138
  json!(format!(