Claude Skill

review

Front door for review of any kind — code, a plan, a document that needs named reviewer perspectives, or pending changes with a security question. Classifies the subject in front of it and routes to exactly one review skill; does no reviewing itself. Use for 'review this', '/revie

LLM Mart · 0 points · 13 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download ConnorGriffin-skills-skills_workflows_review-872be56.zip · 5 KB
Part of connorgriffin/skills — 25 skills

Install

skills CLI npx skills add https://github.com/ConnorGriffin/skills/tree/main/skills/workflows/review
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install connorgriffin-skills@llmmart
Git git clone https://github.com/ConnorGriffin/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole connorgriffin/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Review

Front door for review, the way scope is the front door for work that isn't ready to build. Classify what is in front of it, announce the route, invoke that route's skill. This skill does none of the reviewing itself — the standards-and-spec pass it used to run now lives in code-review.

Routes

Routes are data, not prose, layered from two files:

  • Shipped: routes.json in this directory. Four rows ship today:
    • code → code-review — changed code against the repo's documented standards and the originating issue.
    • plan → plan-review — a plan, spec, work order, or agent brief, before anything is built.
    • personas → persona-review — a document that needs named reviewer perspectives.
    • security → the security review that ships with the agent — pending changes carrying a security question.
  • Operator: ~/.config/review/routes.json. A row whose route matches a shipped row replaces it; any other route extends the table. See Registering a review type below.

Process

  1. Classify. Read what's in front of you — a diff, a document, the user's own words — and pick the route whose for text matches it.
  2. Announce. Say the route in one line before invoking anything: "routing to code-review" or equivalent. This is how the caller knows which review ran, not a request for approval.
  3. Invoke. Call that route's skill (or, for security, the agent's built-in security review), return its result to the caller, and let the caller continue its own completion boundary. Nothing here re-runs the review or second-guesses its output.

Ambiguity

Matching scope: pick a route and announce it. Ask exactly one framing question only when the subject genuinely admits two routes — a spec with code already written against it, say. Never ask when the subject is clearly one thing; a clear subject paired with a manufactured question is stalling, not scoping.

The stop rule

A registered route whose skill is missing stops and reports what is missing and how to install it. It never runs a nearby review instead. This is the load-bearing rule in this skill: a missing security route that silently becomes a code review produces a passing verdict nobody should trust, which is worse than no review at all.

Route resolution decides this mechanically — see Resolving a route below — and its answer is final, not a suggestion to route around.

The not-a-route case reads differently from not-installed, on purpose: one means "review has no idea what that is," the other means "review knows what that is and can't reach it yet." Conflating them either hides a real gap behind "not supported," or manufactures support behind a name nobody registered.

Resolving a route

scripts/resolve_route.py makes the outcomes above machine-decidable instead of judgment calls:

python3 scripts/resolve_route.py <route>
python3 scripts/resolve_route.py --list

Exit statuses:

  • 0 — installed. The route is registered and its skill was found (or, for an agent-builtin row, ships with the agent — presence not verified on disk).
  • 3 — registered but missing. The route is registered, its skill is a skill kind, and no skill directory was found. The message names the skill and, for a skill this pack ships, the install command; for one it doesn't ship, the row's source file instead. It never names another route.
  • 4 — not a route. The name matches no row. The message lists the registered route names.
  • 2 — malformed config. ~/.config/review/routes.json is not valid JSON, or a row is missing a field or carries an unknown kind. Names the file and the problem. This exit is never returned for the three outcomes above.

--list prints every registered row as route<TAB>skill<TAB>kind<TAB>for, exit 0.

Registering a review type

An installation with its own review skill — an infra-plan review, a compliance review, whatever it runs internally — registers it by adding a row to ~/.config/review/routes.json:

[
  { "route": "code", "skill": "internal-code-review", "kind": "skill",
    "for": "changed code, using our internal standards checker" },
  { "route": "infra", "skill": "infra-plan-review", "kind": "skill",
    "for": "a pulumi or terraform plan before it's applied" }
]

