use std::process::Command;
use netsky_core::agent::AgentId;
use netsky_core::consts::{AGENT0_NAME, TMUX_BIN};
use netsky_core::runtime::CodexConfig;
use netsky_core::runtime::Runtime;
use netsky_core::spawn::{self, SpawnOptions, SpawnOutcome};
use netsky_db::SessionEvent;
use crate::cli::AgentType;
use crate::cmd::init;
use crate::observability;
pub fn run(n: u32, agent_type: AgentType) -> netsky_core::Result<()> {
let root = require_home_netsky_cwd()?;
if !root.join(".agents/skills").is_dir() {
init::run(Some(root.clone()), false)?;
}
for i in 0..=n {
let agent = AgentId::from_number(i);
let runtime = runtime_for(agent, agent_type);
let opts = SpawnOptions {
runtime,
cwd: root.clone(),
};
announce(agent, spawn::spawn(agent, &opts)?, &opts);
}
{
let agent = AgentId::Agentinfinity;
let opts = SpawnOptions::defaults_for(agent, root.clone());
announce(agent, spawn::spawn(agent, &opts)?, &opts);
}
observability::record_session(netsky_core::consts::AGENT0_NAME, 0, SessionEvent::Up);
if should_attach_agent0() {
let _ = Command::new(TMUX_BIN)
.args(["attach", "-t", AGENT0_NAME])
.status();
}
Ok(())
}
fn require_home_netsky_cwd() -> netsky_core::Result<std::path::PathBuf> {
let home = netsky_core::paths::home();
let expected = home.join("netsky");
let cwd = std::env::current_dir()?;
let cwd_canon = std::fs::canonicalize(&cwd).unwrap_or(cwd);
let expected_canon = std::fs::canonicalize(&expected).unwrap_or(expected.clone());
if cwd_canon != expected_canon {
netsky_core::bail!(
"netsky up: must run from ~/netsky (got {})",
cwd_canon.display(),
);
}
Ok(expected_canon)
}
fn runtime_for(agent: AgentId, agent_type: AgentType) -> Runtime {
if agent.is_agent0() {
return Runtime::defaults_for(agent);
}
match agent_type {
AgentType::Claude => Runtime::defaults_for(agent),
AgentType::Codex => Runtime::Codex(CodexConfig::defaults_for()),
}
}
fn announce(agent: AgentId, outcome: SpawnOutcome, opts: &SpawnOptions) {
match outcome {
SpawnOutcome::AlreadyUp => {
println!("[netsky up] '{}' already up", agent);
}
SpawnOutcome::Spawned => {
println!(
"[netsky up] spawned '{}' ({})",
agent,
opts.runtime.describe()
);
}
}
}
fn should_attach_agent0() -> bool {
use std::io::IsTerminal;
std::io::stdout().is_terminal() && std::env::var("TMUX").is_err()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn runtime_for_claude_yields_claude_runtime() {
let rt = runtime_for(AgentId::Agent0, AgentType::Claude);
assert_eq!(rt.name(), "claude");
}
#[test]
fn runtime_for_codex_keeps_agent0_on_claude() {
let rt = runtime_for(AgentId::Agent0, AgentType::Codex);
assert_eq!(rt.name(), "claude");
}
#[test]
fn runtime_for_codex_yields_codex_runtime_for_clones() {
let rt = runtime_for(AgentId::Clone(4), AgentType::Codex);
assert_eq!(rt.name(), "codex");
}
}