#!/usr/bin/env nu
# domains/provisioning/commands.nu — Provisioning domain CLI for ontoref.
# Dispatched by the ontoref bash wrapper when repo_kind in {DevWorkspace, Mixed}:
#   ore provisioning <command> [args]
#
# Works for both DevWorkspace (workspace-level) and Mixed (platform-level) projects.
# Commands that require project-specific files (card.ncl, reflection/backlog.ncl)
# degrade gracefully when those files are absent.

def project-root [] {
    $env.ONTOREF_PROJECT_ROOT? | default (pwd | path expand)
}

# Resolves an ontoref section path against ADR-032 layout (canonical `.ontoref/<rel>`)
# falling back to legacy root-level layout for projects that have not yet applied
# migration 0023. Pass logical paths like `ontology/state.ncl`, `reflection/backlog.ncl`,
# `card.ncl`. Returns the absolute path of whichever variant exists, or the canonical
# path string if neither exists (so callers can still produce informative errors).
def resolve-onto-path [logical: string] {
    let root = (project-root)
    let canonical = $"($root)/.ontoref/($logical)"
    if ($canonical | path exists) { return $canonical }
    let legacy_rel = if ($logical | str starts-with "ontology/") {
        $logical | str replace "ontology/" ".ontology/"
    } else { $logical }
    let legacy = $"($root)/($legacy_rel)"
    if ($legacy | path exists) { return $legacy }
    $canonical
}

# ─── Loaders ──────────────────────────────────────────────────────────────────

def load-state       [] { ^nickel export (resolve-onto-path "ontology/state.ncl")       | from json }
def load-core        [] { ^nickel export (resolve-onto-path "ontology/core.ncl")         | from json }
def load-gate        [] { ^nickel export (resolve-onto-path "ontology/gate.ncl")         | from json }
def load-connections [] { ^nickel export (resolve-onto-path "ontology/connections.ncl")  | from json }
def load-manifest    [] { ^nickel export (resolve-onto-path "ontology/manifest.ncl")     | from json }

def load-card [] {
    let path = (resolve-onto-path "card.ncl")
    if ($path | path exists) { ^nickel export $path | from json } else { null }
}

def load-backlog [] {
    let path = (resolve-onto-path "reflection/backlog.ncl")
    if ($path | path exists) { ^nickel export $path | from json } else { { items: [] } }
}

# ─── state / next / validate ──────────────────────────────────────────────────

export def "state" [] {
    load-state | get dimensions | each { |dim|
        { dimension: $dim.id, current: $dim.current_state, desired: $dim.desired_state, horizon: $dim.horizon }
    }
}

export def "next" [] {
    load-state | get dimensions | each { |dim|
        let active = $dim.transitions | where from == $dim.current_state
        if ($active | is-empty) {
            { dimension: $dim.id, from: $dim.current_state, to: "–", condition: "no active transition", blocker: "–", catalyst: "–" }
        } else {
            let tr = $active | first
            { dimension: $dim.id, from: $tr.from, to: $tr.to, condition: $tr.condition, blocker: $tr.blocker, catalyst: $tr.catalyst }
        }
    }
}

export def "validate" [decision: string] {
    let core  = load-core
    let words = ($decision | str downcase | split row " " | where { |w| ($w | str length) > 3 })
    let affected = ($core.nodes | each { |n|
        let text = $"($n.id) ($n.name) ($n.description)" | str downcase
        if ($words | any { |w| $text =~ $w }) { $n } else { null }
    } | compact)
    {
        decision:           $decision,
        invariants_at_risk: ($affected | where invariant == true | select id level name),
        tensions_touched:   ($affected | where level == "Tension" | select id name),
        practices_touched:  ($affected | where level == "Practice" | select id name),
        verdict:            "manual review required — if any invariant is contradicted, justification is required",
    }
}

# ─── connections / gates ──────────────────────────────────────────────────────

