mod ccd1_prompt;
mod commands;
mod content_trust;
mod db;
mod display_safe;
mod handoff;
mod ids;
mod logging;
mod mcp;
mod output;
mod paths;
mod profile;
mod recovery;
mod repo;
mod session_boundary;
mod shipped_skills;
mod state;
mod symbolic_prompt;
mod timestamps;

use std::path::PathBuf;
use std::process::ExitCode;

use anyhow::Result;
use clap::{Args, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
use output::OutputFormat;

#[derive(Parser)]
#[command(
    name = "ccd",
    version,
    about = "Runtime-neutral continuity and governance kernel for AI coding"
)]
struct Cli {
    #[arg(long, global = true, value_enum, default_value_t = OutputFormat::Text)]
    output: OutputFormat,
    #[command(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    Describe,
    Attach(AttachArgs),
    Scaffold(ScaffoldArgs),
    Repo {
        #[command(subcommand)]
        command: RepoCommand,
    },
    Marker {
        #[command(subcommand)]
        command: MarkerCommand,
    },
    Link(LinkArgs),
    Check(CheckArgs),
    Preflight(PreflightArgs),
    Start(StartArgs),
    Status(StatusArgs),
    ContextCheck(ContextCheckArgs),
    PolicyCheck(PolicyCheckArgs),
    Gc(GcArgs),
    Unlink(UnlinkArgs),
    Doctor(DoctorArgs),
    Consistency(ConsistencyArgs),
    Drift(DriftArgs),
    Handoff {
        #[command(subcommand)]
        command: HandoffCommand,
    },
    State {
        #[command(subcommand)]
        command: StateCommand,
    },
    Thread {
        #[command(subcommand)]
        command: ThreadCommand,
    },
    Curl {
        #[command(subcommand)]
        command: CurlCommand,
    },
    EscalationState {
        #[command(subcommand)]
        command: EscalationStateCommand,
    },
    Handover(HandoverArgs),
    Checkpoint(CheckpointArgs),
    Recovery {
        #[command(subcommand)]
        command: RecoveryCommand,
    },
    Session {
        #[command(subcommand)]
        command: SessionCommand,
    },
    SessionState {
        #[command(subcommand)]
        command: SessionStateCommand,
    },
    Skills {
        #[command(subcommand)]
        command: SkillsCommand,
    },
    Machine {
        #[command(subcommand)]
        command: MachineCommand,
    },
    #[command(hide = true)]
    McpServe,
}

impl Command {
    fn name(&self) -> &'static str {
        match self {
            Self::Describe => "Describe",
            Self::Attach(_) => "Attach",
            Self::Scaffold(_) => "Scaffold",
            Self::Repo { .. } => "Repo",
            Self::Marker { .. } => "Marker",
            Self::Link(_) => "Link",
            Self::Check(_) => "Check",
            Self::Preflight(_) => "Preflight",
            Self::Start(_) => "Start",
            Self::Status(_) => "Status",
            Self::ContextCheck(_) => "ContextCheck",
            Self::PolicyCheck(_) => "PolicyCheck",
            Self::Gc(_) => "Gc",
            Self::Unlink(_) => "Unlink",
            Self::Doctor(_) => "Doctor",
            Self::Consistency(_) => "Consistency",
            Self::Drift(_) => "Drift",
            Self::Handoff { .. } => "Handoff",
            Self::State { .. } => "RuntimeState",
            Self::Thread { .. } => "Thread",
            Self::Curl { .. } => "Curl",
            Self::EscalationState { .. } => "EscalationState",
            Self::Handover(_) => "Handover",
            Self::Checkpoint(_) => "Checkpoint",
            Self::Recovery { .. } => "Recovery",
            Self::Session { .. } => "Session",
            Self::SessionState { .. } => "SessionState",
            Self::Skills { .. } => "Skills",
            Self::Machine { .. } => "Machine",
            Self::McpServe => "McpServe",
        }
    }
}

#[derive(Subcommand)]
enum RepoCommand {
    Status(RepoStatusArgs),
    List(RepoListArgs),
    Relink(RepoRelinkArgs),
    Merge(RepoMergeArgs),
    Split(RepoSplitArgs),
}

#[derive(Subcommand)]
enum HandoffCommand {
    Refresh(HandoffRefreshArgs),
    Export(HandoffExportArgs),
    Write(HandoffWriteArgs),
}

