feat: CLASSIFICATION function-header field + shadow-classification lint #163

Merged
rootiest merged 7 commits from feat/classification-header-field into main 2026-09-22 02:12:58 +00:00
91 changed files with 556 additions and 35 deletions
+12
View File
@@ -391,6 +391,7 @@ all optional except where noted:
| `CATEGORY` | **Required to appear in the manual at all** — see below. | | `CATEGORY` | **Required to appear in the manual at all** — see below. |
| `COMPONENT` | Only for functions gated by the [opinionated-component system](#opinionated-components). | | `COMPONENT` | Only for functions gated by the [opinionated-component system](#opinionated-components). |
| `DEPENDENCIES` | Other functions this one calls that a reader may want to look up. | | `DEPENDENCIES` | Other functions this one calls that a reader may want to look up. |
| `CLASSIFICATION` | Hazard/shadow-interaction tags — see below. |
| `SYNOPSIS` | One-line usage form. | | `SYNOPSIS` | One-line usage form. |
| `DESCRIPTION` | Prose description; can span multiple paragraphs. | | `DESCRIPTION` | Prose description; can span multiple paragraphs. |
| `ARGUMENTS` | Flags/positional args, one per line. | | `ARGUMENTS` | Flags/positional args, one per line. |
@@ -414,6 +415,9 @@ A full example (`functions/claude.fish`):
# DEPENDENCIES # DEPENDENCIES
# agents-init # agents-init
# #
# CLASSIFICATION
# bypasses-shadow(claude)
#
# SYNOPSIS # SYNOPSIS
# claude [ARGS...] # claude [ARGS...]
# #
@@ -443,6 +447,14 @@ If your function genuinely doesn't fit any of these, add a new
`docs/manual/05-functions/NN-your-category.md` stub (with frontmatter `docs/manual/05-functions/NN-your-category.md` stub (with frontmatter
matching its siblings) rather than force-fitting it into an existing one. matching its siblings) rather than force-fitting it into an existing one.
**`CLASSIFICATION` flags hazards and shadow interactions, optional and
omitted when nothing applies:** whether the function calls a
[C1-shadowed command](docs/manual/08-components-reference/01-c1-command-shadows.md)
bare wanting the override (`uses-shadow(ls)`) or bypasses it deliberately
via `command`/`builtin` (`bypasses-shadow(cat)`), and general hazards —
`destructive`, `network`, `blocking-prompt`. Full tag definitions and
placement rule: [`docs/function-classification-schema.md`](docs/function-classification-schema.md).
### Private/internal helper functions ### Private/internal helper functions
Functions named with a leading `_` (e.g. `_agents_init_ensure_gitignore`, Functions named with a leading `_` (e.g. `_agents_init_ensure_gitignore`,
+3
View File
@@ -24,6 +24,9 @@ __fish_config_op_enabled (status basename); or exit
# COMPONENT # COMPONENT
# autoexec/sync # autoexec/sync
# #
# CLASSIFICATION
# bypasses-shadow(cat)
#
# SYNOPSIS # SYNOPSIS
# __auto_pull_on_pwd (event handler, --on-variable PWD) # __auto_pull_on_pwd (event handler, --on-variable PWD)
# #
+6 -4
View File
@@ -97,11 +97,13 @@ if __fish_config_op_enabled (status basename) tricks-bang
end end
end end
# Fish command history override to show timestamps # Timestamped history view. Named pretty-history (not history) so it never
# Shadowing the history command is opinionated (C1 aliasing); when disabled, # shadows the fish builtin -- every function in this config that expects
# the function is never defined and fish's stock history behavior applies. # stock `history` semantics (search, --max, merge, ...) would otherwise
# silently break, which has happened more than once. Opinionated (C1
# aliasing); when disabled, the function is never defined.
if __fish_config_op_enabled (status basename) aliases-tricks if __fish_config_op_enabled (status basename) aliases-tricks
function history function pretty-history --description 'History with timestamps prepended to every entry'
builtin history --show-time='%F %T ' builtin history --show-time='%F %T '
end end
end end
+25
View File
@@ -703,6 +703,29 @@ ENTRY_HEADS = {
} }
def _classification_tags(raw: list[str]) -> list[str]:
"""Split a CLASSIFICATION body into its comma-separated tags.
A plain comma split (as `names()` uses for DEPENDENCIES) would break on
the commas inside `uses-shadow(rm, cp)`-style tags, so this only splits
on commas at paren depth 0.
"""
text = " ".join(raw)
tags: list[str] = []
depth = 0
start = 0
for i, ch in enumerate(text):
if ch == "(":
depth += 1
elif ch == ")":
depth = max(0, depth - 1)
elif ch == "," and depth == 0:
tags.append(text[start:i].strip())
start = i + 1
tags.append(text[start:].strip())
return [t for t in tags if t]
def render_entry(fn: dict[str, list[str]], used_by: list[str], link=None) -> str: def render_entry(fn: dict[str, list[str]], used_by: list[str], link=None) -> str:
"""Render one parsed function header as a manual entry body. """Render one parsed function header as a manual entry body.
@@ -739,6 +762,7 @@ def render_entry(fn: dict[str, list[str]], used_by: list[str], link=None) -> str
refs = [] refs = []
for label, values in ( for label, values in (
("Dependencies", names(fn.get("DEPENDENCIES", []))), ("Dependencies", names(fn.get("DEPENDENCIES", []))),
("Classification", _classification_tags(fn.get("CLASSIFICATION", []))),
("Used by", sorted(used_by)), ("Used by", sorted(used_by)),
): ):
if values: if values:
@@ -884,6 +908,7 @@ def render_entry_site(fn: dict[str, list[str]], used_by: list[str], link=None) -
refs = [] refs = []
for label, values in ( for label, values in (
("Dependencies", names(fn.get("DEPENDENCIES", []))), ("Dependencies", names(fn.get("DEPENDENCIES", []))),
("Classification", _classification_tags(fn.get("CLASSIFICATION", []))),
("Used by", sorted(used_by)), ("Used by", sorted(used_by)),
): ):
if values: if values:
+2
View File
@@ -291,6 +291,8 @@ network-shadow=### network
monitor=### monitor monitor=### monitor
shell-tools=### shell-tools shell-tools=### shell-tools
dev-tools=### dev-tools dev-tools=### dev-tools
classification=### For function authors
shadow-classification=### For function authors
c2=## C2 — Startup Side-Effects c2=## C2 — Startup Side-Effects
autoexec=## C2 — Startup Side-Effects autoexec=## C2 — Startup Side-Effects
startup=## C2 — Startup Side-Effects startup=## C2 — Startup Side-Effects
+101
View File
@@ -0,0 +1,101 @@
# Function CLASSIFICATION schema
This is the canonical definition of the `# CLASSIFICATION` function
doc-header label. It's referenced from code comments and commit messages —
link here, not to anything under `AGENTS/` (that tree is git-ignored local
agent state, not part of the repo).
See [Public function documentation header](../CONTRIBUTING.md#public-function-documentation-header)
in `CONTRIBUTING.md` for where `CLASSIFICATION` fits among the other header
labels, and [C1 — Command Shadows](manual/08-components-reference/01-c1-command-shadows.md)
for the full list of C1-shadowed commands this schema's shadow tags refer to.
## Format
Optional. Comma-separated tags from the closed set below, on the indented
body line directly under the label:
```fish
# CLASSIFICATION
# uses-shadow(ls), destructive
```
Omit the label entirely when nothing applies — omission means "nothing to
flag," not "not yet audited," so don't add it speculatively, and don't add
it empty as a placeholder.
## Tags
- **`uses-shadow(name[,name...])`** — calls a C1-shadowed command (see the
C1 doc linked above) bare, deliberately wanting the overridden behavior
(e.g. `ls` wanting eza's icons for a human to read).
- **`bypasses-shadow(name[,name...])`** — calls `command <name>`,
`builtin <name>`, or (for `help` specifically) `__original_help $argv`,
deliberately forcing stock behavior because the shadow's override would
break this function's logic: timestamps leaking into a parsed capture,
`-i` prompting on a path meant to run unattended, structural output
changes breaking a `string`/`sed` parse, etc.
- **`self-limiting(name[,name...])`** — calls a shadowed command bare, and
it's safe not because the caller did anything but because *the shadow's
own logic* already neutralizes the override for this call. Verify the
actual condition per shadow, it's not the same check for each one:
- `rm` falls back to `command rm` for any flag **except** a bare `-r`,
`-R`, or `--recursive` (those still route to `trash put`) — so
`rm -f`/`rm -rf` qualify, but `rm -r $dir` alone does not.
- `mkdir` falls back to `command mkdir -p` for *any* flag at all, no
exception.
- `--color=auto`/`bat`'s own tty auto-detection (`grep`, `fgrep`,
`egrep`, `dir`, `vdir`, `cat` — verified byte-identical to stock when
piped or captured, since none of these force color on a
non-terminal).
Document it explicitly rather than leaving the bare call untagged: if a
shadow's bypass condition is ever weakened, narrowed, or removed, every
`self-limiting` site is one grep away instead of silently wrong.
Don't use this for `ls` — eza's long-format/icon layout is structural,
not tty-gated, so it stays different from stock `ls` even piped; a
bare `ls` call still needs `uses-shadow(ls)` or a real bypass.
- **`destructive`** — can irreversibly delete or overwrite data: `rm -f`,
`rm -rf`, truncating or force-overwriting a file, `git push --force`.
Routine cleanup of the function's own `$tmpdir`/`$_tmpdir`/`mktemp`
output (or other output it just created in this same call) is expected
behavior, not a hazard — don't tag it.
- **`network`** — makes an outbound network call: `curl`, `wget`, `ssh`,
`git fetch`/`pull`/`push`/`clone`, `paru`/`yay` (package-manager network
ops), talking to an API, etc.
- **`blocking-prompt`** — can block waiting on interactive confirmation
with no non-interactive escape hatch: a shadow's forced `-i`, fish's
`read` (genuinely waiting on a terminal — not a `string split | read`
or `while read` consuming a pipe, which never blocks), a `confirm`-style
prompt with no `--yes`/`--force`/`--silent` bypass. Don't tag a function
that's only ever meant to be run interactively at a prompt (a keybinding
handler, an fzf-driven picker) — the hazard this tag exists for is a
script or another function calling it unexpectedly, not a human running
it themselves.
## Placement
Directly under `# DEPENDENCIES` if the header has one; otherwise directly
under `# COMPONENT`; otherwise directly under `# CATEGORY`; otherwise as
the first label in the header block (this is the common case for internal
`_`-prefixed helpers, which usually carry none of the three).
## Judgment calls
`uses-shadow` vs `bypasses-shadow` is the easiest place to get subtly
wrong — verify against the actual code, not just whether the name appears
in the file. A function that only calls a *helper* which itself interacts
with a shadow does not get the tag; the tag belongs on the helper. When
generating these tags in bulk (e.g. delegating the sweep to another
model), review every result against the source before trusting it — this
schema's own rollout caught several false positives this way: a piped
`read` misread as an interactive prompt, a documented `--yes` flag missed
as an escape hatch, and cleanup of a function's own temp output flagged
as `destructive` despite the explicit exclusion above.
`rm` specifically has its own internal flag check (any flag other than
`-r`/`-R`/`--recursive` falls back to `command rm` *inside the shadow
itself*, before it ever touches trash) — a caller writing plain `rm -f`
or `rm -rf` is not bypassing anything itself, the shadow is. Only tag
`bypasses-shadow(rm)` when the caller explicitly writes `command rm` or
`builtin rm`; a bare `rm -f`/`rm -rf` call gets no shadow tag at all.
@@ -19,7 +19,6 @@ all of these commands.
rg rg --hyperlink-format=kitty system rg rg rg --hyperlink-format=kitty system rg
mkdir verbose path-tree display on creation mkdir -p silently mkdir verbose path-tree display on creation mkdir -p silently
bash XDG bashrc + $SHELL reset on exit system bash bash XDG bashrc + $SHELL reset on exit system bash
history timestamps prepended to every entry fish builtin history
cp / mv forced -i confirmation prompt cp / mv unmodified cp / mv forced -i confirmation prompt cp / mv unmodified
wget forced --continue (resume downloads) system wget wget forced --continue (resume downloads) system wget
grep/fgrep/egrep forced --color=auto system grep variants grep/fgrep/egrep forced --color=auto system grep variants
@@ -31,6 +30,11 @@ all of these commands.
When C1 is disabled, `rm` uses bare `command rm` with no wrapper — files When C1 is disabled, `rm` uses bare `command rm` with no wrapper — files
are permanently deleted, not trashed. There is no intermediate safety net. are permanently deleted, not trashed. There is no intermediate safety net.
`history` itself is never shadowed — every function in this config that
reads history depends on its stock builtin semantics. `pretty-history`
(same `aliases-tricks` toggle) is a separate command that prints history
with a timestamp prepended to every entry.
## Sub-categories ## Sub-categories
`__fish_config_op_aliases` sub-divides into six sub-categories, each with `__fish_config_op_aliases` sub-divides into six sub-categories, each with
@@ -63,3 +67,48 @@ and the `help config` interception.
`claude` (AGENTS.md/CLAUDE.md auto-linking) and `edit` (multi-editor `claude` (AGENTS.md/CLAUDE.md auto-linking) and `edit` (multi-editor
launcher), plus `agy`. launcher), plus `agy`.
## For function authors
Calling one of these names bare from inside your own function means the
override runs whenever C1 (or its sub-category) is on — which may not be
what your function wants: a shadow can change stdout (`cat`'s syntax
highlighting, `mkdir`'s tree display), prompt interactively where none is
expected (`cp`/`mv`'s forced `-i`), or reshape output structurally (`ls`'s
icons/columns, `rg`'s hyperlink markers). If your function's logic depends
on stock behavior, bypass the shadow deterministically, regardless of the
toggle state:
Shadow Bypass Why
─────────────────────────────────────────────────────────────────────────
ls, cat, rm, less, du, command <name> Real external
top, ping, ssh, rg, binaries — a
mkdir, bash, cp, mv, real system command
wget, grep/fgrep/egrep, exists to fall
dir/vdir, claude back to.
cd builtin cd The one true
fish builtin
in this table.
help config __original_help $argv `help` is neither
a builtin nor an
external binary
(embedded in the
fish binary
itself) — see
conf.d/help.fish
for why the
wrapper keeps its
own backup copy.
edit (nothing to bypass to) Purely our own
invention, no
stock command
exists. Call
$EDITOR/$VISUAL
yourself if you
want a plain
editor launch.
A function's own doc header records which of these it depends on: see the
`CLASSIFICATION` label (`uses-shadow(...)` / `bypasses-shadow(...)`),
documented in full at
[`docs/function-classification-schema.md`](../../function-classification-schema.md).
+1
View File
@@ -64,6 +64,7 @@ SECTIONS = (
"CATEGORY", "CATEGORY",
"COMPONENT", "COMPONENT",
"DEPENDENCIES", "DEPENDENCIES",
"CLASSIFICATION",
"SYNOPSIS", "SYNOPSIS",
"DESCRIPTION", "DESCRIPTION",
"ARGUMENTS", "ARGUMENTS",
@@ -4,6 +4,9 @@
# COMPONENT # COMPONENT
# logging/terminal-capture # logging/terminal-capture
# #
# CLASSIFICATION
# self-limiting(rm,mkdir)
#
# SYNOPSIS # SYNOPSIS
# __fish_config_sync_logging # __fish_config_sync_logging
# #
+3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# bypasses-shadow(cat)
#
# SYNOPSIS # SYNOPSIS
# __fish_help_header <name> [args...] # __fish_help_header <name> [args...]
# #
+3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# self-limiting(grep)
#
# SYNOPSIS # SYNOPSIS
# __fish_real_command <name> # __fish_real_command <name>
# #
+3
View File
@@ -4,6 +4,9 @@
# COMPONENT # COMPONENT
# autoexec/sync # autoexec/sync
# #
# CLASSIFICATION
# self-limiting(rm), destructive
#
# SYNOPSIS # SYNOPSIS
# __fish_user_dots_link # __fish_user_dots_link
# #
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# bypasses-shadow(grep)
#
# SYNOPSIS # SYNOPSIS
# __kitty_logging_has_watcher # __kitty_logging_has_watcher
# #
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# self-limiting(grep)
#
# SYNOPSIS # SYNOPSIS
# _agents_init_ensure_gitignore <root> <label> <pattern>... # _agents_init_ensure_gitignore <root> <label> <pattern>...
# #
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# self-limiting(rm,mkdir)
#
# SYNOPSIS # SYNOPSIS
# _agents_repo_ensure_symlink <link> <target> # _agents_repo_ensure_symlink <link> <target>
# #
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# uses-shadow(mkdir), bypasses-shadow(cp,grep)
#
# SYNOPSIS # SYNOPSIS
# _agents_repo_install_tools <repo_dir> # _agents_repo_install_tools <repo_dir>
# #
+6 -3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# self-limiting(rm,mkdir,cat), bypasses-shadow(cp,bash), destructive, network, blocking-prompt
#
# SYNOPSIS # SYNOPSIS
# _fish_deps_install # _fish_deps_install
# #
@@ -281,7 +284,7 @@ function _fish_deps_install
end end
test $_go_status -eq 0 test $_go_status -eq 0
case special-lazydocker case special-lazydocker
curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | command bash
case special-marktext-paru case special-marktext-paru
paru -S --noconfirm marktext-bin paru -S --noconfirm marktext-bin
case special-marktext-yay case special-marktext-yay
@@ -309,7 +312,7 @@ function _fish_deps_install
-o "$_tmpdir/$_zip" -o "$_tmpdir/$_zip"
and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir" and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir"
and mkdir -p "$_wt_dir" "$HOME/.local/bin" and mkdir -p "$_wt_dir" "$HOME/.local/bin"
and cp "$_tmpdir/$_bin_src" "$_wt_bin" and command cp "$_tmpdir/$_bin_src" "$_wt_bin"
and chmod +x "$_wt_bin" and chmod +x "$_wt_bin"
and ln -sf "$_wt_bin" "$HOME/.local/bin/wakatime" and ln -sf "$_wt_bin" "$HOME/.local/bin/wakatime"
rm -rf "$_tmpdir" rm -rf "$_tmpdir"
@@ -320,7 +323,7 @@ function _fish_deps_install
and curl -fL "https://github.com/equalsraf/win32yank/releases/latest/download/$_zip" \ and curl -fL "https://github.com/equalsraf/win32yank/releases/latest/download/$_zip" \
-o "$_tmpdir/$_zip" -o "$_tmpdir/$_zip"
and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir" and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir"
and cp "$_tmpdir/win32yank.exe" "$HOME/.local/bin/win32yank.exe" and command cp "$_tmpdir/win32yank.exe" "$HOME/.local/bin/win32yank.exe"
and chmod +x "$HOME/.local/bin/win32yank.exe" and chmod +x "$HOME/.local/bin/win32yank.exe"
set -l _dl_status $status set -l _dl_status $status
rm -rf "$_tmpdir" rm -rf "$_tmpdir"
+4 -1
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# self-limiting(rm,mkdir), bypasses-shadow(mv), destructive, network
#
# SYNOPSIS # SYNOPSIS
# _fish_deps_marktext_appimage # _fish_deps_marktext_appimage
# #
@@ -54,7 +57,7 @@ function _fish_deps_marktext_appimage
and chmod +x "$tmp/marktext" and chmod +x "$tmp/marktext"
# Replace via mv, not a write into $dest: overwriting a running AppImage # Replace via mv, not a write into $dest: overwriting a running AppImage
# in place corrupts the live mount. # in place corrupts the live mount.
and mv -f "$tmp/marktext" "$dest" and command mv -f "$tmp/marktext" "$dest"
and set ok 1 and set ok 1
rm -rf $tmp rm -rf $tmp
+6 -3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# self-limiting(rm), bypasses-shadow(cp,bash), destructive, network
#
# SYNOPSIS # SYNOPSIS
# _fish_deps_update # _fish_deps_update
# #
@@ -90,7 +93,7 @@ function _fish_deps_update
# lazydocker: re-run the official install/update script # lazydocker: re-run the official install/update script
if test "$special" = curl-lazydocker if test "$special" = curl-lazydocker
echo "Updating $bin..." echo "Updating $bin..."
curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | command bash
set updated_any 1 set updated_any 1
set i (math $i + 1) set i (math $i + 1)
continue continue
@@ -138,7 +141,7 @@ function _fish_deps_update
curl -L "https://github.com/wakatime/wakatime-cli/releases/latest/download/$_zip" \ curl -L "https://github.com/wakatime/wakatime-cli/releases/latest/download/$_zip" \
-o "$_tmpdir/$_zip" -o "$_tmpdir/$_zip"
and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir" and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir"
and cp "$_tmpdir/$_bin_src" "$_wt_bin" and command cp "$_tmpdir/$_bin_src" "$_wt_bin"
and chmod +x "$_wt_bin" and chmod +x "$_wt_bin"
rm -rf "$_tmpdir" rm -rf "$_tmpdir"
and set updated_any 1 and set updated_any 1
@@ -155,7 +158,7 @@ function _fish_deps_update
curl -fL "https://github.com/equalsraf/win32yank/releases/latest/download/$_zip" \ curl -fL "https://github.com/equalsraf/win32yank/releases/latest/download/$_zip" \
-o "$_tmpdir/$_zip" -o "$_tmpdir/$_zip"
and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir" and unzip -o "$_tmpdir/$_zip" -d "$_tmpdir"
and cp "$_tmpdir/win32yank.exe" "$HOME/.local/bin/win32yank.exe" and command cp "$_tmpdir/win32yank.exe" "$HOME/.local/bin/win32yank.exe"
and chmod +x "$HOME/.local/bin/win32yank.exe" and chmod +x "$HOME/.local/bin/win32yank.exe"
set -l _up_status $status set -l _up_status $status
rm -rf "$_tmpdir" rm -rf "$_tmpdir"
+3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# uses-shadow(mkdir)
#
# SYNOPSIS # SYNOPSIS
# _fish_mkdir_p [--path|--tree|--silent] <dir> # _fish_mkdir_p [--path|--tree|--silent] <dir>
# #
+3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# _mkrep_default_remote_cmd <type> # _mkrep_default_remote_cmd <type>
# #
+3
View File
@@ -4,6 +4,9 @@
# DEPENDENCIES # DEPENDENCIES
# gh, glab, tea # gh, glab, tea
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# _mkrep_repo_exists <type> <user> <name> # _mkrep_repo_exists <type> <user> <name>
# #
+3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# bypasses-shadow(ls,rm), destructive
#
# SYNOPSIS # SYNOPSIS
# _prune_terminal_logs <prefix> # _prune_terminal_logs <prefix>
# #
+6 -3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# bypasses-shadow(cat,rm), self-limiting(grep), destructive
#
# SYNOPSIS # SYNOPSIS
# _scrollback_prune_junk [dir] # _scrollback_prune_junk [dir]
# #
@@ -26,7 +29,7 @@ function _scrollback_prune_junk --description 'Remove empty, trivial, and Kitty
# Remove any completely empty log file regardless of source # Remove any completely empty log file regardless of source
for f in $dir/*.log $dir/*.txt for f in $dir/*.log $dir/*.txt
test -f $f || continue test -f $f || continue
not test -s $f; and rm $f not test -s $f; and command rm -f $f
end end
# Remove any log with only a single meaningful line (e.g. [exited], a lone prompt, or a trivial error) # Remove any log with only a single meaningful line (e.g. [exited], a lone prompt, or a trivial error)
@@ -34,7 +37,7 @@ function _scrollback_prune_junk --description 'Remove empty, trivial, and Kitty
test -f $f || continue test -f $f || continue
set -l line_count (command cat $f | sed 's/\x1b\[[0-9;:]*[a-zA-Z]//g' | grep -cv '^\s*$') set -l line_count (command cat $f | sed 's/\x1b\[[0-9;:]*[a-zA-Z]//g' | grep -cv '^\s*$')
if test $line_count -le 1 if test $line_count -le 1
rm $f command rm -f $f
end end
end end
@@ -42,7 +45,7 @@ function _scrollback_prune_junk --description 'Remove empty, trivial, and Kitty
for f in $dir/scrollback_*.log $dir/scrollback_*.txt for f in $dir/scrollback_*.log $dir/scrollback_*.txt
test -f $f || continue test -f $f || continue
if command cat $f | sed 's/\x1b\[[0-9;:]*[a-zA-Z]//g' | grep -q 'Enter the new title for this tab below' if command cat $f | sed 's/\x1b\[[0-9;:]*[a-zA-Z]//g' | grep -q 'Enter the new title for this tab below'
rm $f command rm -f $f
end end
end end
end end
+3
View File
@@ -1,6 +1,9 @@
# Copyright (C) 2026 Rootiest # Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
# CLASSIFICATION
# uses-shadow(mkdir)
#
# SYNOPSIS # SYNOPSIS
# _tmux_pipe_log # _tmux_pipe_log
# #
+3
View File
@@ -4,6 +4,9 @@
# COMPONENT # COMPONENT
# logging/multiplexer-capture # logging/multiplexer-capture
# #
# CLASSIFICATION
# uses-shadow(mkdir), bypasses-shadow(rm)
#
# SYNOPSIS # SYNOPSIS
# _zellij_dump_log # _zellij_dump_log
# #
+7 -4
View File
@@ -7,6 +7,9 @@
# DEPENDENCIES # DEPENDENCIES
# _agents_repo_install_tools, _agents_repo_sync, _agents_init_ensure_gitignore # _agents_repo_install_tools, _agents_repo_sync, _agents_init_ensure_gitignore
# #
# CLASSIFICATION
# self-limiting(rm,mkdir), bypasses-shadow(mv)
#
# SYNOPSIS # SYNOPSIS
# agents-init [-a | --agents] [-p | --plugins] [-v | --verbose] # agents-init [-a | --agents] [-p | --plugins] [-v | --verbose]
# [-q | --quiet] [-s | --silent] [-h | --help] # [-q | --quiet] [-s | --silent] [-h | --help]
@@ -212,7 +215,7 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi
if test $has_agents -eq 1; and test $has_claude -eq 1 if test $has_agents -eq 1; and test $has_claude -eq 1
# Both exist: preserve each as its own file in AGENTS/ # Both exist: preserve each as its own file in AGENTS/
if not test -f "$agents_dir/AGENTS.md" if not test -f "$agents_dir/AGENTS.md"
if not mv "$root/AGENTS.md" "$agents_dir/AGENTS.md" if not command mv "$root/AGENTS.md" "$agents_dir/AGENTS.md"
echo "$c_err""Error: could not move AGENTS.md → AGENTS/AGENTS.md$c_reset" >&2 echo "$c_err""Error: could not move AGENTS.md → AGENTS/AGENTS.md$c_reset" >&2
return 1 return 1
end end
@@ -220,7 +223,7 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi
test $verbose -eq 1; and echo "$c_ok→ Moved AGENTS.md → AGENTS/AGENTS.md$c_reset" test $verbose -eq 1; and echo "$c_ok→ Moved AGENTS.md → AGENTS/AGENTS.md$c_reset"
end end
if not test -f "$agents_dir/CLAUDE.md"; and not test -L "$agents_dir/CLAUDE.md" if not test -f "$agents_dir/CLAUDE.md"; and not test -L "$agents_dir/CLAUDE.md"
if not mv "$root/CLAUDE.md" "$agents_dir/CLAUDE.md" if not command mv "$root/CLAUDE.md" "$agents_dir/CLAUDE.md"
echo "$c_err""Error: could not move CLAUDE.md → AGENTS/CLAUDE.md$c_reset" >&2 echo "$c_err""Error: could not move CLAUDE.md → AGENTS/CLAUDE.md$c_reset" >&2
return 1 return 1
end end
@@ -229,7 +232,7 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi
end end
else if test $has_agents -eq 1 else if test $has_agents -eq 1
if not test -f "$agents_dir/AGENTS.md" if not test -f "$agents_dir/AGENTS.md"
if not mv "$root/AGENTS.md" "$agents_dir/AGENTS.md" if not command mv "$root/AGENTS.md" "$agents_dir/AGENTS.md"
echo "$c_err""Error: could not move AGENTS.md → AGENTS/AGENTS.md$c_reset" >&2 echo "$c_err""Error: could not move AGENTS.md → AGENTS/AGENTS.md$c_reset" >&2
return 1 return 1
end end
@@ -239,7 +242,7 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi
else if test $has_claude -eq 1 else if test $has_claude -eq 1
# Only CLAUDE.md: treat it as the agent spec # Only CLAUDE.md: treat it as the agent spec
if not test -f "$agents_dir/AGENTS.md" if not test -f "$agents_dir/AGENTS.md"
if not mv "$root/CLAUDE.md" "$agents_dir/AGENTS.md" if not command mv "$root/CLAUDE.md" "$agents_dir/AGENTS.md"
echo "$c_err""Error: could not move CLAUDE.md → AGENTS/AGENTS.md$c_reset" >&2 echo "$c_err""Error: could not move CLAUDE.md → AGENTS/AGENTS.md$c_reset" >&2
return 1 return 1
end end
+3
View File
@@ -9,6 +9,9 @@
# _agents_repo_ensure_symlink, _agents_repo_sync, # _agents_repo_ensure_symlink, _agents_repo_sync,
# _agents_repo_install_tools, git, hostname # _agents_repo_install_tools, git, hostname
# #
# CLASSIFICATION
# self-limiting(rm,mkdir)
#
# SYNOPSIS # SYNOPSIS
# agents-vault [--link] [--push] [--restore] [--status] # agents-vault [--link] [--push] [--restore] [--status]
# [--adopt=SLUG] [--remote=URL] # [--adopt=SLUG] [--remote=URL]
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 12-ai-and-developer-tools # 12-ai-and-developer-tools
# #
# CLASSIFICATION
# self-limiting(grep)
#
# SYNOPSIS # SYNOPSIS
# antigravity-ide [args...] # antigravity-ide [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/shell-tools # aliases/shell-tools
# #
# CLASSIFICATION
# bypasses-shadow(bash)
#
# SYNOPSIS # SYNOPSIS
# bash [args...] # bash [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/filesystem # aliases/filesystem
# #
# CLASSIFICATION
# uses-shadow(ls), bypasses-shadow(cat)
#
# SYNOPSIS # SYNOPSIS
# cat [args...] # cat [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 14-miscellaneous # 14-miscellaneous
# #
# CLASSIFICATION
# uses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# cffetch [args...] # cffetch [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 12-ai-and-developer-tools # 12-ai-and-developer-tools
# #
# CLASSIFICATION
# uses-shadow(claude)
#
# SYNOPSIS # SYNOPSIS
# claude-docs # claude-docs
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 12-ai-and-developer-tools # 12-ai-and-developer-tools
# #
# CLASSIFICATION
# uses-shadow(claude)
#
# SYNOPSIS # SYNOPSIS
# claude-pr # claude-pr
# #
+3
View File
@@ -10,6 +10,9 @@
# DEPENDENCIES # DEPENDENCIES
# agents-init, agents-vault # agents-init, agents-vault
# #
# CLASSIFICATION
# bypasses-shadow(claude)
#
# SYNOPSIS # SYNOPSIS
# claude [ARGS...] # claude [ARGS...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 05-package-management # 05-package-management
# #
# CLASSIFICATION
# self-limiting(grep)
#
# SYNOPSIS # SYNOPSIS
# cleanup # cleanup
# #
+4 -1
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 14-miscellaneous # 14-miscellaneous
# #
# CLASSIFICATION
# self-limiting(grep), bypasses-shadow(less)
#
# SYNOPSIS # SYNOPSIS
# config-help [section] # config-help [section]
# config-help --html # config-help --html
@@ -347,7 +350,7 @@ function config-help --description 'Open the offline fish shell configuration ma
else if type -q less else if type -q less
string replace -ra $span_raw $span_bold <"$doc_file" \ string replace -ra $span_raw $span_bold <"$doc_file" \
| less -R +"$start_line" | command less -R +"$start_line"
else else
string replace -ra $span_raw $span_bold <"$doc_file" string replace -ra $span_raw $span_bold <"$doc_file"
+3
View File
@@ -8,6 +8,9 @@
# __fish_palette, __config_settings_state, __config_settings_apply, # __fish_palette, __config_settings_state, __config_settings_apply,
# __config_settings_set_value, python3 # __config_settings_set_value, python3
# #
# CLASSIFICATION
# bypasses-shadow(rm)
#
# SYNOPSIS # SYNOPSIS
# config-settings [-h | --help] # config-settings [-h | --help]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(cp)
#
# SYNOPSIS # SYNOPSIS
# copy <source> <dest> # copy <source> <dest>
# #
+4 -1
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 13-media-and-utilities # 13-media-and-utilities
# #
# CLASSIFICATION
# self-limiting(rm)
#
# SYNOPSIS # SYNOPSIS
# dng2avif [-h] [-i <file>] [-o <file>] [-q <n>] [-s <n>] [input.dng] # dng2avif [-h] [-i <file>] [-o <file>] [-q <n>] [-s <n>] [input.dng]
# #
@@ -119,7 +122,7 @@ function dng2avif --description 'Convert DNG raw to 10-bit HDR AVIF'
end end
# Final Cleanup # Final Cleanup
test -f "$temp_pnm"; and rm "$temp_pnm" test -f "$temp_pnm"; and rm -f "$temp_pnm"
set -l size (stat -c '%s' "$output" | numfmt --to=iec) set -l size (stat -c '%s' "$output" | numfmt --to=iec)
echo (set_color yellow)"Complete: $output ($size)"(set_color normal) echo (set_color yellow)"Complete: $output ($size)"(set_color normal)
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 14-miscellaneous # 14-miscellaneous
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# dockup [-h] [directory] # dockup [-h] [directory]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/filesystem # aliases/filesystem
# #
# CLASSIFICATION
# bypasses-shadow(du)
#
# SYNOPSIS # SYNOPSIS
# du [--disk|--dir|--dua] [args...] # du [--disk|--dir|--dua] [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# uses-shadow(du)
#
# SYNOPSIS # SYNOPSIS
# dusize [dir] # dusize [dir]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/dev-tools # aliases/dev-tools
# #
# CLASSIFICATION
# bypasses-shadow(rm)
#
# SYNOPSIS # SYNOPSIS
# edit [-V|-t] [-e EDITOR] [-c] [-x TEXT] [-n] [-v|-s] [FILE...] # edit [-V|-t] [-e EDITOR] [-c] [-x TEXT] [-n] [-v|-s] [FILE...]
# #
+6 -3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 03-editors-and-viewers # 03-editors-and-viewers
# #
# CLASSIFICATION
# bypasses-shadow(rm), self-limiting(cat)
#
# SYNOPSIS # SYNOPSIS
# fc [command_prefix] # fc [command_prefix]
# #
@@ -50,15 +53,15 @@ function fc --description 'Edit and execute the last command (Bash-style fc)'
# Final check if user cleared the file in the editor # Final check if user cleared the file in the editor
if test -s $tmpfile if test -s $tmpfile
set -l command (cat $tmpfile) set -l command (cat $tmpfile)
rm $tmpfile command rm -f $tmpfile
commandline -r "$command" commandline -r "$command"
commandline -f execute commandline -f execute
else else
rm $tmpfile command rm -f $tmpfile
echo "fc: Aborted (empty file)" echo "fc: Aborted (empty file)"
end end
else else
rm $tmpfile command rm -f $tmpfile
echo "fc: Could not retrieve history" echo "fc: Could not retrieve history"
end end
end end
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 14-miscellaneous # 14-miscellaneous
# #
# CLASSIFICATION
# uses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# ffetch [args...] # ffetch [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 06-dependency-management # 06-dependency-management
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# fzf-update # fzf-update
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 04-git-and-version-control # 04-git-and-version-control
# #
# CLASSIFICATION
# self-limiting(grep,cat), network, blocking-prompt
#
# SYNOPSIS # SYNOPSIS
# gi [-h] [-b] [-p] [-s] [-l] [targets...] # gi [-h] [-b] [-p] [-s] [-l] [targets...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 10-network # 10-network
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# gip # gip
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 10-network # 10-network
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# gip4 # gip4
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 10-network # 10-network
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# gip6 # gip6
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 04-git-and-version-control # 04-git-and-version-control
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# git-clean [-h] [-f] # git-clean [-h] [-f]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 04-git-and-version-control # 04-git-and-version-control
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# gitup [args...] # gitup [args...]
# #
+1 -1
View File
@@ -34,7 +34,7 @@ function hist --description 'Search fish history and put it in the prompt'
return 1 return 1
end end
set -l selected (history | fzf --reverse --height 40% --with-nth 3..) set -l selected (builtin history --show-time='%F %T ' | fzf --reverse --height 40% --with-nth 3..)
if test -n "$selected" if test -n "$selected"
# Strip the timestamp for the final output # Strip the timestamp for the final output
+3
View File
@@ -7,6 +7,9 @@
# DEPENDENCIES # DEPENDENCIES
# tmux, screen, __jobrunner_sessions # tmux, screen, __jobrunner_sessions
# #
# CLASSIFICATION
# bypasses-shadow(cat,rm)
#
# SYNOPSIS # SYNOPSIS
# jobrunner [-t <tool>] [<subcommand>] [<name>] [<command>...] # jobrunner [-t <tool>] [<subcommand>] [<name>] [<command>...]
# jr [-t <tool>] [<subcommand>] [<name>] [<command>...] # jr [-t <tool>] [<subcommand>] [<name>] [<command>...]
+3
View File
@@ -7,6 +7,9 @@
# DEPENDENCIES # DEPENDENCIES
# gpg, tar # gpg, tar
# #
# CLASSIFICATION
# bypasses-shadow(rm), destructive
#
# SYNOPSIS # SYNOPSIS
# key-crypt [options] <input> [output] # key-crypt [options] <input> [output]
# key-crypt -i <input> -o <output> [options] # key-crypt -i <input> -o <output> [options]
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# logging/terminal-capture # logging/terminal-capture
# #
# CLASSIFICATION
# bypasses-shadow(grep,mkdir,rm)
#
# SYNOPSIS # SYNOPSIS
# kitty-logging [install | uninstall | status | dismiss] [-h] # kitty-logging [install | uninstall | status | dismiss] [-h]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# lD [args...] # lD [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/shell-tools # aliases/shell-tools
# #
# CLASSIFICATION
# bypasses-shadow(cat,less)
#
# SYNOPSIS # SYNOPSIS
# less [args...] # less [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# integrations/history-logs # integrations/history-logs
# #
# CLASSIFICATION
# bypasses-shadow(cat), self-limiting(rm), network
#
# SYNOPSIS # SYNOPSIS
# logs [-h] [-c <category>] # logs [-h] [-c <category>]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/filesystem # aliases/filesystem
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# ls [args...] # ls [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# lsr [args...] # lsr [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# lss [args...] # lss [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# lstree [args...] # lstree [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# lt [args...] # lt [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# ltr [args...] # ltr [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(ls)
#
# SYNOPSIS # SYNOPSIS
# lx [args...] # lx [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# DEPENDENCIES # DEPENDENCIES
# marktext, firejail, bkg # marktext, firejail, bkg
# #
# CLASSIFICATION
# uses-shadow(mkdir)
#
# SYNOPSIS # SYNOPSIS
# md [-r] [--foreground] [marktext-args...] [FILE...] # md [-r] [--foreground] [marktext-args...] [FILE...]
# #
+4 -1
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# bypasses-shadow(cd)
#
# SYNOPSIS # SYNOPSIS
# mkcd [-s | --silent] <dir> # mkcd [-s | --silent] <dir>
# #
@@ -65,7 +68,7 @@ function mkcd --description 'Create a directory (with parents) and cd into it'
_fish_mkdir_p --tree $dir; or return $status _fish_mkdir_p --tree $dir; or return $status
end end
cd $dir builtin cd $dir
or return $status or return $status
if test $is_new -eq 1 if test $is_new -eq 1
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/filesystem # aliases/filesystem
# #
# CLASSIFICATION
# bypasses-shadow(mkdir)
#
# SYNOPSIS # SYNOPSIS
# mkdir [args...] # mkdir [args...]
# #
+12 -9
View File
@@ -9,6 +9,9 @@
# _mkrep_add_origin, _mkrep_default_remote_cmd, _mkrep_remote_url, # _mkrep_add_origin, _mkrep_default_remote_cmd, _mkrep_remote_url,
# _mkrep_repo_exists, git # _mkrep_repo_exists, git
# #
# CLASSIFICATION
# bypasses-shadow(cd), self-limiting(rm), destructive, network
#
# SYNOPSIS # SYNOPSIS
# mkrep [--cd | --no-cd] [--mkdir | --no-mkdir] [--git | --no-git] # mkrep [--cd | --no-cd] [--mkdir | --no-mkdir] [--git | --no-git]
# [-c | --clean | --no-clean] [--strict] [-v | --verbose] # [-c | --clean | --no-clean] [--strict] [-v | --verbose]
@@ -304,7 +307,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
_mkrep_say $silent "$c_warn""→$c_reset $c_arg$dir$c_reset already exists" _mkrep_say $silent "$c_warn""→$c_reset $c_arg$dir$c_reset already exists"
end end
cd $dir builtin cd $dir
or begin or begin
echo "$c_err""✘$c_reset Failed to enter $c_arg$dir$c_reset" >&2 echo "$c_err""✘$c_reset Failed to enter $c_arg$dir$c_reset" >&2
return 1 return 1
@@ -326,7 +329,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
end end
or begin or begin
echo "$c_err""✘$c_reset git init failed in $c_arg$dir$c_reset" >&2 echo "$c_err""✘$c_reset git init failed in $c_arg$dir$c_reset" >&2
cd $orig_pwd builtin cd $orig_pwd
return 1 return 1
end end
@@ -341,7 +344,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
_mkrep_verbose $silent $verbose "$c_dim""Running: git remote add origin $_flag_remote$c_reset" _mkrep_verbose $silent $verbose "$c_dim""Running: git remote add origin $_flag_remote$c_reset"
_mkrep_add_origin $silent $_flag_remote _mkrep_add_origin $silent $_flag_remote
or begin or begin
cd $orig_pwd builtin cd $orig_pwd
return 1 return 1
end end
end end
@@ -351,7 +354,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
test -z "$cmd"; and set cmd $MKREP_REMOTE_CMD test -z "$cmd"; and set cmd $MKREP_REMOTE_CMD
if test -z "$cmd" if test -z "$cmd"
echo "$c_err""✘$c_reset --new-remote given no command and \$MKREP_REMOTE_CMD is unset" >&2 echo "$c_err""✘$c_reset --new-remote given no command and \$MKREP_REMOTE_CMD is unset" >&2
cd $orig_pwd builtin cd $orig_pwd
return 1 return 1
end end
@@ -368,7 +371,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
end end
or begin or begin
echo "$c_err""✘$c_reset Remote-create command failed" >&2 echo "$c_err""✘$c_reset Remote-create command failed" >&2
cd $orig_pwd builtin cd $orig_pwd
return 1 return 1
end end
_mkrep_say $silent "$c_ok""✔$c_reset Ran remote-create command" _mkrep_say $silent "$c_ok""✔$c_reset Ran remote-create command"
@@ -390,7 +393,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
_mkrep_say $silent "$c_warn""→$c_reset $c_arg$USER/$name$c_reset already exists on $srv_type; linking instead of creating" _mkrep_say $silent "$c_warn""→$c_reset $c_arg$USER/$name$c_reset already exists on $srv_type; linking instead of creating"
_mkrep_add_origin $silent $url _mkrep_add_origin $silent $url
or begin or begin
cd $orig_pwd builtin cd $orig_pwd
return 1 return 1
end end
else else
@@ -421,7 +424,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
if string match -q '*{server}*' -- $cmd if string match -q '*{server}*' -- $cmd
if test -z "$srv_url" if test -z "$srv_url"
echo "$c_err""✘$c_reset No base URL resolved for $srv_type (set \$GITEA_URL/\$GITEA_HOST or \$GITLAB_URL/\$GITLAB_HOST)" >&2 echo "$c_err""✘$c_reset No base URL resolved for $srv_type (set \$GITEA_URL/\$GITEA_HOST or \$GITLAB_URL/\$GITLAB_HOST)" >&2
cd $orig_pwd builtin cd $orig_pwd
return 1 return 1
end end
set cmd (string replace -a '{server}' $srv_url -- $cmd) set cmd (string replace -a '{server}' $srv_url -- $cmd)
@@ -437,7 +440,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
end end
or begin or begin
echo "$c_err""✘$c_reset Remote-create command failed" >&2 echo "$c_err""✘$c_reset Remote-create command failed" >&2
cd $orig_pwd builtin cd $orig_pwd
return 1 return 1
end end
set -l url (_mkrep_remote_url $srv_type $USER $name $srv_url) set -l url (_mkrep_remote_url $srv_type $USER $name $srv_url)
@@ -448,7 +451,7 @@ function mkrep --description 'Create a directory, cd into it, and git init it'
end end
if test $do_cd -eq 0 if test $do_cd -eq 0
cd $orig_pwd builtin cd $orig_pwd
else else
_mkrep_say $silent "$c_ok""✔$c_reset Entered $c_arg$dir$c_reset" _mkrep_say $silent "$c_ok""✔$c_reset Entered $c_arg$dir$c_reset"
end end
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/filesystem # aliases/filesystem
# #
# CLASSIFICATION
# bypasses-shadow(mv)
#
# SYNOPSIS # SYNOPSIS
# mv [args...] # mv [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 05-package-management # 05-package-management
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# parur # parur
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/network # aliases/network
# #
# CLASSIFICATION
# bypasses-shadow(ping)
#
# SYNOPSIS # SYNOPSIS
# ping [args...] # ping [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 05-package-management # 05-package-management
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# pkg [-h] [-i|-u] <package> [package...] # pkg [-h] [-i|-u] <package> [package...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 10-network # 10-network
# #
# CLASSIFICATION
# self-limiting(cat), network
#
# SYNOPSIS # SYNOPSIS
# qr [text...] # qr [text...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 14-miscellaneous # 14-miscellaneous
# #
# CLASSIFICATION
# bypasses-shadow(cat)
#
# SYNOPSIS # SYNOPSIS
# rand_string [COMPONENTS/MODIFIERS]... # rand_string [COMPONENTS/MODIFIERS]...
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 14-miscellaneous # 14-miscellaneous
# #
# CLASSIFICATION
# bypasses-shadow(bash,cd)
#
# SYNOPSIS # SYNOPSIS
# replay <commands> # replay <commands>
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/search # aliases/search
# #
# CLASSIFICATION
# bypasses-shadow(rg)
#
# SYNOPSIS # SYNOPSIS
# rg [args...] # rg [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/filesystem # aliases/filesystem
# #
# CLASSIFICATION
# bypasses-shadow(rm), destructive
#
# SYNOPSIS # SYNOPSIS
# rm [-e [options] | -S | args...] # rm [-e [options] | -S | args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 07-system-and-monitoring # 07-system-and-monitoring
# #
# CLASSIFICATION
# self-limiting(grep)
#
# SYNOPSIS # SYNOPSIS
# sbver [--brief] # sbver [--brief]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 01-file-and-directory # 01-file-and-directory
# #
# CLASSIFICATION
# uses-shadow(rm), bypasses-shadow(rm), destructive
#
# SYNOPSIS # SYNOPSIS
# scrub [-a] [-d] [-h] # scrub [-a] [-d] [-h]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 05-package-management # 05-package-management
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# search [args...] # search [args...]
# #
+3
View File
@@ -8,6 +8,9 @@
# site exit-plain: overrides/key-bindings # site exit-plain: overrides/key-bindings
# site logging-guard: logging/terminal-capture # site logging-guard: logging/terminal-capture
# #
# CLASSIFICATION
# self-limiting(rm,mkdir), destructive
#
# SYNOPSIS # SYNOPSIS
# smart_exit [-h] [-n] # smart_exit [-h] [-n]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/network # aliases/network
# #
# CLASSIFICATION
# bypasses-shadow(ssh), network
#
# SYNOPSIS # SYNOPSIS
# ssh [args...] # ssh [args...]
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 12-ai-and-developer-tools # 12-ai-and-developer-tools
# #
# CLASSIFICATION
# uses-shadow(claude)
#
# SYNOPSIS # SYNOPSIS
# superpowers [on|off] [-g] # superpowers [on|off] [-g]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/monitor # aliases/monitor
# #
# CLASSIFICATION
# bypasses-shadow(top)
#
# SYNOPSIS # SYNOPSIS
# top [args...] # top [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# integrations/pkg-upgrade # integrations/pkg-upgrade
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# upgrade # upgrade
# #
+3
View File
@@ -4,6 +4,9 @@
# CATEGORY # CATEGORY
# 03-editors-and-viewers # 03-editors-and-viewers
# #
# CLASSIFICATION
# uses-shadow(less)
#
# SYNOPSIS # SYNOPSIS
# view [args...] # view [args...]
# #
+3
View File
@@ -7,6 +7,9 @@
# COMPONENT # COMPONENT
# aliases/network # aliases/network
# #
# CLASSIFICATION
# network
#
# SYNOPSIS # SYNOPSIS
# yt-dlp [args...] URL [URL...] # yt-dlp [args...] URL [URL...]
# #
+83
View File
@@ -53,6 +53,89 @@ if test $syntax_failed -ne 0 -o $indent_failed -ne 0
set overall_failed 1 set overall_failed 1
end end
# ---- Phase 1b: shadow-classification lint --------------------------------
# Catches a bare C1-shadowed-command call in a function body with no
# matching uses-shadow(name) or self-limiting(name) in that function's own
# CLASSIFICATION header -- the exact bug class fixed across fc.fish,
# dng2avif.fish, _scrollback_prune_junk.fish, mkcd.fish, and mkrep.fish. A
# bare call is either declared (uses-shadow: wanted; self-limiting: safe
# because the shadow's own logic neutralizes it, e.g. rm/mkdir's flag check
# or grep/cat's tty-auto-detected color) or it's undocumented at best, a bug
# at worst -- the lint never guesses which on its own; see
# docs/function-classification-schema.md for the full tag definitions and
# why the reasoning belongs in a tag, not in this script.
#
# Scoped to functions/*.fish only: the one-function-per-file convention
# there makes "everything after the function line is its body" exact, with
# no block-depth parser needed. conf.d/*.fish can define several functions
# in one file and isn't covered -- see docs/function-classification-schema.md.
echo
echo "== Shadow-classification lint =="
# help and edit are deliberately excluded: help's real bypass is
# __original_help (not command/builtin), and edit has no backing binary at
# all to bypass to -- see docs/manual/08-components-reference/01-c1-command-shadows.md.
set -l shadow_names ls cat cd rm less du top ping ssh rg mkdir bash cp mv wget grep fgrep egrep dir vdir claude
set -l class_checked 0
set -l class_files_failed 0
set -l class_issues 0
for f in $repo_root/functions/*.fish
set -l lines (cat $f)
# Find the function line; everything before it is header, everything
# from it onward is body (one function per file).
set -l func_idx 0
for i in (seq (count $lines))
if string match -qr '^function ' -- $lines[$i]
set func_idx $i
break
end
end
test $func_idx -eq 0; and continue
set class_checked (math $class_checked + 1)
# Pull uses-shadow(...) and self-limiting(...) names from the
# CLASSIFICATION tag line, if any -- either one accounts for a bare call.
set -l declared
for i in (seq (math $func_idx - 1))
if test "$lines[$i]" = "# CLASSIFICATION"; and test $i -lt $func_idx
set -l tagline $lines[(math $i + 1)]
for tag in uses-shadow self-limiting
set -l m (string match -r "$tag"'\(([^)]*)\)' -- $tagline)
test -n "$m[2]"; and set -a declared (string trim -- (string split ',' -- $m[2]))
end
break
end
end
set -l file_failed 0
for i in (seq $func_idx (count $lines))
set -l line $lines[$i]
# Strip quoted spans and comments so string literals (error
# messages, --description text) never masquerade as a call.
set -l stripped (string replace -ra '"[^"]*"' '' -- $line)
set stripped (string replace -ra "'[^']*'" '' -- $stripped)
set stripped (string replace -r '#.*$' '' -- $stripped)
for name in $shadow_names
if string match -qr '(^|[;|(]|\band\b|\bor\b|\bnot\b|\bif\b|\bwhile\b|\bbegin\b)\s*'"$name"'(\s|$)' -- $stripped
if not contains -- $name $declared
echo " FAIL (shadow) "(string replace $repo_root/ '' $f)": line $i calls bare '$name' with no uses-shadow($name)/self-limiting($name)"
set class_issues (math $class_issues + 1)
set file_failed 1
end
end
end
end
test $file_failed -eq 1; and set class_files_failed (math $class_files_failed + 1)
end
echo (math $class_checked - $class_files_failed)"/$class_checked functions passed shadow-classification check"
if test $class_issues -ne 0
set overall_failed 1
end
# ---- Phase 2: discover suites -------------------------------------------- # ---- Phase 2: discover suites --------------------------------------------
# Mode is declared by the suite, not by this driver. Detection is # Mode is declared by the suite, not by this driver. Detection is
# case-insensitive so a near-miss like "# Mode: in-session" is caught rather # case-insensitive so a near-miss like "# Mode: in-session" is caught rather