export def "connections" [] {
    let c = load-connections
    if ($c.upstream? | default [] | is-not-empty) {
        print ""
        print "UPSTREAM"
        $c.upstream | each { |u|
            print $"  ($u.project)  kind=($u.kind)  via=($u.via)"
            if ($u.node? | default "" | is-not-empty) { print $"    node: ($u.node)" }
            print $"    ($u.note)"
        }
    }
    if ($c.downstream? | default [] | is-not-empty) {
        print ""
        print "DOWNSTREAM"
        $c.downstream | each { |d|
            print $"  ($d.project)  kind=($d.kind)  via=($d.via)"
            if ($d.node? | default "" | is-not-empty) { print $"    node: ($d.node)" }
            print $"    ($d.note)"
        }
    }
    if ($c.peers? | default [] | is-not-empty) {
        print ""
        print "PEERS"
        $c.peers | each { |p| print $"  ($p.project)  kind=($p.kind)  via=($p.via)" }
    }
}

export def "gates" [] {
    load-gate | get membranes | each { |m|
        {
            membrane:    $m.id,
            name:        $m.name,
            active:      $m.active,
            permeability: $m.permeability,
            condition:   ($m.opening_condition.description? | default ""),
        }
    }
}

# ─── card (DevWorkspace only) ─────────────────────────────────────────────────

export def "card" [] {
    let c = load-card
    if ($c | is-empty) {
        print "No card.ncl found — this command is only available for DevWorkspace projects."
        return
    }
    print $"id:       ($c.id)"
    print $"name:     ($c.name)"
    print $"tagline:  ($c.tagline)"
    print $"status:   ($c.status)"
    print $"version:  ($c.version)"
    print $"started:  ($c.started_at)"
    if ($c.tags? | default [] | is-not-empty)  { print $"tags:     ($c.tags | str join ', ')" }
    if ($c.tools? | default [] | is-not-empty) { print $"tools:    ($c.tools | str join ', ')" }
    if ($c.features? | default [] | is-not-empty) {
        print ""
        print "features:"
        $c.features | each { |f| print $"  - ($f)" }
    }
}

# ─── capabilities (manifest.capabilities) ────────────────────────────────────

export def "capabilities" [] {
    let caps = load-manifest | get capabilities? | default []
    if ($caps | is-empty) { print "No capabilities declared in manifest."; return }
    $caps | select id name summary
}

# ─── backlog (platform only) ──────────────────────────────────────────────────

export def "backlog" [--priority: string = ""] {
    let items = load-backlog | get items
    if ($items | is-empty) {
        print "No backlog.ncl found — this command is only available for platform projects."
        return
    }
    let filtered = if ($priority | is-empty) { $items } else { $items | where priority == $priority }
    $filtered | each { |i|
        {
            id:         $i.id,
            priority:   $i.priority,
            blocked_by: ($i.blocked_by | str join ", "),
        }
    }
}

export def "backlog show" [id: string] {
    let items = load-backlog | get items
    if ($items | is-empty) { error make { msg: "No backlog.ncl found for this project." } }
    let item = $items | where id == $id
    if ($item | is-empty) { error make { msg: $"Backlog item not found: ($id)" } }
    let r = $item | first
    print $"id:       ($r.id)"
    print $"priority: ($r.priority)"
    print ""
    print "description:"
    print $"  ($r.description)"
    if ($r.blocked_by? | default [] | is-not-empty) {
        print ""
        print $"blocked_by:  ($r.blocked_by | str join ', ')"
    }
    if ($r.related_nodes? | default [] | is-not-empty) {
        print $"nodes:       ($r.related_nodes | str join ', ')"
    }
}

# ─── install (Mixed/platform only) ───────────────────────────────────────────

def detect-platform []: nothing -> string {
    if (do { ^which docker } | complete | get exit_code) == 0 { "docker" }
    else if (do { ^which podman } | complete | get exit_code) == 0 { "podman" }
    else if (do { ^which kubectl } | complete | get exit_code) == 0 { "kubernetes" }
    else { "" }
}

