@nielspeter/sonarlint-mcp-server 0.4.0 → 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 +17 -0
- 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 +2 -1
- package/dist/index.js.map +1 -1
- package/dist/sloop-bridge.d.ts +5 -0
- package/dist/sloop-bridge.d.ts.map +1 -1
- package/dist/sloop-bridge.js +65 -53
- package/dist/sloop-bridge.js.map +1 -1
- package/dist/state.d.ts +0 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +2 -2
- package/dist/state.js.map +1 -1
- package/dist/tools/analyze-file.d.ts.map +1 -1
- package/dist/tools/analyze-file.js +3 -5
- 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 +58 -83
- 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 +64 -138
- 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 +4 -75
- 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 +14 -6
- 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/package.json +1 -1
|
@@ -10,45 +10,42 @@ import { getOrCreateScope } from "../utils/scope.js";
|
|
|
10
10
|
import { detectLanguage } from "../utils/language.js";
|
|
11
11
|
import { transformSloopIssues } from "../utils/transforms.js";
|
|
12
12
|
import { formatBatchAnalysisResult } from "../utils/formatting.js";
|
|
13
|
+
import { filterBySeverity } from "../utils/quick-fix.js";
|
|
13
14
|
import { batchResults } from "../state.js";
|
|
14
15
|
/**
|
|
15
|
-
* Expand glob patterns
|
|
16
|
-
*
|
|
16
|
+
* Expand glob patterns and resolve relative paths against basePath.
|
|
17
|
+
* Absolute paths/globs are used as-is; relative ones need basePath.
|
|
17
18
|
*/
|
|
18
|
-
function expandGlobs(paths) {
|
|
19
|
+
function expandGlobs(paths, basePath) {
|
|
19
20
|
const result = [];
|
|
20
21
|
for (const p of paths) {
|
|
22
|
+
const isRelative = !p.startsWith('/');
|
|
23
|
+
if (isRelative && !basePath) {
|
|
24
|
+
throw new SloopError(`Relative path requires basePath: ${p}`, `Relative paths need a basePath to resolve against. Provide basePath (the project root) or use absolute paths.`, false);
|
|
25
|
+
}
|
|
21
26
|
if (p.includes('*') || p.includes('?')) {
|
|
22
|
-
const
|
|
23
|
-
|
|
27
|
+
const cwd = isRelative ? basePath : process.cwd();
|
|
28
|
+
const matches = globSync(p, { cwd });
|
|
29
|
+
result.push(...matches.map((m) => resolve(cwd, m)));
|
|
24
30
|
}
|
|
25
31
|
else {
|
|
26
|
-
result.push(p);
|
|
32
|
+
result.push(isRelative ? resolve(basePath, p) : p);
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
|
-
// Deduplicate (multiple globs could match the same file)
|
|
30
35
|
return [...new Set(result)];
|
|
31
36
|
}
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
if (!Array.isArray(rawPaths) || rawPaths.length === 0) {
|
|
35
|
-
throw new SloopError("No files provided", "Please provide at least one file path or glob pattern to analyze.", false);
|
|
36
|
-
}
|
|
37
|
-
// Expand any glob patterns (e.g. "src/**/*.ts") into concrete file paths
|
|
38
|
-
const filePaths = expandGlobs(rawPaths);
|
|
37
|
+
function resolveAndValidatePaths(rawPaths, basePath) {
|
|
38
|
+
const filePaths = expandGlobs(rawPaths, basePath);
|
|
39
39
|
if (filePaths.length === 0) {
|
|
40
|
-
throw new SloopError("No files matched",
|
|
40
|
+
throw new SloopError("No files matched", "No files matched the provided patterns:\n" + rawPaths.map(p => `- ${p}`).join('\n'), false);
|
|
41
41
|
}
|
|
42
|
-
// Validate all resolved files exist
|
|
43
42
|
const missingFiles = filePaths.filter(fp => !existsSync(fp));
|
|
44
43
|
if (missingFiles.length > 0) {
|
|
45
|
-
throw new SloopError(`Files not found: ${missingFiles.join(', ')}`,
|
|
44
|
+
throw new SloopError(`Files not found: ${missingFiles.join(', ')}`, "The following files do not exist:\n" + missingFiles.map(f => `- ${f}`).join('\n'), false);
|
|
46
45
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Group files by project root for scope management
|
|
51
|
-
// Pass all files to getOrCreateScope so they're pre-registered for listFiles
|
|
46
|
+
return filePaths;
|
|
47
|
+
}
|
|
48
|
+
async function groupByScope(filePaths) {
|
|
52
49
|
const filesByScope = new Map();
|
|
53
50
|
for (const filePath of filePaths) {
|
|
54
51
|
const scopeId = await getOrCreateScope(filePath, filePaths);
|
|
@@ -57,79 +54,57 @@ export async function handleAnalyzeFiles(args) {
|
|
|
57
54
|
}
|
|
58
55
|
filesByScope.get(scopeId).push(filePath);
|
|
59
56
|
}
|
|
60
|
-
|
|
57
|
+
return filesByScope;
|
|
58
|
+
}
|
|
59
|
+
function buildSummary(allResults) {
|
|
60
|
+
const bySeverity = { blocker: 0, critical: 0, major: 0, minor: 0, info: 0 };
|
|
61
|
+
for (const result of allResults) {
|
|
62
|
+
for (const issue of result.issues) {
|
|
63
|
+
const key = issue.severity.toLowerCase();
|
|
64
|
+
if (key in bySeverity)
|
|
65
|
+
bySeverity[key]++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
totalFiles: allResults.length,
|
|
70
|
+
totalIssues: allResults.reduce((sum, r) => sum + r.issueCount, 0),
|
|
71
|
+
filesWithIssues: allResults.filter(r => r.issueCount > 0).length,
|
|
72
|
+
bySeverity,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export async function handleAnalyzeFiles(args) {
|
|
76
|
+
const { filePaths: rawPaths, basePath, minSeverity, excludeRules } = args;
|
|
77
|
+
if (!Array.isArray(rawPaths) || rawPaths.length === 0) {
|
|
78
|
+
throw new SloopError("No files provided", "Please provide at least one file path or glob pattern to analyze.", false);
|
|
79
|
+
}
|
|
80
|
+
const filePaths = resolveAndValidatePaths(rawPaths, basePath);
|
|
81
|
+
console.error(`[MCP] Batch analyzing ${filePaths.length} files...`);
|
|
82
|
+
const bridge = await ensureSloopBridge();
|
|
83
|
+
const filesByScope = await groupByScope(filePaths);
|
|
61
84
|
const allResults = [];
|
|
62
85
|
for (const [scopeId, scopeFiles] of filesByScope) {
|
|
63
86
|
console.error(`[MCP] Analyzing ${scopeFiles.length} files in scope ${scopeId}`);
|
|
64
87
|
const rawResult = await bridge.analyzeFilesAndTrack(scopeId, scopeFiles);
|
|
65
88
|
const rawIssues = rawResult.raisedIssues?.length ? rawResult.raisedIssues : (rawResult.rawIssues || []);
|
|
66
|
-
// Group issues by file
|
|
67
89
|
const issuesByFile = new Map();
|
|
68
90
|
for (const issue of rawIssues) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
issuesByFile.get(fileUri).push(issue);
|
|
91
|
+
const arr = issuesByFile.get(issue.fileUri) || [];
|
|
92
|
+
arr.push(issue);
|
|
93
|
+
issuesByFile.set(issue.fileUri, arr);
|
|
74
94
|
}
|
|
75
|
-
// Create results for each file
|
|
76
95
|
for (const filePath of scopeFiles) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const minLevel = severityOrder[minSeverity];
|
|
84
|
-
transformedIssues = transformedIssues.filter(issue => (severityOrder[issue.severity] || 0) >= minLevel);
|
|
85
|
-
}
|
|
86
|
-
if (excludeRules && excludeRules.length > 0) {
|
|
87
|
-
transformedIssues = transformedIssues.filter(issue => !excludeRules.includes(issue.rule));
|
|
88
|
-
}
|
|
89
|
-
allResults.push({
|
|
90
|
-
filePath,
|
|
91
|
-
language: detectLanguage(filePath),
|
|
92
|
-
issueCount: transformedIssues.length,
|
|
93
|
-
issues: transformedIssues,
|
|
94
|
-
});
|
|
96
|
+
let issues = transformSloopIssues(issuesByFile.get(`file://${filePath}`) || []);
|
|
97
|
+
if (minSeverity)
|
|
98
|
+
issues = filterBySeverity(issues, minSeverity);
|
|
99
|
+
if (excludeRules?.length)
|
|
100
|
+
issues = issues.filter(i => !excludeRules.includes(i.rule));
|
|
101
|
+
allResults.push({ filePath, language: detectLanguage(filePath), issueCount: issues.length, issues });
|
|
95
102
|
}
|
|
96
103
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
totalFiles: allResults.length,
|
|
100
|
-
totalIssues: allResults.reduce((sum, r) => sum + r.issueCount, 0),
|
|
101
|
-
filesWithIssues: allResults.filter(r => r.issueCount > 0).length,
|
|
102
|
-
bySeverity: {
|
|
103
|
-
blocker: 0,
|
|
104
|
-
critical: 0,
|
|
105
|
-
major: 0,
|
|
106
|
-
minor: 0,
|
|
107
|
-
info: 0,
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
for (const result of allResults) {
|
|
111
|
-
for (const issue of result.issues) {
|
|
112
|
-
const severity = issue.severity.toLowerCase();
|
|
113
|
-
if (severity in overallSummary.bySeverity) {
|
|
114
|
-
overallSummary.bySeverity[severity]++;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const batchResult = {
|
|
119
|
-
files: allResults,
|
|
120
|
-
summary: overallSummary,
|
|
121
|
-
};
|
|
122
|
-
// Store in batch results for MCP resources
|
|
123
|
-
const batchId = `batch-${Date.now()}`;
|
|
124
|
-
batchResults.set(batchId, batchResult);
|
|
125
|
-
const formattedResult = formatBatchAnalysisResult(batchResult);
|
|
104
|
+
const batchResult = { files: allResults, summary: buildSummary(allResults) };
|
|
105
|
+
batchResults.set(`batch-${Date.now()}`, batchResult);
|
|
126
106
|
return {
|
|
127
|
-
content: [
|
|
128
|
-
{
|
|
129
|
-
type: "text",
|
|
130
|
-
text: formattedResult,
|
|
131
|
-
},
|
|
132
|
-
],
|
|
107
|
+
content: [{ type: "text", text: formatBatchAnalysisResult(batchResult) }],
|
|
133
108
|
};
|
|
134
109
|
}
|
|
135
110
|
//# sourceMappingURL=analyze-files.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze-files.js","sourceRoot":"","sources":["../../src/tools/analyze-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,0EAA0E;AAC1E,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAA4E,CAAC;AAC9G,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C;;;GAGG;AACH,SAAS,WAAW,CAAC,KAAe;
|
|
1
|
+
{"version":3,"file":"analyze-files.js","sourceRoot":"","sources":["../../src/tools/analyze-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-all-quick-fixes.d.ts","sourceRoot":"","sources":["../../src/tools/apply-all-quick-fixes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apply-all-quick-fixes.d.ts","sourceRoot":"","sources":["../../src/tools/apply-all-quick-fixes.ts"],"names":[],"mappings":"AAwEA,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,GAAG;;;;;GA0CvD"}
|
|
@@ -4,163 +4,89 @@ import { ensureSloopBridge } from "../utils/sloop.js";
|
|
|
4
4
|
import { getOrCreateScope } from "../utils/scope.js";
|
|
5
5
|
import { notifyFileSystemChanged } from "../utils/filesystem.js";
|
|
6
6
|
import { transformSloopIssues } from "../utils/transforms.js";
|
|
7
|
+
import { applyTextEdits } from "../utils/quick-fix.js";
|
|
8
|
+
function applyFixesToFile(filePath, issues) {
|
|
9
|
+
const applied = [];
|
|
10
|
+
const failed = [];
|
|
11
|
+
// Sort descending by line to avoid line number shifts
|
|
12
|
+
const sorted = [...issues].sort((a, b) => {
|
|
13
|
+
return (b.textRange?.startLine || b.startLine || 0) - (a.textRange?.startLine || a.startLine || 0);
|
|
14
|
+
});
|
|
15
|
+
for (const issue of sorted) {
|
|
16
|
+
const line = issue.textRange?.startLine || issue.startLine || 0;
|
|
17
|
+
const rule = issue.ruleKey;
|
|
18
|
+
const quickFix = issue.quickFixes[0];
|
|
19
|
+
try {
|
|
20
|
+
const lines = readFileSync(filePath, 'utf-8').split('\n');
|
|
21
|
+
applyTextEdits(lines, quickFix);
|
|
22
|
+
writeFileSync(filePath, lines.join('\n'), 'utf-8');
|
|
23
|
+
applied.push({ line, rule, message: quickFix.message || 'Applied automated fix' });
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
failed.push({ line, rule, error: error instanceof Error ? error.message : String(error) });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { applied, failed };
|
|
30
|
+
}
|
|
31
|
+
function formatFixList(label, items) {
|
|
32
|
+
if (items.length === 0)
|
|
33
|
+
return '';
|
|
34
|
+
const lines = items.map(i => `- Line ${i.line}: ${i.rule} - ${i.message || i.error}`);
|
|
35
|
+
return `**${label}:**\n${lines.join('\n')}\n\n`;
|
|
36
|
+
}
|
|
37
|
+
function formatRemainingIssues(remaining) {
|
|
38
|
+
if (remaining.length === 0)
|
|
39
|
+
return `🎉 All issues resolved! The file has no remaining code quality issues.\n`;
|
|
40
|
+
let out = `**Remaining Issues (require manual fixing):**\n`;
|
|
41
|
+
for (const severity of ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']) {
|
|
42
|
+
const issues = remaining.filter(i => i.severity === severity);
|
|
43
|
+
if (issues.length === 0)
|
|
44
|
+
continue;
|
|
45
|
+
out += `\n${severity} (${issues.length}):\n`;
|
|
46
|
+
for (const issue of issues)
|
|
47
|
+
out += `- Line ${issue.line}: ${issue.rule} - ${issue.message}\n`;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function formatSummary(filePath, applied, failed, remaining) {
|
|
52
|
+
let summary = `✅ **Quick fixes applied**\n\nFile: ${filePath}\nApplied: ${applied.length} fixes\n`;
|
|
53
|
+
if (failed.length > 0)
|
|
54
|
+
summary += `Failed: ${failed.length} fixes\n`;
|
|
55
|
+
summary += `Remaining issues: ${remaining.length}\n\n`;
|
|
56
|
+
summary += formatFixList('Fixed Issues', applied);
|
|
57
|
+
summary += formatFixList('Failed Fixes', failed);
|
|
58
|
+
summary += formatRemainingIssues(remaining);
|
|
59
|
+
return summary;
|
|
60
|
+
}
|
|
7
61
|
export async function handleApplyAllQuickFixes(args) {
|
|
8
62
|
const { filePath } = args;
|
|
9
|
-
console.error(`[MCP] Applying all quick fixes for ${filePath}`);
|
|
10
|
-
// Validate file exists
|
|
11
63
|
if (!existsSync(filePath)) {
|
|
12
64
|
throw new SloopError(`File not found: ${filePath}`, `The file ${filePath} does not exist. Please check the path and try again.`, false);
|
|
13
65
|
}
|
|
14
|
-
// Analyze the file to get all issues with quick fixes
|
|
15
66
|
const bridge = await ensureSloopBridge();
|
|
16
67
|
const scopeId = await getOrCreateScope(filePath);
|
|
17
68
|
const rawResult = await bridge.analyzeFilesAndTrack(scopeId, [filePath]);
|
|
18
69
|
const rawIssues = rawResult.raisedIssues?.length ? rawResult.raisedIssues : (rawResult.rawIssues || []);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const issuesWithQuickFixes = rawIssues.filter((issue) => {
|
|
22
|
-
const hasQuickFixes = issue.quickFixes && issue.quickFixes.length > 0;
|
|
23
|
-
if (hasQuickFixes) {
|
|
24
|
-
console.error(`[DEBUG] Issue at line ${issue.textRange?.startLine || issue.startLine}: ${issue.ruleKey} has ${issue.quickFixes.length} quick fixes`);
|
|
25
|
-
}
|
|
26
|
-
return hasQuickFixes;
|
|
27
|
-
});
|
|
28
|
-
console.error(`[MCP] Found ${issuesWithQuickFixes.length} issues with quick fixes`);
|
|
29
|
-
if (issuesWithQuickFixes.length === 0) {
|
|
70
|
+
const fixableIssues = rawIssues.filter((issue) => issue.quickFixes?.length > 0);
|
|
71
|
+
if (fixableIssues.length === 0) {
|
|
30
72
|
return {
|
|
31
|
-
content: [
|
|
32
|
-
{
|
|
73
|
+
content: [{
|
|
33
74
|
type: "text",
|
|
34
75
|
text: `ℹ️ **No quick fixes available**\n\nFile: ${filePath}\nTotal issues: ${rawIssues.length}\n\nNone of the issues in this file have automated quick fixes available. All issues must be fixed manually.`,
|
|
35
|
-
},
|
|
36
|
-
],
|
|
76
|
+
}],
|
|
37
77
|
};
|
|
38
78
|
}
|
|
39
|
-
|
|
40
|
-
const sortedIssues = [...issuesWithQuickFixes].sort((a, b) => {
|
|
41
|
-
const aLine = a.textRange?.startLine || a.startLine || 0;
|
|
42
|
-
const bLine = b.textRange?.startLine || b.startLine || 0;
|
|
43
|
-
return bLine - aLine; // Descending order
|
|
44
|
-
});
|
|
45
|
-
// Apply each quick fix
|
|
46
|
-
const appliedFixes = [];
|
|
47
|
-
const failedFixes = [];
|
|
48
|
-
for (const issue of sortedIssues) {
|
|
49
|
-
const line = issue.textRange?.startLine || issue.startLine || 0;
|
|
50
|
-
const rule = issue.ruleKey;
|
|
51
|
-
const quickFix = issue.quickFixes[0]; // Use first available quick fix
|
|
52
|
-
console.error(`[MCP] Applying fix for ${rule} at line ${line}`);
|
|
53
|
-
try {
|
|
54
|
-
// Read current file content
|
|
55
|
-
let fileContent = readFileSync(filePath, 'utf-8');
|
|
56
|
-
const lines = fileContent.split('\n');
|
|
57
|
-
// Apply the quick fix edits
|
|
58
|
-
const fileEdits = quickFix.inputFileEdits || quickFix.fileEdits || [];
|
|
59
|
-
if (fileEdits.length > 0) {
|
|
60
|
-
for (const fileEdit of fileEdits) {
|
|
61
|
-
if (fileEdit.textEdits) {
|
|
62
|
-
// Sort edits in reverse order to maintain line numbers
|
|
63
|
-
const sortedEdits = [...fileEdit.textEdits].sort((a, b) => {
|
|
64
|
-
const aStart = a.range?.startLine || 0;
|
|
65
|
-
const bStart = b.range?.startLine || 0;
|
|
66
|
-
return bStart - aStart;
|
|
67
|
-
});
|
|
68
|
-
for (const edit of sortedEdits) {
|
|
69
|
-
const startLine = (edit.range?.startLine || 1) - 1;
|
|
70
|
-
const startCol = edit.range?.startLineOffset || 0;
|
|
71
|
-
const endLine = (edit.range?.endLine || startLine + 1) - 1;
|
|
72
|
-
const endCol = edit.range?.endLineOffset || lines[endLine]?.length || 0;
|
|
73
|
-
const newText = edit.newText || '';
|
|
74
|
-
if (startLine === endLine) {
|
|
75
|
-
const currentLine = lines[startLine];
|
|
76
|
-
lines[startLine] = currentLine.substring(0, startCol) + newText + currentLine.substring(endCol);
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
const firstLine = lines[startLine].substring(0, startCol) + newText;
|
|
80
|
-
const lastLine = lines[endLine].substring(endCol);
|
|
81
|
-
lines.splice(startLine, endLine - startLine + 1, firstLine + lastLine);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Write back to file
|
|
88
|
-
fileContent = lines.join('\n');
|
|
89
|
-
writeFileSync(filePath, fileContent, 'utf-8');
|
|
90
|
-
appliedFixes.push({
|
|
91
|
-
line,
|
|
92
|
-
rule,
|
|
93
|
-
message: quickFix.message || 'Applied automated fix',
|
|
94
|
-
});
|
|
95
|
-
console.error(`[MCP] Successfully applied fix for ${rule} at line ${line}`);
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
console.error(`[MCP] Failed to apply fix for ${rule} at line ${line}:`, error);
|
|
99
|
-
failedFixes.push({
|
|
100
|
-
line,
|
|
101
|
-
rule,
|
|
102
|
-
error: error instanceof Error ? error.message : String(error),
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// Notify SLOOP about file changes
|
|
107
|
-
console.error(`[Cache] Sending file system update notification...`);
|
|
79
|
+
const { applied, failed } = applyFixesToFile(filePath, fixableIssues);
|
|
108
80
|
await notifyFileSystemChanged(filePath, scopeId);
|
|
109
81
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
110
|
-
// Re-analyze to get remaining issues
|
|
111
82
|
const finalResult = await bridge.analyzeFilesAndTrack(scopeId, [filePath]);
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
// Format summary
|
|
115
|
-
let summary = `✅ **Quick fixes applied**\n\n`;
|
|
116
|
-
summary += `File: ${filePath}\n`;
|
|
117
|
-
summary += `Applied: ${appliedFixes.length} fixes\n`;
|
|
118
|
-
if (failedFixes.length > 0) {
|
|
119
|
-
summary += `Failed: ${failedFixes.length} fixes\n`;
|
|
120
|
-
}
|
|
121
|
-
summary += `Remaining issues: ${remainingIssues.length}\n\n`;
|
|
122
|
-
if (appliedFixes.length > 0) {
|
|
123
|
-
summary += `**Fixed Issues:**\n`;
|
|
124
|
-
for (const fix of appliedFixes) {
|
|
125
|
-
summary += `- Line ${fix.line}: ${fix.rule} - ${fix.message}\n`;
|
|
126
|
-
}
|
|
127
|
-
summary += `\n`;
|
|
128
|
-
}
|
|
129
|
-
if (failedFixes.length > 0) {
|
|
130
|
-
summary += `**Failed Fixes:**\n`;
|
|
131
|
-
for (const fail of failedFixes) {
|
|
132
|
-
summary += `- Line ${fail.line}: ${fail.rule} - ${fail.error}\n`;
|
|
133
|
-
}
|
|
134
|
-
summary += `\n`;
|
|
135
|
-
}
|
|
136
|
-
if (remainingIssues.length > 0) {
|
|
137
|
-
summary += `**Remaining Issues (require manual fixing):**\n`;
|
|
138
|
-
const groupedBySeverity = transformedRemaining.reduce((acc, issue) => {
|
|
139
|
-
if (!acc[issue.severity])
|
|
140
|
-
acc[issue.severity] = [];
|
|
141
|
-
acc[issue.severity].push(issue);
|
|
142
|
-
return acc;
|
|
143
|
-
}, {});
|
|
144
|
-
for (const severity of ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']) {
|
|
145
|
-
const issues = groupedBySeverity[severity] || [];
|
|
146
|
-
if (issues.length > 0) {
|
|
147
|
-
summary += `\n${severity} (${issues.length}):\n`;
|
|
148
|
-
for (const issue of issues) {
|
|
149
|
-
summary += `- Line ${issue.line}: ${issue.rule} - ${issue.message}\n`;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
summary += `🎉 All issues resolved! The file has no remaining code quality issues.\n`;
|
|
156
|
-
}
|
|
83
|
+
const remainingRaw = finalResult.raisedIssues?.length ? finalResult.raisedIssues : (finalResult.rawIssues || []);
|
|
84
|
+
const remaining = transformSloopIssues(remainingRaw);
|
|
157
85
|
return {
|
|
158
|
-
content: [
|
|
159
|
-
{
|
|
86
|
+
content: [{
|
|
160
87
|
type: "text",
|
|
161
|
-
text:
|
|
162
|
-
},
|
|
163
|
-
],
|
|
88
|
+
text: formatSummary(filePath, applied, failed, remaining),
|
|
89
|
+
}],
|
|
164
90
|
};
|
|
165
91
|
}
|
|
166
92
|
//# sourceMappingURL=apply-all-quick-fixes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-all-quick-fixes.js","sourceRoot":"","sources":["../../src/tools/apply-all-quick-fixes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-all-quick-fixes.js","sourceRoot":"","sources":["../../src/tools/apply-all-quick-fixes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-quick-fix.d.ts","sourceRoot":"","sources":["../../src/tools/apply-quick-fix.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apply-quick-fix.d.ts","sourceRoot":"","sources":["../../src/tools/apply-quick-fix.ts"],"names":[],"mappings":"AAOA,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,GAAG;;;;;GAuDlD"}
|
|
@@ -3,19 +3,17 @@ import { SloopError } from "../errors.js";
|
|
|
3
3
|
import { ensureSloopBridge } from "../utils/sloop.js";
|
|
4
4
|
import { getOrCreateScope } from "../utils/scope.js";
|
|
5
5
|
import { notifyFileSystemChanged } from "../utils/filesystem.js";
|
|
6
|
+
import { applyTextEdits } from "../utils/quick-fix.js";
|
|
6
7
|
export async function handleApplyQuickFix(args) {
|
|
7
8
|
const { filePath, line, rule } = args;
|
|
8
9
|
console.error(`[MCP] Applying quick fix for ${rule} at ${filePath}:${line}`);
|
|
9
|
-
// Validate file exists
|
|
10
10
|
if (!existsSync(filePath)) {
|
|
11
11
|
throw new SloopError(`File not found: ${filePath}`, `The file ${filePath} does not exist. Please check the path and try again.`, false);
|
|
12
12
|
}
|
|
13
|
-
// Re-analyze the file to get current issues with quick fixes
|
|
14
13
|
const bridge = await ensureSloopBridge();
|
|
15
14
|
const scopeId = await getOrCreateScope(filePath);
|
|
16
15
|
const rawResult = await bridge.analyzeFilesAndTrack(scopeId, [filePath]);
|
|
17
16
|
const rawIssues = rawResult.raisedIssues?.length ? rawResult.raisedIssues : (rawResult.rawIssues || []);
|
|
18
|
-
// Find the issue at the specified line with the specified rule
|
|
19
17
|
const targetIssue = rawIssues.find((issue) => {
|
|
20
18
|
const issueLine = issue.textRange?.startLine || issue.startLine || 0;
|
|
21
19
|
return issueLine === line && issue.ruleKey === rule;
|
|
@@ -26,80 +24,11 @@ export async function handleApplyQuickFix(args) {
|
|
|
26
24
|
if (!targetIssue.quickFixes || targetIssue.quickFixes.length === 0) {
|
|
27
25
|
throw new SloopError(`No quick fix available`, `The issue at line ${line} (${rule}) does not have an automated quick fix available.`, false);
|
|
28
26
|
}
|
|
29
|
-
// Apply the first quick fix
|
|
30
27
|
const quickFix = targetIssue.quickFixes[0];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
writeFileSync(
|
|
34
|
-
targetIssue: {
|
|
35
|
-
ruleKey: targetIssue.ruleKey,
|
|
36
|
-
textRange: targetIssue.textRange,
|
|
37
|
-
quickFixes: targetIssue.quickFixes
|
|
38
|
-
},
|
|
39
|
-
quickFix: quickFix
|
|
40
|
-
}, null, 2), 'utf-8');
|
|
41
|
-
let fileContent = readFileSync(filePath, 'utf-8');
|
|
42
|
-
const lines = fileContent.split('\n');
|
|
43
|
-
// Apply each edit in the quick fix
|
|
44
|
-
const fileEdits = quickFix.inputFileEdits || quickFix.fileEdits || [];
|
|
45
|
-
if (fileEdits.length > 0) {
|
|
46
|
-
console.error(`[DEBUG] Found ${fileEdits.length} file edits`);
|
|
47
|
-
for (const fileEdit of fileEdits) {
|
|
48
|
-
if (fileEdit.textEdits) {
|
|
49
|
-
console.error(`[DEBUG] Found ${fileEdit.textEdits.length} text edits`);
|
|
50
|
-
// Sort edits in reverse order to maintain line numbers
|
|
51
|
-
const sortedEdits = [...fileEdit.textEdits].sort((a, b) => {
|
|
52
|
-
const aStart = a.range?.startLine || 0;
|
|
53
|
-
const bStart = b.range?.startLine || 0;
|
|
54
|
-
return bStart - aStart; // Reverse order
|
|
55
|
-
});
|
|
56
|
-
for (const edit of sortedEdits) {
|
|
57
|
-
const startLine = (edit.range?.startLine || 1) - 1; // Convert to 0-based
|
|
58
|
-
const startCol = edit.range?.startLineOffset || 0;
|
|
59
|
-
const endLine = (edit.range?.endLine || startLine + 1) - 1;
|
|
60
|
-
const endCol = edit.range?.endLineOffset || lines[endLine]?.length || 0;
|
|
61
|
-
const newText = edit.newText || '';
|
|
62
|
-
console.error(`[DEBUG] Applying edit at line ${startLine + 1}:${startCol} to ${endLine + 1}:${endCol}`);
|
|
63
|
-
console.error(`[DEBUG] Old text: "${lines[startLine].substring(startCol, endCol)}"`);
|
|
64
|
-
console.error(`[DEBUG] New text: "${newText}"`);
|
|
65
|
-
// Apply the edit
|
|
66
|
-
if (startLine === endLine) {
|
|
67
|
-
const line = lines[startLine];
|
|
68
|
-
lines[startLine] = line.substring(0, startCol) + newText + line.substring(endCol);
|
|
69
|
-
console.error(`[DEBUG] Result: "${lines[startLine]}"`);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
// Multi-line edit
|
|
73
|
-
const firstLine = lines[startLine].substring(0, startCol) + newText;
|
|
74
|
-
const lastLine = lines[endLine].substring(endCol);
|
|
75
|
-
lines.splice(startLine, endLine - startLine + 1, firstLine + lastLine);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
console.error('[DEBUG] No fileEdits found in quick fix!');
|
|
83
|
-
}
|
|
84
|
-
// Write the modified content back
|
|
85
|
-
fileContent = lines.join('\n');
|
|
86
|
-
console.error(`[DEBUG] About to write file: ${filePath}`);
|
|
87
|
-
console.error(`[DEBUG] File content length: ${fileContent.length} chars`);
|
|
88
|
-
console.error(`[DEBUG] First 200 chars: ${fileContent.substring(0, 200)}`);
|
|
89
|
-
try {
|
|
90
|
-
writeFileSync(filePath, fileContent, 'utf-8');
|
|
91
|
-
console.error(`[DEBUG] File written successfully`);
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
console.error(`[DEBUG] Error writing file:`, err);
|
|
95
|
-
throw err;
|
|
96
|
-
}
|
|
97
|
-
// Notify SLOOP that file system was updated (proper cache invalidation)
|
|
98
|
-
console.error(`[Cache] Sending file system update notification...`);
|
|
28
|
+
const lines = readFileSync(filePath, 'utf-8').split('\n');
|
|
29
|
+
applyTextEdits(lines, quickFix);
|
|
30
|
+
writeFileSync(filePath, lines.join('\n'), 'utf-8');
|
|
99
31
|
await notifyFileSystemChanged(filePath, scopeId);
|
|
100
|
-
console.error(`[Cache] File system update notification sent, waiting for SLOOP to process...`);
|
|
101
|
-
// CRITICAL: Give SLOOP time to process the file system notification
|
|
102
|
-
// Without this delay, the next analysis request may arrive before SLOOP updates its registry
|
|
103
32
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
104
33
|
return {
|
|
105
34
|
content: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-quick-fix.js","sourceRoot":"","sources":["../../src/tools/apply-quick-fix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-quick-fix.js","sourceRoot":"","sources":["../../src/tools/apply-quick-fix.ts"],"names":[],"mappings":"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"}
|