#[derive(Subcommand)]
enum SkillsCommand {
    Install(SkillsInstallArgs),
}

#[derive(Subcommand)]
enum MachineCommand {
    /// List every machine registered under the flat `~/.ccd/machines/` layout.
    List,
}

#[derive(Subcommand)]
enum StateCommand {
    Export(StateExportArgs),
}

#[derive(Subcommand)]
enum ThreadCommand {
    /// List first-class continuity threads in the current project overlay.
    List(ThreadListArgs),
    /// Show a first-class continuity thread and its event log.
    Show(ThreadShowArgs),
    /// Rename a first-class continuity thread by canonical id.
    Name(ThreadNameArgs),
    /// Bind the current channel to a dormant first-class thread.
    Resume(ThreadResumeArgs),
    /// Close a first-class continuity thread and remove active bindings.
    Close(ThreadCloseArgs),
    /// Export an experimental portable thread transfer capsule.
    Export(ThreadExportArgs),
    /// Preview or apply an experimental portable thread transfer capsule import.
    Import(ThreadImportArgs),
    /// Inspect the configured filesystem thread sync location.
    Status(ThreadStatusArgs),
}

#[derive(Subcommand)]
enum CurlCommand {
    /// List bounded work-arc records in the current workspace-local state.
    List(CurlListArgs),
    /// Show one bounded work-arc record and its event log.
    Show(CurlShowArgs),
    /// Search compact curl metadata and summaries.
    Search(CurlSearchArgs),
    /// Export bounded curl records.
    Export(CurlExportArgs),
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum PolicyActionFamilyArg {
    #[value(name = "read_only")]
    ReadOnly,
    #[value(name = "local_execution")]
    LocalExecution,
    #[value(name = "local_isolation")]
    LocalIsolation,
    #[value(name = "local_finalize")]
    LocalFinalize,
    #[value(name = "publish_external")]
    PublishExternal,
    #[value(name = "shared_queue_mutation")]
    SharedQueueMutation,
    #[value(name = "repair_destructive")]
    RepairDestructive,
    #[value(name = "protected_surface_mutation")]
    ProtectedSurfaceMutation,
}

impl PolicyActionFamilyArg {
    fn into_policy_action_family(self) -> state::policy_projection::PolicyActionFamily {
        match self {
            Self::ReadOnly => state::policy_projection::PolicyActionFamily::ReadOnly,
            Self::LocalExecution => state::policy_projection::PolicyActionFamily::LocalExecution,
            Self::LocalIsolation => state::policy_projection::PolicyActionFamily::LocalIsolation,
            Self::LocalFinalize => state::policy_projection::PolicyActionFamily::LocalFinalize,
            Self::PublishExternal => state::policy_projection::PolicyActionFamily::PublishExternal,
            Self::SharedQueueMutation => {
                state::policy_projection::PolicyActionFamily::SharedQueueMutation
            }
            Self::RepairDestructive => {
                state::policy_projection::PolicyActionFamily::RepairDestructive
            }
            Self::ProtectedSurfaceMutation => {
                state::policy_projection::PolicyActionFamily::ProtectedSurfaceMutation
            }
        }
    }
}

#[derive(Subcommand)]
enum RecoveryCommand {
    Write(RecoveryWriteArgs),
}

#[derive(Subcommand)]
enum EscalationStateCommand {
    Set(EscalationStateSetArgs),
    Clear(EscalationStateClearArgs),
    List(EscalationStateListArgs),
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum ProjectionTargetArg {
    Default,
    Planning,
    Session,
}

impl ProjectionTargetArg {
    fn into_projection_target(self) -> state::compiled::ProjectionTarget {
        match self {
            Self::Default => state::compiled::ProjectionTarget::Default,
            Self::Planning => state::compiled::ProjectionTarget::Planning,
            Self::Session => state::compiled::ProjectionTarget::Session,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum ProjectionFormatArg {
    Narrative,
    Symbolic,
    Bundle,
}

impl ProjectionFormatArg {
    fn into_projection_format(self) -> state::compiled::ProjectionFormat {
        match self {
            Self::Narrative => state::compiled::ProjectionFormat::Narrative,
            Self::Symbolic => state::compiled::ProjectionFormat::Symbolic,
            Self::Bundle => state::compiled::ProjectionFormat::Bundle,
        }
    }
}

#[derive(Subcommand)]
enum SessionStateCommand {
    Start(SessionStateStartArgs),
    Heartbeat(SessionStateHeartbeatArgs),
    Clear(SessionStateClearArgs),
    Takeover(SessionStateTakeoverArgs),
    Gates {
        #[command(subcommand)]
        command: SessionGateCommand,
    },
}

#[derive(Subcommand)]
enum SessionGateCommand {
    List(SessionStateArgs),
    Replace(SessionGateReplaceArgs),
    Seed(SessionGateSeedArgs),
    SetStatus(SessionGateSetStatusArgs),
    Advance(SessionGateMutateArgs),
    Clear(SessionGateMutateArgs),
}

#[derive(Subcommand)]
enum SessionCommand {
    Open(SessionOpenArgs),
    Renew(SessionRenewArgs),
}

#[derive(Subcommand)]
enum SessionRenewCommand {
    Prepare(SessionRenewPrepareArgs),
}

#[derive(Args)]
struct SessionStateArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args, Clone, Default)]
struct ProtectedWriteArgs {
    #[arg(long = "actor-id")]
    actor_id: Option<String>,
    #[arg(long = "session-id")]
    session_id: Option<String>,
    #[arg(long = "expected-revision")]
    expected_revision: Option<u64>,
}

impl ProtectedWriteArgs {
    fn into_state_options(self) -> state::protected_write::ExclusiveWriteOptions {
        state::protected_write::ExclusiveWriteOptions {
            actor_id: self.actor_id,
            session_id: self.session_id,
            expected_revision: self.expected_revision,
        }
    }
}

#[derive(Args)]
struct SessionStateStartArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[arg(long, value_enum)]
    mode: Option<SessionModeArg>,
    #[arg(long, value_enum, default_value_t = SessionLifecycleArg::Interactive)]
    lifecycle: SessionLifecycleArg,
    #[arg(long = "owner-kind", value_enum)]
    owner_kind: Option<AutonomousOwnerKindArg>,
    #[arg(long = "actor-id")]
    actor_id: Option<String>,
    #[arg(long = "supervisor-id")]
    supervisor_id: Option<String>,
    #[arg(long = "lease-seconds")]
    lease_seconds: Option<u64>,
    /// Explicitly recover this actor's own stale autonomous lease, preserving prior owner/supervisor/TTL unless overridden.
    #[arg(long = "recover-stale")]
    recover_stale: bool,
}

#[derive(Args)]
struct SessionStateHeartbeatArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[arg(long = "actor-id")]
    actor_id: String,
    #[arg(long)]
    activity: Option<String>,
}

#[derive(Args)]
struct SessionStateClearArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[arg(long = "actor-id")]
    actor_id: Option<String>,
    #[arg(long)]
    reason: Option<String>,
}

