221 lines
9.2 KiB
Text
221 lines
9.2 KiB
Text
|
|
#!/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)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ─── Loaders ──────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def load-state [] { ^nickel export $"(project-root)/.ontology/state.ncl" | from json }
|
|||
|
|
def load-core [] { ^nickel export $"(project-root)/.ontology/core.ncl" | from json }
|
|||
|
|
def load-gate [] { ^nickel export $"(project-root)/.ontology/gate.ncl" | from json }
|
|||
|
|
def load-connections [] { ^nickel export $"(project-root)/.ontology/connections.ncl" | from json }
|
|||
|
|
def load-manifest [] { ^nickel export $"(project-root)/.ontology/manifest.ncl" | from json }
|
|||
|
|
|
|||
|
|
def load-card [] {
|
|||
|
|
let path = $"(project-root)/card.ncl"
|
|||
|
|
if ($path | path exists) { ^nickel export $path | from json } else { null }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def load-backlog [] {
|
|||
|
|
let path = $"(project-root)/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 ', ')"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ─── help + main ──────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
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"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def main [
|
|||
|
|
...args: string
|
|||
|
|
--priority: string = ""
|
|||
|
|
] {
|
|||
|
|
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 }
|
|||
|
|
"backlog" => { backlog --priority $priority }
|
|||
|
|
_ if ($sub | str starts-with "backlog show ") => { backlog show ($args | get 2) }
|
|||
|
|
_ if ($sub | str starts-with "validate ") => { validate ($args | skip 1 | str join " ") }
|
|||
|
|
_ => { print $"Unknown command: ($sub)"; show-help }
|
|||
|
|
}
|
|||
|
|
}
|