| name | fish-shell-scripting |
| description | Write, review, audit, refactor, and diagnose Fish code and configuration. Use it whenever code is intended to run in Fish or a migration targets Fish.
Use it across `.fish` files, Fish hashbangs, `config.fish`, autoloaded functions, prompts, and completions.
Do not use when the requested output is only code for another shell. |
Fish Shell Scripting
Fish code is clearest when it is written on the language’s own terms. This skill preserves Fish’s list semantics, command-oriented conditions, explicit variable scopes, and purpose-built builtins instead of translating another shell’s habits line by line.
Write only for the latest stable Fish release unless the user or target environment requires another version.
Workflow
Choose the branch that matches the request. An explicit change takes precedence when the request also uses review or audit language.
- Change: Inspect the affected Fish files, call sites, execution context, current Fish behavior, and project validation entrypoints before making the smallest complete edit.
- Review: Remain read-only and report only concrete correctness, compatibility, maintainability, or established-policy problems.
- Audit: Remain read-only, bound the file inventory first, apply every applicable rule to that inventory, and report evidence-backed findings rather than style preferences.
- Diagnosis: Remain read-only until the failure is reproduced or isolated. Trace expansion, scope, status, startup context, and command resolution before proposing a root-cause fix.
Fish Context
- Identify Fish from its hashbang and syntax rather than its filename alone. Include extensionless entrypoints with a Fish hashbang.
- Classify the target as a noninteractive script, interactive configuration, autoloaded function, event handler, prompt, or completion. Each surface has different loading, status, output, and performance constraints.
- Choose the execution boundary according to who owns the state. Use a function for reusable behavior that must affect the current Fish process. Source a file only when file-based code must affect its caller. Execute a script when process isolation is intended. A sourced file has no process boundary, while an executed Fish script still reads startup configuration by default and inherits its environment.
- For agent-selected invocations and command examples, default to
fish --no-config when using Fish as a noninteractive interpreter. Do not apply this default to repository scripts, workflows, or configuration. This default also does not apply when Fish startup configuration or configured runtime behavior is in scope.
- Set
MANPAGER=cat and PAGER=cat for agent-selected Fish-owned help commands so they terminate without opening an interactive pager.
- Prefer the project’s formatter, lint wrapper, tests, and conventions when they preserve Fish semantics. Do not import POSIX-shell policy merely because another shell exists in the same repository.
Load bundled guidance when the corresponding decision enters scope:
- Use Fish-Native Idioms whenever a task touches variables, lists, quoting, expansions, conditions, paths, globs, redirections, pipelines, process boundaries, or builtin selection.
- Use Configuration, Functions, and Events for startup files, autoloaded functions, wrappers, abbreviations, universal variables, or event handlers.
- Use Fish Completions for completion definitions.
- Use Fish Prompts for prompt functions.
Design Principles
Use Fish’s design principles as defaults for code and configuration decisions.
Use Fish-Native Structure
Choose one capable Fish-native construct instead of overlapping aliases, expansion families, heredocs, subshell forms, or hand-built variants. Use functions as the reusable abstraction. Express loops, conditions, assignments, and scopes through Fish’s uniform command model rather than exposing lower-level process machinery.
Follow POSIX selectively where Fish already does. Never weaken Fish semantics or emulate unsupported syntax merely to resemble another shell.
Keep Interactive Behavior Deliberate
Keep startup, prompt, and completion paths responsive. Minimize forks, disk access, and synchronous work that the user did not directly initiate. Give every completion item a useful description when Fish’s API allows one. Make errors identify what went wrong and the relevant action or help surface.
Do not add a setting when the code can infer one reliable behavior. Treat each new configuration branch as a maintenance and compatibility cost.
Source Conventions
When applicable project or global policy requires alphabetization, apply it to order-independent Fish declarations, completion candidates, option lists, and configuration entries. Preserve order that communicates or controls precedence, lifecycle, dependency, or presentation.
For new or materially rewritten commands:
- Prefer a supported full-length option name such as
--all over its short form such as -a. Keep the short form only when no equivalent long option exists or exact syntax is part of the interface being preserved.
- Prefer the variable name
param over arg. Exempt Fish’s built-in $argv variable.
- Treat 100 columns, including indentation, as the default wrapping threshold when no project or formatter rule sets another limit. Do not reflow existing code solely for length. Break at a meaningful argument, operator, pipe, or redirection boundary. Rely on Fish’s grammatical continuation where available, and use
\ only when the line would otherwise terminate. Let the formatter own indentation, and allow an overlong line when no useful break exists.
Function Documentation
A Fish function source docstring is a contiguous block of # comment lines immediately above an explicit function declaration, with no blank line between the docstring and declaration.
Treat a function as exposed when its name is a supported command interface for users or integrations. Fish’s lack of private function visibility does not make an implementation detail or lifecycle callback exposed. Give every exposed function a concise --description that states its purpose or observable contract and remains suitable for completion display. Use the runtime description instead of repeating the same statement in a source docstring.
Give every unexposed explicit function a source docstring, including private helpers, event handlers, prompt functions, completion helpers, and intentionally empty overrides. State its purpose, observable contract, compatibility boundary, or non-obvious constraint instead of narrating the implementation or repeating an obvious name.
Add a source docstring to an exposed function only when it communicates a non-obvious contract, compatibility boundary, or constraint beyond what the concise runtime description can carry. Keep each source docstring attached when moving or refactoring the function.
function resolve_repository --description 'Resolve a repository path to its canonical form'
path resolve -- $argv[1]
end
Human-Facing Text
Function descriptions, source docstrings, explanatory comments, help and usage text, diagnostics, warnings, prompts, completion descriptions, interactive labels, and test titles are human-facing technical copy.
Load human-facing-writing whenever a Fish task creates, changes, or reviews human-facing text whose contract is in scope, including adjacent tests written in another language. Provide the Fish surface, required semantics, and relevant evidence, then let that skill select its applicable routes.
Fish semantics and project policy own what the text must communicate. human-facing-writing owns wording, reading order, terminology, tone, and surface-appropriate presentation within those facts. Do not rewrite machine-readable output, exact command syntax, destination-supplied values, or preserved upstream errors merely for prose style.
If human-facing-writing is unavailable locally and available evidence shows that remote use would materially improve the wording, follow the optional public-peer workflow. If the peer remains unavailable, preserve complete standalone behavior. Write concise, neutral text that leads with the purpose or outcome, explains non-obvious intent rather than control flow, preserves exact technical tokens, and gives an actionable reason only when evidence establishes one.
Validation
Run behavioral checks only when they cannot modify user state. Cover empty and multi-element lists, paths containing whitespace, failed commands, unmatched globs, and option boundaries when those cases matter.
When a standalone target must not depend on startup configuration, exercise it under fish --no-config and its normal target context. Treat this as a configuration-independence check rather than a hermetic environment.
For configuration, prompts, completions, and events, validate the relevant interactive, noninteractive, login, autoload, or event-loading context without persisting universal variables or overwriting user configuration.
Define Validation Scope
- Change: Validate each changed Fish file, function, and human-facing string, plus every affected call site, execution context, and cross-file contract needed to establish the resulting behavior.
- Review, Audit, or Diagnosis: Validate the complete resolved read-only scope. For an audit, use the bounded inventory established before inspection. For a review or diagnosis, include every affected file, call site, execution context, and cross-file contract. Keep every check nonmutating.
Run Validation
- Run the project’s narrowest applicable Fish checks and diagnostics first.
- Parse each script in the validation scope with
fish --no-config --no-execute <path> when the project workflow does not already do so.
- Check formatting with the project formatter’s check mode or
fish_indent --check <path> when no project formatter is established.
- Exercise the relevant behavioral checks above.
- Recheck every function in the validation scope against the function-documentation contract and every human-facing string in that scope under the human-facing text contract.
Stale Guidance
Classify each part of this skill’s guidance used by the selected workflow as required, optional, or supporting. Treat missing local targets, malformed destinations, and HTTP responses that report a resource as missing or permanently unavailable as broken references. Broken references and verified conflicts with the current interface or behavior mean the guidance is stale. Use any failure response the guidance defines. Otherwise, report the stale guidance and evidence, recommend updating this skill, and follow the appropriate recovery below.
When required guidance is stale, stop only the affected branch and use any complete fallback provided by the available guidance. Without one, ask whether to continue. The choice applies only to this conversation and to work independent of the stale guidance. Stale optional or supporting guidance does not stop the workflow.
Access restrictions, authentication problems, network failures, and HTTP server errors are not evidence of staleness. Use any relevant access or retrieval guidance. If none applies, stop retrieving the resource and report the resource, attempted method, exact error, and smallest corrective action.
Never infer missing content. Never substitute an unverified location. Never weaken scope, approval, mutation, or security boundaries.