Commit Graph
343 Commits
Author SHA1 Message Date
fishconfig-bot cde9396e70 chore(docs): regenerate manual, man page, and component registry 2026-09-22 07:14:38 +00:00
rootiest b0e09ef2a3 feat(functions): add -l/--local flag to mkrep
Add -l/--local flag to enforce strictly local repository creation in mkrep, overriding and ignoring any remote flags or environment variables that would link to or create a remote.
2026-09-22 02:54:14 -04:00
fishconfig-bot 694b084ff1 chore(docs): regenerate manual, man page, and component registry 2026-09-22 02:42:06 +00: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 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 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 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
fishconfig-bot 6fd25028d9 chore(docs): regenerate manual, man page, and component registry 2026-09-21 23:20:18 +00:00
rootiest e4df160fc0 docs(key-bindings): document ctrl-alt-u empty-prompt history recall 2026-09-21 19:07:02 -04:00
fishconfig-bot 21ef25bb56 chore(docs): regenerate manual, man page, and component registry 2026-09-21 07:03:41 +00:00
rootiest 0cd4787674 Merge branch 'main' into feat/win32yank-wsl2-clipboard 2026-09-21 06:55:47 +00: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 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
fishconfig-bot 02c060f9f1 chore(docs): regenerate manual, man page, and component registry 2026-09-19 04:56:58 +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
fishconfig-bot b8e97d7223 chore(docs): regenerate manual, man page, and component registry 2026-09-16 23:45:42 +00:00
fishconfig-bot 30a3288141 chore(docs): regenerate manual, man page, and component registry 2026-09-16 23:25:57 +00:00
fishconfig-bot 6aebf5e189 chore(docs): regenerate manual, man page, and component registry 2026-09-16 08:26:00 +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
fishconfig-bot 311efdce68 chore(docs): regenerate manual, man page, and component registry 2026-09-16 08:05:10 +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
fishconfig-bot f445ec5426 chore(docs): regenerate manual, man page, and component registry 2026-09-15 03:44:31 +00:00
fishconfig-bot 9821e64ae4 chore(docs): regenerate manual, man page, and component registry 2026-09-14 23:04:56 +00:00
Claude 2c1aa52871 fix(docs): render function entries per-section for the Starlight site
Function entry pages previously reused the man-page pipeline's single
indented block plus its paragraph-guessing heuristics (_is_prose's
per-line word count, _as_table's 2+ row minimum), so whether a
Description, Arguments, or Exit Status section landed as flowing
prose, a table, or an unhighlighted code block depended on incidental
shape -- a short wrapped line, a single argument, a narrow column --
rather than which section it was. `cat`, `copy`, and `ltr` each ended
up formatted differently for no functional reason.

Add render_entry_site, a site-only renderer that builds each entry
straight from the parsed function header instead of re-deriving
structure from indented text: every present section (Synopsis,
Description, Arguments, Exit Status, Returns, Notes, Example) gets its
own `###` heading, Arguments/Exit Status become a table via the new
_kv_rows column parser (which also fixes narrow single-space columns
and name-only rows with wrapped continuations), and everything else is
unwrapped into normal paragraphs. The man-page/pandoc path
(render_entry, build_concat) is untouched.
2026-09-14 18:56:25 -04:00
fishconfig-bot ecfcea5106 chore(docs): regenerate manual, man page, and component registry 2026-09-10 03:58:16 +00:00
Gitea Actions 6199caf0d0 chore(docs): regenerate manual, man page, and component registry 2026-09-10 01:11:47 +00:00
rootiest 2dc978e719 fix(docs): align registry generator output with fish_indent 2026-09-09 20:27:21 -04:00
Gitea Actions b59318e957 chore(docs): regenerate manual, man page, and component registry 2026-09-09 19:59:39 +00:00
Gitea Actions 69de224cbf chore(docs): regenerate manual, man page, and component registry 2026-09-09 04:49:54 +00:00
rootiest 8e4b719425 fix(docs): title distro code blocks and fix missed shell highlighting (#140)
CI / github-mirror (push) Skipped
CI / test (push) Successful in 2m19s
CI / build-docs (push) Successful in 3m46s
2026-09-09 04:43:50 +00:00
Gitea Actions d4c40519e5 chore(docs): regenerate manual, man page, and component registry 2026-09-08 16:50:35 +00:00
rootiest d49d90a2de chore(docs): regenerate manual
docs/fish-config.1 is left for CI to regenerate: the local pandoc is 3.10.2
against CI's 3.1.3, so rebuilding it here emits ~4700 lines of formatter
churn unrelated to this change. The component registry rebuilt identically.
2026-09-07 20:01:24 -04:00
rootiest 85c753aaa6 docs: point tailscale and cheat completions at completions/
Follows the relocation out of conf.d/. The completions/ branch of the file
tree is expanded from the real directory at build time, so it needs no
hand-written children. Generated docs are regenerated separately.
2026-09-07 19:59:53 -04:00
Gitea Actions 08e66c81ca chore(docs): regenerate manual, man page, and component registry 2026-09-04 21:28:58 +00:00
rootiest e6d3fd80b3 style(docs): add fish to logo icon 2026-09-04 17:14:17 -04:00
rootiest 54375a9530 style(docs): match Gitea icon size to GitHub's 24px (1.5rem) 2026-09-04 16:50:16 -04:00
rootiest 2ee2806e01 fix(docs): make the Gitea icon override actually apply, tune GitHub to 1.5x
UnoCSS's .i-pajamas:gitea rule is unlayered CSS; our override lived in
@layer starlight.core, and unlayered rules always beat layered ones
regardless of specificity or source order. The Gitea icon has silently
stayed at UnoCSS's 1em default since it was first added -- confirmed via
computed style in the browser, not just reading the stylesheet source.
!important restores the override across the layer boundary.

Also drops the GitHub icon from 2x to 1.5x per visual feedback.
2026-09-04 16:49:04 -04:00
rootiest 23cae4bd10 chore(docs): regenerate man page
pandoc wasn't available when this branch's earlier commit ran
build-manual.py --concat; regenerate docs/fish-config.1 from the
current docs/fish-config.md now that it is.
2026-09-04 16:43:27 -04:00
rootiest 8c21d34943 style(docs): double the Gitea/GitHub header icon size
--sl-nav-height is a fixed CSS var, not driven by icon content, so both
icons can grow without changing the header bar's height.
2026-09-04 16:43:27 -04:00
rootiest 4912c4052f feat(docs): add GitHub mirror icon, README-sourced doc sections, auto-generated TOC
Adds a GitHub social icon to the docs site header alongside the existing
Gitea one, and documents in the README's Contributing section that
git.rootiest.dev is the base repo while the GitHub copy is a one-way
mirror, so forks/issues/PRs should go through Gitea.

Adds Testing, Contributing, Attribution, and License sections to the
manual/man page/site, sourced directly from README.md via a new
`<!-- README: Heading -->` placeholder mechanism in build-manual.py, so
the README stays the single source of truth for those sections instead
of a hand-maintained copy drifting out of sync.

Also converts docs/manual/00-table-of-contents.md from a hand-typed list
to a generated one (mt.walk()-driven), fixing a numbering drift where
Components Reference was omitted and every section after it was off by
one relative to its own manTitle.
2026-09-04 16:34:22 -04:00
Gitea Actions c5bbbf06e3 chore(docs): regenerate manual, man page, and component registry 2026-09-04 02:52:27 +00:00
rootiest 28a88a9bdf feat(privacy): add DO_NOT_TRACK and DISABLE_TELEMETRY env vars under C3 privacy 2026-09-03 22:35:05 -04:00
Gitea Actions d278a47b32 chore(docs): regenerate manual, man page, and component registry 2026-09-03 23:52:10 +00:00
rootiest d9b56790c5 fix(agents-vault): keep the knowledge walk and the launch push inside their bounds
The agy knowledge allowlist walked the store with `**` and copied with
plain cp, so a symlink inside the store was both followed and dereferenced.
The extension rule still bounded what kind of file was collected, but not
whose: a link to a home directory hands over settings.json, CLAUDE.md and
every cached .json in it, and those reached a commit. A link to / made the
walk itself unbounded, on the path that runs before every agent launch.
The tree is now walked a level at a time and nothing that is a symlink is
followed or copied.

Autopush had the same shape one layer out. Neither GIT_TERMINAL_PROMPT nor
GIT_ASKPASS closes a socket, and git has no connect timeout to set: against
a blackholed address a push took 135s with http.lowSpeedLimit and
http.lowSpeedTime set as well as without them. ssh can time itself out and
is now told to; the autopush pull and push are additionally capped with
timeout(1). An explicit --push stays uncapped, since it is watched and has
to report what a real transfer really did.

Also: scaffold /.migrate-stash into .gitignore beside /.adopt-stash, which
the comment already claimed was covered; and drop the live memory path
during a slug migration only when it is a link. Reached from the
path-derived fallback candidate it can be a real populated directory, where
rm -f correctly refuses -- but said so in rm's voice, so a --silent run that
had succeeded printed what read as an error.
2026-09-03 18:56:46 -04:00