npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shizhanyu13/ironbound-hook

v0.1.2

Published

Claude Code PreToolUse hook that hard-gates dangerous shell commands before they ever run, with a double-layer degrade counter (same-issue repeated -> 'ask' human review; cumulative -> hard 'deny'). Zero DSH/runtime deps; cross-platform. Ports the deny +

Downloads

520

Readme

ironbound-hook

Ironbound hard-gate for Claude Code. A Claude Code PreToolUse hook that blocks dangerous shell commands before they run, with a double-layer degrade counter: the same issue repeated escalates from a flat deny to an ask (human review), and cumulative abuse is a hard deny. Zero DSH dependencies, zero runtime deps, cross-platform.

npm version npm downloads License: MIT ESM


Why this exists

@shizhanyu13/dsh-ironbound-policy hard-gates dangerous tool calls inside the DeepSeek Harness, keying its degrade counter on an in-process Agent object. But Claude Code's PreToolUse hook is a fresh process every call — an in-memory counter would reset each time, so the degrade/block escalation would never trigger.

@shizhanyu13/ironbound-hook takes the same deny list + degrade logic and:

  • persists the counter to <cwd>/.ironbound/counter.json (override with IRONBOUND_STATE_DIR), so the escalation survives across calls;
  • speaks the Claude Code PreToolUse contract directly (hookSpecificOutput with permissionDecision);
  • is fully buildable (tsc → lib/) with 0 runtime deps, so it runs anywhere Node 20+ is.

Division of labor

  • dsh-ironbound-policy — the same guard inside DSH (in-process counter on an Agent).
  • ironbound-hook (this package) — the same guard at the Claude Code boundary (file-persisted counter, host decides allow/deny/ask).

The degrade-counter differentiation

| state | permissionDecision | model-facing reason | |---|---|---| | safe / non-shell tool | (no output — tool runs) | — | | first deny-list hit | deny | dangerous command blocked (A:Bypass): <cmd> | | same issue repeated to the per-issue limit | ask | degraded — this issue repeated Nx; human review required | | cumulative denies reach the total limit | deny | BLOCKED — cumulative limit (N/M) reached; human review required | | a later clean shell run | (no output — tool runs) | resets the per-issue counter (same-issue streak recovered) |

A model that keeps hitting the same wall escalates from a flat deny to an ask (a human gates it), and a cumulative abuser is a hard deny — never a silent allow.

The loop closes: a clean shell command (a shell tool, non-empty command, no deny-list match) resets the per-issue counter, so a same-issue streak recovers back to deny instead of ratcheting irreversibly into BLOCKED. The total is preserved on a clean run — it is lifetime accountability and never resets, so a persistent abuser still hits the hard block. Enforce-vs-recover, not just punish.


Quickstart

1. Wire the hook into Claude Code

Write this to .claude/settings.json (or merge into your project settings):

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "npx -y @shizhanyu13/ironbound-hook" }]
      }
    ]
  }
}

npx caches the package, so the hook starts fast after the first run. For an offline/standalone install, add @shizhanyu13/ironbound-hook as a devDependency and point command at node_modules/.bin/ironbound-hook.

2. Configure the gates (optional)

| env var | default | meaning | |---|---|---| | IRONBOUND_STATE_DIR | <cwd>/.ironbound | where counter.json lives | | IRONBOUND_PER_ISSUE_LIMIT | 3 | same-issue repeats before an ask | | IRONBOUND_TOTAL_LIMIT | 10 | cumulative denies before a hard deny | | IRONBOUND_DENY_EXTRA | (empty) | extra deny-list regexes (newline/comma separated) |


The default deny list

Ported verbatim from Claude Code's /scripts/defenses/block-dangerous-cmd.sh:

rm -rf, git push --force, git reset --hard, git clean -f[d|x],
git push origin --delete, git commit --no-verify, git commit -n,
DROP (TABLE|DATABASE|SCHEMA|INDEX), TRUNCATE TABLE,
prisma migrate reset, prisma db push --force-reset,
shutdown|reboot|restart, format|diskpart, reg delete,
chmod -R 777, del /f /s | rmdir /s /q | rd /s /q

Extend it with IRONBOUND_DENY_EXTRA — each pattern is a JS regex, matched case-insensitively and added to the built-in list.


Behavior contract

  • Matching shell tools (claude code + DSH aliases): Bash, pwsh, PowerShell, tool-bash, tool-pwsh, Shell.
  • Non-shell tools and safe commands emit no stdout — a PreToolUse hook that outputs nothing lets the tool run.
  • Atomic-enough persistencesaveState writes to counter.json and never throws; persistence failure can never break the hook.
  • Malformed/missing stdin exits 0 without blocking — a broken hook must never block a tool call.

API

import { evaluateCommand, permissionDecisionOf, BUILTIN_DENY } from '@shizhanyu13/ironbound-hook'
import { hookDecision, run } from '@shizhanyu13/ironbound-hook/hook'
  • @shizhanyu13/ironbound-hook (the . export) is the pure coreBUILTIN_DENY, evaluateCommand, recordDenial, permissionDecisionOf, denialReason, isShell, matchDeny, emptyState. No I/O, no side effects.
  • @shizhanyu13/ironbound-hook/hook is the stdin/stdout hookhookDecision, loadState, saveState, respond, run, plus the CLI entrypoint (bin: lib/hook.js).

Relationship to @shizhanyu13/dsh-ironbound-policy

dsh-ironbound-policy is the same guard inside DSH (its counter is a WeakMap<Agent> held in-process). ironbound-hook reuses the deny list + degrade verdict but swaps the counter to a file-persisted store and the execution seam to the Claude Code PreToolUse boundary. Upgrade the deny list / degrade thresholds in DSH → port the same change to src/core.ts and re-release.


License

MIT