devist 0.1.0

Project bootstrap CLI for AI-assisted development. Spin up new projects from templates, manage backends, and keep your codebase comprehensible.
use anyhow::{anyhow, Result};
use clap::Subcommand;
use console::style;

use crate::registry::Registry;
use crate::state::State;

#[derive(Subcommand)]
pub enum ProjectsCmd {
    /// List all registered projects
    List,
    /// Remove a project from the registry (does not delete files)
    Forget { name: String },
}

pub fn run(cmd: ProjectsCmd) -> Result<()> {
    match cmd {
        ProjectsCmd::List => list(),
        ProjectsCmd::Forget { name } => forget(name),
    }
}

fn list() -> Result<()> {
    let reg = Registry::load()?;
    let state = State::load()?;
    let active = state.active_project.as_deref();

    if reg.projects.is_empty() {
        println!("{}", style("No projects registered.").yellow());
        println!();
        println!(
            "  Create one with: {}",
            style("devist init <name> --template=<t>").cyan()
        );
        return Ok(());
    }

    println!("{}", style("Registered projects").bold());
    println!();

    for p in &reg.projects {
        let active_marker = if Some(p.name.as_str()) == active {
            style("").green().to_string()
        } else {
            "  ".to_string()
        };

        println!(
            "{}{}  {}",
            active_marker,
            style(format!("{:20}", p.name)).cyan(),
            style(format!("[{}]", p.template)).dim()
        );
        println!("    {}", style(p.path.display()).dim());
        println!("    {}", style(format!("created {}", p.created_at)).dim());
    }
    println!();
    if active.is_some() {
        println!("  {} = active backend", style("").green());
    }
    Ok(())
}

fn forget(name: String) -> Result<()> {
    let mut reg = Registry::load()?;
    if !reg.remove(&name) {
        return Err(anyhow!("Project not found in registry: {}", name));
    }
    reg.save()?;

    // Also clear state if this was the active project
    let mut state = State::load()?;
    if state.active_project.as_deref() == Some(&name) {
        state.set_active(None);
        state.save()?;
    }

    println!("  {} {}", style("[FORGOTTEN]").green(), name);
    println!("  {}", style("(project files on disk are untouched)").dim());
    Ok(())
}