Commit Graph
832 Commits
Author SHA1 Message Date
rootiest e40d67df59 style: format fish files with fish_indent 2026-09-09 20:27:48 -04:00
rootiest 2dc978e719 fix(docs): align registry generator output with fish_indent 2026-09-09 20:27:21 -04:00
rootiest de6349f2dc test(net): add network isolation harnesses and failure edge case coverage 2026-09-09 19:51:17 -04:00
Gitea Actions b59318e957 chore(docs): regenerate manual, man page, and component registry 2026-09-09 19:59:39 +00:00
rootiest 90aaa6dcda Merge pull request 'feat(config-settings): replace the ANSI renderer with a curses front-end' (#141) from feat/config-settings-curses-tui into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m47s
CI / build-docs (push) Successful in 3m45s
Reviewed-on: #141
2026-09-09 19:54:11 +00:00
rootiest 9af0462d29 test(config-settings): cover the empty state dump refusal
The guard added in 208ad95 had no test. This is the failure worth
covering, because it is the one that does not announce itself: with an
empty dump every row renders as DEFAULT, indistinguishable from a config
where nothing is set, so the user toggles from a false baseline.

Reaching the guard needs a real terminal -- the isatty check sits in
front of it -- so the case runs fish under a pty via python3's stdlib
pty module, which this suite already depends on. An empty
__config_settings_state is shadowed in to fake the failure.

The pty reader's 15s deadline is load-bearing rather than defensive: if
the guard regresses, config-settings does not fail, it opens the TUI and
blocks on getch(), so an unbounded read would hang the suite instead of
failing it. Verified both ways -- passes with the guard, and with the
guard removed the deadline fires and the case fails with a legible
excerpt rather than a screenful of escape sequences.
2026-09-09 15:41:39 -04:00
rootiest 208ad95883 fix(config-settings): refuse to open on an empty state dump
An empty dump does not fail loudly: the TUI renders every row as
DEFAULT, which is indistinguishable from a config where nothing is set.
That is a wrong answer rather than a missing one -- the user would be
looking at ON rows reported as DEFAULT -- so the launcher now checks the
dump is non-empty and bails with a message instead. The taxonomy alone
guarantees output on any working checkout. Also guard a failed mktemp,
which would otherwise send the dump to /state and hand an empty path to
rm -rf.

README: mention that / searches sub-categories across every category.
2026-09-09 15:36:40 -04:00
rootiest 983434ffb4 docs(config-settings): drop backticks from the function header
test_concat_section_five_stays_verbatim forbids backticks inside
generated Section 5 entries -- function headers are rendered verbatim, so
inline code markup there breaks the man page.
2026-09-09 14:51:59 -04:00
rootiest 078d01666e feat(config-settings): replace the ANSI renderer with a curses front-end
config-settings is now a launcher for scripts/config-settings-tui.py,
drawn with Python's stdlib curses. The seven fish files that hand-rolled
the ANSI renderer are gone, along with the golden harness that had to pin
their byte-exact output.

The TUI is a child process, so it can neither read the session's global
variables nor write them. State goes in as a dump from the new
__config_settings_state; the edits come back as a fish script that
config-settings sources, which is what lets the Session page's `set -g`
land in the caller's shell instead of in a child that is about to exit.
Every edit is emitted as a call to __config_settings_apply or
__config_settings_set_value, so list splitting, the SCROLLBACK_HISTORY_*
export mirror and the shadow-warning suppression all stay in the fish
layer that already owned them.

The consequence, and the one behaviour change: edits are applied in one
batch on exit rather than on each keypress. The status bar shows a
pending count.

New: `/` filters the current page, and on the Universal and Session pages
it reaches into every category's sub-categories, listing hits as
"Category › Sub" so a sub-category can be toggled without drilling into
its parent first. Also a `?` help overlay, mouse selection, and a
drill-down page that leads with the category's own toggle.

Gone with the renderer: the four width tiers, the wrap-aware erase
arithmetic, the stty/dd/od raw key reader, the panel-height bookkeeping
and the hand-written redraw differ. curses owns all of it, and the alt
screen plus absolute addressing makes the desync class behind 608b022,
4210f3b, 93fc5e0 and 3c4f720 unreachable.

The sub-category taxonomy is NOT duplicated in Python: it travels in the
state dump, still sourced from __config_settings_subcats. The category,
Sponge and Paths row tables move into Python, consolidating the two
copies the fish renderers kept.

Dependency: python3 with curses. Stdlib on Arch, Fedora and a full
Debian/Ubuntu python3; python3-minimal alone lacks _curses. The launcher
checks for both and names what is missing. Called out in the README.

Verified: 416/416 assertions, plus a live end-to-end in a sandbox HOME
confirming the Universal page writes universal scope only (U1/G0) and the
Session page global scope only (U0/G1).
2026-09-09 14:50:26 -04:00
rootiest c5d2624a06 feat(config-settings): curses prototype front-end
Add scripts/config-settings-tui.py, a stdlib-curses prototype of the
config-settings interface, plus a gate that runs its --self-test.

The backend is stubbed: values live in an in-memory dict and nothing is
read from or written to fish variables. This is here to evaluate the
render engine and the interaction model before committing to a rewrite.

Why curses rather than more ANSI arithmetic:

  - No flicker, structurally. curses diffs its virtual screen against the
    physical one and emits only the changed cells, which is what
    __config_settings_diff_redraw.fish reimplements by hand.
  - Alternate screen plus absolute addressing. Stray output cannot desync
    the display, so the bug class behind 608b022 (fish's read prompt),
    4210f3b (a shadow warning on stderr), 93fc5e0 and 3c4f720 (line wrap
    breaking the erase height) cannot occur at all.
  - Resize is a repaint rather than wrap-factor arithmetic.
  - Overlays, panes, live filtering and mouse input cost a few lines each.

The layout departs from the current single panel: a page sidebar with a
live filter on the left, a scrolling detail pane on the right, a help
overlay on '?', and mouse selection. Sub-category drill-down, the
tri-state badges and the Sponge/Paths value rows all carry over.

Dependency note: python3 with the curses module. That is stdlib on Arch,
Fedora and a full Debian/Ubuntu python3; python3-minimal alone does not
carry _curses, so the test asserts the import.

The prototype is not wired into config-settings and nothing existing
changed.
2026-09-09 14:29:40 -04:00
Gitea Actions 69de224cbf chore(docs): regenerate manual, man page, and component registry 2026-09-09 04:49:54 +00:00
rootiest 8e4b719425 fix(docs): title distro code blocks and fix missed shell highlighting (#140)
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m19s
CI / build-docs (push) Successful in 3m46s
2026-09-09 04:43:50 +00:00
rootiest 8f48120ba9 Merge pull request 'docs: strip gitignored AGENTS/ and JOB-BRIEF-FINDINGS references from code comments' (#139) from docs/strip-agents-internal-refs into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m57s
CI / build-docs (push) Successful in 3m27s
2026-09-08 16:59:05 +00:00
rootiest 9d7cddf758 docs: strip gitignored AGENTS/ and JOB-BRIEF-FINDINGS references from code comments
Comments in tracked files pointed readers at AGENTS/specs/*.md,
AGENTS.md, and JOB-BRIEF-FINDINGS.md -- all gitignored, transient
dev notes not part of the base repo. Rewrote each to be self-contained
or point at tracked docs (docs/fish-config.md) instead.
2026-09-08 12:54:53 -04:00
Gitea Actions d4c40519e5 chore(docs): regenerate manual, man page, and component registry 2026-09-08 16:50:35 +00:00
rootiest fb454da3bc Merge pull request 'fix(docs): strip backticks from Section 5 verbatim EXIT STATUS/ARGUMENTS lines' (#138) from fix/docs-verbatim-backticks into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m17s
CI / build-docs (push) Successful in 3m56s
2026-09-08 16:44:10 +00:00
rootiest 91dabd565c fix(docs): strip backticks from Section 5 verbatim EXIT STATUS/ARGUMENTS lines
Section 5 (function manpages) is pandoc-rendered verbatim, so a backtick
there is a literal character on the page, not markup. Nine EXIT STATUS/
ARGUMENTS lines across check_fish_deps, lock, cleanup, claude-pr,
claude-docs, dops, ports, screensleep, and steam-dl quoted a command with
backticks; reworded to plain text matching each function's own DESCRIPTION
style.

Fixes docs/verify-manual.py's test_concat_section_five_stays_verbatim,
failing in CI since before this branch (pre-existing on main, confirmed
against the commit before PR #133 merged).
2026-09-08 12:42:30 -04:00
rootiest 453a8500af Merge pull request 'refactor(config-settings): shared frame renderer + line-diff redraw' (#133) from refactor/config-settings-renderer into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m1s
CI / build-docs (push) Failing after 1m6s
Reviewed-on: #133
2026-09-08 16:28:51 +00:00
rootiest afbc3e97d7 Merge remote-tracking branch 'origin/main' into refactor/config-settings-renderer
# Conflicts:
#	functions/__config_settings_draw.fish
#	functions/__config_settings_draw_subcat.fish
#	functions/__config_settings_draw_value.fish
#	tests/functional.fish
2026-09-08 12:19:31 -04:00
rootiest 3c4f720749 fix(config-settings): repair diff-redraw review findings
- Print the missing exit-repaint after the inline editor's
  __cs_dispatch_draw call (was silently changed to capture-only earlier
  in the branch; this call site was missed, blanking the panel on
  commit/cancel of an inline edit).
- Hoist (count $prev_edit_frame) out of a quoted math string in the
  inline editor's per-keystroke redraw -- fish doesn't expand a command
  substitution there, so math silently errored.
- Add a >= 52 column floor to both diff-path guards: below the
  narrowest tier's own 52-column box width, lines wrap and the diff
  path's one-physical-row-per-logical-line walk corrupts the display.
- Reword a stale test comment that described panel_h as mirroring a
  hand-set constant in __cs_dispatch_draw; it derives panel_h from real
  output now.
- Declare prev_edit_frame with -l alongside edit_frame instead of a
  bare set, matching the file's convention.
- Move prev_frame's declaration to its point of use instead of an
  empty top-level placeholder, matching old_h in the same block.
2026-09-08 04:37:27 -04:00
rootiest a0491b2bd1 feat(config-settings): diff redraw in the inline value editor 2026-09-08 04:21:07 -04:00
rootiest fae15b8d55 feat(config-settings): diff redraw in the main navigation loop 2026-09-08 04:17:32 -04:00
rootiest ab14837e39 refactor(config-settings): dispatch captures the frame instead of printing it 2026-09-08 04:13:52 -04:00
rootiest ece3e9dbfb feat(config-settings): add __config_settings_diff_redraw 2026-09-08 04:08:00 -04:00
rootiest 7b02348f64 test(config-settings): lock draw-function line counts to their panel heights 2026-09-08 03:57:26 -04:00
rootiest 4f62710061 Merge pull request 'chore(gitignore): ignore agy's .agents/ cache via shared AI-tool glob' (#137) from fix/gitignore-agy-agents-dir into main
Reviewed-on: #137
2026-09-08 07:02:37 +00:00
rootiest c77a52acd3 chore(gitignore): ignore agy's .agents/ cache via shared AI-tool glob
Root-cause fix in the shared boilerplate template (~/.config/.user-dots/git/gitignore),
same tier as .claude*/.gemini*/.antigrav*. Drops the manual one-off .agents
line; re-stamped this repo's boilerplate block to the new template hash.
2026-09-08 03:00:21 -04:00
rootiest 31b89bbac7 Merge pull request 'refactor(functions): consolidate 198 duplicate color declarations into one helper' (#131) from refactor/shared-color-palette into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m59s
CI / build-docs (push) Failing after 1m14s
2026-09-08 05:44:16 +00:00
rootiest b541323949 Merge remote-tracking branch 'origin/main' into refactor/shared-color-palette
# Conflicts:
#	tests/functional.fish
2026-09-08 01:43:01 -04:00
rootiest 6679d19a09 Merge pull request 'fix: split dops/docker, fix header doc gaps from Job 3 findings' (#135) from fix/dops-docker-and-header-gaps into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m2s
CI / build-docs (push) Failing after 1m18s
2026-09-08 05:40:55 +00:00
rootiest 6b2cc3aefc Merge remote-tracking branch 'origin/main' into fix/dops-docker-and-header-gaps 2026-09-08 01:39:44 -04:00
rootiest f2744a9a60 Merge pull request 'feat(help): render --help from function headers; stop 8 functions executing on --help' (#132) from feat/header-driven-help into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m9s
CI / build-docs (push) Failing after 3m59s
2026-09-08 05:39:23 +00:00
rootiest 2e82761b7d Merge remote-tracking branch 'origin/main' into feat/header-driven-help
# Conflicts:
#	tests/functional.fish
2026-09-08 01:38:24 -04:00
rootiest 001c5d3df7 Merge pull request 'fix: tricks.fish double-source and paru/yay wrapper duplication' (#136) from fix/tricks-double-source-and-paru-wrapper-dedup into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m46s
CI / build-docs (push) Failing after 4m22s
2026-09-08 05:31:52 +00:00
rootiest 8487f124c7 Merge remote-tracking branch 'origin/main' into fix/tricks-double-source-and-paru-wrapper-dedup 2026-09-08 01:30:43 -04:00
rootiest a994f9e91c Merge pull request 'perf(conf.d): cut shell startup latency by ~30%' (#130) from perf/startup-latency into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m49s
CI / build-docs (push) Failing after 3m48s
2026-09-08 05:30:05 +00:00
rootiest 43752aa268 Merge remote-tracking branch 'origin/main' into perf/startup-latency
# Conflicts:
#	tests/functional.fish
2026-09-08 01:28:27 -04:00
rootiest 0a2290640f Merge pull request 'test: unify the test harness, discover suites, fix a real hermeticity leak' (#134) from test/test-harness-overhaul into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m49s
CI / build-docs (push) Successful in 3m46s
2026-09-08 05:26:28 +00:00
rootiest 23bf649c5f fix(functions): dedupe conflicting paru/yay wrapper generators
__fish_config_sync_logging carried its own inferior copy of the
paru/yay wrapper generator (tee-based, no PTY, no progress-bar
rendering, hard-coded /usr/bin/paru|yay) alongside the canonical
version-6 generators in conf.d/paru-wrapper.fish and
conf.d/yay-wrapper.fish. Both wrote the same file with different
version markers and the same misattributed provenance comment, so
whichever ran last won and a subsequent C5 toggle would flip it back.
See startup-latency-JOB-BRIEF-FINDINGS.md §2.

__fish_config_sync_logging now delegates entirely to the canonical
generators instead of carrying a copy: they already resolve the real
binary via __fish_real_command (never /usr/bin-assumed) and
independently gate on their own C2/C5 keys, covering both the
enabled-regenerate and disabled-remove cases.

One behavior change falls out of delegating rather than special-casing
around it: a wrapper is no longer generated when C2 (paru-autoexec /
yay-autoexec) is disabled, even if C5 logging is on. The removed
sync-logging copy never checked C2, so it could reinstall a wrapper
the user had explicitly turned auto-exec off for.

Adds functions/_fish_source_scoped.fish: a small helper that runs
source inside its own function-call boundary. source itself runs in
the caller's scope, so a bare return inside a sourced conf.d guard
(both files have several) would otherwise unwind whatever function
called source directly -- verified with a minimal repro before relying
on it. Routing through this helper contains the return to just that
call, so calling paru's generator and then yay's actually reaches the
second call.

Manually verified end-to-end in an isolated HOME/XDG sandbox with a
stubbed paru/yay: enable generates both v6 wrappers, disable removes
both and drops the sentinel, re-enable regenerates them and clears the
sentinel.
2026-09-08 01:22:22 -04:00
rootiest f5c4016fa6 perf(conf.d): stop re-running expensive setup on tricks.fish's second source
tricks.fish is sourced twice per shell on CachyOS: once by the conf.d
autoload, once forced by config.fish to re-win over the distro's own
tricks.fish (measured 4.55ms for the second pass, see
startup-latency-JOB-BRIEF-FINDINGS.md §1). The bang-bang functions,
aliases, and history override further down need to re-run on both
passes since those are what re-assert over the distro config, but the
PATH/MANPAGER setup does not.

Gates fish_add_path and the type -q bat probe behind a once-per-session
global, guarding only that block. Everything else in the file, and the
coupling with config.fish's own cachyos-tricks guard, is unchanged.
2026-09-08 01:22:10 -04:00
rootiest dccf897c9f docs(functions): add missing EXIT STATUS header docs
Fills the gaps recorded in JOB-BRIEF-FINDINGS.md for the functions
converted to header-driven --help: 16 argument-less functions gain an
EXIT STATUS section (check_fish_deps, claude-docs, claude-pr, cleanup,
fast, fzf-update, gip, gip4, hist, limine-edit, lock, ports,
screensleep, steam-dl, swapstat, tmux-clean), and 8 functions that
already document ARGUMENTS gain EXIT STATUS too (lD, lsr, lss, lstree,
lt, ltr, lx, qr).

Not touched: gip6, ld, parur, sudo-toggle, upgrade, the JOB-BRIEF's
'missing ARGUMENTS' group. Re-checked their bodies against that claim
-- none of the five take a positional argument ($argv only appears
forwarded to the --help check), so an ARGUMENTS section would document
something that doesn't exist. All five already have EXIT STATUS.
2026-09-08 01:12:04 -04:00
rootiest 3aa462a2b8 style(functions): remove blank line in sponge_filter_secrets.fish header
The man-page comment header was separated from the function line by a
blank line -- the only file in functions/ with that gap. Cosmetic, no
runtime effect (manualtools and the help-flag renderer both tolerate
it by walking past a blank separator). See JOB-BRIEF-FINDINGS.md §2;
the renderer's tolerance for this case is left in place deliberately,
it is not dead code.
2026-09-08 01:11:56 -04:00
rootiest 23420b3235 fix(functions): split dops.fish into dops and docker
dops.fish defined `docker`, not `dops`. dops was never defined; docker
was only conditionally defined as a side effect of the failed dops
autoload, so its behavior could silently change mid-session. See
JOB-BRIEF-FINDINGS.md §1.

- functions/dops.fish now defines dops: a real enhanced `docker ps`
  listing (custom Names/Image/Status/Ports table), with its own --help.
- functions/docker.fish is a new file holding the ps-redirect wrapper,
  fixed to actually call dops (previously called the still-undefined
  dops from inside itself).
- Bare `docker` with no arguments no longer falls through an
  if-with-no-else (the fish false-zero, AGENTS.md standing gotcha #5)
  and does nothing; it now runs the real docker binary, which prints
  its own usage.
- tests/functional.fish: updated the now-stale comment explaining why
  the help-flag check resolves the real function name instead of the
  file stem.
2026-09-08 01:11:49 -04:00
rootiest a3853a93d8 test: run the config-settings render golden in the test runner
A byte-identity gate CI never runs will rot, and a rotted gate is worse
than no gate. Adds the render harness as Phase 4, in the same shape as the
vault suite: its own process, its own sandbox, no loaded config needed.

Kept to one self-contained block so it can be dropped or re-applied by
hand if the runner is restructured. The functional suite's 317/317 count
is untouched; the render cases report separately.
2026-09-07 20:13:08 -04:00
rootiest 328e30882e test: assert converted functions still emit colour in --help
Runtime escape-presence check over 24 user-facing functions.

Deliberately not a static grep for __fish_palette: measured on a broken
logs.fish whose --help block lost its declarations without gaining a call,
`fish -n` exited 0 and `grep -c __fish_palette` returned 1 while the help
output went 431 B -> 150 B with every escape stripped. The full suite was
green too. Only running the function and looking for an \e byte catches it;
that demonstration is recorded as a comment above the test.

Verified red as well as green -- deleting the __fish_palette call from
functions/mkcd.fish yields:

  --help lost its colour: mkcd
  FAIL  test_functions_keep_their_palette
  16/17 passed

fish_prompt.fish is excluded by name (its own hex palette); qc is excluded
because its --help shells out to aichat, which CI lacks.
2026-09-07 20:12:59 -04:00
rootiest 1f847bfce0 test(guards): cover always/on, always/off and AND via a synthetic registry
14 cases. The production registry has 65 entries all carrying exactly one
tag and no always/* anywhere, so these three documented semantics are
unreachable from real data -- the synthetic keys/values table is the only
way to reach them. Reasoning recorded at the fixture.

Also pins the degenerate no-slash tag and reaches C5 through
__fish_config_op_enabled, which is the path production code takes.
2026-09-07 20:12:22 -04:00
rootiest 7a6a7339dc test(guards): cover op_enabled against the real registry
5 cases: default-on, .fish suffix stripping, category off, subcategory
overriding it, and the documented fail-open on a missing entry.
2026-09-07 20:11:06 -04:00
rootiest 7d815b17e8 refactor: retire drifted palette names in the last five functions
c_rst/c_txt -> c_reset, c_ttl -> c_head, c_bold -> c_cmd, c_primary -> c_arg
across 67 interpolation sites (c_rst 53, c_primary 10, c_bold 2, c_ttl 1,
c_txt 1). Each maps to a variable holding identical bytes, so output is
unchanged. Unlike the 33 structural conversions this edits text that
renders, so it is gated on its own per-file byte-diff against main:

  ok  logs --help         (431 B out, 0 B err)
  ok  mkcd --help         (437 B out, 0 B err)
  ok  qc --help           (936 B out, 0 B err)
  ok  rand_string --help  (883 B out, 0 B err)
  ok  smart_exit --help   (265 B out, 0 B err)
  ok  mkcd                (437 B out, 0 B err)   <- no-args error path

c_accent is deliberately NOT merged into c_ok -- both are (set_color green)
but a role is a semantic slot, not a colour. See JOB-BRIEF-FINDINGS.md.

logs.fish needed two calls (the C4 guard block and the --help block, both at
8-space indentation); it is the only multi-run file in the repository and the
exact trap a depth-deduplicating converter falls into. logs.fish also declared
c_bold without ever interpolating it -- that declaration is simply deleted.
2026-09-07 20:10:43 -04:00
rootiest 2efbcfc53f test(guards): cover registry lookup, including identity:site pairing
5 cases. The wrong-site case pins that the key is the identity:site pair
rather than the identity alone.
2026-09-07 20:10:15 -04:00
rootiest f6ead99f48 feat(help): header-driven --help for development functions, plus the wiring guard
bd-pull, branch, check_fish_deps, claude-docs, claude-pr, fast, poke, qr
and parur take the standard call site. fish-deps instead routes -h/--help
to its existing __fish_deps_help, which previously answered only via the
unknown-subcommand path, printing "Unknown subcommand: --help" and
exiting 1.

This completes the 40 conversions and lands
test_every_user_facing_function_has_help -- acceptance criterion 6, the
check that fails when a new user-facing function ships without help. It
was developed red against the pre-conversion tree and reported exactly
the 40 names of the design's CONVERT table:

    bd-pull bkg branch check_fish_deps claude-docs claude-pr cleanup
    fast fc fish-deps fzf-update gip gip4 gip6 hist lD ld limine-edit
    lock lsr lss lstree lt ltr lx parur poke ports qr sbver screensleep
    split spwin steam-dl sudo-toggle swapstat tab tmux-clean upgrade
    wake-lock

each as "<name>: no -h/--help handling and not in $__help_exempt", with
the guard exiting 1. The count fell 40 -> 32 -> 24 -> 17 -> 10 -> 0
across the conversion commits. It is committed green so every commit on
this branch passes the suite.

$__help_exempt is the single machine-readable exemption record; a second
assertion fails if an exempt name stops being a published function, so a
rename or deletion upstream cannot silently exempt nothing.
2026-09-07 20:09:30 -04:00