use async_trait::async_trait; use crate::governed::command_trait::*; use crate::governed::session::{GovernedSession, Governed}; pub struct PipelineCommand; #[async_trait] impl ShellCommand for PipelineCommand { fn name(&self) -> &str { "pipeline" } fn aliases(&self) -> Vec<&str> { vec!["pl"] } fn tier(&self) -> CommandTier { CommandTier::Engineer } fn required_scope(&self) -> RequiredScope { // Base command is read-only; subcommands like "trigger" require elevation. RequiredScope::ReadOnly } fn description(&self) -> &str { "Pipeline management (list, show, trigger)" } fn usage(&self) -> &str { "pipeline [args...]" } async fn execute( &self, args: &[String], session: &mut GovernedSession, ) -> Result { let subcommand = args.first().map(|s| s.as_str()).unwrap_or("list"); match subcommand { "list" | "ls" => { let lines = vec![ OutputLine::Table { headers: vec![ "RUN ID".to_string(), "PIPELINE".to_string(), "STATUS".to_string(), "BRANCH".to_string(), ], rows: vec![ vec!["(no runs loaded)".to_string(), "-".to_string(), "-".to_string(), "-".to_string()], ], }, ]; Ok(CommandOutput { lines }) } "show" => { let run_id = args.get(1).ok_or_else(|| { CommandError::InvalidArgs("Usage: pipeline show ".to_string()) })?; Ok(CommandOutput::text(format!( "Pipeline run: {} (stub: not connected to runner-controller)", run_id ))) } "trigger" => { if session.tenant_context().is_none() { return Err(CommandError::NoTenantContext); } // Trigger requires elevation. Err(CommandError::ElevationRequired { registry: "pipeline".to_string(), verb: "trigger".to_string(), }) } _ => Err(CommandError::InvalidArgs(format!( "Unknown subcommand: {}. Use: list, show, trigger", subcommand ))), } } }