#!/usr/bin/env nu # domains/personal/commands.nu — PersonalOntology domain CLI for ontoref. # Dispatched by the ontoref bash wrapper when repo_kind = PersonalOntology: # ore personal [args] # # Reads data from ONTOREF_PROJECT_ROOT (set by the bash wrapper). # Code lives in ONTOREF_ROOT/domains/personal/. Data lives in the project. def project-root [] { $env.ONTOREF_PROJECT_ROOT? | default (pwd | path expand) } # ─── Loaders ────────────────────────────────────────────────────────────────── def load-core [] { ^nickel export $"(project-root)/.ontology/core.ncl" | from json } def load-state [] { ^nickel export $"(project-root)/.ontology/state.ncl" | from json } def load-career [] { ^nickel export $"(project-root)/.ontology/career.ncl" | from json } def load-personal [] { ^nickel export $"(project-root)/.ontology/personal.ncl" | from json } def load-backlog [] { ^nickel export $"(project-root)/reflection/backlog.ncl" | from json } # ─── Display helpers ───────────────────────────────────────────────────────── def render-links [links: list] { if ($links | is-empty) { return } print "" print "links:" $links | each { |lk| let label = if ($lk.label? | default "" | is-not-empty) { $" — ($lk.label)" } else { "" } print $" [($lk.kind)] ($lk.url)($label)" } } # ─── NCL patch helpers ──────────────────────────────────────────────────────── def ncl-validate [path: string] { ^nickel export $path | from json | ignore } def ncl-block-field-idx [lines: list, target_id: string, field: string] { let id_idx = ( $lines | enumerate | where { |l| $l.item | str contains $"\"($target_id)\"" } | first | get index ) let next_id_idx = ( $lines | enumerate | skip ($id_idx + 1) | where { |l| ($l.item | str trim | str starts-with "id ") and ($l.item | str contains "=") and ($l.item | str contains "\"") } | if ($in | is-empty) { [{ index: ($lines | length) }] } else { $in } | first | get index ) $lines | enumerate | where { |l| $l.index > $id_idx and $l.index < $next_id_idx } | where { |l| $l.item | str trim | str starts-with $"($field) " } | first | get index } # ─── state / next / validate / audit ───────────────────────────────────────── export def "state" [] { load-state | get dimensions | each { |dim| let cur = $dim.states | where id == $dim.current_state | first { dimension: $dim.id, current: $dim.current_state, desired: $dim.desired_state, tension: $cur.tension, 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: "–", horizon: "–" } } else { let tr = $active | first { dimension: $dim.id, from: $tr.from, to: $tr.to, condition: $tr.condition, blocker: $tr.blocker, catalyst: $tr.catalyst, horizon: $tr.horizon } } } } 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, core_axioms: ($core.nodes | where { |n| $n.id in $core.core } | select id level name), 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", } } export def "audit" [] { let core = load-core let nodes = $core.nodes let edges = $core.edges let practice_ids = ($nodes | where level == "Practice" | get id) let tensions = ($nodes | where level == "Tension" | each { |t| let linked = ($edges | where kind == "ManifestsIn" | where { |e| $e.from == $t.id or $e.to == $t.id } | each { |e| let peer = if $e.from == $t.id { $e.to } else { $e.from }; if $peer in $practice_ids { $peer } else { null } } | compact) { tension: $t.id, name: $t.name, practice_count: ($linked | length), practices: ($linked | str join ", "), status: (if ($linked | is-empty) { "no practice" } else { "ok" }) } }) let axioms = ($nodes | where level == "Axiom" | each { |a| let manifests = ($edges | where from == $a.id and kind == "ManifestsIn" | each { |e| $nodes | where id == $e.to | first }) { axiom: $a.id, name: $a.name, total_links: ($manifests | length), project_links: ($manifests | where level == "Project" | length), status: (if ($manifests | where level == "Project" | is-empty) { "no project link" } else { "ok" }) } }) { tensions: $tensions, axioms: $axioms, active_gap: $core.tension_without_practice } } # ─── projects ──────────────────────────────────────────────────────────────── export def "projects" [] { load-core | get nodes | where level == "Project" | select id name description } export def "projects show" [id: string] { let nodes = load-core | get nodes let r = if ($id =~ '^[0-9]+$') { let projects = ($nodes | where level == "Project") let idx = ($id | into int) if $idx >= ($projects | length) { print --stderr $"projects show: index out of range ($idx) — total ($projects | length)"; exit 1 } $projects | skip $idx | first } else { let found = ($nodes | where id == $id and level == "Project") if ($found | is-empty) { print --stderr $"projects show: not found — ($id)"; exit 1 } $found | first } print $"id: ($r.id)" print $"name: ($r.name)" print $"description: ($r.description)" if ($r.artifact_paths? | default [] | is-not-empty) { print "" print "artifacts:" $r.artifact_paths | each { |p| print $" ($p)" } } render-links ($r.links? | default []) } # ─── career ─────────────────────────────────────────────────────────────────── export def "career skills" [--tier: string = ""] { let items = load-career | get skills let filtered = if ($tier | is-empty) { $items } else { $items | where tier == $tier } $filtered | sort-by --reverse proficiency | select name tier proficiency years } export def "career talks" [--status: string = ""] { let items = load-career | get talks let filtered = if ($status | is-empty) { $items } else { $items | where status == $status } $filtered | select id title event date status } export def "career publications" [--featured] { let items = load-career | get publications | sort-by sort_order let filtered = if $featured { $items | where featured == true } else { $items } $filtered | select project_node tagline status version featured } export def "career positioning" [] { load-career | get positioning | select name core_message target } # ─── content ────────────────────────────────────────────────────────────────── export def "content" [--status: string = ""] { let items = load-personal | get contents let filtered = if ($status | is-empty) { $items } else { $items | where status == $status } $filtered | select id title kind status } # ─── opportunities ──────────────────────────────────────────────────────────── export def "opportunities" [--status: string = ""] { let items = load-personal | get opportunities let filtered = if ($status | is-empty) { $items } else { $items | where status == $status } $filtered | select id name kind status deadline } export def "opportunities show" [id: string] { let items = load-personal | get opportunities let r = if ($id =~ '^[0-9]+$') { let idx = ($id | into int) if $idx >= ($items | length) { print --stderr $"opportunities show: index out of range ($idx) — total ($items | length)"; exit 1 } $items | skip $idx | first } else { let found = ($items | where id == $id) if ($found | is-empty) { print --stderr $"opportunities show: not found — ($id)"; exit 1 } $found | first } print $"id: ($r.id)" print $"name: ($r.name)" print $"kind: ($r.kind)" print $"status: ($r.status)" print $"deadline: ($r.deadline)" render-links ($r.links? | default []) if ($r.fit_signals | is-not-empty) { print ""; print $"fit_signals: ($r.fit_signals | str join ', ')" } if ($r.linked_nodes | is-not-empty) { print ""; print $"nodes: ($r.linked_nodes | str join ', ')" } print ""; print "note:"; print $" ($r.note)" } export def "opportunities update" [id: string, --status: string] { let valid = ["Watching" "Evaluating" "Active" "Submitted" "Closed"] if ($status | is-empty) { print --stderr "opportunities update: --status required"; exit 1 } if not ($status in $valid) { print --stderr $"opportunities update: invalid status '($status)' — valid: ($valid | str join ', ')"; exit 1 } let items = load-personal | get opportunities if ($items | where id == $id | is-empty) { print --stderr $"opportunities update: not found — ($id)"; exit 1 } let path = $"(project-root)/.ontology/personal.ncl" let lines = open --raw $path | lines let status_idx = (ncl-block-field-idx $lines $id "status") let new_status = ($lines | get $status_idx | str replace --regex `'\w+` $"'($status)") let patched = ($lines | enumerate | each { |l| if $l.index == $status_idx { $new_status } else { $l.item } } | str join "\n") $patched | save --force $path ncl-validate $path print $"($id): status → '($status)" } # ─── cfp ────────────────────────────────────────────────────────────────────── export def "cfp" [--stage: string = ""] { let items = load-backlog | get cfp let filtered = if ($stage | is-empty) { $items } else { $items | where stage == $stage } $filtered | select id name stage deadline sessionize_session_id next_action } export def "cfp show" [id: string] { let items = load-backlog | get cfp let r = if ($id =~ '^[0-9]+$') { let idx = ($id | into int) if $idx >= ($items | length) { print --stderr $"cfp show: index out of range ($idx) — total ($items | length)"; exit 1 } $items | skip $idx | first } else { let found = ($items | where id == $id) if ($found | is-empty) { print --stderr $"cfp show: not found — ($id)"; exit 1 } $found | first } print $"id: ($r.id)" print $"name: ($r.name)" print $"stage: ($r.stage)" print $"deadline: ($r.deadline)" print $"opportunity_id: ($r.opportunity_id)" print $"sessionize_session_id: ($r.sessionize_session_id)" print $"related_mode: ($r.related_mode)" print $"created: ($r.created)" print $"updated: ($r.updated)" render-links ($r.links? | default []) print ""; print "next_action:"; print $" ($r.next_action)" print ""; print "note:"; print $" ($r.note)" } export def "cfp update" [id: string, --stage: string] { let valid = ["Watching" "Evaluating" "Drafting" "Submitted" "Accepted" "Declined" "Delivered"] if ($stage | is-empty) { print --stderr "cfp update: --stage required"; exit 1 } if not ($stage in $valid) { print --stderr $"cfp update: invalid stage '($stage)' — valid: ($valid | str join ', ')"; exit 1 } let items = load-backlog | get cfp if ($items | where id == $id | is-empty) { print --stderr $"cfp update: not found — ($id)"; exit 1 } let today = (date now | format date "%Y-%m-%d") let path = $"(project-root)/reflection/backlog.ncl" let lines = open --raw $path | lines let stage_idx = (ncl-block-field-idx $lines $id "stage") let updated_idx = (ncl-block-field-idx $lines $id "updated") let new_stage = ($lines | get $stage_idx | str replace --regex `'\w+` $"'($stage)") let new_updated = ($lines | get $updated_idx | str replace --regex `"[0-9-]+"` $"\"($today)\"") let patched = ($lines | enumerate | each { |l| if $l.index == $stage_idx { $new_stage } else if $l.index == $updated_idx { $new_updated } else { $l.item } } | str join "\n") $patched | save --force $path ncl-validate $path print $"($id): stage → '($stage) | updated → ($today)" } # ─── help + main ────────────────────────────────────────────────────────────── def show-help [] { print "Personal Ontology — ontoref domain extension" print "" print "USAGE" print " ore personal [args]" print "" print "COMMANDS" print " projects Project portfolio (Project-level nodes)" print " projects show Full detail of a project node" print " state Current FSM position across all dimensions" print " next Next valid transition per dimension" print " validate Check against ontological invariants" print " audit Coherence audit: gaps in coverage" print " career skills [--tier] Skills by proficiency" print " career talks [--status] Talks filtered by status" print " career publications [--featured] Publication cards" print " career positioning Positioning strategies" print " content [--status] Content pipeline" print " opportunities [--status] Opportunities" print " opportunities show Full opportunity detail" print " opportunities update Update opportunity status" print " cfp [--stage] CFP pipeline" print " cfp show Full CFP detail" print " cfp update --stage Update CFP stage" } def main [ ...args: string --tier: string = "" --status: string = "" --stage: string = "" --featured ] { if ($args | is-empty) or ($args | first) == "help" { show-help; return } let sub = ($args | str join " ") match $sub { "state" => { state } "next" => { next } "audit" => { audit } "projects" => { projects } "projects show" => { print --stderr "usage: personal projects show "; exit 1 } _ if ($sub | str starts-with "projects show ") => { projects show ($args | get 2) } _ if ($sub | str starts-with "projects ") => { projects show ($args | get 1) } _ if ($sub | str starts-with "validate ") => { validate ($args | skip 1 | str join " ") } "career skills" => { career skills --tier $tier } "career talks" => { career talks --status $status } "career publications" => { if $featured { career publications --featured } else { career publications } } "career positioning" => { career positioning } "content" => { content --status $status } "opportunities" => { opportunities --status $status } "opportunities show" => { print --stderr "usage: personal opportunities show "; exit 1 } _ if ($sub | str starts-with "opportunities show ") => { opportunities show ($args | get 2) } _ if ($sub | str starts-with "opportunities update ") => { opportunities update ($args | get 2) --status $status } _ if ($sub | str starts-with "opportunities ") => { opportunities show ($args | get 1) } "cfp" => { cfp --stage $stage } "cfp show" => { print --stderr "usage: personal cfp show "; exit 1 } _ if ($sub | str starts-with "cfp show ") => { cfp show ($args | get 2) } _ if ($sub | str starts-with "cfp update ") => { cfp update ($args | get 2) --stage $stage } _ if ($sub | str starts-with "cfp ") => { cfp show ($args | get 1) } _ => { print $"Unknown command: ($sub)"; show-help } } }