#[derive(Args)]
struct SessionStateTakeoverArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[arg(long = "actor-id")]
    actor_id: String,
    #[arg(long = "supervisor-id")]
    supervisor_id: Option<String>,
    #[arg(long)]
    reason: String,
}

#[derive(Args)]
struct SessionGateReplaceArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
    #[arg(long = "gate", required = true)]
    gates: Vec<String>,
}

#[derive(Args)]
struct SessionGateSeedArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
    #[arg(long, value_enum)]
    from: GateSeedSourceArg,
}

#[derive(Args)]
struct SessionGateSetStatusArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
    #[arg(long)]
    index: usize,
    #[arg(long, value_enum)]
    status: GateStatusArg,
}

#[derive(Args)]
struct SessionGateMutateArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum GateSeedSourceArg {
    ImmediateActions,
    DefinitionOfDone,
}

impl GateSeedSourceArg {
    fn into_state_source(self) -> state::session_gates::GateSeedSource {
        match self {
            Self::ImmediateActions => state::session_gates::GateSeedSource::ImmediateActions,
            Self::DefinitionOfDone => state::session_gates::GateSeedSource::DefinitionOfDone,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum GateStatusArg {
    Open,
    Done,
    Blocked,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum SessionModeArg {
    General,
    Research,
    Implement,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum SessionLifecycleArg {
    Interactive,
    Autonomous,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum AutonomousOwnerKindArg {
    RuntimeWorker,
    RuntimeSupervisor,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum LifeloopResetPathArg {
    Native,
    Wrapper,
    Manual,
}

impl SessionModeArg {
    fn into_state_mode(self) -> state::session::SessionMode {
        match self {
            Self::General => state::session::SessionMode::General,
            Self::Research => state::session::SessionMode::Research,
            Self::Implement => state::session::SessionMode::Implement,
        }
    }
}

impl SessionLifecycleArg {
    fn into_state_lifecycle(self) -> state::session::SessionLifecycle {
        match self {
            Self::Interactive => state::session::SessionLifecycle::Interactive,
            Self::Autonomous => state::session::SessionLifecycle::Autonomous,
        }
    }
}

impl AutonomousOwnerKindArg {
    fn into_state_owner_kind(self) -> state::session::SessionOwnerKind {
        match self {
            Self::RuntimeWorker => state::session::SessionOwnerKind::RuntimeWorker,
            Self::RuntimeSupervisor => state::session::SessionOwnerKind::RuntimeSupervisor,
        }
    }
}

impl LifeloopResetPathArg {
    fn into_state_reset_path(self) -> state::renewal::LifeloopResetPath {
        match self {
            Self::Native => state::renewal::LifeloopResetPath::Native,
            Self::Wrapper => state::renewal::LifeloopResetPath::Wrapper,
            Self::Manual => state::renewal::LifeloopResetPath::Manual,
        }
    }
}

impl GateStatusArg {
    fn into_status(self) -> state::session_gates::ExecutionGateStatus {
        match self {
            Self::Open => state::session_gates::ExecutionGateStatus::Open,
            Self::Done => state::session_gates::ExecutionGateStatus::Done,
            Self::Blocked => state::session_gates::ExecutionGateStatus::Blocked,
        }
    }
}

#[derive(Args)]
struct SessionOpenArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long)]
    worktree: Option<PathBuf>,
    #[arg(long)]
    branch: Option<String>,
    #[arg(long = "from")]
    from_ref: Option<String>,
}

#[derive(Args)]
struct SessionRenewArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[arg(long = "adapter")]
    lifeloop_adapter_id: Option<String>,
    #[arg(long = "reset-path", value_enum)]
    reset_path: Option<LifeloopResetPathArg>,
    #[arg(long = "lifeloop-receipt")]
    lifeloop_receipt_path: Option<PathBuf>,
    #[command(subcommand)]
    command: Option<SessionRenewCommand>,
}

#[derive(Args)]
struct SessionRenewPrepareArgs {
    #[command(flatten)]
    base: SessionStateArgs,
    #[arg(long = "adapter")]
    lifeloop_adapter_id: Option<String>,
    #[arg(long = "reset-path", value_enum)]
    reset_path: Option<LifeloopResetPathArg>,
    #[arg(long = "lifeloop-receipt")]
    lifeloop_receipt_path: Option<PathBuf>,
}

#[derive(Args)]
struct SkillsInstallArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long, default_value_t = true)]
    global: bool,
    #[arg(long)]
    local: bool,
    /// Harness skill layout to install into. Defaults to active-harness detection.
    #[arg(long)]
    harness: Option<String>,
}

