use anyhow::{Context, Result};
use parking_lot::RwLock;
use std::sync::{Arc, Once};
use std::path::PathBuf;
use std::collections::HashMap;
use crate::orchestrator::{DxTool, ExecutionContext};
use crate::core::Forge;
static INIT: Once = Once::new();
static mut FORGE_INSTANCE: Option<Arc<RwLock<Forge>>> = None;
static mut TOOL_REGISTRY: Option<Arc<RwLock<HashMap<String, Arc<RwLock<Box<dyn DxTool>>>>>>> = None;
static mut CURRENT_CONTEXT: Option<Arc<RwLock<ExecutionContext>>> = None;
pub fn initialize_forge() -> Result<()> {
let mut init_result = Ok(());
INIT.call_once(|| {
tracing::info!("๐ Initializing Forge v{}", crate::VERSION);
let project_root = detect_workspace_root().unwrap_or_else(|_| {
std::env::current_dir().expect("Failed to get current directory")
});
tracing::info!("๐ Project root: {:?}", project_root);
match Forge::new(&project_root) {
Ok(forge) => {
unsafe {
FORGE_INSTANCE = Some(Arc::new(RwLock::new(forge)));
TOOL_REGISTRY = Some(Arc::new(RwLock::new(HashMap::new())));
let forge_path = project_root.join(".dx/forge");
let context = ExecutionContext::new(project_root.clone(), forge_path);
CURRENT_CONTEXT = Some(Arc::new(RwLock::new(context)));
}
tracing::info!("โ
Forge initialization complete");
}
Err(e) => {
init_result = Err(e).context("Failed to initialize forge");
}
}
});
init_result
}
pub fn register_tool(tool: Box<dyn DxTool>) -> Result<String> {
ensure_initialized()?;
let tool_name = tool.name().to_string();
let tool_version = tool.version().to_string();
let tool_id = format!("{}@{}", tool_name, tool_version);
tracing::info!("๐ฆ Registering tool: {}", tool_id);
unsafe {
if let Some(registry) = (*std::ptr::addr_of!(TOOL_REGISTRY)).as_ref() {
let tool_arc = Arc::new(RwLock::new(tool));
registry.write().insert(tool_id.clone(), tool_arc);
}
}
Ok(tool_id)
}
pub fn get_tool_context() -> Result<ExecutionContext> {
ensure_initialized()?;
unsafe {
if let Some(context) = (*std::ptr::addr_of!(CURRENT_CONTEXT)).as_ref() {
Ok(context.read().clone())
} else {
anyhow::bail!("Tool context not available")
}
}
}
pub fn shutdown_forge() -> Result<()> {
tracing::info!("๐ Shutting down Forge...");
unsafe {
if let Some(registry) = (*std::ptr::addr_of_mut!(TOOL_REGISTRY)).take() {
let count = registry.read().len();
tracing::info!("๐ฆ Unregistering {} tools", count);
drop(registry);
}
if let Some(forge) = (*std::ptr::addr_of_mut!(FORGE_INSTANCE)).take() {
tracing::info!("๐งน Cleaning up forge instance");
drop(forge);
}
CURRENT_CONTEXT = None;
}
tracing::info!("โ
Forge shutdown complete");
Ok(())
}
fn ensure_initialized() -> Result<()> {
unsafe {
if (*std::ptr::addr_of!(FORGE_INSTANCE)).is_none() {
anyhow::bail!("Forge not initialized. Call initialize_forge() first.");
}
}
Ok(())
}
fn detect_workspace_root() -> Result<PathBuf> {
let mut current = std::env::current_dir()?;
loop {
if current.join(".dx").exists() {
return Ok(current);
}
if current.join(".git").exists() {
return Ok(current);
}
if let Some(parent) = current.parent() {
current = parent.to_path_buf();
} else {
break;
}
}
Ok(std::env::current_dir()?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::orchestrator::ToolOutput;
struct TestTool;
impl DxTool for TestTool {
fn name(&self) -> &str { "test-tool" }
fn version(&self) -> &str { "1.0.0" }
fn priority(&self) -> u32 { 50 }
fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
Ok(ToolOutput::success())
}
}
#[test]
fn test_lifecycle() {
initialize_forge().ok();
let result = register_tool(Box::new(TestTool));
assert!(result.is_ok());
let ctx = get_tool_context();
assert!(ctx.is_ok());
}
}