use camino::Utf8PathBuf;
use crate::ai::{agent_for_kind, resolve_backend};
use crate::applied::AppliedState;
use crate::config::ProjectEntry;
use crate::error::{Error, Result};
use crate::manifest::{AgentKind, AiMode};
use crate::preset::TemplateRef;
use crate::runner::{PjApplyOptions, apply_to_pj};
use crate::ui;
use super::{parse_cli_vars, resolve_ai_concurrency, resolve_pj_root, resolve_project_name};
#[allow(clippy::too_many_arguments)]
pub async fn run(
template_spec: String,
rev: Option<String>,
at: Option<Utf8PathBuf>,
vars: Vec<String>,
ai_kind: AgentKind,
no_ai: bool,
yes: bool,
ai_prompt: Option<String>,
ai_mode_override: Option<AiMode>,
ai_concurrency_override: Option<usize>,
interactive: bool,
no_color: bool,
) -> Result<()> {
let cwd = resolve_pj_root(at)?;
let pj_root = crate::paths::find_pj_root(&cwd).ok_or_else(|| {
Error::Config(format!(
"no .kata/applied.toml found at or above {cwd}; run `kata init` first"
))
})?;
let applied = AppliedState::load(&pj_root)?;
let base_dir = applied.base_dir.clone().unwrap_or(cwd);
if applied.templates.iter().any(|t| t.source == template_spec) {
return Err(Error::Config(format!(
"template `{template_spec}` is already applied to this project; use `kata update` to bump its rev"
)));
}
let mut templates: Vec<TemplateRef> = applied
.templates
.iter()
.map(|t| TemplateRef {
source: t.source.clone(),
rev: Some(t.rev.clone()),
subdir: t.subdir.clone(),
})
.collect();
templates.push(TemplateRef {
source: template_spec.clone(),
rev,
subdir: None,
});
let project = ProjectEntry {
name: resolve_project_name(&pj_root).await,
path: pj_root.clone(),
tags: vec![],
overrides: None,
};
let agent = if no_ai { None } else { agent_for_kind(ai_kind) };
let agent_backend = if no_ai {
None
} else {
resolve_backend(ai_kind)
};
let ai_concurrency = resolve_ai_concurrency(ai_concurrency_override);
let opts = PjApplyOptions {
dry_run: false,
no_ai,
interactive,
cli_vars: parse_cli_vars(vars)?,
force_once: false,
yes_all: yes,
ai_prompt,
agent_backend,
ai_mode_override,
ai_concurrency,
};
let result = apply_to_pj(
project,
pj_root.clone(),
templates,
base_dir,
toml::Table::new(),
applied.preset.clone(),
opts,
agent,
)
.await?;
ui::print_pj_header(&result.project_name, pj_root.as_str(), no_color);
for (dst, kind) in &result.actions {
ui::print_outcome(dst, *kind, no_color);
}
if !result.errors.is_empty() {
eprintln!("\nerrors:");
for (dst, msg) in &result.errors {
eprintln!(" {dst}: {msg}");
}
return Err(Error::Other(anyhow::anyhow!(
"{} file(s) failed to apply",
result.errors.len()
)));
}
Ok(())
}