/scan
Report prompt defects a lexical scan finds in documents a model executes. Use to audit a repository's skills, CLAUDE.md, agent definitions, or a product's prompt files, or to gate them in CI or a pre-commit hook, or propose cuts a reading pass finds.
Claude Code
$ npx -y skills add bendrucker/claude --skill scan --agent claude-code
Codex
$ npx skills add bendrucker/claude --skill scan -a codex -y
Cursor
$ npx skills add bendrucker/claude --skill scan -a cursor -y
Gemini CLI
$ npx skills add bendrucker/claude --skill scan -a gemini-cli -y
GitHub Copilot
$ npx skills add bendrucker/claude --skill scan -a github-copilot -y
OpenCode
$ npx skills add bendrucker/claude --skill scan -a opencode -y
Windsurf
$ npx skills add bendrucker/claude --skill scan -a windsurf -y
SKILL.md
scan.SKILL.mdname: prompting:scan
description: "Report prompt defects a lexical scan finds in documents a model executes. Use to audit a repository's skills, CLAUDE.md, agent definitions, or a product's prompt files, or to gate them in CI or a pre-commit hook, or propose cuts a reading pass finds."
argument-hint: "[audit] [<path>...] [--all] [--quiet] [--score]"
effort: low
allowed-tools:
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/scan.ts:*)
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/audit.ts:*)
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/edit.ts:*)
- Read
- EditScan
Run the scanner over the paths in `$ARGUMENTS`, defaulting to the current directory:
bun ${CLAUDE_SKILL_DIR}/scripts/scan.ts [<path>...] [--all] [--quiet] [--score]It reports each alert as `file:line:col: level: check: message` on stdout and a per-check count table on stderr, and exits non-zero on any alert. A check is an `error` once the precision eval confirms it and a `warning` until then. `--quiet` drops the table. `--score` replaces it with one row per scanned file, clean files included, giving its alerts, its prose words outside frontmatter and code, and alerts per 1,000 words, plus a total. Run it before and after an edit round over the same paths to compare them.
Without `--all` it reads the paths that hold documents a model executes: `SKILL.md`, `CLAUDE.md`, `AGENTS.md`, `.claude/agents`, `.claude/commands`, `.claude/rules`, a skill's `references/`, and a `prompt/` or `prompts/` directory holding `.md` or `.txt` files. Use `--all` for a prompt that lives somewhere else, such as one extracted from a string in a product repo. It reads `.md` and `.txt` under either setting, so `--all` widens which documents count without pulling in source files. Either way it also scans the files a scanned `CLAUDE.md` or `AGENTS.md` imports with `@path`, up to four hops deep, and lists on stderr each import outside the scanned paths or missing. It skips `node_modules`, `.git`, `fixtures`, `__snapshots__`, and `evals`.
Checks
Rewrites
Sharpen the sentence the alert names.
- `Prompting.WeakModality`: the instruction is a suggestion the model can decline, so whether it fires is left to the run.
- `Prompting.VagueCriterion`: the done-state is one the model cannot check. The model decides for itself when to stop.
Deletions
Delete the span the alert names.
- `Prompting.NoOp`: the instruction restates a default the model already follows, spending context and changing nothing.
- `Prompting.StaleMeasurement`: the document reports a tally a past run counted. The run prints the number again, so the document caches its own output and drifts as the work behind it moves.
- `Prompting.TicketRef`: a bare issue number points at a tracker the model cannot read, and the prose around it goes stale when the issue closes.
Acting on an Alert
An alert is a candidate. The scanner matches words rather than intent, so read the sentence and confirm it is an instruction before acting on it. A descriptive sentence that happens to contain the phrase is a false hit.
Load the `prompting` skill for the fix. Each check maps to a section: Sentence Form for weak modality, Completion Criteria for a vague criterion, and Pruning for the rest, under No-ops for a no-op, Cache for a stale measurement, and Relevance for a ticket reference, project status, or a maintainer aside.
Audit
With `audit` in `$ARGUMENTS`, run one round of the prune loop instead of the lexical scan. A judge reads each document and proposes cuts of lines a current model no longer needs, Ben accepts or rejects each in review, and the verdicts become telemetry and rule-precision labels. The round is done when every ledger proposal has a verdict event and the per-class table is printed.
List the units, defaulting to the current directory:
bun ${CLAUDE_SKILL_DIR}/scripts/audit.ts units [<path>...]Where the `observability:session` index is available, rank the units by its `memory-loads` query, which multiplies each rule or nested `CLAUDE.md`'s load count by its injected size, and judge the costliest first.
Judge one unit inline. Judge more than one through a Workflow with one judge per batch of units, kept within the session's workflow size, since this skill's instruction to run one is the opt-in. [references/judge.md](references/judge.md) holds the judge prompt and output schema, and [references/classes.md](references/classes.md) the classes it tags. Write the returned proposals to `tmp/prompting-audit/<run>/proposals.jsonl` in the audited repo, one per line with a unique `id`, where `<run>` names the round.
Then drive the ledger through `audit.ts` and `edit.ts`:
- `audit.ts check <ledger>` runs each `enforced` proposal's planted-violation check and lists any that did not hold.
- `edit.ts apply <ledger>` applies every proposal whose check held to the working tree and lists any quote it could not locate. A failed check or a missing quote drops the proposal out of the round.
- Ben reviews the diff with `plannotator review`, which opens a browser and runs outside the sandbox. An annotation on a hunk rejects that proposal.
- `edit.ts revert <ledger> <id...> --reason <annotation>` restores each rejected span.
- Commit the round with nothing else in the commit, and open it as its own pull request, so rule-precision can read its squash commit as labels.
- `verdict <ledger> --commit <sha>` reconciles each proposal against the commit, so a hand edit Ben makes before committing counts too. It prints the per-class table and the `labels.json` entry. Once the round's pull request merges, add its squash SHA under `agent` in `plugins/prompting/evals/rule-precision/labels.json` in `bendrucker/claude`.
Gotchas
- Fenced blocks, inline code, quoted phrases, and blockquotes are exempt. A document that teaches a rule by showing the wording it rejects reports nothing.
- A linked issue keeps its context, so `TicketRef` reads only a bare number. A number that ends a
Read more
name: prompting:scan
description: "Report prompt defects a lexical scan finds in documents a model executes. Use to audit a repository's skills, CLAUDE.md, agent definitions, or a product's prompt files, or to gate them in CI or a pre-commit hook, or propose cuts a reading pass finds."
argument-hint: "[audit] [<path>...] [--all] [--quiet] [--score]"
effort: low
allowed-tools:
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/scan.ts:*)
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/audit.ts:*)
- Bash(bun ${CLAUDE_SKILL_DIR}/scripts/edit.ts:*)
- Read
- EditScan
Run the scanner over the paths in `$ARGUMENTS`, defaulting to the current directory:
bun ${CLAUDE_SKILL_DIR}/scripts/scan.ts [<path>...] [--all] [--quiet] [--score]It reports each alert as `file:line:col: level: check: message` on stdout and a per-check count table on stderr, and exits non-zero on any alert. A check is an `error` once the precision eval confirms it and a `warning` until then. `--quiet` drops the table. `--score` replaces it with one row per scanned file, clean files included, giving its alerts, its prose words outside frontmatter and code, and alerts per 1,000 words, plus a total. Run it before and after an edit round over the same paths to compare them.
Without `--all` it reads the paths that hold documents a model executes: `SKILL.md`, `CLAUDE.md`, `AGENTS.md`, `.claude/agents`, `.claude/commands`, `.claude/rules`, a skill's `references/`, and a `prompt/` or `prompts/` directory holding `.md` or `.txt` files. Use `--all` for a prompt that lives somewhere else, such as one extracted from a string in a product repo. It reads `.md` and `.txt` under either setting, so `--all` widens which documents count without pulling in source files. Either way it also scans the files a scanned `CLAUDE.md` or `AGENTS.md` imports with `@path`, up to four hops deep, and lists on stderr each import outside the scanned paths or missing. It skips `node_modules`, `.git`, `fixtures`, `__snapshots__`, and `evals`.
Checks
Rewrites
Sharpen the sentence the alert names.
- `Prompting.WeakModality`: the instruction is a suggestion the model can decline, so whether it fires is left to the run.
- `Prompting.VagueCriterion`: the done-state is one the model cannot check. The model decides for itself when to stop.
Deletions
Delete the span the alert names.
- `Prompting.NoOp`: the instruction restates a default the model already follows, spending context and changing nothing.
- `Prompting.StaleMeasurement`: the document reports a tally a past run counted. The run prints the number again, so the document caches its own output and drifts as the work behind it moves.
- `Prompting.TicketRef`: a bare issue number points at a tracker the model cannot read, and the prose around it goes stale when the issue closes.
Acting on an Alert
An alert is a candidate. The scanner matches words rather than intent, so read the sentence and confirm it is an instruction before acting on it. A descriptive sentence that happens to contain the phrase is a false hit.
Load the `prompting` skill for the fix. Each check maps to a section: Sentence Form for weak modality, Completion Criteria for a vague criterion, and Pruning for the rest, under No-ops for a no-op, Cache for a stale measurement, and Relevance for a ticket reference, project status, or a maintainer aside.
Audit
With `audit` in `$ARGUMENTS`, run one round of the prune loop instead of the lexical scan. A judge reads each document and proposes cuts of lines a current model no longer needs, Ben accepts or rejects each in review, and the verdicts become telemetry and rule-precision labels. The round is done when every ledger proposal has a verdict event and the per-class table is printed.
List the units, defaulting to the current directory:
bun ${CLAUDE_SKILL_DIR}/scripts/audit.ts units [<path>...]Where the `observability:session` index is available, rank the units by its `memory-loads` query, which multiplies each rule or nested `CLAUDE.md`'s load count by its injected size, and judge the costliest first.
Judge one unit inline. Judge more than one through a Workflow with one judge per batch of units, kept within the session's workflow size, since this skill's instruction to run one is the opt-in. [references/judge.md](references/judge.md) holds the judge prompt and output schema, and [references/classes.md](references/classes.md) the classes it tags. Write the returned proposals to `tmp/prompting-audit/<run>/proposals.jsonl` in the audited repo, one per line with a unique `id`, where `<run>` names the round.
Then drive the ledger through `audit.ts` and `edit.ts`:
- `audit.ts check <ledger>` runs each `enforced` proposal's planted-violation check and lists any that did not hold.
- `edit.ts apply <ledger>` applies every proposal whose check held to the working tree and lists any quote it could not locate. A failed check or a missing quote drops the proposal out of the round.
- Ben reviews the diff with `plannotator review`, which opens a browser and runs outside the sandbox. An annotation on a hunk rejects that proposal.
- `edit.ts revert <ledger> <id...> --reason <annotation>` restores each rejected span.
- Commit the round with nothing else in the commit, and open it as its own pull request, so rule-precision can read its squash commit as labels.
- `verdict <ledger> --commit <sha>` reconciles each proposal against the commit, so a hand edit Ben makes before committing counts too. It prints the per-class table and the `labels.json` entry. Once the round's pull request merges, add its squash SHA under `agent` in `plugins/prompting/evals/rule-precision/labels.json` in `bendrucker/claude`.
Gotchas
- Fenced blocks, inline code, quoted phrases, and blockquotes are exempt. A document that teaches a rule by showing the wording it rejects reports nothing.
- A linked issue keeps its context, so `TicketRef` reads only a bare number. A number that ends a
Other skills on claude.
cli
Type-safe CLI argument parsing with commander and @commander-js/extra-typings, the standard parser for this repo's Bun scripts.…
prose-lab
Turn the user's labels on repo prose, or their notes from reviewing a document in Plannotator, into calibrated Vale rules.
activity
Report real device usage from ActivityWatch. Covers per-app time, window titles, and active vs idle spans. Use when asked "what…
agent-team
Orchestrating Claude Code agent teams. Use when creating teams, spawning teammates, assigning tasks, configuring teammate modes,…
handoff
Hand the current conversation off to a fresh background agent that picks up the work immediately, so you can step away and track…
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about scan.
- Which coding agents can use scan?
- scan installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