export def "install" [
    --mode: string = "solo"     # Deployment mode: solo|multi-user|cicd|enterprise
    --platform: string = ""     # Container platform: docker|podman|kubernetes (auto-detected)
    --dry-run                   # Print what would be run without executing
]: nothing -> nothing {
    let root = (project-root)

    let installer_bin = [$root, "platform", "target", "release", "provisioning-installer"] | path join
    let installer_src = [$root, "platform", "installer"] | path join
    let just_available = (do { ^which just } | complete | get exit_code) == 0

    let resolved_platform = if ($platform | is-not-empty) { $platform } else { detect-platform }

    print $"Platform install  mode=($mode)  platform=(if ($resolved_platform | is-empty) { '?' } else { $resolved_platform })"
    print ""

    if ($resolved_platform | is-empty) {
        print "No supported container platform found (docker, podman, kubectl)."
        print "Install one and retry."
        return
    }

    if ($installer_bin | path exists) {
        print $"installer binary: ($installer_bin)"
        if $dry_run {
            print $"[dry-run] would run: ($installer_bin) --headless --mode ($mode) --platform ($resolved_platform) --yes"
            return
        }
        do { ^$installer_bin --headless --mode $mode --platform $resolved_platform --yes } | complete | ignore
        return
    }

    if ($installer_src | path exists) {
        print "installer binary not built — building from source..."
        if $dry_run {
            print $"[dry-run] would run: cargo build --release --manifest-path ($installer_src)/Cargo.toml"
            return
        }
        let r = do { ^cargo build --release --manifest-path $"($installer_src)/Cargo.toml" } | complete
        if $r.exit_code != 0 {
            error make { msg: $"installer build failed:\n($r.stderr)" }
        }
        do { ^$installer_bin --headless --mode $mode --platform $resolved_platform --yes } | complete | ignore
        return
    }

    if $just_available {
        print "No installer binary or source found — delegating to: just install"
        if $dry_run {
            print $"[dry-run] would run: just install mode=($mode)"
            return
        }
        let r = do { ^just install $"mode=($mode)" } | complete
        if $r.exit_code != 0 {
            error make { msg: $"just install failed:\n($r.stderr)" }
        }
        return
    }

    print "installer not found and 'just' is not available."
    print $"Build the installer first: cargo build --release --manifest-path ($installer_src)/Cargo.toml"
}

# ─── ops (ADR-037: NATS ops contract) ────────────────────────────────────────

def workspace-id []: nothing -> string {
    let card = load-card
    if ($card | is-empty) { "unknown" } else { $card.id }
}

def nats-available []: nothing -> bool {
    (do { ^which nats } | complete | get exit_code) == 0
}

def keeper-cli-available []: nothing -> bool {
    (do { ^which keeper-cli } | complete | get exit_code) == 0
}

def rad-available []: nothing -> bool {
    (do { ^which rad } | complete | get exit_code) == 0
}

export def "ops list" []: nothing -> nothing {
    let ws = workspace-id
    if not (nats-available) {
        print "nats CLI not found — install nats-io/natscli"
        return
    }
    let stream = $"OPS_PENDING_(($ws | str upcase))"
    let r = do { ^nats stream info $stream --json } | complete
    if $r.exit_code != 0 {
        print $"Stream ($stream) not found or NATS unreachable: ($r.stderr | str trim)"
        return
    }
    let info = $r.stdout | from json
    print $"stream:    ($stream)"
    print $"messages:  ($info.state.messages)"
    print $"first_seq: ($info.state.first_seq)"
    print $"last_seq:  ($info.state.last_seq)"
}

export def "ops describe" [id: string]: nothing -> nothing {
    let ws = workspace-id
    if not (nats-available) { print "nats CLI not found"; return }
    let subject = $"ops.pending.($ws).>"
    let r = do { ^nats req $subject "" --count 50 --json } | complete
    if $r.exit_code != 0 { print $"NATS request failed: ($r.stderr | str trim)"; return }
    let msgs = $r.stdout | lines | each { |l| $l | from json | get data? | default {} }
    let found = $msgs | where { |m| ($m.jti? | default "") == $id }
    if ($found | is-empty) { print $"Op not found: ($id)"; return }
    let op = $found | first
    print $"jti:                ($op.jti)"
    print $"op_type:            ($op.op_type? | default 'unknown')"
    print $"target:             ($op.target? | default 'unknown')"
    print $"expected_version:   ($op.expected_state_version? | default 'none')"
    print $"scopes:             ($op.scopes? | default [] | str join ', ')"
    print $"issued_at:          ($op.iat? | default 'unknown')"
}

