# Setup

> Verify the bash-format hook's runtime prerequisites and configuration for this repository. Use when: 'set up bash-format', 'configure bash-format', 'is bash-format working', shell lint or formatting silently isn't happening, or the hook reported a missing prerequisite. Actions: check (read-only verification, default) | apply (resolve what check found). Re-runnable and safe.

- Skill: `melodic-software/setup-24` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add melodic-software/setup-24`
- Raw SKILL.md: https://api.skillmd.com/api/skills/melodic-software/setup-24/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: melodic-software (https://skillmd.com/u/melodic-software)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/melodic-software/setup-24

---


## Purpose

Thin check-centric setup per the uniform setup contract (`docs/PLUGIN-PHILOSOPHY.md`
"Setup is explicit and repeatable" in the marketplace repository): `check` inspects and
reports, `apply` resolves. This plugin owns no consumer-project configuration. Linting
rules come from the repository's own `.shellcheckrc`, formatting from its `.editorconfig`,
and the only tunable is the native `userConfig` toggle. Every prerequisite is a `PATH`
binary the plugin never bundles, and the plugin never installs system packages, so `apply`
is guidance-only with **no write path**. It never modifies the repository, user settings, or
the plugin cache.

Action routing: no argument or `check` runs the check; `apply` runs the check first, then
offers remediation guidance. Both are non-interactive. Never prompt when the action is given.

## `check` (read-only)

The hook script (`${CLAUDE_PLUGIN_ROOT}/hooks/bash-format.sh`) is the single source of truth
for what it requires and how it resolves things.

**Read it first.** Probe what it actually does, don't recite this file. Then run each probe via
Bash and report a PASS/FAIL/INFO table with one remediation line per FAIL. Do not modify anything.

The lint pass and the format pass are independent; report each separately.

When the plugin's toggle is disabled, every prerequisite absence downgrades from FAIL to
INFO. The hook exits through its enabled-gate before probing anything, so a deliberately
disabled plugin is not broken. Report the probes informationally and note that re-enabling
restores the FAIL semantics.

1. **Bash version.** Check against the hook's documented floor (README Requirements),
   noting any features the hook degrades without (for example telemetry's `EPOCHREALTIME`,
   a Bash 5.0+ builtin).
2. **`jq`.** `command -v jq`. FAIL if absent: the hook then skips with a visible
   once-per-session notice instead of running either pass.
3. **`shellcheck`** (lint pass). `command -v shellcheck`. FAIL if absent: the lint pass
   skips with a visible once-per-session notice.
4. **`shfmt`** (format pass). `command -v shfmt`. Its FAIL/INFO status depends on the
   `.editorconfig` opt-in below, because the format pass runs **only when the repo has opted
   in**:
   - opted in AND `shfmt` absent → FAIL: the format pass skips with a visible once-per-session
     notice.
   - not opted in → INFO regardless of `shfmt`: the format pass stays quiet by design (the
     repo chose not to format), so a missing `shfmt` is not a defect here.
5. **`.editorconfig` shell opt-in.** Mirror the hook's opt-in logic
   (`shell_editorconfig_opt_in` / `section_applies_to_shell`), not merely "does an
   `.editorconfig` exist". The opt-in is an EditorConfig **section that names shell files**:
   a shell glob such as `[*.sh]`, `[*.bash]`, or `[*.{sh,bash}]` (including path-prefixed
   forms like `[**/*.sh]`), discovered by walking up from the file's directory to the repo
   root and stopping at a `root = true` config. A bare `[*]` catch-all does NOT count (most
   repos only set line-ending / charset properties there). Path-only sections like
   `[scripts/**]` do NOT count either. Report as INFO: whether a governing shell section
   exists and therefore whether the format pass is active. If none exists, INFO-note the
   consequence per the hook's logic: shell files are left unformatted rather than rewritten
   to shfmt's built-in defaults.
6. **`.shellcheckrc`.** INFO: ShellCheck auto-discovers `.shellcheckrc` by walking up from
   the file's directory. Report whether one exists; its absence is not a FAIL (ShellCheck
   applies its own defaults).
7. **Hook toggle.** Report the effective `bash_format_enabled` value:
   `${user_config.bash_format_enabled}` (unexpanded or empty means default `true`; any value
   other than `true` disables the hook).
8. **Hook registration.** INFO: confirm the plugin is enabled for this project
   (`/plugin` → Installed) rather than parsing settings files.
9. **Project scope.** INFO: when `CLAUDE_PROJECT_DIR` is set, the hook acts only
   on shell files inside it (symlink-resolved membership guard in the shared hook
   library, aware of Windows 8.3 short-name spellings of in-project paths, a
   per-volume property; only volumes with 8.3 generation enabled produce them);
   a `.sh`/`.bash` file written *outside* the project (temp/scratchpad
   dirs) is silently skipped. No lint, no format, no notice. The OS temp tree
   counts as outside even when it sits under `CLAUDE_PROJECT_DIR`, unless the
   project root itself lives under temp. When
   `CLAUDE_PROJECT_DIR` is **unset** (e.g. some headless `-p` sessions) the guard
   is skipped and any existing edited file is processed. Report this so a green
   `check` is not read as "every shell edit anywhere is covered".

When every probe passes, report the result **with the scope caveat** (item 9).
Never an unqualified "fully operational", which would imply out-of-project shell
edits are covered when they are deliberately skipped.

## `apply` (idempotent)

Run `check`, then for each FAIL point at the resolution. This skill installs nothing:

- missing `shellcheck`: install guidance from the README Requirements section
  (the [ShellCheck install guide](https://github.com/koalaman/shellcheck#installing)); this
  skill never installs system packages.
- missing `shfmt` while the repo opts in: install guidance
  ([shfmt](https://github.com/mvdan/sh#shfmt)); this skill never installs system packages.
- missing `jq` / Bash: platform install instructions from the README Requirements section.
- no shell `.editorconfig` opt-in (and formatting is wanted): explain that adding a governing
  shell section (`[*.sh]`, `[*.bash]`, or `[*.{sh,bash}]`) to an `.editorconfig` opts the
  repo in. A bare `[*]` is not enough, but this skill does not write it. `.editorconfig` is
  cross-cutting (it governs every editor and tool in the repo), so the choice and the edit
  belong to the consumer.
- toggle off: reconfigure through Claude Code's native flow, per the marketplace's
  plugin-reconfiguration convention
  (<https://github.com/melodic-software/claude-code-plugins/blob/main/docs/conventions/plugin-reconfiguration/README.md>,
  which owns the verified-version record): interactive
  `/plugin configure bash-format@<marketplace>` any time, or headless
  `claude plugin install bash-format@<marketplace> -s <scope> --config bash_format_enabled=true`
  (repeatable per key) — against an already-installed plugin it prints `already installed`
  **and still writes the value**. Do **not** uninstall to reconfigure: that drops this plugin's
  entire stored `pluginConfigs` entry, resetting every option in the README's Options reference
  to its manifest default. `-s` defaults to `user`; pass the scope `claude plugin list` reports
  for this plugin, and run from that project's directory for a `project`/`local` scope, or the
  write lands at a scope that does not load. This skill never writes user settings or
  `pluginConfigs`. Afterwards rerun `check` in a **fresh session** — the rendered
  `${user_config.*}` is injected at skill load and each hook receives its
  `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a same-session
  `check` still reports the OLD value; report the observed effective value, never an
  unobserved change.

After pointing at a remediation, re-run the relevant `check` probe and report its actual
result. Never claim resolved on the reader's report that they installed something.

Re-running `apply` after everything passes changes nothing and reports "already configured".

## What this skill does NOT do

- Run the linter or formatter. Editing any `.sh` or `.bash` file exercises the hook
  end-to-end.
- Write the plugin cache, Claude Code user settings, or `pluginConfigs`. Nor the repository,
  including `.editorconfig` / `.shellcheckrc`. Every prerequisite is a `PATH` binary or the
  native toggle, so remediation is guidance only.
- Download or execute tools during `check` beyond the read-only `command -v` presence probes.

## Gotchas

- **`ENAMETOOLONG` when grepping the plugin cache.** Installed plugins run from a
  deeply nested, cache-isolated path. Piping a `grep`/`rg` over the long absolute
  path to `hooks/hook-utils.sh` (or another bundled file) can spawn-fail with
  `ENAMETOOLONG` on some hosts. Read the file directly (or `cd` into the plugin
  hooks dir first and grep a short relative path) rather than passing the full
  cache path on the command line.
- **`check` PASS ≠ every shell edit is covered.** When `CLAUDE_PROJECT_DIR` is set
  the hook is project-scoped (probe 9): shell files written outside it are silently
  skipped, so a fully green `check` still does not cover out-of-project edits. (When
  `CLAUDE_PROJECT_DIR` is unset the scoping does not apply. See probe 9.)
- **`shfmt` FAIL is opt-in-conditional.** A missing `shfmt` is only a FAIL when an
  `.editorconfig` section governs shell files; without that opt-in it is INFO, not
  a defect. Resolve the `.editorconfig` opt-in state before calling `shfmt` a failure.

