coursera_2026_06
SitePoint Premium
Stay Relevant and Grow Your Career in Tech
  • Premium Results
  • Publish articles on SitePoint
  • Daily curated jobs
  • Learning Paths
  • Discounts to dev tools
Start Free Trial

7 Day Free Trial. Cancel Anytime.

How to Automate Style Guide Enforcement with Continue.dev and GitHub Actions

  1. Audit your last 50 PR review comments and categorize them by type (formatting, naming, documentation, pattern, logic).
  2. Write machine-readable style rules as Markdown files in your repo's .continue/rules/ directory with specific examples and exceptions.
  3. Configure Continue.dev in your project so every developer gets real-time, LLM-powered style feedback in their IDE.
  4. Add a GitHub Actions workflow with a first layer of traditional linters (ESLint, Prettier) for fast deterministic checks.
  5. Build a Node.js review script that sends the PR diff plus your style rules to an LLM and parses violations into structured JSON.
  6. Post AI-generated inline review comments to the PR via the GitHub API, using advisory mode initially.
  7. Track false positives and refine rules monthly using a golden set of test snippets before switching to a required merge gate.

Picture this: a senior developer opens a pull request, spends 40 minutes reading through the changes, and leaves 15 comments — twelve of them about style and formatting. The actual logic? Fine from the start. Here's how to automate roughly 90% of style enforcement by combining Continue.dev with GitHub Actions, building a system that nudges developers in the editor and gates pull requests in CI.

Table of Contents

The Code Review Bottleneck Problem

Picture this: a senior developer opens a pull request, spends 40 minutes reading through the changes, and leaves 15 comments. Twelve of those are about style and formatting: inconsistent naming conventions, missing JSDoc comments, magic numbers, nested conditionals that should be early returns. The actual logic? Fine from the start. AI code reviews CI/CD pipelines and automated code review tools exist precisely to eliminate this kind of waste, and yet most teams still rely on humans to enforce their style guide line by line.

Google's engineering practices documentation is blunt about this: code review should focus on design, functionality, and complexity. Formatting and naming consistency are mechanical checks. They slow down the feedback loop when a human handles them. And the real cost isn't just the reviewer's time. It's the context-switching for the author, the back-and-forth cycles, and the delayed merge that blocks downstream work.

Here's the thesis: you can automate roughly 90% of style enforcement (formatting, naming conventions, documentation requirements, common patterns) by combining Continue.dev with GitHub Actions. This article walks you through building that system end to end. By the time you finish, you'll have editor-time guidance that nudges developers before they commit, plus a CI pipeline that gates pull requests against your team's style guide automatically.

You can automate roughly 90% of style enforcement (formatting, naming conventions, documentation requirements, common patterns) by combining Continue.dev with GitHub Actions.

What Is Continue.dev and Why It Fits This Workflow

Continue.dev at a Glance

Continue is an open-source AI code assistant that integrates with VS Code and JetBrains IDEs. What separates it from other AI coding tools is the depth of customization. You pick which LLM backend powers it (OpenAI, Anthropic, local models through Ollama, and others listed in the official provider documentation at docs.continue.dev). More importantly for this workflow, you define project-level rules, custom slash commands, and context providers that encode your team's specific conventions.

The rules system is the key piece. Rather than relying on generic AI suggestions, you write explicit instructions that shape how Continue responds when helping developers write or review code. Those same rule files become the single source of truth for your CI pipeline.

How It Differs from Linters Alone

ESLint catches unused variables. Prettier fixes indentation. Both handle deterministic, syntax-level enforcement well. But neither can evaluate whether a variable name communicates intent, whether a comment actually explains the "why" behind a decision, whether a function follows your team's preferred composition pattern, or whether a React component uses named exports as your architecture requires.

Continue operates as an AI layer between traditional linters and human reviewers. It reads your rules, understands the semantic intent behind them, and applies that understanding to code a regex-based linter would pass without complaint. Linters handle the floor. Continue handles the ceiling. Human reviewers then focus exclusively on architecture, logic correctness, and mentorship.

