From easy-cheese
Reviews a diff, PR, branch, or path across ten quality dimensions and emits a severity-grouped findings report, then auto-applies fixes via `/cure`.
How this skill is triggered — by the user, by Claude, or both
Slash command
/easy-cheese:ageThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill to review a diff or scoped path before merging, after `/press`, or whenever the user wants evidence-backed observations rather than an approval verdict.
Use this skill to review a diff or scoped path before merging, after /press, or whenever the user wants evidence-backed observations rather than an approval verdict.
Do not use it to apply fixes directly. Hand fix work to /cure, which owns applying findings.
Accept:
/age [<ref-or-range>] [--scope <path>] [--comprehensive] [--full] [--safe] [--open-pr] [--auto]
/age <slug> [--full] [--safe] [--open-pr] [--auto]
--full un-collapses the ## Low section when 10 or more low-severity findings exist (the default report collapses them to a one-line summary). Suppressed lows feed the cure-selection table only when --full is passed.
--safe re-introduces the cure-selection gate that the autonomous default skips (see ## Handoff). Use it when you want to choose findings before anything is fixed. --open-pr propagates to /cure so a clean cure may open a new PR when none exists (otherwise /cure only pushes an already-open one); see skills/cure/SKILL.md. Both flags propagate forward to /cure at the handoff.
When called with a <slug>, resolve .cheese/press/<slug>.md (if present) for press context and review the current working diff. When called with a <ref-or-range>, review that range. Default to the current working diff when neither is supplied. If the base branch is unclear, ask or use the repository's documented default.
--auto is the propagated autonomous-mode flag from /cook --auto. It changes the handoff (see ## Handoff). Track the cure-pass count internally so the two-cure-pass cap can be enforced — increment after each /cure --auto returns. The full chain is age → cure → age → cure → age → stop: up to three /age --auto invocations and up to two /cure --auto passes. Once two cure passes have completed, the next /age --auto writes the final report and stops without invoking /cure again. (This in-session contract uses conversation memory to track passes — it works because /cook --auto runs every phase in the same context. When invoked from /ultracook, each phase boots in fresh context with no shared memory; see ### When invoked from /ultracook below for the no-shared-memory variant.)
--hard is the propagated metacognitive-gate flag from /cook --hard (or /cheese --hard). Age does not fire the gate; it only passes --hard forward to /cure at the handoff so the gate can fire at the share-for-review boundary. See skills/hard-cheese/SKILL.md.
Dimensions answer what kind of problem. Severity (blocker / high / medium / low) is per-finding, computed from base + location + compounding modifiers (see references/dimensions.md § Severity computation).
| Dimension | Base range | Look for |
|---|---|---|
| correctness | low → blocker | broken behaviour, silent failures, ordering, null/empty edge cases, races, lost writes |
| security | low → blocker | auth, injection, secrets, unsafe parsing, tainted inputs, weak crypto |
| encapsulation | low → blocker | class-private peeks, module-internal leaks, cross-slice internals, ingress/egress contract violations, caller-shadowed domain invariants |
| spec | low → blocker | drift from stated requirements or acceptance criteria; silent drift on security/data/correctness reqs |
| complexity | low → high | unnecessary nesting, long functions, speculative abstractions, redundant state, parameter sprawl, stringly-typed code |
| deslop | low → high | dead code, AI residue, duplicated logic, copy-paste-with-variation, vague names |
| assertions | low → blocker | weak tests, shallow existence checks, swallowed errors, mocked SUT |
| nih | low → high | reinvented dependency, stdlib, or existing project helper / utility / component |
| efficiency | low → blocker | unnecessary work, missed concurrency, hot-path bloat, no-op updates, time-of-check/time-of-use (TOCTOU) pre-checks, memory leaks, overly broad reads |
| telemetry | low → blocker | silent error branches on non-interactive paths (servers, daemons, workers, outbound API/DB/queue calls), un-instrumented outbound calls, silent worker loops, hand-rolled logging infrastructure, missing rotation/retention/config hygiene on new file logging, unstructured logs, wrong log levels, double-logging, errors logged without context, missing correlation/trace ids, high-cardinality metric labels or span names, logs-as-metrics, print()/console.log in production, tests asserting on log strings |
Per-dimension base-severity tables, location-sensitivity, fix-cost-now / fix-cost-later, and recommendation shapes live in references/dimensions.md. This reduced workflow intentionally omits the git-history/precedent dimension.
.cheese/press/<slug>.md exists, read it and include a ## Press findings sub-section in the age report summarising unresolved items — /cure reads only .cheese/age/<slug>.md and cannot access the press report directly.blocker). Group findings by severity (## Blocker → ## High → ## Medium → ## Low); within a severity group, order by file..cheese/age/<slug>.md and print the path.## Handoff below). By default age auto-selects the recommended fix set and dispatches /cure in the same turn, gating for a human decision only when there is a genuine reason (a sprawling/structural fix in the set, or conflicting findings) or when --safe is passed. Age never applies fixes itself — /cure owns application — it only owns the selection and the dispatch.Code search and reading go through the cheez-* skills (/cheez-search, /cheez-read) — see those skills for tool selection rules. For caller graphs specifically, age uses cheez-search with kind: "callers" and tilth_deps (cheez-search owns the routing).
Beyond cheez-* there are review-specific tools:
| Need | Prefer | Fallback |
|---|---|---|
| Diff inspection | delta | git diff --unified=3 |
| Risk-scored impact + curated review context | code-review-graph: get_review_context_tool, get_impact_radius_tool, detect_changes_tool | tilth_deps + manual scoping |
| Architecture / hotspot framing for large diffs | code-review-graph: get_architecture_overview_tool, get_hub_nodes_tool, get_bridge_nodes_tool | skip and note in confidence |
| GitHub/PR context | gh | local git commands or user-provided PR data |
| Merge/conflict awareness | mergiraf | manual conflict checks |
Freshness: before the first code-review-graph query in a run, call build_or_update_graph_tool. The graph is persistent and goes stale between sessions. See /cheez-search for the full freshness contract and when semantic search beats tilth — steel threads across renamed layers, concepts under divergent names, spec-vs-code vocabulary mismatch.
Missing optional tools should not block review. State which evidence was unavailable and reduce confidence accordingly.
/age should fork a read-only review-context sub-agent when evidence gathering is likely to exceed the parent context, especially for --comprehensive reviews.
Spawn when any of these are true:
tilth_deps output is needed for hotspot, bridge-node, or blast-radius framing.The sub-agent returns a digest: orientation paragraph, high-signal path:line citations, gap list. The parent owns the ten-dimension review, severity grading, and the .cheese/age/<slug>.md report. Do not spawn for small diffs, to outsource severity grading, or to outsource the final verdict.
Digest size, parent-vs-sub-agent split, and harness-agnostic sub-agent selection live in references/sub-agent-gate.md — single source of truth for the cross-cutting rules.
Cross-cutting house style and citation form: ../../shared/formatting.md. This section owns the findings-report shape; formatting.md owns the voice rules and the footnote primitive.
Write to .cheese/age/<slug>.md with a minimum handoff slug at the top so /ultracook and /cheese --continue can chain without re-parsing the report:
status: ok | halt: <one-line reason>
next: cure | done
artifact: <path-to-press-report-or-prior-cure-if-any>
<one-line orientation: what the diff does>
# Age Report — <slug>
## Orientation
<one or two factual sentences about what the diff does>
## Press findings
<omit this section when `.cheese/press/<slug>.md` does not exist. When it does, summarise unresolved press items in one or two bullets so `/cure` (which never reads the press report directly) sees them.>
## Blocker
- **[encapsulation:blocker]** `src/users/index.ts:42` — `index` re-exports `SqlPgUser` (infra ORM type) across slice boundary. 3 consumer slices already import it.
- location: contract · fix-cost-now: sprawling · fix-cost-later: structural
- recommendation: define `User` in the slice's public types, map at the boundary, deprecate the leaked export.
## High
- **[security:high]** `src/api/admin/users.ts:55` — admin route accepts user-supplied filter without validation.
- location: contract · fix-cost-now: contained · fix-cost-later: contained
- recommendation: validate against `AdminFilter` schema at boundary.
## Medium
- **[complexity:medium]** `src/utils/format.ts:200-240` — 60-line function, 5 params.
- location: module · fix-cost-now: contained · fix-cost-later: contained
- recommendation: extract `formatHeader` / `formatBody`.
## Low
- **[deslop:low]** `src/utils/format.ts:18` — variable `data` shadows outer `data`.
- location: class · fix-cost-now: contained · fix-cost-later: contained
- recommendation: rename to `lineItems`.
## Confidence
<`certain` | `speculating` | `don't know`> — <one-line justification including which evidence sources were unavailable>
## Next step
Auto-fixing the recommended set via `/cure` (or, on a reason to ask / `--safe`, the selection prompt rendered inline — pick findings to cure or `none` to stop).
Empty severity sections are omitted entirely. When ten or more low findings exist, collapse the ## Low section to a single line:
## Low
*N low-severity findings suppressed.* Re-run with `--full` (or `/age --full`) to see them.
Suppressed lows feed the cure-selection table only when --full is passed.
status: ok when the review completed. status: halt: <reason> when evidence was unreachable in a way that blocks honest review. next: cure when at least one finding meets the medium+ floor (medium-or-above, or a cheap contained-fix low) and the chain has cure passes remaining; next: done when no finding meets the medium+ floor or the two-cure-pass cap has been reached.
Then print:
Age report: .cheese/age/<slug>.md
Pipeline: culture → mold → cook → press → [age] → cure → ship
After the report is on disk, age decides whether to act or ask. The default is to act: auto-select the recommended fix set and dispatch /cure in the same turn, no gate. The gate is reserved for the cases that genuinely need a human decision. Age never applies fixes — /cure owns application — it only owns the selection.
Compute the recommended set. The recommended selection is the composite all-medium, cheap — floor at medium (blockers + high + medium) unioned with every Low whose fix-cost-now: contained. Expand it against the report into resolved_ids.
Decide act vs ask:
Empty set — no finding meets the medium floor and no cheap lows exist. Nothing to cure: write next: done, print the report path, and stop. No question.
Reason to ask present — render the selection gate (below) and wait for a choice. A reason to ask is any of:
fix-cost-now: sprawling or fix-cost-later: structural (auto-applying a large/structural change is unrequested scope);--safe was passed (always gate).When the only reason is heavy findings, pre-select the recommended composite in the gate and flag the heavy rows so the user can drop them while the rest still go.
Otherwise — act. Announce the selection in one line (e.g. Auto-fixing 4 findings (all-medium, cheap) → /cure) and dispatch /cure immediately (see Dispatch below). No gate.
--safe, or a reason to ask)Use the shared handoff gate in ../../shared/handoff-gate.md.
Render the numbered selection table per ../cure/references/selection.md directly inline (one row per finding, grouped by severity); mark any sprawling/structural-fix row as heavy.
Ask which findings to cure. Lead each option with the verb (what the user wants to do next); the underlying selection verb is the backing detail. Lead with the recommended composite, then present the same four severity-floor options below it, in the same most-inclusive-to-least order, so the gate is predictable across every run:
all-medium, cheap (floor at medium — blockers + high + medium — unioned with every Low whose fix-cost-now: contained). The cheap lows are the small valid nits that are cheaper to fix than to defer; sprawling/structural lows are left out.all (every finding regardless of severity).all-medium (floor at medium: blockers + high + medium — the severity-floor portion of the medium+ auto-floor; add cheap to also union the contained-fix lows, i.e. the recommended composite above).all-high (floor at high, includes blockers).all-blocker.Then offer the two non-floor options last:
../cure/references/selection.md (1,3,5, all-blocker, all-medium, all-high, cheap, all, none, skip N; comma-compose to union).none.Present all four severity options on every run even when a severity band is empty (e.g. no blockers): a floor that resolves to an empty set is a valid, predictable no-op — do not drop or reorder options based on which bands happen to be populated. If the user selects a floor (or the recommended composite) that resolves to an empty set, treat the selection as none: report that no findings match and do not dispatch /cure with empty resolved_ids (the non-empty-selection contract in Dispatch still holds).
On a non-empty selection — whether auto-selected by default or chosen at the gate — immediately dispatch /cure <slug> [--safe] [--open-pr] [--hard] with the selection locked in via context, not a CLI flag:
handoff_context:
source_skill: /age
source_report: .cheese/age/<slug>.md
selection: "<recognized verb or explicit ids>"
resolved_ids: [<expanded ids>]
/cure skips its own selection prompt when this context is present, re-confirms the cited ids still exist, then owns the apply / validate / push loop. Always emit resolved_ids alongside selection — expand the verb yourself rather than leaving the field empty; /cure re-confirms against the report regardless. Propagate --safe, --open-pr, and --hard to /cure when they are in scope.
On none / Stop (only reachable via the gate), exit cleanly with the report path.
--auto substitutes a severity-floor selection and its own chain — see ### Auto mode below.
When invoked with --auto:
/cure again even if findings remain.medium+ floor (medium-or-above, or a Low whose fix-cost-now: contained), invoke /cure <slug> --auto --stake medium+ (forward --open-pr when it is in scope) and increment the cure-pass count when it returns.medium+ floor (no medium-or-above and no cheap lows remain), stop the chain with a one-line "auto chain clean" note and the report path./ultracook spawns age as a fresh-context sub-agent and owns the chain itself. Honour the no-chain override:
.cheese/age/<slug>.md (with the handoff slug at the top) and stop. Do not invoke /cure <slug> --auto --stake medium+ from inside the sub-agent.next: from what you observe on this run, not from any guess about chain position. next: cure when at least one finding meets the medium+ floor (medium-or-above, or a cheap contained-fix low); next: done when none do.next: field. Fresh-context age cannot count prior cure passes anyway, so this is the only honest contract. The orchestrator uses next: done for early-stop signalling; the natural terminal stop is the chain table running out of entries.When /age detects it is running as a sub-agent (the parent passes the invoked-from: cheese-factory-curd marker or equivalent context line in the prompt), it runs its ten dimensions inline within its own context instead of spawning per-dimension sub-agents. This honours the host's nesting-depth limit (harnesses cap sub-agent nesting depth, and the orchestrator's own spawn may already sit at that cap).
Detection mechanism: scan the invoking prompt for an invoked-from: line — values like cheese-factory-curd, fromagerie-curd, or any harness-specific marker the orchestrator passes in. When present, switch modes:
## Sub-agent context gate above is skipped under inline-degrade)./cure from the sub-agent — the orchestrator owns the chain.Inline-degrade is forced when the marker is present; there is no opt-out. Spawning a deeper sub-agent from inside a curd worker can exceed the harness's nesting limit and fail silently — the marker is the only honest signal that the parent has already consumed the available depth.
/cure, which owns application./cure without a gate. Ask first only on a genuine reason (a sprawling/structural fix in the set, or conflicting findings) or under --safe. An empty recommended set is a clean stop, not a question.certain | speculating | don't know); never emit a numeric score./cure reads the markdown directly.references/voice.md (output discipline, reasoning posture, confidence vocabulary).references/dimensions.md — per-dimension rubrics and recommendation shapes.references/voice.md — shared output discipline, reasoning posture, and confidence vocabulary.references/sub-agent-gate.md — shared sub-agent kernel: digest contract, harness-agnostic selection, what the parent never delegates.npx claudepluginhub paulnsorensen/easy-cheeseReviews diffs, PRs, and agent output for bugs, security issues, mocks, and code quality. Automates codebase audits with domain-specific checks and deep scanning.
Performs ad-hoc code reviews of PRs, diffs, or files across four dimensions: security, performance, correctness, maintainability. No track context needed.
Reviews implementation code for bugs, security issues, and quality problems. Creates FIX tasks for blocking issues before merge.