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
@@ -0,0 +1,794 @@
1
+ /**
2
+ * Enforcement layer tests for v1.2.0
3
+ * Tests for templates, code quality audit, and hook templates
4
+ */
5
+ import { describe, it, before } from "node:test";
6
+ import * as assert from "node:assert";
7
+ import * as fs from "node:fs/promises";
8
+ import * as path from "node:path";
9
+ import {
10
+ createTestDir,
11
+ createMockProject,
12
+ readTestFile,
13
+ testFileExists,
14
+ assertToolSuccess,
15
+ getToolResultText,
16
+ parseToolResultJson,
17
+ } from "./test-utils.js";
18
+ import { initializeTools, invokeTool } from "./tool-harness.js";
19
+
20
+ // Initialize tools before all tests
21
+ before(() => {
22
+ initializeTools();
23
+ });
24
+
25
+ // ─────────────────────────────────────────────────────────────────────────────
26
+ // SECTION 7.1: Template Content Tests
27
+ // ─────────────────────────────────────────────────────────────────────────────
28
+
29
+ describe("Template Content Tests", () => {
30
+ describe("CLAUDE.md templates", () => {
31
+ const templateDir = path.join(process.cwd(), "templates/claudemd");
32
+
33
+ it("should contain required sections in nextjs.md", async () => {
34
+ const content = await fs.readFile(path.join(templateDir, "nextjs.md"), "utf-8");
35
+
36
+ assert.ok(
37
+ content.includes("## Tech Stack"),
38
+ "Template should contain Tech Stack header"
39
+ );
40
+ assert.ok(
41
+ content.includes("## Build & Test"),
42
+ "Template should contain Build & Test header"
43
+ );
44
+ assert.ok(
45
+ content.includes("## Architecture Rules"),
46
+ "Template should contain Architecture Rules header"
47
+ );
48
+ assert.ok(
49
+ content.includes("## Code Conventions"),
50
+ "Template should contain Code Conventions header"
51
+ );
52
+ });
53
+
54
+ it("should contain required sections in fastapi.md", async () => {
55
+ const content = await fs.readFile(path.join(templateDir, "fastapi.md"), "utf-8");
56
+
57
+ assert.ok(
58
+ content.includes("##"),
59
+ "Template should contain section headers"
60
+ );
61
+ assert.ok(
62
+ content.length > 200,
63
+ "Template should have substantial content"
64
+ );
65
+ });
66
+
67
+ it("should contain required sections in springboot.md", async () => {
68
+ const content = await fs.readFile(path.join(templateDir, "springboot.md"), "utf-8");
69
+
70
+ assert.ok(
71
+ content.includes("##"),
72
+ "Template should contain section headers"
73
+ );
74
+ assert.ok(
75
+ content.length > 200,
76
+ "Template should have substantial content"
77
+ );
78
+ });
79
+
80
+ it("should contain required sections in react-spa.md", async () => {
81
+ const content = await fs.readFile(path.join(templateDir, "react-spa.md"), "utf-8");
82
+
83
+ assert.ok(
84
+ content.includes("##"),
85
+ "Template should contain section headers"
86
+ );
87
+ assert.ok(
88
+ content.length > 200,
89
+ "Template should have substantial content"
90
+ );
91
+ });
92
+
93
+ it("should contain required sections in monorepo.md", async () => {
94
+ const content = await fs.readFile(path.join(templateDir, "monorepo.md"), "utf-8");
95
+
96
+ assert.ok(
97
+ content.includes("##"),
98
+ "Template should contain section headers"
99
+ );
100
+ assert.ok(
101
+ content.length > 200,
102
+ "Template should have substantial content"
103
+ );
104
+ });
105
+
106
+ it("should have valid Handlebars variable syntax in templates", async () => {
107
+ const templates = ["nextjs.md", "fastapi.md", "springboot.md", "react-spa.md", "monorepo.md"];
108
+
109
+ for (const template of templates) {
110
+ const content = await fs.readFile(path.join(templateDir, template), "utf-8");
111
+
112
+ // Check that all variables use proper Handlebars syntax
113
+ const variableMatches = content.match(/\{\{[^}]+\}\}/g) || [];
114
+
115
+ for (const match of variableMatches) {
116
+ // Should be valid Handlebars: {{var}}, {{#if var}}, {{/if}}, {{#each var}}, {{/each}}, {{#unless var}}, {{/unless}}, {{this}}, {{#if has_foo}}
117
+ assert.ok(
118
+ /^\{\{(#if\s+[\w_]+|#each\s+[\w_]+|#unless\s+[\w_]+|\/if|\/each|\/unless|else|this|[\w_]+)\}\}$/.test(match),
119
+ `Invalid variable syntax in ${template}: ${match}`
120
+ );
121
+ }
122
+ }
123
+ });
124
+ });
125
+
126
+ describe("Skill templates", () => {
127
+ const templateDir = path.join(process.cwd(), "templates/skills");
128
+ const skillTemplates = ["code-review.md", "test-writer.md", "security-scan.md", "documentation.md", "performance-audit.md"];
129
+
130
+ for (const template of skillTemplates) {
131
+ it(`should have valid YAML frontmatter in ${template}`, async () => {
132
+ const content = await fs.readFile(path.join(templateDir, template), "utf-8");
133
+
134
+ // Check for YAML frontmatter
135
+ assert.ok(
136
+ content.startsWith("---\n"),
137
+ `${template} should start with YAML frontmatter`
138
+ );
139
+
140
+ const frontmatterEnd = content.indexOf("\n---\n", 4);
141
+ assert.ok(
142
+ frontmatterEnd > 0,
143
+ `${template} should have closing frontmatter marker`
144
+ );
145
+
146
+ const frontmatter = content.substring(4, frontmatterEnd);
147
+
148
+ // Check required fields (name and description are required per Claude Code spec)
149
+ assert.ok(
150
+ frontmatter.includes("name:"),
151
+ `${template} frontmatter should have name field`
152
+ );
153
+ assert.ok(
154
+ frontmatter.includes("description:"),
155
+ `${template} frontmatter should have description field`
156
+ );
157
+ });
158
+
159
+ it(`should have detailed instructions in ${template}`, async () => {
160
+ const content = await fs.readFile(path.join(templateDir, template), "utf-8");
161
+
162
+ // Should have substantial content (not just placeholders)
163
+ assert.ok(
164
+ content.length > 500,
165
+ `${template} should have substantial content (>500 chars), got ${content.length}`
166
+ );
167
+
168
+ // Should not contain generic placeholder text (but TODO in instructions to user is ok)
169
+ assert.ok(
170
+ !content.includes("add appropriate content"),
171
+ `${template} should not contain generic placeholder text`
172
+ );
173
+ });
174
+ }
175
+ });
176
+
177
+ describe("Agent templates", () => {
178
+ const templateDir = path.join(process.cwd(), "templates/agents");
179
+ const agentTemplates = ["explore.md", "plan.md", "test-runner.md", "security-auditor.md"];
180
+
181
+ for (const template of agentTemplates) {
182
+ it(`should have valid markdown structure in ${template}`, async () => {
183
+ const content = await fs.readFile(path.join(templateDir, template), "utf-8");
184
+
185
+ // Agent templates use markdown format (not YAML frontmatter)
186
+ // Check for valid markdown structure with headers
187
+ assert.ok(
188
+ content.startsWith("# "),
189
+ `${template} should start with a markdown header`
190
+ );
191
+
192
+ // Should have role or purpose section
193
+ assert.ok(
194
+ content.includes("## "),
195
+ `${template} should have subsections`
196
+ );
197
+ });
198
+
199
+ it(`should have detailed instructions in ${template}`, async () => {
200
+ const content = await fs.readFile(path.join(templateDir, template), "utf-8");
201
+
202
+ // Should have substantial content
203
+ assert.ok(
204
+ content.length > 300,
205
+ `${template} should have substantial content (>300 chars), got ${content.length}`
206
+ );
207
+ });
208
+ }
209
+ });
210
+ });
211
+
212
+ // ─────────────────────────────────────────────────────────────────────────────
213
+ // SECTION 7.2: Code Quality Audit Tests
214
+ // ─────────────────────────────────────────────────────────────────────────────
215
+
216
+ // TODO: Implement rebar_audit_code_quality tool
217
+ describe.skip("rebar_audit_code_quality", () => {
218
+ it("should detect TODO comments", async () => {
219
+ const { path: testDir, cleanup } = await createTestDir();
220
+
221
+ try {
222
+ await createMockProject(testDir);
223
+
224
+ // Create file with TODO
225
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
226
+ await fs.writeFile(
227
+ path.join(testDir, "src/todo-test.ts"),
228
+ `// TODO: implement this later
229
+ export function incomplete() {
230
+ return null;
231
+ }
232
+ `
233
+ );
234
+
235
+ const result = await invokeTool("rebar_audit_code_quality", {
236
+ project_path: testDir,
237
+ output_format: "json",
238
+ });
239
+
240
+ assertToolSuccess(result);
241
+
242
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
243
+ const data = parsed.data as Record<string, unknown>;
244
+ const issues = data.issues as Array<Record<string, unknown>>;
245
+
246
+ const todoIssue = issues.find(i => i.category === "Incomplete Work");
247
+ assert.ok(todoIssue, "Should detect TODO comment");
248
+ assert.strictEqual(todoIssue.severity, "MEDIUM");
249
+ } finally {
250
+ await cleanup();
251
+ }
252
+ });
253
+
254
+ it("should detect empty catch blocks", async () => {
255
+ const { path: testDir, cleanup } = await createTestDir();
256
+
257
+ try {
258
+ await createMockProject(testDir);
259
+
260
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
261
+ await fs.writeFile(
262
+ path.join(testDir, "src/empty-catch.ts"),
263
+ `export function badErrorHandling() {
264
+ try {
265
+ throw new Error("oops");
266
+ } catch (e) {}
267
+ }
268
+ `
269
+ );
270
+
271
+ const result = await invokeTool("rebar_audit_code_quality", {
272
+ project_path: testDir,
273
+ output_format: "json",
274
+ });
275
+
276
+ assertToolSuccess(result);
277
+
278
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
279
+ const data = parsed.data as Record<string, unknown>;
280
+ const issues = data.issues as Array<Record<string, unknown>>;
281
+
282
+ const catchIssue = issues.find(i => i.category === "Empty Catch Block");
283
+ assert.ok(catchIssue, "Should detect empty catch block");
284
+ assert.strictEqual(catchIssue.severity, "HIGH");
285
+ } finally {
286
+ await cleanup();
287
+ }
288
+ });
289
+
290
+ it("should detect any type usage", async () => {
291
+ const { path: testDir, cleanup } = await createTestDir();
292
+
293
+ try {
294
+ await createMockProject(testDir);
295
+
296
+ // Create a file with explicit any type usage
297
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
298
+ const anyTypeContent = `// Test file for any type detection
299
+ export function unsafeFunction(data: any) {
300
+ return data.foo;
301
+ }
302
+
303
+ const value: any = "test";
304
+ `;
305
+ await fs.writeFile(path.join(testDir, "src/any-type.ts"), anyTypeContent);
306
+
307
+ const result = await invokeTool("rebar_audit_code_quality", {
308
+ project_path: testDir,
309
+ language: "typescript", // Explicitly set language to avoid auto-detection issues
310
+ output_format: "json",
311
+ });
312
+
313
+ assertToolSuccess(result);
314
+
315
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
316
+ const data = parsed.data as Record<string, unknown>;
317
+ const issues = data.issues as Array<Record<string, unknown>>;
318
+ const scannedFiles = data.scanned_files as number;
319
+
320
+ // Verify files were scanned
321
+ assert.ok(scannedFiles > 0, `Should scan at least 1 file, got ${scannedFiles}`);
322
+
323
+ const anyIssue = issues.find(i => i.category === "Type Safety");
324
+ assert.ok(anyIssue, `Should detect any type usage. Issues found: ${JSON.stringify(issues.map(i => i.category))}`);
325
+ assert.strictEqual(anyIssue.severity, "HIGH");
326
+ } finally {
327
+ await cleanup();
328
+ }
329
+ });
330
+
331
+ it("should detect console.log in production code", async () => {
332
+ const { path: testDir, cleanup } = await createTestDir();
333
+
334
+ try {
335
+ await createMockProject(testDir);
336
+
337
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
338
+ await fs.writeFile(
339
+ path.join(testDir, "src/debug-code.ts"),
340
+ `export function process() {
341
+ console.log("debugging here");
342
+ return 42;
343
+ }
344
+ `
345
+ );
346
+
347
+ const result = await invokeTool("rebar_audit_code_quality", {
348
+ project_path: testDir,
349
+ output_format: "json",
350
+ });
351
+
352
+ assertToolSuccess(result);
353
+
354
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
355
+ const data = parsed.data as Record<string, unknown>;
356
+ const issues = data.issues as Array<Record<string, unknown>>;
357
+
358
+ const consoleIssue = issues.find(i => i.category === "Debug Code");
359
+ assert.ok(consoleIssue, "Should detect console.log");
360
+ assert.strictEqual(consoleIssue.severity, "MEDIUM");
361
+ } finally {
362
+ await cleanup();
363
+ }
364
+ });
365
+
366
+ it("should detect large files (>400 lines)", async () => {
367
+ const { path: testDir, cleanup } = await createTestDir();
368
+
369
+ try {
370
+ await createMockProject(testDir);
371
+
372
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
373
+
374
+ // Create a file with 450 lines
375
+ const lines: string[] = [];
376
+ lines.push("export const largeFile = {");
377
+ for (let i = 0; i < 445; i++) {
378
+ lines.push(` prop${i}: ${i},`);
379
+ }
380
+ lines.push("};");
381
+ lines.push("");
382
+
383
+ await fs.writeFile(path.join(testDir, "src/large-file.ts"), lines.join("\n"));
384
+
385
+ const result = await invokeTool("rebar_audit_code_quality", {
386
+ project_path: testDir,
387
+ output_format: "json",
388
+ });
389
+
390
+ assertToolSuccess(result);
391
+
392
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
393
+ const data = parsed.data as Record<string, unknown>;
394
+ const issues = data.issues as Array<Record<string, unknown>>;
395
+
396
+ const largeFileIssue = issues.find(i => i.category === "Large File");
397
+ assert.ok(largeFileIssue, "Should detect large file");
398
+ assert.strictEqual(largeFileIssue.severity, "MEDIUM");
399
+ } finally {
400
+ await cleanup();
401
+ }
402
+ });
403
+
404
+ it("should detect hardcoded secrets", async () => {
405
+ const { path: testDir, cleanup } = await createTestDir();
406
+
407
+ try {
408
+ await createMockProject(testDir);
409
+
410
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
411
+ await fs.writeFile(
412
+ path.join(testDir, "src/secrets.ts"),
413
+ `// Bad practice!
414
+ const apiKey = "sk-proj-1234567890abcdefghijklmnop";
415
+
416
+ export function getKey() {
417
+ return apiKey;
418
+ }
419
+ `
420
+ );
421
+
422
+ const result = await invokeTool("rebar_audit_code_quality", {
423
+ project_path: testDir,
424
+ output_format: "json",
425
+ });
426
+
427
+ assertToolSuccess(result);
428
+
429
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
430
+ const data = parsed.data as Record<string, unknown>;
431
+ const issues = data.issues as Array<Record<string, unknown>>;
432
+
433
+ const secretIssue = issues.find(i => i.category === "Hardcoded Secret");
434
+ assert.ok(secretIssue, "Should detect hardcoded secret");
435
+ assert.strictEqual(secretIssue.severity, "CRITICAL");
436
+ } finally {
437
+ await cleanup();
438
+ }
439
+ });
440
+
441
+ it("should calculate quality score correctly", async () => {
442
+ const { path: testDir, cleanup } = await createTestDir();
443
+
444
+ try {
445
+ await createMockProject(testDir);
446
+
447
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
448
+
449
+ // Create file with known issues:
450
+ // 1 CRITICAL (hardcoded secret) = -15
451
+ // 1 HIGH (empty catch) = -5
452
+ // 1 MEDIUM (TODO) = -2
453
+ // Expected score: 100 - 15 - 5 - 2 = 78
454
+ await fs.writeFile(
455
+ path.join(testDir, "src/multi-issues.ts"),
456
+ `// TODO: fix this
457
+ const apiKey = "sk-1234567890abcdefghijklmnopqrs";
458
+
459
+ export function bad() {
460
+ try {
461
+ throw new Error();
462
+ } catch (e) {}
463
+ return apiKey;
464
+ }
465
+ `
466
+ );
467
+
468
+ const result = await invokeTool("rebar_audit_code_quality", {
469
+ project_path: testDir,
470
+ output_format: "json",
471
+ });
472
+
473
+ assertToolSuccess(result);
474
+
475
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
476
+ const data = parsed.data as Record<string, unknown>;
477
+ const score = data.score as number;
478
+
479
+ // Score = 100 - 15 (critical) - 5 (high) - 2 (medium) = 78
480
+ assert.ok(score <= 78, `Expected score <= 78 but got ${score}`);
481
+ assert.ok(score >= 70, `Expected score >= 70 but got ${score}`);
482
+ } finally {
483
+ await cleanup();
484
+ }
485
+ });
486
+
487
+ it("should return markdown output format", async () => {
488
+ const { path: testDir, cleanup } = await createTestDir();
489
+
490
+ try {
491
+ await createMockProject(testDir);
492
+
493
+ const result = await invokeTool("rebar_audit_code_quality", {
494
+ project_path: testDir,
495
+ output_format: "markdown",
496
+ });
497
+
498
+ assertToolSuccess(result);
499
+
500
+ const text = getToolResultText(result);
501
+ assert.ok(text.includes("## Code Quality Audit Report"), "Should have markdown header");
502
+ assert.ok(text.includes("| Severity | Count |"), "Should have summary table");
503
+ assert.ok(text.includes("### Quality Score"), "Should have quality score section");
504
+ } finally {
505
+ await cleanup();
506
+ }
507
+ });
508
+
509
+ it("should skip test files for console.log detection", async () => {
510
+ const { path: testDir, cleanup } = await createTestDir();
511
+
512
+ try {
513
+ await createMockProject(testDir);
514
+
515
+ await fs.mkdir(path.join(testDir, "src"), { recursive: true });
516
+
517
+ // console.log in test file should be allowed
518
+ await fs.writeFile(
519
+ path.join(testDir, "src/helper.test.ts"),
520
+ `import { helper } from "./helper";
521
+
522
+ test("helper works", () => {
523
+ console.log("debugging test");
524
+ expect(helper()).toBe(true);
525
+ });
526
+ `
527
+ );
528
+
529
+ const result = await invokeTool("rebar_audit_code_quality", {
530
+ project_path: testDir,
531
+ output_format: "json",
532
+ });
533
+
534
+ assertToolSuccess(result);
535
+
536
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
537
+ const data = parsed.data as Record<string, unknown>;
538
+ const issues = data.issues as Array<Record<string, unknown>>;
539
+
540
+ const consoleIssue = issues.find(
541
+ i => i.category === "Debug Code" && (i.file as string).includes("helper.test.ts")
542
+ );
543
+ assert.ok(!consoleIssue, "Should NOT flag console.log in test files");
544
+ } finally {
545
+ await cleanup();
546
+ }
547
+ });
548
+ });
549
+
550
+ // ─────────────────────────────────────────────────────────────────────────────
551
+ // SECTION 7.3: Hook Template Tests
552
+ // ─────────────────────────────────────────────────────────────────────────────
553
+
554
+ describe("Hook Template Tests", () => {
555
+ const templateDir = path.join(process.cwd(), "templates/hooks");
556
+ const validEvents = ["PreToolCall", "PostToolCall"];
557
+
558
+ // Hook templates that actually exist
559
+ const hookTemplates = [
560
+ "format-on-write.json",
561
+ "lint-on-write.json",
562
+ "danger-blocker.json",
563
+ "secret-detector.json",
564
+ ];
565
+
566
+ for (const template of hookTemplates) {
567
+ describe(template, () => {
568
+ it("should be valid JSON", async () => {
569
+ const content = await fs.readFile(path.join(templateDir, template), "utf-8");
570
+
571
+ assert.doesNotThrow(
572
+ () => JSON.parse(content),
573
+ `${template} should be valid JSON`
574
+ );
575
+ });
576
+
577
+ it("should have valid hook structure", async () => {
578
+ const content = await fs.readFile(path.join(templateDir, template), "utf-8");
579
+ const parsed = JSON.parse(content) as Record<string, unknown>;
580
+
581
+ // New template structure: { description, hooks: [{ event, matcher, command, exit_behavior }] }
582
+ assert.ok(typeof parsed.description === "string", `${template}: Should have description`);
583
+ assert.ok(Array.isArray(parsed.hooks), `${template}: Should have hooks array`);
584
+
585
+ const hooks = parsed.hooks as Array<Record<string, unknown>>;
586
+ for (const hook of hooks) {
587
+ assert.ok(
588
+ validEvents.includes(hook.event as string),
589
+ `${template}: Event "${hook.event}" must be one of: ${validEvents.join(", ")}`
590
+ );
591
+ assert.ok(typeof hook.matcher === "string", `${template}: Hook should have matcher string`);
592
+ assert.ok(typeof hook.command === "string", `${template}: Hook should have command string`);
593
+ assert.ok(
594
+ ["notify", "block"].includes(hook.exit_behavior as string),
595
+ `${template}: exit_behavior should be "notify" or "block"`
596
+ );
597
+ }
598
+ });
599
+ });
600
+ }
601
+
602
+ describe("danger-blocker.json specific tests", () => {
603
+ it("should have Bash matcher and block exit behavior", async () => {
604
+ const content = await fs.readFile(path.join(templateDir, "danger-blocker.json"), "utf-8");
605
+ const parsed = JSON.parse(content) as Record<string, unknown>;
606
+ const hooks = parsed.hooks as Array<Record<string, unknown>>;
607
+
608
+ const bashHook = hooks.find(h => h.matcher === "Bash");
609
+ assert.ok(bashHook, "Should have Bash matcher hook");
610
+ assert.strictEqual(bashHook.exit_behavior, "block", "Should block dangerous commands");
611
+ assert.strictEqual(bashHook.event, "PreToolCall", "Should run before tool call");
612
+ });
613
+ });
614
+
615
+ describe("secret-detector.json specific tests", () => {
616
+ it("should have Bash matcher for secret detection", async () => {
617
+ const content = await fs.readFile(path.join(templateDir, "secret-detector.json"), "utf-8");
618
+ const parsed = JSON.parse(content) as Record<string, unknown>;
619
+ const hooks = parsed.hooks as Array<Record<string, unknown>>;
620
+
621
+ const bashHook = hooks.find(h => h.matcher === "Bash");
622
+ assert.ok(bashHook, "Should have Bash matcher hook");
623
+ assert.strictEqual(bashHook.exit_behavior, "block", "Should block commits with secrets");
624
+ });
625
+ });
626
+
627
+ describe("format-on-write.json specific tests", () => {
628
+ it("should have PostToolCall event for formatting", async () => {
629
+ const content = await fs.readFile(path.join(templateDir, "format-on-write.json"), "utf-8");
630
+ const parsed = JSON.parse(content) as Record<string, unknown>;
631
+ const hooks = parsed.hooks as Array<Record<string, unknown>>;
632
+
633
+ assert.ok(hooks.length > 0, "Should have at least one hook");
634
+ const hook = hooks[0];
635
+ assert.strictEqual(hook.event, "PostToolCall", "Should run after tool call");
636
+ assert.ok(
637
+ (hook.matcher as string).includes("Write") || (hook.matcher as string).includes("Edit"),
638
+ "Should match Write or Edit tools"
639
+ );
640
+
641
+ // Should have variants for different formatters
642
+ const variants = parsed.variants as Record<string, string>;
643
+ assert.ok(variants, "Should have formatter variants");
644
+ assert.ok(variants.prettier, "Should have prettier variant");
645
+ });
646
+ });
647
+
648
+ describe("lint-on-write.json specific tests", () => {
649
+ it("should have PostToolCall event for linting", async () => {
650
+ const content = await fs.readFile(path.join(templateDir, "lint-on-write.json"), "utf-8");
651
+ const parsed = JSON.parse(content) as Record<string, unknown>;
652
+ const hooks = parsed.hooks as Array<Record<string, unknown>>;
653
+
654
+ assert.ok(hooks.length > 0, "Should have at least one hook");
655
+ const hook = hooks[0];
656
+ assert.strictEqual(hook.event, "PostToolCall", "Should run after tool call");
657
+ assert.strictEqual(hook.exit_behavior, "block", "Should block on lint errors");
658
+
659
+ // Should have variants for different linters
660
+ const variants = parsed.variants as Record<string, string>;
661
+ assert.ok(variants, "Should have linter variants");
662
+ assert.ok(variants.eslint, "Should have eslint variant");
663
+ });
664
+ });
665
+ });
666
+
667
+ // ─────────────────────────────────────────────────────────────────────────────
668
+ // Integration: rebar_init_project installs enforcement
669
+ // ─────────────────────────────────────────────────────────────────────────────
670
+
671
+ describe("rebar_init_project enforcement installation", () => {
672
+ it("should install all hook templates", async () => {
673
+ const { path: testDir, cleanup } = await createTestDir();
674
+
675
+ try {
676
+ // Create minimal package.json
677
+ await fs.writeFile(
678
+ path.join(testDir, "package.json"),
679
+ JSON.stringify({ name: "test", version: "1.0.0" })
680
+ );
681
+
682
+ const result = await invokeTool("rebar_init_project", {
683
+ project_path: testDir,
684
+ tech_stack: ["node"],
685
+ team_size: 5,
686
+ });
687
+
688
+ assertToolSuccess(result);
689
+
690
+ // Check settings.json has hooks
691
+ const settingsContent = await readTestFile(testDir, ".claude/settings.json");
692
+ assert.ok(settingsContent, "Should create settings.json");
693
+
694
+ const settings = JSON.parse(settingsContent);
695
+
696
+ // Check for hook events
697
+ assert.ok(
698
+ settings.hooks?.PreToolUse || settings.hooks?.PostToolUse,
699
+ "Settings should have hooks configured"
700
+ );
701
+ } finally {
702
+ await cleanup();
703
+ }
704
+ });
705
+
706
+ it("should install all skill templates", async () => {
707
+ const { path: testDir, cleanup } = await createTestDir();
708
+
709
+ try {
710
+ await fs.writeFile(
711
+ path.join(testDir, "package.json"),
712
+ JSON.stringify({ name: "test", version: "1.0.0" })
713
+ );
714
+
715
+ const result = await invokeTool("rebar_init_project", {
716
+ project_path: testDir,
717
+ tech_stack: ["node"],
718
+ team_size: 5,
719
+ });
720
+
721
+ assertToolSuccess(result);
722
+
723
+ // Check for skills
724
+ const skillsDir = path.join(testDir, ".claude/skills");
725
+ const expectedSkills = ["code-review", "test-writer", "security-scan", "documentation", "performance-audit"];
726
+
727
+ for (const skill of expectedSkills) {
728
+ const skillExists = await testFileExists(testDir, `.claude/skills/${skill}/SKILL.md`);
729
+ assert.ok(skillExists, `Should install ${skill} skill`);
730
+ }
731
+ } finally {
732
+ await cleanup();
733
+ }
734
+ });
735
+
736
+ it("should install all agent templates", async () => {
737
+ const { path: testDir, cleanup } = await createTestDir();
738
+
739
+ try {
740
+ await fs.writeFile(
741
+ path.join(testDir, "package.json"),
742
+ JSON.stringify({ name: "test", version: "1.0.0" })
743
+ );
744
+
745
+ const result = await invokeTool("rebar_init_project", {
746
+ project_path: testDir,
747
+ tech_stack: ["node"],
748
+ team_size: 5,
749
+ });
750
+
751
+ assertToolSuccess(result);
752
+
753
+ // Check for agents
754
+ const expectedAgents = ["explore.md", "plan.md", "test-runner.md", "security-auditor.md"];
755
+
756
+ for (const agent of expectedAgents) {
757
+ const agentExists = await testFileExists(testDir, `.claude/agents/${agent}`);
758
+ assert.ok(agentExists, `Should install ${agent} agent`);
759
+ }
760
+ } finally {
761
+ await cleanup();
762
+ }
763
+ });
764
+
765
+ it("should create CLAUDE.md with essential sections", async () => {
766
+ const { path: testDir, cleanup } = await createTestDir();
767
+
768
+ try {
769
+ await fs.writeFile(
770
+ path.join(testDir, "package.json"),
771
+ JSON.stringify({ name: "test", version: "1.0.0" })
772
+ );
773
+
774
+ const result = await invokeTool("rebar_init_project", {
775
+ project_path: testDir,
776
+ tech_stack: ["node"],
777
+ team_size: 5,
778
+ });
779
+
780
+ assertToolSuccess(result);
781
+
782
+ const claudemd = await readTestFile(testDir, "CLAUDE.md");
783
+ assert.ok(claudemd, "Should create CLAUDE.md");
784
+
785
+ // Check for essential sections in generated CLAUDE.md
786
+ assert.ok(
787
+ claudemd.includes("##") && claudemd.length > 100,
788
+ "CLAUDE.md should have sections and substantial content"
789
+ );
790
+ } finally {
791
+ await cleanup();
792
+ }
793
+ });
794
+ });