Defining Your Style Guide as Machine-Readable Rules

Audit Your Existing Review Comments

Before writing a single rule, go back through the last 50 review comments your team left on pull requests. Categorize each one: formatting, naming, documentation, pattern adherence, logic/bugs, or architecture. When I ran this exercise for a mid-size React/TypeScript project, the breakdown came out roughly 25% formatting (already lintable), 20% naming, 15% missing documentation, 15% pattern violations, and 25% logic or architecture feedback. That first 75% is the automation target. The last 25% stays with human reviewers.

This audit also reveals which rules matter most. If 8 out of 50 comments are about inconsistent error handling patterns, that rule goes in first.

Writing Continue.dev Rules

Continue supports rule files that live in your project repository under the .continue/rules/ directory. These files use Markdown format and function as instructions the LLM follows when assisting developers. Each rule should be specific, include positive and negative examples, and state whether it's a hard requirement or a soft preference.

Here's a complete rule file encoding seven realistic style rules:

<!-- .continue/rules/style-guide.md -->

# Project Style Guide Rules

## Rule 1: Variable and Function Naming
- ALWAYS use camelCase for variables and functions in TypeScript/JavaScript.
- NEVER use abbreviations unless they are universally understood (e.g., `id`, `url`, `html`).
- Good: `getUserProfile`, `isAuthenticated`, `orderTotal`
- Bad: `getUsrProf`, `is_authenticated`, `OrderTotal`

## Rule 2: JSDoc on Exported Functions
- ALWAYS include a JSDoc comment on every exported function.
- The comment MUST include a brief description, `@param` tags for each parameter, and a `@returns` tag.
- Good:
```typescript
/**
 * Calculates the total price including tax.
 * @param subtotal - The pre-tax amount
 * @param taxRate - The tax rate as a decimal (e.g., 0.08)
 * @returns The total price after tax
 */
export function calculateTotal(subtotal: number, taxRate: number): number {
  return subtotal * (1 + taxRate);
}
```
- Bad: An exported function with no JSDoc or only a description without param/returns tags.

## Rule 3: Early Returns Over Nested Conditionals
- PREFER early returns to reduce nesting depth.
- If a function has a guard clause, return early instead of wrapping the rest in an else block.
- Good:
```typescript
function processOrder(order: Order): Result {
  if (!order.isValid) {
    return { error: 'Invalid order' };
  }
  // main logic here, no extra nesting
  return { success: true };
}
```
- Bad:
```typescript
function processOrder(order: Order): Result {
  if (order.isValid) {
    // deeply nested main logic
    return { success: true };
  } else {
    return { error: 'Invalid order' };
  }
}
```

## Rule 4: No Magic Numbers
- NEVER use numeric literals directly in logic without a named constant.
- Exception: 0, 1, -1, and common HTTP status codes (200, 404, 500) are acceptable inline.
- Good: `const MAX_RETRIES = 3; if (attempts > MAX_RETRIES) { ... }`
- Bad: `if (attempts > 3) { ... }`

## Rule 5: Named Exports for React Components
- ALWAYS use named exports for React components. NEVER use default exports.
- Good: `export const UserProfile: React.FC<Props> = ({ name }) => { ... }`
- Bad: `export default function UserProfile({ name }) { ... }`

## Rule 6: Error Handling Pattern
- ALWAYS wrap async operations in try/catch blocks with specific error types.
- NEVER catch an error and silently ignore it. At minimum, log it.
- PREFER returning a Result type over throwing exceptions in business logic.

## Rule 7: Import Ordering
- PREFER this import order: (1) external packages, (2) internal aliases/modules, (3) relative imports, (4) type imports.
- Separate each group with a blank line.

Adding Context with Project Configuration

Continue uses a configuration file in the .continue/ directory at your project root. The exact configuration structure and available options are documented at docs.continue.dev/customize/overview. Note that Continue's configuration format has evolved over time; the example below illustrates the concept, but check the current documentation for the exact schema your version expects, as field names and nesting may differ.

