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. actionlint
auto-discovers its own optional config from the repository, and the tunables are the native
userConfig options (the actionlint_enabled toggle and stdin_read_timeout). 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/actionlint-check.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.
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.
- 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).
jq. command -v jq. FAIL if absent: the hook then skips with a visible
once-per-session notice instead of linting.
actionlint. command -v actionlint. FAIL if absent: the hook skips workflow lint
with a visible once-per-session notice (it ships no binary of its own).
- actionlint config. INFO: actionlint auto-discovers an optional
.github/actionlint.yaml from the repository when present. It is not required. actionlint
runs with its built-in defaults without one. Report whether one exists for the reader's
awareness; its absence is not a FAIL.
- Hook toggle. Report the effective
actionlint_enabled value:
${user_config.actionlint_enabled} (unexpanded or empty means default true; any value
other than true disables the hook).
5b. Stdin read timeout. INFO: report the effective stdin_read_timeout value:
${user_config.stdin_read_timeout} (unexpanded or empty means default 2 seconds,
minimum 1). It is an IDLE bound. Any byte arriving resets it, so it fires only once
the pipe has gone silent for that long, at which point this hook fails open. A value
read -t will not accept, or 0, falls back to the default.
- Hook registration. INFO: confirm the plugin is enabled for this project
(
/plugin → Installed) rather than parsing settings files.
apply (idempotent)
Run check, then for each FAIL point at the resolution. This skill installs nothing:
- missing
actionlint: platform install guidance from the README Requirements section
(the actionlint install guide).
- missing
jq / Bash: platform install instructions from the README Requirements section.
- 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 actionlint@<marketplace> any time, or headless
claude plugin install actionlint@<marketplace> -s <scope> --config actionlint_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".
Gotchas
- A userConfig knob is reachable natively only if the manifest declares it. Per current
docs,
claude plugin install --config <key=value> sets options "declared in the plugin's
manifest". An undeclared key silently cannot be set through native config surfaces (a raw
settings env block still works). That is why stdin_read_timeout is declared in this
plugin's manifest even though the shared hook lib supplies its default; hook plugins reusing
the shared lib should declare it too.
--config's post-install behavior is undocumented, so the guidance above rests on
observation. The official docs describe --config only as a claude plugin install flag
and say nothing about an already-installed plugin. The verified-version record — which CLI
release the still-writes claim was observed on, and which conditions it covered — lives only
in the plugin-reconfiguration convention cited in apply above.
-shellcheck= / -pyflakes= are deliberate, and the deadlock claim is a local
observation. The hook disables actionlint's external run-block linters primarily for
edit-time latency; the additional "ShellCheck deadlocks on large blocks under the Windows
subprocess IPC path in actionlint 1.7.x" rationale is the hook author's own reproduction.
No matching upstream rhysd/actionlint issue as of 2026-07-23. The latency rationale alone
justifies the flags for an advisory edit-time hook; deep run-block linting belongs in a
commit hook or CI.
What this skill does NOT do
- Run the linter. Editing any
.github/workflows/*.yml or *.yaml file exercises the hook
end-to-end.
- Write the plugin cache, Claude Code user settings, or
pluginConfigs. Nor the repository.
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.
1---2name: setup-163description: Verify the actionlint-check hook's runtime prerequisites and configuration for this repository. Use when: 'set up actionlint', 'configure actionlint', 'is actionlint working', workflow lint 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.4---56## Purpose78Thin check-centric setup per the uniform setup contract (`docs/PLUGIN-PHILOSOPHY.md`9"Setup is explicit and repeatable" in the marketplace repository): `check` inspects and10reports, `apply` resolves. This plugin owns no consumer-project configuration. actionlint11auto-discovers its own optional config from the repository, and the tunables are the native12`userConfig` options (the `actionlint_enabled` toggle and `stdin_read_timeout`). Every13prerequisite is a `PATH` binary the plugin never bundles, and the plugin never installs14system packages, so `apply` is guidance-only with **no write path**. It never modifies the15repository, user settings, or the plugin cache.1617Action routing: no argument or `check` runs the check; `apply` runs the check first, then18offers remediation guidance. Both are non-interactive. Never prompt when the action is given.1920## `check` (read-only)2122The hook script (`${CLAUDE_PLUGIN_ROOT}/hooks/actionlint-check.sh`) is the single source of23truth for what it requires and how it resolves things.2425**Read it first.** Probe what it actually does, don't recite this file. Then run each probe via26Bash and report a PASS/FAIL/INFO table with one remediation line per FAIL. Do not modify anything.2728When the plugin's toggle is disabled, every prerequisite absence downgrades from FAIL to29INFO. The hook exits through its enabled-gate before probing anything, so a deliberately30disabled plugin is not broken. Report the probes informationally and note that re-enabling31restores the FAIL semantics.32331. **Bash version.** Check against the hook's documented floor (README Requirements),34 noting any features the hook degrades without (for example telemetry's `EPOCHREALTIME`,35 a Bash 5.0+ builtin).362. **`jq`.** `command -v jq`. FAIL if absent: the hook then skips with a visible37 once-per-session notice instead of linting.383. **`actionlint`.** `command -v actionlint`. FAIL if absent: the hook skips workflow lint39 with a visible once-per-session notice (it ships no binary of its own).404. **actionlint config.** INFO: actionlint auto-discovers an optional41 `.github/actionlint.yaml` from the repository when present. It is not required. actionlint42 runs with its built-in defaults without one. Report whether one exists for the reader's43 awareness; its absence is not a FAIL.445. **Hook toggle.** Report the effective `actionlint_enabled` value:45 `${user_config.actionlint_enabled}` (unexpanded or empty means default `true`; any value46 other than `true` disables the hook).475b. **Stdin read timeout.** INFO: report the effective `stdin_read_timeout` value:48 `${user_config.stdin_read_timeout}` (unexpanded or empty means default `2` seconds,49 minimum `1`). It is an IDLE bound. Any byte arriving resets it, so it fires only once50 the pipe has gone silent for that long, at which point this hook fails open. A value51 `read -t` will not accept, or `0`, falls back to the default.526. **Hook registration.** INFO: confirm the plugin is enabled for this project53 (`/plugin` → Installed) rather than parsing settings files.5455## `apply` (idempotent)5657Run `check`, then for each FAIL point at the resolution. This skill installs nothing:5859- missing `actionlint`: platform install guidance from the README Requirements section60 (the [actionlint install guide](https://github.com/rhysd/actionlint/blob/main/docs/install.md)).61- missing `jq` / Bash: platform install instructions from the README Requirements section.62- toggle off: reconfigure through Claude Code's native flow, per the marketplace's63 plugin-reconfiguration convention64 (<https://github.com/melodic-software/claude-code-plugins/blob/main/docs/conventions/plugin-reconfiguration/README.md>,65 which owns the verified-version record): interactive66 `/plugin configure actionlint@<marketplace>` any time, or headless67 `claude plugin install actionlint@<marketplace> -s <scope> --config actionlint_enabled=true`68 (repeatable per key) — against an already-installed plugin it prints `already installed`69 **and still writes the value**. Do **not** uninstall to reconfigure: that drops this plugin's70 entire stored `pluginConfigs` entry, resetting every option in the README's Options reference71 to its manifest default. `-s` defaults to `user`; pass the scope `claude plugin list` reports72 for this plugin, and run from that project's directory for a `project`/`local` scope, or the73 write lands at a scope that does not load. This skill never writes user settings or74 `pluginConfigs`. Afterwards rerun `check` in a **fresh session** — the rendered75 `${user_config.*}` is injected at skill load and each hook receives its76 `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a same-session77 `check` still reports the OLD value; report the observed effective value, never an78 unobserved change.7980After pointing at a remediation, re-run the relevant `check` probe and report its actual81result. Never claim resolved on the reader's report that they installed something.8283Re-running `apply` after everything passes changes nothing and reports "already configured".8485## Gotchas8687- **A userConfig knob is reachable natively only if the manifest declares it.** Per current88 docs, `claude plugin install --config <key=value>` sets options "declared in the plugin's89 manifest". An undeclared key silently cannot be set through native config surfaces (a raw90 settings `env` block still works). That is why `stdin_read_timeout` is declared in this91 plugin's manifest even though the shared hook lib supplies its default; hook plugins reusing92 the shared lib should declare it too.93- **`--config`'s post-install behavior is undocumented, so the guidance above rests on94 observation.** The official docs describe `--config` only as a `claude plugin install` flag95 and say nothing about an already-installed plugin. The verified-version record — which CLI96 release the still-writes claim was observed on, and which conditions it covered — lives only97 in the plugin-reconfiguration convention cited in `apply` above.98- **`-shellcheck=` / `-pyflakes=` are deliberate, and the deadlock claim is a local99 observation.** The hook disables actionlint's external run-block linters primarily for100 edit-time latency; the additional "ShellCheck deadlocks on large blocks under the Windows101 subprocess IPC path in actionlint 1.7.x" rationale is the hook author's own reproduction.102 No matching upstream rhysd/actionlint issue as of 2026-07-23. The latency rationale alone103 justifies the flags for an advisory edit-time hook; deep run-block linting belongs in a104 commit hook or CI.105106## What this skill does NOT do107108- Run the linter. Editing any `.github/workflows/*.yml` or `*.yaml` file exercises the hook109 end-to-end.110- Write the plugin cache, Claude Code user settings, or `pluginConfigs`. Nor the repository.111 Every prerequisite is a `PATH` binary or the native toggle, so remediation is guidance only.112- Download or execute tools during `check` beyond the read-only `command -v` presence probes.