export def "ops sign" [id: string]: nothing -> nothing {
    if not (keeper-cli-available) { print "keeper-cli not found — build from platform/crates/ops-keeper"; return }
    let ws = workspace-id
    let r = do { ^keeper-cli --workspace $ws sign $id } | complete
    if $r.exit_code != 0 {
        error make { msg: $"keeper-cli sign failed: ($r.stderr | str trim)" }
    }
    print $r.stdout
}

export def "ops history" [--workspace: string = ""]: nothing -> nothing {
    let ws = if ($workspace | is-empty) { workspace-id } else { $workspace }
    let repo_path = $env.HOME | path join ".local/share/radicle/repos" $"state-($ws)"
    if not ($repo_path | path exists) {
        print $"Radicle state repo not found at ($repo_path)"
        print "Run: rad clone rad:<rid> state-($ws)"
        return
    }
    let r = do { ^git -C $repo_path log --oneline --no-decorate -20 } | complete
    if $r.exit_code != 0 { print $"git log failed: ($r.stderr | str trim)"; return }
    $r.stdout | lines | each { |l|
        let parts = $l | split row " " | enumerate
        let hash = $parts | where index == 0 | get item.0
        let msg  = $parts | skip 1 | get item | str join " "
        { commit: $hash, message: $msg }
    }
}

# ─── playbook (extensions/playbooks/) ────────────────────────────────────────

def playbooks-root []: nothing -> string {
    let root = project-root
    [$root, "..", "..", "provisioning", "extensions", "playbooks"] | path join | path expand
}

export def "playbook list" []: nothing -> nothing {
    let root = playbooks-root
    if not ($root | path exists) { print "No extensions/playbooks/ directory found."; return }
    ^ls $root | where type == dir | get name | each { |d|
        let playbook_path = [$d, "playbook.ncl"] | path join
        let has_run = ([$d, "run.nu"] | path join | path exists)
        { name: ($d | path basename), has_run: $has_run, has_playbook: ($playbook_path | path exists) }
    }
}

export def "playbook describe" [name: string]: nothing -> nothing {
    let root = playbooks-root
    let playbook_path = [$root, $name, "playbook.ncl"] | path join
    if not ($playbook_path | path exists) {
        print $"Playbook not found: ($name)"
        print $"Expected at: ($playbook_path)"
        return
    }
    let r = do { ^nickel export $playbook_path } | complete
    if $r.exit_code != 0 { print $"Failed to load playbook: ($r.stderr | str trim)"; return }
    let p = $r.stdout | from json
    print $"name:         ($p.name)"
    print $"description:  ($p.description)"
    print $"version:      ($p.version)"
    print ""
    print "preconditions:"
    $p.preconditions? | default [] | each { |c| print $"  - ($c)" }
    print ""
    print "steps:"
    $p.steps? | default [] | enumerate | each { |s|
        print $"  ($s.index + 1). ($s.item.id): ($s.item.description)"
    }
    if ($p.rollback_strategy? | default "none") != "none" {
        print $"\nrollback: ($p.rollback_strategy)"
    }
}

export def "playbook run" [name: string, --dry-run]: nothing -> nothing {
    let root = playbooks-root
    let run_path = [$root, $name, "run.nu"] | path join
    if not ($run_path | path exists) {
        print $"run.nu not found for playbook: ($name)"
        return
    }
    let ws = workspace-id
    if $dry_run {
        let dry_path = [$root, $name, "tests", "dry_run.nu"] | path join
        if ($dry_path | path exists) {
            let r = do { ^nu $dry_path --workspace $ws } | complete
            if $r.exit_code != 0 { error make { msg: $"dry-run failed: ($r.stderr | str trim)" } }
            print $r.stdout
        } else {
            print $"[dry-run] would execute: nu ($run_path) --workspace ($ws)"
            print "No dry_run.nu found — simulating step listing only."
            playbook describe $name
        }
        return
    }
    let r = do { ^nu $run_path --workspace $ws } | complete
    if $r.exit_code != 0 {
        error make { msg: $"playbook run failed: ($r.stderr | str trim)" }
    }
    print $r.stdout
}

