use anyhow::{Result, bail};
use std::collections::HashMap;
use crate::agent;
use crate::cli::task::args::TaskFromTemplateArgs;
use crate::commands::task::{TaskBuildOptions, build_task};
use crate::config;
use crate::template::load_template_with_context;
pub fn handle(resolved: &config::Resolved, args: &TaskFromTemplateArgs, force: bool) -> Result<()> {
let variables = parse_variable_overrides(&args.variables)?;
let target = variables.get("target").cloned();
let loaded = load_template_with_context(
&args.template,
&resolved.repo_root,
target.as_deref(),
args.strict_templates,
)?;
for warning in &loaded.warnings {
log::warn!("Template '{}': {}", args.template, warning);
}
let request = args.title.clone();
let overrides = agent::resolve_agent_overrides(&agent::AgentArgs {
runner: args.runner.clone(),
model: args.model.clone(),
effort: args.effort.clone(),
repo_prompt: args.repo_prompt,
runner_cli: args.runner_cli.clone(),
})?;
let hint_tags = args.tags.clone().unwrap_or_default();
let opts = TaskBuildOptions {
request,
hint_tags,
hint_scope: String::new(),
runner_override: overrides.runner,
model_override: overrides.model,
reasoning_effort_override: overrides.reasoning_effort,
runner_cli_overrides: overrides.runner_cli,
force,
repoprompt_tool_injection: agent::resolve_rp_required(args.repo_prompt, resolved),
template_hint: Some(args.template.clone()),
template_target: target,
strict_templates: args.strict_templates,
estimated_minutes: None,
};
if args.dry_run {
println!("Would create task from template '{}'", args.template);
println!("Request: {}", opts.request);
if !args.tags.as_deref().unwrap_or("").is_empty() {
println!("Additional tags: {}", args.tags.as_deref().unwrap_or(""));
}
if let Some(ref t) = opts.template_target {
println!("Target: {}", t);
}
println!("(Dry run - no task created)");
Ok(())
} else {
build_task(resolved, opts)
}
}
fn parse_variable_overrides(overrides: &[String]) -> Result<HashMap<String, String>> {
let mut vars = HashMap::new();
for override_str in overrides {
let parts: Vec<&str> = override_str.splitn(2, '=').collect();
if parts.len() != 2 {
bail!(
"Invalid --set format: '{}'. Expected VAR=value (e.g., target=src/main.rs)",
override_str
);
}
vars.insert(parts[0].to_string(), parts[1].to_string());
}
Ok(vars)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_variable_overrides_single() {
let overrides = vec!["target=src/main.rs".to_string()];
let vars = parse_variable_overrides(&overrides).unwrap();
assert_eq!(vars.get("target"), Some(&"src/main.rs".to_string()));
}
#[test]
fn test_parse_variable_overrides_multiple() {
let overrides = vec![
"target=src/main.rs".to_string(),
"component=auth".to_string(),
];
let vars = parse_variable_overrides(&overrides).unwrap();
assert_eq!(vars.get("target"), Some(&"src/main.rs".to_string()));
assert_eq!(vars.get("component"), Some(&"auth".to_string()));
}
#[test]
fn test_parse_variable_overrides_with_equals_in_value() {
let overrides = vec!["target=src/path=with=equals".to_string()];
let vars = parse_variable_overrides(&overrides).unwrap();
assert_eq!(
vars.get("target"),
Some(&"src/path=with=equals".to_string())
);
}
#[test]
fn test_parse_variable_overrides_invalid_format() {
let overrides = vec!["invalidformat".to_string()];
let result = parse_variable_overrides(&overrides);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Invalid --set format")
);
}
#[test]
fn test_parse_variable_overrides_empty() {
let overrides: Vec<String> = vec![];
let vars = parse_variable_overrides(&overrides).unwrap();
assert!(vars.is_empty());
}
}