@nielspeter/sonarlint-mcp-server 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +14 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.js +5 -23
- package/dist/index.js.map +1 -1
- package/dist/sloop-bridge.d.ts +10 -1
- package/dist/sloop-bridge.d.ts.map +1 -1
- package/dist/sloop-bridge.js +102 -160
- package/dist/sloop-bridge.js.map +1 -1
- package/dist/state.d.ts +2 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +6 -2
- package/dist/state.js.map +1 -1
- package/dist/tools/analyze-content.js +1 -1
- package/dist/tools/analyze-content.js.map +1 -1
- package/dist/tools/analyze-file.d.ts.map +1 -1
- package/dist/tools/analyze-file.js +4 -6
- package/dist/tools/analyze-file.js.map +1 -1
- package/dist/tools/analyze-files.d.ts.map +1 -1
- package/dist/tools/analyze-files.js +75 -71
- package/dist/tools/analyze-files.js.map +1 -1
- package/dist/tools/apply-all-quick-fixes.d.ts.map +1 -1
- package/dist/tools/apply-all-quick-fixes.js +65 -139
- package/dist/tools/apply-all-quick-fixes.js.map +1 -1
- package/dist/tools/apply-quick-fix.d.ts.map +1 -1
- package/dist/tools/apply-quick-fix.js +5 -76
- package/dist/tools/apply-quick-fix.js.map +1 -1
- package/dist/tools/health-check.d.ts.map +1 -1
- package/dist/tools/health-check.js +15 -7
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/list-active-rules.d.ts.map +1 -1
- package/dist/tools/list-active-rules.js +3 -2
- package/dist/tools/list-active-rules.js.map +1 -1
- package/dist/utils/file-registration.js +1 -1
- package/dist/utils/file-registration.js.map +1 -1
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/quick-fix.d.ts +14 -0
- package/dist/utils/quick-fix.d.ts.map +1 -0
- package/dist/utils/quick-fix.js +44 -0
- package/dist/utils/quick-fix.js.map +1 -0
- package/dist/utils/scope.d.ts +24 -3
- package/dist/utils/scope.d.ts.map +1 -1
- package/dist/utils/scope.js +46 -7
- package/dist/utils/scope.js.map +1 -1
- package/dist/utils/sloop.d.ts.map +1 -1
- package/dist/utils/sloop.js +5 -1
- package/dist/utils/sloop.js.map +1 -1
- package/package.json +1 -1
- package/dist/tools/analyze-project.d.ts +0 -7
- package/dist/tools/analyze-project.d.ts.map +0 -1
- package/dist/tools/analyze-project.js +0 -109
- package/dist/tools/analyze-project.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
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.
|
|
4
4
|
|
|
5
|
+
### [0.4.1](https://github.com/nielspeter/sonarlint-mcp-server/compare/v0.4.0...v0.4.1) (2026-03-30)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* add basePath parameter for relative paths and globs in check_files ([3fbc1ff](https://github.com/nielspeter/sonarlint-mcp-server/commit/3fbc1ff2737c12b174818bc9ba31cd38c79ba695))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* resolve 15 sonarlint issues across codebase ([be91de6](https://github.com/nielspeter/sonarlint-mcp-server/commit/be91de6d3da6b7c38e2a7720e5285f57137f3d81))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Code Refactoring
|
|
19
|
+
|
|
20
|
+
* reduce cognitive complexity to zero sonarlint issues ([213aad6](https://github.com/nielspeter/sonarlint-mcp-server/commit/213aad634427b302c31d23ab2f9dbaa7ebe1f138))
|
|
21
|
+
|
|
22
|
+
## [0.4.0](https://github.com/nielspeter/sonarlint-mcp-server/compare/v0.3.1...v0.4.0) (2026-03-30)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* resolve SLOOP analysis hang by pre-registering files before scope creation ([ee1bfb2](https://github.com/nielspeter/sonarlint-mcp-server/commit/ee1bfb24be94b72838fc1fb4e4ff17ba297a9d19))
|
|
28
|
+
|
|
5
29
|
### [0.3.1](https://github.com/nielspeter/sonarlint-mcp-server/compare/v0.3.0...v0.3.1) (2026-03-30)
|
|
6
30
|
|
|
7
31
|
|
package/README.md
CHANGED
|
@@ -97,7 +97,6 @@ function process(data) {
|
|
|
97
97
|
|------|-------------|
|
|
98
98
|
| `check_quality` | Check a file for code quality issues |
|
|
99
99
|
| `check_files` | Check multiple files in one call |
|
|
100
|
-
| `check_project` | Run a full project-wide quality scan |
|
|
101
100
|
| `check_code` | Check a code snippet (no file on disk needed) |
|
|
102
101
|
| `fix_issue` | Automatically fix one specific issue |
|
|
103
102
|
| `fix_all_issues` | Automatically fix all fixable issues in a file |
|
|
@@ -158,6 +157,20 @@ The server uses SonarLint's standalone SLOOP backend with:
|
|
|
158
157
|
- **Bi-directional RPC:** Client request handlers implemented
|
|
159
158
|
- **Session Storage:** Results stored in memory for multi-turn conversations
|
|
160
159
|
|
|
160
|
+
### SLOOP Integration: Scope Lifecycle
|
|
161
|
+
|
|
162
|
+
SLOOP requires a specific initialization sequence. Getting this wrong causes analysis to hang:
|
|
163
|
+
|
|
164
|
+
1. **Pre-register files** — Store file DTOs in `scopeFiles` map before creating the scope. SLOOP calls `listFiles` synchronously during scope creation, so files must already be available.
|
|
165
|
+
2. **Create scope** — Send `addConfigurationScope` notification to SLOOP.
|
|
166
|
+
3. **Wait for readiness** — SLOOP sends `didChangeAnalysisReadiness` when the scope is ready. Analysis requests before this point will fail silently.
|
|
167
|
+
4. **Analyse** — Call `analyzeFilesAndTrack` with the files.
|
|
168
|
+
|
|
169
|
+
Key design decisions:
|
|
170
|
+
- **No directory scanning in `listFiles`** — Only return the specific files requested for analysis. Scanning the project root returned 500+ files on real projects and caused multi-minute hangs.
|
|
171
|
+
- **`getBaseDir` returns project root** — Detected via `package.json`, `.git`, etc. SLOOP uses this for `.gitignore` matching and file exclusion patterns.
|
|
172
|
+
- **`ideRelativePath` relative to project root** — SLOOP's `WildcardPattern.match` requires this; null values cause NPEs.
|
|
173
|
+
|
|
161
174
|
## Development
|
|
162
175
|
|
|
163
176
|
```bash
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IAG1B,WAAW,EAAE,MAAM;IACnB,WAAW,EAAE,OAAO;gBAF3B,OAAO,EAAE,MAAM,EACR,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,OAAe;CAKtC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO;;;;;;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IAG1B,WAAW,EAAE,MAAM;IACnB,WAAW,EAAE,OAAO;gBAF3B,OAAO,EAAE,MAAM,EACR,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,OAAe;CAKtC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO;;;;;;EA6B7C"}
|
package/dist/errors.js
CHANGED
|
@@ -30,7 +30,13 @@ export function handleToolError(error) {
|
|
|
30
30
|
isError: true,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
let errorMessage;
|
|
34
|
+
if (error instanceof Error)
|
|
35
|
+
errorMessage = error.message;
|
|
36
|
+
else if (typeof error === 'string')
|
|
37
|
+
errorMessage = error;
|
|
38
|
+
else
|
|
39
|
+
errorMessage = JSON.stringify(error);
|
|
34
40
|
return {
|
|
35
41
|
content: [
|
|
36
42
|
{
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IAG1B;IACA;IAHT,YACE,OAAe,EACR,WAAmB,EACnB,cAAuB,KAAK;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,gBAAW,GAAX,WAAW,CAAQ;QACnB,gBAAW,GAAX,WAAW,CAAiB;QAGnC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;IAExD,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;QAChC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,gBAAgB,KAAK,CAAC,WAAW,EAAE;iBAC1C;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IAG1B;IACA;IAHT,YACE,OAAe,EACR,WAAmB,EACnB,cAAuB,KAAK;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,gBAAW,GAAX,WAAW,CAAQ;QACnB,gBAAW,GAAX,WAAW,CAAiB;QAGnC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;IAExD,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;QAChC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,gBAAgB,KAAK,CAAC,WAAW,EAAE;iBAC1C;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,IAAI,YAAoB,CAAC;IACzB,IAAI,KAAK,YAAY,KAAK;QAAE,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;SACpD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,YAAY,GAAG,KAAK,CAAC;;QACpD,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAE1C,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,gBAAgB,YAAY,EAAE;aACrC;SACF;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,6 @@ import { handleAnalyzeFiles } from "./tools/analyze-files.js";
|
|
|
11
11
|
import { handleAnalyzeContent } from "./tools/analyze-content.js";
|
|
12
12
|
import { handleListActiveRules } from "./tools/list-active-rules.js";
|
|
13
13
|
import { handleHealthCheck } from "./tools/health-check.js";
|
|
14
|
-
import { handleAnalyzeProject } from "./tools/analyze-project.js";
|
|
15
14
|
import { handleApplyQuickFix } from "./tools/apply-quick-fix.js";
|
|
16
15
|
import { handleApplyAllQuickFixes } from "./tools/apply-all-quick-fixes.js";
|
|
17
16
|
import { getSloopBridge } from "./state.js";
|
|
@@ -28,7 +27,7 @@ const server = new McpServer({
|
|
|
28
27
|
});
|
|
29
28
|
// Register tool: check_quality
|
|
30
29
|
server.registerTool('check_quality', {
|
|
31
|
-
description: "Check a file for code quality issues — bugs, code smells, security vulnerabilities, and complexity problems. Like having SonarLint in your IDE. Use after writing or modifying code to catch issues early. Returns issues with exact line numbers, severity, and available quick fixes. For multiple files use check_files
|
|
30
|
+
description: "Check a file for code quality issues — bugs, code smells, security vulnerabilities, and complexity problems. Like having SonarLint in your IDE. Use after writing or modifying code to catch issues early. Returns issues with exact line numbers, severity, and available quick fixes. For multiple files use check_files.",
|
|
32
31
|
inputSchema: {
|
|
33
32
|
filePath: z.string().describe("Absolute path to the file to analyze (e.g., /path/to/file.js)"),
|
|
34
33
|
minSeverity: z.enum(["INFO", "MINOR", "MAJOR", "CRITICAL", "BLOCKER"]).optional().describe("Minimum severity level to include. Filters out issues below this level. Default: INFO (show all)"),
|
|
@@ -44,9 +43,10 @@ server.registerTool('check_quality', {
|
|
|
44
43
|
});
|
|
45
44
|
// Register tool: check_files
|
|
46
45
|
server.registerTool('check_files', {
|
|
47
|
-
description: "Check multiple files for code quality issues in one call — bugs, code smells, security vulnerabilities. Use when reviewing or modifying several files. Output is compact: only files with issues are shown, clean files get a summary count. For a single file use check_quality
|
|
46
|
+
description: "Check multiple files for code quality issues in one call — bugs, code smells, security vulnerabilities. Use when reviewing or modifying several files. Supports glob patterns (e.g. 'src/**/*.ts'). When using relative paths or globs, provide basePath so they resolve correctly. Output is compact: only files with issues are shown, clean files get a summary count. For a single file use check_quality.",
|
|
48
47
|
inputSchema: {
|
|
49
|
-
filePaths: stringArray.describe("Array of
|
|
48
|
+
filePaths: stringArray.describe("Array of file paths or glob patterns to analyze (e.g., ['/path/to/file.ts', 'src/**/*.js'])"),
|
|
49
|
+
basePath: z.string().optional().describe("Project root directory for resolving relative paths and globs (e.g., '/Users/me/project'). Required when filePaths contains relative paths."),
|
|
50
50
|
groupByFile: z.boolean().optional().default(true).describe("Group issues by file in output (default: true)"),
|
|
51
51
|
minSeverity: z.enum(["INFO", "MINOR", "MAJOR", "CRITICAL", "BLOCKER"]).optional().describe("Minimum severity level to include. Filters out issues below this level. Default: INFO (show all)"),
|
|
52
52
|
excludeRules: stringArray.optional().describe("List of rule IDs to exclude (e.g., ['typescript:S1135', 'javascript:S125'])"),
|
|
@@ -101,24 +101,6 @@ server.registerTool('health_check', {
|
|
|
101
101
|
return handleToolError(error);
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
|
-
// Register tool: check_project
|
|
105
|
-
server.registerTool('check_project', {
|
|
106
|
-
description: "Run a full code quality scan on a project directory — finds bugs, code smells, security vulnerabilities, and complexity issues across all source files. Use for project-wide quality assessment or before a release. Output is compact: only files with issues are shown, clean files get a summary count.",
|
|
107
|
-
inputSchema: {
|
|
108
|
-
projectPath: z.string().describe("Absolute path to the project directory to scan"),
|
|
109
|
-
maxFiles: z.number().optional().default(100).describe("Maximum number of files to analyze (default: 100, prevents overwhelming output)"),
|
|
110
|
-
minSeverity: z.enum(["INFO", "MINOR", "MAJOR", "CRITICAL", "BLOCKER"]).optional().describe("Minimum severity level to include. Filters out issues below this level. Default: INFO (show all)"),
|
|
111
|
-
excludeRules: stringArray.optional().describe("List of rule IDs to exclude (e.g., ['typescript:S1135', 'javascript:S125'])"),
|
|
112
|
-
includePatterns: stringArray.optional().describe("File glob patterns to include (e.g., ['src/**/*.ts', 'lib/**/*.js']). Default: all supported extensions"),
|
|
113
|
-
},
|
|
114
|
-
}, async (args) => {
|
|
115
|
-
try {
|
|
116
|
-
return await handleAnalyzeProject(args);
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
return handleToolError(error);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
104
|
// Register tool: fix_issue
|
|
123
105
|
server.registerTool('fix_issue', {
|
|
124
106
|
description: "Automatically fix one specific code quality issue. Applies the SonarLint-suggested fix for the issue at the given file, line, and rule. The file is modified directly. To fix all issues in a file at once, use fix_all_issues instead.",
|
|
@@ -173,7 +155,7 @@ async function main() {
|
|
|
173
155
|
console.error("[MCP] Starting SonarLint MCP Server...");
|
|
174
156
|
console.error(`[MCP] Version: ${packageJson.version}`);
|
|
175
157
|
console.error("[MCP] Mode: Standalone (no IDE required)");
|
|
176
|
-
console.error("[MCP] Tools: check_quality, check_files, check_code,
|
|
158
|
+
console.error("[MCP] Tools: check_quality, check_files, check_code, list_rules, fix_issue, fix_all_issues, health_check");
|
|
177
159
|
console.error("[MCP] Features:");
|
|
178
160
|
console.error("[MCP] - Session storage for multi-turn conversations");
|
|
179
161
|
console.error("[MCP] - Batch analysis for multiple files");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,oEAAoE;AACpE,MAAM,WAAW,GAAG,CAAC,CAAC,UAAU,CAC9B,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EACxD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CACpB,CAAC;AACF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/sloop-bridge.d.ts
CHANGED
|
@@ -22,7 +22,11 @@ export declare class SloopBridge extends EventEmitter {
|
|
|
22
22
|
private setupMessageHandlers;
|
|
23
23
|
private processMessages;
|
|
24
24
|
private handleMessage;
|
|
25
|
-
private
|
|
25
|
+
private handleResponse;
|
|
26
|
+
private handleNotification;
|
|
27
|
+
private onRaiseIssues;
|
|
28
|
+
private onRaiseHotspots;
|
|
29
|
+
private onAnalysisReadiness;
|
|
26
30
|
private handleClientRequest;
|
|
27
31
|
private sendResponse;
|
|
28
32
|
sendRequest(method: string, params?: any): Promise<any>;
|
|
@@ -37,6 +41,11 @@ export declare class SloopBridge extends EventEmitter {
|
|
|
37
41
|
* Returns a map of rule key -> rule definition.
|
|
38
42
|
*/
|
|
39
43
|
listAllStandaloneRulesDefinitions(): Promise<any>;
|
|
44
|
+
/**
|
|
45
|
+
* Wait for a scope to become ready for analysis.
|
|
46
|
+
* SLOOP emits didChangeAnalysisReadiness after processing addConfigurationScope.
|
|
47
|
+
*/
|
|
48
|
+
waitForScopeReady(scopeId: string, timeoutMs?: number): Promise<void>;
|
|
40
49
|
addConfigurationScope(scopeId: string, params?: {
|
|
41
50
|
name?: string;
|
|
42
51
|
parentId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sloop-bridge.d.ts","sourceRoot":"","sources":["../src/sloop-bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"sloop-bridge.d.ts","sourceRoot":"","sources":["../src/sloop-bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAQtC,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAI3B;IACL,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsF;IAC7G,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;gBAE3C,WAAW,CAAC,EAAE,MAAM,EAAE,MAAM,GAAE,WAAgB;IAwB1D,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,gBAAgB;IAUlB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwE9B,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,mBAAmB;IAqD3B,OAAO,CAAC,YAAY;IAgBd,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAyC7D,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,IAAI;IAuBpD,OAAO,CAAC,cAAc;YAeR,UAAU;IAoFxB,OAAO,CAAC,gBAAgB;IAQxB,WAAW,IAAI,OAAO;IAIhB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC;;;OAGG;IACG,iCAAiC,IAAI,OAAO,CAAC,GAAG,CAAC;IAIvD;;;OAGG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBpE,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;IAYzF,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;CA4DrF"}
|
package/dist/sloop-bridge.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
3
|
import { tmpdir } from 'os';
|
|
4
|
-
import { join
|
|
5
|
-
import { existsSync, mkdirSync, readdirSync
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync } from 'fs';
|
|
6
6
|
import { IssueCollector } from './utils/issue-collector.js';
|
|
7
7
|
import { buildClientFileDtos } from './utils/file-registration.js';
|
|
8
|
+
import { scopeToProjectRoot, scopeFiles } from './state.js';
|
|
8
9
|
export class SloopBridge extends EventEmitter {
|
|
9
10
|
process = null;
|
|
10
11
|
connected = false;
|
|
@@ -116,7 +117,7 @@ export class SloopBridge extends EventEmitter {
|
|
|
116
117
|
});
|
|
117
118
|
}
|
|
118
119
|
setupMessageHandlers() {
|
|
119
|
-
if (!this.process
|
|
120
|
+
if (!this.process?.stdout)
|
|
120
121
|
return;
|
|
121
122
|
this.process.stdout.on('data', (data) => {
|
|
122
123
|
this.messageBuffer += data.toString();
|
|
@@ -132,7 +133,7 @@ export class SloopBridge extends EventEmitter {
|
|
|
132
133
|
processMessages() {
|
|
133
134
|
while (true) {
|
|
134
135
|
// Look for Content-Length header
|
|
135
|
-
const headerMatch =
|
|
136
|
+
const headerMatch = /Content-Length: (\d+)\r?\n\r?\n/.exec(this.messageBuffer);
|
|
136
137
|
if (!headerMatch)
|
|
137
138
|
break;
|
|
138
139
|
const contentLength = parseInt(headerMatch[1]);
|
|
@@ -157,179 +158,96 @@ export class SloopBridge extends EventEmitter {
|
|
|
157
158
|
const timestamp = new Date().toISOString();
|
|
158
159
|
console.error(`[DEBUG ${timestamp}] Received message:`, JSON.stringify(message, null, 2).substring(0, 1000));
|
|
159
160
|
}
|
|
160
|
-
// Handle requests FROM SLOOP (client RPC methods) - check method field FIRST
|
|
161
161
|
// Requests have both id AND method, responses have id but no method
|
|
162
162
|
if (message.id && message.method) {
|
|
163
163
|
this.handleClientRequest(message);
|
|
164
|
-
return;
|
|
165
164
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
pending.reject(new Error(message.error.message || 'RPC Error'));
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
console.error('[DEBUG] Resolving request', message.id, 'with result');
|
|
179
|
-
pending.resolve(message.result);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
165
|
+
else if (message.id && !message.method) {
|
|
166
|
+
this.handleResponse(message);
|
|
167
|
+
}
|
|
168
|
+
else if (!message.id && message.method) {
|
|
169
|
+
this.handleNotification(message);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
handleResponse(message) {
|
|
173
|
+
if (!this.pendingRequests.has(message.id))
|
|
182
174
|
return;
|
|
175
|
+
const pending = this.pendingRequests.get(message.id);
|
|
176
|
+
clearTimeout(pending.timeout);
|
|
177
|
+
this.pendingRequests.delete(message.id);
|
|
178
|
+
if (message.error) {
|
|
179
|
+
console.error('Full RPC Error:', JSON.stringify(message.error, null, 2).substring(0, 2000));
|
|
180
|
+
pending.reject(new Error(message.error.message || 'RPC Error'));
|
|
183
181
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (message.method === 'log') {
|
|
188
|
-
this.emit('log', message.params);
|
|
189
|
-
}
|
|
190
|
-
else if (message.method === 'raiseIssues' && message.params) {
|
|
191
|
-
const { analysisId, issuesByFileUri } = message.params;
|
|
192
|
-
if (analysisId && issuesByFileUri) {
|
|
193
|
-
this.issueCollector.addIssues(analysisId, issuesByFileUri);
|
|
194
|
-
const count = Object.values(issuesByFileUri).reduce((sum, arr) => sum + arr.length, 0);
|
|
195
|
-
console.error(`[SLOOP] Received raiseIssues: ${count} issues for analysis ${analysisId}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
else if (message.method === 'raiseHotspots' && message.params) {
|
|
199
|
-
const { analysisId, hotspotsByFileUri } = message.params;
|
|
200
|
-
if (analysisId && hotspotsByFileUri) {
|
|
201
|
-
this.issueCollector.addHotspots(analysisId, hotspotsByFileUri);
|
|
202
|
-
const count = Object.values(hotspotsByFileUri).reduce((sum, arr) => sum + arr.length, 0);
|
|
203
|
-
console.error(`[SLOOP] Received raiseHotspots: ${count} hotspots for analysis ${analysisId}`);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
182
|
+
else {
|
|
183
|
+
console.error('[DEBUG] Resolving request', message.id, 'with result');
|
|
184
|
+
pending.resolve(message.result);
|
|
206
185
|
}
|
|
207
186
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
files.push(...this.findFilesInDirectory(fullPath, baseDir));
|
|
225
|
-
}
|
|
226
|
-
else if (stat.isFile()) {
|
|
227
|
-
// Only include source files
|
|
228
|
-
if (/\.(js|ts|py|java|html|css|php|go|rb)$/.test(item)) {
|
|
229
|
-
files.push(`file://${fullPath}`);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
187
|
+
handleNotification(message) {
|
|
188
|
+
this.emit('notification', message);
|
|
189
|
+
const { method, params } = message;
|
|
190
|
+
switch (method) {
|
|
191
|
+
case 'log':
|
|
192
|
+
this.emit('log', params);
|
|
193
|
+
break;
|
|
194
|
+
case 'raiseIssues':
|
|
195
|
+
this.onRaiseIssues(params);
|
|
196
|
+
break;
|
|
197
|
+
case 'raiseHotspots':
|
|
198
|
+
this.onRaiseHotspots(params);
|
|
199
|
+
break;
|
|
200
|
+
case 'didChangeAnalysisReadiness':
|
|
201
|
+
this.onAnalysisReadiness(params);
|
|
202
|
+
break;
|
|
233
203
|
}
|
|
234
|
-
|
|
235
|
-
|
|
204
|
+
}
|
|
205
|
+
onRaiseIssues(params) {
|
|
206
|
+
const { analysisId, issuesByFileUri } = params ?? {};
|
|
207
|
+
if (!analysisId || !issuesByFileUri)
|
|
208
|
+
return;
|
|
209
|
+
this.issueCollector.addIssues(analysisId, issuesByFileUri);
|
|
210
|
+
const count = Object.values(issuesByFileUri).reduce((sum, arr) => sum + arr.length, 0);
|
|
211
|
+
console.error(`[SLOOP] Received raiseIssues: ${count} issues for analysis ${analysisId}`);
|
|
212
|
+
}
|
|
213
|
+
onRaiseHotspots(params) {
|
|
214
|
+
const { analysisId, hotspotsByFileUri } = params ?? {};
|
|
215
|
+
if (!analysisId || !hotspotsByFileUri)
|
|
216
|
+
return;
|
|
217
|
+
this.issueCollector.addHotspots(analysisId, hotspotsByFileUri);
|
|
218
|
+
const count = Object.values(hotspotsByFileUri).reduce((sum, arr) => sum + arr.length, 0);
|
|
219
|
+
console.error(`[SLOOP] Received raiseHotspots: ${count} hotspots for analysis ${analysisId}`);
|
|
220
|
+
}
|
|
221
|
+
onAnalysisReadiness(params) {
|
|
222
|
+
const { configurationScopeIds, areReadyForAnalysis } = params ?? {};
|
|
223
|
+
console.error(`[SLOOP] Analysis readiness changed: scopes=${configurationScopeIds}, ready=${areReadyForAnalysis}`);
|
|
224
|
+
if (!areReadyForAnalysis)
|
|
225
|
+
return;
|
|
226
|
+
for (const scopeId of configurationScopeIds) {
|
|
227
|
+
this.emit('scopeReady', scopeId);
|
|
236
228
|
}
|
|
237
|
-
return files;
|
|
238
229
|
}
|
|
239
230
|
handleClientRequest(request) {
|
|
240
231
|
console.error(`[DEBUG] Handling client request: ${request.method}`);
|
|
241
|
-
//
|
|
232
|
+
// listFiles — return only the files pre-registered in scopeFiles.
|
|
233
|
+
// IMPORTANT: Do NOT scan the project directory here. SLOOP calls listFiles
|
|
234
|
+
// during addConfigurationScope, so only the files the caller asked to analyse
|
|
235
|
+
// should be returned. Scanning caused 500+ file responses and hangs on real
|
|
236
|
+
// projects. Files are pre-registered in getOrCreateScope() before the scope
|
|
237
|
+
// is created. See scope.ts for the full sequencing explanation.
|
|
242
238
|
if (request.method === 'listFiles') {
|
|
243
239
|
const configScopeId = request.params?.configScopeId;
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
// Helper to detect language from extension
|
|
247
|
-
const detectLanguage = (filePath) => {
|
|
248
|
-
if (filePath.endsWith('.js'))
|
|
249
|
-
return 'JS';
|
|
250
|
-
if (filePath.endsWith('.ts'))
|
|
251
|
-
return 'TS';
|
|
252
|
-
if (filePath.endsWith('.py'))
|
|
253
|
-
return 'PYTHON';
|
|
254
|
-
if (filePath.endsWith('.java'))
|
|
255
|
-
return 'JAVA';
|
|
256
|
-
if (filePath.endsWith('.html'))
|
|
257
|
-
return 'HTML';
|
|
258
|
-
if (filePath.endsWith('.css'))
|
|
259
|
-
return 'CSS';
|
|
260
|
-
return null;
|
|
261
|
-
};
|
|
262
|
-
// Scan root directory for source files
|
|
263
|
-
try {
|
|
264
|
-
const rootItems = readdirSync(baseDir);
|
|
265
|
-
for (const item of rootItems) {
|
|
266
|
-
const fullPath = join(baseDir, item);
|
|
267
|
-
try {
|
|
268
|
-
const stat = statSync(fullPath);
|
|
269
|
-
if (stat.isFile() && /\.(js|ts|py|java|html|css|php|go|rb)$/.test(item)) {
|
|
270
|
-
const content = readFileSync(fullPath, 'utf-8');
|
|
271
|
-
const relativePath = relative(baseDir, fullPath);
|
|
272
|
-
const language = detectLanguage(fullPath);
|
|
273
|
-
// Debug: Log first 200 chars of content for test files
|
|
274
|
-
if (item === 'test-simple.js' || item === 'test-python.py') {
|
|
275
|
-
console.error(`[DEBUG listFiles] ${item}: ${content.substring(0, 200)}`);
|
|
276
|
-
}
|
|
277
|
-
fileDtos.push({
|
|
278
|
-
uri: `file://${fullPath}`,
|
|
279
|
-
fsPath: fullPath,
|
|
280
|
-
ideRelativePath: relativePath,
|
|
281
|
-
configScopeId: configScopeId,
|
|
282
|
-
isTest: false,
|
|
283
|
-
charset: 'UTF-8',
|
|
284
|
-
content: content,
|
|
285
|
-
detectedLanguage: language,
|
|
286
|
-
isUserDefined: true // CRITICAL: Must be true for SLOOP to analyze!
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
else if (stat.isDirectory() && item === 'src') {
|
|
290
|
-
// Only scan src directory recursively
|
|
291
|
-
const srcFiles = this.findFilesInDirectory(fullPath);
|
|
292
|
-
for (const fileUri of srcFiles) {
|
|
293
|
-
const filePath = fileUri.replace('file://', '');
|
|
294
|
-
try {
|
|
295
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
296
|
-
const relativePath = relative(baseDir, filePath);
|
|
297
|
-
const language = detectLanguage(filePath);
|
|
298
|
-
fileDtos.push({
|
|
299
|
-
uri: fileUri,
|
|
300
|
-
fsPath: filePath,
|
|
301
|
-
ideRelativePath: relativePath,
|
|
302
|
-
configScopeId: configScopeId,
|
|
303
|
-
isTest: relativePath.includes('test'),
|
|
304
|
-
charset: 'UTF-8',
|
|
305
|
-
content: content,
|
|
306
|
-
detectedLanguage: language,
|
|
307
|
-
isUserDefined: true // CRITICAL: Must be true for SLOOP to analyze!
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
catch (readErr) {
|
|
311
|
-
console.error(`Could not read file ${filePath}:`, readErr);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
catch (e) {
|
|
317
|
-
// Skip files we can't access
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
catch (err) {
|
|
322
|
-
console.error('Error listing files:', err);
|
|
323
|
-
}
|
|
324
|
-
console.error(`[DEBUG] Returning ${fileDtos.length} ClientFileDto objects for listFiles`);
|
|
240
|
+
const fileDtos = scopeFiles.get(configScopeId) || [];
|
|
241
|
+
console.error(`[DEBUG] listFiles for scope ${configScopeId}: returning ${fileDtos.length} registered files`);
|
|
325
242
|
this.sendResponse(request.id, { files: fileDtos });
|
|
326
243
|
return;
|
|
327
244
|
}
|
|
328
245
|
// Implement getBaseDir - SLOOP needs the base directory for the config scope
|
|
329
246
|
if (request.method === 'getBaseDir') {
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
247
|
+
const configScopeId = request.params?.configurationScopeId ?? request.params?.configScopeId;
|
|
248
|
+
const projectRoot = scopeToProjectRoot.get(configScopeId) || process.cwd();
|
|
249
|
+
console.error(`[DEBUG] getBaseDir for scope ${configScopeId}: ${projectRoot}`);
|
|
250
|
+
this.sendResponse(request.id, { path: projectRoot });
|
|
333
251
|
return;
|
|
334
252
|
}
|
|
335
253
|
// Implement getFileExclusions - file patterns to exclude from analysis
|
|
@@ -526,8 +444,8 @@ export class SloopBridge extends EventEmitter {
|
|
|
526
444
|
try {
|
|
527
445
|
await this.sendRequest('shutdown');
|
|
528
446
|
}
|
|
529
|
-
catch
|
|
530
|
-
|
|
447
|
+
catch {
|
|
448
|
+
console.error('[SLOOP] Shutdown request failed (process may already be exiting)');
|
|
531
449
|
}
|
|
532
450
|
this.process.kill();
|
|
533
451
|
this.process = null;
|
|
@@ -542,6 +460,26 @@ export class SloopBridge extends EventEmitter {
|
|
|
542
460
|
async listAllStandaloneRulesDefinitions() {
|
|
543
461
|
return this.sendRequest('rules/listAllStandaloneRulesDefinitions');
|
|
544
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* Wait for a scope to become ready for analysis.
|
|
465
|
+
* SLOOP emits didChangeAnalysisReadiness after processing addConfigurationScope.
|
|
466
|
+
*/
|
|
467
|
+
waitForScopeReady(scopeId, timeoutMs = 30000) {
|
|
468
|
+
return new Promise((resolve, reject) => {
|
|
469
|
+
const timer = setTimeout(() => {
|
|
470
|
+
this.removeListener('scopeReady', onReady);
|
|
471
|
+
reject(new Error(`Timeout waiting for scope ${scopeId} to become ready`));
|
|
472
|
+
}, timeoutMs);
|
|
473
|
+
const onReady = (readyScopeId) => {
|
|
474
|
+
if (readyScopeId === scopeId) {
|
|
475
|
+
clearTimeout(timer);
|
|
476
|
+
this.removeListener('scopeReady', onReady);
|
|
477
|
+
resolve();
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
this.on('scopeReady', onReady);
|
|
481
|
+
});
|
|
482
|
+
}
|
|
545
483
|
addConfigurationScope(scopeId, params = {}) {
|
|
546
484
|
this.sendNotification('configuration/didAddConfigurationScopes', {
|
|
547
485
|
addedScopes: [{
|
|
@@ -566,7 +504,11 @@ export class SloopBridge extends EventEmitter {
|
|
|
566
504
|
console.error(`[ANALYSIS] Analysis ID: ${analysisId}`);
|
|
567
505
|
const startTime = Date.now();
|
|
568
506
|
// Register files in SLOOP's virtual file system before analysis
|
|
569
|
-
const
|
|
507
|
+
const projectRoot = scopeToProjectRoot.get(configScopeId);
|
|
508
|
+
const fileDtos = buildClientFileDtos(filePaths, configScopeId, projectRoot);
|
|
509
|
+
// Store so listFiles callback returns only these files, not a full directory scan
|
|
510
|
+
const existing = scopeFiles.get(configScopeId) || [];
|
|
511
|
+
scopeFiles.set(configScopeId, [...existing, ...fileDtos]);
|
|
570
512
|
this.sendNotification('file/didUpdateFileSystem', {
|
|
571
513
|
addedFiles: fileDtos,
|
|
572
514
|
changedFiles: [],
|