govctl 0.19.0

Project governance CLI for RFC, ADR, and Work Item management
use super::super::BuiltinOp;
use crate::cmd;
use crate::config::Config;
use crate::write::WriteOp;

use super::{CommandResult, render::execute_global_render};

pub(super) fn execute_builtin(config: &Config, builtin: &BuiltinOp, op: WriteOp) -> CommandResult {
    match builtin {
        BuiltinOp::Init { force } => cmd::new::init_project(config, *force, op),
        BuiltinOp::InitSkills { force, format, dir } => {
            cmd::new::sync_skills(config, *force, format, dir.as_deref(), op)
        }
        BuiltinOp::Agent {
            operation,
            selector,
        } => cmd::agent::manage(*operation, *selector, op),
        BuiltinOp::AgentHook { event } => cmd::agent_hooks::handle_hook(*event),
        BuiltinOp::Check => cmd::check::check_all(config),
        BuiltinOp::Status => cmd::status::show_status(config),
        BuiltinOp::RenderGlobal {
            target,
            dry_run,
            force,
        } => execute_global_render(config, *target, *dry_run, *force),
        BuiltinOp::Migrate => cmd::migrate::migrate(config, op),
        BuiltinOp::Verify { guard_ids, work } => {
            cmd::verify::verify(config, guard_ids, work.as_deref())
        }
        BuiltinOp::Search {
            query,
            types,
            tags,
            limit,
            output,
            reindex,
        } => cmd::search::search(config, query, types, tags, *limit, *output, *reindex),
        BuiltinOp::Describe { context } => cmd::describe::describe(config, *context),
        BuiltinOp::SelfUpdate { check } => cmd::self_update::self_update(*check),
        BuiltinOp::Completions { shell } => {
            let mut cmd = completion_command();
            clap_complete::generate(*shell, &mut cmd, "govctl", &mut std::io::stdout());
            Ok(vec![])
        }
        #[cfg(feature = "tui")]
        BuiltinOp::Tui => crate::tui::run(config).map(|()| vec![]),
        BuiltinOp::ReleaseCut { version, date } => {
            cmd::lifecycle::cut_release(config, version, date.as_deref(), op)
        }
        BuiltinOp::ReleaseUndo { expected_version } => {
            cmd::lifecycle::undo_release(config, expected_version, op)
        }
        BuiltinOp::TagNew { tag } => cmd::tag::tag_new(config, tag, op),
        BuiltinOp::TagDelete { tag } => cmd::tag::tag_delete(config, tag, op),
        BuiltinOp::TagList { output } => cmd::tag::tag_list(config, *output),
        BuiltinOp::LoopStart { loop_id, work_ids } => {
            cmd::loop_cmd::start(config, loop_id.as_deref(), work_ids, op)
        }
        BuiltinOp::LoopList {
            filter,
            limit,
            output,
        } => cmd::loop_cmd::list(config, filter.as_deref(), *limit, *output),
        BuiltinOp::LoopShow { loop_id } => cmd::loop_cmd::show(config, loop_id),
        BuiltinOp::LoopResume { loop_id } => cmd::loop_cmd::resume(config, loop_id),
        BuiltinOp::LoopReplan { loop_id } => cmd::loop_cmd::replan(config, loop_id, op),
        BuiltinOp::LoopAdd {
            loop_id,
            field,
            value,
        } => cmd::loop_cmd::add_work_item(config, loop_id, field, value, op),
        BuiltinOp::LoopRemove {
            loop_id,
            field,
            value,
        } => cmd::loop_cmd::remove_work_item(config, loop_id, field, value, op),
        BuiltinOp::LoopRun {
            loop_id,
            target_work_ids,
        } => cmd::loop_cmd::run(config, loop_id, target_work_ids, op),
        BuiltinOp::ConformanceList {
            filter,
            limit,
            output,
            tags,
        } => cmd::conformance::list(config, filter.as_deref(), *limit, *output, tags),
        BuiltinOp::ConformanceGet { id, field, output } => {
            cmd::conformance::get(config, id, field.as_deref(), *output)
        }
        BuiltinOp::ConformanceShow {
            id,
            output,
            history,
        } => cmd::conformance::show(config, id, *output, *history),
        BuiltinOp::ConformanceNew {
            title,
            path,
            selector,
            requirements,
            guards,
            id,
        } => cmd::conformance::new_case(
            config,
            cmd::conformance::NewCaseRequest {
                title,
                path,
                selector,
                requirements,
                guards,
                id: id.as_deref(),
            },
            op,
        ),
        BuiltinOp::ConformanceEdit { id, path, action } => {
            cmd::conformance::edit(config, id, path, action, op)
        }
        BuiltinOp::ConformanceDelete { id, force } => {
            cmd::conformance::delete(config, id, *force, op)
        }
        BuiltinOp::ConformanceTrace { target, output } => {
            cmd::conformance::trace(config, target.as_deref(), *output)
        }
    }
}

fn completion_command() -> clap::Command {
    use crate::Cli;
    use clap::CommandFactory;

    Cli::command().mut_subcommand("agent", |agent| {
        let visible = agent
            .get_subcommands()
            .filter(|subcommand| !subcommand.is_hide_set())
            .cloned()
            .collect::<Vec<_>>();
        // [[RFC-0002:C-AGENT-INTEGRATION]]: the bundled hook adapter is an
        // internal protocol entry point, so completions expose management only.
        clap::Command::new("agent")
            .about("Manage the user-scoped govctl agent integration")
            .subcommands(visible)
    })
}

#[cfg(test)]
mod tests {
    use super::completion_command;

    #[test]
    fn completion_tree_excludes_internal_agent_commands() -> Result<(), Box<dyn std::error::Error>>
    {
        let command = completion_command();
        let agent = command
            .get_subcommands()
            .find(|subcommand| subcommand.get_name() == "agent")
            .ok_or_else(|| std::io::Error::other("missing agent command"))?;
        assert_eq!(
            agent
                .get_subcommands()
                .map(clap::Command::get_name)
                .collect::<Vec<_>>(),
            ["doctor", "install", "update"]
        );
        Ok(())
    }
}