mod action_watch;
mod code_index;
mod command_jobs;
mod command_output;
mod command_session;
mod command_watch;
mod dev_tools;
mod events;
mod menu;
mod migration;
mod model;
mod nudge;
mod project;
mod prompts;
mod provider;
mod repetition;
mod run_control;
mod runner;
mod setup;
mod stall_diagnostic;
mod symbols;
mod ui;
mod web_tools;
mod workspace;
use anyhow::Result;
use clap::{Parser, Subcommand};
use std::{
path::PathBuf,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};
#[derive(Parser)]
#[command(
version,
about = "Persistent projects and continuous local model refinement"
)]
struct Cli {
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Subcommand)]
enum Command {
Setup,
Settings {
#[arg(long)]
show: bool,
},
Init {
#[arg(long, default_value = "chuggin.json")]
config: PathBuf,
#[arg(long)]
repo: PathBuf,
#[arg(long)]
goal: String,
},
Run {
#[arg(long, default_value = "chuggin.json")]
config: PathBuf,
#[arg(long, default_value_t = 1)]
cycles: u64,
#[arg(long)]
forever: bool,
},
Migrate {
#[arg(long, default_value = "chuggin.json")]
config: PathBuf,
#[arg(long)]
apply: bool,
#[arg(long)]
clear_staging: bool,
},
Status {
#[arg(long)]
config: Option<PathBuf>,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
let stopped = Arc::new(AtomicBool::new(false));
let running = Arc::new(AtomicBool::new(false));
let flag = stopped.clone();
let active = running.clone();
ctrlc::set_handler(move || {
if !active.load(Ordering::SeqCst) || flag.swap(true, Ordering::SeqCst) {
ui::restore();
project::kill_active_check();
eprintln!("Stopped. Run chuggin again to resume from saved progress.");
std::process::exit(130);
}
events::log("Stop requested: finishing this loop, then saving. In the dashboard, press R to resume; Ctrl-C again stops immediately.".into());
})?;
match cli.command {
None => menu::home(stopped, running),
Some(Command::Setup) => {
setup::wizard()?;
Ok(())
}
Some(Command::Settings { show }) => setup::configure(show),
Some(Command::Init { config, repo, goal }) => runner::init(&config, &repo, &goal),
Some(Command::Migrate {
config,
apply,
clear_staging,
}) => {
if apply {
runner::migration_apply(&config, clear_staging)
} else {
events::log(runner::migration_preview(&config)?);
Ok(())
}
}
Some(Command::Status { config }) => {
let path = config
.or(setup::find_project()?)
.unwrap_or(PathBuf::from("chuggin.json"));
runner::status(&path)
}
Some(Command::Run {
config,
cycles,
forever,
}) => {
running.store(true, Ordering::SeqCst);
let config = if config == std::path::Path::new("chuggin.json") && !config.exists() {
setup::find_project()?.unwrap_or(config)
} else {
config
};
runner::run(&config, if forever { None } else { Some(cycles) }, stopped)
}
}
}