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.
Files changed (202) hide show
  1. package/CHANGELOG.md +308 -0
  2. package/LICENSE +21 -0
  3. package/README.md +550 -0
  4. package/bin/claude-crap.mjs +141 -0
  5. package/dist/adapters/bandit.d.ts +48 -0
  6. package/dist/adapters/bandit.d.ts.map +1 -0
  7. package/dist/adapters/bandit.js +145 -0
  8. package/dist/adapters/bandit.js.map +1 -0
  9. package/dist/adapters/common.d.ts +73 -0
  10. package/dist/adapters/common.d.ts.map +1 -0
  11. package/dist/adapters/common.js +78 -0
  12. package/dist/adapters/common.js.map +1 -0
  13. package/dist/adapters/eslint.d.ts +52 -0
  14. package/dist/adapters/eslint.d.ts.map +1 -0
  15. package/dist/adapters/eslint.js +142 -0
  16. package/dist/adapters/eslint.js.map +1 -0
  17. package/dist/adapters/index.d.ts +47 -0
  18. package/dist/adapters/index.d.ts.map +1 -0
  19. package/dist/adapters/index.js +64 -0
  20. package/dist/adapters/index.js.map +1 -0
  21. package/dist/adapters/semgrep.d.ts +30 -0
  22. package/dist/adapters/semgrep.d.ts.map +1 -0
  23. package/dist/adapters/semgrep.js +130 -0
  24. package/dist/adapters/semgrep.js.map +1 -0
  25. package/dist/adapters/stryker.d.ts +55 -0
  26. package/dist/adapters/stryker.d.ts.map +1 -0
  27. package/dist/adapters/stryker.js +165 -0
  28. package/dist/adapters/stryker.js.map +1 -0
  29. package/dist/ast/cyclomatic.d.ts +48 -0
  30. package/dist/ast/cyclomatic.d.ts.map +1 -0
  31. package/dist/ast/cyclomatic.js +106 -0
  32. package/dist/ast/cyclomatic.js.map +1 -0
  33. package/dist/ast/index.d.ts +26 -0
  34. package/dist/ast/index.d.ts.map +1 -0
  35. package/dist/ast/index.js +23 -0
  36. package/dist/ast/index.js.map +1 -0
  37. package/dist/ast/language-config.d.ts +70 -0
  38. package/dist/ast/language-config.d.ts.map +1 -0
  39. package/dist/ast/language-config.js +192 -0
  40. package/dist/ast/language-config.js.map +1 -0
  41. package/dist/ast/tree-sitter-engine.d.ts +133 -0
  42. package/dist/ast/tree-sitter-engine.d.ts.map +1 -0
  43. package/dist/ast/tree-sitter-engine.js +270 -0
  44. package/dist/ast/tree-sitter-engine.js.map +1 -0
  45. package/dist/config.d.ts +57 -0
  46. package/dist/config.d.ts.map +1 -0
  47. package/dist/config.js +78 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/crap-config.d.ts +97 -0
  50. package/dist/crap-config.d.ts.map +1 -0
  51. package/dist/crap-config.js +144 -0
  52. package/dist/crap-config.js.map +1 -0
  53. package/dist/dashboard/server.d.ts +65 -0
  54. package/dist/dashboard/server.d.ts.map +1 -0
  55. package/dist/dashboard/server.js +147 -0
  56. package/dist/dashboard/server.js.map +1 -0
  57. package/dist/index.d.ts +32 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +574 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/metrics/crap.d.ts +71 -0
  62. package/dist/metrics/crap.d.ts.map +1 -0
  63. package/dist/metrics/crap.js +67 -0
  64. package/dist/metrics/crap.js.map +1 -0
  65. package/dist/metrics/index.d.ts +31 -0
  66. package/dist/metrics/index.d.ts.map +1 -0
  67. package/dist/metrics/index.js +27 -0
  68. package/dist/metrics/index.js.map +1 -0
  69. package/dist/metrics/score.d.ts +143 -0
  70. package/dist/metrics/score.d.ts.map +1 -0
  71. package/dist/metrics/score.js +224 -0
  72. package/dist/metrics/score.js.map +1 -0
  73. package/dist/metrics/tdr.d.ts +106 -0
  74. package/dist/metrics/tdr.d.ts.map +1 -0
  75. package/dist/metrics/tdr.js +117 -0
  76. package/dist/metrics/tdr.js.map +1 -0
  77. package/dist/metrics/workspace-walker.d.ts +43 -0
  78. package/dist/metrics/workspace-walker.d.ts.map +1 -0
  79. package/dist/metrics/workspace-walker.js +137 -0
  80. package/dist/metrics/workspace-walker.js.map +1 -0
  81. package/dist/sarif/index.d.ts +21 -0
  82. package/dist/sarif/index.d.ts.map +1 -0
  83. package/dist/sarif/index.js +19 -0
  84. package/dist/sarif/index.js.map +1 -0
  85. package/dist/sarif/sarif-builder.d.ts +128 -0
  86. package/dist/sarif/sarif-builder.d.ts.map +1 -0
  87. package/dist/sarif/sarif-builder.js +79 -0
  88. package/dist/sarif/sarif-builder.js.map +1 -0
  89. package/dist/sarif/sarif-store.d.ts +205 -0
  90. package/dist/sarif/sarif-store.d.ts.map +1 -0
  91. package/dist/sarif/sarif-store.js +246 -0
  92. package/dist/sarif/sarif-store.js.map +1 -0
  93. package/dist/sarif/sarif-validator.d.ts +45 -0
  94. package/dist/sarif/sarif-validator.d.ts.map +1 -0
  95. package/dist/sarif/sarif-validator.js +138 -0
  96. package/dist/sarif/sarif-validator.js.map +1 -0
  97. package/dist/schemas/tool-schemas.d.ts +216 -0
  98. package/dist/schemas/tool-schemas.d.ts.map +1 -0
  99. package/dist/schemas/tool-schemas.js +208 -0
  100. package/dist/schemas/tool-schemas.js.map +1 -0
  101. package/dist/sdk.d.ts +45 -0
  102. package/dist/sdk.d.ts.map +1 -0
  103. package/dist/sdk.js +44 -0
  104. package/dist/sdk.js.map +1 -0
  105. package/dist/tools/index.d.ts +24 -0
  106. package/dist/tools/index.d.ts.map +1 -0
  107. package/dist/tools/index.js +23 -0
  108. package/dist/tools/index.js.map +1 -0
  109. package/dist/tools/test-harness.d.ts +75 -0
  110. package/dist/tools/test-harness.d.ts.map +1 -0
  111. package/dist/tools/test-harness.js +137 -0
  112. package/dist/tools/test-harness.js.map +1 -0
  113. package/dist/workspace-guard.d.ts +53 -0
  114. package/dist/workspace-guard.d.ts.map +1 -0
  115. package/dist/workspace-guard.js +61 -0
  116. package/dist/workspace-guard.js.map +1 -0
  117. package/package.json +133 -0
  118. package/plugin/.claude-plugin/plugin.json +29 -0
  119. package/plugin/.mcp.json +18 -0
  120. package/plugin/CLAUDE.md +143 -0
  121. package/plugin/bundle/dashboard/public/index.html +368 -0
  122. package/plugin/bundle/dashboard/public/vendor/vue.global.prod.js +9 -0
  123. package/plugin/bundle/mcp-server.mjs +8718 -0
  124. package/plugin/bundle/mcp-server.mjs.map +7 -0
  125. package/plugin/bundle/tdr-engine.mjs +50 -0
  126. package/plugin/bundle/tdr-engine.mjs.map +7 -0
  127. package/plugin/hooks/hooks.json +62 -0
  128. package/plugin/hooks/lib/crap-config.mjs +152 -0
  129. package/plugin/hooks/lib/gatekeeper-rules.mjs +257 -0
  130. package/plugin/hooks/lib/hook-io.mjs +151 -0
  131. package/plugin/hooks/lib/quality-gate.mjs +329 -0
  132. package/plugin/hooks/lib/test-harness.mjs +152 -0
  133. package/plugin/hooks/post-tool-use.mjs +245 -0
  134. package/plugin/hooks/pre-tool-use.mjs +290 -0
  135. package/plugin/hooks/session-start.mjs +109 -0
  136. package/plugin/hooks/stop-quality-gate.mjs +226 -0
  137. package/plugin/package.json +18 -0
  138. package/plugin/skills/adopt/SKILL.md +74 -0
  139. package/plugin/skills/analyze/SKILL.md +77 -0
  140. package/plugin/skills/check-test/SKILL.md +50 -0
  141. package/plugin/skills/score/SKILL.md +31 -0
  142. package/scripts/bug-report.mjs +328 -0
  143. package/scripts/build-fast.mjs +130 -0
  144. package/scripts/bundle-plugin.mjs +74 -0
  145. package/scripts/doctor.mjs +320 -0
  146. package/scripts/install.mjs +192 -0
  147. package/scripts/lib/cli-ui.mjs +122 -0
  148. package/scripts/postinstall.mjs +127 -0
  149. package/scripts/run-tests.mjs +95 -0
  150. package/scripts/status.mjs +110 -0
  151. package/scripts/uninstall.mjs +72 -0
  152. package/src/adapters/bandit.ts +191 -0
  153. package/src/adapters/common.ts +133 -0
  154. package/src/adapters/eslint.ts +187 -0
  155. package/src/adapters/index.ts +78 -0
  156. package/src/adapters/semgrep.ts +150 -0
  157. package/src/adapters/stryker.ts +218 -0
  158. package/src/ast/cyclomatic.ts +131 -0
  159. package/src/ast/index.ts +33 -0
  160. package/src/ast/language-config.ts +231 -0
  161. package/src/ast/tree-sitter-engine.ts +385 -0
  162. package/src/config.ts +109 -0
  163. package/src/crap-config.ts +196 -0
  164. package/src/dashboard/public/index.html +368 -0
  165. package/src/dashboard/public/vendor/vue.global.prod.js +9 -0
  166. package/src/dashboard/server.ts +205 -0
  167. package/src/index.ts +696 -0
  168. package/src/metrics/crap.ts +101 -0
  169. package/src/metrics/index.ts +51 -0
  170. package/src/metrics/score.ts +329 -0
  171. package/src/metrics/tdr.ts +155 -0
  172. package/src/metrics/workspace-walker.ts +146 -0
  173. package/src/sarif/index.ts +31 -0
  174. package/src/sarif/sarif-builder.ts +139 -0
  175. package/src/sarif/sarif-store.ts +347 -0
  176. package/src/sarif/sarif-validator.ts +145 -0
  177. package/src/schemas/tool-schemas.ts +225 -0
  178. package/src/sdk.ts +110 -0
  179. package/src/tests/adapters/bandit.test.ts +111 -0
  180. package/src/tests/adapters/dispatch.test.ts +100 -0
  181. package/src/tests/adapters/eslint.test.ts +138 -0
  182. package/src/tests/adapters/semgrep.test.ts +125 -0
  183. package/src/tests/adapters/stryker.test.ts +103 -0
  184. package/src/tests/crap-config.test.ts +228 -0
  185. package/src/tests/crap.test.ts +59 -0
  186. package/src/tests/cyclomatic.test.ts +87 -0
  187. package/src/tests/dashboard-http.test.ts +108 -0
  188. package/src/tests/dashboard-integrity.test.ts +128 -0
  189. package/src/tests/integration/mcp-server.integration.test.ts +352 -0
  190. package/src/tests/pre-tool-use-hook.test.ts +178 -0
  191. package/src/tests/sarif-store.test.ts +241 -0
  192. package/src/tests/sarif-validator.test.ts +164 -0
  193. package/src/tests/score.test.ts +260 -0
  194. package/src/tests/skills-frontmatter.test.ts +172 -0
  195. package/src/tests/stop-quality-gate-strictness.test.ts +243 -0
  196. package/src/tests/tdr.test.ts +86 -0
  197. package/src/tests/test-harness.test.ts +153 -0
  198. package/src/tests/workspace-guard.test.ts +111 -0
  199. package/src/tools/index.ts +24 -0
  200. package/src/tools/test-harness.ts +158 -0
  201. package/src/workspace-guard.ts +64 -0
  202. package/tsconfig.json +27 -0
