Commit Graph
100 Commits
Author SHA1 Message Date
rootiest 100cb478bc fix(functions): stop leaking scratch files to trash via bare rm
Verified an agy audit of every bare rm call (the trash-routing C1
shadow) by hand rather than trusting its report. Confirmed correct:
scrub.fish's custom_rm strategy and logs.fish's Ctrl-D delete both
deliberately want trash for a real, user-facing deletion.

Confirmed and fixed three cases where a function's own throwaway
scratch file was going to the user's trash instead of being wiped:
fc.fish's edited-command tmpfile, dng2avif.fish's intermediate PNM
(inconsistent with its own failure-path cleanup two lines up, which
already used -f), and _scrollback_prune_junk.fish's junk log files
(its sibling _prune_terminal_logs.fish already documents this exact
pitfall in its header).

Also went further than the report and classified every bypasses-shadow(rm)
caller found by grep that had never been audited at all:
config-settings.fish and edit.fish (own scratch cleanup, no destructive
data at stake) and key-crypt.fish (--remove deletes the user's real
input file after encryption, genuinely destructive, already documented
in its own header as 'not a secure wipe'). Corrected scrub.fish's tag,
which was missing uses-shadow(rm) for its deliberate trash-routing
branch alongside the bypass branch it already had tagged.

Added a note to the schema doc: rm's flag-based fallback lives inside
the shadow itself, so a bare rm -f/rm -rf call is not the caller
bypassing anything -- only an explicit command rm/builtin rm earns
the tag. This is why dng2avif.fish's fix needed no CLASSIFICATION
change: it already used rm -f, which was never actually the bug --
the missing -f on line 122 was.
2026-09-21 21:26:50 -04:00
rootiest 069a1f7743 docs(classification): move schema out of gitignored AGENTS/, into docs/
AGENTS/functions/CLAUDE.md is git-ignored local agent state, not part
of the repo -- a comment/commit referencing it as the schema's home
points contributors at a file they can't see. The canonical CLASSIFICATION
schema now lives at docs/function-classification-schema.md (tracked),
with CONTRIBUTING.md's existing function-header-conventions section
extended to introduce it, and the C1 shadow doc's pointer updated to
match. AGENTS/functions/CLAUDE.md keeps only a one-line pointer to the
tracked file instead of duplicating the definitions.
2026-09-21 21:26:47 -04:00
rootiest 859f14a6e9 feat(functions): tag CLASSIFICATION across functions/ and conf.d/
Audits every function's interaction with the C1-shadowed commands
(uses-shadow/bypasses-shadow) and general hazards (destructive,
network, blocking-prompt) per the CLASSIFICATION schema.

Delegated the initial mechanical sweep to agy, then reviewed every
file by hand: fixed a systemic double-blank-comment-line formatting
bug from the delegate pass, and corrected several judgment errors
found on review -- three false blocking-prompt tags where a fish
'read' was consuming piped input rather than waiting on a terminal
(open-url.fish, sbver.fish, play-media.fish, now untagged entirely),
a blocking-prompt tag on mkrep.fish despite its documented --yes
escape hatch, an untagged read in jobrunner.fish's own baseless
blocking-prompt claim (removed, along with a destructive tag on
cleanup of its own mktemp output -- the schema explicitly excludes
that), the same own-output-cleanup false positive on
_zellij_dump_log.fish's destructive tag, an interactive fzf-gated
confirmation on logs.fish and replay.fish's piped read misread the
same way as the first three, and a uses-shadow(mkdir) on mkcd.fish
that actually belongs to the _fish_mkdir_p helper it delegates to,
not to mkcd itself.
2026-09-21 21:26:44 -04:00
rootiest aef686af86 feat(docs): add CLASSIFICATION function-header field, rename history shadow
Rename the C1 history() shadow to pretty-history so it never collides
with the fish builtin -- every function expecting stock history
semantics (search, --max, merge, ...) would otherwise silently break.
hist.fish, which relied on the shadow's timestamp formatting, now
requests it explicitly via builtin history --show-time.

