Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Lintellect

.NETLicense: MITCIAPI VersionCLI Version

AI-powered code review assistant that enhances pull request analysis with intelligent insights and automated suggestions.

Lintellect reviews your pull requests with AI. A small CLI runs in your CI pipeline and sends the PR context to a self-hosted Lintellect API, which calls Claude or Azure AI Foundry and posts the review back to GitHub or Azure DevOps:

Your CI pipeline (Lintellect CLI)
→ your Lintellect API (self-hosted, one instance per organization)
→ AI analysis (Claude / Azure AI Foundry)
→ review comment, inline suggestions and description summary on the PR

The review is work-item aware (linked work items / issues, including acceptance criteria and repro steps, are fed into the analysis) and respects repo-level instruction files (copilot-instructions.md, AGENTS.md, CLAUDE.md).

Table of Contents

Quick Start

Setting up Lintellect for your organization takes three steps: run the API, add the CLI to your pipeline, open a pull request.

1. Run the API

The API is a single container plus PostgreSQL. Database migrations run automatically on startup.

# Build the image from this repository
docker build -t lintellect-api -f src/Lintellect.Api/Dockerfile .# Start PostgreSQL and the API
docker network create lintellect
docker run -d --name lintellect-db --network lintellect \
-e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=lintellect postgres:17
docker run -d --name lintellect-api --network lintellect -p 7000:8080 \
-e ApiKey="choose-a-secure-api-key" \
-e ConnectionStrings__postgresdb="Host=lintellect-db;Database=lintellect;Username=postgres;Password=change-me" \
-e CLAUDE_API_KEY="sk-ant-..." \
-e AZURE_DEVOPS_PAT="your-pat" \
-e AZURE_DEVOPS_ORG_URL="https://dev.azure.com/your-org" \
lintellect-api

Minimum configuration:

SettingPurpose
ApiKeyThe key your pipelines use to call this API
ConnectionStrings__postgresdbPostgreSQL connection string
CLAUDE_API_KEYorAZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT_NAMEAt least one AI provider
AZURE_DEVOPS_PAT + AZURE_DEVOPS_ORG_URLand/orGITHUB_TOKENCredentials for the Git provider(s) that host your PRs

The Azure DevOps PAT needs Code (Read & Write), Pull Request (Read & Write) and Work Items (Read) scopes; the GitHub token needs the repo scope. Everything else has sensible defaults — see Configuration.

2. Add the CLI to your pipeline

The pipeline only needs the API URL and key — Git provider credentials stay on the API server.

Azure DevOps (trigger via build validation policy on your target branch):

trigger: nonepool:
vmImage: "ubuntu-latest"steps:
- task: UseDotNet@2inputs: { packageType: "sdk", version: "10.0.x" }
- script: dotnet tool install --global Lintellect.ClidisplayName: "Install Lintellect CLI"
- script: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary displayName: "Analyze PR" env: LINTELLECT_API_URL: $(LINTELLECT_API_URL) LINTELLECT_API_KEY: $(LINTELLECT_API_KEY)

GitHub Actions:

name: PR Analysison:
pull_request:
types: [opened, synchronize, reopened]jobs:
analyze:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4with:
dotnet-version: "10.0.x"
- run: dotnet tool install --global Lintellect.Cli
- run: > Lintellect analyze --language "csharp" --enable-summary-comment --enable-inline-suggestions --enable-description-summary env: LINTELLECT_API_URL: ${{ secrets.LINTELLECT_API_URL }} LINTELLECT_API_KEY: ${{ secrets.LINTELLECT_API_KEY }}

3. Open a pull request

On the next PR you'll see a "🔄 Lintellect analysis is in progress" placeholder, which is replaced by the review once the analysis completes — typically a detailed review comment, inline code suggestions and a summary appended to the PR description.

Usage

CLI options

# Full analysis (summary comment, inline suggestions, description summary; Semgrep runs by default)
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-description-summary
# Exclude paths, add Azure DevOps code-owner assignment
Lintellect analyze \
--language "csharp" \
--exclude "**/bin/**" \
--exclude "**/obj/**" \
--exclude "**/Generated/**" \
--enable-summary-comment \
--enable-inline-suggestions \
--enable-azure-devops-code-owners
# Opt out of individual features
Lintellect analyze \
--language "csharp" \
--enable-summary-comment \
--enable-semgrep false \
--enable-static-analysis false \
--enable-work-item-context false

