Call EnterPlanMode immediately before doing anything else.
You are performing a comprehensive, language-aware idiom audit on the entire codebase. Detect the primary programming language, examine the source through three orthogonal language-specific lenses simultaneously, synthesize a prioritized report, and — after user approval — ship the remediation as one or more PR-sized bundles, each landed as an independent pull request on GitHub.
This skill takes no argument. The audit always covers the whole repository (with optional narrowing if the codebase is large).
Step 1: Detect Primary Language and Resolve Scope
Determine the primary programming language and gather the source files to audit.
Detect the primary language by inspecting manifest files in the repository root:
ls -1 Cargo.toml go.mod pyproject.toml setup.py setup.cfg package.json tsconfig.json Gemfile pom.xml build.gradle build.gradle.kts composer.json Package.swift 2>/dev/null
find . -maxdepth 3 -type f -name '*.csproj' 2>/dev/null | head -5
Map manifests to languages:
| Manifest |
Primary language |
Cargo.toml |
Rust |
go.mod |
Go |
pyproject.toml, setup.py, setup.cfg |
Python |
package.json + tsconfig.json |
TypeScript |
package.json (no tsconfig.json) |
JavaScript |
Gemfile |
Ruby |
pom.xml, build.gradle* |
Java / Kotlin |
*.csproj |
C# |
Package.swift |
Swift |
composer.json |
PHP |
If multiple manifests are present, count source files per language to pick the dominant one. Use these extension globs (excluding common vendor/build directories):
find . -type f \( -name '*.rs' -o -name '*.go' -o -name '*.py' -o -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' -o -name '*.rb' -o -name '*.java' -o -name '*.kt' -o -name '*.cs' -o -name '*.swift' -o -name '*.php' \) ! -path '*/node_modules/*' ! -path '*/vendor/*' ! -path '*/target/*' ! -path '*/dist/*' ! -path '*/build/*' ! -path '*/__pycache__/*' ! -path '*/.next/*' ! -path '*/.venv/*' ! -path '*/.git/*' ! -path '*/coverage/*' | sed 's/.*\.//' | sort | uniq -c | sort -rn
Pick the language with the most source files. If the top two languages are within ~20% of each other (genuinely polyglot repo), use AskUserQuestion to let the user pick the lens.
Enumerate source files in scope — non-test, non-vendored source files for the chosen language. For example, for Rust:
find . -type f -name '*.rs' ! -path '*/target/*' ! -path '*/.git/*' ! -name 'build.rs' ! -path '*/tests/*'
Adjust the extensions and exclusions per the chosen language. Always exclude tests, generated code, and vendored / dependency directories.
Read project conventions to ground findings in the repo's actual style:
CLAUDE.md — project-specific instructions
.editorconfig — formatting rules
- Language-specific:
- Rust:
clippy.toml, rustfmt.toml, rust-toolchain*
- Python:
pyproject.toml (ruff/black/mypy sections), .flake8, mypy.ini
- TypeScript / JavaScript:
tsconfig.json, biome.json, .eslintrc*, .prettierrc*
- Go:
.golangci.yml, go.mod (Go version)
- Ruby:
.rubocop.yml, .ruby-version
- Java / Kotlin:
build.gradle*, pom.xml, .editorconfig
- C#:
.editorconfig, *.csproj (TargetFramework, LangVersion)
- Swift:
.swift-version, .swiftlint.yml
- PHP:
composer.json, phpcs.xml, phpstan.neon
Scope narrowing — if more than 50 source files are in scope, list the top-level subdirectories with file counts and use AskUserQuestion to ask the user to narrow the audit (e.g. "audit only src/" vs. "audit the whole tree"). This is the only prompt before launching the analysis agents.
State the resolved language (with version if detected), the file count and total lines, the conventions discovered, and the final scope clearly before proceeding to Step 2.
Step 2: Multi-Lens Language-Specific Analysis
Run the audit as a Workflow of exactly 3 read-only Explore agents in parallel — one per lens from the language matrix below. Call the Workflow tool with a script along these lines, substituting the language, scope, and conventions resolved in Step 1 and the three lens briefs from the matching row of the Language Lens Matrix:
export const meta = {
name: 'idiom-check-analysis',
description: 'Three-lens language-specific idiom audit of the codebase',
phases: [{ title: 'Audit' }],
}
const CONTEXT = `<the detected language (and version), the full list of source files in scope, and the conventions gathered in Step 1>`
const RULES = `<the three "Each agent's instructions must include" rules below: the ~12 cap, "why in THIS codebase" framing, and the Looks Good callouts>`
// Mirrors the structured format below — the harness validates each agent's return against it.
// maxItems: 12 enforces the per-agent cap; minItems on looks_good enforces the mandatory callouts.
const FINDINGS = {
type: 'object', additionalProperties: false,
properties: {
findings: { type: 'array', maxItems: 12, items: {
type: 'object', additionalProperties: false,
properties: {
id: { type: 'string' }, file: { type: 'string' }, line: { type: 'string' },
title: { type: 'string', maxLength: 80 },
current: { type: 'string' }, idiomatic: { type: 'string' }, why: { type: 'string' },
severity: { type: 'string', enum: ['high', 'medium', 'low'] },
confidence: { type: 'string', enum: ['high', 'medium', 'low'] },
risk: { type: 'string', enum: ['safe', 'moderate', 'breaking'] },
},
required: ['id', 'file', 'line', 'title', 'current', 'idiomatic', 'why', 'severity', 'confidence', 'risk'],
} },
looks_good: { type: 'array', minItems: 2, maxItems: 3, items: { type: 'string' } },
},
required: ['findings', 'looks_good'],
}
const LENSES = [
{ key: '<lens 1 slug, e.g. ownership>', brief: `<Lens 1 bullet list from the matrix row, verbatim>` },
{ key: '<lens 2 slug, e.g. types>', brief: `<Lens 2 bullet list from the matrix row, verbatim>` },
{ key: '<lens 3 slug, e.g. idioms>', brief: `<Lens 3 bullet list from the matrix row, verbatim>` },
]
const reports = await parallel(LENSES.map(l => () =>
agent(`Audit the codebase through the ${l.key} lens. Read the FULL target files, not just snippets.\n${CONTEXT}\n\n${l.brief}\n\n${RULES}`,
{ label: `audit:${l.key}`, phase: 'Audit', agentType: 'Explore', schema: FINDINGS })))
return { lenses: LENSES.map((l, i) => ({ key: l.key, report: reports[i] })) }
Wait for the Workflow's completion notification before continuing — never synthesize from partial results. Each report is a validated { findings, looks_good } object; a null report means that agent was skipped or failed — say so in the report header rather than silently dropping the lens.
Fallback. If the Workflow tool is not available in this session, launch the same three briefs as 3 Explore subagents in parallel via the Agent tool (subagent_type: "Explore", model: "opus").
Provide each agent with:
- The detected primary language (and version, if known)
- The full list of source files in scope
- The project conventions gathered in Step 1 (CLAUDE.md excerpts, lint/style configs)
- The agent's specific lens (one of the three lenses for the detected language — see the lens matrix below)
IMPORTANT: All subagents MUST be launched with agentType: 'Explore' inside the Workflow script (omit model — each agent inherits the session model), or, on the Agent-tool fallback, with subagent_type: "Explore" and model: "opus" (resolves to the latest Claude Opus, the most capable model). The Explore agent is read-only by design (Edit and Write are denied at the agent level). This ensures no subagent can accidentally modify the project during analysis. The explicit model: "opus" on the Agent path pins the fan-out to the latest Opus even when a cheaper default subagent model is configured, so the analysis never silently runs on a smaller model. Never use general-purpose subagents in this skill.
IMPORTANT: Instruct each agent to read the full target files (not just snippets) so they understand the complete code structure, how functions relate to each other, and whether a proposed change would break callers or dependents.
Each agent's instructions must include:
- Cap findings at ~12 per agent. Quality over quantity — nit-flooding kills signal. If the agent has more candidates than that, keep only the most consequential ones for this codebase.
- Frame every finding as "why in THIS codebase" — not generic blog advice. Tie each finding to the surrounding code, the project's conventions, the data flow, or the call sites. A finding that could appear verbatim in a tutorial is not a finding.
- Return 2-3 mandatory "Looks Good" callouts — things this codebase already does well in the agent's lens. This grounds the report and prevents over-engineering.
Each finding must be returned in this structured format (the FINDINGS schema above enforces it on the Workflow path; on the Agent-tool fallback, include the list in each prompt):
- ID: agent-local identifier (e.g., O1, T1, F1 for the first agent's findings)
- File: exact path and line number(s)
- Title: short description, under 80 characters
- Current pattern: the code as it stands now, with a code snippet
- Idiomatic alternative: the proposed replacement, with a code snippet
- Why (in this codebase): rationale tied to the surrounding code, the conventions, or specific call sites
- Severity: High (correctness or safety risk from non-idiomatic pattern), Medium (clarity or maintainability impact), Low (style / preference)
- Confidence: High / Medium / Low (how certain the agent is that this is an actual issue)
- Risk: Safe (behavior-preserving), Moderate (behavior-preserving but context-dependent), Breaking (intentionally changes behavior for correctness)
Language Lens Matrix
The three lenses are language-specific. Use the row that matches the language detected in Step 1.
Rust
- Lens 1 — Ownership, Borrowing & Lifetimes: unnecessary
.clone() and .to_owned(), String parameters where &str or Cow<'_, str> fits, premature RefCell/Mutex where &mut self would work, Rc<T>/Arc<T> introduced before there's actual sharing, lifetime annotations that could be elided, ownership inversion at API boundaries (taking T when &T suffices), unnecessary Box<T> indirection.
- Lens 2 — Type System & Trait Design:
? propagation over .unwrap()/.expect() outside tests, idiomatic From/Into/TryFrom/AsRef usage, Display vs Debug placement on user-facing types, newtype pattern for invariants and unit safety, trait objects (dyn Trait) vs generics, sealed traits via private supertraits, missing #[non_exhaustive] on public enums/structs, missing #[must_use] on result-like types.
- Lens 3 — Idioms & Control Flow: exhaustive
match over chained if let, iterator chains over manual index loops, ? propagation over nested match, Default impl over manual zero-init constructors, builder pattern for >3-arg constructors, NonZero* / NonNull for invariants, OnceCell/LazyLock over runtime-init globals, let-else for early returns.
Python
- Lens 1 — Pythonic Constructs: comprehensions over manual
for-append loops, context managers (with) over manual try/finally, EAFP over LBYL where appropriate, tuple unpacking over indexing, walrus := where it improves clarity, pathlib.Path over os.path strings, f-strings over .format() and %, enumerate/zip over index counters, any/all over manual flag loops.
- Lens 2 — Type Hints & Data Modeling: modern syntax (
X | Y, list[T], dict[K, V]) on Python ≥3.9/3.10, Protocol over ABC for structural typing, dataclass/Pydantic/TypedDict over plain dicts as records, Generic[T] and TypeVar correctness, Optional[T] vs T | None consistency, Final/ClassVar, Self (3.11+), missing return-type annotations on public APIs.
- Lens 3 — Std Library & Patterns:
collections (Counter, defaultdict, deque, ChainMap), functools (@cache, partial, singledispatch, reduce), itertools (chain, groupby, pairwise, batched), contextlib (@contextmanager, ExitStack, suppress), async idioms (asyncio.gather, asyncio.TaskGroup, async with), generators over building intermediate lists.
TypeScript / JavaScript
- Lens 1 — Type System & Inference:
any discipline (replace with unknown or a real type), narrowing via discriminated unions, satisfies operator over as assertions, as const for literal-narrowed values, generic constraints (T extends ...), branded / nominal types for invariants (IDs, units), exhaustive switch with never fallthrough, avoiding non-null assertions (!), type predicates over runtime checks.
- Lens 2 — Async & Functional Patterns:
Promise.all/Promise.allSettled over serial await, async/await over .then chains, immutability (spread, as const, readonly), map/filter/reduce over imperative loops where it improves clarity, optional chaining (?.) and nullish coalescing (??) over manual null checks, AbortController for cancellable operations.
- Lens 3 — Module Boundaries & Modern Syntax: ESM imports over CJS where the project supports it, named exports over default,
import type for type-only imports, structural patterns over inheritance, modern features (Set/Map over object-as-dict, structuredClone, Object.groupBy, Array.prototype.at), avoiding namespace imports, top-level await where supported.
Go
- Lens 1 — Idioms & Error Handling: error wrapping with
fmt.Errorf("...: %w", err) and inspection via errors.Is/errors.As, sentinel vs typed errors used appropriately, defer correctness (argument capture vs invocation order), errgroup.Group for fan-out with cancellation, returning errors over panics, named return values used sparingly and only when they aid clarity, package naming (lowercase, no underscores).
- Lens 2 — Concurrency & Channels: channel direction (
chan<-/<-chan) in function signatures, context.Context propagation as first parameter, sync.Once / sync.Map for the right shape of problem, goroutine leak risks (missing select on ctx.Done()), mutex granularity (sync.RWMutex where reads dominate), select patterns with timeouts, sync.WaitGroup correctness.
- Lens 3 — Interface Design & Composition: small interfaces (accept-interfaces, return-structs), embedding over inheritance, type assertions with comma-ok form, generics (Go 1.18+) used only for genuinely generic code (not as a Java-style cure-all), receiver naming (single-letter, consistent across methods), package-private types where the API doesn't require export.
Ruby
- Lens 1 — Object Design & Duck Typing:
Module vs Class (mixins for behavior, classes for things), attr_reader/attr_writer/attr_accessor over manual accessors, Struct/Data for value types, Comparable/Enumerable inclusion to inherit batteries, # frozen_string_literal: true magic comment, refinements over global monkey-patching.
- Lens 2 — Blocks, Procs & Enumerable:
yield vs &block parameters, lazy enumerables (.lazy) for large or infinite sequences, each vs map vs reduce choice, tap for side-effecting in a chain, then / yield_self for piping, avoiding .each_with_index { |x, i| arr << ... } in favor of .map.with_index.
- Lens 3 — Metaprogramming Restraint & Style:
define_method only when the method shape genuinely varies, method_missing paired with respond_to_missing?, class << self over repeated self. prefixes, Symbol#to_proc (&:method) over { |x| x.method }, keyword arguments over option hashes, hash shorthand syntax (Ruby 3.1+), avoiding clever metaprogramming where plain code reads better.
Java / Kotlin / C# / Swift / PHP (generic template)
For languages without an explicit matrix above, instruct the three lenses as:
- Lens 1 — Type System & Null Safety: non-null annotations / non-optional types, sealed interfaces / sealed classes, records / data classes, generics correctness, optional types over null sentinels, immutability where the language supports it.
- Lens 2 — Idiomatic Patterns: collection / stream / sequence operations over manual loops, builder patterns for complex construction, modern equality / hashing /
toString, pattern matching where supported, language-specific resource management (try-with-resources, using, defer, Disposable).
- Lens 3 — Modern Language Features: switch expressions / pattern matching, sealed interfaces, records / data classes, primary constructors,
var / val inference, extension methods / extension functions, modern null-safe operators, structured concurrency primitives where applicable.
In your prompt to each agent, explicitly include:
- The language name and detected version
- Which of the three lenses the agent is responsible for (with the bullet list of focus areas)
- The list of files in scope
- The conventions discovered in Step 1
- The required structured return format
- The cap of ~12 findings
- The "why in THIS codebase" framing requirement
- The mandatory 2-3 "Looks Good" callouts
Step 3: Synthesize Severity-Sorted Report
Collect all findings from the 3 agents and produce a single, prioritized report. ultrathink during deduplication and prioritization — multiple agents may flag the same code for related-but-different reasons, and the synthesis quality depends on resolving those overlaps thoughtfully.
Synthesis rules:
- Deduplicate: if two agents flagged the same line or function for related reasons, merge into one finding with combined context. Note all applicable lenses on the merged finding.
- Prioritize: sort by severity (High > Medium > Low). Within each severity, sort by confidence (High > Medium > Low).
- Be specific: every finding must have a file path and line number. No hand-waving.
- Be actionable: every finding must include both the current code snippet and the idiomatic replacement. No "consider improving" without showing what to do.
- Frame "why in THIS codebase": every finding's rationale must reference something specific in the surrounding code, conventions, or call sites — not generic best-practice prose.
- Omit empty sections: if there are no High findings, do not include the High heading. Same for Medium and Low.
- Looks Good is mandatory: include 3-5 callouts merged from the agents' "Looks Good" lists. This is non-negotiable per the skill's design.
Assign findings sequential IDs across the entire report: [I1], [I2], [I3], etc.
Use this report format:
## Idiom Audit: <language> — <scope description>
**Scope**: <N files, M total lines> | **Language**: <name + version if detected>
**Findings**: <X total> (<A high, B medium, C low>)
---
### High
| ID | File:Line | Title | Lens | Confidence | Risk |
|----|-----------|-------|------|------------|------|
| [I1] | `src/foo.rs:42` | <title> | Ownership | High | Safe |
**[I1]** `src/foo.rs:42` — <Title>
**Current**:
```<lang>
<current code>
Idiomatic:
<replacement code>
Why (in this codebase): <rationale tied to surrounding code, conventions, or call sites>
Medium
| ID |
File:Line |
Title |
Lens |
Confidence |
Risk |
(same per-finding detail format)
Low
| ID |
File:Line |
Title |
Lens |
Confidence |
Risk |
(same per-finding detail format)
Looks Good (do not change)
- <Strength callout from Lens 1>
- <Strength callout from Lens 2>
- <Strength callout from Lens 3>
---
## Step 4: Group Findings into PR-Sized Remediation Bundles
Cluster the findings into tight, mergeable units. Each bundle becomes one pull request.
**Bundling rules:**
- Group by **file proximity** (same file or adjacent modules) and **theme** (same lens, same idiom). A bundle should read as one coherent change.
- Target size: **3-7 findings, 1-3 files, ~30-60 minute review effort**. A single high-impact finding can be its own bundle if the change is large.
- Avoid mixing severities in a single bundle when possible — High-severity bundles get reviewed under more scrutiny than style-only bundles.
- Skip findings that are too risky to bundle automatically (Risk = Breaking). Leave them in the report so the user can decide manually.
**Each bundle has:**
- **ID**: `B1`, `B2`, `B3`, …
- **Title**: imperative, ≤60 chars (this becomes the PR title and commit subject)
- **Theme**: one-line description of the unifying idea
- **Findings**: list of finding IDs covered (e.g., I1, I3, I7)
- **Files**: count + paths
- **Effort**: S (<30 min review) / M (30-60 min) / L (>60 min)
- **Risk**: Safe / Moderate / Breaking
- **Branch name**: `idiom-check/<kebab-slug-of-title>`
**Bundle plan format:**
Remediation Bundles
| ID |
Title |
Findings |
Files |
Effort |
Risk |
| B1 |
Replace clones with borrows in parser |
I1, I3, I7 |
2 |
M |
Safe |
| B2 |
Idiomatic error propagation with ? |
I2, I5, I9, I12 |
3 |
M |
Safe |
| B3 |
Iterator chains over manual loops |
I4, I6 |
1 |
S |
Safe |
[B1] Replace clones with borrows in parser
- Findings: I1, I3, I7
- Files:
src/parser/mod.rs, src/parser/tokens.rs
- Effort: M (~45 min review)
- Risk: Safe — behavior-preserving
- Branch:
idiom-check/replace-clones-with-borrows
[B2] …
After presenting the bundle plan, call `ExitPlanMode`, then ask:
> **Apply which bundles?** (e.g., "apply all", "apply B1 and B2", "apply B1 only", "skip — keep the report")
If there are no actionable bundles (e.g., the report is all "Looks Good" with a couple of Low-severity stylistic notes), skip the apply offer and confirm the codebase is in good shape.
---
## Step 5: Apply Selected Bundles (Full Ship)
After the user picks one or more bundles, ship each one as an independent PR. Process bundles in their listed order; each bundle branches off the default branch so the PRs are independent and can be merged in any order.
**Pre-flight checks** — do these once before processing the first bundle:
```bash
git rev-parse --is-inside-work-tree 2>/dev/null || echo "not_a_repo"
git remote get-url origin 2>/dev/null | grep -qE 'github\.com[:/]' || echo "no_github_remote"
gh auth status 2>&1 | grep -q "Logged in" || echo "gh_not_authenticated"
git status --porcelain
If the working tree is dirty, stop and ask the user whether to stash or abort. When stashing, use the explicit-SHA git stash create + git stash store pattern (consistent with /refactor and /docstring-check) so the snapshot can be restored reliably later — git stash push exits 0 even when there's nothing to stash, which makes a "revert all" hard to reason about:
backup_sha=$(git stash create "idiom-check-backup: pre-bundle stash" 2>/dev/null)
if [ -n "$backup_sha" ]; then
git stash store -m "idiom-check-backup: pre-bundle stash" "$backup_sha"
fi
Record $backup_sha so the user can git stash apply "$backup_sha" later if they want the pre-bundle state back. If $backup_sha is empty, the working tree was clean — proceed without a backup.
If gh is not authenticated or the remote is not GitHub, stop with an actionable error.
Resolve the default branch:
default_branch=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
[ -z "$default_branch" ] && git rev-parse --verify main >/dev/null 2>&1 && default_branch=main
[ -z "$default_branch" ] && git rev-parse --verify master >/dev/null 2>&1 && default_branch=master
Track progress with tasks. Before processing the first bundle, call TaskCreate once per approved bundle so the user sees live progress through the ship phase. Each task's subject should be the bundle title ([B1] <title>). As you start a bundle, mark its task in_progress; after the PR is opened, mark it completed. If a bundle's commit or push fails, leave the task in_progress and report the failure inline.
Per-bundle workflow — repeat for each approved bundle:
Start from a clean default branch:
git checkout "$default_branch"
git pull --ff-only origin "$default_branch" 2>/dev/null || true
git checkout -b "idiom-check/<bundle-slug>"
Apply each finding's edit with the Edit tool (or Write if the bundle introduces a new file). After each edit, briefly state what was applied:
[I1] Applied: replace .clone() with & borrow (src/parser/mod.rs:42)
Commit with the bundle title as the subject and the theme + finding IDs in the body. Never use --no-verify:
git add -A
git commit -m "<bundle title>" -m "$(cat <<'EOF'
<bundle theme>
Applied findings: I1, I3, I7
EOF
)"
Push the branch:
git push -u origin HEAD
Open the PR with gh pr create:
gh pr create --base "$default_branch" --title "<bundle title>" --body "$(cat <<'EOF'
## Summary
<bundle theme>
Generated by `/idiom-check`. Idiomatic improvements:
- **[I1]** <I1 title> (`file:line`)
- **[I3]** <I3 title> (`file:line`)
- **[I7]** <I7 title> (`file:line`)
## Test plan
- [ ] Run the project's test suite
- [ ] Manual sanity check on the touched modules
EOF
)"
Return to the default branch before processing the next bundle so each bundle branches independently:
git checkout "$default_branch"
Final report after every approved bundle has been shipped:
Idiom audit applied. Opened PRs:
Note: when merging these PRs, avoid gh pr merge --delete-branch if you stack any of them on top of each other — it closes dependent PRs.
Skill handoff. After the PRs are open, offer to do an independent second pass via /vet on each bundle's diff:
Next: Want me to hand off to /vet for one or more of these PRs? Useful as a second pair of eyes before you merge — /vet covers correctness, security, and conventions through a different lens than the idiom audit did.
Use the Skill tool to invoke /vet (with the PR's branch as the argument) if the user agrees. Skip the offer when the bundles were trivially small (single-file Low-severity polish) or when the user wants to merge immediately.
1---2name: idiom-check3description: Audits a codebase through a programming-language-specific idiom lens, produces a prioritized report, and offers remediation in PR-sized bundles.4---56Call `EnterPlanMode` immediately before doing anything else.78You are performing a comprehensive, language-aware idiom audit on the entire codebase. Detect the primary programming language, examine the source through three orthogonal language-specific lenses simultaneously, synthesize a prioritized report, and — after user approval — ship the remediation as one or more PR-sized bundles, each landed as an independent pull request on GitHub.910This skill takes no argument. The audit always covers the whole repository (with optional narrowing if the codebase is large).1112---1314## Step 1: Detect Primary Language and Resolve Scope1516Determine the primary programming language and gather the source files to audit.1718**Detect the primary language** by inspecting manifest files in the repository root:1920```bash21ls -1 Cargo.toml go.mod pyproject.toml setup.py setup.cfg package.json tsconfig.json Gemfile pom.xml build.gradle build.gradle.kts composer.json Package.swift 2>/dev/null22find . -maxdepth 3 -type f -name '*.csproj' 2>/dev/null | head -523```2425Map manifests to languages:2627| Manifest | Primary language |28|----------|------------------|29| `Cargo.toml` | Rust |30| `go.mod` | Go |31| `pyproject.toml`, `setup.py`, `setup.cfg` | Python |32| `package.json` + `tsconfig.json` | TypeScript |33| `package.json` (no `tsconfig.json`) | JavaScript |34| `Gemfile` | Ruby |35| `pom.xml`, `build.gradle*` | Java / Kotlin |36| `*.csproj` | C# |37| `Package.swift` | Swift |38| `composer.json` | PHP |3940**If multiple manifests are present**, count source files per language to pick the dominant one. Use these extension globs (excluding common vendor/build directories):4142```bash43find . -type f \( -name '*.rs' -o -name '*.go' -o -name '*.py' -o -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' -o -name '*.rb' -o -name '*.java' -o -name '*.kt' -o -name '*.cs' -o -name '*.swift' -o -name '*.php' \) ! -path '*/node_modules/*' ! -path '*/vendor/*' ! -path '*/target/*' ! -path '*/dist/*' ! -path '*/build/*' ! -path '*/__pycache__/*' ! -path '*/.next/*' ! -path '*/.venv/*' ! -path '*/.git/*' ! -path '*/coverage/*' | sed 's/.*\.//' | sort | uniq -c | sort -rn44```4546Pick the language with the most source files. If the top two languages are within ~20% of each other (genuinely polyglot repo), use `AskUserQuestion` to let the user pick the lens.4748**Enumerate source files in scope** — non-test, non-vendored source files for the chosen language. For example, for Rust:4950```bash51find . -type f -name '*.rs' ! -path '*/target/*' ! -path '*/.git/*' ! -name 'build.rs' ! -path '*/tests/*'52```5354Adjust the extensions and exclusions per the chosen language. Always exclude tests, generated code, and vendored / dependency directories.5556**Read project conventions** to ground findings in the repo's actual style:5758- `CLAUDE.md` — project-specific instructions59- `.editorconfig` — formatting rules60- Language-specific:61 - **Rust**: `clippy.toml`, `rustfmt.toml`, `rust-toolchain*`62 - **Python**: `pyproject.toml` (ruff/black/mypy sections), `.flake8`, `mypy.ini`63 - **TypeScript / JavaScript**: `tsconfig.json`, `biome.json`, `.eslintrc*`, `.prettierrc*`64 - **Go**: `.golangci.yml`, `go.mod` (Go version)65 - **Ruby**: `.rubocop.yml`, `.ruby-version`66 - **Java / Kotlin**: `build.gradle*`, `pom.xml`, `.editorconfig`67 - **C#**: `.editorconfig`, `*.csproj` (TargetFramework, LangVersion)68 - **Swift**: `.swift-version`, `.swiftlint.yml`69 - **PHP**: `composer.json`, `phpcs.xml`, `phpstan.neon`7071**Scope narrowing** — if more than 50 source files are in scope, list the top-level subdirectories with file counts and use `AskUserQuestion` to ask the user to narrow the audit (e.g. "audit only `src/`" vs. "audit the whole tree"). This is the only prompt before launching the analysis agents.7273State the resolved language (with version if detected), the file count and total lines, the conventions discovered, and the final scope clearly before proceeding to Step 2.7475---7677## Step 2: Multi-Lens Language-Specific Analysis7879Run the audit as a **`Workflow` of exactly 3 read-only Explore agents in parallel** — one per lens from the language matrix below. Call the `Workflow` tool with a script along these lines, substituting the language, scope, and conventions resolved in Step 1 and the three lens briefs from the matching row of the **Language Lens Matrix**:8081```js82export const meta = {83 name: 'idiom-check-analysis',84 description: 'Three-lens language-specific idiom audit of the codebase',85 phases: [{ title: 'Audit' }],86}8788const CONTEXT = `<the detected language (and version), the full list of source files in scope, and the conventions gathered in Step 1>`89const RULES = `<the three "Each agent's instructions must include" rules below: the ~12 cap, "why in THIS codebase" framing, and the Looks Good callouts>`9091// Mirrors the structured format below — the harness validates each agent's return against it.92// maxItems: 12 enforces the per-agent cap; minItems on looks_good enforces the mandatory callouts.93const FINDINGS = {94 type: 'object', additionalProperties: false,95 properties: {96 findings: { type: 'array', maxItems: 12, items: {97 type: 'object', additionalProperties: false,98 properties: {99 id: { type: 'string' }, file: { type: 'string' }, line: { type: 'string' },100 title: { type: 'string', maxLength: 80 },101 current: { type: 'string' }, idiomatic: { type: 'string' }, why: { type: 'string' },102 severity: { type: 'string', enum: ['high', 'medium', 'low'] },103 confidence: { type: 'string', enum: ['high', 'medium', 'low'] },104 risk: { type: 'string', enum: ['safe', 'moderate', 'breaking'] },105 },106 required: ['id', 'file', 'line', 'title', 'current', 'idiomatic', 'why', 'severity', 'confidence', 'risk'],107 } },108 looks_good: { type: 'array', minItems: 2, maxItems: 3, items: { type: 'string' } },109 },110 required: ['findings', 'looks_good'],111}112113const LENSES = [114 { key: '<lens 1 slug, e.g. ownership>', brief: `<Lens 1 bullet list from the matrix row, verbatim>` },115 { key: '<lens 2 slug, e.g. types>', brief: `<Lens 2 bullet list from the matrix row, verbatim>` },116 { key: '<lens 3 slug, e.g. idioms>', brief: `<Lens 3 bullet list from the matrix row, verbatim>` },117]118119const reports = await parallel(LENSES.map(l => () =>120 agent(`Audit the codebase through the ${l.key} lens. Read the FULL target files, not just snippets.\n${CONTEXT}\n\n${l.brief}\n\n${RULES}`,121 { label: `audit:${l.key}`, phase: 'Audit', agentType: 'Explore', schema: FINDINGS })))122123return { lenses: LENSES.map((l, i) => ({ key: l.key, report: reports[i] })) }124```125126Wait for the Workflow's completion notification before continuing — never synthesize from partial results. Each `report` is a validated `{ findings, looks_good }` object; a `null` report means that agent was skipped or failed — say so in the report header rather than silently dropping the lens.127128**Fallback.** If the `Workflow` tool is not available in this session, launch the same three briefs as **3 Explore subagents in parallel** via the `Agent` tool (`subagent_type: "Explore"`, `model: "opus"`).129130Provide each agent with:131- The detected primary language (and version, if known)132- The full list of source files in scope133- The project conventions gathered in Step 1 (CLAUDE.md excerpts, lint/style configs)134- The agent's specific lens (one of the three lenses for the detected language — see the lens matrix below)135136**IMPORTANT:** All subagents MUST be launched with `agentType: 'Explore'` inside the `Workflow` script (omit `model` — each agent inherits the session model), or, on the `Agent`-tool fallback, with `subagent_type: "Explore"` and `model: "opus"` (resolves to the latest Claude Opus, the most capable model). The Explore agent is read-only by design (Edit and Write are denied at the agent level). This ensures no subagent can accidentally modify the project during analysis. The explicit `model: "opus"` on the `Agent` path pins the fan-out to the latest Opus even when a cheaper default subagent model is configured, so the analysis never silently runs on a smaller model. Never use general-purpose subagents in this skill.137138**IMPORTANT:** Instruct each agent to read the **full target files** (not just snippets) so they understand the complete code structure, how functions relate to each other, and whether a proposed change would break callers or dependents.139140**Each agent's instructions must include:**1411421. **Cap findings at ~12** per agent. Quality over quantity — nit-flooding kills signal. If the agent has more candidates than that, keep only the most consequential ones for this codebase.1432. **Frame every finding as "why in THIS codebase"** — not generic blog advice. Tie each finding to the surrounding code, the project's conventions, the data flow, or the call sites. A finding that could appear verbatim in a tutorial is not a finding.1443. **Return 2-3 mandatory "Looks Good" callouts** — things this codebase already does well in the agent's lens. This grounds the report and prevents over-engineering.145146**Each finding must be returned in this structured format** (the `FINDINGS` schema above enforces it on the Workflow path; on the Agent-tool fallback, include the list in each prompt):147148- **ID**: agent-local identifier (e.g., O1, T1, F1 for the first agent's findings)149- **File**: exact path and line number(s)150- **Title**: short description, under 80 characters151- **Current pattern**: the code as it stands now, with a code snippet152- **Idiomatic alternative**: the proposed replacement, with a code snippet153- **Why (in this codebase)**: rationale tied to the surrounding code, the conventions, or specific call sites154- **Severity**: High (correctness or safety risk from non-idiomatic pattern), Medium (clarity or maintainability impact), Low (style / preference)155- **Confidence**: High / Medium / Low (how certain the agent is that this is an actual issue)156- **Risk**: Safe (behavior-preserving), Moderate (behavior-preserving but context-dependent), Breaking (intentionally changes behavior for correctness)157158---159160### Language Lens Matrix161162The three lenses are language-specific. Use the row that matches the language detected in Step 1.163164#### Rust165166- **Lens 1 — Ownership, Borrowing & Lifetimes**: unnecessary `.clone()` and `.to_owned()`, `String` parameters where `&str` or `Cow<'_, str>` fits, premature `RefCell`/`Mutex` where `&mut self` would work, `Rc<T>`/`Arc<T>` introduced before there's actual sharing, lifetime annotations that could be elided, ownership inversion at API boundaries (taking `T` when `&T` suffices), unnecessary `Box<T>` indirection.167- **Lens 2 — Type System & Trait Design**: `?` propagation over `.unwrap()`/`.expect()` outside tests, idiomatic `From`/`Into`/`TryFrom`/`AsRef` usage, `Display` vs `Debug` placement on user-facing types, newtype pattern for invariants and unit safety, trait objects (`dyn Trait`) vs generics, sealed traits via private supertraits, missing `#[non_exhaustive]` on public enums/structs, missing `#[must_use]` on result-like types.168- **Lens 3 — Idioms & Control Flow**: exhaustive `match` over chained `if let`, iterator chains over manual index loops, `?` propagation over nested `match`, `Default` impl over manual zero-init constructors, builder pattern for >3-arg constructors, `NonZero*` / `NonNull` for invariants, `OnceCell`/`LazyLock` over runtime-init globals, `let-else` for early returns.169170#### Python171172- **Lens 1 — Pythonic Constructs**: comprehensions over manual `for`-append loops, context managers (`with`) over manual `try/finally`, EAFP over LBYL where appropriate, tuple unpacking over indexing, walrus `:=` where it improves clarity, `pathlib.Path` over `os.path` strings, f-strings over `.format()` and `%`, `enumerate`/`zip` over index counters, `any`/`all` over manual flag loops.173- **Lens 2 — Type Hints & Data Modeling**: modern syntax (`X | Y`, `list[T]`, `dict[K, V]`) on Python ≥3.9/3.10, `Protocol` over `ABC` for structural typing, `dataclass`/`Pydantic`/`TypedDict` over plain dicts as records, `Generic[T]` and `TypeVar` correctness, `Optional[T]` vs `T | None` consistency, `Final`/`ClassVar`, `Self` (3.11+), missing return-type annotations on public APIs.174- **Lens 3 — Std Library & Patterns**: `collections` (`Counter`, `defaultdict`, `deque`, `ChainMap`), `functools` (`@cache`, `partial`, `singledispatch`, `reduce`), `itertools` (`chain`, `groupby`, `pairwise`, `batched`), `contextlib` (`@contextmanager`, `ExitStack`, `suppress`), async idioms (`asyncio.gather`, `asyncio.TaskGroup`, `async with`), generators over building intermediate lists.175176#### TypeScript / JavaScript177178- **Lens 1 — Type System & Inference**: `any` discipline (replace with `unknown` or a real type), narrowing via discriminated unions, `satisfies` operator over `as` assertions, `as const` for literal-narrowed values, generic constraints (`T extends ...`), branded / nominal types for invariants (IDs, units), exhaustive `switch` with `never` fallthrough, avoiding non-null assertions (`!`), type predicates over runtime checks.179- **Lens 2 — Async & Functional Patterns**: `Promise.all`/`Promise.allSettled` over serial `await`, `async/await` over `.then` chains, immutability (spread, `as const`, `readonly`), `map`/`filter`/`reduce` over imperative loops where it improves clarity, optional chaining (`?.`) and nullish coalescing (`??`) over manual null checks, `AbortController` for cancellable operations.180- **Lens 3 — Module Boundaries & Modern Syntax**: ESM imports over CJS where the project supports it, named exports over default, `import type` for type-only imports, structural patterns over inheritance, modern features (`Set`/`Map` over object-as-dict, `structuredClone`, `Object.groupBy`, `Array.prototype.at`), avoiding namespace imports, top-level `await` where supported.181182#### Go183184- **Lens 1 — Idioms & Error Handling**: error wrapping with `fmt.Errorf("...: %w", err)` and inspection via `errors.Is`/`errors.As`, sentinel vs typed errors used appropriately, `defer` correctness (argument capture vs invocation order), `errgroup.Group` for fan-out with cancellation, returning errors over panics, named return values used sparingly and only when they aid clarity, package naming (lowercase, no underscores).185- **Lens 2 — Concurrency & Channels**: channel direction (`chan<-`/`<-chan`) in function signatures, `context.Context` propagation as first parameter, `sync.Once` / `sync.Map` for the right shape of problem, goroutine leak risks (missing `select` on `ctx.Done()`), mutex granularity (`sync.RWMutex` where reads dominate), `select` patterns with timeouts, `sync.WaitGroup` correctness.186- **Lens 3 — Interface Design & Composition**: small interfaces (accept-interfaces, return-structs), embedding over inheritance, type assertions with comma-ok form, generics (Go 1.18+) used only for genuinely generic code (not as a Java-style cure-all), receiver naming (single-letter, consistent across methods), package-private types where the API doesn't require export.187188#### Ruby189190- **Lens 1 — Object Design & Duck Typing**: `Module` vs `Class` (mixins for behavior, classes for things), `attr_reader`/`attr_writer`/`attr_accessor` over manual accessors, `Struct`/`Data` for value types, `Comparable`/`Enumerable` inclusion to inherit batteries, `# frozen_string_literal: true` magic comment, refinements over global monkey-patching.191- **Lens 2 — Blocks, Procs & Enumerable**: `yield` vs `&block` parameters, lazy enumerables (`.lazy`) for large or infinite sequences, `each` vs `map` vs `reduce` choice, `tap` for side-effecting in a chain, `then` / `yield_self` for piping, avoiding `.each_with_index { |x, i| arr << ... }` in favor of `.map.with_index`.192- **Lens 3 — Metaprogramming Restraint & Style**: `define_method` only when the method shape genuinely varies, `method_missing` paired with `respond_to_missing?`, `class << self` over repeated `self.` prefixes, `Symbol#to_proc` (`&:method`) over `{ |x| x.method }`, keyword arguments over option hashes, hash shorthand syntax (Ruby 3.1+), avoiding clever metaprogramming where plain code reads better.193194#### Java / Kotlin / C# / Swift / PHP (generic template)195196For languages without an explicit matrix above, instruct the three lenses as:197198- **Lens 1 — Type System & Null Safety**: non-null annotations / non-optional types, sealed interfaces / sealed classes, records / data classes, generics correctness, optional types over null sentinels, immutability where the language supports it.199- **Lens 2 — Idiomatic Patterns**: collection / stream / sequence operations over manual loops, builder patterns for complex construction, modern equality / hashing / `toString`, pattern matching where supported, language-specific resource management (`try-with-resources`, `using`, `defer`, `Disposable`).200- **Lens 3 — Modern Language Features**: switch expressions / pattern matching, sealed interfaces, records / data classes, primary constructors, `var` / `val` inference, extension methods / extension functions, modern null-safe operators, structured concurrency primitives where applicable.201202In your prompt to each agent, explicitly include:203- The language name and detected version204- Which of the three lenses the agent is responsible for (with the bullet list of focus areas)205- The list of files in scope206- The conventions discovered in Step 1207- The required structured return format208- The cap of ~12 findings209- The "why in THIS codebase" framing requirement210- The mandatory 2-3 "Looks Good" callouts211212---213214## Step 3: Synthesize Severity-Sorted Report215216Collect all findings from the 3 agents and produce a single, prioritized report. **ultrathink** during deduplication and prioritization — multiple agents may flag the same code for related-but-different reasons, and the synthesis quality depends on resolving those overlaps thoughtfully.217218**Synthesis rules:**2192201. **Deduplicate**: if two agents flagged the same line or function for related reasons, merge into one finding with combined context. Note all applicable lenses on the merged finding.2212. **Prioritize**: sort by severity (High > Medium > Low). Within each severity, sort by confidence (High > Medium > Low).2223. **Be specific**: every finding must have a file path and line number. No hand-waving.2234. **Be actionable**: every finding must include both the current code snippet and the idiomatic replacement. No "consider improving" without showing what to do.2245. **Frame "why in THIS codebase"**: every finding's rationale must reference something specific in the surrounding code, conventions, or call sites — not generic best-practice prose.2256. **Omit empty sections**: if there are no High findings, do not include the High heading. Same for Medium and Low.2267. **Looks Good is mandatory**: include 3-5 callouts merged from the agents' "Looks Good" lists. This is non-negotiable per the skill's design.227228Assign findings sequential IDs across the entire report: `[I1]`, `[I2]`, `[I3]`, etc.229230**Use this report format:**231232```233## Idiom Audit: <language> — <scope description>234235**Scope**: <N files, M total lines> | **Language**: <name + version if detected>236**Findings**: <X total> (<A high, B medium, C low>)237238---239240### High241242| ID | File:Line | Title | Lens | Confidence | Risk |243|----|-----------|-------|------|------------|------|244| [I1] | `src/foo.rs:42` | <title> | Ownership | High | Safe |245246**[I1]** `src/foo.rs:42` — <Title>247**Current**:248```<lang>249<current code>250```251**Idiomatic**:252```<lang>253<replacement code>254```255**Why (in this codebase)**: <rationale tied to surrounding code, conventions, or call sites>256257---258259### Medium260261| ID | File:Line | Title | Lens | Confidence | Risk |262|----|-----------|-------|------|------------|------|263264(same per-finding detail format)265266---267268### Low269270| ID | File:Line | Title | Lens | Confidence | Risk |271|----|-----------|-------|------|------------|------|272273(same per-finding detail format)274275---276277### Looks Good (do not change)278279- <Strength callout from Lens 1>280- <Strength callout from Lens 2>281- <Strength callout from Lens 3>282```283284---285286## Step 4: Group Findings into PR-Sized Remediation Bundles287288Cluster the findings into tight, mergeable units. Each bundle becomes one pull request.289290**Bundling rules:**291292- Group by **file proximity** (same file or adjacent modules) and **theme** (same lens, same idiom). A bundle should read as one coherent change.293- Target size: **3-7 findings, 1-3 files, ~30-60 minute review effort**. A single high-impact finding can be its own bundle if the change is large.294- Avoid mixing severities in a single bundle when possible — High-severity bundles get reviewed under more scrutiny than style-only bundles.295- Skip findings that are too risky to bundle automatically (Risk = Breaking). Leave them in the report so the user can decide manually.296297**Each bundle has:**298299- **ID**: `B1`, `B2`, `B3`, …300- **Title**: imperative, ≤60 chars (this becomes the PR title and commit subject)301- **Theme**: one-line description of the unifying idea302- **Findings**: list of finding IDs covered (e.g., I1, I3, I7)303- **Files**: count + paths304- **Effort**: S (<30 min review) / M (30-60 min) / L (>60 min)305- **Risk**: Safe / Moderate / Breaking306- **Branch name**: `idiom-check/<kebab-slug-of-title>`307308**Bundle plan format:**309310```311### Remediation Bundles312313| ID | Title | Findings | Files | Effort | Risk |314|----|-------|----------|-------|--------|------|315| B1 | Replace clones with borrows in parser | I1, I3, I7 | 2 | M | Safe |316| B2 | Idiomatic error propagation with ? | I2, I5, I9, I12 | 3 | M | Safe |317| B3 | Iterator chains over manual loops | I4, I6 | 1 | S | Safe |318319**[B1]** Replace clones with borrows in parser320- Findings: I1, I3, I7321- Files: `src/parser/mod.rs`, `src/parser/tokens.rs`322- Effort: M (~45 min review)323- Risk: Safe — behavior-preserving324- Branch: `idiom-check/replace-clones-with-borrows`325326**[B2]** …327```328329After presenting the bundle plan, call `ExitPlanMode`, then ask:330331> **Apply which bundles?** (e.g., "apply all", "apply B1 and B2", "apply B1 only", "skip — keep the report")332333If there are no actionable bundles (e.g., the report is all "Looks Good" with a couple of Low-severity stylistic notes), skip the apply offer and confirm the codebase is in good shape.334335---336337## Step 5: Apply Selected Bundles (Full Ship)338339After the user picks one or more bundles, ship each one as an independent PR. Process bundles in their listed order; each bundle branches off the default branch so the PRs are independent and can be merged in any order.340341**Pre-flight checks** — do these once before processing the first bundle:342343```bash344git rev-parse --is-inside-work-tree 2>/dev/null || echo "not_a_repo"345git remote get-url origin 2>/dev/null | grep -qE 'github\.com[:/]' || echo "no_github_remote"346gh auth status 2>&1 | grep -q "Logged in" || echo "gh_not_authenticated"347git status --porcelain348```349350If the working tree is dirty, stop and ask the user whether to stash or abort. When stashing, use the explicit-SHA `git stash create` + `git stash store` pattern (consistent with `/refactor` and `/docstring-check`) so the snapshot can be restored reliably later — `git stash push` exits 0 even when there's nothing to stash, which makes a "revert all" hard to reason about:351352```bash353backup_sha=$(git stash create "idiom-check-backup: pre-bundle stash" 2>/dev/null)354if [ -n "$backup_sha" ]; then355 git stash store -m "idiom-check-backup: pre-bundle stash" "$backup_sha"356fi357```358359Record `$backup_sha` so the user can `git stash apply "$backup_sha"` later if they want the pre-bundle state back. If `$backup_sha` is empty, the working tree was clean — proceed without a backup.360361If `gh` is not authenticated or the remote is not GitHub, stop with an actionable error.362363**Resolve the default branch:**364365```bash366default_branch=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')367[ -z "$default_branch" ] && git rev-parse --verify main >/dev/null 2>&1 && default_branch=main368[ -z "$default_branch" ] && git rev-parse --verify master >/dev/null 2>&1 && default_branch=master369```370371**Track progress with tasks.** Before processing the first bundle, call `TaskCreate` once per approved bundle so the user sees live progress through the ship phase. Each task's subject should be the bundle title (`[B1] <title>`). As you start a bundle, mark its task `in_progress`; after the PR is opened, mark it `completed`. If a bundle's commit or push fails, leave the task `in_progress` and report the failure inline.372373**Per-bundle workflow** — repeat for each approved bundle:3743751. **Start from a clean default branch:**376 ```bash377 git checkout "$default_branch"378 git pull --ff-only origin "$default_branch" 2>/dev/null || true379 git checkout -b "idiom-check/<bundle-slug>"380 ```3813822. **Apply each finding's edit** with the `Edit` tool (or `Write` if the bundle introduces a new file). After each edit, briefly state what was applied:383 > **[I1]** Applied: replace `.clone()` with `&` borrow (`src/parser/mod.rs:42`)3843853. **Commit** with the bundle title as the subject and the theme + finding IDs in the body. Never use `--no-verify`:386 ```bash387 git add -A388 git commit -m "<bundle title>" -m "$(cat <<'EOF'389 <bundle theme>390 391 Applied findings: I1, I3, I7392 EOF393 )"394 ```3953964. **Push the branch:**397 ```bash398 git push -u origin HEAD399 ```4004015. **Open the PR** with `gh pr create`:402 ```bash403 gh pr create --base "$default_branch" --title "<bundle title>" --body "$(cat <<'EOF'404 ## Summary405 <bundle theme>406407 Generated by `/idiom-check`. Idiomatic improvements:408 - **[I1]** <I1 title> (`file:line`)409 - **[I3]** <I3 title> (`file:line`)410 - **[I7]** <I7 title> (`file:line`)411412 ## Test plan413 - [ ] Run the project's test suite414 - [ ] Manual sanity check on the touched modules415 EOF416 )"417 ```4184196. **Return to the default branch** before processing the next bundle so each bundle branches independently:420 ```bash421 git checkout "$default_branch"422 ```423424**Final report** after every approved bundle has been shipped:425426> **Idiom audit applied.** Opened <N> PRs:427> - **[B1]** <B1 title> → <pr_url>428> - **[B2]** <B2 title> → <pr_url>429> - **[B3]** <B3 title> → <pr_url>430>431> Note: when merging these PRs, avoid `gh pr merge --delete-branch` if you stack any of them on top of each other — it closes dependent PRs.432433**Skill handoff.** After the PRs are open, offer to do an independent second pass via `/vet` on each bundle's diff:434435> **Next:** Want me to hand off to `/vet` for one or more of these PRs? Useful as a second pair of eyes before you merge — `/vet` covers correctness, security, and conventions through a different lens than the idiom audit did.436437Use the `Skill` tool to invoke `/vet` (with the PR's branch as the argument) if the user agrees. Skip the offer when the bundles were trivially small (single-file Low-severity polish) or when the user wants to merge immediately.