The first row replaces the shipped code route (same route value); the second adds a new one. Registering a row does not install the skill it names — the operator still installs infra-plan-review separately, and until then resolve_route.py infra reports it registered but missing.

Files (skills)
  • agents
    • openai.yaml 230 B
      interface:
        display_name: "Review"
        short_description: "Route to one review type: code, plan, personas, or security"
        default_prompt: "Use $review to route this to the right review skill — code, plan, personas, or security."
      
  • scripts
    • resolve_route.py 5.8 KB
      #!/usr/bin/env python3
      """Resolve a review route to its skill, or report why it can't be resolved.
      
      Route rows are data, layered from two files: the shipped ``routes.json`` beside
      this script's parent directory, and an operator's ``~/.config/review/routes.json``
      (or ``$REVIEW_ROUTES_CONFIG``). An operator row replaces a shipped row with the
      same ``route`` value; any other ``route`` value extends the table.
      
      Three outcomes are machine-decidable for a single route: installed (exit 0),
      registered but its skill is missing (exit 3), and not a registered route at all
      (exit 4). A fourth, malformed config (exit 2), is a trust-boundary failure, not a
      normal outcome, and is never returned for the other three.
      """
      
      from __future__ import annotations
      
      import json
      import os
      import sys
      from collections import OrderedDict
      from pathlib import Path
      
      SHIPPED_ROUTES = Path(__file__).resolve().parent.parent / "routes.json"
      ROUTE_FIELDS = {"route", "skill", "kind", "for"}
      VALID_KINDS = {"skill", "agent-builtin"}
      
      # Skill names this pack itself ships a directory for. A row naming one of these
      # gets the pack's own install command when missing; any other skill name gets a
      # pointer at the row's source file instead, since this pack can't install it.
      PACK_SHIPPED_SKILLS = {"code-review", "plan-review", "persona-review"}
      INSTALL_COMMAND = "npx skills add ConnorGriffin/skills --skill {skill}"
      
      EXIT_INSTALLED = 0
      EXIT_USAGE = 1
      EXIT_MALFORMED_CONFIG = 2
      EXIT_MISSING = 3
      EXIT_NOT_A_ROUTE = 4
      
      
      class ConfigError(Exception):
          pass
      
      
      def operator_config_path() -> Path:
          override = os.environ.get("REVIEW_ROUTES_CONFIG")
          if override:
              return Path(override)
          return Path(os.path.expanduser("~/.config/review/routes.json"))
      
      
      def load_rows(path: Path, *, required: bool) -> list[dict]:
          if not path.exists():
              if required:
                  raise ConfigError(f"{path}: missing")
              return []
          try:
              text = path.read_text(encoding="utf-8")
          except OSError as error:
              raise ConfigError(f"{path}: {error}")
          try:
              data = json.loads(text)
          except json.JSONDecodeError as error:
              raise ConfigError(f"{path}: invalid JSON: {error.msg}")
          if not isinstance(data, list):
              raise ConfigError(f"{path}: must be a JSON list of route rows")
          rows = []
          for index, row in enumerate(data):
              if not isinstance(row, dict) or set(row) != ROUTE_FIELDS:
                  raise ConfigError(
                      f"{path}: row {index} must have exactly route, skill, kind, for"
                  )
              if not all(isinstance(row[field], str) and row[field] for field in ROUTE_FIELDS):
                  raise ConfigError(f"{path}: row {index} fields must be nonempty strings")
              if row["kind"] not in VALID_KINDS:
                  raise ConfigError(f"{path}: row {index} has an unknown kind {row['kind']!r}")
              rows.append({**row, "_source": str(path)})
          return rows
      
      
      def merged_rows() -> "OrderedDict[str, dict]":
          merged: "OrderedDict[str, dict]" = OrderedDict()
          for row in load_rows(SHIPPED_ROUTES, required=True):
              merged[row["route"]] = row
          for row in load_rows(operator_config_path(), required=False):
              merged[row["route"]] = row
          return merged
      
      
      def skill_roots() -> list[Path]:
          override = os.environ.get("REVIEW_SKILL_ROOTS")
          if override is not None:
              return [Path(part) for part in override.split(os.pathsep) if part]
          roots: list[Path] = []
          project_dir = os.environ.get("CLAUDE_PROJECT_DIR")
          if project_dir:
              roots.append(Path(project_dir) / ".claude" / "skills")
          roots.append(Path(".claude") / "skills")
          roots.append(Path(os.path.expanduser("~/.claude/skills")))
          roots.append(Path(".agents") / "skills")
          roots.append(Path(os.path.expanduser("~/.agents/skills")))
          return roots
      
      
      def find_skill(skill: str) -> Path | None:
          for root in skill_roots():
              candidate = root / skill / "SKILL.md"
              if candidate.is_file():
                  return candidate
          return None
      
      
      def cmd_list(rows: "OrderedDict[str, dict]") -> int:
          for row in rows.values():
              print(f"{row['route']}\t{row['skill']}\t{row['kind']}\t{row['for']}")
          return EXIT_INSTALLED
      
      
      def cmd_resolve(rows: "OrderedDict[str, dict]", route: str) -> int:
          row = rows.get(route)
          if row is None:
              names = ", ".join(rows.keys())
              print(
                  f"{route!r} is not a registered review type here. "
                  f"Registered routes: {names}"
              )
              return EXIT_NOT_A_ROUTE
      
          if row["kind"] == "agent-builtin":
              print(
                  f"{route} -> {row['skill']} ships with the agent itself; its presence "
                  f"was not verified on disk. for: {row['for']}"
              )
              return EXIT_INSTALLED
      
          path = find_skill(row["skill"])
          if path is not None:
              print(f"{route} -> {row['skill']} at {path}. for: {row['for']}")
              return EXIT_INSTALLED
      
          if row["skill"] in PACK_SHIPPED_SKILLS:
              print(
                  f"{route} -> {row['skill']} is registered but not installed. "
                  f"Install it with: {INSTALL_COMMAND.format(skill=row['skill'])}"
              )
          else:
              print(
                  f"{route} -> {row['skill']} is registered but not installed, and this "
                  f"pack does not ship it. See {row['_source']} for where this row came from."
              )
          return EXIT_MISSING
      
      
      def main(argv: list[str]) -> int:
          try:
              rows = merged_rows()
          except ConfigError as error:
              print(f"resolve_route: {error}", file=sys.stderr)
              return EXIT_MALFORMED_CONFIG
      
          if argv == ["--list"]:
              return cmd_list(rows)
          if len(argv) == 1 and argv[0] != "--list":
              return cmd_resolve(rows, argv[0])
      
          print("usage: resolve_route.py <route> | --list", file=sys.stderr)
          return EXIT_USAGE
      
      
      if __name__ == "__main__":
          raise SystemExit(main(sys.argv[1:]))
      
  • routes.json 635 B
    [
      {
        "route": "code",
        "skill": "code-review",
        "kind": "skill",
        "for": "changed code against the repo's documented standards and the originating issue"
      },
      {
        "route": "plan",
        "skill": "plan-review",
        "kind": "skill",
        "for": "a plan, spec, work order, or agent brief, before anything is built"
      },
      {
        "route": "personas",
        "skill": "persona-review",
        "kind": "skill",
        "for": "a document that needs named reviewer perspectives"
      },
      {
        "route": "security",
        "skill": "security-review",
        "kind": "agent-builtin",
        "for": "pending changes carrying a security question"
      }
    ]
    
  • SKILL.md 5 KB
    ---
    name: review
    description: Front door for review of any kind — code, a plan, a document that needs named reviewer perspectives, or pending changes with a security question. Classifies the subject in front of it and routes to exactly one review skill; does no reviewing itself. Use for 'review this', '/review', or any request to review a PR, diff, plan, spec, brief, document, or security-sensitive change.
    ---
    
    # Review
    
    Front door for review, the way `scope` is the front door for work that isn't ready to
    build. Classify what is in front of it, announce the route, invoke that route's
    skill. This skill does none of the reviewing itself — the standards-and-spec pass it
    used to run now lives in `code-review`.
    
    ## Routes
    
    Routes are data, not prose, layered from two files:
    
    - **Shipped:** [`routes.json`](routes.json) in this directory. Four rows ship today:
      - `code` → `code-review` — changed code against the repo's documented standards and
        the originating issue.
      - `plan` → `plan-review` — a plan, spec, work order, or agent brief, before anything
        is built.
      - `personas` → `persona-review` — a document that needs named reviewer
        perspectives.
      - `security` → the security review that ships with the agent — pending changes
        carrying a security question.
    - **Operator:** `~/.config/review/routes.json`. A row whose `route` matches a shipped
      row replaces it; any other `route` extends the table. See *Registering a review
      type* below.
    
    ## Process
    
    1. **Classify.** Read what's in front of you — a diff, a document, the user's own
       words — and pick the route whose `for` text matches it.
    2. **Announce.** Say the route in one line before invoking anything: "routing to
       `code-review`" or equivalent. This is how the caller knows which review ran, not a
       request for approval.
    3. **Invoke.** Call that route's skill (or, for `security`, the agent's built-in
       security review), return its result to the caller, and let the caller continue its
       own completion boundary. Nothing here re-runs the review or second-guesses its output.
    
    ## Ambiguity
    
    Matching `scope`: pick a route and announce it. Ask exactly **one** framing question
    only when the subject genuinely admits two routes — a spec with code already written
    against it, say. Never ask when the subject is clearly one thing; a clear subject
    paired with a manufactured question is stalling, not scoping.
    
    ## The stop rule
    
    A registered route whose skill is missing **stops** and reports what is missing and
    how to install it. It never runs a nearby review instead. This is the load-bearing
    rule in this skill: a missing `security` route that silently becomes a `code` review
    produces a passing verdict nobody should trust, which is worse than no review at all.
    
    Route resolution decides this mechanically — see *Resolving a route* below — and its
    answer is final, not a suggestion to route around.
    
    The **not-a-route** case reads differently from **not-installed**, on purpose: one
    means "review has no idea what that is," the other means "review knows what that is
    and can't reach it yet." Conflating them either hides a real gap behind "not
    supported," or manufactures support behind a name nobody registered.
    
    ## Resolving a route
    
    `scripts/resolve_route.py` makes the outcomes above machine-decidable instead of
    judgment calls:
    
    ```
    python3 scripts/resolve_route.py <route>
    python3 scripts/resolve_route.py --list
    ```
    
    Exit statuses:
    
    - **0 — installed.** The route is registered and its skill was found (or, for an
      `agent-builtin` row, ships with the agent — presence not verified on disk).
    - **3 — registered but missing.** The route is registered, its skill is a `skill`
      kind, and no skill directory was found. The message names the skill and, for a
      skill this pack ships, the install command; for one it doesn't ship, the row's
      source file instead. It never names another route.
    - **4 — not a route.** The name matches no row. The message lists the registered
      route names.
    - **2 — malformed config.** `~/.config/review/routes.json` is not valid JSON, or a
      row is missing a field or carries an unknown `kind`. Names the file and the
      problem. This exit is never returned for the three outcomes above.
    
    `--list` prints every registered row as `route<TAB>skill<TAB>kind<TAB>for`, exit 0.
    
    ## Registering a review type
    
    An installation with its own review skill — an infra-plan review, a compliance
    review, whatever it runs internally — registers it by adding a row to
    `~/.config/review/routes.json`:
    
    ```json
    [
      { "route": "code", "skill": "internal-code-review", "kind": "skill",
        "for": "changed code, using our internal standards checker" },
      { "route": "infra", "skill": "infra-plan-review", "kind": "skill",
        "for": "a pulumi or terraform plan before it's applied" }
    ]
    ```
    
    The first row replaces the shipped `code` route (same `route` value); the second adds
    a new one. Registering a row does **not** install the skill it names — the operator
    still installs `infra-plan-review` separately, and until then `resolve_route.py infra`
    reports it registered but missing.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related