147 lines
5.4 KiB
Rust
147 lines
5.4 KiB
Rust
//! `/dashboard` — open the Agent Dashboard view.
|
|
//!
|
|
//! Centralised overview of every running session (top-level + subagents)
|
|
//! with peek, attach, and dispatch from one screen. The dashboard reuses
|
|
//! the existing fullscreen subagent takeover for "attach to subagent",
|
|
//! so attaching never bypasses `active_subagent`.
|
|
//!
|
|
//! Same `Action`-only run path as other session-less commands, no args.
|
|
//! `/sessions` is an alias (see [`SlashCommand::aliases`]): the dashboard
|
|
//! replaced the removed sessions picker modal for switching, renaming, and
|
|
//! closing active sessions. Visibility is feature-flag-gated: the
|
|
//! command is hidden by default in the registry and revealed when the
|
|
//! dashboard feature is enabled (`dashboard_enabled()`), via
|
|
//! [`crate::app::agent_view::AgentView::set_dashboard_visible`]. When
|
|
//! `[dashboard].enabled = false` or `GROK_AGENT_DASHBOARD=0` is set, the
|
|
//! dispatcher prints a friendly toast and refuses to open. The dashboard is
|
|
//! independent of leader mode.
|
|
|
|
use crate::app::actions::Action;
|
|
use crate::slash::command::{AppCtx, CommandExecCtx, CommandResult, SlashCommand};
|
|
|
|
/// Open the Agent Dashboard view.
|
|
pub struct DashboardCommand;
|
|
|
|
impl SlashCommand for DashboardCommand {
|
|
fn name(&self) -> &str {
|
|
"dashboard"
|
|
}
|
|
|
|
/// `/agents-dashboard` is registered as an alias. The canonical
|
|
/// name remains `/dashboard`.
|
|
///
|
|
/// `/sessions` survives the sessions-modal removal as an alias: the
|
|
/// dashboard is the replacement surface for switching, renaming, and
|
|
/// closing active sessions, so old muscle memory redirects here. As an
|
|
/// alias it inherits the feature-flag gate (`set_dashboard_visible`
|
|
/// hides by canonical name) and the minimal-mode gates below.
|
|
fn aliases(&self) -> &[&str] {
|
|
&["agents-dashboard", "sessions"]
|
|
}
|
|
|
|
fn description(&self) -> &str {
|
|
"Open the Agent Dashboard — a fullscreen overview of every running session"
|
|
}
|
|
|
|
fn usage(&self) -> &str {
|
|
"/dashboard"
|
|
}
|
|
|
|
/// The agent dashboard is intentionally out of scope in minimal mode
|
|
/// (single-session standalone — K14/§6.15). Gated off with a message.
|
|
fn available_in_minimal(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
/// Hidden from the completion dropdown in minimal mode: the dashboard
|
|
/// (and its `/sessions` / `/agents-dashboard` spellings) has nothing to
|
|
/// open there, so offering it just to refuse at dispatch is noise. A
|
|
/// fully-typed invocation still resolves and hits the central
|
|
/// `available_in_minimal` dispatch gate (friendly refusal, fail-closed).
|
|
fn visible(&self, ctx: &AppCtx) -> bool {
|
|
!ctx.screen_mode.is_minimal()
|
|
}
|
|
|
|
fn run(&self, _ctx: &mut CommandExecCtx, _args: &str) -> CommandResult {
|
|
CommandResult::Action(Action::OpenDashboard)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::acp::model_state::ModelState;
|
|
use crate::app::bundle::BundleState;
|
|
use crate::slash::command::{AppCtx, CommandExecCtx, CommandResult};
|
|
|
|
#[test]
|
|
fn run_returns_open_dashboard_action() {
|
|
let models = ModelState::default();
|
|
let bundle = BundleState::default();
|
|
let mut ctx = CommandExecCtx {
|
|
models: &models,
|
|
session_id: None,
|
|
bundle_state: &bundle,
|
|
screen_mode: crate::app::ScreenMode::Inline,
|
|
pager_state: crate::settings::PagerLocalSnapshot {
|
|
multiline_mode: false,
|
|
yolo_mode: false,
|
|
..crate::settings::PagerLocalSnapshot::default()
|
|
},
|
|
};
|
|
let cmd = DashboardCommand;
|
|
assert!(matches!(
|
|
cmd.run(&mut ctx, ""),
|
|
CommandResult::Action(Action::OpenDashboard)
|
|
));
|
|
}
|
|
|
|
/// Feature-flag gating is applied externally by the registry
|
|
/// (`set_dashboard_visible`), not via `visible()` — `AppCtx` carries no
|
|
/// dashboard state. `visible()` only gates on screen mode: offered in
|
|
/// fullscreen/inline, hidden from the minimal-mode dropdown (where the
|
|
/// dashboard has nothing to open and dispatch would just refuse).
|
|
#[test]
|
|
fn visible_everywhere_except_minimal() {
|
|
let models = ModelState::default();
|
|
let cmd = DashboardCommand;
|
|
let ctx = |screen_mode| AppCtx {
|
|
models: &models,
|
|
cwd: std::path::Path::new("."),
|
|
has_session_announcements: false,
|
|
screen_mode,
|
|
};
|
|
assert!(cmd.visible(&ctx(crate::app::ScreenMode::Fullscreen)));
|
|
assert!(cmd.visible(&ctx(crate::app::ScreenMode::Inline)));
|
|
assert!(
|
|
!cmd.visible(&ctx(crate::app::ScreenMode::Minimal)),
|
|
"the dashboard (and its /sessions alias) must not be offered in minimal mode"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn does_not_take_args() {
|
|
let cmd = DashboardCommand;
|
|
assert!(!cmd.takes_args());
|
|
}
|
|
|
|
#[test]
|
|
fn name_is_dashboard() {
|
|
let cmd = DashboardCommand;
|
|
assert_eq!(cmd.name(), "dashboard");
|
|
}
|
|
|
|
/// `/sessions` (removed picker modal) and `/agents-dashboard`
|
|
/// both spell this command.
|
|
#[test]
|
|
fn aliases_include_sessions() {
|
|
let cmd = DashboardCommand;
|
|
assert_eq!(cmd.aliases(), &["agents-dashboard", "sessions"]);
|
|
}
|
|
|
|
#[test]
|
|
fn not_available_in_minimal() {
|
|
// The dashboard is out of scope in scrollback-native minimal mode.
|
|
assert!(!DashboardCommand.available_in_minimal());
|
|
}
|
|
}
|