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
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env node
2
+ // @ts-check
3
+ /**
4
+ * claude-crap :: Stop / SubagentStop hook — final quality gate.
5
+ *
6
+ * When the agent (or a subagent) declares it is done with a task,
7
+ * this hook has the last word. It reads the consolidated SARIF
8
+ * report, estimates workspace LOC, computes the Technical Debt
9
+ * Ratio, and checks every configured policy:
10
+ *
11
+ * - `SONAR-GATE-TDR` — project TDR rating must not exceed
12
+ * the configured `TDR_MAX_RATING`.
13
+ * - `SONAR-GATE-ERRORS` — no SARIF finding at level "error" may
14
+ * survive.
15
+ *
16
+ * Strictness (v0.1.0):
17
+ *
18
+ * The behavior on a failing verdict is controlled by the
19
+ * workspace sonar configuration. Teams can adopt claude-crap in
20
+ * stages by editing `.claude-crap.json` at their workspace root
21
+ * or setting the `CLAUDE_CRAP_STRICTNESS` environment variable.
22
+ * Three modes are supported:
23
+ *
24
+ * - `strict` (default) — exit 2 with the full BLOCKED box on
25
+ * stderr. Claude Code injects stderr into the agent's context,
26
+ * so the agent must remediate before retrying the Stop hook.
27
+ * This is the original, hard-failing behavior of the plugin.
28
+ * - `warn` — exit 0 with the full WARNING box on
29
+ * stdout. The task is allowed to close, but the hook transcript
30
+ * still carries every failing rule so the agent can choose to
31
+ * remediate on its next turn.
32
+ * - `advisory` — exit 0 with a single-line ADVISORY note
33
+ * on stdout. Minimal pressure on the agent.
34
+ *
35
+ * A passing verdict always exits 0 and emits the same JSON status
36
+ * line regardless of strictness.
37
+ *
38
+ * This hook deliberately lives outside the MCP server process so
39
+ * that it runs deterministically even if the server is momentarily
40
+ * disconnected. It imports the TDR classification functions
41
+ * directly from the compiled MCP server's `dist/` so the math is
42
+ * the single source of truth.
43
+ *
44
+ * @module hooks/stop-quality-gate
45
+ */
46
+
47
+ import { ExitCodes, readStdinJson, runHook } from "./lib/hook-io.mjs";
48
+ import { evaluateQualityGate, loadQualityGateConfig } from "./lib/quality-gate.mjs";
49
+ import {
50
+ DEFAULT_STRICTNESS,
51
+ loadCrapConfig,
52
+ CrapConfigError,
53
+ } from "./lib/crap-config.mjs";
54
+
55
+ /**
56
+ * Resolve the effective strictness for this Stop hook invocation.
57
+ * Falls back to {@link DEFAULT_STRICTNESS} on any loader error so a
58
+ * busted `.claude-crap.json` never deadlocks the user — the file
59
+ * error is logged to stderr for diagnostics but the gate still
60
+ * runs.
61
+ *
62
+ * @param {string} workspaceRoot Absolute path to the user's workspace.
63
+ * @returns {{strictness: import("./lib/crap-config.mjs").Strictness, source: "env" | "file" | "default" | "fallback"}}
64
+ */
65
+ function resolveStrictness(workspaceRoot) {
66
+ try {
67
+ const config = loadCrapConfig({ workspaceRoot });
68
+ return { strictness: config.strictness, source: config.strictnessSource };
69
+ } catch (err) {
70
+ if (err instanceof CrapConfigError) {
71
+ process.stderr.write(
72
+ `[claude-crap] Stop hook: ${err.message}\n` +
73
+ `[claude-crap] falling back to strictness='${DEFAULT_STRICTNESS}' for this run.\n`,
74
+ );
75
+ return { strictness: DEFAULT_STRICTNESS, source: "fallback" };
76
+ }
77
+ throw err;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Render the failing verdict as the existing BLOCKED box used by
83
+ * `strict` mode. Keeps the shape stable so strict-mode users see
84
+ * exactly the same output as before this feature landed.
85
+ *
86
+ * @param {import("./lib/quality-gate.mjs").GateVerdict} verdict
87
+ * @returns {string}
88
+ */
89
+ function renderBlockedBox(verdict) {
90
+ return renderVerdictBox(verdict, "BLOCKED");
91
+ }
92
+
93
+ /**
94
+ * Render the failing verdict as a WARNING box. Same information
95
+ * density as the BLOCKED box, but the header changes so agents and
96
+ * humans can distinguish a blocking gate from an advisory one.
97
+ *
98
+ * @param {import("./lib/quality-gate.mjs").GateVerdict} verdict
99
+ * @returns {string}
100
+ */
101
+ function renderWarningBox(verdict) {
102
+ return renderVerdictBox(verdict, "WARNING");
103
+ }
104
+
105
+ /**
106
+ * Render a single-line advisory note. Used by `advisory` mode so
107
+ * the agent is informed of the quality state without being pushed
108
+ * into a remediation loop.
109
+ *
110
+ * @param {import("./lib/quality-gate.mjs").GateVerdict} verdict
111
+ * @returns {string}
112
+ */
113
+ function renderAdvisoryLine(verdict) {
114
+ const { summary, failures } = verdict;
115
+ const ruleIds = failures.map((f) => f.ruleId).join(", ") || "<none>";
116
+ return (
117
+ `claude-crap :: Stop quality gate ADVISORY — ${failures.length} policy note(s). ` +
118
+ `TDR ${summary.tdrPercent}% (rating ${summary.tdrRating}), ` +
119
+ `${summary.errorFindings} error / ${summary.warningFindings} warning / ${summary.noteFindings} note, ` +
120
+ `rules: ${ruleIds}. This was an advisory run — the task may close.`
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Shared renderer for the BLOCKED and WARNING variants. The only
126
+ * difference between them is the header label and the explanatory
127
+ * sentence at the top.
128
+ *
129
+ * @param {import("./lib/quality-gate.mjs").GateVerdict} verdict
130
+ * @param {"BLOCKED" | "WARNING"} label
131
+ * @returns {string}
132
+ */
133
+ function renderVerdictBox(verdict, label) {
134
+ const { summary, failures } = verdict;
135
+ const header = [
136
+ `╭─ claude-crap :: Stop quality gate ${label} ─────────────────────`,
137
+ `│ total findings : ${summary.totalFindings}`,
138
+ `│ error / warn / note: ${summary.errorFindings} / ${summary.warningFindings} / ${summary.noteFindings}`,
139
+ `│ remediation minutes : ${summary.remediationMinutes}`,
140
+ `│ workspace LOC : ${summary.physicalLoc}`,
141
+ `│ TDR : ${summary.tdrPercent}% → rating ${summary.tdrRating}`,
142
+ `│ tools seen : ${summary.toolsSeen.join(", ") || "<none>"}`,
143
+ `│`,
144
+ `│ ${failures.length} policy failure(s):`,
145
+ `│`,
146
+ ];
147
+ /** @type {string[]} */
148
+ const body = [];
149
+ failures.forEach((f, idx) => {
150
+ body.push(`│ [${idx + 1}] ${f.ruleId}`);
151
+ for (const line of f.message.split("\n")) {
152
+ body.push(`│ ${line}`);
153
+ }
154
+ body.push(`│`);
155
+ });
156
+ const footer = [`╰──────────────────────────────────────────────────────────────────`];
157
+ return [...header, ...body, ...footer].join("\n");
158
+ }
159
+
160
+ async function main() {
161
+ // We read stdin so hook consumers see we honored the contract,
162
+ // but the Stop hook does not actually need per-call state — the
163
+ // verdict is entirely derived from the on-disk SARIF and the
164
+ // workspace.
165
+ await readStdinJson().catch(() => ({}));
166
+
167
+ const gateConfig = loadQualityGateConfig();
168
+ const verdict = await evaluateQualityGate(gateConfig);
169
+
170
+ // A passing verdict always exits 0 with the same status line
171
+ // regardless of strictness — the feature only changes the FAILING
172
+ // path.
173
+ if (verdict.passed) {
174
+ process.stdout.write(
175
+ JSON.stringify({
176
+ status: "passed",
177
+ gate: "stop",
178
+ summary: verdict.summary,
179
+ }) + "\n",
180
+ );
181
+ process.exit(ExitCodes.ALLOW);
182
+ return;
183
+ }
184
+
185
+ const { strictness } = resolveStrictness(gateConfig.workspaceRoot);
186
+
187
+ if (strictness === "strict") {
188
+ // Hard block: render the BLOCKED box to stderr so Claude Code
189
+ // injects it into the agent's context, then exit 2.
190
+ process.stderr.write(renderBlockedBox(verdict) + "\n");
191
+ process.exit(ExitCodes.BLOCK);
192
+ return;
193
+ }
194
+
195
+ if (strictness === "warn") {
196
+ // Soft nudge: render the WARNING box to stdout so it lands in
197
+ // the hook transcript and the agent still sees every failing
198
+ // rule, then exit 0 so the task is allowed to close.
199
+ process.stdout.write(renderWarningBox(verdict) + "\n");
200
+ process.stdout.write(
201
+ JSON.stringify({
202
+ status: "warning",
203
+ gate: "stop",
204
+ strictness: "warn",
205
+ summary: verdict.summary,
206
+ }) + "\n",
207
+ );
208
+ process.exit(ExitCodes.ALLOW);
209
+ return;
210
+ }
211
+
212
+ // strictness === "advisory"
213
+ // Minimal pressure: single-line note on stdout, then exit 0.
214
+ process.stdout.write(renderAdvisoryLine(verdict) + "\n");
215
+ process.stdout.write(
216
+ JSON.stringify({
217
+ status: "advisory",
218
+ gate: "stop",
219
+ strictness: "advisory",
220
+ summary: verdict.summary,
221
+ }) + "\n",
222
+ );
223
+ process.exit(ExitCodes.ALLOW);
224
+ }
225
+
226
+ runHook("Stop quality gate", main);
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "claude-crap-plugin",
3
+ "version": "0.1.2",
4
+ "private": true,
5
+ "description": "Runtime dependencies for the claude-crap plugin bundle",
6
+ "type": "module",
7
+ "dependencies": {
8
+ "@fastify/static": "^8.0.3",
9
+ "@modelcontextprotocol/sdk": "^1.0.4",
10
+ "fastify": "^5.2.0",
11
+ "pino": "^9.5.0",
12
+ "tree-sitter-wasms": "^0.1.12",
13
+ "web-tree-sitter": "^0.24.4"
14
+ },
15
+ "engines": {
16
+ "node": ">=20.0.0"
17
+ }
18
+ }
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: adopt
3
+ description: Interactive onboarding walkthrough for teams introducing claude-crap on an existing codebase. Use this skill whenever the user asks "how do I install claude-crap on my project", "we're adopting claude-crap for our team", "how should I roll claude-crap out", "what strictness mode should I pick", "we have a lot of legacy code, can we ease into this gradually", "our project has a bunch of existing errors, how do I avoid getting blocked", or is otherwise looking for guidance on gradual rollout strategy instead of just dropping a strict quality gate on a messy codebase. The skill asks a short three-question assessment about test coverage, existing findings, and appetite for hard enforcement, recommends one of three strictness modes (advisory / warn / strict), and emits a copy-pasteable .claude-crap.json snippet for the workspace root so the team can commit the policy in a single step.
4
+ ---
5
+
6
+ # Help a team adopt claude-crap gradually
7
+
8
+ Walk the user through picking the right initial strictness mode for claude-crap on their workspace, then hand them the exact `.claude-crap.json` to commit. The whole interaction should take under two minutes — it is an onboarding assistant, not a full quality audit.
9
+
10
+ ## Interview
11
+
12
+ Ask the user these three questions, one at a time. Wait for each answer before moving to the next so the user is not overwhelmed.
13
+
14
+ 1. **"How much existing test coverage does your workspace have — would you call it green (80%+, most modules are tested), patchy (some well-tested, some bare), or near-zero?"**
15
+ 2. **"Have you run any SAST or quality scanners on the workspace recently — Semgrep, ESLint with security rules, Bandit, Stryker, anything like that? Roughly how many `error`-level findings would a fresh scan produce: zero, a handful (say 1–5), or more than that?"**
16
+ 3. **"Does the team want claude-crap to hard-block task closures from day one, or would you rather see the quality verdict for a week first and decide to enforce it later?"**
17
+
18
+ ## Recommendation logic
19
+
20
+ Pick the strictness using this table. The principle is simple: if the project would immediately red-light under strict mode, don't start there — the team will just disable the plugin. Start at the loosest mode that still surfaces the findings, and tighten over time.
21
+
22
+ | Test coverage | Existing error findings | Wants hard block from day one? | Recommend |
23
+ | :------------ | :---------------------- | :----------------------------- | :---------- |
24
+ | Green | 0 | Yes | `strict` |
25
+ | Green | 0 | No / unsure | `warn` |
26
+ | Green | 1–5 | Either | `warn` |
27
+ | Patchy | 0 | Either | `warn` |
28
+ | Patchy | 1–5 | Either | `advisory` |
29
+ | Patchy | 6+ | Either | `advisory` |
30
+ | Near-zero | Any | Any | `advisory` |
31
+
32
+ If the user's answers fall outside the table exactly, lean toward the looser mode. Rolling out tight and loosening is painful; rolling out loose and tightening is natural.
33
+
34
+ ## Produce the config
35
+
36
+ Output the recommended `.claude-crap.json` in a fenced JSON code block. Use this exact shape, substituting `<recommended-mode>` with the actual value from the table (`strict`, `warn`, or `advisory`):
37
+
38
+ ```jsonc
39
+ // .claude-crap.json — commit this to the workspace root
40
+ {
41
+ "$schema": "https://raw.githubusercontent.com/ahernandez-developer/claude-crap/main/schemas/crap-config.json",
42
+ "strictness": "<recommended-mode>"
43
+ }
44
+ ```
45
+
46
+ Then explain what happens in the mode you picked, in one sentence per mode:
47
+
48
+ - **strict**: The Stop hook exits 2 on any policy failure. The full verdict is injected into the agent's context via stderr, and the task cannot close until the rules are satisfied. Use this when you want claude-crap to enforce the same way CI would.
49
+ - **warn**: The Stop hook exits 0 but writes the full verdict to stdout so the agent still sees every failing rule in its hook transcript. The task is allowed to close, and the agent can choose to remediate voluntarily on the next turn. Use this when you want pressure without blocking.
50
+ - **advisory**: The Stop hook exits 0 and writes a one-line summary to stdout. Minimal pressure on the agent — the task closes with a soft nudge. Use this when you're just collecting data and want the team to see the quality readings before enforcing anything.
51
+
52
+ ## Follow-up roadmap
53
+
54
+ End with a one-paragraph gradual-adoption roadmap tailored to the mode you picked:
55
+
56
+ 1. **Commit `.claude-crap.json`** to the workspace root so the whole team picks up the policy.
57
+ 2. **Let the team run for 1–2 weeks** in that mode, reviewing the local Vue dashboard at `http://127.0.0.1:5117` to see the SARIF findings accumulate.
58
+ 3. **Tighten the mode by one step** (`advisory` → `warn` → `strict`) once the team is comfortable with what they're seeing and the finding count is trending down.
59
+ 4. **Advanced tip**: any team member can override per-session with `CLAUDE_CRAP_STRICTNESS=<mode> claude` — useful for a one-off lenient run during an emergency, without changing the committed policy.
60
+
61
+ ## Why this skill exists
62
+
63
+ Adopting a deterministic quality gate on an existing codebase is a change-management problem more than a technical one. Teams that jump straight to `strict` mode without checking their baseline tend to hit a wall the first time the Stop hook blocks a task close, get frustrated, and disable the plugin — losing all the benefit of the PreToolUse gatekeeper and the PostToolUse verifier too, which would have worked fine at their current codebase.
64
+
65
+ This skill turns the adoption question into a 30-second assessment and a ready-to-commit config. The three questions are deliberately minimal: they capture the only signals that actually matter for picking an initial mode (coverage, baseline findings, enforcement appetite), and they avoid the overhead of running a full scan before the team has even installed the plugin.
66
+
67
+ The gradual-adoption roadmap is the other half of the value: most teams never pick the "right" strictness on the first try, and the skill's job is to leave them with a clear path to tighten over time instead of a one-shot recommendation they'll regret in a month.
68
+
69
+ ## Do not
70
+
71
+ - Do not recommend `strict` mode to a team that has any unresolved `error`-level findings in a recent scan. That is a guaranteed rage-quit on the first task close.
72
+ - Do not skip the interview questions. The whole point is that the recommendation matches the team's actual situation, not some default assumption about what they "should" want.
73
+ - Do not output a placeholder like `<recommended-mode>` in the JSON code block. Fill it with the actual value from the table so the user can copy-paste directly without editing.
74
+ - Do not run `score_project` as part of this skill. The interview is faster and gives the team more control over the recommendation than a baseline scan would. Reach for `/claude-crap:score` separately if they ask for hard numbers.
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: analyze
3
+ description: Run tree-sitter AST analysis on a source file using claude-crap's deterministic engine and report per-function cyclomatic complexity plus physical and logical lines of code. Use this skill whenever the user asks "what's the complexity of this file", "how complex is foo.ts", "show me cyclomatic complexity per function", "analyze this file's structure", "which functions in this file are too complex", "where should I refactor first in this module", or needs to pick candidates for refactoring based on complexity. Also use this skill proactively when a file feels "big" and you want a ranked list of hot-spot functions before proposing a refactor plan — the tree-sitter engine gives exact numbers instead of eyeball estimates. Supports TypeScript, JavaScript, Python, Java, and C#; the language is detected automatically from the file extension. Takes a single file path as the argument after the skill name.
4
+ ---
5
+
6
+ # Analyze a source file with tree-sitter
7
+
8
+ Run the `analyze_file_ast` MCP tool from the claude-crap server against a user-supplied file and report deterministic AST metrics.
9
+
10
+ ## Arguments
11
+
12
+ The user supplies a file path as the argument, typed after the skill name:
13
+
14
+ ```
15
+ /claude-crap:analyze src/index.ts
16
+ /claude-crap:analyze app/main.py
17
+ /claude-crap:analyze lib/foo.cs
18
+ ```
19
+
20
+ ## Steps
21
+
22
+ 1. **Parse the file path**. Take `$ARGUMENTS` as the path. Reject it with a friendly one-liner if it contains `../` — the server's workspace guard will reject path traversal attempts regardless, but failing fast in the skill is cleaner than surfacing a stack trace from the MCP layer.
23
+
24
+ 2. **Detect the language from the extension**. Use this table:
25
+
26
+ | Extension | `language` argument |
27
+ | :------------------------------------------ | :------------------ |
28
+ | `.ts` / `.tsx` / `.mts` / `.cts` | `typescript` |
29
+ | `.js` / `.jsx` / `.mjs` / `.cjs` | `javascript` |
30
+ | `.py` | `python` |
31
+ | `.java` | `java` |
32
+ | `.cs` | `csharp` |
33
+
34
+ If the extension does not map to one of those five, tell the user which languages are supported and do not invoke the tool. Claude-sonar's tree-sitter engine is language-scoped; there is no "auto-detect from content" mode.
35
+
36
+ 3. **Invoke the MCP tool** `analyze_file_ast` with `filePath: "$ARGUMENTS"` and the detected `language`.
37
+
38
+ 4. **Display file-level metrics first**: physical LOC (every newline-terminated line including blanks and comments) and logical LOC (lines with at least one non-whitespace character). The delta between the two is an informal "comment density" signal.
39
+
40
+ 5. **Display a ranked function list**, sorted by cyclomatic complexity descending. For each function, show:
41
+ - Function name (or `<anonymous>` if the tree-sitter grammar could not resolve a name)
42
+ - Start line → end line
43
+ - Cyclomatic complexity
44
+ - Physical line count (`endLine - startLine + 1`)
45
+
46
+ 6. **Flag refactoring candidates**. Any function whose cyclomatic complexity exceeds `15` (the plugin's default `cyclomaticMax`) is a Stop-gate warning candidate — call those out explicitly as "above the cyclomatic ceiling; refactor candidate". A function above `30` will fail the CRAP quality gate regardless of coverage and MUST be decomposed; those are the highest-priority targets.
47
+
48
+ 7. **Handle errors gracefully**. If the tool returns `status: "error"`, the most likely cause is a tree-sitter grammar that failed to load (`Could not load wasm grammar for language X`). Tell the user they may need to reinstall the plugin via `/plugin install claude-crap@herz` so the bundled WASM grammars are re-extracted into the plugin cache.
49
+
50
+ ## What the user will see
51
+
52
+ A compact report along the lines of:
53
+
54
+ ```
55
+ src/index.ts — 658 LOC (512 logical)
56
+
57
+ Functions ranked by cyclomatic complexity:
58
+ 1. handleRequest lines 120–245 CC=22 (126 lines) ⚠ above cyclomatic ceiling (15)
59
+ 2. parseBody lines 48–102 CC=14 ( 55 lines)
60
+ 3. sendResponse lines 300–340 CC= 9 ( 41 lines)
61
+ 4. init lines 10–30 CC= 3 ( 21 lines)
62
+
63
+ ⚠ `handleRequest` has CC=22 (ceiling is 15). Refactor candidate.
64
+ ```
65
+
66
+ Exact formatting can vary — the important thing is that the function list is ranked and the refactoring candidates are called out explicitly.
67
+
68
+ ## Why this skill exists
69
+
70
+ The `analyze_file_ast` MCP tool is the deterministic alternative to "Claude reads the file and estimates complexity." Manual complexity estimation is a known failure mode for LLMs — we overcount branches, we undercount nested constructs, and we are not self-consistent across runs. The tree-sitter engine gives exact, reproducible per-function metrics that the user can trust for refactoring decisions, and the numbers match what the Stop quality gate will eventually grade against, so using this tool up-front closes the loop between exploration and enforcement.
71
+
72
+ ## Do not
73
+
74
+ - Do not invoke this skill for files outside the workspace. The workspace guard will reject them; use a workspace-relative path.
75
+ - Do not compute cyclomatic complexity by reading the file and counting branches yourself. The tree-sitter engine is the single source of truth. If the user disagrees with the number, the disagreement is almost always a misunderstanding of where a branch lives in the AST, not a bug in the engine.
76
+ - Do not refactor based on eyeball estimates of complexity when this tool is available. A refactor proposal that cites "handleRequest is too complex" without an exact CC number is not actionable.
77
+ - Do not invoke the skill on a minified file — the numbers will be meaningless (the whole file is one "function" to the parser). Minified artifacts belong in `.gitignore`, not in the analyzer.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: check-test
3
+ description: Check whether a production source file has an accompanying characterization test using claude-crap's deterministic test-harness resolver. Use this skill whenever the user asks "is this file tested", "does foo.ts have a test", "where's the test for bar.py", "check test coverage for this file", "do I have a test harness for src/baz.java", or is about to modify a source file and wants to know if the CLAUDE.md Golden Rule test prerequisite is already satisfied. Use it proactively at the start of any work on a .ts / .tsx / .js / .jsx / .mjs / .cjs / .py / .java / .cs file to verify a characterization test exists — this is required by CLAUDE.md and claude-crap's PostToolUse hook will flag the violation later if you skip the check. The skill takes a single file-path argument after the skill name.
4
+ ---
5
+
6
+ # Check the test harness for a file
7
+
8
+ Run the `require_test_harness` MCP tool from the claude-crap server against a user-supplied file path and report whether a matching test exists.
9
+
10
+ ## Arguments
11
+
12
+ The user supplies a file path as the argument, typed after the skill name:
13
+
14
+ ```
15
+ /claude-crap:check-test src/foo/bar.ts
16
+ /claude-crap:check-test pkg/mod.py
17
+ /claude-crap:check-test app/src/main/java/com/example/Service.java
18
+ ```
19
+
20
+ Pass `$ARGUMENTS` verbatim as the `filePath` argument to the MCP tool. Do not normalize, strip quotes, or resolve the path yourself — the server's workspace guard handles that.
21
+
22
+ ## Steps
23
+
24
+ 1. Invoke the MCP tool `require_test_harness` with `filePath: "$ARGUMENTS"`.
25
+ 2. If the tool returns `hasTest: true`:
26
+ - Tell the user the test file path from the `testFile` field, formatted as a clickable relative path.
27
+ - If `isTestFile: true`, note that the user's input was itself already a test file — the resolver short-circuited. This usually means the user meant to ask about a different file; suggest they re-run the skill with the production file path.
28
+ 3. If the tool returns `hasTest: false`:
29
+ - Tell the user no matching test was found.
30
+ - Remind them that CLAUDE.md's Golden Rule forbids writing functional code in this file until a characterization test exists — that's the plugin's core contract.
31
+ - List the first 3 paths from the `candidates` array as suggested locations for the new test. These are ordered by the resolver's convention priority: sibling `<name>.test.<ext>` first, then `__tests__/<name>.test.<ext>`, then mirror-tree layouts under `tests/`, `test/`, and `__tests__/` at the workspace root, then the nearest-ancestor flat `tests/` directory, then Python `test_<name>.py` variants. Suggest the highest-priority candidate that matches the project's existing conventions — if you can see other test files in the repo, pick the layout that matches them.
32
+ 4. If the tool returns an error (`status: "error"`):
33
+ - The most common cause is a path that escapes the workspace root. Tell the user to use a workspace-relative path (e.g. `src/foo.ts`, not `../other-repo/foo.ts`).
34
+ - The second-most-common cause is a typo in the file path. Ask them to double-check the path exists.
35
+
36
+ ## What the user will see
37
+
38
+ A one-line verdict ("Test found: `src/foo.test.ts`" or "No test found") followed by guidance on what to do next. For the "no test" case, the list of candidate paths is the most valuable thing — it tells the user exactly where the plugin's resolver expects the test to live, without them having to read the resolver source.
39
+
40
+ ## Why this skill exists
41
+
42
+ The Golden Rule in CLAUDE.md forbids writing functional code before a characterization test exists. The `require_test_harness` tool is the deterministic check for that prerequisite, and it is wired into the PostToolUse hook to catch violations after the fact. But a pre-flight check before starting work on a file is cheaper than fighting the PostToolUse warning later — the user can decide up front whether to write the test first or pick a different file.
43
+
44
+ The resolver understands seven conventions (sibling, `__tests__`, mirror tree, nearest-ancestor flat `tests/`, Python `test_` prefix, and two more) so it works across the five languages the plugin supports without requiring the user to configure anything.
45
+
46
+ ## Do not
47
+
48
+ - Do not guess at test file locations. The resolver enumerates every convention claude-crap supports — if it returns `hasTest: false`, a matching test genuinely does not exist yet.
49
+ - Do not suggest the user rename an existing spec file to match the convention. Instead, create a new test file at the highest-priority candidate path so the existing test remains in place if other tooling depends on its name.
50
+ - Do not invoke the skill with an absolute path that points outside the workspace. The workspace guard will reject it, and the user will be confused about why.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: score
3
+ description: Run claude-crap's deterministic project quality gate and display the A..E letter grade across Maintainability, Reliability, Security, and Overall. Use this skill whenever the user asks "how's the code quality", "what's my CRAP index", "is this project shippable", "show me the technical debt ratio", "run the quality gate", "score the project", or wants a one-glance read on whether the current workspace passes its policy ceiling. Also use this skill proactively at the start of a refactoring session or before a release to establish a quality baseline, and after a substantial refactor to verify the grade has not regressed. Do NOT use it for incident debugging or per-file analysis — for a single file, reach for /claude-crap:analyze instead.
4
+ ---
5
+
6
+ # Score the project
7
+
8
+ Run the `score_project` MCP tool from the claude-crap server and display the result.
9
+
10
+ ## Steps
11
+
12
+ 1. Invoke the MCP tool `score_project` with `format: "both"` so you get both the Markdown summary and the JSON snapshot in one call.
13
+ 2. Display the Markdown summary verbatim. Do not paraphrase, round, or reorder the dimension ratings — they are deterministic, they come directly from the CRAP / TDR / rating engines, and they must be shown exactly as the engine produced them so the user can verify against the dashboard.
14
+ 3. If the tool response has `isError: true`, the overall rating is worse than the configured policy ceiling under strict mode. Explicitly tell the user their workspace **FAILS** the policy, surface the rule IDs from the failures array (e.g. `SONAR-GATE-TDR`, `SONAR-GATE-ERRORS`), and recommend they drill into the dashboard URL or the consolidated SARIF report to see the underlying findings.
15
+ 4. If the tool succeeds, tell the user the dashboard URL from the Markdown content so they can open it in a browser for per-file hot spots, and the SARIF report path so they can grep it with other tools if they prefer.
16
+
17
+ ## What the user will see
18
+
19
+ The output includes four A..E letter grades (one per dimension plus an overall), the TDR percent, per-level finding counts, workspace LOC, the scanners that have already ingested findings, the dashboard URL, and the SARIF report path. The overall grade is the worst of the three dimension grades by policy, so a single E-rated security finding drops the whole score to E regardless of maintainability.
20
+
21
+ ## Why this skill exists
22
+
23
+ The `score_project` MCP tool is the canonical "is this project shippable?" reading for claude-crap. It gets run automatically at the Stop quality gate on every task close, but users often want an ad-hoc read mid-session — to know whether a refactor actually improved the grade, to get a baseline before starting new work, or to decide whether to open a PR now or keep iterating. Making it a single slash command instead of a three-step MCP tool invocation removes the friction from those workflows.
24
+
25
+ The engine is deterministic: given the same SARIF store and the same workspace, `score_project` always returns the same grade. That is a feature, not a limitation — it means two developers on the same branch will see identical readings, and it means the grade is safe to cite in PR descriptions and commit messages without the usual LLM caveats about reproducibility.
26
+
27
+ ## Do not
28
+
29
+ - Do not compute CRAP or TDR by hand, or estimate the grade "based on what you've seen of the codebase". The only valid answer is what `score_project` returns.
30
+ - Do not suggest remediations that aren't grounded in the SARIF findings the tool reports. If the Security dimension is D, open the SARIF file and read the rule IDs before recommending fixes — do not speculate.
31
+ - Do not hide the dashboard URL. Users rely on it to drill into the detail the Markdown summary necessarily elides.