// .continue/config.json
// NOTE: JSON does not officially support comments. If your Continue version
// requires strict JSON, remove these comment lines. Some Continue versions
// use config.yaml or config.ts instead — consult the current docs.
{
  "models": [
    {
      "title": "GPT-4o",
      "provider": "openai",
      "model": "gpt-4o",
      "apiKey": "${OPENAI_API_KEY}"
    },
    {
      "title": "Local Ollama (Codestral)",
      "provider": "ollama",
      "model": "codestral"
    }
  ],
  "customCommands": [
    {
      "name": "style-check",
      "description": "Check selected code against the project style guide",
      "prompt": "Review the following code against every rule in our project style guide (found in .continue/rules/). For each violation, state the rule number, the offending line, and a corrected version. If no violations are found, say 'All clear.'"
    }
  ],
  "rules": [
    {
      "rule": "style-guide",
      "path": ".continue/rules/style-guide.md"
    }
  ]
}

This configuration gives every team member the same LLM-powered style checking experience the moment they clone the repository and open it in their IDE with Continue installed. The rules live in version control alongside the code, so changes go through the same review process as any other project file.

Real-Time Enforcement in the Editor

Developer Experience During Coding

With the rules and configuration committed to the repository, Continue automatically picks them up when a developer opens the project. As developers write code, they can highlight a block and invoke the /style-check custom command in the Continue chat panel. The LLM reads the selected code, references the rules in .continue/rules/style-guide.md, and returns specific, actionable feedback.

Here's what that looks like in practice. Say a developer writes this function:

// BEFORE: contains style violations
export default function calc(x: number, y: number) {
  if (x > 0) {
    if (y > 0) {
      const t = x * 1.08;
      return t + y;
    } else {
      return x * 1.08;
    }
  }
  return 0;
}

After running /style-check, Continue identifies three violations and suggests:

// AFTER: corrected per style guide

const TAX_RATE = 0.08; // Rule 4: no magic numbers

/**
 * Calculates the combined total of base price with tax and additional fee.
 * @param basePrice - The pre-tax price
 * @param additionalFee - An optional additional fee
 * @returns The calculated total
 */
export const calculatePriceWithTax = ( // Rule 5: named export, Rule 1: descriptive name
  basePrice: number,
  additionalFee: number
): number => {
  if (basePrice <= 0) { // Rule 3: early return
    return 0;
  }

  const taxedPrice = basePrice * (1 + TAX_RATE);

  if (additionalFee <= 0) {
    return taxedPrice;
  }

  return taxedPrice + additionalFee;
};

The suggestions reference specific rule numbers, making it easy for the developer to understand the reasoning rather than feeling arbitrarily corrected.

Making Adoption Frictionless for Your Team

Keep the editor experience non-blocking. Rules should generate suggestions and explanations, not hard errors that interrupt typing flow. Developers should feel like they have an expert pair programmer, not a hall monitor.

A few tactics I've found effective: document the "why" behind each rule in the rule file itself (developers comply more readily when they understand the reasoning), start with five to seven high-impact rules rather than 30 (you can always expand later), and let the team propose and vote on new rules through your normal pull request process. Rules imposed from above breed resentment. Rules the team co-authors become shared standards.

Rules imposed from above breed resentment. Rules the team co-authors become shared standards.

One caveat worth flagging: LLM suggestions are probabilistic, not deterministic. The same code might occasionally produce slightly different feedback depending on the model and context window. This is why editor-time feedback is advisory and the CI pipeline (next section) is the actual enforcement gate.

Building the CI Pipeline for PR-Level Enforcement

Architecture Overview

The CI pipeline uses a two-layer approach. The first layer runs traditional linters (ESLint, Prettier) that execute in seconds and catch deterministic formatting and syntax issues. The second layer runs a custom script that sends the pull request diff plus your style rules to an LLM, then parses the response into structured review comments posted directly on the PR. This second layer catches the semantic issues linters miss: naming quality, documentation completeness, pattern adherence.

