rebar-mcp 2.0.0

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 (248) hide show
  1. package/.claude/agents/template-writer.md +43 -0
  2. package/.claude/agents/test-runner.md +47 -0
  3. package/.claude/mcp.json +9 -0
  4. package/.claude/settings.json +29 -0
  5. package/.claude/skills/ /SKILL.md +21 -0
  6. package/.claude/skills/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SKILL.md +21 -0
  7. package/.claude/skills/bmmibwetxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  8. package/.claude/skills/bmmibwjgvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  9. package/.claude/skills/bmmibwsesxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  10. package/.claude/skills/bmmibwxufxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  11. package/.claude/skills/bmmibx3r9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  12. package/.claude/skills/bmmji0lrkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  13. package/.claude/skills/bmmjiniphxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  14. package/.claude/skills/bmmjio86zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  15. package/.claude/skills/bmmjiolfbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  16. package/.claude/skills/bmmjit1lvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  17. package/.claude/skills/bmmjita1qxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  18. package/.claude/skills/bnd-mmibweu3/SKILL.md +21 -0
  19. package/.claude/skills/bnd-mmibwjh4/SKILL.md +21 -0
  20. package/.claude/skills/bnd-mmibwsey/SKILL.md +21 -0
  21. package/.claude/skills/bnd-mmibwxup/SKILL.md +21 -0
  22. package/.claude/skills/bnd-mmibx3rg/SKILL.md +21 -0
  23. package/.claude/skills/bnd-mmji0lrp/SKILL.md +21 -0
  24. package/.claude/skills/bnd-mmjinipm/SKILL.md +21 -0
  25. package/.claude/skills/bnd-mmjio875/SKILL.md +21 -0
  26. package/.claude/skills/bnd-mmjiolfg/SKILL.md +21 -0
  27. package/.claude/skills/bnd-mmjit1m3/SKILL.md +21 -0
  28. package/.claude/skills/bnd-mmjita1x/SKILL.md +21 -0
  29. package/.claude/skills/coercion-test/SKILL.md +50 -0
  30. package/.claude/skills/large-skill/SKILL.md +21 -0
  31. package/.claude/skills/long-desc-skill/SKILL.md +21 -0
  32. package/.claude/skills/mcp-dev/SKILL.md +61 -0
  33. package/.claude/skills/nl-mmibweus/SKILL.md +25 -0
  34. package/.claude/skills/nl-mmibwjhf/SKILL.md +25 -0
  35. package/.claude/skills/nl-mmibwsf7/SKILL.md +25 -0
  36. package/.claude/skills/nl-mmibwxvq/SKILL.md +25 -0
  37. package/.claude/skills/nl-mmibx3rt/SKILL.md +25 -0
  38. package/.claude/skills/nl-mmji0lrz/SKILL.md +25 -0
  39. package/.claude/skills/nl-mmjinipx/SKILL.md +25 -0
  40. package/.claude/skills/nl-mmjio87f/SKILL.md +25 -0
  41. package/.claude/skills/nl-mmjiolfs/SKILL.md +25 -0
  42. package/.claude/skills/nl-mmjit1mc/SKILL.md +25 -0
  43. package/.claude/skills/nl-mmjita26/SKILL.md +25 -0
  44. package/.claude/skills/rapid-1/SKILL.md +21 -0
  45. package/.claude/skills/rapid-2/SKILL.md +21 -0
  46. package/.claude/skills/rapid-3/SKILL.md +21 -0
  47. package/.claude/skills/rapid-4/SKILL.md +21 -0
  48. package/.claude/skills/rapid-5/SKILL.md +21 -0
  49. package/.claude/skills/test/", /"malicious/": /"true/SKILL.md" +69 -0
  50. package/.claude/skills/test-emoji-/360/237/230/200-skill/SKILL.md +69 -0
  51. package/.claude/skills/test-skill/SKILL.md +69 -0
  52. package/.claude/skills/test; rm -rf /; skill/SKILL.md +69 -0
  53. package/.claude/skills/test<script>alert(1)</script>skill/SKILL.md +69 -0
  54. package/.claudeignore +5 -0
  55. package/.mcp.json +3 -0
  56. package/CHANGELOG.md +29 -0
  57. package/CLAUDE.md +76 -0
  58. package/LICENSE +21 -0
  59. package/README.md +149 -0
  60. package/ROADMAP.md +526 -0
  61. package/ccboot-PRD-v1.0.docx.md +732 -0
  62. package/ccboot-v1.2.0-enforcement-spec.md +1272 -0
  63. package/dist/cli.d.ts +3 -0
  64. package/dist/cli.d.ts.map +1 -0
  65. package/dist/cli.js +674 -0
  66. package/dist/cli.js.map +1 -0
  67. package/dist/constants.d.ts +25 -0
  68. package/dist/constants.d.ts.map +1 -0
  69. package/dist/constants.js +118 -0
  70. package/dist/constants.js.map +1 -0
  71. package/dist/index.d.ts +3 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +47 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/schemas/common.d.ts +62 -0
  76. package/dist/schemas/common.d.ts.map +1 -0
  77. package/dist/schemas/common.js +15 -0
  78. package/dist/schemas/common.js.map +1 -0
  79. package/dist/schemas/scaffolding.d.ts +277 -0
  80. package/dist/schemas/scaffolding.d.ts.map +1 -0
  81. package/dist/schemas/scaffolding.js +133 -0
  82. package/dist/schemas/scaffolding.js.map +1 -0
  83. package/dist/services/claudemd-generator.d.ts +16 -0
  84. package/dist/services/claudemd-generator.d.ts.map +1 -0
  85. package/dist/services/claudemd-generator.js +426 -0
  86. package/dist/services/claudemd-generator.js.map +1 -0
  87. package/dist/services/codex-generator.d.ts +6 -0
  88. package/dist/services/codex-generator.d.ts.map +1 -0
  89. package/dist/services/codex-generator.js +35 -0
  90. package/dist/services/codex-generator.js.map +1 -0
  91. package/dist/services/cursor-generator.d.ts +15 -0
  92. package/dist/services/cursor-generator.d.ts.map +1 -0
  93. package/dist/services/cursor-generator.js +134 -0
  94. package/dist/services/cursor-generator.js.map +1 -0
  95. package/dist/services/file-ops.d.ts +48 -0
  96. package/dist/services/file-ops.d.ts.map +1 -0
  97. package/dist/services/file-ops.js +153 -0
  98. package/dist/services/file-ops.js.map +1 -0
  99. package/dist/services/output-formatter.d.ts +57 -0
  100. package/dist/services/output-formatter.d.ts.map +1 -0
  101. package/dist/services/output-formatter.js +88 -0
  102. package/dist/services/output-formatter.js.map +1 -0
  103. package/dist/services/platform-detect.d.ts +14 -0
  104. package/dist/services/platform-detect.d.ts.map +1 -0
  105. package/dist/services/platform-detect.js +63 -0
  106. package/dist/services/platform-detect.js.map +1 -0
  107. package/dist/services/project-analyzer.d.ts +71 -0
  108. package/dist/services/project-analyzer.d.ts.map +1 -0
  109. package/dist/services/project-analyzer.js +595 -0
  110. package/dist/services/project-analyzer.js.map +1 -0
  111. package/dist/services/rules-engine.d.ts +41 -0
  112. package/dist/services/rules-engine.d.ts.map +1 -0
  113. package/dist/services/rules-engine.js +304 -0
  114. package/dist/services/rules-engine.js.map +1 -0
  115. package/dist/services/strictness.d.ts +37 -0
  116. package/dist/services/strictness.d.ts.map +1 -0
  117. package/dist/services/strictness.js +182 -0
  118. package/dist/services/strictness.js.map +1 -0
  119. package/dist/services/template-engine.d.ts +16 -0
  120. package/dist/services/template-engine.d.ts.map +1 -0
  121. package/dist/services/template-engine.js +85 -0
  122. package/dist/services/template-engine.js.map +1 -0
  123. package/dist/services/validation.d.ts +41 -0
  124. package/dist/services/validation.d.ts.map +1 -0
  125. package/dist/services/validation.js +104 -0
  126. package/dist/services/validation.js.map +1 -0
  127. package/dist/services/windsurf-generator.d.ts +15 -0
  128. package/dist/services/windsurf-generator.d.ts.map +1 -0
  129. package/dist/services/windsurf-generator.js +127 -0
  130. package/dist/services/windsurf-generator.js.map +1 -0
  131. package/dist/tests/enforcement.test.d.ts +2 -0
  132. package/dist/tests/enforcement.test.d.ts.map +1 -0
  133. package/dist/tests/enforcement.test.js +541 -0
  134. package/dist/tests/enforcement.test.js.map +1 -0
  135. package/dist/tests/enterprise.test.d.ts +2 -0
  136. package/dist/tests/enterprise.test.d.ts.map +1 -0
  137. package/dist/tests/enterprise.test.js +353 -0
  138. package/dist/tests/enterprise.test.js.map +1 -0
  139. package/dist/tests/fuzzing.test.d.ts +2 -0
  140. package/dist/tests/fuzzing.test.d.ts.map +1 -0
  141. package/dist/tests/fuzzing.test.js +596 -0
  142. package/dist/tests/fuzzing.test.js.map +1 -0
  143. package/dist/tests/knowledge.test.d.ts +2 -0
  144. package/dist/tests/knowledge.test.d.ts.map +1 -0
  145. package/dist/tests/knowledge.test.js +292 -0
  146. package/dist/tests/knowledge.test.js.map +1 -0
  147. package/dist/tests/management.test.d.ts +2 -0
  148. package/dist/tests/management.test.d.ts.map +1 -0
  149. package/dist/tests/management.test.js +338 -0
  150. package/dist/tests/management.test.js.map +1 -0
  151. package/dist/tests/scaffolding.test.d.ts +2 -0
  152. package/dist/tests/scaffolding.test.d.ts.map +1 -0
  153. package/dist/tests/scaffolding.test.js +419 -0
  154. package/dist/tests/scaffolding.test.js.map +1 -0
  155. package/dist/tests/test-utils.d.ts +76 -0
  156. package/dist/tests/test-utils.d.ts.map +1 -0
  157. package/dist/tests/test-utils.js +171 -0
  158. package/dist/tests/test-utils.js.map +1 -0
  159. package/dist/tests/tool-harness.d.ts +18 -0
  160. package/dist/tests/tool-harness.d.ts.map +1 -0
  161. package/dist/tests/tool-harness.js +51 -0
  162. package/dist/tests/tool-harness.js.map +1 -0
  163. package/dist/tools/enterprise.d.ts +8 -0
  164. package/dist/tools/enterprise.d.ts.map +1 -0
  165. package/dist/tools/enterprise.js +571 -0
  166. package/dist/tools/enterprise.js.map +1 -0
  167. package/dist/tools/knowledge.d.ts +7 -0
  168. package/dist/tools/knowledge.d.ts.map +1 -0
  169. package/dist/tools/knowledge.js +120 -0
  170. package/dist/tools/knowledge.js.map +1 -0
  171. package/dist/tools/management.d.ts +10 -0
  172. package/dist/tools/management.d.ts.map +1 -0
  173. package/dist/tools/management.js +1541 -0
  174. package/dist/tools/management.js.map +1 -0
  175. package/dist/tools/scaffolding.d.ts +8 -0
  176. package/dist/tools/scaffolding.d.ts.map +1 -0
  177. package/dist/tools/scaffolding.js +736 -0
  178. package/dist/tools/scaffolding.js.map +1 -0
  179. package/dist/types.d.ts +54 -0
  180. package/dist/types.d.ts.map +1 -0
  181. package/dist/types.js +5 -0
  182. package/dist/types.js.map +1 -0
  183. package/landing/app/layout.tsx +30 -0
  184. package/landing/app/page.tsx +944 -0
  185. package/landing/next-env.d.ts +6 -0
  186. package/landing/next.config.js +6 -0
  187. package/landing/package-lock.json +896 -0
  188. package/landing/package.json +20 -0
  189. package/landing/tsconfig.json +40 -0
  190. package/package.json +49 -0
  191. package/rebar-v2.0.0-platform-spec.md +1567 -0
  192. package/server.json +20 -0
  193. package/src/cli.ts +735 -0
  194. package/src/constants.ts +131 -0
  195. package/src/index.ts +54 -0
  196. package/src/schemas/common.ts +22 -0
  197. package/src/schemas/scaffolding.ts +161 -0
  198. package/src/services/claudemd-generator.ts +481 -0
  199. package/src/services/codex-generator.ts +44 -0
  200. package/src/services/cursor-generator.ts +153 -0
  201. package/src/services/file-ops.ts +172 -0
  202. package/src/services/platform-detect.ts +80 -0
  203. package/src/services/project-analyzer.ts +690 -0
  204. package/src/services/rules-engine.ts +353 -0
  205. package/src/services/strictness.ts +202 -0
  206. package/src/services/template-engine.ts +119 -0
  207. package/src/services/validation.ts +138 -0
  208. package/src/services/windsurf-generator.ts +145 -0
  209. package/src/tests/enforcement.test.ts +794 -0
  210. package/src/tests/enterprise.test.ts +483 -0
  211. package/src/tests/fuzzing.test.ts +690 -0
  212. package/src/tests/knowledge.test.ts +371 -0
  213. package/src/tests/management.test.ts +451 -0
  214. package/src/tests/scaffolding.test.ts +575 -0
  215. package/src/tests/test-utils.ts +206 -0
  216. package/src/tests/tool-harness.ts +70 -0
  217. package/src/tools/enterprise.ts +666 -0
  218. package/src/tools/knowledge.ts +162 -0
  219. package/src/tools/management.ts +1706 -0
  220. package/src/tools/scaffolding.ts +909 -0
  221. package/src/types.ts +93 -0
  222. package/supabase/.temp/cli-latest +1 -0
  223. package/supabase/.temp/gotrue-version +1 -0
  224. package/supabase/.temp/pooler-url +1 -0
  225. package/supabase/.temp/postgres-version +1 -0
  226. package/supabase/.temp/project-ref +1 -0
  227. package/supabase/.temp/rest-version +1 -0
  228. package/supabase/.temp/storage-migration +1 -0
  229. package/supabase/.temp/storage-version +1 -0
  230. package/templates/agents/explore.md +41 -0
  231. package/templates/agents/plan.md +73 -0
  232. package/templates/agents/security-auditor.md +77 -0
  233. package/templates/agents/test-runner.md +60 -0
  234. package/templates/claudemd/fastapi.md +49 -0
  235. package/templates/claudemd/monorepo.md +48 -0
  236. package/templates/claudemd/nextjs.md +52 -0
  237. package/templates/claudemd/react-spa.md +50 -0
  238. package/templates/claudemd/springboot.md +50 -0
  239. package/templates/hooks/danger-blocker.json +11 -0
  240. package/templates/hooks/format-on-write.json +17 -0
  241. package/templates/hooks/lint-on-write.json +16 -0
  242. package/templates/hooks/secret-detector.json +11 -0
  243. package/templates/skills/code-review.md +68 -0
  244. package/templates/skills/documentation.md +62 -0
  245. package/templates/skills/performance-audit.md +80 -0
  246. package/templates/skills/security-scan.md +66 -0
  247. package/templates/skills/test-writer.md +56 -0
  248. package/tsconfig.json +19 -0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function runCLI(args: string[]): Promise<number>;
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAspBA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAwE5D"}
package/dist/cli.js ADDED
@@ -0,0 +1,674 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Rebar CLI — Standalone command-line interface
4
+ *
5
+ * Run Rebar commands without an MCP client. Useful for:
6
+ * - CI/CD pipelines (audit, badge generation)
7
+ * - Quick project setup (init, doctor)
8
+ * - Local development (metrics, set-strictness)
9
+ */
10
+ import * as path from "node:path";
11
+ import * as fs from "node:fs/promises";
12
+ import { resolveProjectPath, readFileSafe, fileExists, listFiles, listFilesRecursive, } from "./services/file-ops.js";
13
+ import { validateClaudeMD, validateSkillFrontmatter, validateJSON, estimateTokens } from "./services/validation.js";
14
+ import { detectCurrentProfile, getHooksForProfile, hooksToSettingsFormat } from "./services/strictness.js";
15
+ import { analyzeProject } from "./services/project-analyzer.js";
16
+ import { generateClaudeMD as generateClaudeMDContent } from "./services/claudemd-generator.js";
17
+ import { resolvePlatforms } from "./services/platform-detect.js";
18
+ import { generateCursorConfig } from "./services/cursor-generator.js";
19
+ import { generateWindsurfConfig } from "./services/windsurf-generator.js";
20
+ import { generateCodexConfig } from "./services/codex-generator.js";
21
+ import { atomicWrite } from "./services/file-ops.js";
22
+ import { CONTEXT_WINDOW_TOKENS } from "./constants.js";
23
+ const VERSION = "2.0.0";
24
+ function parseArgs(args) {
25
+ const options = {
26
+ path: ".",
27
+ strictness: "standard",
28
+ platforms: ["all"],
29
+ format: "markdown",
30
+ threshold: 0,
31
+ };
32
+ const positional = [];
33
+ let command = "";
34
+ for (let i = 0; i < args.length; i++) {
35
+ const arg = args[i];
36
+ if (arg.startsWith("--")) {
37
+ const key = arg.slice(2);
38
+ const value = args[++i];
39
+ switch (key) {
40
+ case "path":
41
+ options.path = value;
42
+ break;
43
+ case "strictness":
44
+ if (value === "standard" || value === "strict" || value === "paranoid") {
45
+ options.strictness = value;
46
+ }
47
+ break;
48
+ case "platforms":
49
+ options.platforms = value.split(",");
50
+ break;
51
+ case "format":
52
+ if (value === "markdown" || value === "json") {
53
+ options.format = value;
54
+ }
55
+ break;
56
+ case "threshold":
57
+ options.threshold = parseInt(value, 10);
58
+ break;
59
+ }
60
+ }
61
+ else if (arg.startsWith("-")) {
62
+ // Short options (ignored for now)
63
+ }
64
+ else if (!command) {
65
+ command = arg;
66
+ }
67
+ else {
68
+ positional.push(arg);
69
+ }
70
+ }
71
+ return { command, options, positional };
72
+ }
73
+ async function cmdInit(options) {
74
+ const projectPath = resolveProjectPath(options.path);
75
+ console.log(`Initializing Rebar in ${projectPath}...`);
76
+ console.log(`Strictness: ${options.strictness}`);
77
+ console.log(`Platforms: ${options.platforms.join(", ")}`);
78
+ console.log();
79
+ try {
80
+ // Analyze project
81
+ const analysis = await analyzeProject(projectPath);
82
+ console.log(`Detected tech stack: ${analysis.stacks.join(", ") || "none"}`);
83
+ console.log(`Project name: ${analysis.name}`);
84
+ console.log();
85
+ // Generate CLAUDE.md
86
+ const claudeMdContent = generateClaudeMDContent(analysis);
87
+ const claudeMdPath = path.join(projectPath, "CLAUDE.md");
88
+ await atomicWrite(claudeMdPath, claudeMdContent);
89
+ console.log("✓ Created CLAUDE.md");
90
+ // Set up directories
91
+ const claudeDir = path.join(projectPath, ".claude");
92
+ await fs.mkdir(claudeDir, { recursive: true });
93
+ await fs.mkdir(path.join(claudeDir, "skills"), { recursive: true });
94
+ await fs.mkdir(path.join(claudeDir, "agents"), { recursive: true });
95
+ await fs.mkdir(path.join(claudeDir, "docs"), { recursive: true });
96
+ console.log("✓ Created .claude/ directory structure");
97
+ // Derive commands from analysis
98
+ const testCmd = Object.values(analysis.testCommands)[0] || "npm test";
99
+ const lintCmd = analysis.linter ? `npx ${analysis.linter.toLowerCase()} .` : "npm run lint";
100
+ const formatCmd = analysis.formatter ? `npx ${analysis.formatter.toLowerCase()} --write .` : "npm run format";
101
+ // Generate settings with hooks based on strictness
102
+ const hooks = getHooksForProfile(options.strictness, testCmd, lintCmd, formatCmd);
103
+ const settingsContent = JSON.stringify({ hooks: hooksToSettingsFormat(hooks) }, null, 2);
104
+ await atomicWrite(path.join(claudeDir, "settings.json"), settingsContent);
105
+ console.log(`✓ Created .claude/settings.json (${options.strictness} strictness)`);
106
+ // Generate platform-specific configs
107
+ const platforms = await resolvePlatforms(projectPath, options.platforms);
108
+ for (const platform of platforms) {
109
+ if (platform === "cursor") {
110
+ await generateCursorConfig(projectPath, analysis.name, claudeMdContent, []);
111
+ console.log("✓ Created .cursor/rules/ for Cursor");
112
+ }
113
+ else if (platform === "windsurf") {
114
+ await generateWindsurfConfig(projectPath, claudeMdContent, []);
115
+ console.log("✓ Created .windsurf/rules/ for Windsurf");
116
+ }
117
+ else if (platform === "codex") {
118
+ await generateCodexConfig(projectPath, analysis.name, claudeMdContent);
119
+ console.log("✓ Created AGENTS.md for Codex");
120
+ }
121
+ }
122
+ // Create .claudeignore
123
+ const claudeignorePath = path.join(projectPath, ".claudeignore");
124
+ if (!(await fileExists(claudeignorePath))) {
125
+ const claudeignoreContent = `# Rebar default .claudeignore
126
+ node_modules/
127
+ .git/
128
+ dist/
129
+ build/
130
+ coverage/
131
+ *.log
132
+ .env
133
+ .env.*
134
+ *.pem
135
+ *.key
136
+ `;
137
+ await atomicWrite(claudeignorePath, claudeignoreContent);
138
+ console.log("✓ Created .claudeignore");
139
+ }
140
+ console.log();
141
+ console.log("Rebar initialization complete!");
142
+ console.log();
143
+ console.log("Next steps:");
144
+ console.log(" rebar doctor Check setup health");
145
+ console.log(" rebar audit Run quality audit");
146
+ console.log();
147
+ return 0;
148
+ }
149
+ catch (err) {
150
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
151
+ return 1;
152
+ }
153
+ }
154
+ async function cmdDoctor(options) {
155
+ const projectPath = resolveProjectPath(options.path);
156
+ const checks = [];
157
+ try {
158
+ // Check 1: CLAUDE.md
159
+ const claudemd = await readFileSafe(path.join(projectPath, "CLAUDE.md"));
160
+ if (!claudemd) {
161
+ checks.push({ name: "CLAUDE.md", status: "FAIL", message: "Not found", details: ["Run: rebar init"] });
162
+ }
163
+ else {
164
+ const validation = validateClaudeMD(claudemd, "CLAUDE.md");
165
+ if (validation.errors.length > 0) {
166
+ checks.push({ name: "CLAUDE.md", status: "FAIL", message: "Invalid", details: validation.errors });
167
+ }
168
+ else {
169
+ checks.push({ name: "CLAUDE.md", status: "PASS", message: "Present and valid" });
170
+ }
171
+ }
172
+ // Check 2: Settings/Hooks
173
+ const settings = await readFileSafe(path.join(projectPath, ".claude/settings.json"));
174
+ if (!settings) {
175
+ checks.push({ name: "Hooks", status: "FAIL", message: "No settings.json", details: ["Run: rebar init"] });
176
+ }
177
+ else {
178
+ const parsed = validateJSON(settings, "settings.json");
179
+ if (!parsed.valid) {
180
+ checks.push({ name: "Hooks", status: "FAIL", message: "Invalid JSON", details: parsed.errors });
181
+ }
182
+ else {
183
+ const s = parsed.parsed;
184
+ const hooks = s.hooks;
185
+ if (!hooks || Object.keys(hooks).length === 0) {
186
+ checks.push({ name: "Hooks", status: "WARN", message: "No hooks configured" });
187
+ }
188
+ else {
189
+ const count = Object.values(hooks).reduce((sum, arr) => sum + arr.length, 0);
190
+ checks.push({ name: "Hooks", status: "PASS", message: `${count} hook(s) installed` });
191
+ }
192
+ }
193
+ }
194
+ // Check 3: Skills
195
+ const skillsDir = path.join(projectPath, ".claude/skills");
196
+ const skillFiles = await listFilesRecursive(skillsDir, ".md");
197
+ if (skillFiles.length === 0) {
198
+ checks.push({ name: "Skills", status: "WARN", message: "None installed (optional)" });
199
+ }
200
+ else {
201
+ checks.push({ name: "Skills", status: "PASS", message: `${skillFiles.length} skill(s)` });
202
+ }
203
+ // Check 4: .claudeignore
204
+ if (await fileExists(path.join(projectPath, ".claudeignore"))) {
205
+ checks.push({ name: ".claudeignore", status: "PASS", message: "Present" });
206
+ }
207
+ else {
208
+ checks.push({ name: ".claudeignore", status: "WARN", message: "Not found (recommended)" });
209
+ }
210
+ // Check 5: Strictness profile
211
+ if (settings) {
212
+ const parsed = validateJSON(settings, "settings.json");
213
+ if (parsed.valid && parsed.parsed) {
214
+ const s = parsed.parsed;
215
+ const hooks = s.hooks;
216
+ if (hooks) {
217
+ const profile = detectCurrentProfile(hooks);
218
+ if (profile) {
219
+ checks.push({ name: "Strictness", status: "PASS", message: `Profile: ${profile}` });
220
+ }
221
+ else {
222
+ checks.push({ name: "Strictness", status: "WARN", message: "Custom/mixed config" });
223
+ }
224
+ }
225
+ }
226
+ }
227
+ // Check 6: Context budget
228
+ let totalTokens = 0;
229
+ if (claudemd)
230
+ totalTokens += estimateTokens(claudemd);
231
+ for (const sf of skillFiles) {
232
+ const content = await readFileSafe(sf);
233
+ if (content)
234
+ totalTokens += estimateTokens(content);
235
+ }
236
+ const budgetPercent = (totalTokens / CONTEXT_WINDOW_TOKENS) * 100;
237
+ if (budgetPercent > 10) {
238
+ checks.push({ name: "Context", status: "FAIL", message: `${budgetPercent.toFixed(1)}% (>10%)` });
239
+ }
240
+ else if (budgetPercent > 5) {
241
+ checks.push({ name: "Context", status: "WARN", message: `${budgetPercent.toFixed(1)}% (target <5%)` });
242
+ }
243
+ else {
244
+ checks.push({ name: "Context", status: "PASS", message: `${budgetPercent.toFixed(1)}%` });
245
+ }
246
+ // Output
247
+ const passCount = checks.filter(c => c.status === "PASS").length;
248
+ const warnCount = checks.filter(c => c.status === "WARN").length;
249
+ const failCount = checks.filter(c => c.status === "FAIL").length;
250
+ if (options.format === "json") {
251
+ console.log(JSON.stringify({
252
+ overall: failCount === 0 ? (warnCount === 0 ? "Healthy" : "Needs Attention") : "Broken",
253
+ summary: { pass: passCount, warn: warnCount, fail: failCount },
254
+ checks,
255
+ }, null, 2));
256
+ }
257
+ else {
258
+ console.log("Rebar Doctor Report");
259
+ console.log("===================");
260
+ console.log();
261
+ for (const check of checks) {
262
+ const icon = check.status === "PASS" ? "✓" : check.status === "WARN" ? "⚠" : "✗";
263
+ console.log(`${icon} ${check.name}: ${check.message}`);
264
+ if (check.details) {
265
+ for (const d of check.details) {
266
+ console.log(` ${d}`);
267
+ }
268
+ }
269
+ }
270
+ console.log();
271
+ const overall = failCount === 0 ? (warnCount === 0 ? "✓ Healthy" : "⚠ Needs Attention") : "✗ Broken";
272
+ console.log(`Overall: ${overall} (${passCount} pass, ${warnCount} warn, ${failCount} fail)`);
273
+ }
274
+ return failCount > 0 ? 1 : 0;
275
+ }
276
+ catch (err) {
277
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
278
+ return 1;
279
+ }
280
+ }
281
+ async function cmdAudit(options) {
282
+ const projectPath = resolveProjectPath(options.path);
283
+ const results = [];
284
+ try {
285
+ // 1. CLAUDE.md quality (25 points)
286
+ const claudemd = await readFileSafe(path.join(projectPath, "CLAUDE.md"));
287
+ const claudemdResult = { category: "CLAUDE.md", score: 0, maxScore: 25, issues: [] };
288
+ if (claudemd) {
289
+ claudemdResult.score += 10; // Exists
290
+ if (validateClaudeMD(claudemd, "CLAUDE.md").valid)
291
+ claudemdResult.score += 5;
292
+ if (/##\s*(Build|Test)/i.test(claudemd))
293
+ claudemdResult.score += 5;
294
+ if (/##\s*(Architecture)/i.test(claudemd))
295
+ claudemdResult.score += 5;
296
+ }
297
+ else {
298
+ claudemdResult.issues.push("No CLAUDE.md found");
299
+ }
300
+ results.push(claudemdResult);
301
+ // 2. Enforcement hooks (25 points)
302
+ const settings = await readFileSafe(path.join(projectPath, ".claude/settings.json"));
303
+ const hooksResult = { category: "Enforcement", score: 0, maxScore: 25, issues: [] };
304
+ if (settings) {
305
+ const parsed = validateJSON(settings, "settings.json");
306
+ if (parsed.valid && parsed.parsed) {
307
+ const s = parsed.parsed;
308
+ const hooks = s.hooks;
309
+ if (hooks) {
310
+ hooksResult.score += 10; // Has hooks
311
+ const profile = detectCurrentProfile(hooks);
312
+ if (profile === "standard")
313
+ hooksResult.score += 5;
314
+ if (profile === "strict")
315
+ hooksResult.score += 10;
316
+ if (profile === "paranoid")
317
+ hooksResult.score += 15;
318
+ if (hooks.PreToolCall || hooks.PostToolCall)
319
+ hooksResult.score += 0; // Already counted
320
+ }
321
+ else {
322
+ hooksResult.issues.push("No hooks configured");
323
+ }
324
+ }
325
+ else {
326
+ hooksResult.issues.push("Invalid settings.json");
327
+ }
328
+ }
329
+ else {
330
+ hooksResult.issues.push("No settings.json found");
331
+ }
332
+ results.push(hooksResult);
333
+ // 3. Skills quality (20 points)
334
+ const skillsDir = path.join(projectPath, ".claude/skills");
335
+ const skillFiles = await listFilesRecursive(skillsDir, ".md");
336
+ const skillsResult = { category: "Skills", score: 0, maxScore: 20, issues: [] };
337
+ if (skillFiles.length > 0) {
338
+ skillsResult.score += 10;
339
+ let validSkills = 0;
340
+ for (const sf of skillFiles) {
341
+ const content = await readFileSafe(sf);
342
+ if (content && validateSkillFrontmatter(content).valid) {
343
+ validSkills++;
344
+ }
345
+ }
346
+ if (validSkills === skillFiles.length) {
347
+ skillsResult.score += 10;
348
+ }
349
+ else {
350
+ skillsResult.issues.push(`${skillFiles.length - validSkills} skill(s) have invalid frontmatter`);
351
+ }
352
+ }
353
+ else {
354
+ skillsResult.score += 10; // Skills are optional, don't penalize heavily
355
+ }
356
+ results.push(skillsResult);
357
+ // 4. Context budget (15 points)
358
+ const contextResult = { category: "Context Budget", score: 0, maxScore: 15, issues: [] };
359
+ let totalTokens = 0;
360
+ if (claudemd)
361
+ totalTokens += estimateTokens(claudemd);
362
+ for (const sf of skillFiles) {
363
+ const content = await readFileSafe(sf);
364
+ if (content)
365
+ totalTokens += estimateTokens(content);
366
+ }
367
+ const budgetPercent = (totalTokens / CONTEXT_WINDOW_TOKENS) * 100;
368
+ if (budgetPercent <= 2) {
369
+ contextResult.score = 15;
370
+ }
371
+ else if (budgetPercent <= 5) {
372
+ contextResult.score = 10;
373
+ }
374
+ else if (budgetPercent <= 10) {
375
+ contextResult.score = 5;
376
+ contextResult.issues.push(`Config uses ${budgetPercent.toFixed(1)}% of context`);
377
+ }
378
+ else {
379
+ contextResult.issues.push(`Config uses ${budgetPercent.toFixed(1)}% (too high)`);
380
+ }
381
+ results.push(contextResult);
382
+ // 5. Best practices (15 points)
383
+ const practicesResult = { category: "Best Practices", score: 0, maxScore: 15, issues: [] };
384
+ if (await fileExists(path.join(projectPath, ".claudeignore"))) {
385
+ practicesResult.score += 5;
386
+ }
387
+ else {
388
+ practicesResult.issues.push("No .claudeignore");
389
+ }
390
+ if (await fileExists(path.join(projectPath, ".claude/agents"))) {
391
+ const agents = await listFiles(path.join(projectPath, ".claude/agents"), ".md");
392
+ if (agents.length > 0)
393
+ practicesResult.score += 5;
394
+ }
395
+ if (await fileExists(path.join(projectPath, ".claude/docs"))) {
396
+ practicesResult.score += 5;
397
+ }
398
+ results.push(practicesResult);
399
+ // Calculate total
400
+ const totalScore = results.reduce((sum, r) => sum + r.score, 0);
401
+ const maxScore = results.reduce((sum, r) => sum + r.maxScore, 0);
402
+ const percentage = Math.round((totalScore / maxScore) * 100);
403
+ // Output
404
+ if (options.format === "json") {
405
+ console.log(JSON.stringify({
406
+ score: percentage,
407
+ total: totalScore,
408
+ maxScore,
409
+ categories: results,
410
+ pass: options.threshold === 0 || percentage >= options.threshold,
411
+ }, null, 2));
412
+ }
413
+ else {
414
+ console.log("Rebar Quality Audit");
415
+ console.log("===================");
416
+ console.log();
417
+ for (const r of results) {
418
+ const bar = "█".repeat(Math.round(r.score / r.maxScore * 10)) +
419
+ "░".repeat(10 - Math.round(r.score / r.maxScore * 10));
420
+ console.log(`${r.category.padEnd(16)} ${bar} ${r.score}/${r.maxScore}`);
421
+ for (const issue of r.issues) {
422
+ console.log(` ⚠ ${issue}`);
423
+ }
424
+ }
425
+ console.log();
426
+ console.log(`Overall Score: ${percentage}/100`);
427
+ if (options.threshold > 0) {
428
+ if (percentage >= options.threshold) {
429
+ console.log(`✓ Passed threshold (${options.threshold})`);
430
+ }
431
+ else {
432
+ console.log(`✗ Below threshold (${options.threshold})`);
433
+ }
434
+ }
435
+ }
436
+ if (options.threshold > 0 && percentage < options.threshold) {
437
+ return 1;
438
+ }
439
+ return 0;
440
+ }
441
+ catch (err) {
442
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
443
+ return 1;
444
+ }
445
+ }
446
+ async function cmdSetStrictness(options, level) {
447
+ const projectPath = resolveProjectPath(options.path);
448
+ if (level !== "standard" && level !== "strict" && level !== "paranoid") {
449
+ console.error(`Invalid strictness level: ${level}`);
450
+ console.error("Valid levels: standard, strict, paranoid");
451
+ return 1;
452
+ }
453
+ try {
454
+ // Detect test/lint/format commands
455
+ const analysis = await analyzeProject(projectPath);
456
+ const testCmd = Object.values(analysis.testCommands)[0] || "npm test";
457
+ const lintCmd = analysis.linter ? `npx ${analysis.linter.toLowerCase()} .` : "npm run lint";
458
+ const formatCmd = analysis.formatter ? `npx ${analysis.formatter.toLowerCase()} --write .` : "npm run format";
459
+ // Generate hooks for profile
460
+ const hooks = getHooksForProfile(level, testCmd, lintCmd, formatCmd);
461
+ // Read existing settings or create new
462
+ const settingsPath = path.join(projectPath, ".claude/settings.json");
463
+ let existingSettings = {};
464
+ const settingsContent = await readFileSafe(settingsPath);
465
+ if (settingsContent) {
466
+ try {
467
+ existingSettings = JSON.parse(settingsContent);
468
+ }
469
+ catch {
470
+ // Start fresh
471
+ }
472
+ }
473
+ // Merge hooks into settings
474
+ existingSettings.hooks = hooksToSettingsFormat(hooks);
475
+ // Ensure directory exists
476
+ await fs.mkdir(path.join(projectPath, ".claude"), { recursive: true });
477
+ // Write settings
478
+ await atomicWrite(settingsPath, JSON.stringify(existingSettings, null, 2));
479
+ console.log(`✓ Strictness set to: ${level}`);
480
+ console.log();
481
+ console.log("Installed hooks:");
482
+ for (const hook of hooks) {
483
+ // Determine if hook blocks based on command contents
484
+ const isBlocking = hook.command.includes("exit 2");
485
+ const behavior = isBlocking ? "blocks" : "notifies";
486
+ console.log(` • ${hook.event}: ${hook.description} (${behavior})`);
487
+ }
488
+ return 0;
489
+ }
490
+ catch (err) {
491
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
492
+ return 1;
493
+ }
494
+ }
495
+ async function cmdMetrics(options) {
496
+ const projectPath = resolveProjectPath(options.path);
497
+ // Metrics are stored in .rebar/metrics.json
498
+ const metricsPath = path.join(projectPath, ".rebar/metrics.json");
499
+ const metricsContent = await readFileSafe(metricsPath);
500
+ if (!metricsContent) {
501
+ console.log("No metrics history found.");
502
+ console.log("Run 'rebar audit' to generate metrics.");
503
+ return 0;
504
+ }
505
+ try {
506
+ const metrics = JSON.parse(metricsContent);
507
+ if (options.format === "json") {
508
+ console.log(JSON.stringify(metrics, null, 2));
509
+ }
510
+ else {
511
+ console.log("Rebar Quality Metrics History");
512
+ console.log("=============================");
513
+ console.log();
514
+ for (const m of metrics.slice(-10)) { // Last 10 entries
515
+ const date = new Date(m.timestamp).toLocaleDateString();
516
+ const bar = "█".repeat(Math.round(m.score / 10)) + "░".repeat(10 - Math.round(m.score / 10));
517
+ console.log(`${date} ${bar} ${m.score}/100`);
518
+ }
519
+ }
520
+ return 0;
521
+ }
522
+ catch (err) {
523
+ console.error(`Error parsing metrics: ${err instanceof Error ? err.message : String(err)}`);
524
+ return 1;
525
+ }
526
+ }
527
+ async function cmdBadge(options) {
528
+ const projectPath = resolveProjectPath(options.path);
529
+ // Run audit to get current score
530
+ let score = 0;
531
+ try {
532
+ const claudemd = await readFileSafe(path.join(projectPath, "CLAUDE.md"));
533
+ const settings = await readFileSafe(path.join(projectPath, ".claude/settings.json"));
534
+ // Quick score calculation
535
+ if (claudemd)
536
+ score += 25;
537
+ if (settings) {
538
+ const parsed = validateJSON(settings, "settings.json");
539
+ if (parsed.valid) {
540
+ const s = parsed.parsed;
541
+ if (s.hooks && Object.keys(s.hooks).length > 0) {
542
+ score += 25;
543
+ }
544
+ }
545
+ }
546
+ if (await fileExists(path.join(projectPath, ".claudeignore")))
547
+ score += 15;
548
+ if (await fileExists(path.join(projectPath, ".claude/skills")))
549
+ score += 15;
550
+ score += 20; // Base context budget score
551
+ }
552
+ catch {
553
+ // Use score of 0
554
+ }
555
+ // Determine color
556
+ let color;
557
+ let label;
558
+ if (score >= 80) {
559
+ color = "#4c1"; // Green
560
+ label = "excellent";
561
+ }
562
+ else if (score >= 60) {
563
+ color = "#97ca00"; // Yellow-green
564
+ label = "good";
565
+ }
566
+ else if (score >= 40) {
567
+ color = "#dfb317"; // Yellow
568
+ label = "fair";
569
+ }
570
+ else {
571
+ color = "#e05d44"; // Red
572
+ label = "needs work";
573
+ }
574
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="120" height="20">
575
+ <linearGradient id="b" x2="0" y2="100%">
576
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
577
+ <stop offset="1" stop-opacity=".1"/>
578
+ </linearGradient>
579
+ <clipPath id="a">
580
+ <rect width="120" height="20" rx="3" fill="#fff"/>
581
+ </clipPath>
582
+ <g clip-path="url(#a)">
583
+ <path fill="#555" d="M0 0h55v20H0z"/>
584
+ <path fill="${color}" d="M55 0h65v20H55z"/>
585
+ <path fill="url(#b)" d="M0 0h120v20H0z"/>
586
+ </g>
587
+ <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
588
+ <text x="27.5" y="15" fill="#010101" fill-opacity=".3">rebar</text>
589
+ <text x="27.5" y="14">rebar</text>
590
+ <text x="87.5" y="15" fill="#010101" fill-opacity=".3">${score}/100</text>
591
+ <text x="87.5" y="14">${score}/100</text>
592
+ </g>
593
+ </svg>`;
594
+ if (options.format === "json") {
595
+ console.log(JSON.stringify({ score, label, color, svg }, null, 2));
596
+ }
597
+ else {
598
+ // Write SVG file
599
+ const badgePath = path.join(projectPath, ".rebar/badge.svg");
600
+ await fs.mkdir(path.join(projectPath, ".rebar"), { recursive: true });
601
+ await atomicWrite(badgePath, svg);
602
+ console.log(`✓ Badge generated: ${badgePath}`);
603
+ console.log(` Score: ${score}/100 (${label})`);
604
+ console.log();
605
+ console.log("Add to README.md:");
606
+ console.log(" ![Rebar Score](.rebar/badge.svg)");
607
+ }
608
+ return 0;
609
+ }
610
+ export async function runCLI(args) {
611
+ const { command, options, positional } = parseArgs(args);
612
+ switch (command) {
613
+ case "init":
614
+ return cmdInit(options);
615
+ case "doctor":
616
+ return cmdDoctor(options);
617
+ case "audit":
618
+ return cmdAudit(options);
619
+ case "set-strictness":
620
+ if (positional.length === 0) {
621
+ console.error("Usage: rebar set-strictness <standard|strict|paranoid>");
622
+ return 1;
623
+ }
624
+ return cmdSetStrictness(options, positional[0]);
625
+ case "metrics":
626
+ return cmdMetrics(options);
627
+ case "badge":
628
+ return cmdBadge(options);
629
+ case "version":
630
+ case "--version":
631
+ case "-v":
632
+ console.log(`rebar-mcp v${VERSION}`);
633
+ return 0;
634
+ case "help":
635
+ case "--help":
636
+ case "-h":
637
+ case "":
638
+ console.log(`Rebar — Reinforcement for AI-generated code
639
+
640
+ Usage:
641
+ rebar init [options] Initialize Rebar enforcement in a project
642
+ rebar audit [options] Run quality audit (exit 1 if below threshold)
643
+ rebar doctor [options] Check Rebar setup health
644
+ rebar set-strictness <level> Change enforcement strictness
645
+ rebar metrics [options] View quality score history
646
+ rebar badge [options] Generate quality score badge SVG
647
+ rebar version Print version
648
+ rebar help Show this help
649
+
650
+ Options:
651
+ --path <dir> Project path (default: current directory)
652
+ --strictness <level> standard | strict | paranoid (default: standard)
653
+ --platforms <list> claude-code,cursor,windsurf,codex,all (default: all)
654
+ --format <type> markdown | json (default: markdown)
655
+ --threshold <score> Minimum quality score for audit (exits 1 if below)
656
+
657
+ Examples:
658
+ rebar init --strictness strict --platforms all
659
+ rebar audit --threshold 80 --format json
660
+ rebar doctor
661
+ npx rebar-mcp audit --threshold 70 # CI/CD usage
662
+
663
+ Strictness Levels:
664
+ standard - Notify on issues (tests, lint, secrets)
665
+ strict - Block on test/lint failures
666
+ paranoid - Block + file size limits + TODO guards`);
667
+ return 0;
668
+ default:
669
+ console.error(`Unknown command: ${command}`);
670
+ console.error("Run 'rebar help' for usage information.");
671
+ return 1;
672
+ }
673
+ }
674
+ //# sourceMappingURL=cli.js.map