Skip to content
Back to skills

Setup

BSecurity

Verify the rate-limit-guard plugin's wiring on this machine: jq, node, the installed statusline shim, statusline wiring (including legacy version-pinned plugin-cache paths), tee freshness, and the StopFailure hook. Print the exact statusline edit for the operator to apply, and install the statusline shim. Use when: 'set up rate-limit-guard', 'is the rate-limit tee working', 'wire the rate-limit statusline', the tee file is stale, or a consuming loop lane reports guard mode unknown. Actions: c...

  • 13 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 2, 2026
ai-agentsgoshellbashreactnodenodejsgitapi

Works with

  • claude code
  • api

Security analysis

B88/100
  • criticalPipes output to a shell interpreter

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add melodic-software/claude-code-plugins --skill setup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Setup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Setup
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/melodic-software-setup-ed5448fc/badge)](https://www.skillsdirectory.com/skills/melodic-software-setup-ed5448fc)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
description: "Verify the rate-limit-guard plugin's wiring on this machine: jq, node, the installed statusline shim, statusline wiring (including legacy version-pinned plugin-cache paths), tee freshness, and the StopFailure hook. Print the exact statusline edit for the operator to apply, and install the statusline shim. Use when: 'set up rate-limit-guard', 'is the rate-limit tee working', 'wire the rate-limit statusline', the tee file is stale, or a consuming loop lane reports guard mode unknown. Actions: check (read-only; never edits settings), apply (writes ONLY ~/.claude/rate-limit-guard/bin/statusline-shim.sh, on explicit request)."
argument-hint: "[check|apply]"
user-invocable: true
disable-model-invocation: true
shell: bash
---

## Pre-computed context

`check`'s `jq` and `node` probes ran at load time. Read these rows instead of re-issuing them; each
shows the tool's path when present, or `absent` when missing:

- `jq`: !`{ command -v jq 2>/dev/null || echo "absent"; }`
- `node`: !`{ command -v node 2>/dev/null || echo "absent"; }`

A row reading `[shell command execution disabled by policy]` carries no result: run that tool's
`command -v` probe via Bash instead.

## Purpose

Narrow-write setup. This plugin's **configuration** surface is three kinds of thing setup cannot
conformingly write:

- **System tools** (`jq`, `node`). `check` probes them; installing them is the operator's.
- **One native `userConfig` toggle** (`rate_limit_guard_enabled`), whose only stored home is the
  `pluginConfigs` setup must never write. 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
  rate-limit-guard@<marketplace>` any time, or headless `claude plugin install
  rate-limit-guard@<marketplace> -s <scope> --config rate_limit_guard_enabled=<value>` (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, and run from that
  project's directory for a `project`/`local` scope, or the rerun adds a second install
  record at the scope passed and enables the plugin there; the value itself always lands in user
  settings. A rejected value prints a warning yet exits 0, so read the output.
  Afterwards rerun `check` in a **fresh session**, because the rendered `${user_config.*}` is
  injected at skill load and each hook's `CLAUDE_PLUGIN_OPTION_*` is fixed at session start, so a
  same-session `check` still reports the old value; report the observed effective value, never an
  unobserved change.
- **The statusline wiring**, which lives in the **user's own** `settings.json`, neither
  `userConfig` nor tracked project config, and a Claude Code settings surface setup must never
  mutate.

So `check` inspects, reports PASS/FAIL/INFO with one remediation line per FAIL, and **prints the
exact statusline edit for the operator to apply by hand**, fully resolved, marked as the
operator's, and naming what re-invalidates it. Silence would not be the conforming response on an
unwritable surface; a printed edit is.

What obliges an `apply` is not configuration at all. The machine files under
`~/.claude/rate-limit-guard/` that the tee and the hook write remain runtime-owned plugin data, not
an operator-editable surface, and so does the shim's own resolved-tee cache, which follows the
effective config dir rather than `$HOME` and so sits elsewhere under a relocated
`CLAUDE_CONFIG_DIR`,
but the **statusline shim** `~/.claude/rate-limit-guard/bin/statusline-shim.sh` is an owned
writable artifact this plugin must place, because it is the durable path the operator's own wiring
names. `apply` writes that one file and nothing else.

Action routing: no argument or `check` runs the check.

**Why the shim exists (the durable-wiring rule).** `${CLAUDE_PLUGIN_ROOT}` is version-pinned and
changes on every plugin update, and the old version directory is pruned about 14 days later
(plugins reference, "Plugin cache and file access"). A statusline wired straight to
`<plugin-root>/scripts/statusline-tee.sh` therefore stops teeing at the next version bump and, once
the old directory is pruned, `bash <missing-path>` exits 127 and takes the operator's whole
statusline down with it. So the operator wires the **shim**, never the tee: the shim lives at a
path that never changes, resolves the newest installed tee at run time, and degrades to running the
wrapped command alone when no tee is installed.

The scripts are the source of truth for their own behavior. Read
`${CLAUDE_PLUGIN_ROOT}/scripts/statusline-shim.sh`,
`${CLAUDE_PLUGIN_ROOT}/scripts/statusline-tee.sh` and
`${CLAUDE_PLUGIN_ROOT}/hooks/record-rate-limit-stop.sh` first; probe what they actually do rather
than reciting this file. The consumer-facing constants (tee path, threshold, staleness rule) are
owned by `${CLAUDE_PLUGIN_ROOT}/reference/reader-contract.md`.

## `check` (read-only)

1. **`jq`.** The pre-computed `jq` row. FAIL if absent: without it the wrapper cannot tee (it stays
   transparent and shows a visible notice) and the standalone statusline degrades. Remediation:
   install jq (<https://jqlang.org/download/>).
   Then the `node` row. FAIL if absent: the `StopFailure` row in `hooks/hooks.json` is exec form,
   `"command": "node"` with `hooks/exec-bash.mjs` in `args`, so without `node` on `PATH` the hook
   never launches, a launch failure is non-blocking, and no rate-limit stop is recorded. The
   statusline tee does not need it. Remediation: install Node.js (<https://nodejs.org/en/download>).
   Verified 2026-09-29 against <https://code.claude.com/docs/en/setup>: the native `claude` binary
   does not ship or use Node.js. Recheck when that page says the native binary bundles Node or the
   hook row stops using `node`.
2. **Installed shim state.** The shim is the wiring target, so check it before the wiring. Compare
   `~/.claude/rate-limit-guard/bin/statusline-shim.sh` (the durable shim copy) against
   `${CLAUDE_PLUGIN_ROOT}/scripts/statusline-shim.sh` (the shipped source) and classify per
   [reference/legacy-statusline-detect.md](reference/legacy-statusline-detect.md) "Installed shim
   state", shared with the sibling guard plugin and synced byte-identical.
3. **Statusline wiring state.** Read (never write) every settings scope that can carry a
   `statusLine` (user `~/.claude/settings.json`, project `.claude/settings.json`, local
   `.claude/settings.local.json`) and determine which one owns the effective command (the most
   specific scope wins). All wiring states below are evaluated against that effective command,
   and the printed edit in step 6 targets that scope's file. Wiring the user file while a
   project-level `statusLine` shadows it would apply cleanly and never run; when a shadow
   exists, say so explicitly and print the edit for the shadowing file (or note that removing
   the override is the alternative). Distinguish four states:
   - **No `statusLine` configured.** The wrapper is not running because nothing is. Print the
     standalone wiring from the template below (the shim is then the whole statusline).
   - **`statusLine` present, command references neither the shim nor `statusline-tee.sh`.**
     wrapper missing. Print the wrapped wiring below with the user's current command preserved as
     the wrapped command.
   - **`statusLine` references a `rate-limit-guard` `statusline-tee.sh` under the plugin cache.**
     Legacy version-pinned wiring: classify, report, and remediate per
     [reference/legacy-statusline-detect.md](reference/legacy-statusline-detect.md) "Legacy
     version-pinned wiring" (the fix's `apply` is step 2's).
   - **`statusLine` invokes `~/.claude/rate-limit-guard/bin/statusline-shim.sh`.** PASS. No path
     comparison against `${CLAUDE_PLUGIN_ROOT}` applies or is meaningful here; the shim resolves
     the tee at run time.
4. **Tee freshness.** Probe the fixed contract path `~/.claude/rate-limit-guard/rate-limits.json`:
   - `jq -e '.rate_limits and .captured_at'` passes and `captured_at` is within the staleness
     window the reader contract's operable floor fixes (read the value there, never from here;
     a number restated in this file is a copy nothing keeps in step with the contract) → PASS
     (proactive mode available).
   - File fresh but `rate_limits` absent → INFO: this session's auth exposes no subscription
     windows (API-key or enterprise auth); consumers correctly run reactive-only. Not a defect.
   - File absent or stale while the wiring in step 3 looked correct → FAIL: the wrapper is wired
     but not running (statusline refreshes only in interactive sessions; also re-check steps 2 and
     3, because a shim that is wired but not installed produces exactly this). Note the file only updates
     while some interactive session is active.
5. **StopFailure hook.** INFO: the hook needs no wiring (it registers via the plugin's
   `hooks/hooks.json`); confirm the plugin is enabled (`/plugin` → Installed) and report the
   effective kill switch `${user_config.rate_limit_guard_enabled}` (unexpanded or empty means the
   default `true`). Report whether `~/.claude/rate-limit-guard/stop-events.jsonl` exists. Absent
   just means no rate-limit stop has been recorded yet.
6. **Print the operator edit.** Always print the applicable `settings.json` statusline edit,
   marked clearly as the operator's to apply. The wiring target is the shim's fixed path, never
   `${CLAUDE_PLUGIN_ROOT}`, which is version-pinned and belongs in no operator file.

   Compose the value by running
   `bash "${CLAUDE_PLUGIN_ROOT}/scripts/compose-statusline-wiring.sh"` over the effective
   `statusLine` value from step 3, never by peeling and wrapping the string yourself:

   ```bash
   jq '.statusLine' <the settings file that owns the effective command> |
     bash "${CLAUDE_PLUGIN_ROOT}/scripts/compose-statusline-wiring.sh" \
       --wrap 'bash ~/.claude/rate-limit-guard/bin/statusline-shim.sh' --block --explain
   ```

   Read [`reference/unwrap-before-compose.md`](reference/unwrap-before-compose.md) for that
   script's argument and exit-code contract and the judgments it leaves to you, shared
   byte-identical with context-guard. Composing by hand double-wraps a sibling tee and stacks
   another `sh -c` layer on every re-run. The blocks below are this plugin's printed paths; the
   script emits whichever one the current value selects, and its angle-bracket placeholders show
   each form's shape. The script fills them; substituting into one by hand is the arithmetic it
   exists to replace.

   Wrapping an existing statusline command (the script substitutes the operator's own renderer,
   recovered by the peel, as the trailing arguments):

   ```json
   {
     "statusLine": {
       "type": "command",
       "command": "bash ~/.claude/rate-limit-guard/bin/statusline-shim.sh <current statusline command>"
     }
   }
   ```

   No statusline configured (standalone minimal statusline):

   ```json
   {
     "statusLine": {
       "type": "command",
       "command": "bash ~/.claude/rate-limit-guard/bin/statusline-shim.sh"
     }
   }
   ```

   When the script selects the shell-wrapped form, `<escaped original command>` is already escaped
   and JSON-escaped in its output and needs no further editing:

   ```json
   {
     "statusLine": {
       "type": "command",
       "command": "bash ~/.claude/rate-limit-guard/bin/statusline-shim.sh sh -c '<escaped original command>'"
     }
   }
   ```

   Sibling tees compose by nesting, each through its own shim. The tees are transparent wrappers,
   so the innermost command still owns stdout and the exit code. Print this form (its tee outermost,
   matching that plugin's setup skill) only when `context-guard` is installed and its shim is
   already present at `~/.claude/context-guard/bin/statusline-shim.sh`. The sibling shim is written
   by `/context-guard:setup apply`, which the operator may not have run yet. Naming a path that
   does not exist reintroduces exactly the failure this wiring exists to remove, because `bash
   <missing-path>` exits 127 before the operator's renderer ever runs. When the sibling plugin is
   installed but its shim is absent, print the single-shim form above and say that
   `/context-guard:setup apply` followed by a re-run of this check yields the combined wiring:

   ```json
   {
     "statusLine": {
       "type": "command",
       "command": "bash ~/.claude/context-guard/bin/statusline-shim.sh bash ~/.claude/rate-limit-guard/bin/statusline-shim.sh <current statusline command>"
     }
   }
   ```

   The combined form is one invocation, not a second transform: pass both shims as `--wrap`
   prefixes in the order they nest, context-guard's first.

   ```bash
   jq '.statusLine' <the settings file that owns the effective command> |
     bash "${CLAUDE_PLUGIN_ROOT}/scripts/compose-statusline-wiring.sh" \
       --wrap 'bash ~/.claude/context-guard/bin/statusline-shim.sh' \
       --wrap 'bash ~/.claude/rate-limit-guard/bin/statusline-shim.sh' --block --explain
   ```

   Naming only one shim drops the other, because the peel strips every shim prefix it finds. The
   shim paths are the only part that nests; whether the innermost command takes an `sh -c` adapter
   is the same decision the script already made:

   ```json
   {
     "statusLine": {
       "type": "command",
       "command": "bash ~/.claude/context-guard/bin/statusline-shim.sh bash ~/.claude/rate-limit-guard/bin/statusline-shim.sh sh -c '<escaped original command>'"
     }
   }
   ```

   State the measured cost with the combined form: each tee adds roughly 0.6–0.9 s per statusline
   refresh on Windows/Git Bash (process-spawn bound), on top of the operator's own statusline
   command.
   `refreshInterval` sets how often that runs; the statusline is not on the input path, so the
   cost is display latency, not typing latency.

   Windows note: the command must run under Git Bash. `bash` is invoked explicitly for exactly
   that reason (the script's stated shell requirement); with Git Bash absent Claude Code routes
   statusline commands through PowerShell and this wiring does not apply (statusline reference,
   "Windows configuration"). State this with the printed edit: the wiring is applied once and
   survives every later plugin update, because the shim, not the version-pinned cache path, is
   what the settings file names.
7. **Dotfiles tracking proposal.** The printed edit changes a durable user-scope file the operator
   maintains. When the operator's home directory is managed by a dotfiles system (chezmoi, yadm, a
   bare-repo setup, ...), surface the reminder to capture the `settings.json` change through that
   system's own add/track flow so the wiring survives machine rebuilds. This skill only surfaces
   the reminder; it runs no dotfiles command.

## `apply` (writes ONLY the shim, on explicit request)

Copy `${CLAUDE_PLUGIN_ROOT}/scripts/statusline-shim.sh` to
`~/.claude/rate-limit-guard/bin/statusline-shim.sh`, creating `bin/` if needed, and `chmod +x` the
result (a no-op on Windows ACL volumes; the wiring invokes it through `bash` anyway):

- The installed copy is **byte-identical** to the shipped source, never a rewrite, never a
  templated variant. That is what makes `check` step 2 a plain `cmp`.
- **Idempotent**: if the file already exists and compares equal, write nothing and say so.
  Otherwise overwrite it (this is the update path after a plugin version bump changes the shim)
  and report the `# shim-revision:` values, old → new.
- The shim is **inert until wired**: installing it starts nothing. Only the operator's
  `settings.json` edit, step 6 of `check`, which this skill never applies, puts it on the
  statusline path. Say that explicitly when reporting the write.
- After installing, print the wiring edit (`check` step 6) so the operator's next action is in
  front of them, and note that a statusline already wired to the shim needs no change now or on
  any future plugin update.

`apply` never touches `settings.json`, `rate-limits.json`, `stop-events.jsonl`, or anything outside
`~/.claude/rate-limit-guard/bin/`.

## Uninstalling

Uninstalling the plugin removes the cache directory, not the operator's files. Nothing breaks: the
shim finds no tee and passes the wrapped statusline through unchanged (a wired-standalone shim
prints one notice line instead). Two operator cleanup steps remain, and their order matters.
Report both together, in this order, when asked how to back this out:

1. **Unwrap the `statusLine` command first**, restoring the operator's own renderer (or removing
   the field entirely if the shim was the whole statusline).