Supported languages: csharp (with Roslyn static analysis), javascript, typescript, python, java and others (AI review of the diff; Semgrep where applicable).

What gets posted

  • Review comment — the detailed analysis, posted into (and resolving) the placeholder comment. It ends with a context footer showing what the review actually saw: linked work items, whether custom instructions were found, and whether the diff was full or incremental.
  • Description summary — appended to the PR description.
  • Inline suggestions — one suggestion comment per finding, capped per file and globally.
  • Code owners (Azure DevOps, opt-in) — reviewers added from CODEOWNERS rules matching the changed files.

Posting is fault-tolerant per step: if the PR becomes read-only mid-analysis, the remaining steps still run and the job still completes. If a job fails outright, the placeholder comment is updated with a failure note instead of staying "in progress" forever.

Work-item context

Linked work items / issues are used as PR context by default (--enable-work-item-context false to opt out):

  • Azure DevOps: linked work items are resolved server-side via the WIT REST API. The context includes description, acceptance criteria and repro steps (configurable via Analysis:WorkItemBodyFields), so the review can flag work-item scope the PR doesn't cover.
  • GitHub: the PR body is parsed for Closes/Fixes/Resolves #N keywords.

Custom review instructions

Lintellect looks for a repo-level instruction file on the pull request's source branch and injects it into every review prompt. The first match wins:

/.github/copilot-instructions.md (+ uppercase and /.copilot, /docs, / variants)
/AGENTS.md
/CLAUDE.md (+ /CLAUDE, /.claude/CLAUDE.md, /.github/CLAUDE.md)
/.github/AGENTS.md
/docs/AGENTS.md

Use it for project conventions the reviewer should enforce ("we ban AutoMapper", "public APIs need XML docs"). No file → no custom instructions; analysis proceeds normally.

Re-triggered analyses