export def "playbook history" []: nothing -> nothing {
    let ws = workspace-id
    let repo_path = $env.HOME | path join ".local/share/radicle/repos" $"state-($ws)"
    if not ($repo_path | path exists) {
        print $"Radicle state repo not found at ($repo_path)"
        return
    }
    let r = do { ^git -C $repo_path log --oneline --no-decorate --grep="playbook:" -20 } | complete
    if $r.exit_code != 0 { print $"git log failed: ($r.stderr | str trim)"; return }
    if ($r.stdout | is-empty) { print "No playbook audit entries found."; return }
    $r.stdout | lines | each { |l|
        let parts = $l | split row " " | enumerate
        let hash = $parts | where index == 0 | get item.0
        let msg  = $parts | skip 1 | get item | str join " "
        { commit: $hash, message: $msg }
    }
}

# ─── keeper (ADR-037 dual-mode signing) ──────────────────────────────────────

export def "keeper status" []: nothing -> nothing {
    let ws = workspace-id
    if not (keeper-cli-available) { print "keeper-cli not found"; return }
    let r = do { ^keeper-cli --workspace $ws status } | complete
    if $r.exit_code != 0 {
        print $"keeper-cli unavailable: ($r.stderr | str trim)"
        return
    }
    print $r.stdout
}

export def "keeper policy" []: nothing -> nothing {
    let ws = workspace-id
    let policy_repo = $env.HOME | path join ".local/share/radicle/repos" $"policy-($ws)"
    if not ($policy_repo | path exists) {
        print $"Policy repo not cloned locally: policy-($ws)"
        print "Run: rad clone rad:<rid> policy-($ws)"
        return
    }
    let policy_path = [$policy_repo, "policy.ncl"] | path join
    if not ($policy_path | path exists) { print "policy.ncl not found in policy repo"; return }
    let r = do { ^nickel export $policy_path } | complete
    if $r.exit_code != 0 { print $"Failed to load policy: ($r.stderr | str trim)"; return }
    let p = $r.stdout | from json
    print $"workspace:     ($p.workspace)"
    print $"version:       ($p.version)"
    print ""
    print "auto_sign rules:"
    $p.auto_sign? | default [] | each { |rule|
        print $"  op_type=($rule.op_type) target=($rule.target? | default '*') scope=($rule.scope? | default 'any')"
    }
    print ""
    print "require_manual rules:"
    $p.require_manual? | default [] | each { |rule|
        print $"  op_type=($rule.op_type) target=($rule.target? | default '*')"
    }
}

export def "keeper switch" [mode: string]: nothing -> nothing {
    let valid_modes = ["auto", "operator-only"]
    if not ($valid_modes | any { |m| $m == $mode }) {
        error make { msg: $"Invalid mode '($mode)'. Valid modes: ($valid_modes | str join ', ')" }
    }
    let playbook = if $mode == "auto" { "switch_to_vm_ops" } else { "switch_to_operator_only" }
    print $"Delegating to playbook: ($playbook)"
    playbook run $playbook
}

# ─── governance (ADR-038 Radicle delegation) ─────────────────────────────────

export def "governance delegations" []: nothing -> nothing {
    let ws = workspace-id
    let repos = ["policy", "desired", "state"] | each { |r| $"($r)-($ws)" }
    $repos | each { |repo_name|
        let r = do { ^rad id --repo $repo_name --json } | complete
        if $r.exit_code != 0 {
            { repo: $repo_name, error: ($r.stderr | str trim), delegates: [] }
        } else {
            let info = $r.stdout | from json
            { repo: $repo_name, threshold: ($info.threshold? | default 1), delegates: ($info.delegates? | default []) }
        }
    }
}

export def "governance signers" []: nothing -> nothing {
    let ws = workspace-id
    if not (nats-available) { print "nats CLI not found"; return }
    let r = do { ^nats auth account ls --json } | complete
    if $r.exit_code != 0 {
        print $"NATS auth query failed: ($r.stderr | str trim)"
        return
    }
    let accounts = $r.stdout | from json
    let ops_accounts = $accounts | where { |a| ($a.name? | default "") | str contains $ws }
    if ($ops_accounts | is-empty) { print $"No NATS accounts found for workspace: ($ws)"; return }
    $ops_accounts | each { |a|
        { account: $a.name, subject: ($a.subject? | default ""), signers: ($a.signing_keys? | default []) }
    }
}