#[derive(Args)]
struct StateExportArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_enum)]
    projection_target: Option<ProjectionTargetArg>,
    #[arg(long, value_enum)]
    projection_format: Option<ProjectionFormatArg>,
}

#[derive(Args)]
struct ThreadListArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct ThreadShowArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    id_or_name: String,
}

#[derive(Args)]
struct ThreadNameArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    thread_id: String,
    name: String,
}

#[derive(Args)]
struct ThreadResumeArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    id_or_name: String,
}

#[derive(Args)]
struct ThreadCloseArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    id_or_name: String,
}

#[derive(Args)]
struct ThreadExportArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    /// Publish the exported capsule into a filesystem directory shared by an external sync tool.
    /// Omit DIR to use CCD_THREAD_SYNC_DIR.
    #[arg(long = "to-sync-dir", value_name = "DIR", num_args = 0..=1)]
    to_sync_dir: Option<Option<PathBuf>>,
}

#[derive(Args)]
struct ThreadImportArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_name = "FILE")]
    from: Option<PathBuf>,
    /// Import the newest synced capsule from a filesystem directory shared by an external sync tool.
    /// Omit DIR to use CCD_THREAD_SYNC_DIR.
    #[arg(long = "from-sync-dir", value_name = "DIR", num_args = 0..=1)]
    from_sync_dir: Option<Option<PathBuf>>,
}

