-
Ask which mode if the user hasn't said — it's the first question. One question, not two:
Which Sonar are you using — SonarQube server (self-hosted) or SonarCloud (SaaS at sonarcloud.io)?
If they describe the host URL, infer: anything matching sonarcloud.io ⇒ SonarCloud; anything else ⇒ SonarQube server. Don't ask twice.
-
Collect the inputs. Ask for whichever are missing. Cap at two more clarifying questions per turn; pick defaults for the rest and call them out:
| Input | Required for | Default if not given |
|---|
Project key (e.g. myorg_payments-api) | both | <repo-name> (derive from git remote get-url origin) |
Organization key (SonarCloud only, e.g. my-org) | SonarCloud | Required — ask if missing |
SonarQube host URL (e.g. https://sonar.company.com) | SonarQube server | Required — ask if missing |
| Primary language(s) | both | Detect from repo (see step 3) |
| CI system | both | GitHub Actions (emit gitlab-ci/jenkins/azure-pipelines only if user names it) |
| Coverage tool | both | Detect from language (see step 4) |
-
Detect language(s) from the working tree — don't ask if a probe will tell you:
| Marker file | Language | Default source dir |
|---|
package.json | JavaScript / TypeScript | src |
pom.xml | Java (Maven) | src/main/java |
build.gradle / build.gradle.kts | Java / Kotlin (Gradle) | src/main/java or src/main/kotlin |
requirements.txt / pyproject.toml / setup.py | Python | top-level package dir or src |
go.mod | Go | repo root |
*.csproj / *.sln | C# / .NET | project dirs |
Cargo.toml | Rust | src |
composer.json | PHP | src |
Gemfile | Ruby | lib and app |
Multi-language is normal (e.g. TypeScript frontend + Python backend) — set sonar.sources to a comma-separated list and emit one combined sonar-project.properties. Don't generate one file per language.
-
Pick the coverage report path based on language. These are the formats Sonar's importers expect:
| Language | Coverage report | Sonar property |
|---|
| JS / TS | coverage/lcov.info (Jest, Vitest default) | sonar.javascript.lcov.reportPaths |
| Java (Maven) | target/site/jacoco/jacoco.xml | sonar.coverage.jacoco.xmlReportPaths |
| Java (Gradle) | build/reports/jacoco/test/jacocoTestReport.xml | sonar.coverage.jacoco.xmlReportPaths |
| Python | coverage.xml (pytest-cov --cov-report=xml) | sonar.python.coverage.reportPaths |
| Go | coverage.out | sonar.go.coverage.reportPaths |
| C# | coverage.opencover.xml (coverlet) | sonar.cs.opencover.reportsPaths |
If the user has no coverage tool today, don't fabricate a report path. Omit the coverage property and add a # TODO comment in the properties file pointing them at the right one.
-
Generate sonar-project.properties using templates/sonar-project.properties.tmpl. Fill placeholders; remove blocks that don't apply.
For SonarCloud, add this line (immediately after sonar.projectKey):
sonar.organization={{ORG_KEY}}
The organization key is mandatory for SonarCloud and rejected by SonarQube server — emit it for one and not the other.
Exclusions — add these by default; they save the next contributor from a noisy first scan:
sonar.exclusions=**/node_modules/**,**/dist/**,**/build/**,**/.next/**,**/__pycache__/**,**/*.generated.*,**/vendor/**,**/migrations/**
sonar.test.exclusions=**/*.test.*,**/*.spec.*,**/test/**,**/tests/**
sonar.coverage.exclusions=**/*.test.*,**/*.spec.*,**/test/**,**/tests/**,**/*.stories.*,**/index.{js,ts}
Trim language-irrelevant entries (e.g. drop __pycache__ for a pure-JS repo).
-
Generate the CI workflow. Default is GitHub Actions. Pick the template by mode:
Both must:
- Trigger on
push to default branch and pull_request (Sonar needs the PR event to comment on the PR).
- Use
actions/checkout@v4 with fetch-depth: 0 (Sonar needs full history to attribute blame).
- Run the project's build + test + coverage before the scanner, otherwise coverage is empty.
- Reference
SONAR_TOKEN from GitHub Secrets — never inline.
Mode-specific:
- Server also references
SONAR_HOST_URL from Secrets, and runs a separate sonarqube-quality-gate-action step so the job fails on gate failure.
- SonarCloud doesn't need
SONAR_HOST_URL (the action defaults to https://sonarcloud.io), and references GITHUB_TOKEN for PR decoration.
For GitLab / Jenkins / Azure DevOps, see reference.md — only emit those if the user names them.
-
Generate the README snippet using templates/readme-snippet.md. Drop it under a ## Code quality heading. It documents: what the gate enforces, the two required secrets, the local-scan command, and where to read findings (Sonar UI link).
-
List the secrets the user must add manually. End your response with this exact block — the skill cannot set secrets and shouldn't pretend it can. The steps differ by mode:
SonarQube server:
Manual steps you must do before the workflow runs green:
1. Create a SonarQube project at <SONAR_HOST_URL>/projects/create with key `<project-key>`.
2. Generate a project token (My Account → Security → Generate Token).
3. Add GitHub Secret `SONAR_TOKEN` with the token value.
4. Add GitHub Secret `SONAR_HOST_URL` with `<SONAR_HOST_URL>`.
5. (Optional) Tighten the Quality Gate in Sonar UI — defaults are listed in the README snippet.
SonarCloud:
Manual steps you must do before the workflow runs green:
1. Sign in at https://sonarcloud.io with the GitHub account that owns this repo.
2. Create / import the project: + → Analyze new project → pick the repo. Confirm organization is `<org-key>` and project key is `<project-key>`.
3. Disable "Automatic Analysis" on the project (Administration → Analysis Method) — the CI-driven scan and Automatic Analysis cannot both run.
4. Generate a token (My Account → Security → Generate Token).
5. Add GitHub Secret `SONAR_TOKEN` with the token value.
6. (Optional) Tighten the Quality Gate in SonarCloud UI — defaults are listed in the README snippet.