Complete API documentation for the OpenCode SonarQube Plugin.
- Installation
- Quick Start
- SonarQubeAPI Class
- ProjectsAPI
- IssuesAPI
- QualityGateAPI
- RulesAPI
- SourcesAPI
- DuplicationsAPI
- ProjectAnalysesAPI
- QualityProfilesAPI
- BranchesAPI
- MetricsAPI
- ComponentsAPI
- ComputeEngineAPI
- Scanner Functions
- Configuration Functions
- Types
- Error Classes
# Using bun
bun add opencode-sonarqube
# Using npm
npm install opencode-sonarqubeimport {
createSonarQubeAPI,
createSonarQubeAPIWithCredentials,
loadConfig,
bootstrap,
getProjectState,
runAnalysis,
} from "opencode-sonarqube";
// Option 1: Using environment variables
const config = loadConfig();
const state = await getProjectState("./");
const api = createSonarQubeAPI(config, state);
// Option 2: Using explicit credentials
const api = createSonarQubeAPIWithCredentials(
"https://sonarqube.example.com",
"admin",
"password"
);
// Health check
const health = await api.healthCheck();
console.log("Server healthy:", health.healthy);
// Get issues
const issues = await api.issues.getFormattedIssues({
projectKey: "my-project",
severities: ["BLOCKER", "CRITICAL"],
});
// Get quality gate status
const status = await api.qualityGate.getStatus("my-project");
console.log("Quality Gate:", status.projectStatus.status);Main entry point for all API operations.
new SonarQubeAPI(client: SonarQubeClient, logger?: Logger)Create API instance from configuration and project state.
import { createSonarQubeAPI, loadConfig, getProjectState } from "opencode-sonarqube";
const config = loadConfig();
const state = await getProjectState("./my-project");
const api = createSonarQubeAPI(config, state);Create API instance with username/password authentication.
import { createSonarQubeAPIWithCredentials } from "opencode-sonarqube";
const api = createSonarQubeAPIWithCredentials(
"https://sonarqube.example.com",
"admin",
"secure-password"
);Create API instance with token authentication.
import { createSonarQubeAPIWithToken } from "opencode-sonarqube";
const api = createSonarQubeAPIWithToken(
"https://sonarqube.example.com",
"sqp_xxxxxxxxxxxxx"
);| Property | Type | Description |
|---|---|---|
client |
SonarQubeClient |
Low-level HTTP client |
projects |
ProjectsAPI |
Project management operations |
issues |
IssuesAPI |
Issue queries and management |
qualityGate |
QualityGateAPI |
Quality gate operations |
rules |
RulesAPI |
Rule details and explanations |
sources |
SourcesAPI |
Source code context for issues |
duplications |
DuplicationsAPI |
Code duplication detection |
analyses |
ProjectAnalysesAPI |
Analysis history |
profiles |
QualityProfilesAPI |
Quality profile information |
branches |
BranchesAPI |
Multi-branch analysis |
metrics |
MetricsAPI |
Detailed metrics with trends |
components |
ComponentsAPI |
Files/directories with issues |
computeEngine |
ComputeEngineAPI |
Analysis task tracking |
Check server connectivity and health.
const health = await api.healthCheck();
// { healthy: true, version: "26.1.0.15561" }
// { healthy: false, error: "Connection refused" }Get complete analysis summary including quality gate, issues, and metrics.
const summary = await api.getAnalysisSummary("my-project");
/*
{
qualityGate: {
status: "OK",
passed: true,
failedConditions: 0
},
issues: {
total: 5,
blocker: 0,
critical: 1,
major: 2,
minor: 2,
info: 0
},
metrics: {
coverage: 85.5,
duplications: 1.2,
bugs: 0,
vulnerabilities: 0,
codeSmells: 5,
securityHotspots: 0,
reliabilityRating: "A",
securityRating: "A",
maintainabilityRating: "A",
linesOfCode: 2500
}
}
*/Manage SonarQube projects.
Create a new project.
const result = await api.projects.create({
project: "my-project-key",
name: "My Project",
visibility: "private", // optional: "public" | "private"
mainBranch: "main", // optional
});
// { project: { key: "my-project-key", name: "My Project", qualifier: "TRK" } }Check if a project exists.
const exists = await api.projects.exists("my-project");
// true or falseSearch for projects.
const results = await api.projects.search("my-");
/*
{
paging: { pageIndex: 1, pageSize: 100, total: 3 },
components: [
{ key: "my-project", name: "My Project", qualifier: "TRK", visibility: "private" },
...
]
}
*/Delete a project.
await api.projects.delete("my-project");Generate analysis token for a project.
const token = await api.projects.generateToken({
name: "ci-token",
projectKey: "my-project",
type: "PROJECT_ANALYSIS_TOKEN",
expirationDate: "2025-12-31", // optional
});
// { login: "admin", name: "ci-token", token: "sqp_xxx", type: "PROJECT_ANALYSIS_TOKEN" }Assign a quality gate to a project.
await api.projects.setQualityGate("my-project", "Enterprise");Configure project settings.
await api.projects.configureSettings("my-project", {
"sonar.coverage.exclusions": "**/test/**",
"sonar.cpd.exclusions": "**/generated/**",
});Query and manage issues.
Search for issues with filtering.
const issues = await api.issues.search({
projectKey: "my-project",
severities: ["BLOCKER", "CRITICAL"],
types: ["BUG", "VULNERABILITY"],
statuses: ["OPEN", "CONFIRMED"],
resolved: false,
pageSize: 100,
});Parameters:
| Parameter | Type | Description |
|---|---|---|
projectKey |
string |
Project key (required) |
severities |
IssueSeverity[] |
Filter by severity |
types |
IssueType[] |
Filter by type |
statuses |
string[] |
Filter by status |
resolved |
boolean |
Include resolved issues |
assigned |
boolean |
Only assigned issues |
branch |
string |
Branch name |
pullRequest |
string |
Pull request ID |
pageSize |
number |
Results per page (max 500) |
page |
number |
Page number |
Get issues in a simplified format for AI agents.
const issues = await api.issues.getFormattedIssues({
projectKey: "my-project",
severities: ["BLOCKER", "CRITICAL", "MAJOR"],
});
/*
[
{
severity: "CRITICAL",
type: "BUG",
rule: "typescript:S1854",
message: "Remove this useless assignment to variable 'x'",
file: "src/utils/helper.ts",
line: 42,
effort: "5min",
quickFixAvailable: true
},
...
]
*/Get issue counts by severity.
const counts = await api.issues.getCounts("my-project");
// { total: 10, blocker: 0, critical: 1, major: 4, minor: 3, info: 2 }Get issues for a specific file.
const fileIssues = await api.issues.getByFile("my-project", "src/app.ts");Manage quality gates and metrics.
Get quality gate status for a project.
const status = await api.qualityGate.getStatus("my-project");
/*
{
projectStatus: {
status: "ERROR",
conditions: [
{
status: "ERROR",
metricKey: "coverage",
comparator: "LT",
errorThreshold: "80",
actualValue: "65.5"
},
{
status: "OK",
metricKey: "bugs",
comparator: "GT",
errorThreshold: "0",
actualValue: "0"
}
]
}
}
*/Format quality gate status as human-readable string.
const status = await api.qualityGate.getStatus("my-project");
const formatted = api.qualityGate.formatStatus(status);
/*
Quality Gate: ERROR
Failed Conditions:
- coverage: 65.5 (threshold: >= 80)
Passed Conditions:
- bugs: 0 (threshold: <= 0)
*/Get detailed quality metrics.
const metrics = await api.qualityGate.getQualityMetrics("my-project");
/*
{
coverage: 85.5,
duplications: 1.2,
bugs: 0,
vulnerabilities: 0,
codeSmells: 5,
securityHotspots: 0,
reliabilityRating: "A",
securityRating: "A",
maintainabilityRating: "A",
linesOfCode: 2500
}
*/List all quality gates.
const gates = await api.qualityGate.list();
/*
{
qualitygates: [
{ id: 1, name: "Sonar way", isDefault: true, isBuiltIn: true },
{ id: 2, name: "Enterprise", isDefault: false, isBuiltIn: false }
],
default: 1
}
*/Create a new quality gate.
const gate = await api.qualityGate.create("My Custom Gate");
// { id: 5, name: "My Custom Gate" }Add a condition to a quality gate.
await api.qualityGate.createCondition("My Custom Gate", {
metric: "coverage",
op: "LT",
error: "80",
});Get rule details and explanations.
Get detailed information about a specific rule.
const rule = await api.rules.getRule("typescript:S3776");
/*
{
key: "typescript:S3776",
name: "Cognitive Complexity of functions should not be too high",
severity: "CRITICAL",
type: "CODE_SMELL",
description: "Cognitive Complexity is a measure of how hard...",
langName: "TypeScript",
tags: ["brain-overload"]
}
*/Search for rules with filtering.
const rules = await api.rules.searchRules({
languages: "ts",
severities: "CRITICAL,BLOCKER",
types: "BUG,VULNERABILITY",
});Fetch source code context for issues.
Get source code lines for a component.
const lines = await api.sources.getSourceLines({
key: "my-project:src/app.ts",
from: 40,
to: 50,
});
/*
[
{ line: 40, code: "function processData() {" },
{ line: 41, code: " const result = [];", scmRevision: "abc123" },
...
]
*/Get issues with surrounding source code context.
const issuesWithContext = await api.sources.getIssuesWithContext(issues, 5);
// Returns issues with 5 lines of context above and belowFormat an issue with context as markdown.
const formatted = api.sources.formatIssueWithContext(issueWithContext);
/*
### CRITICAL: Remove this useless assignment
**File:** src/utils.ts:42
**Rule:** typescript:S1854
\`\`\`typescript
40 | function calculate() {
41 | let result = 0;
42 > | result = getValue(); // Issue here
43 | return result;
44 | }
\`\`\`
*/Find code duplications across the project.
Get duplications for a specific file.
const duplications = await api.duplications.getDuplications("my-project:src/utils.ts");
/*
[
{
blocks: [
{ from: 10, size: 15, file: "src/utils.ts" },
{ from: 50, size: 15, file: "src/helpers.ts" }
]
}
]
*/Get all duplications in a project.
const allDups = await api.duplications.getProjectDuplications("my-project");Format duplications as markdown for AI agents.
const formatted = api.duplications.formatForAgent(duplications);Get analysis history.
Get past analyses for a project.
const analyses = await api.analyses.getAnalyses({
projectKey: "my-project",
branch: "main",
pageSize: 10,
});
/*
[
{
key: "AYx...",
date: "2024-01-15T10:30:00Z",
projectVersion: "1.0.0",
events: [{ category: "VERSION", name: "1.0.0" }]
},
...
]
*/Format analysis history as markdown.
const formatted = api.analyses.formatAnalysesForAgent(analyses);Get quality profile information.
Get quality profiles assigned to a project.
const profiles = await api.profiles.getProjectProfiles("my-project");
/*
[
{
key: "AYx...",
name: "Sonar way",
language: "ts",
languageName: "TypeScript",
isDefault: true,
isBuiltIn: true,
activeRuleCount: 245,
rulesUpdatedAt: "2024-01-10T00:00:00Z"
}
]
*/Get profile inheritance tree.
const inheritance = await api.profiles.getInheritance("profile-key");Format profiles as markdown.
const formatted = api.profiles.formatProfilesForAgent(profiles, "my-project");Multi-branch analysis management.
Get all branches for a project.
const branches = await api.branches.getBranches("my-project");
/*
[
{
name: "main",
isMain: true,
type: "LONG",
status: { qualityGateStatus: "OK" },
analysisDate: "2024-01-15T10:30:00Z"
},
{
name: "feature/auth",
isMain: false,
type: "SHORT",
status: { qualityGateStatus: "ERROR" }
}
]
*/Delete a branch.
await api.branches.deleteBranch("my-project", "old-feature");Format branches as markdown.
const formatted = api.branches.formatBranchesForAgent(branches);Get detailed metrics with period comparison.
Get measures for a component.
const measures = await api.metrics.getMeasures({
componentKey: "my-project",
metricKeys: ["coverage", "bugs", "vulnerabilities"],
});Get measures with new code period comparison.
const measures = await api.metrics.getMeasuresWithPeriod({
componentKey: "my-project",
branch: "main",
});
/*
[
{
metric: "coverage",
value: "85.5",
period: { value: "2.3" } // Change in new code period
},
...
]
*/Format measures as markdown with interpretation.
const formatted = api.metrics.formatMeasuresForAgent(measures);Get files and directories with issue counts.
Get files with the most issues (for prioritizing refactoring).
const worstFiles = await api.components.getWorstFiles("my-project", 10);
/*
[
{
key: "my-project:src/legacy/old-code.ts",
name: "old-code.ts",
path: "src/legacy/old-code.ts",
measures: [
{ metric: "bugs", value: "5" },
{ metric: "code_smells", value: "23" },
{ metric: "vulnerabilities", value: "2" }
]
},
...
]
*/Get directories with the most issues.
const worstDirs = await api.components.getWorstDirectories("my-project", 5);Format worst files as markdown for AI agents.
const formatted = api.components.formatWorstFilesForAgent(worstFiles);
/*
## Files with Most Issues
| File | Bugs | Vulnerabilities | Code Smells | Total |
|------|------|-----------------|-------------|-------|
| src/legacy/old-code.ts | 5 | 2 | 23 | 30 |
| src/utils/helpers.ts | 2 | 0 | 15 | 17 |
...
*/Track analysis task status.
Get status of an analysis task.
const task = await api.computeEngine.getTask("AYx...");
/*
{
id: "AYx...",
type: "REPORT",
componentKey: "my-project",
status: "SUCCESS",
submittedAt: "2024-01-15T10:30:00Z",
executedAt: "2024-01-15T10:31:00Z",
executionTimeMs: 45000
}
*/Wait for an analysis to complete.
const task = await api.computeEngine.waitForAnalysis("my-project", {
timeout: 120000, // 2 minutes
pollInterval: 2000 // Check every 2 seconds
});Get pending tasks for a component.
const queue = await api.computeEngine.getComponentQueue("my-project");Run the SonarQube scanner.
import { runScanner } from "opencode-sonarqube";
const result = await runScanner({
url: "https://sonarqube.example.com",
token: "sqp_xxx",
projectKey: "my-project",
sources: "src",
directory: "./",
});
// { success: true, output: "..." }Run complete analysis including scanner and result fetching.
import { runAnalysis, loadConfig, getProjectState } from "opencode-sonarqube";
const config = loadConfig();
const state = await getProjectState("./");
const result = await runAnalysis(config, state, { projectKey: state.projectKey }, "./");
/*
{
success: true,
qualityGateStatus: "OK",
issues: { total: 5, blocker: 0, critical: 1, major: 2, minor: 2, info: 0 },
metrics: { coverage: 85.5, ... },
formattedIssues: [...],
timestamp: "2024-01-15T10:30:00Z"
}
*/Format analysis result as markdown for AI agents.
const formatted = formatAnalysisResult(result);
/*
## SonarQube Analysis Results
**Quality Gate: [PASS] OK**
### Summary
- Blockers: 0, Critical: 1, Major: 2, Minor: 2, Info: 0
### Metrics
- Coverage: 85.5%
- Duplications: 1.2%
...
*/Detect project languages and framework.
import { detectProjectType } from "opencode-sonarqube";
const type = await detectProjectType("./my-project");
// { languages: ["typescript", "javascript"], framework: "bun" }Generate and write sonar-project.properties file.
import { writePropertiesFile } from "opencode-sonarqube";
const path = await writePropertiesFile("./", {
projectKey: "my-project",
projectName: "My Project",
sources: "src",
tests: "tests",
});
// "./sonar-project.properties"Load configuration from environment variables.
import { loadConfig } from "opencode-sonarqube";
// Requires: SONAR_HOST_URL, SONAR_USER, SONAR_PASSWORD
const config = loadConfig();
if (!config) {
console.error("SonarQube not configured");
}Get or generate project key for a directory.
import { getProjectKey } from "opencode-sonarqube";
const key = await getProjectKey("./my-project");
// "my-project" (from package.json name or directory name)Get project display name.
import { getProjectName } from "opencode-sonarqube";
const name = await getProjectName("./my-project");
// "My Project" (from package.json or directory name)Initialize a project in SonarQube.
import { bootstrap, loadConfig } from "opencode-sonarqube";
const config = loadConfig();
const result = await bootstrap({
config,
directory: "./my-project",
force: false, // set true to reinitialize
});
/*
{
success: true,
projectKey: "my-project",
projectToken: "sqp_xxx",
qualityGate: "Enterprise",
languages: ["typescript"],
message: "Project created successfully",
isNewProject: true
}
*/Check if project needs initialization.
import { needsBootstrap } from "opencode-sonarqube";
if (await needsBootstrap("./my-project")) {
console.log("Project needs setup");
}Load project state from .sonarqube/project.json.
import { getProjectState } from "opencode-sonarqube";
const state = await getProjectState("./my-project");
/*
{
projectKey: "my-project",
projectToken: "sqp_xxx",
tokenName: "my-project-opencode",
initializedAt: "2024-01-15T10:00:00Z",
lastAnalysis: "2024-01-15T12:00:00Z",
languages: ["typescript"],
qualityGate: "Enterprise",
setupComplete: true
}
*/interface SonarQubeConfig {
url: string; // SonarQube server URL
user: string; // Username
password: string; // Password
level: AnalysisLevel; // "enterprise" | "standard" | "relaxed" | "off"
autoAnalyze: boolean; // Auto-analyze on idle
projectKey?: string; // Override project key
projectName?: string; // Override project name
qualityGate?: string; // Quality gate name
newCodeDefinition: NewCodeDefinition;
sources: string; // Source directories
tests?: string; // Test directories
exclusions?: string; // Exclusion patterns
}interface ProjectState {
projectKey: string;
projectToken: string;
tokenName: string;
initializedAt: string;
lastAnalysis?: string;
languages: string[];
qualityGate?: string;
setupComplete: boolean;
}interface AnalysisResult {
success: boolean;
qualityGateStatus: QualityGateStatus;
issues: {
total: number;
blocker: number;
critical: number;
major: number;
minor: number;
info: number;
};
metrics: {
coverage?: number;
duplications?: number;
codeSmells?: number;
bugs?: number;
vulnerabilities?: number;
securityHotspots?: number;
};
formattedIssues: FormattedIssue[];
timestamp: string;
}interface FormattedIssue {
severity: "BLOCKER" | "CRITICAL" | "MAJOR" | "MINOR" | "INFO";
type: "BUG" | "VULNERABILITY" | "CODE_SMELL" | "SECURITY_HOTSPOT";
rule: string;
message: string;
file: string;
line?: number;
effort?: string;
quickFixAvailable: boolean;
}type IssueSeverity = "BLOCKER" | "CRITICAL" | "MAJOR" | "MINOR" | "INFO";type IssueType = "BUG" | "VULNERABILITY" | "CODE_SMELL" | "SECURITY_HOTSPOT";type QualityGateStatus = "OK" | "WARN" | "ERROR" | "NONE";type AnalysisLevel = "enterprise" | "standard" | "relaxed" | "off";Base error class for all SonarQube errors.
class SonarQubeError extends Error {
code: string;
statusCode?: number;
}Network or connectivity issues.
try {
await api.healthCheck();
} catch (e) {
if (e instanceof ConnectionError) {
console.error("Cannot connect to SonarQube server");
}
}Invalid credentials or token.
try {
await api.projects.create({ project: "test", name: "Test" });
} catch (e) {
if (e instanceof AuthenticationError) {
console.error("Invalid credentials");
}
}Project does not exist.
try {
await api.qualityGate.getStatus("nonexistent");
} catch (e) {
if (e instanceof ProjectNotFoundError) {
console.error("Project not found:", e.message);
}
}API rate limit exceeded.
try {
await api.issues.search({ projectKey: "test" });
} catch (e) {
if (e instanceof RateLimitError) {
console.error("Rate limited, retry later");
}
}Bootstrap or configuration errors.
try {
await bootstrap({ config, directory: "./" });
} catch (e) {
if (e instanceof SetupError) {
console.error("Setup failed:", e.message);
}
}Custom logger with child loggers and level filtering.
import { Logger, createLogger } from "opencode-sonarqube";
// Create logger (standalone)
const logger = new Logger("my-module");
// Create logger with OpenCode client (for plugin use)
const logger = createLogger("my-module", ctx.client);
// Log at different levels
logger.debug("Debug message", { extra: "data" });
logger.info("Info message");
logger.warn("Warning message");
logger.error("Error message", { error: e });
// Create child logger
const childLogger = logger.child("sub-module");
// Logs as: [my-module:sub-module] message