claude-crap 0.1.2
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 +308 -0
- package/LICENSE +21 -0
- package/README.md +550 -0
- package/bin/claude-crap.mjs +141 -0
- package/dist/adapters/bandit.d.ts +48 -0
- package/dist/adapters/bandit.d.ts.map +1 -0
- package/dist/adapters/bandit.js +145 -0
- package/dist/adapters/bandit.js.map +1 -0
- package/dist/adapters/common.d.ts +73 -0
- package/dist/adapters/common.d.ts.map +1 -0
- package/dist/adapters/common.js +78 -0
- package/dist/adapters/common.js.map +1 -0
- package/dist/adapters/eslint.d.ts +52 -0
- package/dist/adapters/eslint.d.ts.map +1 -0
- package/dist/adapters/eslint.js +142 -0
- package/dist/adapters/eslint.js.map +1 -0
- package/dist/adapters/index.d.ts +47 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +64 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/semgrep.d.ts +30 -0
- package/dist/adapters/semgrep.d.ts.map +1 -0
- package/dist/adapters/semgrep.js +130 -0
- package/dist/adapters/semgrep.js.map +1 -0
- package/dist/adapters/stryker.d.ts +55 -0
- package/dist/adapters/stryker.d.ts.map +1 -0
- package/dist/adapters/stryker.js +165 -0
- package/dist/adapters/stryker.js.map +1 -0
- package/dist/ast/cyclomatic.d.ts +48 -0
- package/dist/ast/cyclomatic.d.ts.map +1 -0
- package/dist/ast/cyclomatic.js +106 -0
- package/dist/ast/cyclomatic.js.map +1 -0
- package/dist/ast/index.d.ts +26 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +23 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/ast/language-config.d.ts +70 -0
- package/dist/ast/language-config.d.ts.map +1 -0
- package/dist/ast/language-config.js +192 -0
- package/dist/ast/language-config.js.map +1 -0
- package/dist/ast/tree-sitter-engine.d.ts +133 -0
- package/dist/ast/tree-sitter-engine.d.ts.map +1 -0
- package/dist/ast/tree-sitter-engine.js +270 -0
- package/dist/ast/tree-sitter-engine.js.map +1 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +78 -0
- package/dist/config.js.map +1 -0
- package/dist/crap-config.d.ts +97 -0
- package/dist/crap-config.d.ts.map +1 -0
- package/dist/crap-config.js +144 -0
- package/dist/crap-config.js.map +1 -0
- package/dist/dashboard/server.d.ts +65 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +147 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +574 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/crap.d.ts +71 -0
- package/dist/metrics/crap.d.ts.map +1 -0
- package/dist/metrics/crap.js +67 -0
- package/dist/metrics/crap.js.map +1 -0
- package/dist/metrics/index.d.ts +31 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +27 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/score.d.ts +143 -0
- package/dist/metrics/score.d.ts.map +1 -0
- package/dist/metrics/score.js +224 -0
- package/dist/metrics/score.js.map +1 -0
- package/dist/metrics/tdr.d.ts +106 -0
- package/dist/metrics/tdr.d.ts.map +1 -0
- package/dist/metrics/tdr.js +117 -0
- package/dist/metrics/tdr.js.map +1 -0
- package/dist/metrics/workspace-walker.d.ts +43 -0
- package/dist/metrics/workspace-walker.d.ts.map +1 -0
- package/dist/metrics/workspace-walker.js +137 -0
- package/dist/metrics/workspace-walker.js.map +1 -0
- package/dist/sarif/index.d.ts +21 -0
- package/dist/sarif/index.d.ts.map +1 -0
- package/dist/sarif/index.js +19 -0
- package/dist/sarif/index.js.map +1 -0
- package/dist/sarif/sarif-builder.d.ts +128 -0
- package/dist/sarif/sarif-builder.d.ts.map +1 -0
- package/dist/sarif/sarif-builder.js +79 -0
- package/dist/sarif/sarif-builder.js.map +1 -0
- package/dist/sarif/sarif-store.d.ts +205 -0
- package/dist/sarif/sarif-store.d.ts.map +1 -0
- package/dist/sarif/sarif-store.js +246 -0
- package/dist/sarif/sarif-store.js.map +1 -0
- package/dist/sarif/sarif-validator.d.ts +45 -0
- package/dist/sarif/sarif-validator.d.ts.map +1 -0
- package/dist/sarif/sarif-validator.js +138 -0
- package/dist/sarif/sarif-validator.js.map +1 -0
- package/dist/schemas/tool-schemas.d.ts +216 -0
- package/dist/schemas/tool-schemas.d.ts.map +1 -0
- package/dist/schemas/tool-schemas.js +208 -0
- package/dist/schemas/tool-schemas.js.map +1 -0
- package/dist/sdk.d.ts +45 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +44 -0
- package/dist/sdk.js.map +1 -0
- package/dist/tools/index.d.ts +24 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/test-harness.d.ts +75 -0
- package/dist/tools/test-harness.d.ts.map +1 -0
- package/dist/tools/test-harness.js +137 -0
- package/dist/tools/test-harness.js.map +1 -0
- package/dist/workspace-guard.d.ts +53 -0
- package/dist/workspace-guard.d.ts.map +1 -0
- package/dist/workspace-guard.js +61 -0
- package/dist/workspace-guard.js.map +1 -0
- package/package.json +133 -0
- package/plugin/.claude-plugin/plugin.json +29 -0
- package/plugin/.mcp.json +18 -0
- package/plugin/CLAUDE.md +143 -0
- package/plugin/bundle/dashboard/public/index.html +368 -0
- package/plugin/bundle/dashboard/public/vendor/vue.global.prod.js +9 -0
- package/plugin/bundle/mcp-server.mjs +8718 -0
- package/plugin/bundle/mcp-server.mjs.map +7 -0
- package/plugin/bundle/tdr-engine.mjs +50 -0
- package/plugin/bundle/tdr-engine.mjs.map +7 -0
- package/plugin/hooks/hooks.json +62 -0
- package/plugin/hooks/lib/crap-config.mjs +152 -0
- package/plugin/hooks/lib/gatekeeper-rules.mjs +257 -0
- package/plugin/hooks/lib/hook-io.mjs +151 -0
- package/plugin/hooks/lib/quality-gate.mjs +329 -0
- package/plugin/hooks/lib/test-harness.mjs +152 -0
- package/plugin/hooks/post-tool-use.mjs +245 -0
- package/plugin/hooks/pre-tool-use.mjs +290 -0
- package/plugin/hooks/session-start.mjs +109 -0
- package/plugin/hooks/stop-quality-gate.mjs +226 -0
- package/plugin/package.json +18 -0
- package/plugin/skills/adopt/SKILL.md +74 -0
- package/plugin/skills/analyze/SKILL.md +77 -0
- package/plugin/skills/check-test/SKILL.md +50 -0
- package/plugin/skills/score/SKILL.md +31 -0
- package/scripts/bug-report.mjs +328 -0
- package/scripts/build-fast.mjs +130 -0
- package/scripts/bundle-plugin.mjs +74 -0
- package/scripts/doctor.mjs +320 -0
- package/scripts/install.mjs +192 -0
- package/scripts/lib/cli-ui.mjs +122 -0
- package/scripts/postinstall.mjs +127 -0
- package/scripts/run-tests.mjs +95 -0
- package/scripts/status.mjs +110 -0
- package/scripts/uninstall.mjs +72 -0
- package/src/adapters/bandit.ts +191 -0
- package/src/adapters/common.ts +133 -0
- package/src/adapters/eslint.ts +187 -0
- package/src/adapters/index.ts +78 -0
- package/src/adapters/semgrep.ts +150 -0
- package/src/adapters/stryker.ts +218 -0
- package/src/ast/cyclomatic.ts +131 -0
- package/src/ast/index.ts +33 -0
- package/src/ast/language-config.ts +231 -0
- package/src/ast/tree-sitter-engine.ts +385 -0
- package/src/config.ts +109 -0
- package/src/crap-config.ts +196 -0
- package/src/dashboard/public/index.html +368 -0
- package/src/dashboard/public/vendor/vue.global.prod.js +9 -0
- package/src/dashboard/server.ts +205 -0
- package/src/index.ts +696 -0
- package/src/metrics/crap.ts +101 -0
- package/src/metrics/index.ts +51 -0
- package/src/metrics/score.ts +329 -0
- package/src/metrics/tdr.ts +155 -0
- package/src/metrics/workspace-walker.ts +146 -0
- package/src/sarif/index.ts +31 -0
- package/src/sarif/sarif-builder.ts +139 -0
- package/src/sarif/sarif-store.ts +347 -0
- package/src/sarif/sarif-validator.ts +145 -0
- package/src/schemas/tool-schemas.ts +225 -0
- package/src/sdk.ts +110 -0
- package/src/tests/adapters/bandit.test.ts +111 -0
- package/src/tests/adapters/dispatch.test.ts +100 -0
- package/src/tests/adapters/eslint.test.ts +138 -0
- package/src/tests/adapters/semgrep.test.ts +125 -0
- package/src/tests/adapters/stryker.test.ts +103 -0
- package/src/tests/crap-config.test.ts +228 -0
- package/src/tests/crap.test.ts +59 -0
- package/src/tests/cyclomatic.test.ts +87 -0
- package/src/tests/dashboard-http.test.ts +108 -0
- package/src/tests/dashboard-integrity.test.ts +128 -0
- package/src/tests/integration/mcp-server.integration.test.ts +352 -0
- package/src/tests/pre-tool-use-hook.test.ts +178 -0
- package/src/tests/sarif-store.test.ts +241 -0
- package/src/tests/sarif-validator.test.ts +164 -0
- package/src/tests/score.test.ts +260 -0
- package/src/tests/skills-frontmatter.test.ts +172 -0
- package/src/tests/stop-quality-gate-strictness.test.ts +243 -0
- package/src/tests/tdr.test.ts +86 -0
- package/src/tests/test-harness.test.ts +153 -0
- package/src/tests/workspace-guard.test.ts +111 -0
- package/src/tools/index.ts +24 -0
- package/src/tools/test-harness.ts +158 -0
- package/src/workspace-guard.ts +64 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Technical Debt Ratio (TDR) — deterministic computation and rating.
|
|
3
|
+
*
|
|
4
|
+
* The Technical Debt Ratio expresses how expensive it would be to remediate
|
|
5
|
+
* all known issues in a scope, relative to how much it would have cost to
|
|
6
|
+
* write the code in the first place. Formally (see docs/quality-gate.md):
|
|
7
|
+
*
|
|
8
|
+
* TDR = remediationCost / (costPerLine × totalLinesOfCode)
|
|
9
|
+
*
|
|
10
|
+
* Where the remediation cost is the sum (in minutes) of every linter /
|
|
11
|
+
* scanner / mutator finding's individual estimated effort, and the per-line
|
|
12
|
+
* cost is assumed to be a constant `minutesPerLoc` (industry default: 30
|
|
13
|
+
* minutes per line of code, including design, writing and review).
|
|
14
|
+
*
|
|
15
|
+
* The resulting ratio is converted to a percentage and mapped to a letter
|
|
16
|
+
* grade A..E. The thresholds are strict and non-negotiable:
|
|
17
|
+
*
|
|
18
|
+
* | Rating | TDR % | Meaning |
|
|
19
|
+
* |--------|--------------|-------------------------------------------|
|
|
20
|
+
* | A | 0..5% | Excellent — remediation cost is noise |
|
|
21
|
+
* | B | >5..10% | Low risk |
|
|
22
|
+
* | C | >10..20% | Moderate, watch closely |
|
|
23
|
+
* | D | >20..50% | Critical, remediation plan required |
|
|
24
|
+
* | E | >50% | Unmaintainable — halt feature work |
|
|
25
|
+
*
|
|
26
|
+
* Rating E always halts the workflow at the Stop quality gate, regardless
|
|
27
|
+
* of the configured `TDR_MAX_RATING` tolerance.
|
|
28
|
+
*
|
|
29
|
+
* @module metrics/tdr
|
|
30
|
+
*/
|
|
31
|
+
import type { MaintainabilityRating } from "../config.js";
|
|
32
|
+
/**
|
|
33
|
+
* Inputs required to compute a Technical Debt Ratio over any scope
|
|
34
|
+
* (project, module, or file).
|
|
35
|
+
*/
|
|
36
|
+
export interface TdrInput {
|
|
37
|
+
/** Sum of all finding remediation estimates, in minutes. Must be ≥ 0. */
|
|
38
|
+
readonly remediationMinutes: number;
|
|
39
|
+
/** Total lines of code in the scope. Must be > 0 (division denominator). */
|
|
40
|
+
readonly totalLinesOfCode: number;
|
|
41
|
+
/** Assumed development cost per LOC, in minutes. Must be > 0. */
|
|
42
|
+
readonly minutesPerLoc: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Result of a TDR computation with both the raw ratio and the letter grade.
|
|
46
|
+
*/
|
|
47
|
+
export interface TdrResult {
|
|
48
|
+
/** Raw ratio (remediation / development), rounded to 6 decimals. */
|
|
49
|
+
readonly ratio: number;
|
|
50
|
+
/** Same ratio expressed as a percentage, rounded to 4 decimals. */
|
|
51
|
+
readonly percent: number;
|
|
52
|
+
/** Letter grade derived from `percent` via {@link classifyTdr}. */
|
|
53
|
+
readonly rating: MaintainabilityRating;
|
|
54
|
+
/** Remediation input, echoed for traceability. */
|
|
55
|
+
readonly remediationMinutes: number;
|
|
56
|
+
/** LOC input, echoed for traceability. */
|
|
57
|
+
readonly totalLinesOfCode: number;
|
|
58
|
+
/** Computed `minutesPerLoc × totalLinesOfCode`, useful for the dashboard. */
|
|
59
|
+
readonly developmentCostMinutes: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Convert a letter rating to its numeric rank (A=0, E=4). Useful when
|
|
63
|
+
* comparing two ratings without relying on lexical order.
|
|
64
|
+
*
|
|
65
|
+
* @param rating The rating letter.
|
|
66
|
+
* @returns Its rank in `[0, 4]`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function ratingToRank(rating: MaintainabilityRating): number;
|
|
69
|
+
/**
|
|
70
|
+
* Return `true` when `actual` is strictly worse than `limit`, false otherwise.
|
|
71
|
+
* Used by the Stop quality gate to decide whether to block task completion.
|
|
72
|
+
*
|
|
73
|
+
* @param actual Rating currently achieved by the project.
|
|
74
|
+
* @param limit Maximum tolerated rating (worst allowed).
|
|
75
|
+
* @returns `true` if `actual` should trigger a block.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ratingIsWorseThan("D", "C") // → true
|
|
79
|
+
* ratingIsWorseThan("B", "C") // → false
|
|
80
|
+
* ratingIsWorseThan("C", "C") // → false (equal, not worse)
|
|
81
|
+
*/
|
|
82
|
+
export declare function ratingIsWorseThan(actual: MaintainabilityRating, limit: MaintainabilityRating): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Map a TDR percentage to its letter rating. The boundaries are inclusive
|
|
85
|
+
* on the upper end (5% is still an A, 10% is still a B, etc.).
|
|
86
|
+
*
|
|
87
|
+
* @param percent TDR expressed as a percentage. Must be ≥ 0.
|
|
88
|
+
* @returns Letter rating A..E.
|
|
89
|
+
* @throws When `percent` is negative or not finite.
|
|
90
|
+
*/
|
|
91
|
+
export declare function classifyTdr(percent: number): MaintainabilityRating;
|
|
92
|
+
/**
|
|
93
|
+
* Compute the Technical Debt Ratio for a scope and return the full result.
|
|
94
|
+
* This function is pure and deterministic.
|
|
95
|
+
*
|
|
96
|
+
* @param input Remediation minutes, total LOC and the cost-per-line assumption.
|
|
97
|
+
* @returns A {@link TdrResult} ready to be serialized to SARIF properties.
|
|
98
|
+
* @throws When any numeric input is out of range.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* // 240 minutes of remediation across 500 LOC at 30 min/LOC
|
|
102
|
+
* computeTdr({ remediationMinutes: 240, totalLinesOfCode: 500, minutesPerLoc: 30 })
|
|
103
|
+
* // → { ratio: 0.016, percent: 1.6, rating: "A", ... }
|
|
104
|
+
*/
|
|
105
|
+
export declare function computeTdr(input: TdrInput): TdrResult;
|
|
106
|
+
//# sourceMappingURL=tdr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tdr.d.ts","sourceRoot":"","sources":["../../src/metrics/tdr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,4EAA4E;IAC5E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,iEAAiE;IACjE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,kDAAkD;IAClD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,0CAA0C;IAC1C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACzC;AAKD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAElE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CASlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAwBrD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Technical Debt Ratio (TDR) — deterministic computation and rating.
|
|
3
|
+
*
|
|
4
|
+
* The Technical Debt Ratio expresses how expensive it would be to remediate
|
|
5
|
+
* all known issues in a scope, relative to how much it would have cost to
|
|
6
|
+
* write the code in the first place. Formally (see docs/quality-gate.md):
|
|
7
|
+
*
|
|
8
|
+
* TDR = remediationCost / (costPerLine × totalLinesOfCode)
|
|
9
|
+
*
|
|
10
|
+
* Where the remediation cost is the sum (in minutes) of every linter /
|
|
11
|
+
* scanner / mutator finding's individual estimated effort, and the per-line
|
|
12
|
+
* cost is assumed to be a constant `minutesPerLoc` (industry default: 30
|
|
13
|
+
* minutes per line of code, including design, writing and review).
|
|
14
|
+
*
|
|
15
|
+
* The resulting ratio is converted to a percentage and mapped to a letter
|
|
16
|
+
* grade A..E. The thresholds are strict and non-negotiable:
|
|
17
|
+
*
|
|
18
|
+
* | Rating | TDR % | Meaning |
|
|
19
|
+
* |--------|--------------|-------------------------------------------|
|
|
20
|
+
* | A | 0..5% | Excellent — remediation cost is noise |
|
|
21
|
+
* | B | >5..10% | Low risk |
|
|
22
|
+
* | C | >10..20% | Moderate, watch closely |
|
|
23
|
+
* | D | >20..50% | Critical, remediation plan required |
|
|
24
|
+
* | E | >50% | Unmaintainable — halt feature work |
|
|
25
|
+
*
|
|
26
|
+
* Rating E always halts the workflow at the Stop quality gate, regardless
|
|
27
|
+
* of the configured `TDR_MAX_RATING` tolerance.
|
|
28
|
+
*
|
|
29
|
+
* @module metrics/tdr
|
|
30
|
+
*/
|
|
31
|
+
/** Canonical ordering used by {@link ratingToRank}. */
|
|
32
|
+
const RATING_ORDER = ["A", "B", "C", "D", "E"];
|
|
33
|
+
/**
|
|
34
|
+
* Convert a letter rating to its numeric rank (A=0, E=4). Useful when
|
|
35
|
+
* comparing two ratings without relying on lexical order.
|
|
36
|
+
*
|
|
37
|
+
* @param rating The rating letter.
|
|
38
|
+
* @returns Its rank in `[0, 4]`.
|
|
39
|
+
*/
|
|
40
|
+
export function ratingToRank(rating) {
|
|
41
|
+
return RATING_ORDER.indexOf(rating);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Return `true` when `actual` is strictly worse than `limit`, false otherwise.
|
|
45
|
+
* Used by the Stop quality gate to decide whether to block task completion.
|
|
46
|
+
*
|
|
47
|
+
* @param actual Rating currently achieved by the project.
|
|
48
|
+
* @param limit Maximum tolerated rating (worst allowed).
|
|
49
|
+
* @returns `true` if `actual` should trigger a block.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ratingIsWorseThan("D", "C") // → true
|
|
53
|
+
* ratingIsWorseThan("B", "C") // → false
|
|
54
|
+
* ratingIsWorseThan("C", "C") // → false (equal, not worse)
|
|
55
|
+
*/
|
|
56
|
+
export function ratingIsWorseThan(actual, limit) {
|
|
57
|
+
return ratingToRank(actual) > ratingToRank(limit);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Map a TDR percentage to its letter rating. The boundaries are inclusive
|
|
61
|
+
* on the upper end (5% is still an A, 10% is still a B, etc.).
|
|
62
|
+
*
|
|
63
|
+
* @param percent TDR expressed as a percentage. Must be ≥ 0.
|
|
64
|
+
* @returns Letter rating A..E.
|
|
65
|
+
* @throws When `percent` is negative or not finite.
|
|
66
|
+
*/
|
|
67
|
+
export function classifyTdr(percent) {
|
|
68
|
+
if (!Number.isFinite(percent) || percent < 0) {
|
|
69
|
+
throw new Error(`[tdr] percent is invalid: ${percent}`);
|
|
70
|
+
}
|
|
71
|
+
if (percent <= 5)
|
|
72
|
+
return "A";
|
|
73
|
+
if (percent <= 10)
|
|
74
|
+
return "B";
|
|
75
|
+
if (percent <= 20)
|
|
76
|
+
return "C";
|
|
77
|
+
if (percent <= 50)
|
|
78
|
+
return "D";
|
|
79
|
+
return "E";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Compute the Technical Debt Ratio for a scope and return the full result.
|
|
83
|
+
* This function is pure and deterministic.
|
|
84
|
+
*
|
|
85
|
+
* @param input Remediation minutes, total LOC and the cost-per-line assumption.
|
|
86
|
+
* @returns A {@link TdrResult} ready to be serialized to SARIF properties.
|
|
87
|
+
* @throws When any numeric input is out of range.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* // 240 minutes of remediation across 500 LOC at 30 min/LOC
|
|
91
|
+
* computeTdr({ remediationMinutes: 240, totalLinesOfCode: 500, minutesPerLoc: 30 })
|
|
92
|
+
* // → { ratio: 0.016, percent: 1.6, rating: "A", ... }
|
|
93
|
+
*/
|
|
94
|
+
export function computeTdr(input) {
|
|
95
|
+
if (input.totalLinesOfCode <= 0) {
|
|
96
|
+
throw new Error(`[tdr] totalLinesOfCode must be > 0, got ${input.totalLinesOfCode}`);
|
|
97
|
+
}
|
|
98
|
+
if (input.minutesPerLoc <= 0) {
|
|
99
|
+
throw new Error(`[tdr] minutesPerLoc must be > 0, got ${input.minutesPerLoc}`);
|
|
100
|
+
}
|
|
101
|
+
if (input.remediationMinutes < 0) {
|
|
102
|
+
throw new Error(`[tdr] remediationMinutes must be ≥ 0, got ${input.remediationMinutes}`);
|
|
103
|
+
}
|
|
104
|
+
const developmentCostMinutes = input.minutesPerLoc * input.totalLinesOfCode;
|
|
105
|
+
const ratio = input.remediationMinutes / developmentCostMinutes;
|
|
106
|
+
const percent = ratio * 100;
|
|
107
|
+
const rating = classifyTdr(percent);
|
|
108
|
+
return {
|
|
109
|
+
ratio: Number(ratio.toFixed(6)),
|
|
110
|
+
percent: Number(percent.toFixed(4)),
|
|
111
|
+
rating,
|
|
112
|
+
remediationMinutes: input.remediationMinutes,
|
|
113
|
+
totalLinesOfCode: input.totalLinesOfCode,
|
|
114
|
+
developmentCostMinutes,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=tdr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tdr.js","sourceRoot":"","sources":["../../src/metrics/tdr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAmCH,uDAAuD;AACvD,MAAM,YAAY,GAAyC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAErF;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,MAA6B;IACxD,OAAO,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA6B,EAC7B,KAA4B;IAE5B,OAAO,YAAY,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,6BAA6B,OAAO,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC7B,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC9B,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC9B,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,GAAG,CAAC;IAC9B,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,KAAe;IACxC,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,2CAA2C,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,IAAI,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,6CAA6C,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,sBAAsB,GAAG,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,gBAAgB,CAAC;IAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,kBAAkB,GAAG,sBAAsB,CAAC;IAChE,MAAM,OAAO,GAAG,KAAK,GAAG,GAAG,CAAC;IAC5B,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAEpC,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/B,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM;QACN,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,sBAAsB;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded workspace walker.
|
|
3
|
+
*
|
|
4
|
+
* Counts physical lines of code across a workspace, skipping directories
|
|
5
|
+
* that should not contribute to the Technical Debt Ratio (dependency
|
|
6
|
+
* caches, build artifacts, VCS metadata, etc.) and capping the file
|
|
7
|
+
* count to keep the walk well under the Stop hook's 120-second budget
|
|
8
|
+
* even on pathological repositories.
|
|
9
|
+
*
|
|
10
|
+
* This is the TypeScript twin of `hooks/lib/quality-gate.mjs#estimateWorkspaceLoc`.
|
|
11
|
+
* The two are independent so neither side has to import files from outside
|
|
12
|
+
* its own project tree.
|
|
13
|
+
*
|
|
14
|
+
* @module metrics/workspace-walker
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Result returned by {@link estimateWorkspaceLoc}.
|
|
18
|
+
*/
|
|
19
|
+
export interface WorkspaceWalkResult {
|
|
20
|
+
/** Total physical lines of code across every file the walker read. */
|
|
21
|
+
readonly physicalLoc: number;
|
|
22
|
+
/** Number of code files the walker visited. */
|
|
23
|
+
readonly fileCount: number;
|
|
24
|
+
/** `true` when the walker hit {@link MAX_FILES_WALKED} and stopped early. */
|
|
25
|
+
readonly truncated: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Hard cap on the number of files the walker will read. Protects against
|
|
29
|
+
* pathological repositories where the walk would otherwise dominate the
|
|
30
|
+
* Stop hook's budget. When hit, the walker returns the partial count
|
|
31
|
+
* with `truncated: true` and the caller may decide how to react.
|
|
32
|
+
*/
|
|
33
|
+
export declare const MAX_FILES_WALKED = 20000;
|
|
34
|
+
/**
|
|
35
|
+
* Walk a workspace and return its physical LOC + file count. Never
|
|
36
|
+
* follows symbolic links. Skips hidden directories except `.claude-plugin`
|
|
37
|
+
* (which is tiny and contains the manifest).
|
|
38
|
+
*
|
|
39
|
+
* @param workspaceRoot Absolute path to the workspace root.
|
|
40
|
+
* @returns A {@link WorkspaceWalkResult} snapshot.
|
|
41
|
+
*/
|
|
42
|
+
export declare function estimateWorkspaceLoc(workspaceRoot: string): Promise<WorkspaceWalkResult>;
|
|
43
|
+
//# sourceMappingURL=workspace-walker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-walker.d.ts","sourceRoot":"","sources":["../../src/metrics/workspace-walker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAkDD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAkD9F"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded workspace walker.
|
|
3
|
+
*
|
|
4
|
+
* Counts physical lines of code across a workspace, skipping directories
|
|
5
|
+
* that should not contribute to the Technical Debt Ratio (dependency
|
|
6
|
+
* caches, build artifacts, VCS metadata, etc.) and capping the file
|
|
7
|
+
* count to keep the walk well under the Stop hook's 120-second budget
|
|
8
|
+
* even on pathological repositories.
|
|
9
|
+
*
|
|
10
|
+
* This is the TypeScript twin of `hooks/lib/quality-gate.mjs#estimateWorkspaceLoc`.
|
|
11
|
+
* The two are independent so neither side has to import files from outside
|
|
12
|
+
* its own project tree.
|
|
13
|
+
*
|
|
14
|
+
* @module metrics/workspace-walker
|
|
15
|
+
*/
|
|
16
|
+
import { promises as fs } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
/**
|
|
19
|
+
* Directories that should never contribute to the LOC count. Dependency
|
|
20
|
+
* caches, build artifacts, VCS metadata, claude-crap's own state.
|
|
21
|
+
*/
|
|
22
|
+
const SKIP_DIRS = new Set([
|
|
23
|
+
"node_modules",
|
|
24
|
+
".git",
|
|
25
|
+
"dist",
|
|
26
|
+
"build",
|
|
27
|
+
"out",
|
|
28
|
+
"target",
|
|
29
|
+
".venv",
|
|
30
|
+
"venv",
|
|
31
|
+
"__pycache__",
|
|
32
|
+
".cache",
|
|
33
|
+
".next",
|
|
34
|
+
".nuxt",
|
|
35
|
+
".claude-crap",
|
|
36
|
+
".codesight",
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Extensions the walker treats as "code". Anything else is ignored,
|
|
40
|
+
* including markdown, JSON, YAML, lockfiles, and binaries.
|
|
41
|
+
*/
|
|
42
|
+
const CODE_EXTENSIONS = new Set([
|
|
43
|
+
".ts",
|
|
44
|
+
".tsx",
|
|
45
|
+
".mts",
|
|
46
|
+
".cts",
|
|
47
|
+
".js",
|
|
48
|
+
".jsx",
|
|
49
|
+
".mjs",
|
|
50
|
+
".cjs",
|
|
51
|
+
".py",
|
|
52
|
+
".java",
|
|
53
|
+
".cs",
|
|
54
|
+
".go",
|
|
55
|
+
".rs",
|
|
56
|
+
".rb",
|
|
57
|
+
".php",
|
|
58
|
+
".swift",
|
|
59
|
+
".kt",
|
|
60
|
+
".scala",
|
|
61
|
+
".dart",
|
|
62
|
+
".vue",
|
|
63
|
+
]);
|
|
64
|
+
/**
|
|
65
|
+
* Hard cap on the number of files the walker will read. Protects against
|
|
66
|
+
* pathological repositories where the walk would otherwise dominate the
|
|
67
|
+
* Stop hook's budget. When hit, the walker returns the partial count
|
|
68
|
+
* with `truncated: true` and the caller may decide how to react.
|
|
69
|
+
*/
|
|
70
|
+
export const MAX_FILES_WALKED = 20_000;
|
|
71
|
+
/**
|
|
72
|
+
* Walk a workspace and return its physical LOC + file count. Never
|
|
73
|
+
* follows symbolic links. Skips hidden directories except `.claude-plugin`
|
|
74
|
+
* (which is tiny and contains the manifest).
|
|
75
|
+
*
|
|
76
|
+
* @param workspaceRoot Absolute path to the workspace root.
|
|
77
|
+
* @returns A {@link WorkspaceWalkResult} snapshot.
|
|
78
|
+
*/
|
|
79
|
+
export async function estimateWorkspaceLoc(workspaceRoot) {
|
|
80
|
+
let physicalLoc = 0;
|
|
81
|
+
let fileCount = 0;
|
|
82
|
+
let truncated = false;
|
|
83
|
+
async function walk(dir) {
|
|
84
|
+
if (truncated)
|
|
85
|
+
return;
|
|
86
|
+
let entries;
|
|
87
|
+
try {
|
|
88
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
if (truncated)
|
|
95
|
+
return;
|
|
96
|
+
// Skip hidden files except the plugin manifest dir.
|
|
97
|
+
if (entry.name.startsWith(".") && entry.name !== ".claude-plugin")
|
|
98
|
+
continue;
|
|
99
|
+
const full = join(dir, entry.name);
|
|
100
|
+
if (entry.isDirectory()) {
|
|
101
|
+
if (SKIP_DIRS.has(entry.name))
|
|
102
|
+
continue;
|
|
103
|
+
await walk(full);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (!entry.isFile())
|
|
107
|
+
continue;
|
|
108
|
+
const lower = entry.name.toLowerCase();
|
|
109
|
+
const dot = lower.lastIndexOf(".");
|
|
110
|
+
if (dot < 0)
|
|
111
|
+
continue;
|
|
112
|
+
const ext = lower.substring(dot);
|
|
113
|
+
if (!CODE_EXTENSIONS.has(ext))
|
|
114
|
+
continue;
|
|
115
|
+
fileCount += 1;
|
|
116
|
+
if (fileCount > MAX_FILES_WALKED) {
|
|
117
|
+
truncated = true;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const content = await fs.readFile(full, "utf8");
|
|
122
|
+
if (content.length > 0) {
|
|
123
|
+
// Subtract 1 for trailing newline, matching what most editors
|
|
124
|
+
// report as the file's line count.
|
|
125
|
+
const lines = content.split(/\r?\n/).length;
|
|
126
|
+
physicalLoc += content.endsWith("\n") ? lines - 1 : lines;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Unreadable file (permissions, binary). Skip silently.
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
await walk(workspaceRoot);
|
|
135
|
+
return { physicalLoc, fileCount, truncated };
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=workspace-walker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-walker.js","sourceRoot":"","sources":["../../src/metrics/workspace-walker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public SDK entry point for the SARIF 2.1.0 builder and store.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import {
|
|
8
|
+
* SarifStore,
|
|
9
|
+
* buildSarifDocument,
|
|
10
|
+
* type SarifFinding,
|
|
11
|
+
* type SarifLevel,
|
|
12
|
+
* } from "claude-crap/sarif";
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @module sarif
|
|
16
|
+
*/
|
|
17
|
+
export { buildSarifDocument } from "./sarif-builder.js";
|
|
18
|
+
export type { SarifFinding, SarifLevel, SarifLocation, SarifToolInfo, } from "./sarif-builder.js";
|
|
19
|
+
export { SarifStore } from "./sarif-store.js";
|
|
20
|
+
export type { IngestedFinding, PersistedSarif, SarifStoreOptions, } from "./sarif-store.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sarif/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,YAAY,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,GAClB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public SDK entry point for the SARIF 2.1.0 builder and store.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import {
|
|
8
|
+
* SarifStore,
|
|
9
|
+
* buildSarifDocument,
|
|
10
|
+
* type SarifFinding,
|
|
11
|
+
* type SarifLevel,
|
|
12
|
+
* } from "claude-crap/sarif";
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @module sarif
|
|
16
|
+
*/
|
|
17
|
+
export { buildSarifDocument } from "./sarif-builder.js";
|
|
18
|
+
export { SarifStore } from "./sarif-store.js";
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sarif/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAQxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal SARIF 2.1.0 document builder.
|
|
3
|
+
*
|
|
4
|
+
* Every report that leaves the MCP server on its way to the agent is
|
|
5
|
+
* normalized to SARIF 2.1.0 first. This module provides the typed
|
|
6
|
+
* helpers used to wrap raw findings in the canonical
|
|
7
|
+
* `tool → runs → results` taxonomy with exact file coordinates.
|
|
8
|
+
*
|
|
9
|
+
* Per-scanner adapters (Semgrep, ESLint, Bandit, Stryker) live under
|
|
10
|
+
* `src/adapters/` and call into `buildSarifDocument` through the
|
|
11
|
+
* `wrapResultsInSarif` helper in `src/adapters/common.ts`. The
|
|
12
|
+
* on-disk deduplication store lives in `./sarif-store.ts`.
|
|
13
|
+
*
|
|
14
|
+
* The SARIF 2.1.0 spec lives at:
|
|
15
|
+
* https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
16
|
+
*
|
|
17
|
+
* @module sarif/sarif-builder
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Severity levels supported by SARIF 2.1.0. They map 1:1 to the
|
|
21
|
+
* `result.level` field. `"error"` is the strongest, `"none"` is informational.
|
|
22
|
+
*/
|
|
23
|
+
export type SarifLevel = "none" | "note" | "warning" | "error";
|
|
24
|
+
/**
|
|
25
|
+
* Physical location of a finding inside a source artifact. `startLine` and
|
|
26
|
+
* `startColumn` are 1-based, matching the SARIF spec. `endLine` and
|
|
27
|
+
* `endColumn` are optional — omit them for point-like findings.
|
|
28
|
+
*/
|
|
29
|
+
export interface SarifLocation {
|
|
30
|
+
/** Artifact URI, typically a file path relative to the workspace root. */
|
|
31
|
+
readonly uri: string;
|
|
32
|
+
/** 1-based line number where the finding starts. */
|
|
33
|
+
readonly startLine: number;
|
|
34
|
+
/** 1-based column number where the finding starts. */
|
|
35
|
+
readonly startColumn: number;
|
|
36
|
+
/** Optional 1-based line number where the finding ends. */
|
|
37
|
+
readonly endLine?: number;
|
|
38
|
+
/** Optional 1-based column number where the finding ends. */
|
|
39
|
+
readonly endColumn?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A single finding ready to be embedded in a SARIF run. This is the
|
|
43
|
+
* internal shape used by claude-crap adapters; it is converted into the
|
|
44
|
+
* official SARIF `result` object by {@link buildSarifDocument}.
|
|
45
|
+
*/
|
|
46
|
+
export interface SarifFinding {
|
|
47
|
+
/** Stable rule identifier (e.g. `"SONAR-CRAP-001"`, `"semgrep.python.sqli"`). */
|
|
48
|
+
readonly ruleId: string;
|
|
49
|
+
/** Severity level for this finding. */
|
|
50
|
+
readonly level: SarifLevel;
|
|
51
|
+
/** Human-readable message describing the finding. */
|
|
52
|
+
readonly message: string;
|
|
53
|
+
/** Physical location where the finding was detected. */
|
|
54
|
+
readonly location: SarifLocation;
|
|
55
|
+
/** Optional extra metadata stored in the SARIF `properties` bag. */
|
|
56
|
+
readonly properties?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Metadata describing the tool that produced a SARIF run. The `name` is
|
|
60
|
+
* required by the spec; `version` is strongly recommended so that dashboard
|
|
61
|
+
* diffs can distinguish between scanner releases.
|
|
62
|
+
*/
|
|
63
|
+
export interface SarifToolInfo {
|
|
64
|
+
/** Tool display name (e.g. `"claude-crap"`, `"semgrep"`). */
|
|
65
|
+
readonly name: string;
|
|
66
|
+
/** Tool semantic version. */
|
|
67
|
+
readonly version: string;
|
|
68
|
+
/** Optional URL pointing to the tool's documentation or home page. */
|
|
69
|
+
readonly informationUri?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build a minimal but valid SARIF 2.1.0 document from a list of findings.
|
|
73
|
+
*
|
|
74
|
+
* The returned object conforms to the SARIF JSON schema hosted at:
|
|
75
|
+
* https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json
|
|
76
|
+
*
|
|
77
|
+
* Rules are deduplicated by `ruleId` and emitted in the `tool.driver.rules`
|
|
78
|
+
* array so that downstream consumers (Claude Code, the dashboard, or any
|
|
79
|
+
* third-party SARIF viewer) can render a rule index.
|
|
80
|
+
*
|
|
81
|
+
* @param tool Metadata about the producing tool.
|
|
82
|
+
* @param findings Findings to include in the single run.
|
|
83
|
+
* @returns A SARIF 2.1.0 document literal (frozen by `as const`).
|
|
84
|
+
*/
|
|
85
|
+
export declare function buildSarifDocument(tool: SarifToolInfo, findings: ReadonlyArray<SarifFinding>): {
|
|
86
|
+
readonly $schema: "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json";
|
|
87
|
+
readonly version: "2.1.0";
|
|
88
|
+
readonly runs: readonly [{
|
|
89
|
+
readonly tool: {
|
|
90
|
+
readonly driver: {
|
|
91
|
+
readonly name: string;
|
|
92
|
+
readonly version: string;
|
|
93
|
+
readonly informationUri: string;
|
|
94
|
+
readonly rules: {
|
|
95
|
+
id: string;
|
|
96
|
+
shortDescription: {
|
|
97
|
+
text: string;
|
|
98
|
+
};
|
|
99
|
+
defaultConfiguration: {
|
|
100
|
+
level: SarifLevel;
|
|
101
|
+
};
|
|
102
|
+
}[];
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
readonly results: {
|
|
106
|
+
properties?: Record<string, unknown>;
|
|
107
|
+
ruleId: string;
|
|
108
|
+
level: SarifLevel;
|
|
109
|
+
message: {
|
|
110
|
+
text: string;
|
|
111
|
+
};
|
|
112
|
+
locations: {
|
|
113
|
+
physicalLocation: {
|
|
114
|
+
artifactLocation: {
|
|
115
|
+
uri: string;
|
|
116
|
+
};
|
|
117
|
+
region: {
|
|
118
|
+
endColumn?: number;
|
|
119
|
+
endLine?: number;
|
|
120
|
+
startLine: number;
|
|
121
|
+
startColumn: number;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
}[];
|
|
125
|
+
}[];
|
|
126
|
+
}];
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=sarif-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif-builder.d.ts","sourceRoot":"","sources":["../../src/sarif/sarif-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE/D;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiD5F"}
|