# ─── help + main ──────────────────────────────────────────────────────────────

# ─── registry (i sub-domain) ──────────────────────────────────────────────────

const DEFAULT_REGISTRY = "reg.librecloud.online"

def _find-capabilities-ncl []: nothing -> string {
    let ws_path = ($env | get -o PROVISIONING_WORKSPACE_PATH | default "")
    if ($ws_path | is-not-empty) {
        let candidates = (do { glob $"($ws_path)/infra/*/capabilities.ncl" } | default [])
        if ($candidates | is-not-empty) { return ($candidates | first) }
    }
    mut dir = $env.PWD
    for _ in 0..6 {
        let cur = $dir   # immutable copy for closure capture in glob
        let candidates = (do { glob $"($cur)/infra/*/capabilities.ncl" } | default [])
        if ($candidates | is-not-empty) { return ($candidates | first) }
        let parent = ($dir | path dirname)
        if $parent == $dir { break }
        $dir = $parent
    }
    ""
}

export def "i resolve-registry" []: nothing -> string {
    let from_env = ($env | get -o PROVISIONING_REGISTRY | default "")
    if ($from_env | is-not-empty) { return $from_env }
    let caps = (_find-capabilities-ncl)
    if ($caps | is-not-empty) {
        let ip  = ($env.NICKEL_IMPORT_PATH? | default ($env.PROVISIONING? | default ""))
        let ncl = (do { ^nickel export --import-path $ip $caps } | complete)
        if $ncl.exit_code == 0 {
            let data        = ($ncl.stdout | from json)
            let reg_default = ($data | get -o provides.registries.default | default "")
            let registries  = ($data | get -o provides.registries.registries | default [])
            if ($reg_default | is-not-empty) and ($registries | is-not-empty) {
                let entry = ($registries | where { |e| $e.id == $reg_default } | first | default null)
                if ($entry != null) and ($entry.endpoint? | default "" | is-not-empty) {
                    return $entry.endpoint
                }
            }
        }
    }
    $DEFAULT_REGISTRY
}

export def "i list" [--live]: nothing -> nothing {
    let reg = (i resolve-registry)
    if not $live {
        print $"registry : ($reg)"
        print "Use --live to query the live catalog."
        return
    }
    let url = $"https://($reg)/v2/_catalog"
    let body = try { http get $url } catch {
        print $"Registry catalog unavailable at ($url)"
        return
    }
    let repos = ($body | get -o repositories | default [])

    let domains = ($repos | where { |r| $r | str starts-with "domains/" } | each { |r|
        let parts = ($r | str replace "domains/" "" | split row "/")
        { kind: "domain", participant: ($parts | first), id: ($parts | skip 1 | str join "/"), ref: $"($reg)/($r)" }
    })
    let modes = ($repos | where { |r| $r | str starts-with "modes/" } | each { |r|
        let parts = ($r | str replace "modes/" "" | split row "/")
        { kind: "mode", participant: ($parts | first), id: ($parts | skip 1 | str join "/"), ref: $"($reg)/($r)" }
    })

    let all = ($domains | append $modes)
    if ($all | is-empty) {
        print $"No domains/ or modes/ artifacts found in ($reg)"
        return
    }
    print $"registry: ($reg)  artifacts: ($all | length)"
    print ""
    $all | each { |a|
        print $"  ($a.kind)  ($a.participant)/($a.id)  → ($a.ref)"
    }
    null
}

