一键导入
suede-code-review
Find the bugs a diff can actually ship: TypeScript, React, Next.js, OWASP, accessibility, SEO, database, and deploy-risk review.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Find the bugs a diff can actually ship: TypeScript, React, Next.js, OWASP, accessibility, SEO, database, and deploy-risk review.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
The pack's contract/customer-service negotiator, proven outside a repo: scans an Amazon account for restocking fees, short refunds, and forgotten or overpriced digital subscriptions (Prime Video Channels like Britbox/Starz/AMC+/Paramount+, Audible membership, Kindle Unlimited, Prime itself) — money Amazon is quietly holding or billing that the owner never noticed — then drives Amazon's live chat to get fees waived, refunds issued, or unused subscriptions canceled and the last charge refunded. Recovered $448.31 in one sitting — including a full refund on an item Amazon had already denied once, with no return required. Use this whenever the user mentions Amazon returns, restocking fees, an Amazon refund that looks short, a forgotten subscription (Britbox, Starz, Audible, Kindle Unlimited, etc.), disputing an Amazon charge, checking whether a return was fully refunded, auditing recurring Amazon charges, or asks something like 'did I get charged for that return', 'am I still paying for Britbox', or 'is Amazon sti
The pack's contract negotiator, generalized beyond Amazon: a recurring-charge auditor that finds forgotten, unused, or overpriced subscriptions across any service (Netflix, Spotify, Hulu, Disney+, gym memberships, SaaS tools, news sites, app subscriptions) and either cancels them directly or negotiates a refund/waiver through that service's own support channel. Complements amazon-returns-recovery, which stays scoped to Amazon returns/restocking fees and Amazon-billed subscriptions (Prime Video Channels, Audible, Kindle Unlimited) — this skill covers everything billed outside Amazon: direct-bill streaming and software, App Store and Google Play subscriptions, and PayPal-billed recurring payments. Use this whenever the user wants to audit recurring charges generally, asks what subscriptions they're paying for, mentions a specific non-Amazon subscription (Netflix, Spotify, a gym, a SaaS tool, etc.), wants to find and cancel unused subscriptions, or asks something like 'what am I still paying for', 'find my subsc
Umbrella workflow for 25 public skills: copy, design, code review, SEO, launch packaging, MCP QA, iOS conversion, and creator workflows.
Claude-directed parallel OpenAI Codex CLI worker fleet for bulk generation. Use when a job is high-volume, well-specified, and splits into independent worker-sized tasks (content batches, test generation, bulk refactors) and Codex CLI is installed and logged in. Claude decomposes, briefs, spawns codex exec runs in parallel, and review-gates every output. NOT FOR: multi-lane Claude agents coordinating one complex change (use suede-agent-teams); low-volume, judgment-dense copy Claude should write itself (use suede-copy or johnny-suede-write).
Design and write polished product surfaces people understand fast: landing pages, dashboards, campaigns, restyles, UI copy, and visual QA.
Make Suede interfaces feel intentional: tokens, color, components, type, motion, dark mode, and visual QA for shipped screens.
| name | suede-code-review |
| description | Find the bugs a diff can actually ship: TypeScript, React, Next.js, OWASP, accessibility, SEO, database, and deploy-risk review. |
Review code with full context: changed files, callers, contracts, deploy surface. Find real breakage. Rank by production impact. Every finding has a file, evidence, and a fix path. No findings without evidence. No volume without signal.
Default: Sonnet. Recommend Opus for auth, payments, and public API surface reviews.
Before review, identify:
Build a lightweight graph before judging the diff:
Flag beyond-the-diff risks when related files, defaults, docs, env, or deploy requirements no longer agree.
Do not hand-review for what a tool already decides. Before manual analysis, run the
gates the repo already ships and fold the output into findings. Detect what exists
from package.json scripts, config files, and lockfiles — run only those. Never
introduce a tool the repo does not use, and never fabricate a result you did not run.
typecheck script, or the type checker directly. An error
on a changed line is at least P2; on a changed critical path, P1.The categories above are universal; the actual command differs by surface. Use this as a reference, not a checklist — detect which of these apply to the target repo, run only what exists, and never fabricate a result you did not run.
| Stack | Type check | Lint | Test | Other |
|---|---|---|---|---|
| Web / Node (TS/JS) | npx tsc --noEmit | npm run lint | npm run test | npm audit for dependency CVEs |
| MCP server (Node) | node --check <server>.mjs | repo's configured linter, if any | — | JSON-RPC smoke test: pipe a minimal request over stdio, e.g. echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | node <server>.mjs and confirm a well-formed response |
| iOS / Swift (Xcode) | — (compiler check is the build) | SwiftLint, if configured | XCTest target, if present | xcodebuild -project X.xcodeproj -scheme X -destination 'platform=iOS Simulator,name=iPhone 16' build |
| API / backend (generic) | language's own type/compile step, if any | repo's configured linter | contract or schema test — e.g. OpenAPI/schema validation against the live route, or the repo's own contract-test suite | — |
Cite the command, its exit status, and the file:line it implicates. If a gate cannot run (no script, missing deps, sandboxed), say so in Verification — never report a gate as passed that you did not execute.
A review that fights the house style produces noise, not signal. Honor what the repo already encodes.
CLAUDE.md, AGENTS.md,
CONTRIBUTING.md, .editorconfig, formatter/linter config, and any review-config
file at the repo root or in the changed directories. A documented convention is
binding — do not flag what a rule permits; do flag what it forbids.AGENTS.md overrides a repo-root rule for files under that path.Add a --depth modifier to any review:
--quick (~2 min): Pattern-based scan. Flag obvious bugs, hardcoded
secrets, missing null checks, SQL/command injection patterns, broken error
handling. No cross-file analysis. Use for PRs with narrow blast radius.--standard (default, ~10 min): Per-file analysis: correctness on changed paths, language-specific traps (see checklists below), state handling, test coverage on changed behavior, call graph within changed files. Default for all PRs unless the user says otherwise.--deep (~25 min): Cross-file analysis including full import graph and
call chain tracing. Finds semantic bugs that only appear when you follow data
across module boundaries. Use for auth changes, payment flows, data
migrations, and public API changes.State depth level at the top of every review output.
Check these on every TypeScript file in the diff:
any vs unknown: any disables type checking for everything downstream. If the type is truly unknown at the call site, use unknown and narrow with a type guard. Flag every any annotation that isn't in a third-party type shim.!): Each foo!.bar is a runtime crash waiting for the condition that makes foo null. Flag unless the null case is provably eliminated by a guard two lines above.{ type: 'a', ... } | { type: 'b', ... }, the switch/if must be exhaustive. Missing default: assertNever(x) is a silent future bug.as Foo): A cast without a preceding type guard means "trust me." Flag every as that isn't a DOM cast (as HTMLInputElement) or a narrow type refinement proven by the preceding condition.Object.keys() without keyof typeof: Object.keys(obj).forEach(k => obj[k]) fails type checking silently at runtime when keys are typed. Require (Object.keys(obj) as Array<keyof typeof obj>) or a typed Object.entries().Promise without await in an async function: Returns the Promise object instead of the resolved value. Flag any return someAsyncFn() inside an async that should return await someAsyncFn().Check these on every React component or hook in the diff:
useEffect dependency array: useEffect(fn) (no array) runs on every render. useEffect(fn, []) runs once. useEffect(fn, [dep]) runs when dep changes. Flag any effect where the deps array is absent or obviously incomplete (function references, object literals, values used inside the effect but not listed).setInterval inside useEffect(fn, []) that reads a stateful value. The fix is either adding the dep or using a ref.key props: Any .map() returning JSX elements must have a stable, unique key. Using array index as key is a bug when the list can reorder or filter. Flag index-keyed lists where items have identity (id, slug, etc.).useMemo/useCallback when those objects are created inline in the parent render. Flag components that could be wrapped in React.memo but aren't, when rendered in tight loops or high-frequency update paths.arr.push(item) on state does not trigger a re-render. Flag any direct mutation of state variables.Check these on every React component or HTML-producing file in the diff:
<img> without alt, or alt="" on non-decorative images. Empty alt skips the element for screen readers; flag when the image conveys content.<button>, <a>, <input> with no visible text, no aria-label, and no aria-labelledby. An icon-only button with no label is invisible to assistive tech.<div onClick={...}> used as a button; <span> used as a link; heading levels skipped (h1 → h3) or used for visual styling instead of document hierarchy. Use the correct element or add role= with keyboard handlers.onClick but not onKeyDown (Enter, Space, arrow keys). Flag interactive elements that can't be reached or operated by keyboard alone.text-gray-400 on white, text-white on light-colored buttons, any low-contrast pairing below ~4.5:1 for body text or ~3:1 for large text.<input> or <select> without an associated <label> (via htmlFor/id) or aria-label. Placeholder text is not a label.aria-hidden="true" on interactive elements (traps keyboard users); role="presentation" on semantically meaningful elements; ARIA roles that contradict the native element's semantics.Check these on every Next.js file in the diff:
'use client' cannot import server-only modules (DB clients, fs, crypto, server-side env vars). Any file without 'use client' that uses useState, useEffect, browser globals (window, document), or event handlers is a runtime crash. Flag the mismatch, not just the symptom.Suspense boundary: Async server components that fetch data and are rendered inside a client component tree need a <Suspense fallback={...}> wrapper. Missing boundaries cause the entire parent tree to suspend without a fallback.error.tsx / loading.tsx: Any new route segment that fetches data or can throw should have both. Flag their absence as P2 when the route is user-facing.process.env.SECRET_KEY in a 'use client' file or in a prop passed from server to client component is exposed in the browser bundle. Only NEXT_PUBLIC_* vars are safe client-side. Flag any non-NEXT_PUBLIC_ env var referenced in client code.generateMetadata / getServerSideProps fetches: These run on every request. An uncached external fetch here is a latency and cost bomb. Flag missing { next: { revalidate: N } } or unstable_cache wrapping.useRouter from next/router in App Router: App Router uses next/navigation. Importing from next/router in an App Router project silently fails or returns stale data. Flag the wrong import.Check on any file that touches routes, layouts, metadata, or public-facing content:
generateMetadata, <Head>, <title>, description, og:title, og:description, og:image, twitter:card. Any removal or blanking of previously populated fields is a regression. Flag if generateMetadata now returns fewer keys than before the change.canonical URL changed, removed, or now pointing to a different domain. Flag any change to canonical logic — canonical changes can consolidate or fragment link equity unintentionally.noindex, nofollow, or X-Robots-Tag: noindex appearing on pages that were previously indexable. Flag any new robots metadata that restricts crawling on a route that wasn't restricted before.sitemap.ts / sitemap.xml not updated when routes change. Flag route additions or removals without a corresponding sitemap change.og:image URLs broken, pointing to localhost, missing dimension params, or removed from previously covered pages. Flag layout-level changes that remove OG image generation entirely.schema.org markup changed, fields removed, or @type changed. Flag removals of @type, name, url, or description from any existing structured data block.llms.txt / AI discoverability: if the repo includes llms.txt or llms-full.txt, flag any changes that expand or restrict what AI crawlers are permitted to see.Check these on any file that touches DB queries:
WHERE id IN (...) or a join. Flag any .map() or for loop that calls db.query(), prisma.find*(), or db.select() inside the body.WHERE, ORDER BY, GROUP BY, or join condition on a column that isn't indexed is a full-table scan. Flag new query predicates on columns with no corresponding index in the migration/schema.INSERT/UPDATE/DELETE calls that must succeed or fail together. Flag any multi-table write that isn't wrapped in db.transaction() / prisma.$transaction().UNIQUE constraint are race-condition bugs at scale. Flag schema definitions where uniqueness is enforced in application code but not the DB.db.select().from(table) with no LIMIT on a user-facing route is a DoS vector and a cost spike. Flag selects with no limit when the table can grow.import of a package not already in the bundle. If the package's minzipped size is >20 KB, flag as P3 with the size estimate. Prefer tree-shaken imports (import { X } from 'pkg' not import pkg from 'pkg').<script src="..."> without async or defer in HTML head blocks page paint. Flag in any HTML template or _document.tsx.import at the top of _app.tsx or a layout file when it could be next/dynamic with ssr: false. Flag routes not in the critical path (settings pages, dashboards, modals).next/image: <img src="..."> bypasses Next.js image optimization. Flag raw <img> tags in Next.js files pointing to non-SVG assets.For --deep reviews on auth changes, payment flows, data migrations, or public API changes, run as separate lanes:
Collect consensus first. If high-severity concerns persist after a fix cycle, keep status at hold and name the smallest next check or patch.
The changed-file import graph is the floor. The bugs that ship hide outside the diff. On --deep, widen past the immediate callers:
git log -L or git blame the changed region. If this area was fixed before, a change that reintroduces the old shape is a regression — cite the prior commit. If it churns repeatedly, flag it as fragile.State what you traced. A whole-repo claim with no symbols named is not evidence.
Check on any new route handler, API endpoint, background job, cron, queue consumer, or service function:
try/catch blocks that catch without logging or Sentry capture. Flag catch (e) {} and catch (e) { console.error(e) } — a caught error that only console.errors is invisible in production. Require Sentry.captureException(e) or equivalent on unexpected errors.Run this automatically at the end of every review. No exceptions. It answers one question: is this safe to deploy right now?
Grade each dimension. Each is pass / conditional / block:
git revert fully undo this? Red flags: schema migrations, irreversible external API calls (email sent, payment charged, data permanently deleted), S3/storage mutations, message queue publishes. Block if rollback requires manual data repair.~0% (new feature, flagged), ~partial (specific flow), ~100% (shared middleware, auth, DB query in hot path). Higher blast radius requires more evidence before deploy..github/workflows/ (or equivalent CI) exist and cover the changed surface (build, types, tests)? Are required status checks enforced on main? Block if the changed surface has no automated gate and the repo is production-connected.Output block — required at the end of every review:
DEPLOY SAFETY
Breaking changes: pass | conditional | block — [evidence]
Rollback safety: pass | conditional | block — [evidence or red flag]
Blast radius: ~X% — [which path or user segment]
Environment readiness: pass | conditional | block — [missing vars if any]
Dependency changes: pass | conditional | block — [new deps and CVE status]
Data mutations: pass | conditional | block — [irreversible operations if any]
Security delta: improved | neutral | block — [surface changed]
Verdict: SAFE TO DEPLOY | DEPLOY WITH CONDITIONS | DO NOT DEPLOY
Conditions (if any):
This skill emits no letter grade. When the caller wants lane grades too, run $suede-code (findings + grade) or $suede-code-grader (grade only) — those two carry the canonical Instant-F trigger list, grade caps, and A-F scale. Instant-F patterns found here (hardcoded secrets, injection, auth bypass, unverified payment webhooks, destructive migrations with no rollback, plaintext sensitive data) are P0 findings and set the Ship Gate to hold.
Run automatically on every review. Scan the raw diff for content that should never reach git history. No exception for "it's just a branch" — dirty commits propagate.
Check every line added (+) in the diff for:
.env variable values hardcoded in source. Patterns: sk_, pk_, Bearer , -----BEGIN, password=, secret=, token=, AKIA, ghp_, xoxb-, long hex/base64 strings adjacent to key-like identifiers.console.log, console.error, debugger, print(, pprint(, binding.pry, byebug, dd(, dump(, var_dump(, hardcoded test user IDs, TODO: remove, FIXME: before merge, HACK:, commented-out blocks of real logic.<<<<<<<, =======, >>>>>>>, |||||||. A conflict marker in a + line means the file was not fully resolved.node_modules/, .next/, dist/, build/, *.pyc, __pycache__/, .DS_Store, *.log, *.sqlite, *.db, migration snapshot files that weren't meant to commit, generated lock-file noise from the wrong package manager.[WIP], DO NOT MERGE, TEMP:, SKIP CI in non-CI files, stash this, placeholder copy like lorem ipsum, foo, bar, asdf in production-facing strings.public/ or assets/ directory.Score:
COMMIT DIRT SCORE
Secrets / credentials: clean | suspicious | dirty — [pattern found or "none"]
Debug artifacts: clean | suspicious | dirty — [symbol or line or "none"]
Conflict markers: clean | dirty — ["none" or file:line]
Accidentally staged: clean | dirty — [path or "none"]
WIP breadcrumbs: clean | suspicious | dirty — [marker or "none"]
Oversized / binary: clean | dirty — [file and size or "none"]
Exposed internals: clean | suspicious | dirty — [pattern or "none"]
Overall dirt rating: CLEAN | SUSPICIOUS | DIRTY
A DIRTY overall rating automatically sets the Ship Gate to hold.
Lead with findings, ordered by severity. Group repeated patterns once: "This pattern appears in 4 files: [list]. Fix described once below."
P0 / P1: use the full block:
[P0] path/to/file.ts:142
Issue: JWT secret falls back to empty string; any token is valid when SECRET is unset.
Fix: `process.env.JWT_SECRET ?? (() => { throw new Error('JWT_SECRET required') })()`
Verify: set JWT_SECRET="" and curl /api/me — expect 401, currently 200.
OWASP: A02 Cryptographic Failures
Confidence: high
P2 / P3: one line each:
[P2] components/Feed.tsx:88 — missing key prop on .map() return; use item.id not index. TS will catch post-fix.
[P3] utils/format.ts:12 — magic number 86400; extract as SECONDS_PER_DAY.
Severity:
If the issue cannot be tied to a file, route, command, state, or user-visible behavior, mark it as an open question instead.
When asked to fix, convert each accepted finding into an agent-ready brief:
Fix one risk cluster at a time. After fixing, rerun the relevant review mode and do not close the finding until evidence confirms it.
When the user asks to apply fixes (--fix flag or equivalent instruction):
OWASP check runs automatically on any file in: auth/, api/, middleware/, routes/, pages/api/, or any file importing a crypto, session, or payment module. Cite the OWASP category in the finding.
Always check these when relevant:
Check on every Swift file in the diff. iOS ships on a release cycle with no hot-fix — a crash here is live for days.
!, try!, as!): each crashes when the optional is nil or the cast fails. Flag unless the failure case is provably eliminated immediately above. try! on anything that throws at runtime (decoding, file I/O) is P1.self inside a stored property, Task, Combine sink, or callback leaks the owner. Require [weak self] (or [unowned self] only when lifetime is provably bound).@State, @Published, or UIKit views from a background context is undefined behavior. Flag observable or UI mutation inside Task.detached, a URLSession callback, or a background queue with no hop to @MainActor / DispatchQueue.main.nonisolated used to silence a warning rather than because access is truly isolation-free; @MainActor work awaited from a path that should already be on main.decode on fields the backend does not guarantee.ForEach over a non-stable id (index, or a value that changes) loses state and breaks animations; onAppear used for one-time work re-fires on re-insertion; @StateObject vs @ObservedObject confusion (owning vs observing) re-creates or prematurely releases models.Pair with iOS / Native Contract Drift below: crash risk here, contract break there.
Check on any API route, response shape, auth header, or shared type that iOS or other native consumers depend on:
Authorization, X-Session-Token, or similar headers are validated server-side. If the server changes the expected format, the iOS app gets 401s on every request.{ error: string } or { code: number }. Changing the error envelope silently breaks native error handling with no visible failure on the web surface.types/, shared/, or lib/ consumed by both web and a native build step. A field rename or removal here breaks both surfaces simultaneously.Blast radius note: identify which iOS version is currently in production. If the app hasn't shipped an update in >2 weeks, assume the old contract is live for the majority of users and treat breaking changes as P0.
Flag tech debt patterns as P3, group by file, don't block ship. Do not block a ship on P3 tech debt unless it directly obscures a P0/P1 bug. File as follow-up.
Code that works but does not do what it claimed is still a defect.
For a PR summary or any review spanning four or more files, lead with a walkthrough so the reader sees the shape before the findings:
sequenceDiagram (request → handler → service → data) or flowchart when the change moves data across three or more boundaries or alters an async, auth, or payment path. Skip it for a localized edit — a diagram of a one-file change is noise.The walkthrough orients; it never replaces findings, and stays above the Findings block.
False positives are why reviewers get muted. Self-check the draft findings before emitting:
For a review:
Findings
Deploy Safety
Commit Dirt Score
Open Questions
Verification Checked
Ship Gate
For no findings, say that clearly and name any residual risk or unrun checks.
For a PR summary:
Summary
Change Map
Risk Map
Verification
Ship Gate
The ship gate is always the last line of a review output:
SHIP GATE: hold | ship-with-caveats | ship
Reason: [one sentence, naming the blocking finding ID or named caveat]
hold: a blocker or high-risk unknown remains.ship-with-caveats: no blocker remains, but named non-critical caveats exist.ship: no known blocker remains and required verification passed.