Install
npx skillscat add hitoshura25/claude-devtools/skills-linting-check Install via the SkillsCat registry.
Linting Check Skill
Run linter on code changes and fix all issues before marking work complete.
Purpose
Ensure all code changes meet the project's style and quality standards. This is a mandatory quality gate - work is never complete with unfixed lint errors.
Prerequisites
- Linter must be configured (use
linting-setupskill if needed) - Project has lint commands available
Scope of Responsibility
CRITICAL DISTINCTION: Modified Files vs Unchanged Files
Files You Modify
When you touch a file for ANY reason, you own its code quality:
- ✓ Errors: MUST fix all errors in modified files
- ✓ Warnings: MUST fix OR justify with inline comment
- ✓ Style issues: Auto-fix with formatter
NO "unless documented" escape hatch for files you modify.
Example:
# You modify src/api.ts to add endpoint
# Linter reports 5 pre-existing errors in same file
# YOU MUST FIX ALL 5 + ensure your new code is clean
git diff --name-only main
# src/api.ts ← You touched this, you own all issues
npm run lint -- src/api.ts
# Fix ALL errors, not just your new codeFiles You Don't Touch
For files not in your changeset:
- Errors: Not your responsibility (but create tracking issue if critical)
- Warnings: Not your responsibility
- Don't run lint --fix on entire codebase (changes too many files)
How to verify:
# Only lint files you actually changed
npm run lint -- $(git diff --name-only --diff-filter=ACMRTUXB main | grep '\.ts$')Why This Matters
Incremental improvement philosophy:
- Each PR should leave code cleaner than it found it
- "Boy Scout Rule": Leave the campground cleaner than you found it
- If everyone fixes issues in files they touch, codebase quality improves
Bad practice:
✗ "I added a function to utils.ts, but there are 10 pre-existing
warnings. I'll just document them."Good practice:
✓ "I'm touching utils.ts, so I'll fix the 10 warnings AND ensure
my new function is lint-clean."Process
1. Run Linter
Execute the appropriate lint command for the project:
TypeScript/JavaScript:
npm run lint
# Or check specific files:
npm run lint -- src/auth.tsPython:
# With Ruff (modern)
ruff check .
# With Pylint (traditional)
pylint your_package/
# Check specific file
ruff check src/parser.pyKotlin/Android:
# ktlint
./gradlew ktlintCheck
# Detekt
./gradlew detekt
# Both
./gradlew ktlintCheck detekt2. Review Lint Output
Understand what the linter is reporting:
Example ESLint Output:
src/auth.ts
12:5 error 'token' is assigned a value but never used @typescript-eslint/no-unused-vars
23:10 warning Unexpected console statement no-console
45:1 error Missing return type on function @typescript-eslint/explicit-function-return-type
✖ 3 problems (2 errors, 1 warning)
1 error and 0 warnings potentially fixable with the `--fix` optionExample Ruff Output:
src/parser.py:12:5: F841 Local variable `data` is assigned to but never used
src/parser.py:23:10: E501 Line too long (105 > 88 characters)
src/parser.py:45:1: D103 Missing docstring in public function
Found 3 errors.3. Auto-Fix What's Safe
Many linters can automatically fix certain issues:
TypeScript/JavaScript:
npm run lint:fix
# Or with direct eslint:
npx eslint . --ext .ts,.tsx --fixPython:
# Ruff auto-fix
ruff check --fix .
# Or format with ruff
ruff format .Kotlin:
# ktlint auto-format
./gradlew ktlintFormatWhat gets auto-fixed:
- ✓ Formatting (spacing, indentation, line breaks)
- ✓ Import sorting
- ✓ Missing semicolons
- ✓ Quote style consistency
- ✓ Trailing whitespace
What requires manual fixes:
- ✗ Unused variables
- ✗ Missing return types
- ✗ Complex logic issues
- ✗ API misuse
4. Manually Fix Remaining Issues
For issues that can't be auto-fixed:
Unused Variables:
// Before (error)
function processData(input: string, token: string) {
return input.toUpperCase();
}
// Fix 1: Use the variable
function processData(input: string, token: string) {
validateToken(token);
return input.toUpperCase();
}
// Fix 2: Remove if not needed
function processData(input: string) {
return input.toUpperCase();
}
// Fix 3: Prefix with _ if intentionally unused
function processData(input: string, _token: string) {
return input.toUpperCase();
}Missing Return Types:
// Before (error)
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Fix: Add types
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}Line Too Long:
# Before (error - 120 chars)
user_data = fetch_user_from_database(user_id, include_profile=True, include_preferences=True, include_history=True)
# Fix: Break into multiple lines
user_data = fetch_user_from_database(
user_id,
include_profile=True,
include_preferences=True,
include_history=True
)5. Handle Exceptions (Sparingly)
Sometimes you need to disable a rule, but ALWAYS document why:
TypeScript Inline Disable:
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Legacy API requires any type
function handleLegacyResponse(data: any): void {
// ...
}Python Inline Disable:
# ruff: noqa: E501 -- URL is long and cannot be broken
LEGACY_API_URL = "https://api.example.com/v1/very/long/path/that/cannot/be/shortened"Kotlin Suppress:
@Suppress("MagicNumber") // Port numbers are inherently magic
const val DEFAULT_PORT = 8080Rules for Exceptions:
- ✓ Always include a comment explaining WHY
- ✓ Be as specific as possible (disable one rule, not all)
- ✓ Use inline disables, not file-level or project-level
- ✓ Consider if there's a better way to fix the issue
- ✗ Never disable rules just because you don't want to fix them
6. Re-Run Linter
After fixing issues, run linter again to confirm clean:
npm run lint # TypeScript
ruff check . # Python
./gradlew ktlintCheck # KotlinExpected output:
✓ No linting errors found7. Check Formatting
If project uses a formatter (Prettier, Black, ktlint format):
TypeScript/JavaScript with Prettier:
npm run format:check
# If issues found:
npm run formatPython with Ruff:
ruff format --check .
# If issues found:
ruff format .Kotlin with ktlint:
./gradlew ktlintCheck
# If issues found:
./gradlew ktlintFormatCommon Lint Issues and Fixes
Unused Imports
Before:
import { useState, useEffect, useMemo } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
return <div>{count}</div>;
}Fix: Remove unused imports
import { useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
return <div>{count}</div>;
}Inconsistent Naming
Before:
def Calculate_Total(item_list):
total_sum = 0
for Item in item_list:
total_sum += Item.price
return total_sumFix: Follow conventions (snake_case for Python)
def calculate_total(item_list):
total_sum = 0
for item in item_list:
total_sum += item.price
return total_sumMissing Documentation
Before:
export function processPayment(amount: number, userId: string) {
// ...
}Fix: Add JSDoc
/**
* Process a payment for a user.
* @param amount - Payment amount in cents
* @param userId - User's unique identifier
* @returns Payment confirmation ID
*/
export function processPayment(amount: number, userId: string): string {
// ...
}Complexity Issues
Before (too complex):
function validateUser(user) {
if (user) {
if (user.email) {
if (user.email.includes('@')) {
if (user.password) {
if (user.password.length > 8) {
return true;
}
}
}
}
}
return false;
}Fix: Simplify with early returns
function validateUser(user: User): boolean {
if (!user) return false;
if (!user.email || !user.email.includes('@')) return false;
if (!user.password || user.password.length <= 8) return false;
return true;
}Lint vs Format
Understand the difference:
Linting (code quality):
- Finds bugs and code smells
- Enforces best practices
- Checks for unused code
- Examples: ESLint, Pylint, Ruff, Detekt
Formatting (code style):
- Consistent spacing and indentation
- Line breaks and wrapping
- Quote style
- Examples: Prettier, Black, ktlint format
Both are important, but different:
- Linting catches logic issues
- Formatting ensures visual consistency
Output Confirmation
Before proceeding to next quality gate:
✓ Linter executed successfully
✓ No errors in modified files (MANDATORY)
✓ Warnings in modified files: fixed OR justified with inline comments
✓ Code formatting is consistent in modified files
✓ Pre-existing errors in other files: tracked but not blocking
✓ Scope clearly documented (which files touched)Integration with CI/CD
Lint checks should also run in CI to catch issues:
GitHub Actions Example:
- name: Run linter
run: npm run lint
- name: Check formatting
run: npm run format:checkCRITICAL RULE
NEVER claim work is complete with unfixed lint errors IN FILES YOU MODIFY.
For files in your changeset:
- Auto-fix what you can (
npm run lint:fix) - Manually fix remaining errors (ALL of them)
- Fix warnings OR add inline justification (e.g.,
// eslint-disable-next-line no-console -- Debug logging required) - Re-run until clean
For files outside your changeset:
- Don't auto-fix (would change too many files)
- Create tracking issues if there are widespread problems
- Don't block on these
The "unless documented" escape hatch does NOT apply to files you modify.
How to verify your scope:
# Your changed files
git diff --name-only main
# Lint only those
npm run lint -- $(git diff --name-only main | grep '\.ts$')
# MUST be clean for these filesBut always end with a clean lint report for files you modify.
Performance Tips
Lint only changed files (for large codebases):
Git diff approach:
# Get changed files
git diff --name-only --diff-filter=ACMRTUXB main | grep '\.ts$'
# Lint only those
npm run lint -- $(git diff --name-only --diff-filter=ACMRTUXB main | grep '\.ts$')Ruff with git:
ruff check $(git diff --name-only --diff-filter=ACMRTUXB main | grep '\.py$')This is especially useful for:
- Large legacy codebases
- Pre-commit hooks
- Faster CI feedback
Next Steps
After linting passes:
- Proceed to
security-checkskill - Final step before marking work complete