diff --git a/.cursor/rules/sonar-issue-fix.mdc b/.cursor/rules/sonar-issue-fix.mdc deleted file mode 100644 index 764233d..0000000 --- a/.cursor/rules/sonar-issue-fix.mdc +++ /dev/null @@ -1,39 +0,0 @@ -# sonar-issue-fix (YOLO MODE) - -Aggressive automation for fixing Sonar issues in this repo. - -Pre-flight: - -- Before any coding, auto-fetch issues by running `sonar:fetch`. -- Parse `.sonar/issues.json` and create a TODO list covering ALL issues to solve. -- Order by priority: S3776,S3358,S4165 > S1874,S6551,S6660 > S6759,S4325,S6479,S6478 > S125,S1135. - -Execution loop: - -- Iterate issues one-by-one on the current branch. -- For each issue: - 1. Apply the minimal, targeted change. - 2. Keep control flow readable; prefer early returns; eliminate nested ternaries. - 3. Maintain/improve types; avoid `any` and non-null assertions unless necessary. - 4. Update the TODO list status for the issue to completed. - 5. Auto-commit with a clear message referencing the rule and file (e.g., `fix(sonar): S3358 replace nested ternaries in X.ts`). - -Patterns: - -- S6759: prefer readonly props/types. -- S4325: remove unsafe casts/non-null assertions; add guards/narrowings. -- S6660: simplify branches via early returns. -- S6479: use stable IDs for keys. -- S6478: hoist nested components. -- S6551: avoid implicit object stringification. -- S125: delete commented-out code. -- S3358: replace nested ternaries with `if/else`. -- S3776: extract helpers and deduplicate logic. -- S4165: remove redundant reassignments. -- S1135: resolve TODOs or link to tracked ticket. - -Notes: - -- Keep commits small and scoped to a single rule/area. -- If an edit is risky, create a draft and run checks, then commit. -- Skip issues that depend on missing context and leave a clear note in the TODO list. diff --git a/.cursor/rules/sonarflow-autofix.mdc b/.cursor/rules/sonarflow-autofix.mdc new file mode 100644 index 0000000..8edbd79 --- /dev/null +++ b/.cursor/rules/sonarflow-autofix.mdc @@ -0,0 +1,54 @@ +# sonarflow-autofix (SAFE) + +Purpose: Provide guidance only. Do not modify files or run commands. + +Operating principles: + +- Only offer advice, suggestions, documentation links, and example snippets for each Sonar issue. +- Never edit files, stage changes, or commit. Never run shell commands. +- Do not auto-fetch issues. If issues are missing, ask the user to run: `sonar:fetch`. +- Keep suggestions minimal, focused, and ordered by priority below. + +Priorities (highest first): + +- S3776,S3358,S4165 > S1874,S6551,S6660 > S6759,S4325,S6479,S6478 > S125,S1135. + +Guidelines: + +- Only touch the code conceptually related to the issue; avoid broad refactors. +- Prefer early returns, readable control flow; avoid nested ternaries. +- Maintain or improve types; avoid `any` and non-null assertions. +- Issues are found in `.sonarflow/issues.json` (user-provided). + +Patterns and examples: + +- S6759: prefer readonly props. + Example: + ```ts + // before + const C: React.FC<{ p: T }> = ({ p }) => {} + // after + const C: React.FC> = ({ p }) => {} + ``` +- S4325: remove unsafe casts/non-null assertions; introduce guards/narrowings. +- S6660: replace nested `else if` chains with early returns. +- S6479: use stable IDs for React keys, not indices. +- S6478: hoist nested component definitions out of parents. +- S6551: avoid implicit object stringification; use `JSON.stringify` or specific fields. +- S125: delete commented-out code blocks. +- S3358: replace nested ternaries with `if/else`. +- S3776: extract helpers, flatten branches, deduplicate logic. +- S4165: remove redundant reassignments. +- S1135: resolve TODOs or link a tracked ticket. + +Process (advisory only): + +1. For the current file/issue, suggest a minimal, targeted change. +2. Provide a short rationale (1–2 lines) and a small code example or diff snippet. +3. If risky, propose an alternative safer approach. +4. Ask the user to review and apply. + +Pitfalls: + +- Do not resolve TODOs that are intentionally pending (external dependency or requirement clarification). + diff --git a/.gitignore b/.gitignore index 0d03a8f..f46e168 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ build .cursor/mcp.json .sonarflowrc.json +.sonarflow/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6432df1..c6dadff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [0.3.0-beta.5](https://github.com/bitrockteam/sonarflow/compare/v0.3.0-beta.4...v0.3.0-beta.5) (2025-11-10) + + +### Chores + +* update schema version to v0.3.0-beta.5 ([ccc515a](https://github.com/bitrockteam/sonarflow/commit/ccc515ae5b43c3bfee3403dd39c58eb9e352f4f6)) + +## [0.3.0-beta.4](https://github.com/bitrockteam/sonarflow/compare/v0.3.0-beta.3...v0.3.0-beta.4) (2025-11-10) + + +### Chores + +* update file paths and configurations for Sonarflow integration ([ba97c3e](https://github.com/bitrockteam/sonarflow/commit/ba97c3ef53b7f2e6f05110c550a919a544f806d7)) +* update schema version to v0.3.0-beta.4 ([fad91c7](https://github.com/bitrockteam/sonarflow/commit/fad91c77d31728e3f564066a999f25b0eea38fab)) + ## [0.3.0-beta.2](https://github.com/bitrockteam/sonarflow/compare/v0.3.0-beta.1...v0.3.0-beta.2) (2025-11-01) diff --git a/README.md b/README.md index ad59ccc..908ac88 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ npx @bitrockteam/sonarflow fetch my-branch https://sonarcloud.io/project/issues? ``` - Auto PR detection tries provider API first (GitHub or Bitbucket), then falls back to extracting from branch naming patterns. -- Issues are saved to `.sonar/issues.json`. +- Issues are saved to `.sonarflow/issues.json`. #### Initialize Configuration @@ -206,7 +206,7 @@ npx @bitrockteam/sonarflow@latest 1. Detects the current git branch or uses provided branch name 2. Attempts to find associated PR using GitHub or Bitbucket API, or branch name pattern matching 3. Fetches SonarQube issues for the PR or branch -4. Saves issues to `.sonar/issues.json` +4. Saves issues to `.sonarflow/issues.json` 5. Displays a summary of fetched issues @@ -219,21 +219,21 @@ npx @bitrockteam/sonarflow@latest ## Output Files -- `.sonar/issues.json` - Fetched SonarQube issues in JSON format +- `.sonarflow/issues.json` - Fetched SonarQube issues in JSON format - `.sonarflowrc.json` - Project configuration -- `.cursor/rules/sonar-issue-fix.mdc` - Cursor AI rules (if selected) -- `.vscode/sonar-issue-fix.md` - VSCode rules (if selected) -- `.windsurf/rules/sonar-issue-fix.mdc` - Windsurf rules (if selected) -- `.rules/sonar-issue-fix.md` - Generic rules (if selected "other") +- `.cursor/rules/sonarflow-autofix.mdc` - Cursor AI rules (if selected) +- `.vscode/sonarflow-autofix.md` - VSCode rules (if selected) +- `.windsurf/rules/sonarflow-autofix.mdc` - Windsurf rules (if selected) +- `.rules/sonarflow-autofix.md` - Generic rules (if selected "other") ## AI Editor Integration The tool creates specific rules for your chosen AI editor to help with automated SonarQube issue fixing: -- **Cursor**: Creates `.cursor/rules/sonar-issue-fix.mdc` -- **VSCode with Copilot**: Creates `.vscode/sonar-issue-fix.md` -- **Windsurf**: Creates `.windsurf/rules/sonar-issue-fix.mdc` -- **Other**: Creates `.rules/sonar-issue-fix.md` +- **Cursor**: Creates `.cursor/rules/sonarflow-autofix.mdc` +- **VSCode with Copilot**: Creates `.vscode/sonarflow-autofix.md` +- **Windsurf**: Creates `.windsurf/rules/sonarflow-autofix.mdc` +- **Other**: Creates `.rules/sonarflow-autofix.md` These rules provide patterns and priorities for fixing common SonarQube issues. diff --git a/package.json b/package.json index 684ca69..0ec498c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bitrockteam/sonarflow", - "version": "0.3.0-beta.3", + "version": "0.3.0-beta.5", "description": "CLI utility for fetching SonarQube issues and integrating with Bitbucket/GitHub PR workflows", "main": "dist/cli.js", "bin": { diff --git a/schemas/sonarflowrc.schema.json b/schemas/sonarflowrc.schema.json index 5c5e86e..f211232 100644 --- a/schemas/sonarflowrc.schema.json +++ b/schemas/sonarflowrc.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/bitrockteam/sonarflow/v0.3.0-beta.3/schemas/sonarflowrc.schema.json", + "$id": "https://raw.githubusercontent.com/bitrockteam/sonarflow/v0.3.0-beta.5/schemas/sonarflowrc.schema.json", "title": "Sonarflow Configuration Schema", "description": "Schema for .sonarflowrc.json configuration file", "type": "object", @@ -11,7 +11,8 @@ "sonarProjectKey", "sonarMode", "aiEditor", - "rulesFlavor" + "rulesFlavor", + "rulePath" ], "properties": { "$schema": { @@ -91,9 +92,14 @@ ], "description": "Rules flavor for AI code generation" }, + "rulePath": { + "type": "string", + "description": "Path to the rule file (relative to project root)", + "minLength": 1 + }, "outputPath": { "type": "string", - "description": "Output path for sonar issues (default: '.sonar/')" + "description": "Output path for sonar issues (default: '.sonarflow/')" } }, "allOf": [ diff --git a/src/cli.ts b/src/cli.ts index a36bb34..41961f7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -121,7 +121,7 @@ program program .command("fetch") - .description("Fetch Sonar issues and save to .sonar/issues.json") + .description("Fetch Sonar issues and save to .sonarflow/issues.json") .allowExcessArguments(true) .action(() => { runNodeScript("./versioning/index.js", process.argv.slice(3)); diff --git a/src/init.ts b/src/init.ts index ce5441b..f17343d 100644 --- a/src/init.ts +++ b/src/init.ts @@ -38,6 +38,7 @@ interface InitAnswers { aiEditor: "cursor" | "copilot (vscode)" | "windsurf" | "other"; rulesFlavor: "safe" | "vibe-coder" | "yolo"; + rulePath: string; } interface Config { @@ -54,6 +55,7 @@ interface Config { aiEditor: "cursor" | "copilot (vscode)" | "windsurf" | "other"; rulesFlavor: "safe" | "vibe-coder" | "yolo"; + rulePath: string; } const runInit = async (): Promise => { @@ -134,6 +136,21 @@ const runInit = async (): Promise => { ? defaultRepoName.split("/")[0] : undefined; + const getDefaultRulePath = ( + editor: "cursor" | "copilot (vscode)" | "windsurf" | "other" + ): string => { + if (editor === "cursor") { + return ".cursor/rules/sonarflow-autofix.mdc"; + } + if (editor === "copilot (vscode)") { + return ".vscode/sonarflow-autofix.md"; + } + if (editor === "windsurf") { + return ".windsurf/rules/sonarflow-autofix.mdc"; + } + return ".rules/sonarflow-autofix.md"; + }; + let answers: InitAnswers; try { const repoName = await input({ @@ -244,6 +261,16 @@ const runInit = async (): Promise => { default: "safe", }); + const defaultRulePath = getDefaultRulePath(aiEditor); + const rulePath = await input({ + message: "Rule path:", + default: defaultRulePath, + validate: (val: string) => { + const trimmed = (val ?? "").trim(); + return trimmed ? true : "Rule path is required"; + }, + }); + answers = { repoName, gitProvider, @@ -255,6 +282,7 @@ const runInit = async (): Promise => { sonarMode, sonarBaseUrl, rulesFlavor, + rulePath: rulePath.trim(), }; } catch (error) { // Handle graceful exit on SIGINT (Ctrl+C) @@ -297,6 +325,7 @@ const runInit = async (): Promise => { // automation aiEditor: answers.aiEditor, rulesFlavor: answers.rulesFlavor, + rulePath: answers.rulePath, }; const configSpinner = ora({ @@ -352,17 +381,7 @@ const runInit = async (): Promise => { } const ruleContent = await fs.readFile(templateRulePath, "utf8"); - const editor = answers.aiEditor; - let targetRulePath: string; - if (editor === "cursor") { - targetRulePath = path.join(process.cwd(), ".cursor/rules/sonar-issue-fix.mdc"); - } else if (editor === "copilot (vscode)") { - targetRulePath = path.join(process.cwd(), ".vscode/sonar-issue-fix.md"); - } else if (editor === "windsurf") { - targetRulePath = path.join(process.cwd(), ".windsurf/rules/sonar-issue-fix.mdc"); - } else { - targetRulePath = path.join(process.cwd(), ".rules/sonar-issue-fix.md"); - } + const targetRulePath = path.join(process.cwd(), answers.rulePath); await fs.ensureDir(path.dirname(targetRulePath)); await fs.writeFile(targetRulePath, ruleContent, "utf8"); diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 0d2a7da..a9c4079 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -1,11 +1,13 @@ #!/usr/bin/env node -import { readFileSync } from "node:fs"; +import { execSync } from "node:child_process"; +import { existsSync, readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; +import { SonarIssueExtractor } from "../sonar/sonar-issue-extractor.js"; import { getRepoInfo } from "./tools/bitbucket.js"; import { getQualityGateStatus } from "./tools/sonar.js"; @@ -194,6 +196,116 @@ server.prompt( } ); +// Register sonar autofix prompt +const sonarAutofixPromptContent = loadPrompt("sonar_autofix"); +server.prompt( + "sonar_autofix", + "Automatically fix SonarQube issues for the current branch using autofix rules", + { + workspacePath: z + .string() + .optional() + .describe("Optional workspace path (defaults to current working directory)"), + branch: z.string().optional().describe("Optional branch name (defaults to current git branch)"), + }, + async (args) => { + try { + const workspacePath = args.workspacePath || process.cwd(); + const configPath = path.join(workspacePath, ".sonarflowrc.json"); + + // Load configuration + if (!existsSync(configPath)) { + throw new Error(`Configuration file not found: .sonarflowrc.json at ${configPath}`); + } + + const config = JSON.parse(readFileSync(configPath, "utf8")) as { + repoName: string; + gitOrganization: string; + sonarProjectKey: string; + sonarBaseUrl?: string; + publicSonar?: boolean; + gitProvider?: "github" | "bitbucket"; + sonarOrganization?: string; + sonarMode?: "standard" | "custom"; + outputPath?: string; + [key: string]: unknown; + }; + + // Get current branch + let currentBranch = args.branch; + if (!currentBranch) { + try { + currentBranch = execSync("git branch --show-current", { + encoding: "utf8", + cwd: workspacePath, + }).trim(); + } catch { + throw new Error("Could not determine current git branch"); + } + } + + // Initialize SonarQube extractor and fetch issues + const extractor = new SonarIssueExtractor(); + const issues = await extractor.fetchIssuesForBranch(currentBranch, config); + + // Read autofix rules file + const autofixRulesPath = path.join( + workspacePath, + ".cursor", + "rules", + "sonarflow-autofix.mdc" + ); + let autofixRules = ""; + if (existsSync(autofixRulesPath)) { + autofixRules = readFileSync(autofixRulesPath, "utf-8"); + } else { + autofixRules = `Warning: Autofix rules file not found at ${autofixRulesPath}. Please ensure the file exists.`; + } + + // Combine everything in the prompt message + const issuesJson = JSON.stringify(issues, null, 2); + const promptText = `${sonarAutofixPromptContent} + +## Autofix Rules: +${autofixRules} + +## Current Branch: +${currentBranch} + +## SonarQube Issues: +${issuesJson} + +Please analyze the SonarQube issues above and apply fixes according to the autofix rules provided. Focus on fixing issues one by one, following the priority order and patterns specified in the rules.`; + + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: promptText, + }, + }, + ], + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `Error setting up sonar autofix prompt: ${errorMessage}`, + }, + }, + ], + isError: true, + }; + } + } +); + // Start server with stdio transport const transport = new StdioServerTransport(); diff --git a/src/prompts/sonar_autofix.txt b/src/prompts/sonar_autofix.txt new file mode 100644 index 0000000..da9d9dd --- /dev/null +++ b/src/prompts/sonar_autofix.txt @@ -0,0 +1,3 @@ +You are an expert code fixer specialized in automatically fixing SonarQube issues based on predefined rules. + +Apply the fixes using the rule `sonarflow-autofix` that you will find at the path specified in the `.sonarflowrc.json` file in the `rulePath` property. diff --git a/src/templates/rule-safe.md b/src/templates/rule-safe.md index cf2a580..8edbd79 100644 --- a/src/templates/rule-safe.md +++ b/src/templates/rule-safe.md @@ -1,4 +1,4 @@ -# sonar-issue-fix (SAFE) +# sonarflow-autofix (SAFE) Purpose: Provide guidance only. Do not modify files or run commands. @@ -18,7 +18,7 @@ Guidelines: - Only touch the code conceptually related to the issue; avoid broad refactors. - Prefer early returns, readable control flow; avoid nested ternaries. - Maintain or improve types; avoid `any` and non-null assertions. -- Issues are found in `.sonar/issues.json` (user-provided). +- Issues are found in `.sonarflow/issues.json` (user-provided). Patterns and examples: diff --git a/src/templates/rule-vibe-coder.md b/src/templates/rule-vibe-coder.md index 4fc283d..d9bcd6f 100644 --- a/src/templates/rule-vibe-coder.md +++ b/src/templates/rule-vibe-coder.md @@ -1,4 +1,4 @@ -# sonar-issue-fix (VIBE-CODER) +# sonarflow-autofix (VIBE-CODER) Standard operating procedure for fixing Sonar issues in this repo. @@ -6,7 +6,7 @@ Fetching issues: - Do not auto-run commands. When issues are needed, ask the user to run: - `sonar:fetch` -- Issues will be available at `.sonar/issues.json` after the user runs the command. +- Issues will be available at `.sonarflow/issues.json` after the user runs the command. When asked to fix Sonar issues: @@ -14,7 +14,7 @@ When asked to fix Sonar issues: - Only touch code related to the issue; avoid broad refactors. - Prefer early returns and readable control flow; remove nested ternaries. - Maintain or improve types; avoid `any`, avoid non-null assertions unless truly required. -- Issues are found in `.sonar/issues.json`. +- Issues are found in `.sonarflow/issues.json`. Patterns: diff --git a/src/templates/rule-yolo.md b/src/templates/rule-yolo.md index 764233d..d448dfb 100644 --- a/src/templates/rule-yolo.md +++ b/src/templates/rule-yolo.md @@ -1,11 +1,11 @@ -# sonar-issue-fix (YOLO MODE) +# sonarflow-autofix (YOLO MODE) Aggressive automation for fixing Sonar issues in this repo. Pre-flight: - Before any coding, auto-fetch issues by running `sonar:fetch`. -- Parse `.sonar/issues.json` and create a TODO list covering ALL issues to solve. +- Parse `.sonarflow/issues.json` and create a TODO list covering ALL issues to solve. - Order by priority: S3776,S3358,S4165 > S1874,S6551,S6660 > S6759,S4325,S6479,S6478 > S125,S1135. Execution loop: diff --git a/src/templates/rule.md b/src/templates/rule.md index 82b4c0e..4fac2f1 100644 --- a/src/templates/rule.md +++ b/src/templates/rule.md @@ -1,4 +1,4 @@ -# sonar-issue-fix +# sonarflow-autofix Standard operating procedure for fixing SonarQube issues in this repo. @@ -11,7 +11,7 @@ When asked to fix Sonar issues: - Only touch code related to the issue; avoid broad refactors. - Prefer early returns and readable control flow; remove nested ternaries. - Maintain or improve types; avoid `any`, avoid non-null assertions unless truly required. -- Issues are found in ".sonar/issues.json" +- Issues are found in `.sonarflow/issues.json` Patterns: diff --git a/src/versioning/index.ts b/src/versioning/index.ts index 2f6e5c4..07ac3bf 100644 --- a/src/versioning/index.ts +++ b/src/versioning/index.ts @@ -166,7 +166,7 @@ const fetchSonarIssues = async ( ]); // Save issues to file - const outputPath = config.outputPath || ".sonar/"; + const outputPath = config.outputPath || ".sonarflow/"; const sonarDir = path.join(process.cwd(), outputPath); if (!fs.existsSync(sonarDir)) { fs.mkdirSync(sonarDir, { recursive: true });