package/dist/index.js ADDED
@@ -0,0 +1,574 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * claude-crap MCP server — entrypoint.
4
+ *
5
+ * Transport: stdio. The server is launched by `.mcp.json` with the
6
+ * arguments `--transport stdio` and it never opens sockets or listens
7
+ * on the network: all communication with Claude Code happens over
8
+ * stdin/stdout as JSON-RPC messages.
9
+ *
10
+ * What this file wires together:
11
+ *
12
+ * Tools:
13
+ * - compute_crap (CRAP index for one function)
14
+ * - compute_tdr (Technical Debt Ratio for a scope)
15
+ * - analyze_file_ast (tree-sitter AST metrics for a source file)
16
+ * - ingest_sarif (normalize + dedupe an external SARIF report)
17
+ * - ingest_scanner_output (route Semgrep/ESLint/Bandit/Stryker native output through an adapter and persist the normalized SARIF)
18
+ * - require_test_harness (check that a production source file has a matching test)
19
+ * - score_project (aggregate the workspace into Maintainability / Reliability / Security / Overall ratings)
20
+ *
21
+ * Resources:
22
+ * - sonar://metrics/current (live CRAP / TDR / rating snapshot)
23
+ * - sonar://reports/latest.sarif (last consolidated SARIF document)
24
+ *
25
+ * The handlers delegate to pure engines in `./metrics`, `./ast` and
26
+ * `./sarif`, so the index file stays focused on routing and
27
+ * cross-cutting concerns (configuration, logging, error boundaries).
28
+ *
29
+ * @module index
30
+ */
31
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
32
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
33
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
34
+ import pino from "pino";
35
+ import { adaptScannerOutput } from "./adapters/index.js";
36
+ import { TreeSitterEngine } from "./ast/tree-sitter-engine.js";
37
+ import { loadConfig } from "./config.js";
38
+ import { startDashboard } from "./dashboard/server.js";
39
+ import { computeCrap } from "./metrics/crap.js";
40
+ import { computeProjectScore, renderProjectScoreMarkdown, } from "./metrics/score.js";
41
+ import { computeTdr, classifyTdr } from "./metrics/tdr.js";
42
+ import { estimateWorkspaceLoc } from "./metrics/workspace-walker.js";
43
+ import { SarifStore } from "./sarif/sarif-store.js";
44
+ import { validateSarifDocument } from "./sarif/sarif-validator.js";
45
+ import { loadCrapConfig, CrapConfigError } from "./crap-config.js";
46
+ import { findTestFile } from "./tools/test-harness.js";
47
+ import { resolveWithinWorkspace } from "./workspace-guard.js";
48
+ import { computeCrapSchema, computeTdrSchema, analyzeFileAstSchema, ingestSarifSchema, ingestScannerOutputSchema, requireTestHarnessSchema, scoreProjectSchema, } from "./schemas/tool-schemas.js";
49
+ // IMPORTANT: the MCP stdio transport uses stdout for JSON-RPC framing.
50
+ // Anything the server logs MUST go to stderr (fd 2) to avoid corrupting
51
+ // the wire format. We configure pino explicitly to write to fd 2.
52
+ const logger = pino({ level: process.env.CLAUDE_CRAP_LOG_LEVEL ?? "info" }, pino.destination(2));
53
+ /**
54
+ * Server bootstrap. Loads configuration, instantiates the long-lived
55
+ * engines (tree-sitter, SARIF store), registers tool and resource
56
+ * handlers, and connects the stdio transport. Exits with a non-zero code
57
+ * on fatal startup errors so that Claude Code surfaces the failure to
58
+ * the user instead of silently running without the plugin.
59
+ */
60
+ async function main() {
61
+ const config = loadConfig();
62
+ logger.info({ config: { ...config, pluginRoot: "<redacted>" } }, "claude-crap MCP server starting");
63
+ // Long-lived engines. Created once at boot and reused for every call.
64
+ const astEngine = new TreeSitterEngine();
65
+ const sarifStore = new SarifStore({
66
+ workspaceRoot: config.pluginRoot,
67
+ outputDir: config.sarifOutputDir,
68
+ });
69
+ await sarifStore.loadLatest();
70
+ logger.info({ findings: sarifStore.size(), path: sarifStore.consolidatedReportPath }, "SARIF store ready");
71
+ // Try to start the local Vue.js dashboard. Failures here are
72
+ // intentionally non-fatal — the MCP server still works without it.
73
+ let dashboard = null;
74
+ try {
75
+ dashboard = await startDashboard({
76
+ config,
77
+ sarifStore,
78
+ workspaceStatsProvider: () => estimateWorkspaceLoc(config.pluginRoot),
79
+ logger,
80
+ });
81
+ }
82
+ catch (err) {
83
+ logger.warn({ err: err.message, port: config.dashboardPort }, "claude-crap dashboard failed to start — continuing without it");
84
+ }
85
+ // Make sure the dashboard is closed when the process exits so the TCP
86
+ // port is freed promptly. SIGINT/SIGTERM may arrive from Claude Code's
87
+ // MCP supervisor, from a developer hitting Ctrl-C, or from the test
88
+ // harness in our integration suite.
89
+ //
90
+ // IMPORTANT: installing a custom signal handler overrides Node's
91
+ // default (which exits the process), so we have to call
92
+ // `process.exit()` ourselves once cleanup finishes. Without this the
93
+ // MCP stdio transport would keep reading stdin forever and the
94
+ // Fastify dashboard would keep its listener open, leaving the whole
95
+ // process alive even after SIGTERM.
96
+ for (const signal of ["SIGINT", "SIGTERM"]) {
97
+ process.once(signal, () => {
98
+ void (async () => {
99
+ try {
100
+ await dashboard?.close();
101
+ }
102
+ catch {
103
+ /* best effort — dashboard may already be down */
104
+ }
105
+ // 130 is the conventional exit code for SIGINT, 143 for SIGTERM.
106
+ const exitCode = signal === "SIGINT" ? 130 : 143;
107
+ process.exit(exitCode);
108
+ })();
109
+ });
110
+ }
111
+ const server = new Server({
112
+ name: "claude-crap",
113
+ version: "0.1.0",
114
+ }, {
115
+ capabilities: {
116
+ tools: {},
117
+ resources: {},
118
+ },
119
+ });
120
+ // ------------------------------------------------------------------
121
+ // Tools — declaration (list)
122
+ // ------------------------------------------------------------------
123
+ // The tool list is what the LLM sees when it introspects the server.
124
+ // Keep the descriptions short, imperative and fact-based.
125
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
126
+ tools: [
127
+ {
128
+ name: "compute_crap",
129
+ description: "Compute the CRAP (Change Risk Anti-Patterns) index for a function and block when the score exceeds the configured threshold.",
130
+ inputSchema: computeCrapSchema,
131
+ },
132
+ {
133
+ name: "compute_tdr",
134
+ description: "Compute the Technical Debt Ratio for a scope and return the maintainability rating (A..E).",
135
+ inputSchema: computeTdrSchema,
136
+ },
137
+ {
138
+ name: "analyze_file_ast",
139
+ description: "Analyze a source file with tree-sitter and return deterministic metrics (LOC, cyclomatic complexity, function topology).",
140
+ inputSchema: analyzeFileAstSchema,
141
+ },
142
+ {
143
+ name: "ingest_sarif",
144
+ description: "Ingest a raw SARIF 2.1.0 report from an external scanner (Semgrep, ESLint, Bandit, ...), deduplicate it, and persist the consolidated view.",
145
+ inputSchema: ingestSarifSchema,
146
+ },
147
+ {
148
+ name: "ingest_scanner_output",
149
+ description: "Ingest a scanner's native output (Semgrep, ESLint, Bandit, Stryker), route it through the matching adapter, enrich each finding with an effort estimate, and persist the normalized SARIF report.",
150
+ inputSchema: ingestScannerOutputSchema,
151
+ },
152
+ {
153
+ name: "require_test_harness",
154
+ description: "Check whether a production source file has an accompanying test file. Required by the Golden Rule before any functional code is written.",
155
+ inputSchema: requireTestHarnessSchema,
156
+ },
157
+ {
158
+ name: "score_project",
159
+ description: "Aggregate the project score across Maintainability, Reliability, Security and Overall, returning a chat-friendly Markdown summary, the structured JSON, the local dashboard URL, and the consolidated SARIF report path.",
160
+ inputSchema: scoreProjectSchema,
161
+ },
162
+ ],
163
+ }));
164
+ // ------------------------------------------------------------------
165
+ // Tools — call dispatch
166
+ // ------------------------------------------------------------------
167
+ // The MCP SDK has already validated `args` against the tool's JSON
168
+ // Schema by the time this handler runs, so we cast to the expected
169
+ // shape without re-validating. Each branch delegates to a pure engine.
170
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
171
+ const { name, arguments: args } = request.params;
172
+ logger.info({ tool: name }, "Tool call received");
173
+ switch (name) {
174
+ case "compute_crap": {
175
+ const typed = args;
176
+ const result = computeCrap({ cyclomaticComplexity: typed.cyclomaticComplexity, coveragePercent: typed.coveragePercent }, config.crapThreshold);
177
+ return {
178
+ content: [
179
+ {
180
+ type: "text",
181
+ text: JSON.stringify({ tool: "compute_crap", function: typed.functionName, file: typed.filePath, ...result }, null, 2),
182
+ },
183
+ ],
184
+ // Setting isError=true tells the LLM this call should be treated
185
+ // as a failure, which pushes it toward corrective action rather
186
+ // than assuming the score was acceptable.
187
+ isError: result.exceedsThreshold,
188
+ };
189
+ }
190
+ case "compute_tdr": {
191
+ const typed = args;
192
+ const result = computeTdr({
193
+ remediationMinutes: typed.remediationMinutes,
194
+ totalLinesOfCode: typed.totalLinesOfCode,
195
+ minutesPerLoc: config.minutesPerLoc,
196
+ });
197
+ return {
198
+ content: [
199
+ {
200
+ type: "text",
201
+ text: JSON.stringify({ tool: "compute_tdr", scope: typed.scope, ...result }, null, 2),
202
+ },
203
+ ],
204
+ };
205
+ }
206
+ case "analyze_file_ast": {
207
+ const typed = args;
208
+ const absolutePath = resolveWithinWorkspace(config.pluginRoot, typed.filePath);
209
+ try {
210
+ const metrics = await astEngine.analyzeFile({
211
+ filePath: absolutePath,
212
+ language: typed.language,
213
+ });
214
+ return {
215
+ content: [
216
+ {
217
+ type: "text",
218
+ text: JSON.stringify({ tool: "analyze_file_ast", ...metrics }, null, 2),
219
+ },
220
+ ],
221
+ };
222
+ }
223
+ catch (err) {
224
+ logger.error({ err, filePath: absolutePath, language: typed.language }, "analyze_file_ast failed");
225
+ return {
226
+ content: [
227
+ {
228
+ type: "text",
229
+ text: JSON.stringify({
230
+ tool: "analyze_file_ast",
231
+ status: "error",
232
+ message: err.message,
233
+ filePath: typed.filePath,
234
+ language: typed.language,
235
+ }, null, 2),
236
+ },
237
+ ],
238
+ isError: true,
239
+ };
240
+ }
241
+ }
242
+ case "score_project": {
243
+ const typed = (args ?? {});
244
+ const format = typed.format ?? "both";
245
+ try {
246
+ const workspace = await estimateWorkspaceLoc(config.pluginRoot);
247
+ const score = computeProjectScore({
248
+ workspaceRoot: config.pluginRoot,
249
+ minutesPerLoc: config.minutesPerLoc,
250
+ tdrMaxRating: config.tdrMaxRating,
251
+ workspace: { physicalLoc: workspace.physicalLoc, fileCount: workspace.fileCount },
252
+ sarifStore,
253
+ dashboardUrl: dashboard?.url ?? null,
254
+ sarifReportPath: sarifStore.consolidatedReportPath,
255
+ });
256
+ const blocks = [];
257
+ if (format === "markdown" || format === "both") {
258
+ blocks.push({ type: "text", text: renderProjectScoreMarkdown(score) });
259
+ }
260
+ if (format === "json" || format === "both") {
261
+ blocks.push({ type: "text", text: JSON.stringify(score, null, 2) });
262
+ }
263
+ // Respect the workspace strictness setting: only `strict`
264
+ // mode should flag a failing project as an MCP tool error
265
+ // and push the agent toward remediation. In `warn` and
266
+ // `advisory` modes the Stop hook lets the task close, so
267
+ // `score_project` must stay consistent and return the
268
+ // score as plain content.
269
+ const strictness = safeLoadStrictness(config.pluginRoot, logger);
270
+ const shouldFlagError = strictness === "strict" && !score.overall.passes;
271
+ return {
272
+ content: blocks,
273
+ isError: shouldFlagError,
274
+ };
275
+ }
276
+ catch (err) {
277
+ logger.error({ err }, "score_project failed");
278
+ return {
279
+ content: [
280
+ {
281
+ type: "text",
282
+ text: JSON.stringify({ tool: "score_project", status: "error", message: err.message }, null, 2),
283
+ },
284
+ ],
285
+ isError: true,
286
+ };
287
+ }
288
+ }
289
+ case "require_test_harness": {
290
+ const typed = args;
291
+ const absolutePath = resolveWithinWorkspace(config.pluginRoot, typed.filePath);
292
+ try {
293
+ const resolution = await findTestFile(config.pluginRoot, absolutePath);
294
+ const hasTest = resolution.testFile !== null;
295
+ return {
296
+ content: [
297
+ {
298
+ type: "text",
299
+ text: JSON.stringify({
300
+ tool: "require_test_harness",
301
+ filePath: typed.filePath,
302
+ hasTest,
303
+ isTestFile: resolution.isTestFile,
304
+ testFile: resolution.testFile,
305
+ candidates: resolution.candidates,
306
+ ...(hasTest
307
+ ? {}
308
+ : {
309
+ corrective: "No test file found. Per the CLAUDE.md Golden Rule, create a characterization " +
310
+ "test at one of the candidate paths before writing any functional code for this file.",
311
+ }),
312
+ }, null, 2),
313
+ },
314
+ ],
315
+ // The Golden Rule says "no code without a test", so the absence
316
+ // of a test is a blocking condition. Surface it as an error.
317
+ isError: !hasTest,
318
+ };
319
+ }
320
+ catch (err) {
321
+ logger.error({ err, filePath: absolutePath }, "require_test_harness failed");
322
+ return {
323
+ content: [
324
+ {
325
+ type: "text",
326
+ text: JSON.stringify({
327
+ tool: "require_test_harness",
328
+ status: "error",
329
+ message: err.message,
330
+ filePath: typed.filePath,
331
+ }, null, 2),
332
+ },
333
+ ],
334
+ isError: true,
335
+ };
336
+ }
337
+ }
338
+ case "ingest_scanner_output": {
339
+ const typed = args;
340
+ try {
341
+ const adapted = adaptScannerOutput(typed.scanner, typed.rawOutput);
342
+ // F-A05-01: validate the adapter's output against the same
343
+ // schema used by `ingest_sarif`. Adapters are internal and
344
+ // should already emit conformant documents, but this catches
345
+ // regressions before they reach the store or the dashboard.
346
+ validateSarifDocument(adapted.document);
347
+ const stats = sarifStore.ingestRun(adapted.document, adapted.sourceTool);
348
+ await sarifStore.persist();
349
+ return {
350
+ content: [
351
+ {
352
+ type: "text",
353
+ text: JSON.stringify({
354
+ tool: "ingest_scanner_output",
355
+ status: "accepted",
356
+ scanner: typed.scanner,
357
+ findingsParsed: adapted.findingCount,
358
+ totalEffortMinutes: adapted.totalEffortMinutes,
359
+ accepted: stats.accepted,
360
+ duplicates: stats.duplicates,
361
+ total: stats.total,
362
+ storeSize: sarifStore.size(),
363
+ reportPath: sarifStore.consolidatedReportPath,
364
+ }, null, 2),
365
+ },
366
+ ],
367
+ };
368
+ }
369
+ catch (err) {
370
+ logger.error({ err, scanner: typed.scanner }, "ingest_scanner_output failed");
371
+ return {
372
+ content: [
373
+ {
374
+ type: "text",
375
+ text: JSON.stringify({
376
+ tool: "ingest_scanner_output",
377
+ status: "error",
378
+ scanner: typed.scanner,
379
+ message: err.message,
380
+ }, null, 2),
381
+ },
382
+ ],
383
+ isError: true,
384
+ };
385
+ }
386
+ }
387
+ case "ingest_sarif": {
388
+ const typed = args;
389
+ try {
390
+ // F-A05-01: validate the caller-supplied document against a
391
+ // minimal SARIF 2.1.0 schema BEFORE touching the store. The
392
+ // MCP SDK already validated the outer tool-call shape, but
393
+ // the inner `sarifDocument` is declared as `type: "object"`
394
+ // in tool-schemas.ts and would otherwise flow through
395
+ // un-checked.
396
+ validateSarifDocument(typed.sarifDocument);
397
+ const stats = sarifStore.ingestRun(typed.sarifDocument, typed.sourceTool);
398
+ await sarifStore.persist();
399
+ return {
400
+ content: [
401
+ {
402
+ type: "text",
403
+ text: JSON.stringify({
404
+ tool: "ingest_sarif",
405
+ status: "accepted",
406
+ sourceTool: typed.sourceTool,
407
+ accepted: stats.accepted,
408
+ duplicates: stats.duplicates,
409
+ total: stats.total,
410
+ storeSize: sarifStore.size(),
411
+ reportPath: sarifStore.consolidatedReportPath,
412
+ }, null, 2),
413
+ },
414
+ ],
415
+ };
416
+ }
417
+ catch (err) {
418
+ logger.error({ err, sourceTool: typed.sourceTool }, "ingest_sarif failed");
419
+ return {
420
+ content: [
421
+ {
422
+ type: "text",
423
+ text: JSON.stringify({ tool: "ingest_sarif", status: "error", message: err.message }, null, 2),
424
+ },
425
+ ],
426
+ isError: true,
427
+ };
428
+ }
429
+ }
430
+ default:
431
+ throw new Error(`[claude-crap] Unknown tool: ${name}`);
432
+ }
433
+ });
434
+ // ------------------------------------------------------------------
435
+ // Resources — topology and reports
436
+ // ------------------------------------------------------------------
437
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
438
+ resources: [
439
+ {
440
+ uri: "sonar://metrics/current",
441
+ name: "Current project metrics",
442
+ mimeType: "application/json",
443
+ description: "Snapshot of CRAP, TDR, and Reliability / Security ratings.",
444
+ },
445
+ {
446
+ uri: "sonar://reports/latest.sarif",
447
+ name: "Latest consolidated SARIF 2.1.0 report",
448
+ mimeType: "application/sarif+json",
449
+ description: "Unified SARIF document produced by the most recent Stop quality-gate run.",
450
+ },
451
+ ],
452
+ }));
453
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
454
+ const { uri } = request.params;
455
+ if (uri === "sonar://reports/latest.sarif") {
456
+ const doc = sarifStore.toSarifDocument();
457
+ return {
458
+ contents: [
459
+ {
460
+ uri,
461
+ mimeType: "application/sarif+json",
462
+ text: JSON.stringify(doc, null, 2),
463
+ },
464
+ ],
465
+ };
466
+ }
467
+ if (uri === "sonar://metrics/current") {
468
+ const snapshot = await buildMetricsSnapshot(config, sarifStore);
469
+ return {
470
+ contents: [
471
+ {
472
+ uri,
473
+ mimeType: "application/json",
474
+ text: JSON.stringify(snapshot, null, 2),
475
+ },
476
+ ],
477
+ };
478
+ }
479
+ throw new Error(`[claude-crap] Unknown resource URI: ${uri}`);
480
+ });
481
+ const transport = new StdioServerTransport();
482
+ await server.connect(transport);
483
+ logger.info("claude-crap MCP server ready (stdio)");
484
+ }
485
+ /**
486
+ * Load the workspace strictness without letting a busted config
487
+ * file take down the `score_project` tool. On any loader error we
488
+ * log to stderr via pino and fall back to `"strict"` so the tool
489
+ * stays useful. This is the MCP-server-side counterpart to the
490
+ * `resolveStrictness` helper in `hooks/stop-quality-gate.mjs`.
491
+ *
492
+ * @param workspaceRoot Absolute path the loader should probe for
493
+ * `.claude-crap.json`.
494
+ * @param logger Pino logger used to surface recoverable
495
+ * config errors.
496
+ * @returns The resolved strictness, or `"strict"` on
497
+ * error.
498
+ */
499
+ function safeLoadStrictness(workspaceRoot, logger) {
500
+ try {
501
+ return loadCrapConfig({ workspaceRoot }).strictness;
502
+ }
503
+ catch (err) {
504
+ if (err instanceof CrapConfigError) {
505
+ logger.warn({ err: err.message }, "score_project: invalid sonar config, falling back to strict");
506
+ return "strict";
507
+ }
508
+ throw err;
509
+ }
510
+ }
511
+ /**
512
+ * Build a lightweight metrics snapshot that the LLM can read through
513
+ * the `sonar://metrics/current` resource. This is intentionally thin
514
+ * and side-effect free: it derives everything from the in-memory
515
+ * SARIF store without walking the workspace. Callers that need a
516
+ * full scoring payload (with a real LOC walk and the A..E grades per
517
+ * dimension) should invoke the `score_project` tool, which uses the
518
+ * bounded workspace walker and the `metrics/score.ts` engine.
519
+ *
520
+ * @param config Fully resolved server configuration.
521
+ * @param sarifStore Live SARIF store used to read the latest findings.
522
+ */
523
+ async function buildMetricsSnapshot(config, sarifStore) {
524
+ const findings = sarifStore.list();
525
+ const totalRemediationMinutes = findings.reduce((sum, f) => {
526
+ const effort = f.properties?.["effortMinutes"];
527
+ return typeof effort === "number" ? sum + effort : sum;
528
+ }, 0);
529
+ // Cheap LOC approximation derived from the SARIF report: assume
530
+ // ~100 physical lines per file we have at least one finding in.
531
+ // This keeps the resource read lock-free and synchronous-feeling;
532
+ // the `score_project` tool is the authoritative path when a real
533
+ // workspace walk is required.
534
+ const uniqueFiles = new Set(findings.map((f) => f.location.uri));
535
+ const approxLoc = Math.max(uniqueFiles.size * 100, 1);
536
+ const tdrPercent = totalRemediationMinutes / (config.minutesPerLoc * approxLoc) * 100;
537
+ const rating = classifyTdr(Number.isFinite(tdrPercent) ? tdrPercent : 0);
538
+ return {
539
+ generatedAt: new Date().toISOString(),
540
+ config: {
541
+ crapThreshold: config.crapThreshold,
542
+ tdrMaxRating: config.tdrMaxRating,
543
+ minutesPerLoc: config.minutesPerLoc,
544
+ },
545
+ sarif: {
546
+ reportPath: sarifStore.consolidatedReportPath,
547
+ findings: findings.length,
548
+ files: uniqueFiles.size,
549
+ tools: Array.from(new Set(findings.map((f) => f.sourceTool))),
550
+ },
551
+ tdrApprox: {
552
+ percent: Number(tdrPercent.toFixed(4)),
553
+ rating,
554
+ remediationMinutes: totalRemediationMinutes,
555
+ approxLinesOfCode: approxLoc,
556
+ },
557
+ };
558
+ }
559
+ // Top-level await would be cleaner, but we keep main() + .catch() so
560
+ // any error during async bootstrap (engine init, store load) surfaces as
561
+ // a non-zero exit code visible to Claude Code's MCP diagnostics.
562
+ main().catch((err) => {
563
+ // Fatal errors go to stderr to avoid corrupting the JSON-RPC channel
564
+ // on stdout. We use `process.stderr.write` rather than `console.error`
565
+ // so that no lint suppression is needed and so that no buffering layer
566
+ // can swallow the message. A non-zero exit code causes Claude Code to
567
+ // surface the failure in its MCP-server diagnostics.
568
+ process.stderr.write(`[claude-crap] fatal error during startup: ${String(err)}\n`);
569
+ if (err instanceof Error && err.stack) {
570
+ process.stderr.write(err.stack + "\n");
571
+ }
572
+ process.exit(1);
573
+ });
574
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}