claude-crap 0.3.6 → 0.3.8
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/README.md +25 -0
- package/dist/adapters/common.d.ts +1 -1
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +1 -1
- package/dist/adapters/common.js.map +1 -1
- package/dist/adapters/dart-analyzer.d.ts +41 -0
- package/dist/adapters/dart-analyzer.d.ts.map +1 -0
- package/dist/adapters/dart-analyzer.js +120 -0
- package/dist/adapters/dart-analyzer.js.map +1 -0
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +4 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/crap-config.d.ts +2 -0
- package/dist/crap-config.d.ts.map +1 -1
- package/dist/crap-config.js +36 -28
- package/dist/crap-config.js.map +1 -1
- package/dist/dashboard/file-detail.d.ts +77 -0
- package/dist/dashboard/file-detail.d.ts.map +1 -0
- package/dist/dashboard/file-detail.js +120 -0
- package/dist/dashboard/file-detail.js.map +1 -0
- package/dist/dashboard/server.d.ts +5 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +103 -1
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +36 -4
- package/dist/index.js.map +1 -1
- package/dist/metrics/workspace-walker.d.ts +4 -1
- package/dist/metrics/workspace-walker.d.ts.map +1 -1
- package/dist/metrics/workspace-walker.js +12 -28
- package/dist/metrics/workspace-walker.js.map +1 -1
- package/dist/scanner/auto-scan.d.ts +9 -1
- package/dist/scanner/auto-scan.d.ts.map +1 -1
- package/dist/scanner/auto-scan.js +27 -5
- package/dist/scanner/auto-scan.js.map +1 -1
- package/dist/scanner/bootstrap.d.ts +1 -1
- package/dist/scanner/bootstrap.d.ts.map +1 -1
- package/dist/scanner/bootstrap.js +9 -0
- package/dist/scanner/bootstrap.js.map +1 -1
- package/dist/scanner/complexity-scanner.d.ts +56 -0
- package/dist/scanner/complexity-scanner.d.ts.map +1 -0
- package/dist/scanner/complexity-scanner.js +161 -0
- package/dist/scanner/complexity-scanner.js.map +1 -0
- package/dist/scanner/detector.d.ts +24 -4
- package/dist/scanner/detector.d.ts.map +1 -1
- package/dist/scanner/detector.js +105 -10
- package/dist/scanner/detector.js.map +1 -1
- package/dist/scanner/runner.d.ts +4 -1
- package/dist/scanner/runner.d.ts.map +1 -1
- package/dist/scanner/runner.js +12 -3
- package/dist/scanner/runner.js.map +1 -1
- package/dist/schemas/tool-schemas.d.ts +1 -1
- package/dist/schemas/tool-schemas.js +1 -1
- package/dist/schemas/tool-schemas.js.map +1 -1
- package/dist/shared/exclusions.d.ts +53 -0
- package/dist/shared/exclusions.d.ts.map +1 -0
- package/dist/shared/exclusions.js +126 -0
- package/dist/shared/exclusions.js.map +1 -0
- package/package.json +3 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/bundle/dashboard/public/index.html +432 -12
- package/plugin/bundle/mcp-server.mjs +747 -137
- package/plugin/bundle/mcp-server.mjs.map +4 -4
- package/plugin/package-lock.json +15 -2
- package/plugin/package.json +2 -1
- package/scripts/bundle-plugin.mjs +2 -1
- package/src/adapters/common.ts +1 -1
- package/src/adapters/dart-analyzer.ts +161 -0
- package/src/adapters/index.ts +4 -0
- package/src/crap-config.ts +55 -18
- package/src/dashboard/file-detail.ts +195 -0
- package/src/dashboard/public/index.html +432 -12
- package/src/dashboard/server.ts +140 -1
- package/src/index.ts +37 -4
- package/src/metrics/workspace-walker.ts +15 -27
- package/src/scanner/auto-scan.ts +41 -4
- package/src/scanner/bootstrap.ts +11 -0
- package/src/scanner/complexity-scanner.ts +222 -0
- package/src/scanner/detector.ts +114 -10
- package/src/scanner/runner.ts +12 -2
- package/src/schemas/tool-schemas.ts +1 -1
- package/src/shared/exclusions.ts +156 -0
- package/src/tests/adapters/dispatch.test.ts +2 -2
- package/src/tests/auto-scan.test.ts +2 -2
- package/src/tests/complexity-scanner.test.ts +263 -0
- package/src/tests/exclusions.test.ts +117 -0
- package/src/tests/file-detail-api.test.ts +258 -0
- package/src/tests/scanner-detector.test.ts +31 -11
package/README.md
CHANGED
|
@@ -149,6 +149,31 @@ See [docs/contributing.md](./docs/contributing.md) for Windows setup details.
|
|
|
149
149
|
|
|
150
150
|
---
|
|
151
151
|
|
|
152
|
+
## Supported Languages & Scanners
|
|
153
|
+
|
|
154
|
+
| Language | Extensions | AST analysis | Scanner | Auto-install | Monorepo |
|
|
155
|
+
| :------- | :--------- | :----------: | :------ | :----------: | :------: |
|
|
156
|
+
| TypeScript | `.ts` `.tsx` `.mts` `.cts` | Cyclomatic complexity | ESLint | Yes (npm) | Root |
|
|
157
|
+
| JavaScript | `.js` `.jsx` `.mjs` `.cjs` | Cyclomatic complexity | ESLint | Yes (npm) | Root |
|
|
158
|
+
| Python | `.py` `.pyi` | Cyclomatic complexity | Bandit | Manual | Root |
|
|
159
|
+
| Java | `.java` | Cyclomatic complexity | Semgrep | Manual | Root |
|
|
160
|
+
| C# | `.cs` | Cyclomatic complexity | Semgrep | Manual | Root |
|
|
161
|
+
| Dart / Flutter | `.dart` | LOC only | `dart analyze` | SDK required | Subdir probing |
|
|
162
|
+
| Vue | `.vue` | LOC only | ESLint (via root) | — | — |
|
|
163
|
+
| Go | `.go` | LOC only | — | — | — |
|
|
164
|
+
| Rust | `.rs` | LOC only | — | — | — |
|
|
165
|
+
| Ruby | `.rb` | LOC only | — | — | — |
|
|
166
|
+
| PHP | `.php` | LOC only | — | — | — |
|
|
167
|
+
| Swift | `.swift` | LOC only | — | — | — |
|
|
168
|
+
| Kotlin | `.kt` | LOC only | — | — | — |
|
|
169
|
+
| Scala | `.scala` | LOC only | — | — | — |
|
|
170
|
+
|
|
171
|
+
**AST analysis** = tree-sitter cyclomatic complexity per function. **LOC only** = counted toward workspace metrics but no per-function analysis.
|
|
172
|
+
|
|
173
|
+
**Monorepo support**: auto-scan probes `apps/`, `packages/`, `libs/` and npm workspaces for scanner configs in subdirectories (e.g., `apps/mobile/pubspec.yaml`).
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
152
177
|
## Documentation
|
|
153
178
|
|
|
154
179
|
| Section | Link |
|
|
@@ -21,7 +21,7 @@ import type { SarifLevel } from "../sarif/sarif-builder.js";
|
|
|
21
21
|
* `ingest_scanner_output` MCP tool uses this as its `enum` constraint,
|
|
22
22
|
* so keeping it narrow prevents drift.
|
|
23
23
|
*/
|
|
24
|
-
export declare const KNOWN_SCANNERS: readonly ["semgrep", "eslint", "bandit", "stryker"];
|
|
24
|
+
export declare const KNOWN_SCANNERS: readonly ["semgrep", "eslint", "bandit", "stryker", "dart_analyze"];
|
|
25
25
|
/**
|
|
26
26
|
* Union of supported scanner identifiers.
|
|
27
27
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/adapters/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/adapters/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,cAAc,qEAAsE,CAAC;AAElG;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAK1E,CAAC;AAEH;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,YAAY,EACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAC7B,cAAc,CAgBhB;AAyBD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAM9F"}
|
package/dist/adapters/common.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* `ingest_scanner_output` MCP tool uses this as its `enum` constraint,
|
|
20
20
|
* so keeping it narrow prevents drift.
|
|
21
21
|
*/
|
|
22
|
-
export const KNOWN_SCANNERS = ["semgrep", "eslint", "bandit", "stryker"];
|
|
22
|
+
export const KNOWN_SCANNERS = ["semgrep", "eslint", "bandit", "stryker", "dart_analyze"];
|
|
23
23
|
/**
|
|
24
24
|
* Default remediation effort in minutes per SARIF severity level. These
|
|
25
25
|
* numbers are deliberately conservative — real projects should override
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/adapters/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/adapters/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,CAAU,CAAC;AAOlG;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAyC,MAAM,CAAC,MAAM,CAAC;IAC5F,KAAK,EAAE,EAAE;IACT,OAAO,EAAE,EAAE;IACX,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,CAAC;CACR,CAAC,CAAC;AAmBH;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAwB,EACxB,OAAe,EACf,OAA8B;IAE9B,OAAO;QACL,OAAO,EAAE,qEAAqE;QAC9E,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ;gBACE,IAAI,EAAE;oBACJ,MAAM,EAAE;wBACN,IAAI,EAAE,UAAU;wBAChB,OAAO;qBACR;iBACF;gBACD,OAAO,EAAE,OAA0C;aACpD;SACF;KACgB,CAAC;AACtB,CAAC;AAyBD;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAA6B,EAAE,QAAiB;IACpF,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,IAAI,GAAG,0BAA0B,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter: `dart analyze --format=json` → SARIF 2.1.0.
|
|
3
|
+
*
|
|
4
|
+
* The Dart analyzer emits JSON with this shape:
|
|
5
|
+
*
|
|
6
|
+
* {
|
|
7
|
+
* "version": 1,
|
|
8
|
+
* "diagnostics": [
|
|
9
|
+
* {
|
|
10
|
+
* "code": "unused_import",
|
|
11
|
+
* "severity": "WARNING",
|
|
12
|
+
* "type": "STATIC_WARNING",
|
|
13
|
+
* "location": {
|
|
14
|
+
* "file": "/absolute/path/to/file.dart",
|
|
15
|
+
* "range": {
|
|
16
|
+
* "start": { "offset": 7, "line": 1, "column": 8 },
|
|
17
|
+
* "end": { "offset": 16, "line": 1, "column": 17 }
|
|
18
|
+
* }
|
|
19
|
+
* },
|
|
20
|
+
* "problemMessage": "Unused import: 'dart:io'.",
|
|
21
|
+
* "correctionMessage": "Try removing the import directive.",
|
|
22
|
+
* "documentation": "https://dart.dev/diagnostics/unused_import"
|
|
23
|
+
* }
|
|
24
|
+
* ]
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* Severity mapping:
|
|
28
|
+
* - "ERROR" → SARIF "error" (30 min effort)
|
|
29
|
+
* - "WARNING" → SARIF "warning" (15 min effort)
|
|
30
|
+
* - "INFO" → SARIF "note" (5 min effort)
|
|
31
|
+
*
|
|
32
|
+
* @module adapters/dart-analyzer
|
|
33
|
+
*/
|
|
34
|
+
import { type AdapterResult } from "./common.js";
|
|
35
|
+
/**
|
|
36
|
+
* Convert `dart analyze --format=json` output to SARIF 2.1.0.
|
|
37
|
+
*
|
|
38
|
+
* @param rawOutput The JSON string or pre-parsed object from `dart analyze`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function adaptDartAnalyzer(rawOutput: unknown): AdapterResult;
|
|
41
|
+
//# sourceMappingURL=dart-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dart-analyzer.d.ts","sourceRoot":"","sources":["../../src/adapters/dart-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,aAAa,CAAC;AAsDrB;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,OAAO,GAAG,aAAa,CA+DnE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter: `dart analyze --format=json` → SARIF 2.1.0.
|
|
3
|
+
*
|
|
4
|
+
* The Dart analyzer emits JSON with this shape:
|
|
5
|
+
*
|
|
6
|
+
* {
|
|
7
|
+
* "version": 1,
|
|
8
|
+
* "diagnostics": [
|
|
9
|
+
* {
|
|
10
|
+
* "code": "unused_import",
|
|
11
|
+
* "severity": "WARNING",
|
|
12
|
+
* "type": "STATIC_WARNING",
|
|
13
|
+
* "location": {
|
|
14
|
+
* "file": "/absolute/path/to/file.dart",
|
|
15
|
+
* "range": {
|
|
16
|
+
* "start": { "offset": 7, "line": 1, "column": 8 },
|
|
17
|
+
* "end": { "offset": 16, "line": 1, "column": 17 }
|
|
18
|
+
* }
|
|
19
|
+
* },
|
|
20
|
+
* "problemMessage": "Unused import: 'dart:io'.",
|
|
21
|
+
* "correctionMessage": "Try removing the import directive.",
|
|
22
|
+
* "documentation": "https://dart.dev/diagnostics/unused_import"
|
|
23
|
+
* }
|
|
24
|
+
* ]
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* Severity mapping:
|
|
28
|
+
* - "ERROR" → SARIF "error" (30 min effort)
|
|
29
|
+
* - "WARNING" → SARIF "warning" (15 min effort)
|
|
30
|
+
* - "INFO" → SARIF "note" (5 min effort)
|
|
31
|
+
*
|
|
32
|
+
* @module adapters/dart-analyzer
|
|
33
|
+
*/
|
|
34
|
+
import { wrapResultsInSarif, estimateEffortMinutes, } from "./common.js";
|
|
35
|
+
// ── Severity mapping ───────────────────────────────────────────────
|
|
36
|
+
function mapSeverity(dartSeverity) {
|
|
37
|
+
switch (dartSeverity.toUpperCase()) {
|
|
38
|
+
case "ERROR":
|
|
39
|
+
return "error";
|
|
40
|
+
case "WARNING":
|
|
41
|
+
return "warning";
|
|
42
|
+
case "INFO":
|
|
43
|
+
return "note";
|
|
44
|
+
default:
|
|
45
|
+
return "warning";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// ── Effort estimates per severity ──────────────────────────────────
|
|
49
|
+
const EFFORT_BY_SEVERITY = {
|
|
50
|
+
error: 30,
|
|
51
|
+
warning: 15,
|
|
52
|
+
note: 5,
|
|
53
|
+
none: 0,
|
|
54
|
+
};
|
|
55
|
+
// ── Public API ─────────────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Convert `dart analyze --format=json` output to SARIF 2.1.0.
|
|
58
|
+
*
|
|
59
|
+
* @param rawOutput The JSON string or pre-parsed object from `dart analyze`.
|
|
60
|
+
*/
|
|
61
|
+
export function adaptDartAnalyzer(rawOutput) {
|
|
62
|
+
let parsed;
|
|
63
|
+
if (typeof rawOutput === "string") {
|
|
64
|
+
try {
|
|
65
|
+
parsed = JSON.parse(rawOutput);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new Error("[dart-analyzer adapter] rawOutput is not valid JSON");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (rawOutput && typeof rawOutput === "object" && "diagnostics" in rawOutput) {
|
|
72
|
+
parsed = rawOutput;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
throw new Error("[dart-analyzer adapter] rawOutput must be a JSON string or an object with a 'diagnostics' array");
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(parsed.diagnostics)) {
|
|
78
|
+
throw new Error("[dart-analyzer adapter] 'diagnostics' must be an array");
|
|
79
|
+
}
|
|
80
|
+
const results = [];
|
|
81
|
+
let totalEffortMinutes = 0;
|
|
82
|
+
for (const diag of parsed.diagnostics) {
|
|
83
|
+
const level = mapSeverity(diag.severity);
|
|
84
|
+
const effort = EFFORT_BY_SEVERITY[level] ?? estimateEffortMinutes(level);
|
|
85
|
+
totalEffortMinutes += effort;
|
|
86
|
+
results.push({
|
|
87
|
+
ruleId: diag.code,
|
|
88
|
+
level,
|
|
89
|
+
message: {
|
|
90
|
+
text: diag.problemMessage + (diag.correctionMessage ? ` ${diag.correctionMessage}` : ""),
|
|
91
|
+
},
|
|
92
|
+
locations: [
|
|
93
|
+
{
|
|
94
|
+
physicalLocation: {
|
|
95
|
+
artifactLocation: {
|
|
96
|
+
uri: diag.location.file,
|
|
97
|
+
},
|
|
98
|
+
region: {
|
|
99
|
+
startLine: diag.location.range.start.line,
|
|
100
|
+
startColumn: diag.location.range.start.column,
|
|
101
|
+
endLine: diag.location.range.end.line,
|
|
102
|
+
endColumn: diag.location.range.end.column,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
properties: {
|
|
108
|
+
effortMinutes: effort,
|
|
109
|
+
...(diag.documentation ? { helpUri: diag.documentation } : {}),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
document: wrapResultsInSarif("dart_analyze", "1.0.0", results),
|
|
115
|
+
sourceTool: "dart_analyze",
|
|
116
|
+
findingCount: parsed.diagnostics.length,
|
|
117
|
+
totalEffortMinutes,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=dart-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dart-analyzer.js","sourceRoot":"","sources":["../../src/adapters/dart-analyzer.ts"],"names":[],"mappings":"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"}
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export { adaptSemgrep } from "./semgrep.js";
|
|
|
29
29
|
export { adaptEslint } from "./eslint.js";
|
|
30
30
|
export { adaptBandit } from "./bandit.js";
|
|
31
31
|
export { adaptStryker } from "./stryker.js";
|
|
32
|
+
export { adaptDartAnalyzer } from "./dart-analyzer.js";
|
|
32
33
|
export { DEFAULT_EFFORT_BY_SEVERITY, KNOWN_SCANNERS, estimateEffortMinutes, wrapResultsInSarif, } from "./common.js";
|
|
33
34
|
export type { AdapterResult, KnownScanner } from "./common.js";
|
|
34
35
|
import type { AdapterResult, KnownScanner } from "./common.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO/D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,OAAO,GACjB,aAAa,CAiBf"}
|
package/dist/adapters/index.js
CHANGED
|
@@ -29,11 +29,13 @@ export { adaptSemgrep } from "./semgrep.js";
|
|
|
29
29
|
export { adaptEslint } from "./eslint.js";
|
|
30
30
|
export { adaptBandit } from "./bandit.js";
|
|
31
31
|
export { adaptStryker } from "./stryker.js";
|
|
32
|
+
export { adaptDartAnalyzer } from "./dart-analyzer.js";
|
|
32
33
|
export { DEFAULT_EFFORT_BY_SEVERITY, KNOWN_SCANNERS, estimateEffortMinutes, wrapResultsInSarif, } from "./common.js";
|
|
33
34
|
import { adaptSemgrep } from "./semgrep.js";
|
|
34
35
|
import { adaptEslint } from "./eslint.js";
|
|
35
36
|
import { adaptBandit } from "./bandit.js";
|
|
36
37
|
import { adaptStryker } from "./stryker.js";
|
|
38
|
+
import { adaptDartAnalyzer } from "./dart-analyzer.js";
|
|
37
39
|
/**
|
|
38
40
|
* Route a raw scanner output to the correct adapter based on its
|
|
39
41
|
* name. Preferred entry point for the `ingest_scanner_output` MCP
|
|
@@ -55,6 +57,8 @@ export function adaptScannerOutput(scanner, rawOutput) {
|
|
|
55
57
|
return adaptBandit(rawOutput);
|
|
56
58
|
case "stryker":
|
|
57
59
|
return adaptStryker(rawOutput);
|
|
60
|
+
case "dart_analyze":
|
|
61
|
+
return adaptDartAnalyzer(rawOutput);
|
|
58
62
|
default: {
|
|
59
63
|
const exhaustive = scanner;
|
|
60
64
|
throw new Error(`[adapters] Unknown scanner: ${String(exhaustive)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGvD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAqB,EACrB,SAAkB;IAElB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,SAAS;YACZ,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;QAChC,KAAK,SAAS;YACZ,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;QACjC,KAAK,cAAc;YACjB,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,UAAU,GAAU,OAAO,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/crap-config.d.ts
CHANGED
|
@@ -70,6 +70,8 @@ export interface CrapConfig {
|
|
|
70
70
|
readonly strictness: Strictness;
|
|
71
71
|
/** Where the strictness value actually came from. Useful for diagnostics. */
|
|
72
72
|
readonly strictnessSource: "env" | "file" | "default";
|
|
73
|
+
/** User-defined exclusion patterns (directories with trailing `/`, or file globs). */
|
|
74
|
+
readonly exclude: ReadonlyArray<string>;
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
75
77
|
* Options accepted by {@link loadCrapConfig}. The only required
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crap-config.d.ts","sourceRoot":"","sources":["../src/crap-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,yCAA0C,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,UAAqB,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,gBAAgB,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"crap-config.d.ts","sourceRoot":"","sources":["../src/crap-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,yCAA0C,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,UAAqB,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,gBAAgB,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;IACtD,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,UAAU,CAuBzE"}
|
package/dist/crap-config.js
CHANGED
|
@@ -68,6 +68,10 @@ export class CrapConfigError extends Error {
|
|
|
68
68
|
* @throws {@link CrapConfigError} on any invalid input.
|
|
69
69
|
*/
|
|
70
70
|
export function loadCrapConfig(options) {
|
|
71
|
+
// Always read the file to extract `exclude`, even when strictness
|
|
72
|
+
// comes from the environment variable.
|
|
73
|
+
const fileResult = readFromFile(options.workspaceRoot);
|
|
74
|
+
const exclude = fileResult?.exclude ?? [];
|
|
71
75
|
const envRaw = process.env["CLAUDE_CRAP_STRICTNESS"];
|
|
72
76
|
if (typeof envRaw === "string" && envRaw.trim() !== "") {
|
|
73
77
|
const normalized = envRaw.trim().toLowerCase();
|
|
@@ -75,26 +79,13 @@ export function loadCrapConfig(options) {
|
|
|
75
79
|
throw new CrapConfigError(`[crap-config] CLAUDE_CRAP_STRICTNESS="${envRaw}" is not a valid strictness. ` +
|
|
76
80
|
`Expected one of: ${STRICTNESS_VALUES.join(", ")}.`);
|
|
77
81
|
}
|
|
78
|
-
return { strictness: normalized, strictnessSource: "env" };
|
|
82
|
+
return { strictness: normalized, strictnessSource: "env", exclude };
|
|
79
83
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return { strictness: DEFAULT_STRICTNESS, strictnessSource: "default" };
|
|
84
|
+
if (fileResult?.strictness) {
|
|
85
|
+
return { strictness: fileResult.strictness, strictnessSource: "file", exclude };
|
|
86
|
+
}
|
|
87
|
+
return { strictness: DEFAULT_STRICTNESS, strictnessSource: "default", exclude };
|
|
84
88
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Attempt to read and validate `.claude-crap.json` at the
|
|
87
|
-
* workspace root. Returns `null` when the file is missing (which
|
|
88
|
-
* is the common case for fresh installs). Throws
|
|
89
|
-
* {@link CrapConfigError} on any other failure mode — a malformed
|
|
90
|
-
* JSON file, a non-object root, a missing or wrong-type
|
|
91
|
-
* `strictness` field, or an unknown enum value — so the caller
|
|
92
|
-
* cannot accidentally drop into the default on a typo.
|
|
93
|
-
*
|
|
94
|
-
* @param workspaceRoot Absolute workspace root.
|
|
95
|
-
* @returns The validated strictness, or `null` when no
|
|
96
|
-
* file is present.
|
|
97
|
-
*/
|
|
98
89
|
function readFromFile(workspaceRoot) {
|
|
99
90
|
const filePath = join(workspaceRoot, ".claude-crap.json");
|
|
100
91
|
let raw;
|
|
@@ -118,18 +109,35 @@ function readFromFile(workspaceRoot) {
|
|
|
118
109
|
throw new CrapConfigError(`[crap-config] ${filePath} must be a JSON object at the top level`);
|
|
119
110
|
}
|
|
120
111
|
const doc = parsed;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
// Parse strictness
|
|
113
|
+
let strictness = null;
|
|
114
|
+
if ("strictness" in doc) {
|
|
115
|
+
const value = doc["strictness"];
|
|
116
|
+
if (typeof value !== "string") {
|
|
117
|
+
throw new CrapConfigError(`[crap-config] ${filePath}: 'strictness' must be a string, got ${typeof value}`);
|
|
118
|
+
}
|
|
119
|
+
const normalized = value.trim().toLowerCase();
|
|
120
|
+
if (!isStrictness(normalized)) {
|
|
121
|
+
throw new CrapConfigError(`[crap-config] ${filePath}: 'strictness' is "${value}"; ` +
|
|
122
|
+
`expected one of ${STRICTNESS_VALUES.join(", ")}.`);
|
|
123
|
+
}
|
|
124
|
+
strictness = normalized;
|
|
126
125
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
// Parse exclude
|
|
127
|
+
let exclude = [];
|
|
128
|
+
if ("exclude" in doc) {
|
|
129
|
+
const raw = doc["exclude"];
|
|
130
|
+
if (!Array.isArray(raw)) {
|
|
131
|
+
throw new CrapConfigError(`[crap-config] ${filePath}: 'exclude' must be an array of strings`);
|
|
132
|
+
}
|
|
133
|
+
for (const item of raw) {
|
|
134
|
+
if (typeof item !== "string") {
|
|
135
|
+
throw new CrapConfigError(`[crap-config] ${filePath}: every entry in 'exclude' must be a string, got ${typeof item}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exclude = raw;
|
|
131
139
|
}
|
|
132
|
-
return
|
|
140
|
+
return { strictness, exclude };
|
|
133
141
|
}
|
|
134
142
|
/**
|
|
135
143
|
* Runtime type guard for the {@link Strictness} union. Lets callers
|
package/dist/crap-config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crap-config.js","sourceRoot":"","sources":["../src/crap-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAU,CAAC;AASzE;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAe,QAAQ,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"crap-config.js","sourceRoot":"","sources":["../src/crap-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File detail builder for the dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Given a workspace-relative file path, this module produces a rich
|
|
5
|
+
* detail payload combining source code, per-function AST metrics, and
|
|
6
|
+
* SARIF findings filtered to that file. The dashboard uses this to
|
|
7
|
+
* render a ReportGenerator-style annotated code view.
|
|
8
|
+
*
|
|
9
|
+
* The builder is extracted into its own module (rather than inlined in
|
|
10
|
+
* `server.ts`) so that:
|
|
11
|
+
* - The logic is unit-testable without booting the HTTP server.
|
|
12
|
+
* - The types are importable by both the Fastify route and tests.
|
|
13
|
+
*
|
|
14
|
+
* @module dashboard/file-detail
|
|
15
|
+
*/
|
|
16
|
+
import { type SupportedLanguage } from "../ast/language-config.js";
|
|
17
|
+
import type { TreeSitterEngine } from "../ast/tree-sitter-engine.js";
|
|
18
|
+
import type { SarifStore } from "../sarif/sarif-store.js";
|
|
19
|
+
/** Per-function entry in the detail response. */
|
|
20
|
+
export interface FileDetailFunction {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly startLine: number;
|
|
23
|
+
readonly endLine: number;
|
|
24
|
+
readonly cyclomaticComplexity: number;
|
|
25
|
+
readonly lineCount: number;
|
|
26
|
+
}
|
|
27
|
+
/** Per-finding entry in the detail response. */
|
|
28
|
+
export interface FileDetailFinding {
|
|
29
|
+
readonly ruleId: string;
|
|
30
|
+
readonly level: string;
|
|
31
|
+
readonly message: string;
|
|
32
|
+
readonly sourceTool: string;
|
|
33
|
+
readonly startLine: number;
|
|
34
|
+
readonly startColumn: number;
|
|
35
|
+
readonly endLine: number;
|
|
36
|
+
readonly endColumn: number;
|
|
37
|
+
readonly effortMinutes: number;
|
|
38
|
+
}
|
|
39
|
+
/** Summary statistics for the file. */
|
|
40
|
+
export interface FileDetailSummary {
|
|
41
|
+
readonly totalFindings: number;
|
|
42
|
+
readonly errorCount: number;
|
|
43
|
+
readonly warningCount: number;
|
|
44
|
+
readonly noteCount: number;
|
|
45
|
+
readonly totalEffortMinutes: number;
|
|
46
|
+
readonly avgComplexity: number;
|
|
47
|
+
readonly maxComplexity: number;
|
|
48
|
+
}
|
|
49
|
+
/** Full response payload for the file detail endpoint. */
|
|
50
|
+
export interface FileDetailResponse {
|
|
51
|
+
readonly filePath: string;
|
|
52
|
+
readonly language: SupportedLanguage | null;
|
|
53
|
+
readonly physicalLoc: number;
|
|
54
|
+
readonly logicalLoc: number;
|
|
55
|
+
readonly cyclomaticMax: number;
|
|
56
|
+
readonly sourceLines: string[];
|
|
57
|
+
readonly functions: FileDetailFunction[];
|
|
58
|
+
readonly findings: FileDetailFinding[];
|
|
59
|
+
readonly summary: FileDetailSummary;
|
|
60
|
+
}
|
|
61
|
+
/** Input accepted by {@link buildFileDetail}. */
|
|
62
|
+
export interface BuildFileDetailInput {
|
|
63
|
+
readonly relativePath: string;
|
|
64
|
+
readonly workspaceRoot: string;
|
|
65
|
+
readonly astEngine?: TreeSitterEngine | undefined;
|
|
66
|
+
readonly sarifStore: SarifStore;
|
|
67
|
+
readonly cyclomaticMax: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build the file detail payload. Pure function aside from the file
|
|
71
|
+
* read and the tree-sitter analysis (both deterministic for a given
|
|
72
|
+
* file).
|
|
73
|
+
*
|
|
74
|
+
* @throws When the file does not exist or the path escapes the workspace.
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildFileDetail(input: BuildFileDetailInput): Promise<FileDetailResponse>;
|
|
77
|
+
//# sourceMappingURL=file-detail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-detail.d.ts","sourceRoot":"","sources":["../../src/dashboard/file-detail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAA0B,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAmB,MAAM,8BAA8B,CAAC;AACtF,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,yBAAyB,CAAC;AAI3E,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,kBAAkB,EAAE,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAID;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CAwG7B"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File detail builder for the dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Given a workspace-relative file path, this module produces a rich
|
|
5
|
+
* detail payload combining source code, per-function AST metrics, and
|
|
6
|
+
* SARIF findings filtered to that file. The dashboard uses this to
|
|
7
|
+
* render a ReportGenerator-style annotated code view.
|
|
8
|
+
*
|
|
9
|
+
* The builder is extracted into its own module (rather than inlined in
|
|
10
|
+
* `server.ts`) so that:
|
|
11
|
+
* - The logic is unit-testable without booting the HTTP server.
|
|
12
|
+
* - The types are importable by both the Fastify route and tests.
|
|
13
|
+
*
|
|
14
|
+
* @module dashboard/file-detail
|
|
15
|
+
*/
|
|
16
|
+
import { promises as fs } from "node:fs";
|
|
17
|
+
import { resolveWithinWorkspace } from "../workspace-guard.js";
|
|
18
|
+
import { detectLanguageFromPath } from "../ast/language-config.js";
|
|
19
|
+
// ── Builder ───────────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Build the file detail payload. Pure function aside from the file
|
|
22
|
+
* read and the tree-sitter analysis (both deterministic for a given
|
|
23
|
+
* file).
|
|
24
|
+
*
|
|
25
|
+
* @throws When the file does not exist or the path escapes the workspace.
|
|
26
|
+
*/
|
|
27
|
+
export async function buildFileDetail(input) {
|
|
28
|
+
const { relativePath, workspaceRoot, astEngine, sarifStore, cyclomaticMax } = input;
|
|
29
|
+
// 1. Guard against path traversal
|
|
30
|
+
const absolutePath = resolveWithinWorkspace(workspaceRoot, relativePath);
|
|
31
|
+
// 2. Read source
|
|
32
|
+
const source = await fs.readFile(absolutePath, "utf8");
|
|
33
|
+
const sourceLines = source.split(/\r?\n/);
|
|
34
|
+
// Remove trailing empty line from files ending with \n
|
|
35
|
+
if (sourceLines.length > 0 && sourceLines[sourceLines.length - 1] === "") {
|
|
36
|
+
sourceLines.pop();
|
|
37
|
+
}
|
|
38
|
+
const physicalLoc = sourceLines.length;
|
|
39
|
+
let logicalLoc = 0;
|
|
40
|
+
for (const line of sourceLines) {
|
|
41
|
+
if (line.trim().length > 0)
|
|
42
|
+
logicalLoc += 1;
|
|
43
|
+
}
|
|
44
|
+
// 3. AST analysis (if language is supported)
|
|
45
|
+
const language = detectLanguageFromPath(relativePath);
|
|
46
|
+
let functions = [];
|
|
47
|
+
if (language && astEngine) {
|
|
48
|
+
try {
|
|
49
|
+
const metrics = await astEngine.analyzeFile({
|
|
50
|
+
filePath: absolutePath,
|
|
51
|
+
language,
|
|
52
|
+
});
|
|
53
|
+
functions = metrics.functions.map((fn) => ({
|
|
54
|
+
name: fn.name,
|
|
55
|
+
startLine: fn.startLine,
|
|
56
|
+
endLine: fn.endLine,
|
|
57
|
+
cyclomaticComplexity: fn.cyclomaticComplexity,
|
|
58
|
+
lineCount: fn.lineCount,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Analysis failure is non-fatal — return empty functions
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 4. Filter SARIF findings for this file
|
|
66
|
+
const allFindings = sarifStore.list();
|
|
67
|
+
const fileFindings = allFindings.filter((f) => f.location.uri === relativePath);
|
|
68
|
+
const findings = fileFindings.map((f) => ({
|
|
69
|
+
ruleId: f.ruleId,
|
|
70
|
+
level: f.level,
|
|
71
|
+
message: f.message,
|
|
72
|
+
sourceTool: f.sourceTool,
|
|
73
|
+
startLine: f.location.startLine,
|
|
74
|
+
startColumn: f.location.startColumn,
|
|
75
|
+
endLine: f.location.endLine ?? f.location.startLine,
|
|
76
|
+
endColumn: f.location.endColumn ?? 0,
|
|
77
|
+
effortMinutes: typeof f.properties?.effortMinutes === "number"
|
|
78
|
+
? f.properties.effortMinutes
|
|
79
|
+
: 0,
|
|
80
|
+
}));
|
|
81
|
+
// 5. Build summary
|
|
82
|
+
let errorCount = 0;
|
|
83
|
+
let warningCount = 0;
|
|
84
|
+
let noteCount = 0;
|
|
85
|
+
let totalEffortMinutes = 0;
|
|
86
|
+
for (const f of findings) {
|
|
87
|
+
if (f.level === "error")
|
|
88
|
+
errorCount += 1;
|
|
89
|
+
else if (f.level === "warning")
|
|
90
|
+
warningCount += 1;
|
|
91
|
+
else if (f.level === "note")
|
|
92
|
+
noteCount += 1;
|
|
93
|
+
totalEffortMinutes += f.effortMinutes;
|
|
94
|
+
}
|
|
95
|
+
const complexities = functions.map((f) => f.cyclomaticComplexity);
|
|
96
|
+
const maxComplexity = complexities.length > 0 ? Math.max(...complexities) : 0;
|
|
97
|
+
const avgComplexity = complexities.length > 0
|
|
98
|
+
? Math.round((complexities.reduce((a, b) => a + b, 0) / complexities.length) * 100) / 100
|
|
99
|
+
: 0;
|
|
100
|
+
return {
|
|
101
|
+
filePath: relativePath,
|
|
102
|
+
language,
|
|
103
|
+
physicalLoc,
|
|
104
|
+
logicalLoc,
|
|
105
|
+
cyclomaticMax,
|
|
106
|
+
sourceLines,
|
|
107
|
+
functions,
|
|
108
|
+
findings,
|
|
109
|
+
summary: {
|
|
110
|
+
totalFindings: findings.length,
|
|
111
|
+
errorCount,
|
|
112
|
+
warningCount,
|
|
113
|
+
noteCount,
|
|
114
|
+
totalEffortMinutes,
|
|
115
|
+
avgComplexity,
|
|
116
|
+
maxComplexity,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=file-detail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-detail.js","sourceRoot":"","sources":["../../src/dashboard/file-detail.ts"],"names":[],"mappings":"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"}
|