use crate::core::event::{EventLogger, EventType, ForgeEvent};
use crate::core::plan::PlanManager;
use crate::core::state::{ForgeState, StateManager};
use crate::detect::{detect_tools, display_detected_tools};
use colored::Colorize;
use std::path::Path;
pub fn execute(project_root: &Path, name: Option<String>) -> anyhow::Result<()> {
let forge_dir = project_root.join(".forge");
if forge_dir.exists() {
println!(
"{} Forge is already initialized in this project.",
"!".yellow()
);
println!(" Use {} to see current state.", "forge status".cyan());
return Ok(());
}
println!("{}", "FORGE ORCHESTRATOR — Initialization".bold());
println!("{}", "=".repeat(40));
println!();
println!("{}", "Step 1: Context Discovery".bold());
println!("Scanning project for existing context...\n");
let project_name = name.unwrap_or_else(|| {
project_root
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| "unnamed-project".into())
});
let package_json_exists = project_root.join("package.json").exists();
let cargo_toml_exists = project_root.join("Cargo.toml").exists();
let git_exists = project_root.join(".git").exists();
let pyproject_toml_exists = project_root.join("pyproject.toml").exists();
let requirements_txt_exists = project_root.join("requirements.txt").exists();
let setup_py_exists = project_root.join("setup.py").exists();
let pipfile_exists = project_root.join("Pipfile").exists();
let makefile_exists = project_root.join("Makefile").exists();
let go_mod_exists = project_root.join("go.mod").exists();
let check = |exists: bool| {
if exists {
"✓".green().to_string()
} else {
"✗".dimmed().to_string()
}
};
let mut discovered_md: Vec<String> = Vec::new();
if let Ok(entries) = std::fs::read_dir(project_root) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.ends_with(".md") && entry.path().is_file() {
discovered_md.push(name);
}
}
}
discovered_md.sort();
if discovered_md.is_empty() {
println!(" {} *.md (no markdown files found)", "✗".dimmed());
} else {
for md in &discovered_md {
println!(" {} {}", "✓".green(), md);
}
}
println!(" {} package.json", check(package_json_exists));
println!(" {} Cargo.toml", check(cargo_toml_exists));
if requirements_txt_exists || pyproject_toml_exists || setup_py_exists || pipfile_exists {
println!(" {} requirements.txt", check(requirements_txt_exists));
println!(" {} pyproject.toml", check(pyproject_toml_exists));
println!(" {} setup.py", check(setup_py_exists));
println!(" {} Pipfile", check(pipfile_exists));
}
if makefile_exists {
println!(" {} Makefile", check(makefile_exists));
}
if go_mod_exists {
println!(" {} go.mod", check(go_mod_exists));
}
println!(" {} .git", check(git_exists));
let docs_dir = project_root.join("docs");
if docs_dir.is_dir() {
let doc_count = std::fs::read_dir(&docs_dir)
.map(|entries| {
entries
.flatten()
.filter(|e| e.file_name().to_string_lossy().ends_with(".md"))
.count()
})
.unwrap_or(0);
if doc_count > 0 {
println!(" {} docs/ ({} markdown files)", "✓".green(), doc_count);
}
}
let project_type = if cargo_toml_exists {
"Rust"
} else if package_json_exists {
"JavaScript/TypeScript"
} else if pyproject_toml_exists || requirements_txt_exists || setup_py_exists || pipfile_exists
{
"Python"
} else if go_mod_exists {
"Go"
} else {
"Unknown"
};
println!(
"\n {} Detected project type: {}",
"→".cyan(),
project_type.cyan()
);
println!();
let spec_exists = discovered_md.iter().any(|n| n == "SPEC.md");
println!("{}", "Step 2: Tool Detection".bold());
let spinner = indicatif::ProgressBar::new_spinner();
spinner.set_style(
indicatif::ProgressStyle::default_spinner()
.template(" {spinner:.cyan} {msg}")
.unwrap(),
);
spinner.set_message("Scanning PATH for AI tools (claude, codex, gemini)...");
spinner.enable_steady_tick(std::time::Duration::from_millis(80));
let tools = detect_tools();
spinner.finish_and_clear();
display_detected_tools(&tools);
println!();
println!("{}", "Step 3: Scaffolding .forge/".bold());
scaffold_forge_dir(&forge_dir)?;
let state_mgr = StateManager::new(&forge_dir);
let state = ForgeState {
project_name: project_name.clone(),
tools,
..Default::default()
};
state_mgr.save(&state)?;
println!(" {} state.json", "✓".green());
let plan_mgr = PlanManager::new(&forge_dir);
if spec_exists {
println!(
"\n {} SPEC.md detected — use {} to generate plan from spec.",
"→".cyan(),
"forge plan".cyan()
);
} else {
let template = PlanManager::generate_template(&project_name);
plan_mgr.write_plan(&template)?;
println!(" {} plan.md (template)", "✓".green());
}
let event_logger = EventLogger::new(&forge_dir);
event_logger.log(&ForgeEvent::new(
EventType::PlanCreated,
format!("Forge initialized for project: {project_name}"),
))?;
println!(" {} events.jsonl", "✓".green());
println!("\n{}", "=".repeat(40));
println!(
"{} Forge initialized for {}",
"✓".green().bold(),
project_name.cyan().bold()
);
println!();
println!("Next steps:");
println!(
" {} — choose the AI brain (rule-based is free, openai needs API key)",
"forge config brain openai".cyan()
);
println!(
" {} — generate tasks from your SPEC.md or project context",
"forge plan --generate".cyan()
);
println!(" {} — see the task board", "forge status".cyan());
println!();
Ok(())
}
fn scaffold_forge_dir(forge_dir: &Path) -> anyhow::Result<()> {
let dirs = [
"",
"tasks",
"knowledge",
"knowledge/decisions",
"knowledge/learnings",
"knowledge/research",
"knowledge/patterns",
"results",
];
for dir in &dirs {
let path = forge_dir.join(dir);
std::fs::create_dir_all(&path)?;
}
std::fs::write(
forge_dir.join(".gitignore"),
"# Forge-managed directories\nworktrees/\nresults/\n",
)?;
let governance = serde_json::json!({
"version": "1.0.0",
"created_at": chrono::Utc::now().to_rfc3339(),
"constitution": {
"principles": [],
"constraints": []
},
"workstreams": [],
"audit_trail": []
});
std::fs::write(
forge_dir.join("governance.json"),
serde_json::to_string_pretty(&governance)?,
)?;
println!(" {} .forge/ directory scaffolded", "✓".green());
Ok(())
}