Commit Graph
919 Commits
Author SHA1 Message Date
rootiest 6156aa7cb9 Merge pull request 'fix: CI build-docs link validation + non-portable cp -n warning' (#164) from fix/classification-schema-link-validation into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 4m28s
CI / build-docs (push) Successful in 4m10s
2026-09-22 02:52:42 +00:00
fishconfig-bot 694b084ff1 chore(docs): regenerate manual, man page, and component registry 2026-09-22 02:42:06 +00:00
rootiest 86aecf0d9d fix(functions): use cp --update=none instead of non-portable -n
GNU cp warns that -n's behavior is non-portable and may change; the
long form is stable and silences the CI warning.
2026-09-21 22:33:44 -04:00
rootiest 18e04dd5fb fix(docs): point classification-schema link at Gitea, not a relative path
The schema file lives at docs/function-classification-schema.md but is
not a published Starlight page, so a relative link resolves fine in the
manual source tree but breaks once copied into
docs/site/src/content/docs/ — starlight-links-validator failed CI's
build-docs job on it. Rewrite it as an absolute Gitea blob URL, the same
pattern _rewrite_repo_links already uses for CONTRIBUTING.md/LICENSE.
2026-09-21 22:33:44 -04:00
rootiest abdb9cfacf Merge pull request 'feat: CLASSIFICATION function-header field + shadow-classification lint' (#163) from feat/classification-header-field into main
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m52s
CI / build-docs (push) Failing after 3m29s
Reviewed-on: #163
2026-09-22 02:12:56 +00:00
rootiest 2367f5749d docs(index): add config-help keyword aliases for CLASSIFICATION
The CLASSIFICATION schema (docs/function-classification-schema.md) had
no path into config-help's lookup: no fish-config.index keyword, and
the only in-pipeline section (the C1 doc's "For function authors")
doesn't contain the word classification itself, so even the
normalized-heading-scan fallback missed it on that term. Two aliases
added, pointing at the existing section -- no heading renamed, matching
the index file's own stated purpose.
2026-09-21 21:36:39 -04:00
rootiest 3414f81cb6 feat(tests): add shadow-classification lint; fix real cp/mv/less bugs
New Phase 1b in tests/run-tests.fish: catches a bare C1-shadowed-command
call in a functions/*.fish body with no matching uses-shadow(name) or
self-limiting(name) in that function's own CLASSIFICATION header. This
is exactly the check discussed after the rm and cd audits -- runtime
auto-unwrapping isn't viable in fish (there's no hook finer than
shadowing itself, and rewriting behavior invisibly at runtime is its
own footgun); a static lint using the CLASSIFICATION tag as the
declared-intentional marker is. Scoped to functions/*.fish only: the
one-function-per-file convention there makes body extraction exact
with no block-depth parser needed.

Added a new self-limiting(name) tag to the schema for the case a bare
call is safe not because the caller did anything, but because the
shadow's own logic already neutralizes the override: rm's and mkdir's
flag checks (verified precisely -- rm falls back to command rm for any
flag except a bare -r/-R/--recursive alone, which still routes to
trash; mkdir falls back to command mkdir -p for any flag, no
exception), and grep/fgrep/egrep/dir/vdir/cat's own tty auto-detection
(--color=auto, and bat's default color behavior -- verified
byte-identical to stock cat when piped, since bat also auto-disables
highlighting on a non-terminal). Explicit and durable rather than a
silent lint exemption: if a shadow's bypass condition is ever
weakened, every self-limiting site is one grep away instead of
silently wrong.

Running the first draft of the lint surfaced three more real bugs,
none previously audited:

- config-help.fish's --man pager path checks `type -q less` (proving
  it wants the real less binary specifically, for less-only -R/+N
  flag syntax) then called it bare, routing through our own
  $PAGER -> ov -> less -> more -> cat fallback chain instead -- which
  could hand those less-specific flags to a completely different
  program. Now command less.
- _fish_deps_install.fish and _fish_deps_update.fish's binary-upgrade
  paths cp a freshly downloaded binary over an already-installed one
  with no existence guard -- the update flow's target is guaranteed to
  already exist. Our cp shadow forces -i unconditionally (a plain
  alias, not flag-aware like rm's), so this would hang waiting on a
  confirmation prompt in any non-interactive run. Now command cp.
  Same two files' lazydocker install path piped curl output into bare
  bash, invoking our shell-switch wrapper instead of a plain
  subshell. Now command bash.
- agents-init.fish's AGENTS.md/CLAUDE.md relocation calls mv bare in
  four places; each is already guarded by a preceding test -f check on
  the destination, so the -i alias was unlikely to ever fire in
  practice, but explicit command mv removes the reliance on that guard
  entirely rather than leaving it as the only thing standing between a
  file move and an unattended hang.

The remaining ~65 flagged call sites across ~24 files were reviewed
individually and tagged self-limiting(rm)/self-limiting(mkdir)
(verified flagged with -f/-rf or -p) and self-limiting(grep)/
self-limiting(cat) (verified piped, captured, or -q/-c; none display
color to a human), plus uses-shadow(ls) for two existence-check-only
calls (cffetch.fish, ffetch.fish) whose output is redirected to
/dev/null.
2026-09-21 21:26:55 -04:00
rootiest 373917d002 fix(functions): stop routing deterministic cd through zoxide's shadow
Verified an agy audit of every bare cd call by hand. conf.d/zoxide.fish
gates alias cd=z behind status is-interactive plus the C1 toggle, and
_zoxide_hook fires on --on-variable PWD, so it tracks a directory
change no matter how PWD got there -- switching to builtin cd loses
zoxide's frecency tracking nothing.

mkcd.fish's single cd and mkrep.fish's 9 (entering the new repo, plus
8 rollback-to-original-directory sites on error paths and --no-cd)
were both intended as exact, deterministic path navigation, never a
zoxide query. The real risk was mkrep's rollback path: if $orig_pwd
ever failed cd's own -d check for any reason, z's fallback branch
queries zoxide for a *guessed* frecent directory instead -- landing a
failed run's cleanup in a directory the caller never asked for, not
the one it was trying to return to. All 9 sites now use builtin cd.

Corrected both functions' CLASSIFICATION from uses-shadow(cd) to
bypasses-shadow(cd) -- neither wanted zoxide's query, they were tagged
that way only because the header audit recorded what the code was
doing at the time, not what it needed.

integrations/fzf.fish's fzf-alt-c-widget also calls bare cd, but it's
vendored upstream code (PatrickF1/fzf.fish) and is itself an
interactive directory-jump binding, not a script/automation caller --
left alone, same as fisher.fish's rm calls.
2026-09-21 21:26:52 -04:00
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
fishconfig-bot 6fd25028d9 chore(docs): regenerate manual, man page, and component registry 2026-09-21 23:20:18 +00: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
fishconfig-bot 21ef25bb56 chore(docs): regenerate manual, man page, and component registry 2026-09-21 07:03:41 +00: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
fishconfig-bot b59c73fc53 chore(docs): regenerate manual, man page, and component registry 2026-09-21 06:41:20 +00: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
fishconfig-bot 7a46881ca4 chore(docs): regenerate manual, man page, and component registry 2026-09-21 06:04:49 +00: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
fishconfig-bot 02c060f9f1 chore(docs): regenerate manual, man page, and component registry 2026-09-19 04:56:58 +00: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
fishconfig-bot b8e97d7223 chore(docs): regenerate manual, man page, and component registry 2026-09-16 23:45:42 +00: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
fishconfig-bot 30a3288141 chore(docs): regenerate manual, man page, and component registry 2026-09-16 23:25:57 +00: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