Commit Graph
309 Commits
Author SHA1 Message Date
rootiest 9c52e64373 fix(agents-init): anchor the .gitignore fallback match
The non-git fallback used a substring match, so a negation line such as
!AGENTS/foo counted as the pattern AGENTS/ already being ignored and the
real rule was never appended.
2026-09-16 19:06:47 -04:00
rootiest 63288d9fd6 fix(agents-init): only scaffold inside a git repository
Resolving the root to (pwd) meant running an agent CLI anywhere created an
AGENTS/ git repo, an AGENTS.md, two root symlinks, and a docs/ tree in that
directory. Scaffolding now requires a git repo or a pre-existing agent file
(AGENTS.md, CLAUDE.md, or AGENTS/); elsewhere it is a no-op.
2026-09-16 19:06:43 -04:00
rootiest c67b5ebb35 fix(mkrep): stop the test suite creating real repos, and confirm implicit ones
Two related defects on the `$GIT_SERVER` auto-create path added in #150.

The test suite was creating repositories on a live forge. mkrep resolves a
server from `$GIT_SERVER` plus `$GITEA_URL`/`$GITEA_HOST`, and this repo
doubles as a real ~/.config/fish where all of them are exported, so the six
sections that call a bare `mkrep <dir>` took the auto-create branch and
contacted the server. That is how an empty `rootiest/repo` came to exist on
git.rootiest.dev on 2026-09-14 (since deleted). It also explains why those
cases looked flaky rather than broken: standalone they passed, because the
repo existed and mkrep linked instead of creating, while under
run-tests.fish they failed with `Error: no available login` — a throwaway
XDG_CONFIG_HOME leaves `tea` with no credentials. Passing for that reason is
worse than failing.

The suite now neutralizes all five variables once at setup, the same
shadow-to-empty pattern already used per-section for --check-existing, whose
comment flagged this exact hazard. Nothing depended on the ambient value:
every section wanting a server sets its own `set -lx GIT_SERVER`, and one
section asserts the opposite outright.

mkrep itself now confirms before an implicit create. Creating a repository
on a forge is the only outward-facing thing mkrep does, and on this path an
exported variable is all it takes to reach it, so `mkrep foo` — which reads
as purely local — would make a repo on a server without saying so. It asks
first, defaulting to no; declining leaves the local repo with no remote and
still exits 0. Provenance is what gates the prompt, not the resolved value:
--server, --remote and --new-remote all state outright what they will do, so
none of them prompts, and --yes skips the question. Where it cannot be asked
(a script, a pipe, any non-interactive shell) creation is skipped rather than
assumed, with a note on stderr naming the flags that would allow it.

Nine new assertions cover the skip, its stderr note, --yes, and --server not
prompting. The interactive read is verified by hand under a PTY, both
answers, but is not in the suite: that needs a pseudo-terminal, and the
answer parsing it guards is a single `string match`.
2026-09-16 04:16:35 -04:00
rootiest f153a3db87 feat(deps): add marktext and firejail as optional dependencies
Both back the new `md` wrapper and nothing else, so both land in the
Optional tier, skipped by `fish-deps install`/`sync` unless `--optional`
(or `--all`) is passed.

firejail is a plain system package everywhere, so it needs no special
handling. marktext is not: upstream ships an AUR package and its own
GitHub release assets, and no distro carries it under a common name. Its
`_fdc_pm` entry is therefore deliberately empty, and a `marktext-release`
special offers the AUR package via paru/yay where one is present and
otherwise installs upstream's AppImage to ~/.local/bin/marktext.

The release assets embed their version in the filename, so there is no
stable /releases/latest/download URL to fetch -- `_fish_deps_marktext_appimage`
reads the download URL from the GitHub API instead. Upstream builds the
Linux AppImage for x86_64 only, and the helper says so rather than
downloading an unusable binary.

`fish-deps update` refreshes marktext through the AUR where available, and
otherwise only when ~/.local/bin/marktext exists -- a distro-packaged
marktext belongs to that package manager, and dropping an AppImage into
~/.local/bin would shadow it.
2026-09-16 03:56:48 -04:00
rootiest 8aa2d15ae8 feat(md): launch MarkText detached, with an optional read-only sandbox
`md` forwards every argument to marktext untouched except two flags of its
own: `--read-only`/`-r` and `--foreground`. By default it detaches via
`bkg`, so the shell stays usable and the editor outlives the window that
launched it.

MarkText has no read-only mode, so `-r` sandboxes it with firejail, binding
each named file read-only. The subtlety is that MarkText is single-instance:
a plain launch hands the file to an already-running, unsandboxed, writable
window and exits, silently defeating the sandbox. `-r` therefore also passes
a private `--user-data-dir`, which forces an independent instance the
read-only bind actually covers, plus `--no-sandbox`, since Electron's own
sandbox needs the user namespaces firejail has already taken away.

