Jesús Pérez 0396e4037b
Some checks failed
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
chore: add ontology and reflection
2026-03-13 00:21:04 +00:00

234 lines
7.8 KiB
Plaintext

#!/usr/bin/env nu
# reflection/modules/backlog.nu — backlog management commands.
use env.nu *
use store.nu [daemon-export-safe]
export def "backlog list" [
--status: string = "",
--kind: string = "",
--fmt: string = "",
] {
let f = if ($fmt | is-not-empty) { $fmt } else { "table" }
let items = (backlog-load)
let rows = if ($status | is-not-empty) {
$items | where status == $status
} else if ($kind | is-not-empty) {
$items | where kind == $kind
} else {
$items | where status != "Done" | where status != "Cancelled"
}
let display = $rows | select id title kind priority status | sort-by priority
match $f {
"json" => { $display | to json },
"md" => { $display | to md },
_ => { $display | table },
}
}
export def "backlog show" [id: string] {
let item = (backlog-load) | where id == $id | first
print ""
print $"[($item.status)] ($item.id) — ($item.title)"
print $"Kind: ($item.kind) Priority: ($item.priority)"
if ($item.detail? | default "" | str length) > 0 { print ""; print $item.detail }
let adrs = ($item.related_adrs? | default [])
let modes = ($item.related_modes? | default [])
if ($adrs | is-not-empty) { print $"ADRs: ($adrs | str join ', ')" }
if ($modes | is-not-empty) { print $"Modes: ($modes | str join ', ')" }
if ($item.related_dim? | is-not-empty) { print $"Dimension: ($item.related_dim)" }
if ($item.graduates_to? | is-not-empty) { print $"Graduates to: ($item.graduates_to)" }
print ""
}
export def "backlog add" [
title: string,
--kind: string = "Todo",
--priority: string = "Medium",
--detail: string = "",
--dim: string = "",
--adr: string = "",
--mode: string = "",
] {
let root = (backlog-root)
let file = $"($root)/reflection/backlog.ncl"
if not ($file | path exists) {
print $"No backlog.ncl at ($file)"
return
}
let items = (backlog-load)
let next_num = if ($items | is-empty) { 1 } else {
($items | each { |i| $i.id | str replace "bl-" "" | into int } | sort | last) + 1
}
let new_id = $"bl-($next_num | fill --alignment right --width 3 --character '0')"
let today = (date now | format date "%Y-%m-%d")
let adrs_list = if ($adr | str length) > 0 { [$adr] } else { [] }
let modes_list = if ($mode | str length) > 0 { [$mode] } else { [] }
print ""
print $"New backlog item: ($new_id)"
print $" title = ($title)"
print $" kind = ($kind)"
print $" priority = ($priority)"
print ""
print $"Add to reflection/backlog.ncl:"
print $" \{"
print $" id = \"($new_id)\","
print $" title = \"($title)\","
print $" kind = '($kind),"
print $" priority = '($priority),"
print $" status = 'Open,"
print $" detail = \"($detail)\","
print $" created = \"($today)\","
print $" updated = \"($today)\","
print $" \},"
}
export def "backlog done" [id: string] {
backlog-set-status $id "Done"
}
export def "backlog cancel" [id: string] {
backlog-set-status $id "Cancelled"
}
export def "backlog promote" [id: string] {
let item = (backlog-load) | where id == $id | first
print ""
print $"Promoting ($item.id): ($item.title)"
print $"Graduates to: ($item.graduates_to? | default 'unset')"
print ""
let target = ($item.graduates_to? | default "")
if $target == "Adr" {
let flag = if ($item.priority == "Critical" or $item.priority == "High") { "-a " } else { "" }
print "Run in a Claude session:"
print $" /create-adr ($flag)\"($item.title)\""
} else if $target == "Mode" {
let mode_id = ($item.title | str downcase | str replace --all " " "-" | str replace --all --regex '[^a-z0-9-]' "")
print $" ontoref register → affects_capability=true, mode_id=($mode_id)"
} else if $target == "StateTransition" {
let dim = ($item.related_dim? | default "")
print " ontoref register → changes_ontology_state=true"
if ($dim | str length) > 0 { print $" dimension_id: ($dim)" }
} else if $target == "PrItem" {
print " ontoref mode create-pr"
} else {
print " No graduation target set. Edit backlog.ncl to add graduates_to."
}
print ""
}
export def "backlog roadmap" [] {
let root = (backlog-root)
let bl = (backlog-load) | where status != "Done" | where status != "Cancelled"
let state_file = $"($root)/.ontology/state.ncl"
let dims = if ($state_file | path exists) {
let data = (daemon-export-safe $state_file)
if $data != null { $data | get dimensions } else { [] }
} else { [] }
print ""
print "═══ ROADMAP ═══════════════════════════════════════"
if ($dims | is-not-empty) {
print ""
print "STATE DIMENSIONS"
for d in $dims {
if $d.current_state != $d.desired_state {
print $" ($d.id) ($d.current_state) → ($d.desired_state) [($d.horizon)]"
}
}
}
for p in ["Critical", "High", "Medium", "Low"] {
let p_items = $bl | where priority == $p
if ($p_items | is-not-empty) {
print ""
print ($p | str upcase)
for i in $p_items {
let tag = if $i.status == "InProgress" { "[~]" } else { "[ ]" }
print $" ($tag) ($i.id) ($i.title) [($i.kind)]"
}
}
}
print ""
print "═══════════════════════════════════════════════════"
print ""
}
# ── Internal ────────────────────────────────────────────────────────────────────
def backlog-root []: nothing -> string {
$env.ONTOREF_PROJECT_ROOT? | default $env.ONTOREF_ROOT
}
def backlog-load [] {
let file = $"($env.ONTOREF_PROJECT_ROOT? | default $env.ONTOREF_ROOT)/reflection/backlog.ncl"
if not ($file | path exists) { return [] }
let data = (daemon-export-safe $file)
if $data == null { return [] }
$data | get items
}
def backlog-set-status [id: string, new_status: string] {
let file = $"($env.ONTOREF_PROJECT_ROOT? | default $env.ONTOREF_ROOT)/reflection/backlog.ncl"
let today = (date now | format date "%Y-%m-%d")
let lines = (open --raw $file | lines)
# Find the line index of the id field for this item
let id_pattern = $"\"($id)\""
let id_line = ($lines | enumerate | where { |r| $r.item | str contains $id_pattern } | first)
if ($id_line | is-empty) {
print $" error: id '($id)' not found in ($file)"
return
}
let start = $id_line.index
# Scan forward from the id line to find the closing `},` of this item record.
# Track brace depth: the id line is already inside the outer record (depth=1 going in).
mut depth = 0
mut end_line = $start
for i in ($start..($lines | length | $in - 1)) {
let l = ($lines | get $i)
let opens = ($l | split chars | where $it == "{" | length)
let closes = ($l | split chars | where $it == "}" | length)
$depth = $depth + $opens - $closes
if $depth < 0 {
$end_line = $i
break
}
}
# Bind end_line to immutable before closure (Nu forbids capturing mut vars in closures)
let end_idx = $end_line
# Patch status and updated within the located range [start, end_idx]
let patched = ($lines | enumerate | each { |r|
if $r.index >= $start and $r.index <= $end_idx {
$r.item
| str replace --regex "status[[:space:]]*=[[:space:]]*'[A-Za-z]+" $"status = '($new_status)"
| str replace --regex "updated[[:space:]]*=[[:space:]]*\"[^\"]*\"" $"updated = \"($today)\""
} else {
$r.item
}
} | str join "\n")
$patched | save --force $file
let tc = do { ^nickel typecheck $file } | complete
if $tc.exit_code != 0 {
print " error: typecheck failed — reverting"
do { ^git checkout -- $file } | complete | ignore
return
}
print $" ($id) → ($new_status)"
}