#[derive(Args)]
struct ThreadStatusArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    /// Filesystem directory shared by an external sync tool; falls back to CCD_THREAD_SYNC_DIR.
    #[arg(long = "sync-dir", value_name = "DIR")]
    sync_dir: Option<PathBuf>,
}

#[derive(Args)]
struct CurlListArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct CurlShowArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    curl_id: String,
}

#[derive(Args)]
struct CurlSearchArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long)]
    query: String,
}

#[derive(Args)]
struct CurlExportArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_enum, default_value_t = CurlExportFormatArg::Jsonl)]
    format: CurlExportFormatArg,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum CurlExportFormatArg {
    Jsonl,
}

#[derive(Args, Clone)]
struct EscalationStateArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct EscalationStateSetArgs {
    #[command(flatten)]
    base: EscalationStateArgs,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
    #[arg(long, value_enum, default_value_t = EscalationKindArg::Blocking)]
    kind: EscalationKindArg,
    #[arg(long)]
    id: Option<String>,
    #[arg(long)]
    reason: String,
}

#[derive(Args)]
struct EscalationStateClearArgs {
    #[command(flatten)]
    base: EscalationStateArgs,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
    #[arg(long)]
    id: Option<String>,
}

#[derive(Args)]
struct EscalationStateListArgs {
    #[command(flatten)]
    base: EscalationStateArgs,
}

#[derive(Args)]
struct HandoffRefreshArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long)]
    write: bool,
}

#[derive(Args)]
struct HandoffExportArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct HandoffWriteArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
    /// Skip the write-time path-reference audit. Use this only when the
    /// handoff deliberately names a path that does not exist yet.
    #[arg(long = "allow-missing-refs")]
    allow_missing_refs: bool,
}

#[derive(Args)]
struct RecoveryWriteArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[command(flatten)]
    protected_write: ProtectedWriteArgs,
}

#[derive(Args)]
struct AttachArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long = "project-id")]
    project_id: Option<String>,
    #[arg(long)]
    display_name: Option<String>,
}

#[derive(Args)]
struct ScaffoldArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    force: bool,
}

#[derive(Subcommand)]
enum MarkerCommand {
    Repair(MarkerRepairArgs),
}

#[derive(Args)]
struct MarkerRepairArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    /// Write the repaired marker. Without --yes this command prints a safe preview only.
    #[arg(long)]
    yes: bool,
}

#[derive(Args)]
struct LinkArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long = "project-id")]
    project_id: Option<String>,
    #[arg(long)]
    display_name: Option<String>,
}

#[derive(Args)]
struct RepoStatusArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct RepoListArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct RepoRelinkArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    locality_id: String,
}

#[derive(Args)]
struct RepoMergeArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    target_locality_id: String,
    source_locality_id: String,
    #[arg(long)]
    force: bool,
}

#[derive(Args)]
struct RepoSplitArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    source_locality_id: String,
}

#[derive(Args)]
struct CheckArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
}

#[derive(Args)]
struct PreflightArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct StartArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long)]
    refresh: bool,
    #[arg(long)]
    continuation: Option<String>,
    #[arg(long)]
    check: bool,
    #[arg(long, value_delimiter = ',')]
    fields: Option<Vec<String>>,
    #[arg(long)]
    activate: bool,
    #[arg(long, value_enum)]
    mode: Option<SessionModeArg>,
    /// Disable the default CCD1 prompt-injection frame in JSON start output.
    #[arg(long = "no-ccd1-prompt")]
    no_ccd1_prompt: bool,
}

#[derive(Args)]
struct StatusArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_delimiter = ',')]
    fields: Option<Vec<String>>,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum ContextCheckTriggerArg {
    Interval,
    Manual,
    #[value(name = "pre-compaction")]
    PreCompaction,
    #[value(name = "idle-reset")]
    IdleReset,
    Resume,
    #[value(name = "supervisor-poll")]
    SupervisorPoll,
}

impl ContextCheckTriggerArg {
    fn into_trigger(self) -> state::radar::ContextCheckTrigger {
        match self {
            Self::Interval => state::radar::ContextCheckTrigger::Interval,
            Self::Manual => state::radar::ContextCheckTrigger::Manual,
            Self::PreCompaction => state::radar::ContextCheckTrigger::PreCompaction,
            Self::IdleReset => state::radar::ContextCheckTrigger::IdleReset,
            Self::Resume => state::radar::ContextCheckTrigger::Resume,
            Self::SupervisorPoll => state::radar::ContextCheckTrigger::SupervisorPoll,
        }
    }
}

