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,483 @@
1
+ /**
2
+ * Integration tests for Rebar enterprise tools
3
+ * rebar_apply_compliance, rebar_create_ci_workflow,
4
+ * rebar_create_security_hook, rebar_generate_mcp_config
5
+ */
6
+ import { describe, it, before } from "node:test";
7
+ import * as assert from "node:assert";
8
+ import * as fs from "node:fs/promises";
9
+ import * as path from "node:path";
10
+ import {
11
+ createTestDir,
12
+ createMockProject,
13
+ readTestFile,
14
+ testFileExists,
15
+ readTestJson,
16
+ assertToolSuccess,
17
+ getToolResultText,
18
+ parseToolResultJson,
19
+ createMinimalClaudeMd,
20
+ } from "./test-utils.js";
21
+ import { initializeTools, invokeTool } from "./tool-harness.js";
22
+
23
+ // Initialize tools before all tests
24
+ before(() => {
25
+ initializeTools();
26
+ });
27
+
28
+ describe("rebar_apply_compliance", () => {
29
+ it("should apply HIPAA compliance pack", async () => {
30
+ const { path: testDir, cleanup } = await createTestDir();
31
+
32
+ try {
33
+ await createMockProject(testDir, {
34
+ existingClaudeMd: createMinimalClaudeMd(),
35
+ });
36
+
37
+ const result = await invokeTool("rebar_apply_compliance", {
38
+ project_path: testDir,
39
+ standard: "hipaa",
40
+ scope: "full",
41
+ });
42
+
43
+ assertToolSuccess(result);
44
+
45
+ const text = getToolResultText(result);
46
+ assert.ok(text.includes("HIPAA"), "Should mention HIPAA");
47
+
48
+ // Should have added hooks to settings
49
+ const settings = await readTestJson<Record<string, unknown>>(
50
+ testDir,
51
+ ".claude/settings.json"
52
+ );
53
+ assert.ok(settings, "Settings should exist");
54
+ assert.ok(settings.hooks, "Should have hooks");
55
+ } finally {
56
+ await cleanup();
57
+ }
58
+ });
59
+
60
+ it("should apply SOC2 compliance pack", async () => {
61
+ const { path: testDir, cleanup } = await createTestDir();
62
+
63
+ try {
64
+ await createMockProject(testDir, {
65
+ existingClaudeMd: createMinimalClaudeMd(),
66
+ });
67
+
68
+ const result = await invokeTool("rebar_apply_compliance", {
69
+ project_path: testDir,
70
+ standard: "soc2",
71
+ scope: "full",
72
+ });
73
+
74
+ assertToolSuccess(result);
75
+
76
+ const text = getToolResultText(result);
77
+ assert.ok(text.includes("SOC2"), "Should mention SOC2");
78
+ } finally {
79
+ await cleanup();
80
+ }
81
+ });
82
+
83
+ it("should support hooks_only scope", async () => {
84
+ const { path: testDir, cleanup } = await createTestDir();
85
+
86
+ try {
87
+ await createMockProject(testDir, {
88
+ existingClaudeMd: createMinimalClaudeMd(),
89
+ });
90
+
91
+ const result = await invokeTool("rebar_apply_compliance", {
92
+ project_path: testDir,
93
+ standard: "pci-dss",
94
+ scope: "hooks_only",
95
+ });
96
+
97
+ assertToolSuccess(result);
98
+
99
+ // Should have hooks but not skills
100
+ const settings = await readTestJson<Record<string, unknown>>(
101
+ testDir,
102
+ ".claude/settings.json"
103
+ );
104
+ assert.ok(settings?.hooks, "Should have hooks");
105
+ } finally {
106
+ await cleanup();
107
+ }
108
+ });
109
+
110
+ it("should support dry_run mode", async () => {
111
+ const { path: testDir, cleanup } = await createTestDir();
112
+
113
+ try {
114
+ await createMockProject(testDir, {
115
+ existingClaudeMd: createMinimalClaudeMd(),
116
+ });
117
+
118
+ const result = await invokeTool("rebar_apply_compliance", {
119
+ project_path: testDir,
120
+ standard: "sox",
121
+ scope: "full",
122
+ dry_run: true,
123
+ output_format: "json",
124
+ });
125
+
126
+ assertToolSuccess(result);
127
+
128
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
129
+ assert.strictEqual(parsed.dry_run, true);
130
+
131
+ // Settings should NOT be modified
132
+ assert.ok(
133
+ !(await testFileExists(testDir, ".claude/settings.json")),
134
+ "Settings should not be created in dry-run"
135
+ );
136
+ } finally {
137
+ await cleanup();
138
+ }
139
+ });
140
+ });
141
+
142
+ describe("rebar_create_ci_workflow", () => {
143
+ it("should create GitHub Actions workflow", async () => {
144
+ const { path: testDir, cleanup } = await createTestDir();
145
+
146
+ try {
147
+ await createMockProject(testDir);
148
+
149
+ const result = await invokeTool("rebar_create_ci_workflow", {
150
+ repo_path: testDir,
151
+ review_focus: "security",
152
+ branch_pattern: "main",
153
+ });
154
+
155
+ assertToolSuccess(result);
156
+
157
+ assert.ok(
158
+ await testFileExists(testDir, ".github/workflows/claude-review.yml"),
159
+ "Workflow file should be created"
160
+ );
161
+
162
+ const workflow = await readTestFile(
163
+ testDir,
164
+ ".github/workflows/claude-review.yml"
165
+ );
166
+ assert.ok(workflow, "Workflow should have content");
167
+ assert.ok(workflow.includes("Claude Code Review"), "Should have workflow name");
168
+ assert.ok(workflow.includes("pull_request"), "Should trigger on PRs");
169
+ assert.ok(workflow.includes("security"), "Should include security focus");
170
+ assert.ok(workflow.includes("ANTHROPIC_API_KEY"), "Should reference API key secret");
171
+ } finally {
172
+ await cleanup();
173
+ }
174
+ });
175
+
176
+ it("should support different review focuses", async () => {
177
+ const focuses = ["security", "performance", "style", "all"] as const;
178
+
179
+ for (const focus of focuses) {
180
+ const { path: testDir, cleanup } = await createTestDir();
181
+
182
+ try {
183
+ await createMockProject(testDir);
184
+
185
+ const result = await invokeTool("rebar_create_ci_workflow", {
186
+ repo_path: testDir,
187
+ review_focus: focus,
188
+ });
189
+
190
+ assertToolSuccess(result);
191
+
192
+ const workflow = await readTestFile(
193
+ testDir,
194
+ ".github/workflows/claude-review.yml"
195
+ );
196
+ assert.ok(workflow?.includes(focus), `Should include ${focus} focus`);
197
+ } finally {
198
+ await cleanup();
199
+ }
200
+ }
201
+ });
202
+
203
+ it("should support dry_run mode", async () => {
204
+ const { path: testDir, cleanup } = await createTestDir();
205
+
206
+ try {
207
+ await createMockProject(testDir);
208
+
209
+ const result = await invokeTool("rebar_create_ci_workflow", {
210
+ repo_path: testDir,
211
+ review_focus: "all",
212
+ dry_run: true,
213
+ output_format: "json",
214
+ });
215
+
216
+ assertToolSuccess(result);
217
+
218
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
219
+ assert.strictEqual(parsed.dry_run, true);
220
+
221
+ // Workflow should NOT be created
222
+ assert.ok(
223
+ !(await testFileExists(testDir, ".github/workflows/claude-review.yml")),
224
+ "Workflow should not be created in dry-run"
225
+ );
226
+ } finally {
227
+ await cleanup();
228
+ }
229
+ });
230
+ });
231
+
232
+ describe("rebar_create_security_hook", () => {
233
+ it("should create security hook with standard strictness", async () => {
234
+ const { path: testDir, cleanup } = await createTestDir();
235
+
236
+ try {
237
+ await createMockProject(testDir);
238
+
239
+ const result = await invokeTool("rebar_create_security_hook", {
240
+ project_path: testDir,
241
+ strictness: "standard",
242
+ });
243
+
244
+ assertToolSuccess(result);
245
+
246
+ // Should create blocker script
247
+ assert.ok(
248
+ await testFileExists(testDir, ".claude/scripts/security-blocker.sh"),
249
+ "Blocker script should be created"
250
+ );
251
+
252
+ // Should update settings
253
+ const settings = await readTestJson<Record<string, unknown>>(
254
+ testDir,
255
+ ".claude/settings.json"
256
+ );
257
+ assert.ok(settings, "Settings should exist");
258
+ assert.ok(settings.hooks, "Should have hooks");
259
+ assert.ok(settings.permissions, "Should have permissions");
260
+
261
+ const perms = settings.permissions as Record<string, unknown>;
262
+ assert.ok(Array.isArray(perms.deny), "Should have deny list");
263
+ } finally {
264
+ await cleanup();
265
+ }
266
+ });
267
+
268
+ it("should support strict strictness level", async () => {
269
+ const { path: testDir, cleanup } = await createTestDir();
270
+
271
+ try {
272
+ await createMockProject(testDir);
273
+
274
+ const result = await invokeTool("rebar_create_security_hook", {
275
+ project_path: testDir,
276
+ strictness: "strict",
277
+ });
278
+
279
+ assertToolSuccess(result);
280
+
281
+ const script = await readTestFile(
282
+ testDir,
283
+ ".claude/scripts/security-blocker.sh"
284
+ );
285
+ assert.ok(script, "Script should exist");
286
+ assert.ok(script.includes("strict"), "Should mention strict level");
287
+ } finally {
288
+ await cleanup();
289
+ }
290
+ });
291
+
292
+ it("should support paranoid strictness level", async () => {
293
+ const { path: testDir, cleanup } = await createTestDir();
294
+
295
+ try {
296
+ await createMockProject(testDir);
297
+
298
+ const result = await invokeTool("rebar_create_security_hook", {
299
+ project_path: testDir,
300
+ strictness: "paranoid",
301
+ });
302
+
303
+ assertToolSuccess(result);
304
+
305
+ const script = await readTestFile(
306
+ testDir,
307
+ ".claude/scripts/security-blocker.sh"
308
+ );
309
+ assert.ok(script, "Script should exist");
310
+ assert.ok(script.includes("paranoid"), "Should mention paranoid level");
311
+ } finally {
312
+ await cleanup();
313
+ }
314
+ });
315
+
316
+ it("should support dry_run mode", async () => {
317
+ const { path: testDir, cleanup } = await createTestDir();
318
+
319
+ try {
320
+ await createMockProject(testDir);
321
+
322
+ const result = await invokeTool("rebar_create_security_hook", {
323
+ project_path: testDir,
324
+ strictness: "standard",
325
+ dry_run: true,
326
+ output_format: "json",
327
+ });
328
+
329
+ assertToolSuccess(result);
330
+
331
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
332
+ assert.strictEqual(parsed.dry_run, true);
333
+
334
+ // Script should NOT be created
335
+ assert.ok(
336
+ !(await testFileExists(testDir, ".claude/scripts/security-blocker.sh")),
337
+ "Script should not be created in dry-run"
338
+ );
339
+ } finally {
340
+ await cleanup();
341
+ }
342
+ });
343
+
344
+ it("should make blocker script executable", async () => {
345
+ const { path: testDir, cleanup } = await createTestDir();
346
+
347
+ try {
348
+ await createMockProject(testDir);
349
+
350
+ await invokeTool("rebar_create_security_hook", {
351
+ project_path: testDir,
352
+ strictness: "standard",
353
+ });
354
+
355
+ const scriptPath = path.join(testDir, ".claude/scripts/security-blocker.sh");
356
+ const stats = await fs.stat(scriptPath);
357
+
358
+ // Check if executable bit is set (mode & 0o111)
359
+ assert.ok(
360
+ (stats.mode & 0o111) !== 0,
361
+ "Script should be executable"
362
+ );
363
+ } finally {
364
+ await cleanup();
365
+ }
366
+ });
367
+ });
368
+
369
+ describe("rebar_generate_mcp_config", () => {
370
+ it("should generate project-scoped MCP config", async () => {
371
+ const { path: testDir, cleanup } = await createTestDir();
372
+
373
+ try {
374
+ await createMockProject(testDir);
375
+
376
+ const result = await invokeTool("rebar_generate_mcp_config", {
377
+ project_path: testDir,
378
+ services: ["github", "slack"],
379
+ scope: "project",
380
+ });
381
+
382
+ assertToolSuccess(result);
383
+
384
+ assert.ok(
385
+ await testFileExists(testDir, ".mcp.json"),
386
+ "MCP config should be created"
387
+ );
388
+
389
+ const config = await readTestJson<Record<string, unknown>>(testDir, ".mcp.json");
390
+ assert.ok(config, "Config should be valid JSON");
391
+ assert.ok(config.mcpServers, "Should have mcpServers");
392
+
393
+ const servers = config.mcpServers as Record<string, unknown>;
394
+ assert.ok(servers.github, "Should have github server");
395
+ assert.ok(servers.slack, "Should have slack server");
396
+ } finally {
397
+ await cleanup();
398
+ }
399
+ });
400
+
401
+ it("should support multiple services", async () => {
402
+ const { path: testDir, cleanup } = await createTestDir();
403
+
404
+ try {
405
+ await createMockProject(testDir);
406
+
407
+ const services = ["github", "slack", "sentry", "postgres"] as const;
408
+
409
+ const result = await invokeTool("rebar_generate_mcp_config", {
410
+ project_path: testDir,
411
+ services: [...services],
412
+ scope: "project",
413
+ });
414
+
415
+ assertToolSuccess(result);
416
+
417
+ const config = await readTestJson<Record<string, unknown>>(testDir, ".mcp.json");
418
+ const servers = config?.mcpServers as Record<string, unknown>;
419
+
420
+ for (const service of services) {
421
+ assert.ok(servers[service], `Should have ${service} server`);
422
+ }
423
+ } finally {
424
+ await cleanup();
425
+ }
426
+ });
427
+
428
+ it("should support dry_run mode", async () => {
429
+ const { path: testDir, cleanup } = await createTestDir();
430
+
431
+ try {
432
+ await createMockProject(testDir);
433
+
434
+ const result = await invokeTool("rebar_generate_mcp_config", {
435
+ project_path: testDir,
436
+ services: ["github"],
437
+ scope: "project",
438
+ dry_run: true,
439
+ output_format: "json",
440
+ });
441
+
442
+ assertToolSuccess(result);
443
+
444
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
445
+ assert.strictEqual(parsed.dry_run, true);
446
+
447
+ // Config should NOT be created (unless project already had one)
448
+ // Actually the mock creates a basic one, so check would_create is empty
449
+ const wouldCreate = parsed.would_create as unknown[];
450
+ // In dry-run, if file doesn't exist, would_create should have it
451
+ // Since createMockProject doesn't create .mcp.json by default, it should be in would_create
452
+ } finally {
453
+ await cleanup();
454
+ }
455
+ });
456
+
457
+ it("should return JSON output when requested", async () => {
458
+ const { path: testDir, cleanup } = await createTestDir();
459
+
460
+ try {
461
+ await createMockProject(testDir);
462
+
463
+ const result = await invokeTool("rebar_generate_mcp_config", {
464
+ project_path: testDir,
465
+ services: ["github", "slack"],
466
+ scope: "project",
467
+ output_format: "json",
468
+ });
469
+
470
+ assertToolSuccess(result);
471
+
472
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
473
+ assert.strictEqual(parsed.success, true);
474
+ assert.strictEqual(parsed.operation, "generate_mcp_config");
475
+
476
+ const data = parsed.data as Record<string, unknown>;
477
+ assert.ok(data.config_path, "Should have config_path");
478
+ assert.ok(Array.isArray(data.services), "Should have services array");
479
+ } finally {
480
+ await cleanup();
481
+ }
482
+ });
483
+ });