2. **Then remove `~/.claude/rate-limit-guard/`.** Under a relocated `CLAUDE_CONFIG_DIR`, also remove
   `<that config dir>/rate-limit-guard/`: the shim's resolved-tee cache follows the effective config
   dir rather than `$HOME`, so it is the one file this step would otherwise leave behind. Harmless if
   missed, since nothing reads it once the plugin is gone.

Deleting the directory while the wiring still names the shim leaves `settings.json` invoking a
missing file: `bash <missing-path>` exits 127 and takes the whole statusline down, the exact
failure the shim exists to prevent. The shim's own no-tee fallback cannot cover this, because the
fallback lives in the file that was just deleted.

## What this skill does NOT do

- Write the plugin cache, Claude Code user settings, or `pluginConfigs`, per the uniform setup
  contract (`docs/plugin-philosophy.md` "Setup is explicit and repeatable" in the marketplace
  repository). Nor `settings.json` (user or project) or any other Claude Code settings surface;
  the printed edit is the operator's to apply.
- Install `jq`, `node` or any system package.
- Write to the contract files. The wrapper and the hook own `rate-limits.json` and
  `stop-events.jsonl`, and the shim owns its own `.statusline-tee-path` cache at run time; `apply`
  owns only `bin/statusline-shim.sh`.
- Write anywhere outside `~/.claude/rate-limit-guard/`, including the sibling `context-guard`
  directory, whose own setup skill installs that plugin's shim.

## Spoke paths

The `reference/` files write the plugin's root directory as `<plugin-root>`, which is
`${CLAUDE_PLUGIN_ROOT}`. Put that path in place of the placeholder before running a command or
writing it into a brief. Those files arrive through the Read tool as plain bytes, so a `${…}` token
in them would reach the Bash tool unsubstituted, and the Bash tool's environment has no
`CLAUDE_PLUGIN_ROOT` to expand it from. Basis: the plugins reference,
<https://code.claude.com/docs/en/plugins-reference#where-each-variable-resolves>, verified
2026-09-30; recheck when that table adds supporting files to where a `${…}` reference resolves.

Files in this skill

  • SKILL.md19.3 KB
  • evals/evals.json6.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.