Skills: The Heart of River Review
What is a Skill?
A skill is a reusable, version-controlled code review pattern that encapsulates team knowledge into a reproducible agent capability.
Think of skills as:
- 📚 A playbook - Documented review procedures your team follows
- 🔧 A tool - Ready-to-use review logic that runs automatically
- 📦 An artifact - Versioned, tested, shareable asset
Skills transform implicit knowledge ("we usually check for X") into explicit, automated checks that run consistently every time.
Why Skills Matter
The Problem: Implicit Knowledge
Traditional code review relies on human memory and experience:
- "Did we check for SQL injection?"
- "Remember to verify error handling"
- "Don't forget the accessibility audit"
This creates:
- Inconsistency - Reviews depend on who's available
- Knowledge loss - Experts leave, knowledge disappears
- Scaling issues - Can't review everything thoroughly
The Solution: Skills as Assets
Skills make implicit knowledge explicit and executable:
# Before: "Remember to check for SQL injection"
# After: A versioned, testable skill
id: security-basic
name: Baseline Security Checks
version: 0.1.0
Benefits:
- Reproducible - Same checks every time
- Improvable - Test and refine with fixtures
- Shareable - Teams can exchange skills
- Scalable - Review all PRs without human bottleneck
Skill Anatomy
Every skill has five core components:
1. Metadata (skill.yaml)
Declares what the skill does and when to run it:
id: security-basic # Unique identifier
name: Baseline Security Checks # Human-readable name
version: 0.1.0 # Semantic version
description: Detects common vulnerabilities
# When to activate
phase: midstream # SDLC phase
applyTo: # File patterns
- 'src/**/*.ts'
- 'src/**/*.js'
# What it needs/produces
inputContext: [diff] # Input requirements
outputKind: [findings] # Output type
# Optimization hints
modelHint: cheap # Cost/accuracy balance
severity: major # Finding importance
2. Prompts (prompt/)
The review logic in natural language:
prompt/system.md - Sets the role:
You are a security-focused code reviewer.
Your task is to identify common vulnerabilities.
Focus on:
- SQL injection
- XSS vulnerabilities
- Hardcoded secrets
prompt/user.md - Applies to specific code:
Review this diff for security issues:
{{diff}}
Report findings using this format:
**Finding:** [description]
**Fix:** [suggestion]
**Severity:** [info|minor|major|critical]
3. Fixtures (fixtures/)
Sample inputs for testing:
fixtures/01-sql-injection.md:
+const query = `SELECT * FROM users WHERE id = ${userId}`;
+const result = await db.query(query);
4. Golden Outputs (golden/)
Expected results for each fixture:
golden/01-sql-injection.md:
**Finding:** SQL injection vulnerability in user lookup
**Fix:** Use parameterized queries: `db.query('SELECT * FROM users WHERE id = ?', [userId])`
**Severity:** major
5. Evaluation Config (eval/)
How to test the skill:
eval/promptfoo.yaml:
prompts:
- file://../prompt/system.md
- file://../prompt/user.md
tests:
- vars:
diff: file://../fixtures/01-sql-injection.md
assert:
- type: contains
value: 'SQL injection'
- type: similar
value: file://../golden/01-sql-injection.md
threshold: 0.7
Skill Lifecycle
See also Skill Lifecycle for a deeper walkthrough.
1. Create
# Interactive scaffolding
npm run create:skill
# Manual creation
cp -r scripts/templates/skill skills/my-skill
2. Implement
Edit the prompts to encode your review knowledge:
# prompt/system.md
You are a React code reviewer checking for common pitfalls.
Rules:
1. State updates must be immutable
2. Event handlers should be memoized
3. Lists must have stable keys
3. Test
Add fixtures and golden outputs:
# Add test cases
echo "..." > fixtures/01-state-mutation.md
echo "..." > golden/01-state-mutation.md
# Run evaluation
cd skills/my-skill
npx promptfoo eval
4. Validate
Ensure the skill meets schema requirements:
npm run validate:skill-yaml
5. Deploy
Skills automatically activate when:
- Phase matches (upstream/midstream/downstream)
- File patterns match (
applyToglob) - Required context is available (
inputContext)
No deployment step needed—just commit to repository!
6. Improve
Monitor skill effectiveness and iterate:
# Run regression tests
npx promptfoo eval
# Update prompts based on results
# Add more fixtures for edge cases
# Bump version in skill.yaml
Creating Your First Skill
Step-by-Step Example
Let's create a skill that checks for missing TypeScript null checks:
1. Scaffold:
npm run create:skill
# Enter:
# ID: typescript-nullcheck
# Name: TypeScript Null Safety
# Phase: midstream
# Files: src/**/*.ts
2. Write System Prompt:
# prompt/system.md
You are a TypeScript code reviewer focused on null safety.
Check for:
- Optional chaining (?.) vs explicit null checks
- Nullish coalescing (??) vs || for defaults
- Non-null assertions (!) without validation
3. Write User Prompt:
# prompt/user.md
Review this TypeScript code for null safety issues:
{{diff}}
For each issue, provide:
- **Issue:** What's wrong
- **Line:** Line number
- **Fix:** Corrected code
- **Severity:** minor|major
4. Add Fixtures:
# fixtures/01-unsafe-property-access.md
+function getName(user) {
- return user.profile.name;
+}
5. Add Golden Output:
# golden/01-unsafe-property-access.md
**Issue:** Unsafe property access without null check
**Line:** 2
**Fix:** Use optional chaining: `return user?.profile?.name;`
**Severity:** major
6. Configure Evaluation:
# eval/promptfoo.yaml
prompts:
- file://../prompt/system.md
- file://../prompt/user.md
providers:
- id: openai:gpt-4o
config:
temperature: 0.1
tests:
- description: Unsafe property access
vars:
diff: file://../fixtures/01-unsafe-property-access.md
assert:
- type: contains
value: 'optional chaining'
- type: llm-rubric
value: |
Score 1 if the output correctly identifies the null safety issue
and suggests optional chaining. Score 0 otherwise.
7. Test:
cd skills/midstream/typescript-nullcheck
npx promptfoo eval
# Review results in eval/results.json
8. Commit:
git add skills/typescript-nullcheck
git commit -m "feat: add TypeScript null safety skill"
Done! The skill now runs automatically on TypeScript PRs.
Skill Design Principles
1. Single Responsibility
❌ Bad: "Check code quality, security, and performance" ✅ Good: "Detect SQL injection vulnerabilities"
One skill, one concern. Focused skills are:
- Easier to test
- Easier to understand
- Easier to maintain
2. Clear Contracts
Explicitly declare what you need and produce:
inputContext: [diff, fullFile] # I need both diff and full file
outputKind: [findings, metrics] # I produce findings and metrics
This enables:
- Smart skill selection (only run when context available)
- Better error messages (missing context = skip, not fail)
- Future optimizations (parallel execution based on dependencies)
3. Testable with Fixtures
Every skill should have:
- At least 2 fixtures (happy path + edge case)
- Corresponding golden outputs
- Assertions that verify behavior
fixtures/
01-happy-path.md → golden/01-happy-path.md
02-edge-case-null.md → golden/02-edge-case-null.md
03-false-positive.md → golden/03-false-positive.md
4. Versioned Carefully
Use semantic versioning:
- Patch (0.1.1): Fix typos, improve examples
- Minor (0.2.0): Add new checks, expand scope
- Major (1.0.0): Change output format, break compatibility
5. Model-Hint Appropriate
Choose the right cost/accuracy balance:
modelHint: cheap # Fast syntax checks, style guides
modelHint: balanced # Standard code review
modelHint: high-accuracy # Security audits, architecture review
Cheap skills run first (fast feedback), high-accuracy last (thorough analysis).
Advanced Concepts
Input Contexts
Skills can request different types of input:
inputContext:
- diff # Git diff (fastest, least context)
- fullFile # Complete file content
- tests # Associated test files
- adr # Architecture Decision Records
- commitMessage # Commit message for context
Framework provides the requested context or skips the skill.
Output Kinds
Skills can produce different outputs:
outputKind:
- findings # Standard review comments
- questions # Clarifying questions for author
- metrics # Quantitative measurements
- actions # Suggested automated fixes
Output formatters adapt based on declared kinds.
Dependencies
Skills can declare required tools:
dependencies:
- test_runner # Need to run tests
- coverage_report # Need coverage data
- custom:sonarqube # Custom tool integration
Framework validates availability before running skill.
Skill Composition (Future)
Skills will be able to reference other skills:
dependencies:
- skill:typescript-strict # Run this first
This enables:
- Layered checks (basic → advanced)
- Skill reuse (security skill calls auth skill)
- Workflow orchestration
Evaluation Philosophy
Why Evaluate?
Skills are code. Like any code, they need tests.
Evaluation answers:
- Does it work? (Regression testing)
- Is it effective? (Quality measurement)
- Is it consistent? (Reproducibility check)
Evaluation Strategies
1. Exact Match (Strict)
assert:
- type: contains
value: 'SQL injection'
Use for: Specific technical terms, required phrases
2. Similarity (Flexible)
assert:
- type: similar
value: file://../golden/01-expected.md
threshold: 0.7 # 70% semantic similarity
Use for: Natural language outputs, variable phrasing
3. LLM-as-Judge (Rubric)
assert:
- type: llm-rubric
value: |
Score 1 if the output identifies the vulnerability
and provides a concrete fix. Score 0 otherwise.
Use for: Complex criteria, subjective quality
Best Practice: Combine multiple assertions:
assert:
- type: contains
value: 'SQL injection' # Must mention the issue
- type: llm-rubric
value: 'Suggests parameterized queries' # Must have fix
- type: similar
value: file://../golden/01.md
threshold: 0.6 # Should resemble expected output
Continuous Evaluation
Run evaluations regularly:
# Before commit
npm run eval:fixtures
# In CI (optional - requires API keys)
# See .github/workflows/skills-and-tests.yml
Treat skill quality like code quality—test before merge.
Best Practices
Do's
✅ Start small - One simple skill is better than one complex skill ✅ Test thoroughly - Add fixtures for edge cases and false positives ✅ Document intent - Explain why the skill exists, not just what it does ✅ Version carefully - Breaking changes need major version bumps ✅ Iterate based on feedback - Use evaluation results to improve
Don'ts
❌ Don't be vague - "Check code quality" is too broad ❌ Don't skip fixtures - Untested skills will drift ❌ Don't hard-code specifics - Use variables for file paths, names ❌ Don't ignore false positives - Add fixtures to prevent them ❌ Don't forget severity - Help users prioritize findings
Examples from the Registry
Security Skill
# skills/security-basic/skill.yaml
id: security-basic
name: Baseline Security Checks
phase: midstream
applyTo: ['src/**/*.{ts,js}']
inputContext: [diff]
modelHint: cheap
severity: major
Use Case: Fast security scan on every PR
Architecture Skill
# skills/upstream/adr-decision-quality/SKILL.md
id: adr-decision-quality
name: ADR Decision Quality
phase: upstream
applyTo: ['docs/adr/*.md']
inputContext: [fullFile, adr]
modelHint: high-accuracy
severity: info
Use Case: Thorough architecture decision review
Test Coverage Skill
# skills/downstream/coverage-gap/SKILL.md
id: coverage-gap
name: Test Coverage Gap Detection
phase: downstream
applyTo: ['src/**/*.{ts,js}']
inputContext: [diff, tests, coverage_report]
modelHint: balanced
severity: minor
Use Case: Identify untested code paths
Migration from Legacy Format
Have existing markdown-based skills? Migrate gradually:
# 1. Create registry format skill
npm run create:skill
# 2. Copy prompt content
cp skills/midstream/old-skill.md \
skills/new-skill/prompt/system.md
# 3. Add metadata
# Edit skills/new-skill/skill.yaml
# 4. Add fixtures
# Create skills/new-skill/fixtures/*.md
# 5. Test
cd skills/new-skill
npx promptfoo eval
# 6. Retire old skill
# (Keep it until new skill is validated)
Resources
- Architecture - How skills fit into the framework
- Skill Schema Reference - Complete schema reference
- Skill Template - Starting point for new skills
- promptfoo Docs - Evaluation framework
- Skills Catalog - Registry overview
Next Steps
- Explore existing skills - Browse
skills/for examples - Create your first skill - Use
npm run create:skill - Test with fixtures - Add test cases and run
promptfoo eval - Share with your team - Skills are shareable across projects
Skills are the heart of River Review. By encoding team knowledge as reproducible, testable assets, you build a living library of code review expertise that grows with your team.