Repository navigation
star lint shell finds scripts by their first line and checks each with shfmt, its own shell's -n, then shellcheck; every repository's shell gate runs it #1040
Description
star lint shell runs shfmt and shellcheck, and devlore-cli's CI runs it (.github/workflows/ci.yaml). It finds scripts by extension alone, .sh, .bash and .zsh, and the house's scripts carry none: run in David-Noble-at-work/personal's Home/noblefactor-ops.Unix/.local/bin, 22 scripts with no extension, it reports "No shell files found" and exits 0. It runs shellcheck without -P, so shellcheck cannot follow a script's source line to Declare-BashScript.
Each repository runs its own copy of .github/scripts/shell-lint.sh instead: shfmt, then shellcheck, on the files whose first line names sh or bash. devlore-cli's make shell-lint runs its copy while its CI runs star lint shell, so the two check different files. The copies' pattern matches #!/usr/bin/env bash but not #!/bin/bash or #!/bin/sh, so David-Noble-at-work/personal's Home/common.Unix/.config/git/hooks/pre-commit is checked by neither.
No gate parses a script with its own shell, and none reads zsh. 56 zsh files across the three repositories, the completions whose first line is #compdef and personal's Install-UnixUserConfiguration, are checked by nothing; all 56 pass zsh -n. shellcheck does not read zsh (on a completion it stops at SC2148, not knowing the shell), and shfmt parses a completion as bash unless told otherwise, and fails: "parameter expansion flags are a zsh feature". personal's Home/common.Unix/.zshenv is read by zsh but declares # shellcheck shell=bash.
Found 2026-10-10 on Danoble-MBP-A, against the three clones as they stood on disk, answering the owner's question on bash -n.
Ruled 2026-10-10
The owner: "Can we add bash -n to the static code analysis on bash scripts? I suggest that we run the check in this order: shfmt, bash -n, shellcheck." Then: "I suggest that it run in the context of star lint." Then the reasoning, as the owner drew it:
[ Raw Code ]
│
▼
1. shfmt ──► (Catches structural typos & formatting)
│
▼
2. bash -n ──► (Validates engine-specific syntax compatibility)
│
▼
3. shellcheck ──► (Performs deep logic, type, & safety analysis)
│
▼
[ Deployable Script ]
"This documentation would be good to include with the star lint extensions for shell." And: "we should be smart enough to see whether we must run bash -n, sh -n, or zsh -n."
Asked whether to open this issue now as lane 94 of #916, to be worked after the config rollup: "open the issue as lane 94."
One limit, measured the same day: -n parses with the shell that runs the check, and parses only. macOS's /bin/bash 3.2 passes Start-Claude under -n, then refuses its ${resume_argument,,} at run time: "bad substitution".
Done when
-
star lint shellfinds a script by its first line: a#!line naming sh, bash or zsh,#!/bin/shand#!/bin/bashincluded; a# shellcheck shell=directive; or#compdef. - Each script is checked in the owner's order: shfmt, told the script's shell; then
bash -n,sh -norzsh -n, by the script's shell; then shellcheck, which a zsh file skips. - shellcheck is given the directory that holds Declare-BashScript, as the gates'
-Pgives it. - The extension's documentation carries the owner's picture.
- devlore-cli's
make shell-lintrunsstar lint shell; then David-Noble-at-work/personal's and NobleFactor/noblefactor-ops' CI run it in place of their own.github/scripts/shell-lint.sh, each in its own pull request. - A file whose directive names a shell other than the one that reads it, as
.zshenvdoes, is checked as this issue's plan rules.
Plan and design documents
| Kind | Document |
|---|---|
| Plan | Not yet opened |
Epic: NobleFactor/noblefactor-ops#142 · Feature: NobleFactor/noblefactor-ops#266 · Schedule: #916, lane 94
Activity