def show-help [] {
    print "Provisioning — ontoref domain extension"
    print ""
    print "USAGE"
    print "  ore provisioning <command> [args]"
    print ""
    print "COMMANDS  (all repo_kinds)"
    print "  state                         Current FSM position across all dimensions"
    print "  next                          Next valid transitions with blockers/catalysts"
    print "  validate <decision>           Check against ontological invariants"
    print "  connections                   Upstream/downstream project dependency graph"
    print "  gates                         Membrane status and opening conditions"
    print "  capabilities                  Platform capabilities from manifest"
    print ""
    print "COMMANDS  (DevWorkspace only)"
    print "  card                          Workspace card: identity, clusters, status"
    print ""
    print "COMMANDS  (Mixed/platform only)"
    print "  backlog [--priority]          Backlog items filtered by High|Medium|Low"
    print "  backlog show <id>             Full detail of a backlog item"
    print "  install [--mode] [--platform] Install provisioning platform services"
    print ""
    print "COMMANDS  (DevWorkspace — ops contract, ADR-037)"
    print "  ops list                      Pending ops queue depth for this workspace"
    print "  ops describe <id>             Full op detail: JWT claims, scopes, expected_version"
    print "  ops sign <id>                 Operator signs a pending op via keeper-cli"
    print "  ops history [<workspace>]     Applied ops from <workspace>-state Radicle ledger"
    print ""
    print "COMMANDS  (DevWorkspace — playbooks)"
    print "  playbook list                 Available playbooks for this workspace"
    print "  playbook describe <name>      Steps, params, preconditions of a playbook"
    print "  playbook run <name>           Execute a playbook (use --dry-run first)"
    print "  playbook history              Past playbook executions from audit ledger"
    print ""
    print "COMMANDS  (DevWorkspace — keeper daemon, ADR-037)"
    print "  keeper status                 Keeper mode (auto|operator-only|down) + policy version"
    print "  keeper policy                 Active keeper policy from policy-<workspace> Radicle repo"
    print "  keeper switch <mode>          Switch keeper mode (delegates to switch_to_<mode> playbook)"
    print ""
    print "COMMANDS  (DevWorkspace — governance, ADR-038)"
    print "  governance delegations        Radicle delegation sets for policy/desired/state repos"
    print "  governance signers            Active NATS JWT signers and M-of-N quorum status"
    print ""
    print "COMMANDS  (all repo_kinds — registry)"
    print "  i resolve-registry            Resolved OCI registry endpoint (env → capabilities.ncl → default)"
    print "  i list                        Show resolved registry endpoint"
    print "  i list --live                 Query _catalog and list live domains/ and modes/ artifacts"
}

def main [
    ...args: string
    --priority: string = ""
    --mode: string = "solo"
    --platform: string = ""
    --dry-run
    --live
] {
    if ($args | is-empty) or ($args | first) == "help" { show-help; return }
    let sub = ($args | str join " ")
    match $sub {
        "state"        => { state }
        "next"         => { next }
        "connections"  => { connections }
        "gates"        => { gates }
        "card"         => { card }
        "capabilities" => { capabilities }
        "install"      => { if $dry_run { install --mode $mode --platform $platform --dry-run } else { install --mode $mode --platform $platform } }
        "backlog"      => { backlog --priority $priority }
        "ops list"                      => { ops list }
        "ops history"                   => { ops history }
        "keeper status"                 => { keeper status }
        "keeper policy"                 => { keeper policy }
        "governance delegations"        => { governance delegations }
        "governance signers"            => { governance signers }
        "playbook list"                 => { playbook list }
        "playbook history"              => { playbook history }
        _ if ($sub | str starts-with "backlog show ")       => { backlog show ($args | get 2) }
        _ if ($sub | str starts-with "validate ")           => { validate ($args | skip 1 | str join " ") }
        _ if ($sub | str starts-with "ops describe ")       => { ops describe ($args | get 2) }
        _ if ($sub | str starts-with "ops sign ")           => { ops sign ($args | get 2) }
        _ if ($sub | str starts-with "playbook describe ")  => { playbook describe ($args | get 2) }
        _ if ($sub | str starts-with "playbook run ")       => {
            if $dry_run { playbook run ($args | get 2) --dry-run } else { playbook run ($args | get 2) }
        }
        _ if ($sub | str starts-with "keeper switch ")      => { keeper switch ($args | get 2) }
        _ if ($sub | str starts-with "ops history ")        => { ops history --workspace ($args | get 2) }
        "i resolve-registry"                                 => { i resolve-registry | print }
        "i list"                                             => { if $live { i list --live } else { i list } }
        _              => { print $"Unknown command: ($sub)"; show-help }
    }
}