The API deduplicates analysis per pull request:

  • First trigger → full analysis (summary comment, description summary, inline suggestions).
  • Re-trigger with new commits → incremental analysis that posts inline suggestions only, computed from the diff between the previously analyzed commit and the new source head (falls back to the full PR diff if that range can't be resolved, e.g. after a force-push).
  • Re-trigger without new commits, or while a previous job is still running → no new job; the existing job's id is returned.
  • A failed job never blocks re-analysis — the next trigger runs a full analysis again.

Configuration

All settings can be provided via appsettings.json or environment variables (Section__Key form); the env vars shown in Quick Start are dedicated fallbacks for the most common secrets.

Required Settings

{
"ConnectionStrings": {
"postgresdb": "Host=localhost;Database=lintellect;Username=postgres;Password=password"
},
"ApiKey": "your-secure-api-key"
}

AI Analyzer Settings

Claude Analyzer

{
"ClaudeAnalyzer": {
"ApiKey": "sk-ant-api03-...",
"Model": "claude-sonnet-4-5-20250929",
"MaxTokens": 40960,
"Temperature": 0.5
}
}

Azure AI Foundry

{
"AzureOpenAIAnalyzer": {
"ApiKey": "your-azure-ai-key",
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o"
}
}

Git Provider Settings

Git provider credentials are configured on the API server and used for all analysis requests.

{
"GitCredentials": {
"GitHub": {
"Token": "ghp_..."
},
"AzureDevOps": {
"Pat": "your-pat-token",
"OrgUrl": "https://dev.azure.com/your-org"
}
}
}

Or via environment variables: GITHUB_TOKEN, AZURE_DEVOPS_PAT, AZURE_DEVOPS_ORG_URL.

Analysis Settings

{
"Analysis": {
"SynchronousAnalysis": false,
"WorkItemBodyFields": [
"System.Description",
"Microsoft.VSTS.Common.AcceptanceCriteria",
"Microsoft.VSTS.TCM.ReproSteps"
]
}
}
  • SynchronousAnalysis (default false): when false, Claude analyses run through the Message Batches API (~50% cheaper, but no completion-time guarantee — a busy batch queue can delay a review by many minutes). Set to true (env: Analysis__SynchronousAnalysis) to run direct parallel API calls with predictable latency at full token price.
  • WorkItemBodyFields: the Azure DevOps work item fields composed (in order, labeled) into the work-item context. Fields a work item type doesn't have are skipped, so the defaults cover both stories/PBIs (acceptance criteria) and bugs (repro steps) on the standard Agile/Scrum/CMMI process templates. Override for custom process templates.

API Reference

Authentication

All api/analysis/* endpoints require an API key, passed via the Api-Key request header:

Api-Key: your-api-key

Requests with a missing or incorrect key receive 401 Unauthorized.

Endpoints

MethodRouteDescription
POST/api/analysis/analyzeSubmit a new analysis job for background processing.
GET/api/analysis/status/{jobId}Get the status and results of an analysis job.
GET/api/analysis/historyList analysis jobs (supports optional filtering).
DELETE/api/analysis/historyDelete analysis history (all, or a single jobId).
POST/api/azuredevops/webhooks/pr/commented-onAzure DevOps PR-comment webhook.
POST/api/azuredevops/webhooks/pr/updatedAzure DevOps PR-updated webhook.
GET/health, /health/readyLiveness / readiness health checks (no auth).

In Development, an interactive OpenAPI reference (Scalar) is served at /scalar/v1, backed by the OpenAPI document at /openapi/v1.json.

The CLI submits jobs to POST /api/analysis/analyze; the API persists the job, queues it, and processes it asynchronously in the background. GET /api/analysis/status/{jobId} returns the response below once processing completes.

Response Format

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"startedAt": "2024-01-15T10:30:00Z",
"completedAt": "2024-01-15T10:35:00Z",
"summary": "Analysis completed successfully",
"detailedAnalysis": "Detailed analysis results...",
"analyzerUsed": "Claude"
}

Development & Contributing

Everything below is for working on Lintellect itself, not for using it.

Local development

git clone https://github.com/DawnDevelop/Lintellect.git
cd lintellect
# Recommended: .NET Aspire starts PostgreSQL + API and wires everything upcd src/Lintellect.AppHost
dotnet run
# Aspire dashboard: https://localhost:15000 | API: https://localhost:7000

Prerequisites: .NET 10 SDK, Docker. Configure API keys and Git credentials in src/Lintellect.AppHost/appsettings.json (user secrets are also wired up).

Manual setup without Aspire:

docker run --name postgres-dev -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17
cd src/Lintellect.Api
dotnet ef database update
dotnet run

Testing and building

dotnet test# all tests
dotnet test tests/Lintellect.Api.FunctionalTests/ # integration tests (requires Docker)
dotnet build
dotnet publish src/Lintellect.Api/Lintellect.Api.csproj -c Release -o ./publish

Architecture

Clean Architecture in a single API project:

  • Domain: entities (AnalysisJob, WebhookEvent), domain events, enums
  • Application: CQRS with source-generated Mediator, FluentValidation
  • Infrastructure: EF Core + PostgreSQL (JSONB), AI services, Git clients, background services
  • Apis: minimal API endpoints, API-key auth

Plus Lintellect.Cli (stateless pipeline tool), Lintellect.Shared (contracts) and AppHost/ServiceDefaults (.NET Aspire, local dev only).

Tech stack: .NET 10 / C# 14, PostgreSQL, Polly (resilience), OpenTelemetry (observability), Testcontainers + NSubstitute (testing).

Git workflow and releases

Lintellect uses GitHub Flow with release branches and two independent releases — the API (Docker image, api/v1.2.3) and the CLI (NuGet package, cli/v2.1.0):

./scripts/create-release-api.sh 1.2.0
./scripts/create-release-cli.sh 2.1.0

See the Git Workflow Documentation for branch strategy and the release process, and Git Credentials Configuration for provider credential details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes using conventional commits
  4. Push to the branch and open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Acknowledgments

Support


About

A self-hostable PR static analyzer with AI integration.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages