code-review
Review code for quality, correctness, and maintainability. Use this skill when reviewing pull requests, auditing existing code, refactoring for clarity, or enforcing coding standards. Covers DRY principles, SOLID design, error handling, performance, security, testing, and TypeScript/JavaScript best practices. For Obsidian plugin-specific API guidance, refer to the obsidian-plugin-development skill.
Best use case
code-review is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Review code for quality, correctness, and maintainability. Use this skill when reviewing pull requests, auditing existing code, refactoring for clarity, or enforcing coding standards. Covers DRY principles, SOLID design, error handling, performance, security, testing, and TypeScript/JavaScript best practices. For Obsidian plugin-specific API guidance, refer to the obsidian-plugin-development skill.
Teams using code-review should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/code-review/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How code-review Compares
| Feature / Agent | code-review | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Review code for quality, correctness, and maintainability. Use this skill when reviewing pull requests, auditing existing code, refactoring for clarity, or enforcing coding standards. Covers DRY principles, SOLID design, error handling, performance, security, testing, and TypeScript/JavaScript best practices. For Obsidian plugin-specific API guidance, refer to the obsidian-plugin-development skill.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
AI Agents for Coding
Browse AI agent skills for coding, debugging, testing, refactoring, code review, and developer workflows across Claude, Cursor, and Codex.
Cursor vs Codex for AI Workflows
Compare Cursor and Codex for AI coding workflows, repository assistance, debugging, refactoring, and reusable developer skills.
Best AI Skills for Claude
Explore the best AI skills for Claude and Claude Code across coding, research, workflow automation, documentation, and agent operations.
SKILL.md Source
# Code Review
## When to use this skill
Use this skill when:
- Reviewing a pull request or code diff
- Auditing existing code for quality issues
- Refactoring code for clarity or maintainability
- Enforcing coding standards and conventions
- Identifying bugs, security vulnerabilities, or performance problems
- Evaluating whether code follows DRY, SOLID, or other design principles
- Suggesting improvements to error handling, naming, or structure
For Obsidian plugin API specifics (vault operations, workspace, views, events, CLI), use the **obsidian-plugin-development** skill alongside this one.
## Review priorities
Evaluate code in this order of importance:
1. **Correctness** - Does it work? Are there logic errors, off-by-one bugs, race conditions, or unhandled edge cases?
2. **Security** - Are there injection vulnerabilities, unsafe data handling, or exposed secrets?
3. **Maintainability** - Can another developer understand and modify this code confidently?
4. **Performance** - Are there unnecessary allocations, redundant computations, or O(n^2) algorithms hiding in loops?
5. **Style** - Does it follow project conventions for naming, formatting, and structure?
## DRY - Don't Repeat Yourself
The DRY principle states that every piece of knowledge should have a single, unambiguous representation in the system.
### Recognizing violations
```typescript
// BAD: Logic duplicated across handlers
async function handleCreate(file: TFile) {
const content = await vault.read(file);
const metadata = app.metadataCache.getFileCache(file);
const tags = metadata?.frontmatter?.tags ?? [];
await processFile(content, tags);
logger.log('Processed:', file.path);
}
async function handleModify(file: TFile) {
const content = await vault.read(file);
const metadata = app.metadataCache.getFileCache(file);
const tags = metadata?.frontmatter?.tags ?? [];
await processFile(content, tags);
logger.log('Processed:', file.path);
}
```
```typescript
// GOOD: Extract shared logic
async function readFileWithTags(file: TFile): Promise<{ content: string; tags: string[] }> {
const content = await vault.read(file);
const metadata = app.metadataCache.getFileCache(file);
const tags = metadata?.frontmatter?.tags ?? [];
return { content, tags };
}
async function handleFileEvent(file: TFile) {
const { content, tags } = await readFileWithTags(file);
await processFile(content, tags);
logger.log('Processed:', file.path);
}
```
### When NOT to apply DRY
DRY is about knowledge duplication, not code duplication. Two blocks of code that look the same but represent different concepts should stay separate:
- **Different domains**: A validation rule for user input and a similar check in a database layer serve different purposes and may diverge.
- **Premature abstraction**: Three similar lines are better than a premature abstraction. Wait until the pattern appears 3+ times and is genuinely the same concept before extracting.
- **Test code**: Tests should be explicit and readable. Some repetition in tests is preferable to fragile shared test helpers.
## SOLID principles
### Single Responsibility
Each class or module should have one reason to change.
```typescript
// BAD: Class does API calls, caching, and UI rendering
class DataManager {
async fetchData() {
/* ... */
}
cacheResult(data: any) {
/* ... */
}
renderToView(data: any) {
/* ... */
}
}
// GOOD: Separate concerns
class DataFetcher {
async fetch(): Promise<Data> {
/* ... */
}
}
class DataCache {
store(data: Data) {
/* ... */
}
retrieve(): Data | null {
/* ... */
}
}
class DataView extends ItemView {
render(data: Data) {
/* ... */
}
}
```
### Open/Closed
Extend behavior through composition or interfaces, not by modifying existing code.
```typescript
// GOOD: New tool types don't require modifying the registry
interface Tool {
name: string;
execute(params: unknown): Promise<ToolResult>;
}
class ToolRegistry {
private tools = new Map<string, Tool>();
register(tool: Tool) {
this.tools.set(tool.name, tool);
}
}
```
### Dependency Inversion
Depend on abstractions, not concretions.
```typescript
// BAD: Direct dependency on implementation
class ChatService {
private api = new GeminiDirectApi();
}
// GOOD: Depend on interface
class ChatService {
constructor(private api: ModelApi) {}
}
```
## Error handling
### Principles
1. **Catch at the right level** - Handle errors where you have enough context to do something meaningful.
2. **Never swallow errors silently** - At minimum, log them.
3. **Use typed errors** - Prefer specific error types over generic `Error`.
4. **Fail fast** - Validate inputs early rather than letting invalid data propagate.
### Patterns
```typescript
// BAD: Swallowed error
try {
await riskyOperation();
} catch (e) {
// silently ignored
}
// BAD: Catching too broadly
try {
const data = await fetchData();
processData(data);
renderUI(data);
} catch (e) {
console.log('Something went wrong');
}
// GOOD: Specific handling with context
try {
const data = await fetchData();
} catch (error) {
if (error instanceof NetworkError) {
new Notice('Network unavailable. Please check your connection.');
logger.warn('Network error during fetch:', error.message);
return null;
}
// Re-throw unexpected errors
throw error;
}
```
### Async error handling
```typescript
// BAD: Unhandled promise rejection
someAsyncFunction(); // floating promise
// GOOD: Always await or catch
await someAsyncFunction();
// GOOD: Fire-and-forget with error handling
someAsyncFunction().catch((e) => logger.error('Background task failed:', e));
```
## Naming and readability
### Function and variable names
- **Functions**: Use verb phrases that describe what they do (`fetchUserData`, `validateInput`, `buildContextTree`).
- **Booleans**: Use `is`, `has`, `should`, `can` prefixes (`isValid`, `hasPermission`, `shouldRetry`).
- **Collections**: Use plural nouns (`files`, `tags`, `pendingRequests`).
- **Constants**: Use UPPER_SNAKE_CASE for true constants (`MAX_RETRIES`, `DEFAULT_TIMEOUT`).
- **Avoid abbreviations**: `error` not `err`, `result` not `res`, `response` not `resp` (except in very tight scopes like callbacks).
### Code structure
- **Early returns**: Reduce nesting by returning early for error cases.
- **Guard clauses**: Check preconditions at the top of functions.
- **Consistent abstraction levels**: A function should operate at one level of abstraction.
```typescript
// BAD: Deep nesting
async function processFile(path: string) {
const file = vault.getAbstractFileByPath(path);
if (file) {
if (file instanceof TFile) {
const content = await vault.read(file);
if (content.length > 0) {
// actual logic buried here
}
}
}
}
// GOOD: Early returns
async function processFile(path: string) {
const file = vault.getAbstractFileByPath(path);
if (!(file instanceof TFile)) {
return;
}
const content = await vault.read(file);
if (content.length === 0) {
return;
}
// actual logic at top level
}
```
## TypeScript-specific guidance
### Type safety
- **Avoid `any`**: Use `unknown` when the type is genuinely unknown, then narrow with type guards.
- **Use discriminated unions** over type assertions.
- **Prefer `interface` for object shapes** that may be extended; use `type` for unions, intersections, and aliases.
```typescript
// BAD: any everywhere
function process(data: any): any {
return data.value;
}
// GOOD: Proper typing
interface ProcessInput {
value: string;
metadata?: Record<string, unknown>;
}
function process(data: ProcessInput): string {
return data.value;
}
```
### Null handling
```typescript
// BAD: Non-null assertion hiding potential bugs
const file = vault.getAbstractFileByPath(path)!;
// GOOD: Explicit null check
const file = vault.getAbstractFileByPath(path);
if (!file) {
throw new Error(`File not found: ${path}`);
}
```
### Async patterns
- Never mix callbacks and promises in the same flow.
- Use `Promise.all()` for independent concurrent operations.
- Use `Promise.allSettled()` when partial failures are acceptable.
- Always handle rejections.
## Performance checklist
When reviewing for performance:
1. **Unnecessary re-computation**: Is the same value computed multiple times in a loop? Cache it.
2. **N+1 queries**: Are API calls or file reads happening inside loops? Batch them.
3. **Large data in memory**: Are entire files loaded when only metadata is needed? Use `metadataCache`.
4. **Missing debounce**: Are event handlers (editor changes, resize) triggering expensive operations on every event?
5. **Synchronous blocking**: Are heavy computations running on the main thread? Consider `requestIdleCallback` or chunked processing.
6. **Unnecessary DOM operations**: Are elements being created/destroyed when they could be updated? Batch DOM updates with `DocumentFragment`.
## Security checklist
1. **No `eval()` or `new Function()`**: Dynamic code execution is a security risk and violates Obsidian plugin guidelines.
2. **No `innerHTML` with untrusted content**: Use `createEl()`, `textContent`, or Obsidian's DOM helpers.
3. **Sanitize user input**: Especially file paths, search queries, and template variables.
4. **No hardcoded secrets**: API keys, tokens, and credentials belong in plugin settings, never in source.
5. **Validate external data**: API responses, file contents, and deserialized data should be validated before use.
6. **Use `requestUrl`**: Not `fetch`, for Obsidian plugin network requests (cross-platform compatibility).
## Testing review
When reviewing tests:
1. **Test behavior, not implementation**: Tests should assert observable outcomes, not internal details.
2. **One assertion concept per test**: Each test should verify one logical condition (may use multiple `expect` calls if they test the same concept).
3. **Descriptive test names**: `it('returns empty array when vault has no markdown files')` not `it('test1')`.
4. **Edge cases covered**: Empty inputs, null values, boundary conditions, error paths.
5. **No test interdependence**: Tests must not depend on execution order or shared mutable state.
6. **Mocks are minimal**: Only mock what's necessary. Over-mocking makes tests brittle and less valuable.
## Review comment guidelines
When writing review feedback:
1. **Be specific**: Point to the exact line and explain what's wrong and why.
2. **Suggest alternatives**: Don't just say "this is wrong" - show a better approach.
3. **Distinguish severity**: Mark issues as blocking (must fix), suggestion (should fix), or nit (optional).
4. **Explain the "why"**: Link to the principle being violated so the author learns, not just fixes.
5. **Acknowledge good work**: Call out well-designed code, clean refactors, or thorough tests.
## Further reading
- [TypeScript Patterns](references/typescript-patterns.md) - Common patterns and anti-patterns for TypeScript codebases
- [Review Checklist](references/review-checklist.md) - Quick-reference checklist for code reviewsRelated Skills
vault-semantic-search
Search vault notes by meaning using semantic search (RAG). Activate this skill when users want to find notes by concept or topic rather than exact keywords, or when keyword search tools return poor results.
recall-sessions
Search past agent conversations to recall prior discussions, decisions, and context. Activate this skill when users ask about previous conversations, want to resume past work, or reference earlier decisions.
obsidian-properties
Work with Obsidian note properties (frontmatter). Activate this skill when users want to add, modify, or organize properties, understand property types, format YAML frontmatter, or use properties with templates, search, or Bases.
obsidian-bases
Create and configure Obsidian Bases — database-like views of notes. Activate this skill when users want to create bases, write filters, formulas, or set up table/cards/list/map views.
image-generation
Generate images from text descriptions and save them to the vault. Activate this skill when users want to create illustrations, diagrams, visual content, or any AI-generated images.
gemini-scribe-help
Answer questions about Gemini Scribe plugin features, settings, and usage. Activate this skill when users ask how to use the plugin, configure settings, or troubleshoot issues.
deep-research
Conduct comprehensive, multi-source research and generate cited reports. Activate this skill when users want in-depth research on a topic, need synthesis across web and vault sources, or want a structured research report saved to their vault.
audio-transcription
Transcribe audio and video files into structured notes. Activate this skill when users want to transcribe recordings, meetings, podcasts, voice memos, or any audio/video content in their vault.
ui-ux-guidelines
UI/UX best practices for obsidian-gemini plugin development. Covers modal sizing, text overflow, message formatting, collapsible UI, animations, icons, file chips, session state, CSS containment, and theme compatibility. Use this skill when building or modifying UI components.
release-process
Full release workflow for obsidian-gemini: update release notes, run checks, bump version with npm, create a GitHub release, and verify. Use this skill when preparing a new plugin release.
obsidian-plugin-development
Build, modify, and debug Obsidian plugins using the TypeScript API. Use this skill when working with Obsidian plugin source code, the obsidian npm package, plugin UI (views, modals, settings, commands, ribbons), vault file operations, editor manipulation, workspace management, metadata cache, events, markdown rendering, or the Obsidian CLI. Covers plugin lifecycle, best practices, common patterns, and the full TypeScript API surface.
obsidian-cli
Use the Obsidian CLI to debug, inspect, and test Obsidian plugins during development. Covers plugin reloading, console inspection, runtime evaluation, and common debugging recipes for the gemini-scribe plugin.