Add a CLASSIFICATION doc-header label so a function can declare its
interaction with C1-shadowed commands (uses-shadow/bypasses-shadow)
and general hazards (destructive, network, blocking-prompt) for
anyone deciding to disable an opinionated category or call the
function from automation. Wired into the manual/site build pipeline
(manualtools.py, build-manual.py) and the C1 shadow doc gets a new
"For function authors" bypass-mechanism reference table
(command/builtin/__original_help, and which shadows have no real
bypass target at all).
2026-09-21 21:26:40 -04:00
rootiest 392e6593ac fix(keybind): bypass history shadow in ctrl-alt-u empty-prompt recall
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m3s
CI / build-docs (push) Successful in 4m5s
The tricks.fish C1 history() shadow drops all args and always shows
timestamps. Calling plain history --max 1 hit that shadow, dumping the
entire history with timestamps instead of one plain entry. Use builtin
history --max 1, matching the convention already used by fc.fish,
_fzf_search_history.fish, and bash_expands.fish.
2026-09-21 19:29:37 -04:00
rootiest e2c25fac64 Merge pull request 'feat(keybind): recall last history entry on empty prompt for ctrl-alt-u' (#162) from feat/replace-command-token-empty-recall into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m11s
CI / build-docs (push) Successful in 3m55s
2026-09-21 23:14:16 +00:00
rootiest e4df160fc0 docs(key-bindings): document ctrl-alt-u empty-prompt history recall 2026-09-21 19:07:02 -04:00
rootiest 2630342d81 feat(keybind): recall last history entry on empty prompt for ctrl-alt-u 2026-09-21 19:06:59 -04:00
rootiest 85d13a6aaf Merge pull request 'feat(clipboard): fill the WSL2 clipboard gap with win32yank' (#161) from feat/win32yank-wsl2-clipboard into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m1s
CI / build-docs (push) Successful in 3m52s
2026-09-21 06:57:47 +00:00
rootiest 0cd4787674 Merge branch 'main' into feat/win32yank-wsl2-clipboard 2026-09-21 06:55:47 +00:00
rootiest 6da6999b90 docs(readme): sync OS compatibility section with win32yank fallback 2026-09-21 02:53:11 -04:00
rootiest 59d33e8c76 feat(fish-deps): add win32yank installer for WSL2
New optional-tier catalog entry, gated on WSL2 detection
(/proc/sys/kernel/osrelease) so it never surfaces on a plain Linux
box's install/sync prompts, only in the informational status
listing. Downloads the x86_64 binary from GitHub releases to
~/.local/bin/win32yank.exe; fish-deps update refreshes an
already-installed copy the same way.
2026-09-21 02:52:26 -04:00
rootiest b2be858d8c refactor(clipboard): share provider detection across y/p/paste/hist
Extracts _fish_clipboard_copy and _fish_clipboard_paste so the
wl-copy/xclip/win32yank fallback chain lives in one place instead of
four near-duplicates. hist now goes through the same chain, so it
also gets the xclip (X11) fallback it was missing before, alongside
win32yank on WSL2.
2026-09-21 02:52:17 -04:00
rootiest 07613c7889 feat(clipboard): add win32yank fallback for WSL2
y, p, paste, and hist now try win32yank.exe after wl-copy/wl-paste
and xclip, so clipboard access works under WSL2 once win32yank is
installed and reachable through WSL interop. Updates the OS
compatibility docs accordingly.
2026-09-21 02:44:12 -04:00
rootiest 2686415db5 Merge pull request 'docs: state OS compatibility explicitly (Linux-only)' (#160) from docs/os-compatibility into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m57s
CI / build-docs (push) Successful in 3m50s
2026-09-21 06:35:36 +00:00
rootiest 7c59caebce docs: state OS compatibility explicitly (Linux-only)
Adds an OS Compatibility section to the README and the Installation
manual page: developed and tested on Arch Linux, macOS and Windows
unsupported, with the specific Linux-only calls that back that claim
(systemd-inhibit, zramctl/swapon, sbctl, wl-copy/xclip with no
pbcopy/clip.exe fallback, GNU-only stat/numfmt flags) and the baseline
packages (git, gpg, tar, coreutils) assumed present rather than tracked
by fish-deps.

Adds os/os-compatibility/operating-system/compatibility/linux/macos/
mac/windows/wsl keywords to fish-config.index so `help config os` and
friends jump straight to it.
2026-09-21 02:33:31 -04:00
rootiest cd8d6fce38 Merge pull request 'feat(functions): derive the shared help palette from the fish theme' (#159) from feat/theme-driven-help-palette into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m5s
CI / build-docs (push) Successful in 4m24s
2026-09-21 05:53:21 +00:00
rootiest 1608b9b527 Merge pull request 'feat(functions): port key-crypt to a fish function' (#158) from feat/key-crypt-fish-function into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m58s
CI / build-docs (push) Failing after 4m12s
2026-09-21 05:52:15 +00:00
rootiest 0b4ab30b40 test(session): catch --help color regressions against the live theme
Adds two checks to the shared-palette section, extending the existing
colored list with the functions converted in this branch (config-help,
fish-deps, gi, git-clean, mkrep):

- Every listed function's --help must contain the session's own c_head
  and c_cmd escapes -- not just some escape sequence, which the existing
  presence check already covered but which a wrong-role bug (still some
  color, just not the right one) sails through undetected.
- Every escape sequence found in --help output must be one of
  c_reset/c_head/c_cmd/c_flag/c_arg/c_dim. c_warn/c_err/c_ok/c_accent/
  c_sel/c_hi are legitimate elsewhere but never in --help text; reaching
  for one there is always a bug, which is exactly what logs and
  smart_exit did (static c_accent green for the command name).

Verified against the pre-fix smart_exit.fish (58d5cf3~1): both checks
fail on it -- missing c_head on "Options:", and a disallowed \e[32m for
the command name -- confirming this isn't a tautological pass.
2026-09-21 01:49:34 -04:00
rootiest 58d5cf3fae fix(functions): correct palette roles in logs and smart_exit help text
Both had headings (Usage:/Options:/Keys in fzf:) left completely
uncolored, flags rendered in the argument color instead of the flag
color, and the command name hardcoded to a static ANSI green (c_accent)
instead of the theme-derived c_cmd -- the last of the raw, unconverted
color usage the audit turned up.
2026-09-21 01:44:14 -04:00
rootiest cbf1a0a9a2 fix(functions): color tool/flag names in config-help's pager fallback chain
ov, bat, man, less, and cat were plain text, and man -l's -l wasn't
styled as a flag, in an otherwise-converted help block.
2026-09-21 01:39:01 -04:00
rootiest 1dcbd6498c fix(functions): color the command name in config-help's USAGE section
The USAGE block's "help config" was left as plain text while every
other section (EXAMPLES) correctly colored it with c_cmd.
2026-09-21 01:36:30 -04:00
rootiest 610714476d feat(functions): derive the shared help palette from the fish theme
__fish_palette's c_cmd/c_flag/c_arg/c_dim now read fish's own highlighter
variables (fish_color_command/option/param/autosuggestion) when set, and
c_head reads the pager's fish_pager_color_prefix, so --help text matches
whatever the user's actual theme renders at the prompt instead of a fixed
guess at it. Each role keeps its previous static value as a fallback
(e.g. a --no-config script, or the theme variable being unset or empty).

Routes __fish_help_header's title/section colors and config-help's,
gi's, and git-clean's own hand-rolled --help blocks through the palette
instead of their hardcoded set_color calls, which fixes the
--help-vs-actual-prompt color mismatch across most of the config's
functions in one place (__fish_help_header backs the majority of
user-facing --help output).

Also fixes role mismatches found along the way: several already-palette
functions (dockup, fish-deps, pkg, play-media, dng2avif) were coloring
their <placeholder>/[bracket] argument text with c_dim instead of c_arg,
and scrub's "Aggressive Targets:" heading was still a hardcoded
set_color call sitting next to an otherwise-converted help block.
2026-09-21 01:19:38 -04:00
rootiest fc68486aaf feat(functions): port key-crypt to a fish function
Bring key-crypt in from ~/scripts as functions/key-crypt.fish instead of a
standalone --no-config script. Converts exit-based control flow to return,
drops the fish_exit/signal traps (global-scope hijack risk in a shared
shell) in favor of an outer wrapper that cleans up the temp file and all
scratch globals after every call, and routes rm/mv/mkdir through `command`
so config-level wrapper functions can't intercept them.

--install now writes a small standalone wrapper script (source this
function file, call key-crypt) to ~/.local/bin/key-crypt instead of
copying the file itself, since a fish function can't be exec'd directly by
the .desktop Open With entries. --uninstall is unchanged aside from the
command-prefixed rm.
2026-09-21 01:08:43 -04:00
rootiest 348a0453a9 Merge pull request 'docs: rename "documentation website" heading to "project wiki"' (#157) from docs/rename-manual-heading-wiki into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m58s
CI / build-docs (push) Successful in 3m52s
Reviewed-on: #157
2026-09-19 04:51:11 +00:00
rootiest eb22b1f27f docs: rename "documentation website" heading to "project wiki"
Missed in the prior terminology-sync pass; updates both the manual
source heading and the matching keyword-index entries.
2026-09-19 00:50:08 -04:00
rootiest 6292b12eca Merge pull request 'docs: rename "documentation site" to "project wiki"' (#156) from docs/rename-documentation-wiki into main
Reviewed-on: #156
2026-09-19 01:01:21 +00:00
rootiest d9410701e3 docs: rename "documentation site" to "project wiki"
Consistent terminology across README, CONTRIBUTING, and CI step name.
2026-09-18 20:57:53 -04:00
rootiest 00606ecdca Merge pull request 'fix(mkrep): make origin linking idempotent' (#155) from fix/mkrep-origin-idempotent into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m54s
CI / build-docs (push) Successful in 4m22s
Reviewed-on: #155
2026-09-16 23:39:28 +00:00
rootiest a09d3db800 fix(mkrep): make origin linking idempotent
Both link paths -- --remote and the --server/$GIT_SERVER link-existing
branch -- called `git remote add origin` bare. That fails with "remote
origin already exists" whenever the target is already linked, which is
the normal case for `mkrep .` against an existing checkout and for any
rerun against the same target. The failure took the whole call down with
exit 1.

Route both through a new _mkrep_add_origin helper: add when there is no
origin, accept and report when origin already points at the requested
URL, and refuse when it points elsewhere. A different URL is a different
repo, so repointing stays the caller's explicit decision rather than a
silent rewrite of a checkout mkrep may have been aimed at by mistake.
2026-09-16 19:36:34 -04:00
rootiest f901ec460f Merge pull request 'style(tests): satisfy fish_indent in the agents-vault suite' (#154) from fix/agents-vault-test-indent into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m15s
CI / build-docs (push) Successful in 4m0s
2026-09-16 23:19:41 +00:00
rootiest fb62005140 style(tests): satisfy fish_indent in the agents-vault suite
The gitignore-anchoring check quoted arguments fish_indent removes, so
run-tests.fish failed its indent lint and exited 1 on main even though all
753 assertions passed.
2026-09-16 19:15:30 -04:00
rootiest acbbd8d9e1 Merge pull request 'fix(agents-init): require a git repo to scaffold, and anchor the .gitignore fallback match' (#153) from fix/agents-init-nongit-and-gitignore-anchor into main
CI / github-mirror (push) Skipped
CI / test (push) Failing after 2m18s
CI / build-docs (push) Skipped
Reviewed-on: #153
2026-09-16 23:09:00 +00:00
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 a35ddf9e96 Merge pull request 'fix(mkrep): stop the test suite creating real repos, and confirm implicit ones' (#152) from fix/mkrep-implicit-remote-confirm into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m6s
CI / build-docs (push) Successful in 3m51s
Reviewed-on: #152
2026-09-16 08:20:01 +00: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 fc5ecb591b Merge pull request 'feat(md): add an md wrapper for MarkText, with an optional read-only sandbox' (#151) from feat/md-marktext-wrapper into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m20s
CI / build-docs (push) Successful in 4m19s
Reviewed-on: #151
2026-09-16 07:58:29 +00: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 35b2d0a16c Merge pull request 'feat(git): add --server/--check-existing to mkrep for gitea/gitlab/github' (#150) from feat/mkrep-server-detection into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m5s
CI / build-docs (push) Successful in 3m53s
Reviewed-on: #150
2026-09-15 03:38:37 +00: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 dfb5de5768 Merge pull request 'fix(docs): render function entries per-section for the Starlight site' (#149) from claude/starlight-doc-parsing-fix-voyyvi into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m1s
CI / build-docs (push) Successful in 3m32s
Reviewed-on: #149
2026-09-14 22:59:26 +00:00
rootiest 0736de384a Merge pull request 'feat(git): add pre-push hook to reject unsigned commits' (#148) from chore/pre-push-signature-check into main 2026-09-13 00:58:15 +00:00
rootiest 6c35dc4337 docs(contributing): document the pre-push signature hook opt-in
Contributors need to know .githooks/pre-push exists and how to point
their clone at it, since it isn't wired up automatically.
2026-09-12 20:56:48 -04:00
rootiest 35f024c159 feat(git): add pre-push hook to reject unsigned commits
Gittyup commits via libgit2 directly and never invokes gpg, silently
ignoring commit.gpgsign — root cause of an unsigned commit reaching
main. Adds a tracked .githooks/pre-push that rejects any push
carrying a commit with no signature or a bad signature, bypassable
with --no-verify. Wiring core.hooksPath to it is a per-machine
concern, done separately in user-dots, not shipped here.
2026-09-12 20:51:19 -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 679a703ab2 Merge pull request 'fix(ci): base64-encode the bot GPG key secret' (#147) from fix/ci-gpg-key-base64 into main 2026-09-10 04:00:24 +00:00
rootiest 4da176d507 fix(ci): base64-encode the bot GPG key secret
Raw multi-line armored key piped through 'echo | gpg --import' came out
CRC-corrupted (Invalid keyring) on the first real run -- something in the
secret/env round-trip mangles embedded newlines. Regenerated the bot key
(old one is unrecoverable, secrets are write-only) and store it
base64-encoded, decoded with base64 -d before import.
2026-09-09 23:52:40 -04:00
rootiest cc9531f19f Merge pull request 'fix(agy): map -r/--resume with session id to --conversation' (#146) from fix/agy-resume-conversation-syntax into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m53s
CI / build-docs (push) Failing after 3m38s
Reviewed-on: #146
2026-09-10 03:37:58 +00: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 2597b3ccd3 Merge pull request 'fix(ci): sign docs-regen commits with dedicated bot GPG key' (#145) from fix/ci-commit-signing into main 2026-09-10 02:48:51 +00:00
rootiest a751124b8b fix(ci): sign docs-regen commits with dedicated bot GPG key
actions@gitea was never a verified email on any Gitea account, so the
CI docs-regen commit (git commit && git push, done client-side in the
runner) could never show as verified regardless of server-side
[repository.signing] config -- Gitea only signs commits it generates
itself (merge button, web editor, wiki), never ones a client pushes.

Import a dedicated passphrase-less key for a new fishconfig-bot
account (verified email, no login) from the CI_GPG_PRIVATE_KEY repo
secret and sign with it instead.
2026-09-09 22:16:13 -04:00
rootiest 6732a71eb7 Merge pull request 'fix(git-clean): strip the '+' other-worktree marker too' (#144) from fix/clean-worktree-branch-marker into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 1m58s
CI / build-docs (push) Successful in 3m37s
2026-09-10 01:15:42 +00: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 3c621db727 Merge pull request 'test: expand test coverage across core functions, enforce strict fish_indent linting, and add network isolation harnesses' (#143) from test/network-edge-case-coverage into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m2s
CI / build-docs (push) Successful in 3m34s
2026-09-10 01:00:45 +00:00
rootiest 04edd6de89 test: add core utilities and security test suite 2026-09-09 20:50:21 -04:00
rootiest a1cf06624e fix(security): return full variable name in sponge_filter_secrets 2026-09-09 20:49:16 -04:00
rootiest c7d7cea8ae test: eliminate flakiness in rand_string casing test and improve history cleanup 2026-09-09 20:43:52 -04:00
rootiest ff4f7b3909 test: add string and commandline expansion test suite 2026-09-09 20:39:46 -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 d926a632d5 test: enforce fish_indent --check in test runner 2026-09-09 20:32:43 -04:00
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
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
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
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