#[derive(Args)]
struct ContextCheckArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_enum, default_value = "interval")]
    trigger: ContextCheckTriggerArg,
    #[arg(long, value_delimiter = ',')]
    fields: Option<Vec<String>>,
    /// Disable the default CCD1 prompt-injection frame in JSON context-check output.
    #[arg(long = "no-ccd1-prompt")]
    no_ccd1_prompt: bool,
}

#[derive(Args)]
struct PolicyCheckArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_enum)]
    action_family: PolicyActionFamilyArg,
}

#[derive(Args)]
struct GcArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
}

#[derive(Args)]
struct UnlinkArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum DoctorSeverityFilter {
    Pass,
    Warn,
    Fail,
}

#[derive(Args)]
struct DoctorArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long, hide = true)]
    skip_repo_native_checks: bool,
    #[arg(long, value_enum)]
    severity: Option<DoctorSeverityFilter>,
    #[arg(long, value_delimiter = ',')]
    fields: Option<Vec<String>>,
}

#[derive(Args)]
struct ConsistencyArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
}

#[derive(Args)]
struct DriftArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
}

#[derive(Args)]
struct HandoverArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_delimiter = ',')]
    fields: Option<Vec<String>>,
    #[arg(long)]
    commit: bool,
    /// Apply deterministic clean close-out when handover finds no human-choice surfaces.
    #[arg(long = "close-clean")]
    close_clean: bool,
    #[arg(long)]
    since_session: Option<String>,
    /// Disable the default CCD1 prompt-injection frame in JSON handover output.
    #[arg(long = "no-ccd1-prompt")]
    no_ccd1_prompt: bool,
}

