netsky 0.1.4

netsky CLI: the viable system launcher and subcommand dispatcher
Documentation
//! `netsky agentinit <session>` — bootstrap gate for a freshly-spawned
//! agentinfinity. Dismisses the dev-channels TOS dialog on its pane and
//! waits for the readiness marker to appear.
//!
//! Implementation: a short-lived `claude -p` process driven by a brief
//! rendered from `prompts/agentinit.md`. Pinned to a haiku model for
//! cold-start speed (this helper runs for seconds, not minutes).
//! Bounded by [`AGENTINIT_TIMEOUT_S`] so a stuck claude can't hold the
//! watchdog lock indefinitely.

use std::process::Command;
use std::time::Duration;

use netsky_core::consts::{
    AGENTINIT_ALLOWED_TOOLS, AGENTINIT_EFFORT, AGENTINIT_MODEL, AGENTINIT_TIMEOUT_S, CLAUDE,
    CLAUDE_FLAG_ALLOWED_TOOLS, CLAUDE_FLAG_DANGEROUSLY_SKIP_PERMISSIONS, CLAUDE_FLAG_EFFORT,
    CLAUDE_FLAG_MODEL, CLAUDE_FLAG_PERMISSION_MODE, CLAUDE_FLAG_PRINT, PERMISSION_MODE_BYPASS,
};
use netsky_core::process::run_bounded;
use netsky_sh::tmux;

const BRIEF_TEMPLATE: &str = include_str!("../../prompts/agentinit.md");
const SESSION_VAR: &str = "{{ session }}";

pub fn run(session: &str) -> netsky_core::Result<()> {
    // Defense-in-depth: this session name flows into a prompt the haiku
    // then executes via Bash. Production callers pass the hardcoded
    // AGENTINFINITY_NAME const; validating here keeps direct-CLI invocation
    // safe against unsanitized input (e.g. `agentinit 'x; rm -rf ~'`).
    tmux::validate_session_name(session).map_err(|e| netsky_core::anyhow!("{e}"))?;

    let brief = BRIEF_TEMPLATE.replace(SESSION_VAR, session);

    let mut cmd = Command::new(CLAUDE);
    cmd.arg(CLAUDE_FLAG_MODEL)
        .arg(AGENTINIT_MODEL)
        .arg(CLAUDE_FLAG_EFFORT)
        .arg(AGENTINIT_EFFORT)
        .arg(CLAUDE_FLAG_ALLOWED_TOOLS)
        .arg(AGENTINIT_ALLOWED_TOOLS)
        .arg(CLAUDE_FLAG_DANGEROUSLY_SKIP_PERMISSIONS)
        .arg(CLAUDE_FLAG_PERMISSION_MODE)
        .arg(PERMISSION_MODE_BYPASS)
        .arg(CLAUDE_FLAG_PRINT)
        .arg(&brief);

    let out = run_bounded(cmd, Duration::from_secs(AGENTINIT_TIMEOUT_S))?;

    if out.timed_out {
        netsky_core::bail!("agentinit: claude timed out after {AGENTINIT_TIMEOUT_S}s");
    }
    if !out.success() {
        let code = out.status.and_then(|s| s.code()).unwrap_or(-1);
        netsky_core::bail!("agentinit: claude exited with code {code}");
    }
    Ok(())
}