use anyhow::Result;
use clap::{Parser, Subcommand};
use st::tree_agent::TreeAgent;
#[derive(Parser)]
#[command(name = "n8x")]
#[command(about = "Nexus Agent - Orchestrate the living forest of AI-human development")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Init {
project: String,
},
Assign {
agent: String,
#[arg(long)]
branch: Option<String>,
#[arg(long)]
pane: Option<String>,
},
Observe {
#[arg(long)]
save_to: Option<String>,
},
Commit {
agent: String,
#[arg(short, long)]
msg: String,
},
SuggestMerge {
#[arg(long)]
auto: bool,
},
MoodCheck,
Push {
#[arg(long)]
target: Option<String>,
#[arg(long)]
project: Option<String>,
},
Snapshot {
output: Option<String>,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Init { project } => {
println!("🌱 Initializing living forest: {}", project);
let _agent = TreeAgent::init(&project)?;
std::fs::write(".tree-agent.json", serde_json::to_string_pretty(&project)?)?;
println!("✓ Forest initialized");
println!(" Use 'n8x assign <agent>' to add AI teammates");
}
Commands::Assign {
agent,
branch,
pane,
} => {
let mut tree_agent = load_agent()?;
let branch = branch.unwrap_or_else(|| {
format!(
"{}-{}",
agent.to_lowercase(),
chrono::Utc::now().format("%Y%m%d-%H%M%S")
)
});
tree_agent.assign_agent(&agent, pane.as_deref(), &branch)?;
}
Commands::Observe { save_to } => {
let mut tree_agent = load_agent()?;
let save_path = save_to.as_ref().map(std::path::Path::new);
tree_agent.observe(save_path)?;
}
Commands::Commit { agent, msg } => {
let mut tree_agent = load_agent()?;
tree_agent.commit_agent(&agent, &msg)?;
}
Commands::SuggestMerge { auto } => {
let tree_agent = load_agent()?;
tree_agent.suggest_merge(auto)?;
}
Commands::MoodCheck => {
let tree_agent = load_agent()?;
tree_agent.mood_check()?;
}
Commands::Push { target, project } => {
let tree_agent = load_agent()?;
if target.is_some() || project.is_some() {
println!("Custom nexus configuration not yet implemented");
}
tree_agent.push_to_nexus()?;
}
Commands::Snapshot { output } => {
let tree_agent = load_agent()?;
let filename = output.unwrap_or_else(|| {
format!("snapshot-{}.m8", chrono::Utc::now().format("%Y%m%d-%H%M%S"))
});
println!("📸 Creating snapshot: {}", filename);
let mut buffer = Vec::new();
tree_agent.mem8.export_memories(&mut buffer)?;
let buffer_len = buffer.len();
std::fs::write(&filename, buffer)?;
println!("✓ Snapshot saved ({} bytes)", buffer_len);
}
}
Ok(())
}
fn load_agent() -> Result<TreeAgent> {
let project = if std::path::Path::new(".tree-agent.json").exists() {
let content = std::fs::read_to_string(".tree-agent.json")?;
serde_json::from_str::<String>(&content)?
} else {
return Err(anyhow::anyhow!(
"No n8x project found. Run 'n8x init <project>' first"
));
};
TreeAgent::init(&project)
}