Packs / Release Scrub

Release Scrub

Skillv1.0.0

Scans a folder for emails, phone numbers, IBANs, API keys and a configurable names list before anything is published.

Install

# once per machine
curl -fsSL https://hookcrate.com/install | bash
# then
hookcrate install release-scrub

Get access

One license covers all packs.

What it does

The last gate before code or docs leave a private machine. scrub.ts walks a directory (respecting .gitignore-style excludes), and reports every line that contains an email address, an international phone number, an IBAN that passes the mod-97 check, a credential of a known shape, a private-key block, or any name from your scrub.config.json. Findings carry file:line and a redacted match so the report itself is safe to paste. Non-zero exit makes it usable as a pre-publish check in CI or a pre-commit hook. SKILL.md tells Claude how to triage findings into redact / allowlist / false-positive.

Usage: bun .claude/skills/release-scrub/scripts/scrub.ts ./dist --config scrub.config.json

When to use this

Use Release Scrub as the last step before a folder leaves your machine for anywhere public: open-sourcing a tool, sharing a sample project, publishing documentation, zipping a repo for a client, or pushing a formerly private repository to a public remote. Personal data hides in places nobody re-reads — a fixture file copied from production, a sender address in a mailer, a client's name in an architecture note, an IBAN in a test, a live key in .env.example. The scanner walks the folder, skips binaries and ignored paths, and reports every line that contains an email, an international phone number, an IBAN that passes the mod-97 checksum (so random digit strings do not trigger it), a credential of a known shape, a private-key block, or any name from your private scrub.config.json. Findings carry file:line and a redacted match, so the report itself is safe to paste into an issue. The skill then walks you through triage — redact, allow with a reason, or mark a false positive — and only calls the folder clean when a re-run exits zero. It is especially worth running on anything generated by an agent from real data, because agents copy examples faithfully. It does not scan git history; squash or filter that separately.

When it triggers

  • User is about to publish, open-source, share, zip, or push a folder to a public remote
  • User asks to check for personal data, PII, secrets or leaks in a folder
  • User invokes /release-scrub <folder>

Preview — a sample session

Illustrative transcript showing the pack in use.

User: I want to open-source the invoice-tool/ folder tonight. Make sure nothing personal is in it.

Claude: Release-scrub before anything else. Using the names list from your private config.

● Bash(bun .claude/skills/release-scrub/scripts/scrub.ts ./invoice-tool --config ~/private/scrub.config.json)
  ⎿  release-scrub: /home/dev/invoice-tool
     files scanned: 212  skipped: 1,904  findings: 7

     email: 3  phone: 1  iban: 1  apiKey: 1  names: 1

     src/mailer.ts:14  [email]  fin…om (24)
         const FROM = "fin…om (24)";
     test/fixtures/customer.json:3  [email]  joh…et (19)
         "email": "joh…et (19)",
     test/fixtures/customer.json:4  [phone]  +37…12 (12)
         "phone": "+37…12 (12)",
     test/fixtures/customer.json:6  [iban]  LT1…00 (20)
         "iban": "LT1…00 (20)",
     README.md:88  [email]  sup…om (21)
         Questions? Write to sup…om (21).
     .env.example:2  [apiKey]  sk_…Q7 (41)
         STRIPE_SECRET=sk_…Q7 (41)
     docs/ARCHITECTURE.md:12  [names]  Nor…ds (14)
         Built originally for Nor…ds (14)' accounting team.

     BLOCKED — triage each finding (redact / allow / false positive) and re-run.

Claude: Triage:

  • src/mailer.ts:14 — a real sender address → REDACT to billing@example.com.
  • test/fixtures/customer.json:3,4,6 — a real customer record used as a fixture (the IBAN passes mod-97, so it's genuine) → REDACT all three to synthetic values; the IBAN becomes a valid-checksum test IBAN.
  • README.md:88 — support address: your call, but it's public-facing by intent → ALLOW with reason "public support address".
  • .env.example:2 — a sk_live_ key in an example file → REDACT to sk_live_REPLACE_ME, and I'd rotate that key.
  • docs/ARCHITECTURE.md:12 — client name from your names list → REDACT to "a client's accounting team".
● Edit(invoice-tool/src/mailer.ts)          ● Edit(invoice-tool/test/fixtures/customer.json)
● Edit(invoice-tool/.env.example)           ● Edit(invoice-tool/docs/ARCHITECTURE.md)
● Bash(bun .claude/skills/release-scrub/scripts/scrub.ts ./invoice-tool --config ~/private/scrub.config.json)
  ⎿  release-scrub: /home/dev/invoice-tool
     files scanned: 212  skipped: 1,904  findings: 0
     CLEAN — no personal data or secrets detected.

Claude:

Release scrub: invoice-tool/
Files scanned: 212   Findings: 7 → after triage: 0
Redacted: mailer.ts:14, customer.json:3/4/6, .env.example:2, ARCHITECTURE.md:12
Allowed: README.md:88 support address (public by intent)
Verdict: CLEAN — safe to publish. Manual items: rotate the leaked sk_live key; squash git history before pushing.

FAQ

How does it avoid false positives on phone numbers and IBANs?

IBANs must pass the ISO 13616 mod-97 check, which random digit strings almost never do. Phone candidates are rejected when they sit inside a version string, a date, or next to a long hex hash. Emails at example.com and similar reserved domains are skipped.

Where do I keep the names list?

In scrub.config.json, stored outside the folder you are publishing — it is literally a list of the things you are hiding. Pass its path with --config.

Can I use it in CI?

Yes. It exits 1 when there are findings and 0 when clean, and --json gives machine-readable output, so it drops into a pre-publish job or a pre-commit hook without wrapping.