carryctx 0.3.0

Persistent project context for coding agents
use crate::*;
use carryctx::application::runtime::InvocationContext;
use carryctx::domain::agent::AgentStatus;
use carryctx::error::ExitCode;
use clap::Parser;

// ── Status ───────────────────────────────────────────────────────────────

#[derive(Parser, Debug)]
pub struct StatusArgs {
    /// Show only items assigned to the current agent.
    #[arg(long)]
    pub mine: bool,

    /// Show all items across the entire project regardless of status or assignment.
    #[arg(long)]
    pub all: bool,

    /// Print output in a compact format without detailed descriptions.
    #[arg(long)]
    pub compact: bool,

    /// Include active and recent agent sessions in the status report.
    #[arg(long)]
    pub sessions: bool,

    /// Include active and pending tasks in the status report.
    #[arg(long)]
    pub tasks: bool,

    /// Include current Git worktrees linked to tasks.
    #[arg(long)]
    pub worktrees: bool,

    /// Only show events/status changes that occurred since a specific timestamp or duration (e.g., '24h', '2023-01-01').
    #[arg(long)]
    pub since: Option<String>,
}

// ═══════════════════════════════════════════════════════════════════════════
//  Handler: status
// ═══════════════════════════════════════════════════════════════════════════

pub fn handle_status(
    _args: &StatusArgs,
    ctx: &InvocationContext,
    is_json: bool,
) -> Result<ExitCode, ExitCode> {
    let mut runtime = try_open_runtime(ctx)?;
    let project_id = &runtime.config.project.id;
    let conn = runtime.database.connection_mut();

    let task_repo = SqliteTaskRepository::new(conn);
    let session_repo = SqliteSessionRepository::new(conn);
    let agent_repo = SqliteAgentRepository::new(conn);
    let worktree_repo = SqliteWorktreeRepository::new(conn);
    let all_sessions = session_repo.list(project_id).map_err(|e| e.exit_code)?;
    let active_sessions = all_sessions
        .into_iter()
        .filter(|s| s.state == carryctx::domain::session::SessionState::Active)
        .collect::<Vec<_>>();
    let active_agents = agent_repo
        .list(&AgentFilter {
            project_id: project_id.to_string(),
            status: Some(AgentStatus::Active),
        })
        .map_err(|e| e.exit_code)?;

    let task_filter = TaskFilter {
        project_id: project_id.to_string(),
        status: None,
        owner_agent_id: None,
        ready: false,
        blocked: false,
        mine: None,
    };
    let all_tasks = task_repo.list(&task_filter).map_err(|e| e.exit_code)?;
    let worktrees = worktree_repo.list(project_id).map_err(|e| e.exit_code)?;

    // Check for Markdown format
    if ctx.format == carryctx::application::runtime::OutputFormat::Markdown {
        let branch = runtime.git_project.branch.as_deref().unwrap_or("unknown");
        let head = runtime.git_project.head.as_deref().unwrap_or("none");
        let md = format!(
            "# CarryCtx Status\n\n\
             - **Project**: {name}\n\
             - **Repository**: {root}\n\
             - **Branch**: {branch}\n\
             - **HEAD**: {head}\n\
             - **Active Sessions**: {sessions}\n\
             - **Active Agents**: {agents}\n\
             - **Total Tasks**: {tasks}\n\
             - **Worktrees**: {worktrees}\n",
            name = runtime.config.project.name,
            root = runtime.git_project.repository_root.display(),
            branch = branch,
            head = head,
            sessions = active_sessions.len(),
            agents = active_agents.len(),
            tasks = all_tasks.len(),
            worktrees = worktrees.len(),
        );
        if !ctx.quiet {
            print!("{md}");
        }
        return Ok(ExitCode::Success);
    }

    let data = serde_json::json!({
        "projectId": project_id,
        "projectName": runtime.config.project.name,
        "repositoryRoot": runtime.git_project.repository_root,
        "activeSessions": active_sessions,
        "activeAgents": active_agents,
        "tasks": all_tasks,
        "worktrees": worktrees,
        "head": runtime.git_project.head,
        "branch": runtime.git_project.branch,
    });

    render_and_print("status", Ok(data), is_json, ctx.quiet)
}