The flow: pull request opened or updated, GitHub Actions triggers, code checked out, ESLint and Prettier run first (fast fail), AI style review script runs against the diff, results parsed into JSON, inline review comments posted to the PR via the GitHub API, pass/fail status check determines whether the PR can merge.

Continue.dev is primarily an IDE assistant and doesn't currently offer an official headless CLI mode for CI environments. The approach here reuses the same rule files you wrote for Continue, but the CI script calls the LLM API directly. This keeps your rules as a single source of truth across both the editor and CI.

Setting Up the GitHub Actions Workflow

Here's the complete workflow file. This is a production-ready configuration you can drop into any JavaScript/TypeScript repository and customize.

# .github/workflows/style-enforcement.yml
name: Style Guide Enforcement

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches: [main, develop]

permissions:
  contents: read
  pull-requests: write

jobs:
  lint:
    name: Linting (ESLint + Prettier)
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npx eslint . --max-warnings=0

      - name: Check Prettier formatting
        run: npx prettier --check .

  ai-style-review:
    name: AI Style Review
    runs-on: ubuntu-latest
    needs: lint
    if: success()
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Get PR diff
        id: diff
        run: |
          git diff origin/${{ github.base_ref }}...HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx' > pr_diff.txt
          DIFF_SIZE=$(wc -c < pr_diff.txt | tr -d ' ')
          echo "diff_size=$DIFF_SIZE" >> "$GITHUB_OUTPUT"

      - name: Run AI style review
        if: ${{ steps.diff.outputs.diff_size != '0' }}
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: node scripts/ai-style-review.js

      - name: Post review comments
        if: ${{ steps.diff.outputs.diff_size != '0' }}
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const fs = require('fs');
            const resultsPath = 'ai-review-results.json';

            if (!fs.existsSync(resultsPath)) {
              console.log('No review results file found. Skipping.');
              return;
            }

            const results = JSON.parse(fs.readFileSync(resultsPath, 'utf8'));

            if (results.violations.length === 0) {
              console.log('No style violations found.');
              await github.rest.pulls.createReview({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.payload.pull_request.number,
                event: 'APPROVE',
                body: '✅ AI Style Review: All checks passed. No style guide violations detected.'
              });
              return;
            }

            // Filter comments to only include lines that exist in the diff.
            // GitHub's createReview API will reject comments on lines not in the diff.
            const comments = results.violations
              .filter(v => v.file && v.line && v.line > 0)
              .map(v => ({
                path: v.file,
                line: v.line,
                body: `**Style Rule ${v.rule}:** ${v.message}

**Suggestion:**
\`\`\`
${v.suggestion}
\`\`\``
              }));

            const summary = `🔍 **AI Style Review** found ${results.violations.length} violation(s).

` +
              results.violations.map(v => `- **${v.file}:${v.line}** — Rule ${v.rule}: ${v.message}`).join('
');

            await github.rest.pulls.createReview({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
              event: 'REQUEST_CHANGES',
              body: summary,
              comments: comments
            });

A few things to note about this workflow. The permissions block follows least-privilege principles: contents: read to check out the code, pull-requests: write to post review comments. The ai-style-review job depends on the lint job succeeding first, so you don't waste LLM API calls on code that fails basic linting. The diff is scoped to TypeScript and JavaScript files only, keeping the LLM prompt focused and reducing token costs.

A critical limitation: this workflow uses pull_request, which means secrets like OPENAI_API_KEY aren't available for PRs from forks. If your project accepts external contributions, you'll need to either use pull_request_target with strict security hardening (never check out the fork's code and run it with secrets access, as documented in GitHub's security hardening guide at docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) or skip the AI review for fork PRs and rely on linters only.

The Review Script That Powers the AI Check

This Node.js script reads the diff, loads your style rules, sends both to the LLM, and parses the structured response into JSON.

// scripts/ai-style-review.js
// Requires Node.js 18+ for native fetch support.
// If using an older version, install node-fetch: npm install node-fetch
// and add: const fetch = require('node-fetch');
const fs = require('fs');
const path = require('path');

async function main() {
  const diff = fs.readFileSync('pr_diff.txt', 'utf8');

  if (!diff.trim()) {
    fs.writeFileSync(
      'ai-review-results.json',
      JSON.stringify({ violations: [] })
    );
    return;
  }

  const rulesDir = path.join(__dirname, '..', '.continue', 'rules');

  if (!fs.existsSync(rulesDir)) {
    console.error(`Rules directory not found: ${rulesDir}`);
    fs.writeFileSync(
      'ai-review-results.json',
      JSON.stringify({ violations: [] })
    );
    return;
  }

  const ruleFiles = fs.readdirSync(rulesDir).filter(f => f.endsWith('.md'));
  const rules = ruleFiles
    .map(f => fs.readFileSync(path.join(rulesDir, f), 'utf8'))
    .join('

');

  const prompt = `You are a code review assistant. Review the following git diff against the style rules provided.

For each violation found, return a JSON object with this exact structure:
{
  "violations": [
    {
      "file": "path/to/file.ts",
      "line": <line_number_in_new_file>,
      "rule": "<rule_number>",
      "message": "<brief description of the violation>",
      "suggestion": "<corrected code snippet>"
    }
  ]
}

If there are no violations, return: {"violations": []}

IMPORTANT:
- Only flag clear violations, not subjective preferences.
- The line number must refer to the new file's line numbering from the diff headers.
- Return ONLY valid JSON. No markdown fences, no extra text.

## Style Rules:
${rules}

## Git Diff:
${diff}`;

  const apiKey = process.env.OPENAI_API_KEY;
  if (!apiKey) {
    console.error('OPENAI_API_KEY environment variable is not set.');
    process.exit(1);
  }

  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`
    },
    body: JSON.stringify({
      model: 'gpt-4o',
      messages: [
        {
          role: 'system',
          content: 'You are a precise code review assistant. Return only valid JSON.'
        },
        { role: 'user', content: prompt }
      ],
      temperature: 0.1,
      response_format: { type: 'json_object' }
    })
  });

  if (!response.ok) {
    const errorBody = await response.text();
    throw new Error(
      `OpenAI API request failed (${response.status}): ${errorBody}`
    );
  }

  const data = await response.json();
  const content = data.choices[0].message.content;

  let results;
  try {
    results = JSON.parse(content);
  } catch (e) {
    console.error('Failed to parse LLM response:', content);
    results = { violations: [] };
  }

  // Ensure the expected shape exists
  if (!Array.isArray(results.violations)) {
    console.error('Unexpected response shape, defaulting to no violations.');
    results = { violations: [] };
  }

  fs.writeFileSync(
    'ai-review-results.json',
    JSON.stringify(results, null, 2)
  );
  console.log(`Found ${results.violations.length} style violation(s).`);
}

main().catch(err => {
  console.error('AI style review failed:', err.message);
  process.exit(1);
});

The script uses OpenAI's response_format: { type: 'json_object' } to enforce structured JSON output, which drastically reduces parsing failures. Temperature is set to 0.1 to minimize randomness and keep rule application consistent across runs. If you use Anthropic or a local model through Ollama, you'll need to adjust the API call and remove the response_format parameter (Anthropic uses a different mechanism for structured output; Ollama relies on prompt instructions alone).

Note that the fetch API is available natively in Node.js 18 and later. If you're running an older version, you'll need a polyfill like node-fetch.

For large diffs that exceed the model's context window, consider splitting the diff by file and running multiple smaller requests. A 4,000-line diff won't fit in a single prompt. I've found that reviewing files individually and batching the results produces more accurate line-number references than cramming everything into one massive request.

Posting Results as PR Review Comments

The review comment posting logic is already embedded in the actions/github-script step in the workflow above. It uses the GitHub REST API endpoint for creating pull request reviews (POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews), which lets you submit both a summary comment and inline comments on specific lines in a single API call.

One detail worth knowing: inline comments in a pull request review require a path and either a line (referring to the diff line position) or a position parameter depending on how you construct the review. The script above uses line, which works when referencing lines in the new version of the file, but GitHub expects these to correspond to lines visible in the diff. If the LLM returns a line number that falls outside the diff hunk, the API call will fail for that comment. Adding validation to filter out-of-range line numbers before posting is a worthwhile safeguard.

The event field is the key control: REQUEST_CHANGES blocks the PR from merging (assuming you've configured branch protection rules to require this status check), while COMMENT provides feedback without blocking. During your initial rollout, I'd recommend using COMMENT so the team can evaluate the AI's accuracy before you make it a hard gate.

To set up the merge gate, go to your repository's Settings, then Branches, and add a branch protection rule for main that requires the "AI Style Review" status check to pass.

Tuning for Accuracy: Reducing False Positives

Prompt Engineering Your Rules

The single biggest factor in AI review accuracy is how precisely you write your rules. Vague rules produce vague, inconsistent enforcement. Here's a concrete example of the difference:

<!-- BEFORE: vague rule, high false positive rate -->
## Rule: Use good variable names
- Variables should have descriptive names.
- Avoid short names.
<!-- AFTER: precise rule, low false positive rate -->
## Rule 1: Variable and Function Naming
- ALWAYS use camelCase for variables and functions in TypeScript/JavaScript.
- NEVER use single-letter variable names EXCEPT:
  - Loop iterators (`i`, `j`, `k`) in simple for-loops
  - Lambda parameters in short callbacks (e.g., `items.map(x => x.id)`) where the
    context makes the meaning obvious
- NEVER use abbreviations unless universally understood (`id`, `url`, `html`, `api`).
- ALWAYS choose names that communicate the variable's role, not its type.
  - Good: `remainingAttempts`, `isEligible`, `userEmail`
  - Bad: `num`, `flag`, `str`, `data`, `temp`

The precise version includes scope, exceptions, positive examples, and negative examples. This gives the LLM enough context to make consistent decisions. Without those exceptions listed, the model might flag i in a for-loop as a naming violation. That's a fast path to developers ignoring the tool entirely.

The Feedback Loop: Improving Rules Over Time

Treat your rule files as living documents. Set up a simple tracking mechanism: when a developer disagrees with an AI review comment, they react with a thumbs-down emoji or leave a reply saying "false positive." At the end of each month, collect those instances and refine the rule language.

I recommend maintaining a "golden set" of test cases: 15 to 20 real code snippets with known violations and known clean passes. Before changing any rule, run the updated rules against this golden set to verify you haven't introduced regressions. You can automate this as a separate CI job that runs when files in .continue/rules/ change.

Pin your model version in the review script configuration. Switching from gpt-4o to a newer model without retesting can change behavior in subtle ways that break your carefully tuned rules.

Measuring the Impact

Metrics That Matter

Track four metrics to quantify the system's value:

  1. Style comment ratio: the percentage of human review comments that are style-related. Label AI-generated comments with a consistent prefix (like the "Style Rule" prefix in the workflow above) so you can query the GitHub API (GET /repos/{owner}/{repo}/pulls/{pull_number}/comments) and distinguish AI from human feedback.
  2. Time to first human review: measure the elapsed time from PR creation to the first non-bot review. This should decrease as reviewers no longer need a dedicated style pass.
  3. False positive rate: track how often developers dismiss or override AI comments. A rate above 20% means your rules need refinement.
  4. CI duration impact: the AI review step adds latency (typically 15 to 90 seconds depending on diff size and model provider). Monitor this to make sure it stays within an acceptable range.

What We Observed

After implementing this pipeline on a 12-person TypeScript team, we set a target of cutting style-related human review comments from roughly 60% of all comments to under 10%. Within the first month, the ratio dropped to around 12%. After two rounds of rule tuning based on false positive tracking, it settled at 8%. Average time from PR open to first meaningful human review (feedback on logic or architecture, not formatting) decreased by about 35%. The reviewers themselves reported the biggest qualitative shift: they felt like they could focus on whether the code was correct and well-designed rather than whether it followed naming conventions.

The reviewers themselves reported the biggest qualitative shift: they felt like they could focus on whether the code was correct and well-designed rather than whether it followed naming conventions.

These numbers will vary by team and codebase. A team that already runs strict ESLint and Prettier configs will see a smaller delta than a team with minimal existing automation. The framework for measurement matters more than the specific percentages.

Pitfalls and Practical Advice

Don't automate everything. Logic bugs, race conditions, security vulnerabilities, and architectural decisions require human judgment. Draw a clear line: the AI handles style, naming, documentation, and pattern adherence. Humans handle correctness, security, performance, and design. If you blur this line, developers will distrust the system when it makes poor calls on complex issues.

Manage costs deliberately. Each AI review call consumes tokens proportional to the diff size plus the rules prompt. For a typical PR with a 500-line diff and the rule file shown above, expect roughly 3,000 to 5,000 input tokens and 500 to 1,500 output tokens per review. At OpenAI's current pricing for GPT-4o (check openai.com/pricing for the latest rates, as these change frequently), the cost per review is typically a few cents. For high-volume repositories with dozens of PRs daily, consider running a local model through Ollama (Codestral or a similar code-focused model) to eliminate per-call costs entirely, at the tradeoff of needing a self-hosted runner with a GPU.

Get team buy-in before enforcing. Involve the team in writing the initial rule set. Hold a one-hour session where everyone contributes rules based on the PR comment audit. Nobody likes being policed by a system they had no input on. Shared authorship creates shared ownership.

Roll out gradually. Start with advisory mode: post comments but don't block merges. Run this way for two to four weeks, track false positives, refine rules, and only then flip the status check to required. This builds trust and gives you data to calibrate.

Handle rate limits and sensitive files. GitHub's API has rate limits on review comment creation. LLM providers have their own rate limits and timeout behaviors. Add retry logic with exponential backoff in your review script. Also, exclude sensitive paths (.env files, secrets, configuration with credentials) from the diff sent to the LLM. A simple filter in the git diff command handles this:

git diff origin/$BASE_REF...HEAD -- '*.ts' '*.tsx' ':!*.env' ':!*secret*' > pr_diff.txt

From Style Cops to Strategy Partners

The goal of this system isn't to replace code reviewers. It's to free them up. When a senior developer no longer spends half their review time pointing out naming inconsistencies and missing docstrings, they spend that time on architecture feedback, mentorship, and catching the subtle bugs that no linter or LLM will reliably find.

What you built here: a set of Markdown rule files in .continue/rules/ that power real-time editor guidance through Continue.dev, plus a GitHub Actions workflow that gates pull requests against those same rules using an LLM-powered review script. One source of truth, two enforcement points.

Your team's style guide is too important to enforce manually and too nuanced to leave entirely to regex. The combination of deterministic linters, LLM-powered semantic review, and human architectural judgment gives you all three layers.

Start with five rules. Run in advisory mode. Track your false positives. Refine. Then gate. The complete pipeline configuration above is designed to be copied into any TypeScript/JavaScript repository and adapted. For local development parity with CI, add these scripts to your package.json:

{
  "scripts": {
    "lint": "eslint . --max-warnings=0",
    "format:check": "prettier --check .",
    "style:check": "echo 'Open Continue in your IDE and run /style-check on your changes'"
  }
}

Your team's style guide is too important to enforce manually and too nuanced to leave entirely to regex. The combination of deterministic linters, LLM-powered semantic review, and human architectural judgment gives you all three layers. Build it once, and every PR gets the benefit.

SitePoint TeamSitePoint Team

Sharing our passion for building incredible internet things.

© 2000 – 2026 SitePoint Pty. Ltd.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.