Flags whose entire purpose is terminal output (`--version`, `-v`/`--verbose`,
`--debug`) imply `--foreground`; backgrounding them would send the output
you asked for to /dev/null.

The function is autoloaded and so never shadows an `md` function or alias
defined elsewhere -- fish only looks in functions/ when nothing named `md`
exists. A real `md` *binary* would be shadowed, so the body hands off to it
verbatim whenever marktext is not installed. No conf.d file and no
opinionated guard: `md` is a novel name rather than a command shadow, the
same as `bkg` and `detach`.
2026-09-16 03:56:47 -04:00
rootiest 17721a1cf2 fix(git): don't push an unborn HEAD from mkrep's default remote templates
mkrep only ever runs git init, never a commit, so a freshly created
repo has no HEAD yet. The gitea/gitlab default templates chained
`git push -u origin HEAD` unconditionally after linking the remote,
which fails immediately regardless of the remote ("src refspec HEAD
does not match any") -- reproduced by a real user hitting it on the
first mkrep --server call. Guard the push on HEAD actually resolving
to a commit; skipping it is the correct outcome (nothing to push yet),
and a real push failure once a commit exists still propagates.
2026-09-14 23:22:52 -04:00
rootiest 0a3b332d79 fix(git): prepend https:// to *_HOST vars in mkrep server detection
$GITEA_HOST/$GITLAB_HOST are bare hostnames (git.example.com); only
$GITEA_URL/$GITLAB_URL are expected to carry a scheme. mkrep was using
_HOST values as-is, producing a schemeless clone URL when only the
_HOST var was set.
2026-09-14 23:11:12 -04:00
rootiest 1a07e94002 feat(git): add --server/--check-existing to mkrep for gitea/gitlab/github
mkrep can now pick a git host from --server, $GIT_SERVER, and
$GITEA_URL/$GITEA_HOST/$GITLAB_URL/$GITLAB_HOST (base URL only -- type
must come from --server/$GIT_SERVER, so setting those URL vars for an
unrelated tool can't silently turn a plain mkrep call into a
remote-creating one). Before creating anything it checks via
gh/glab/tea whether the repo already exists and links instead of
recreating it; --check-existing runs just that check and reports
without touching remotes.
2026-09-14 21:06:28 -04:00
rootiest 5444532b5a docs: correct formatting error in documentation
- Removes a formatting error that broke the visual of
the --remote flag in both the documentation and
the function's help output.

-----

Impacted files:
functions/mkrep.fish
2026-09-12 20:41:29 -04:00
rootiest 4ab52329e1 fix(agy): map -r/--resume with session id to --conversation
agy uses different resume syntax than claude: -c/--continue always
resumes the most-recent session, while --conversation takes a
specific session id. Previously -r/--resume was blanket-mapped to
-c/--continue, breaking `agy --resume <id>` and `agy -r=<id>` style
invocations. Now bare -r/--resume (no id following) maps to
-c/--continue; -r/--resume given an id (via =id or a following
non-flag word) maps to --conversation(=id).
2026-09-09 23:24:29 -04:00
rootiest e6c6d2533d fix(git-clean): strip the '+' other-worktree marker too
git branch -vv marks column 1 with '+' (not '*') for a branch checked
out in another linked worktree. Only '*' was stripped from $1, so a
gone branch shown with '+' left a bogus "+" entry in $gone_branches
that then failed to delete: error: branch '+' not found.

Add a regression case reproducing the '+'-marked gone-branch line via
the existing git-clean mock handler in tests/test-network-fish.fish.
2026-09-09 21:14:04 -04:00
rootiest d93cb286ef Merge pull request 'feat(mkrep): add directory + git-repo scaffolding command' (#142) from feat/mkrep-command into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m49s
CI / build-docs (push) Successful in 3m35s
2026-09-10 01:01:37 +00:00
rootiest a1cf06624e fix(security): return full variable name in sponge_filter_secrets 2026-09-09 20:49:16 -04:00
rootiest b3082a3313 feat(mkrep): add directory + git-repo scaffolding command
mkcd's create-and-cd behavior plus git init, remote linking, and
optional remote creation via a user-configured command template
($MKREP_REMOTE_CMD, {name}/{user} placeholders).
2026-09-09 20:38:09 -04:00
rootiest e40d67df59 style: format fish files with fish_indent 2026-09-09 20:27:48 -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 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
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 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 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 6b2cc3aefc Merge remote-tracking branch 'origin/main' into fix/dops-docker-and-header-gaps 2026-09-08 01:39:44 -04: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 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 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 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 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
rootiest d75bf4f1b4 refactor: use __fish_palette in small utility functions (2/2)
25 duplicated declarations replaced by 7 calls. Completes the 33 structural
conversions: 167 declarations removed, 33 calls added, and --structural
confirms every one of the 33 diffs is declaration removals plus inserted
calls only -- no rendering text touched.

functions/y.fish is the one file in the repo with no trailing newline on
main; its original file ending is preserved rather than normalised.
2026-09-07 20:08:45 -04:00
rootiest 3d826c5407 feat(help): header-driven --help for terminal and session functions
spwin, tab, hist, bkg, wake-lock and fc take the standard first-arg-only
call site, so `wake-lock rsync --help` still runs rsync.

split takes --help only: its own ARGUMENTS documents -h as --horizontal,
so a literal test replaces the renderer's own flag check. Verified that
`split -h` still selects a horizontal split and never prints help.
2026-09-07 20:07:25 -04:00
rootiest 3399149cba feat(help): header-driven --help for system-info functions
gip, gip4, gip6, ports, swapstat, sbver and steam-dl. All ignored $argv,
so --help previously ran the query or the launcher. sbver keeps its own
--brief flag: only argv[1] is inspected and --brief is not a help flag,
so the body still runs for it.
2026-09-07 20:06:23 -04:00
rootiest f3f6e6356b refactor: use __fish_palette in small utility functions (1/2)
37 duplicated declarations replaced by 8 calls. Output strings untouched;
byte-identical across 29 harness cases.
2026-09-07 20:06:20 -04:00
rootiest a824005893 refactor(config-settings): draw the value pages through the frame
Width tier, title border, boolean badge, cursor cell and row line now come
from __config_settings_frame. The type badges (PATH/INT/LIST/STR) stay
here -- they are this page's own vocabulary, used nowhere else -- and so
does the STR default arm, which covers a user-settable value rather than
being dead code.

Introduces the `shorten` fit policy: these fields hold arbitrary user
values and want an ellipsis, unlike the toggle page's per-tier text. The
inline editor keeps `pad`, because its field is already length-constrained
and carries a reverse-video caret whose escapes string shorten miscounts.

The panel stays exactly 16 lines -- chrome(6) + nrows + blanks -- so
config-settings.fish's panel_h and its erase are untouched.

Rendering unchanged: 377/377 render cases byte-identical, golden untouched.
Code lines 123 -> 98 (-25).
2026-09-07 20:06:14 -04:00
rootiest b0c6d7f7f5 refactor: use __fish_palette in system and package functions
44 duplicated declarations replaced by 8 calls. Output strings untouched.

fish-deps, upgrade and wake-lock have no --help path and are deliberately
NOT added to the harness case list -- upgrade and wake-lock have side
effects, and runtime coverage is not worth mutating state during tests.
They are covered by --structural, which proves mechanically that no
rendering line changed.
2026-09-07 20:05:23 -04:00
rootiest 71574b5030 feat(help): header-driven --help for listing presets
lD, lsr, lss, lstree, lt, ltr, lx and ld. None shadows a real binary, so
there is no disabled-fallback contract to honour, and `eza --help`
documented eza rather than the preset that is these functions' entire
content. First-arg-only interception leaves `lt -la --help` passing
through to eza untouched.
2026-09-07 20:05:15 -04:00
rootiest ad9926861b refactor(config-settings): draw the sub-category page through the frame
Width tier, title border, badge, cursor cell and row line now come from
__config_settings_frame, and the hand-verified title dash count
(iw - L - S - 22) is derived from the segment's visible width instead.

Introduces the `cut` fit policy, which is this page's documented
divergence from the category list: its labels and descriptions are static
data from __config_settings_subcats rather than per-tier authored text,
and several exceed the narrower tiers' fields. `string pad` only ever
grows a string, so they are truncated before padding. That reason now
lives in two places -- the frame's NOTES and each call site -- and the
DESCRIPTION block stating it here is unchanged.

Drops label_w and desc_w, both left assigned and never read once the
frame derives the field width; the comment recording the 13-wide label
field and how the description field absorbs it stays.

Rendering unchanged: 377/377 render cases byte-identical, golden untouched.
Code lines 85 -> 49 (-36).
2026-09-07 20:04:44 -04:00
rootiest 29afe5c89f refactor: use __fish_palette in the config-settings TUI
27 duplicated declarations replaced by 5 calls. Output strings untouched.

Four of these five files have no --help path, so the byte-identity harness
cannot reach them. Covered instead by driving the real TUI under a pty on
both the baseline and converted trees with an identical paced keystroke
feed (down x3, Tab, Enter, Esc, Tab, down, q):

  9 redraws, 15068 bytes, byte-identical on both sides
  c_sel (ESC[35;1m) x9, c_hi (ESC[37;1m) x8, c_head (ESC[36;1m) x9
  all four draw helpers reached -- draw, pagetab, draw_subcat
  ("cascade default", "(category)"), draw_value ("Allow prev")
2026-09-07 20:04:26 -04:00
rootiest 037588ecf6 fix(help): stop eight functions executing on --help
These eight ignore $argv entirely, so passing --help ran the command
instead of describing it. The new check shadows every external binary
they reach with a recording stub on PATH and fails if anything is
invoked. Before the fix it reported, verbatim:

    cleanup --help EXECUTED: pacman -Qtdq
    fzf-update --help EXECUTED: git clone --depth 1 https://github.com/junegunn/fzf.git /tmp/.../.fzf
    limine-edit --help EXECUTED: sudoedit /boot/limine.conf sudo limine-enroll-config sudo limine-mkinitcpio sudo sbctl sign-all
    lock --help EXECUTED: loginctl lock-session
    screensleep --help EXECUTED: busctl --user call org.kde.kglobalaccel ... invokeShortcut s Turn Off Screen
    sudo-toggle --help EXECUTED: sudo stat -c %s /etc/sudoers.d/nofail-toggle sudo tee /etc/sudoers.d/nofail-toggle
    tmux-clean --help EXECUTED: tmux list-sessions -F #{session_name} #{session_attached}
    upgrade --help EXECUTED: paru -Syu --noconfirm

cleanup's log line is the read that precedes `sudo pacman -Rns $orphans`,
which the stub suppressed by returning no orphans; on a real machine with
orphans present the removal ran.

Each now answers --help from its own comment header. The call site is the
first statement of the body, above the C4 guard, so help stays reachable
when the component is disabled and nothing side-effecting runs first.
2026-09-07 20:04:02 -04:00
rootiest 889f8e0768 refactor(config-settings): draw the category list through the frame
Width tier, title border, badge, cursor cell and row line now come from
__config_settings_frame. The per-tier description sets stay here -- they
are this page's data, authored to fit each tier's field exactly -- and are
selected by width rather than by re-testing $COLUMNS.

Rows pass the `pad` fit policy explicitly. That is a decision, not a
default: `cut` would be a byte-for-byte no-op on these strings today and
would silently discard the property that they are tuned to their field.

Drops c_hi, which was assigned and never read.

Rendering unchanged: 377/377 render cases byte-identical, golden untouched.
Code lines 114 -> 80 (-34).
2026-09-07 20:02:48 -04:00
rootiest e64e964666 refactor: use __fish_palette in agents and auto-pull functions
34 duplicated declarations replaced by 5 calls. Output strings untouched;
byte-identical across 29 harness cases.
2026-09-07 20:02:15 -04:00
rootiest 2ac8e5c0d4 feat(config-settings): add __config_settings_frame shared renderer
The width tier, title-border arithmetic, ON/OFF/DEFAULT badge, cursor cell
and table row are currently hand-copied across the three draw functions.
This adds them once, with the two geometry identities derived rather than
hand-maintained: a row's chrome is a fixed 21 columns, so field_w is
iw - 21 - label_w (reproducing both iw-33 and iw-34), and a title border is
dashes = iw - visible(segment) - 1 (reproducing all three of iw-23,
iw-len-3 and iw-L-S-22).

The frame owns no page height. Every verb prints exactly one line or
fragment, so the fixed-16 category and value pages and the dynamic
7+n sub-category page keep their heights, and config-settings.fish's
erase is unaffected.

No caller yet, so rendering cannot move: the golden's existing page
section is byte-identical (verified with cmp -n over its previous size)
and the file only gains frame-verb cases appended after it.
2026-09-07 20:00:59 -04:00
rootiest a31a46bdc2 feat: add __fish_palette shared output palette helper
Declared --no-scope-shadowing with a bare set, so the 12 colour roles land
in the caller's scope under the same short names the consuming functions
already interpolate. Keeping the names means the conversion never edits an
output string in 33 of the 38 consumers.

No consumer is converted yet; byte-identity harness still reports 29/29.
2026-09-07 15:00:32 -04:00
rootiest 9077d9837e feat(help): add __fish_help_header runtime renderer
Parses a function's own man-page comment header at call time and prints
it as a help menu on stdout, so the documentation that already generates
Section 5 of the manual becomes reachable from the shell.

Reads the .fish source rather than the generated docs/fish-config.md, so
it cannot go stale between a header edit and a docs rebuild. Walks
backwards from the `function` line to collect the header, which resolves
multi-header files (fish-deps, gi, y) without reimplementing
manualtools._block_identity.

Returns 1 only when argv[1] is not a help flag; every other path prints
and returns 0. A return of 1 hands control back to the caller's body.

Nothing calls it yet.
2026-09-07 14:59:05 -04:00