268 lines
10 KiB
Plaintext
268 lines
10 KiB
Plaintext
|
|
# reflection/nulib/dashboard.nu — overview, status, health commands.
|
||
|
|
|
||
|
|
use ./fmt.nu *
|
||
|
|
use ./shared.nu [project-root, load-dimensions, load-gates, adrs-brief]
|
||
|
|
|
||
|
|
export def run-overview [fmt_resolved: string] {
|
||
|
|
let root = project-root
|
||
|
|
let name = ($root | path basename)
|
||
|
|
|
||
|
|
let cargo_toml = $"($root)/Cargo.toml"
|
||
|
|
let has_workspace = ($cargo_toml | path exists) and ((open $cargo_toml | get -o workspace | is-not-empty) == true)
|
||
|
|
|
||
|
|
let crates = if ($cargo_toml | path exists) {
|
||
|
|
let cargo = (open $cargo_toml)
|
||
|
|
let members = ($cargo | get -o workspace.members | default [])
|
||
|
|
if ($members | is-empty) {
|
||
|
|
let pkg_name = ($cargo | get -o package.name | default $name)
|
||
|
|
let deps = ($cargo | get -o dependencies | default {} | columns)
|
||
|
|
let features = ($cargo | get -o features | default {} | columns)
|
||
|
|
[{ name: $pkg_name, deps: ($deps | length), features: $features }]
|
||
|
|
} else {
|
||
|
|
mut result = []
|
||
|
|
for member in $members {
|
||
|
|
let expanded = (glob $"($root)/($member)/Cargo.toml")
|
||
|
|
for ct in $expanded {
|
||
|
|
let c = (open $ct)
|
||
|
|
let cname = ($c | get -o package.name | default ($ct | path dirname | path basename))
|
||
|
|
let deps = ($c | get -o dependencies | default {} | columns)
|
||
|
|
let features = ($c | get -o features | default {} | columns)
|
||
|
|
$result = ($result | append { name: $cname, deps: ($deps | length), features: $features })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$result
|
||
|
|
}
|
||
|
|
} else { [] }
|
||
|
|
|
||
|
|
let total_deps = ($crates | get deps | math sum)
|
||
|
|
|
||
|
|
let systems = [
|
||
|
|
(if ($"($root)/.ontology/core.ncl" | path exists) { "ontology" } else { null }),
|
||
|
|
(if ((glob $"($root)/adrs/adr-*.ncl" | length) > 0) { "ADRs" } else { null }),
|
||
|
|
(if ($"($root)/reflection" | path exists) { "reflection" } else { null }),
|
||
|
|
(if ($"($root)/.ontology/manifest.ncl" | path exists) { "manifest" } else { null }),
|
||
|
|
(if ($"($root)/.coder" | path exists) { ".coder" } else { null }),
|
||
|
|
(if ($"($root)/.claude/CLAUDE.md" | path exists) { ".claude" } else { null }),
|
||
|
|
(if ($"($root)/justfile" | path exists) { "justfile" } else { null }),
|
||
|
|
] | compact
|
||
|
|
|
||
|
|
let report = (sync audit --quick --fmt silent)
|
||
|
|
let health = ($report.health? | default 0.0)
|
||
|
|
let nodes = ($report.nodes? | default {})
|
||
|
|
|
||
|
|
let dims = (load-dimensions $root)
|
||
|
|
let reached = ($dims | where reached == true | length)
|
||
|
|
let total_dims = ($dims | length)
|
||
|
|
|
||
|
|
let brief = adrs-brief
|
||
|
|
|
||
|
|
let modes_count = ((glob $"($env.ONTOREF_ROOT)/reflection/modes/*.ncl" | length) + (if $root != $env.ONTOREF_ROOT { glob $"($root)/reflection/modes/*.ncl" | length } else { 0 }))
|
||
|
|
let just_count = if ($"($root)/justfiles" | path exists) { glob $"($root)/justfiles/*.just" | length } else { 0 }
|
||
|
|
|
||
|
|
let overview_data = {
|
||
|
|
project: $name,
|
||
|
|
root: $root,
|
||
|
|
workspace: $has_workspace,
|
||
|
|
crates: ($crates | each { |c| { name: $c.name, deps: $c.deps, features: ($c.features | str join ", ") } }),
|
||
|
|
total_crates: ($crates | length),
|
||
|
|
total_deps: $total_deps,
|
||
|
|
systems: $systems,
|
||
|
|
health: $health,
|
||
|
|
nodes: $nodes,
|
||
|
|
dimensions: { reached: $reached, total: $total_dims },
|
||
|
|
adrs: $brief,
|
||
|
|
modes: $modes_count,
|
||
|
|
just_modules: $just_count,
|
||
|
|
}
|
||
|
|
|
||
|
|
match $fmt_resolved {
|
||
|
|
"json" => { print ($overview_data | to json); return },
|
||
|
|
"yaml" => { print ($overview_data | to yaml); return },
|
||
|
|
"table" => { print ($overview_data | table --expand); return },
|
||
|
|
"toml" => {
|
||
|
|
print ($overview_data | to json)
|
||
|
|
return
|
||
|
|
},
|
||
|
|
_ => {},
|
||
|
|
}
|
||
|
|
|
||
|
|
let h_color = if $health >= 90.0 { (ansi green_bold) } else if $health >= 70.0 { (ansi yellow_bold) } else { (ansi red_bold) }
|
||
|
|
let bar_len = (($health / 5.0) | math round | into int)
|
||
|
|
let bar_fill = ("█" | fill -c "█" -w $bar_len)
|
||
|
|
let bar_empty = ("░" | fill -c "░" -w (20 - $bar_len))
|
||
|
|
|
||
|
|
print ""
|
||
|
|
fmt-header $name
|
||
|
|
fmt-sep
|
||
|
|
|
||
|
|
let ws_label = if $has_workspace { $"(ansi cyan)workspace(ansi reset)" } else { $"(ansi dark_gray)single crate(ansi reset)" }
|
||
|
|
print $" ($ws_label) (ansi dark_gray)($systems | str join ' · ')(ansi reset)"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
print $" ($h_color)($bar_fill)(ansi dark_gray)($bar_empty)(ansi reset) ($h_color)($health)%(ansi reset)"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
if ($crates | is-not-empty) {
|
||
|
|
let crate_label = $"(ansi white_bold)Crates(ansi reset) ($crates | length) (ansi dark_gray)total deps: ($total_deps)(ansi reset)"
|
||
|
|
print $" ($crate_label)"
|
||
|
|
for c in $crates {
|
||
|
|
let feat_str = if ($c.features | is-not-empty) { $" (ansi dark_gray)features: ($c.features | str join ', ')(ansi reset)" } else { "" }
|
||
|
|
let dep_count = $c.deps
|
||
|
|
print $" (ansi green)($c.name)(ansi reset) (ansi dark_gray)($dep_count) deps(ansi reset)($feat_str)"
|
||
|
|
}
|
||
|
|
print ""
|
||
|
|
}
|
||
|
|
|
||
|
|
let ok = ($nodes.ok? | default 0)
|
||
|
|
let issues = (($nodes.missing? | default 0) + ($nodes.stale? | default 0) + ($nodes.drift? | default 0))
|
||
|
|
let issues_str = if $issues > 0 { $"(ansi yellow)($issues)(ansi reset)" } else { $"(ansi dark_gray)0(ansi reset)" }
|
||
|
|
let adr_label = $"($brief.accepted)A/($brief.superseded)S/($brief.proposed)P"
|
||
|
|
let dim_label = if $total_dims > 0 { $"($reached)/($total_dims)" } else { "—" }
|
||
|
|
|
||
|
|
print $" (ansi white_bold)Nodes(ansi reset) (ansi green)($ok)(ansi reset) ok ($issues_str) issues (ansi white_bold)ADRs(ansi reset) (ansi cyan)($adr_label)(ansi reset) (ansi white_bold)Dims(ansi reset) (ansi cyan)($dim_label)(ansi reset) (ansi white_bold)Modes(ansi reset) ($modes_count) (ansi white_bold)Just(ansi reset) ($just_count)"
|
||
|
|
|
||
|
|
let active_dims = ($dims | where reached == false)
|
||
|
|
if ($active_dims | is-not-empty) {
|
||
|
|
print ""
|
||
|
|
for d in $active_dims {
|
||
|
|
print $" (ansi dark_gray)($d.id): ($d.current_state) → ($d.desired_state)(ansi reset)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print ""
|
||
|
|
}
|
||
|
|
|
||
|
|
export def run-health [fmt_resolved: string, full: bool] {
|
||
|
|
let report = if $full {
|
||
|
|
sync audit --fmt (if $fmt_resolved == "json" { "json" } else { "silent" })
|
||
|
|
} else {
|
||
|
|
sync audit --quick --fmt (if $fmt_resolved == "json" { "json" } else { "silent" })
|
||
|
|
}
|
||
|
|
|
||
|
|
let health_data = {
|
||
|
|
health: ($report.health? | default 0.0),
|
||
|
|
nodes: ($report.nodes? | default {}),
|
||
|
|
}
|
||
|
|
|
||
|
|
match $fmt_resolved {
|
||
|
|
"json" => { print ($health_data | to json); return },
|
||
|
|
"yaml" => { print ($health_data | to yaml); return },
|
||
|
|
"toml" => { print ($health_data | to toml); return },
|
||
|
|
"table" => { print ($health_data | table --expand); return },
|
||
|
|
_ => {},
|
||
|
|
}
|
||
|
|
|
||
|
|
let health = $health_data.health
|
||
|
|
let nodes = $health_data.nodes
|
||
|
|
let ok = ($nodes.ok? | default 0)
|
||
|
|
let missing = ($nodes.missing? | default 0)
|
||
|
|
let stale = ($nodes.stale? | default 0)
|
||
|
|
let drift = ($nodes.drift? | default 0)
|
||
|
|
let broken = ($nodes.broken_edges? | default 0)
|
||
|
|
|
||
|
|
render-health-bar $health
|
||
|
|
print $" (ansi white_bold)Nodes(ansi reset) (ansi green)($ok) OK(ansi reset) (ansi dark_gray)($missing)M ($stale)S ($drift)D ($broken)B(ansi reset)"
|
||
|
|
print ""
|
||
|
|
}
|
||
|
|
|
||
|
|
export def run-status [fmt_resolved: string] {
|
||
|
|
let root = project-root
|
||
|
|
let name = ($root | path basename)
|
||
|
|
|
||
|
|
let report = (sync audit --quick --fmt silent)
|
||
|
|
let health = ($report.health? | default 0.0)
|
||
|
|
|
||
|
|
let dims = (load-dimensions $root)
|
||
|
|
let reached = ($dims | where reached == true | length)
|
||
|
|
let total_dims = ($dims | length)
|
||
|
|
|
||
|
|
let brief = adrs-brief
|
||
|
|
let adr_label = $"($brief.accepted)A/($brief.superseded)S/($brief.proposed)P"
|
||
|
|
|
||
|
|
let gates = (load-gates $root)
|
||
|
|
|
||
|
|
let nodes = ($report.nodes? | default {})
|
||
|
|
let ok = ($nodes.ok? | default 0)
|
||
|
|
let issues = (($nodes.missing? | default 0) + ($nodes.stale? | default 0) + ($nodes.drift? | default 0))
|
||
|
|
|
||
|
|
let recent = (coder log --limit 5)
|
||
|
|
|
||
|
|
let git_log = do { ^git -C $root log --oneline -3 } | complete
|
||
|
|
let commits = if $git_log.exit_code == 0 {
|
||
|
|
$git_log.stdout | lines | where { $in | is-not-empty }
|
||
|
|
} else { [] }
|
||
|
|
|
||
|
|
let status_data = {
|
||
|
|
project: $name,
|
||
|
|
root: $root,
|
||
|
|
health: $health,
|
||
|
|
nodes: { ok: $ok, issues: $issues },
|
||
|
|
dimensions: { reached: $reached, total: $total_dims },
|
||
|
|
adrs: $brief,
|
||
|
|
gates: ($gates | length),
|
||
|
|
recent_coder: ($recent | each { |r| { title: ($r.title? | default ""), kind: ($r.kind? | default ""), ts: ($r.timestamp? | default "") } }),
|
||
|
|
recent_commits: $commits,
|
||
|
|
}
|
||
|
|
|
||
|
|
match $fmt_resolved {
|
||
|
|
"json" => { print ($status_data | to json); return },
|
||
|
|
"yaml" => { print ($status_data | to yaml); return },
|
||
|
|
"toml" => {
|
||
|
|
print ($status_data | to json)
|
||
|
|
return
|
||
|
|
},
|
||
|
|
"table" => { print ($status_data | table --expand); return },
|
||
|
|
_ => {},
|
||
|
|
}
|
||
|
|
|
||
|
|
let h_color = if $health >= 90.0 { (ansi green_bold) } else if $health >= 70.0 { (ansi yellow_bold) } else { (ansi red_bold) }
|
||
|
|
let bar_len = (($health / 5.0) | math round | into int)
|
||
|
|
let bar_fill = ("█" | fill -c "█" -w $bar_len)
|
||
|
|
let bar_empty = ("░" | fill -c "░" -w (20 - $bar_len))
|
||
|
|
|
||
|
|
print ""
|
||
|
|
fmt-header $"($name) — project status"
|
||
|
|
fmt-sep
|
||
|
|
print $" (ansi white_bold)Health(ansi reset) ($h_color)($bar_fill)(ansi dark_gray)($bar_empty)(ansi reset) ($h_color)($health)%(ansi reset) (ansi dark_gray)\(quick)(ansi reset)"
|
||
|
|
let issues_str = if $issues > 0 { $"(ansi yellow)($issues) issues(ansi reset)" } else { $"(ansi dark_gray)0 issues(ansi reset)" }
|
||
|
|
print $" (ansi white_bold)Nodes(ansi reset) (ansi green)($ok) OK(ansi reset) ($issues_str)"
|
||
|
|
print $" (ansi white_bold)ADRs(ansi reset) (ansi cyan)($adr_label)(ansi reset)"
|
||
|
|
print $" (ansi white_bold)Gates(ansi reset) (ansi cyan)($gates | length) active(ansi reset)"
|
||
|
|
|
||
|
|
if $total_dims > 0 {
|
||
|
|
print $" (ansi white_bold)State(ansi reset) (ansi green)($reached)(ansi reset)/(ansi cyan)($total_dims)(ansi reset) dimensions reached"
|
||
|
|
let active_dims = ($dims | where reached == false)
|
||
|
|
for d in $active_dims {
|
||
|
|
print $" (ansi dark_gray)($d.id): ($d.current_state) → ($d.desired_state)(ansi reset)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($commits | is-not-empty) {
|
||
|
|
print ""
|
||
|
|
fmt-section "Recent commits"
|
||
|
|
print ""
|
||
|
|
for c in $commits {
|
||
|
|
print $" (ansi dark_gray)($c)(ansi reset)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($recent | is-not-empty) {
|
||
|
|
print ""
|
||
|
|
fmt-section "Recent activity (.coder/)"
|
||
|
|
print ""
|
||
|
|
for r in $recent {
|
||
|
|
let kind_color = match ($r.kind? | default "") {
|
||
|
|
"done" => (ansi green),
|
||
|
|
"plan" => (ansi cyan),
|
||
|
|
"review" => (ansi magenta),
|
||
|
|
"audit" => (ansi yellow),
|
||
|
|
_ => (ansi default_dimmed),
|
||
|
|
}
|
||
|
|
let ts = ($r.timestamp? | default "" | str substring 0..9)
|
||
|
|
print $" ($kind_color)($r.kind? | default "?")(ansi reset) (ansi dark_gray)($ts)(ansi reset) ($r.title? | default "")"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print ""
|
||
|
|
}
|