#[derive(Args)]
struct CheckpointArgs {
    #[arg(long, default_value = ".")]
    path: PathBuf,
    #[arg(long)]
    profile: Option<String>,
    #[arg(long, value_delimiter = ',')]
    fields: Option<Vec<String>>,
    #[arg(long)]
    commit: bool,
    #[arg(long)]
    since_session: Option<String>,
    /// Disable the default CCD1 prompt-injection frame in JSON checkpoint output.
    #[arg(long = "no-ccd1-prompt")]
    no_ccd1_prompt: bool,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum EscalationKindArg {
    Blocking,
    #[value(alias = "non_blocking")]
    NonBlocking,
}

impl EscalationKindArg {
    fn into_kind(self) -> state::escalation::EscalationKind {
        match self {
            Self::Blocking => state::escalation::EscalationKind::Blocking,
            Self::NonBlocking => state::escalation::EscalationKind::NonBlocking,
        }
    }
}

/// Apply the `--since-session` delta filter to a serialized radar/checkpoint
/// JSON value. Collapses unchanged source-data surfaces to stubs using
/// digests from the session baseline. Shared by both command handlers.
fn apply_since_session_delta(
    mut value: serde_json::Value,
    repo_path: &std::path::Path,
    explicit_profile: Option<&str>,
    since: &str,
    current_digests: &state::compiled::ProjectionDigests,
) -> anyhow::Result<serde_json::Value> {
    let session_layout =
        paths::state::StateLayout::resolve(repo_path, profile::resolve(explicit_profile)?)?;

    let mut delta_status = "filtered";
    let mut delta_warning = None;
    let mut skip_stats = state::compiled::ProjectionDeltaStats::default();
    let target_session_id = if since == "current" {
        match state::session::load_session_id(&session_layout)? {
            Some(session_id) => Some(session_id),
            None => {
                delta_status = "no_active_session";
                delta_warning = Some(
                    "--since-session current: no active session found; emitting full output"
                        .to_owned(),
                );
                None
            }
        }
    } else {
        Some(since.to_owned())
    };

    if let Some(target_session_id) = target_session_id.as_deref() {
        match state::projection_metadata::load_baseline_for_session(
            &session_layout,
            target_session_id,
        )? {
            Some(obs) if obs.projection_digests.is_some() => {
                let baseline = obs.projection_digests.unwrap();
                skip_stats =
                    state::compiled::compute_projection_delta_stats(&baseline, current_digests);
                value = state::radar::apply_delta_filter(value, &baseline, current_digests);
            }
            _ => {
                delta_status = "baseline_missing";
                delta_warning =
                    Some("no baseline found for session; emitting full output".to_owned());
            }
        }
    }

    if let Some(obj) = value.as_object_mut() {
        if let Some(warning) = delta_warning.as_deref() {
            obj.insert("delta_warning".to_owned(), serde_json::json!(warning));
        }
        obj.insert(
            "session_delta".to_owned(),
            serde_json::json!({
                "requested": since,
                "status": delta_status,
                "resolved_session_id": target_session_id,
                "skip_stats": skip_stats,
                "warning": delta_warning,
            }),
        );
    }

    Ok(value)
}

fn main() -> ExitCode {
    logging::init();
    let matches = cli_command().get_matches();
    let output = matches
        .get_one::<OutputFormat>("output")
        .copied()
        .unwrap_or(OutputFormat::Text);

    match run_matches(&matches) {
        Ok(code) => code,
        Err(error) => {
            output::render_error(output, &error);
            ExitCode::from(1)
        }
    }
}

pub(crate) fn cli_command() -> clap::Command {
    Cli::command()
}

fn run_matches(matches: &clap::ArgMatches) -> Result<ExitCode> {
    let _output = matches
        .get_one::<OutputFormat>("output")
        .copied()
        .unwrap_or(OutputFormat::Text);

    let cli = Cli::from_arg_matches(matches)?;
    run(cli)
}

fn run(cli: Cli) -> Result<ExitCode> {
    let command_name = cli.command.name();
    let _span = tracing::info_span!("command", name = command_name).entered();
    tracing::info!(command = command_name, "dispatching command");
    match cli.command {
        Command::Describe => commands::dispatch::maintenance::describe(cli.output),
        Command::Attach(args) => commands::dispatch::bootstrap::attach(args, cli.output),
        Command::Scaffold(args) => commands::dispatch::bootstrap::scaffold(args, cli.output),
        Command::Repo { command } => commands::dispatch::repo::dispatch(command, cli.output),
        Command::Marker { command } => commands::dispatch::maintenance::marker(command, cli.output),
        Command::Link(args) => commands::dispatch::bootstrap::link(args, cli.output),
        Command::Check(args) => commands::dispatch::maintenance::check(args, cli.output),
        Command::Preflight(args) => commands::dispatch::session::preflight(args, cli.output),
        Command::Start(args) => commands::dispatch::session::start(args, cli.output),
        Command::Status(args) => commands::dispatch::session::status(args, cli.output),
        Command::ContextCheck(args) => commands::dispatch::state::context_check(args, cli.output),
        Command::PolicyCheck(args) => commands::dispatch::state::policy_check(args, cli.output),
        Command::Gc(args) => commands::dispatch::maintenance::gc(args, cli.output),
        Command::Unlink(args) => commands::dispatch::bootstrap::unlink(args, cli.output),
        Command::Doctor(args) => commands::dispatch::maintenance::doctor(args, cli.output),
        Command::Consistency(args) => {
            commands::dispatch::maintenance::consistency(args, cli.output)
        }
        Command::Drift(args) => commands::dispatch::maintenance::drift(args, cli.output),
        Command::Handoff { command } => {
            commands::dispatch::continuity::handoff(command, cli.output)
        }
        Command::State { command } => commands::dispatch::state::state_command(command, cli.output),
        Command::Thread { command } => commands::dispatch::state::thread(command, cli.output),
        Command::Curl { command } => commands::dispatch::state::curl(command, cli.output),
        Command::EscalationState { command } => {
            commands::dispatch::state::escalation_state(command, cli.output)
        }
        Command::Handover(args) => commands::dispatch::state::handover(args, cli.output),
        Command::Checkpoint(args) => commands::dispatch::state::checkpoint(args, cli.output),
        Command::Recovery { command } => {
            commands::dispatch::continuity::recovery(command, cli.output)
        }
        Command::Session { command } => commands::dispatch::session::session(command, cli.output),
        Command::SessionState { command } => {
            commands::dispatch::session::session_state(command, cli.output)
        }
        Command::Skills { command } => commands::dispatch::maintenance::skills(command, cli.output),
        Command::Machine { command } => commands::dispatch::machine::dispatch(command, cli.output),
        Command::McpServe => {
            mcp::serve()?;
            Ok(ExitCode::SUCCESS)
        }
    }
}
