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,451 @@
1
+ /**
2
+ * Integration tests for management tools
3
+ * rebar_list_artifacts, rebar_validate_config, rebar_audit_context
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
+ assertToolError,
16
+ getToolResultText,
17
+ parseToolResultJson,
18
+ createMinimalClaudeMd,
19
+ createMinimalSkill,
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_list_artifacts", () => {
29
+ // TODO: This test is flaky due to file system timing issues in temp directories
30
+ // The tool returns empty artifacts even though files exist - needs investigation
31
+ it.skip("should list all artifacts in a configured project", async () => {
32
+ const { path: testDir, cleanup } = await createTestDir();
33
+
34
+ try {
35
+ await createMockProject(testDir, {
36
+ existingClaudeMd: createMinimalClaudeMd(),
37
+ existingSettings: { permissions: { allow: [] } },
38
+ });
39
+
40
+ // Create a skill
41
+ const skillDir = path.join(testDir, ".claude/skills/test-skill");
42
+ await fs.mkdir(skillDir, { recursive: true });
43
+ await fs.writeFile(
44
+ path.join(skillDir, "SKILL.md"),
45
+ createMinimalSkill("test-skill", "A test skill")
46
+ );
47
+
48
+ // Use JSON output for more reliable assertions
49
+ const result = await invokeTool("rebar_list_artifacts", {
50
+ project_path: testDir,
51
+ output_format: "json",
52
+ });
53
+
54
+ assertToolSuccess(result);
55
+
56
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
57
+
58
+ // Detailed debugging
59
+ const debugInfo = `
60
+ Full response: ${JSON.stringify(parsed, null, 2)}
61
+ Project path: ${testDir}
62
+ Data keys: ${Object.keys(parsed.data as Record<string, unknown> || {}).join(", ")}
63
+ `;
64
+
65
+ assert.strictEqual(parsed.success, true, `Tool should succeed. ${debugInfo}`);
66
+
67
+ const data = parsed.data as Record<string, unknown>;
68
+ const artifacts = data.artifacts as Array<Record<string, string>> || [];
69
+
70
+ // Check we found the expected artifacts
71
+ const types = artifacts.map(a => a.type);
72
+ assert.ok(types.includes("claudemd"), `Should find CLAUDE.md. ${debugInfo}`);
73
+ assert.ok(types.includes("skill"), `Should find skill. Found types: ${types.join(", ")}`);
74
+ assert.ok(types.includes("settings"), `Should find settings. Found types: ${types.join(", ")}`);
75
+ } finally {
76
+ await cleanup();
77
+ }
78
+ });
79
+
80
+ it("should filter by artifact type", async () => {
81
+ const { path: testDir, cleanup } = await createTestDir();
82
+
83
+ try {
84
+ await createMockProject(testDir, {
85
+ existingClaudeMd: createMinimalClaudeMd(),
86
+ existingSettings: { permissions: { allow: [] } },
87
+ });
88
+
89
+ // Create a skill
90
+ const skillDir = path.join(testDir, ".claude/skills/filter-test");
91
+ await fs.mkdir(skillDir, { recursive: true });
92
+ await fs.writeFile(
93
+ path.join(skillDir, "SKILL.md"),
94
+ createMinimalSkill("filter-test", "Filter test skill")
95
+ );
96
+
97
+ const result = await invokeTool("rebar_list_artifacts", {
98
+ project_path: testDir,
99
+ type_filter: "skill",
100
+ });
101
+
102
+ assertToolSuccess(result);
103
+
104
+ const text = getToolResultText(result);
105
+ assert.ok(text.includes("SKILL"), "Should list skills");
106
+ assert.ok(!text.includes("CLAUDEMD"), "Should NOT list CLAUDE.md when filtering");
107
+ } finally {
108
+ await cleanup();
109
+ }
110
+ });
111
+
112
+ it("should show 'no artifacts' message for empty project", async () => {
113
+ const { path: testDir, cleanup } = await createTestDir();
114
+
115
+ try {
116
+ // Create minimal project without Claude artifacts
117
+ await fs.mkdir(testDir, { recursive: true });
118
+ await fs.writeFile(
119
+ path.join(testDir, "package.json"),
120
+ JSON.stringify({ name: "empty-project" })
121
+ );
122
+
123
+ const result = await invokeTool("rebar_list_artifacts", {
124
+ project_path: testDir,
125
+ });
126
+
127
+ assertToolSuccess(result);
128
+
129
+ const text = getToolResultText(result);
130
+ assert.ok(
131
+ text.includes("No Claude Code artifacts") || text.includes("artifacts: []"),
132
+ "Should indicate no artifacts found"
133
+ );
134
+ } finally {
135
+ await cleanup();
136
+ }
137
+ });
138
+
139
+ it("should return JSON output when requested", async () => {
140
+ const { path: testDir, cleanup } = await createTestDir();
141
+
142
+ try {
143
+ await createMockProject(testDir, {
144
+ existingClaudeMd: createMinimalClaudeMd(),
145
+ });
146
+
147
+ const result = await invokeTool("rebar_list_artifacts", {
148
+ project_path: testDir,
149
+ output_format: "json",
150
+ });
151
+
152
+ assertToolSuccess(result);
153
+
154
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
155
+ assert.strictEqual(parsed.success, true);
156
+ assert.strictEqual(parsed.operation, "list_artifacts");
157
+
158
+ const data = parsed.data as Record<string, unknown>;
159
+ assert.ok(Array.isArray(data.artifacts), "Should have artifacts array");
160
+ } finally {
161
+ await cleanup();
162
+ }
163
+ });
164
+ });
165
+
166
+ describe("rebar_validate_config", () => {
167
+ it("should pass validation for well-structured config", async () => {
168
+ const { path: testDir, cleanup } = await createTestDir();
169
+
170
+ try {
171
+ await createMockProject(testDir, {
172
+ existingClaudeMd: createMinimalClaudeMd(),
173
+ existingSettings: {
174
+ permissions: { allow: [], deny: [] },
175
+ hooks: {
176
+ PreToolCall: [
177
+ { command: "echo test", description: "Test hook" },
178
+ ],
179
+ },
180
+ },
181
+ });
182
+
183
+ const result = await invokeTool("rebar_validate_config", {
184
+ project_path: testDir,
185
+ });
186
+
187
+ assertToolSuccess(result);
188
+
189
+ const text = getToolResultText(result);
190
+ assert.ok(text.includes("PASSED") || text.includes("valid"), "Should pass validation");
191
+ } finally {
192
+ await cleanup();
193
+ }
194
+ });
195
+
196
+ it("should report errors for missing CLAUDE.md", async () => {
197
+ const { path: testDir, cleanup } = await createTestDir();
198
+
199
+ try {
200
+ await createMockProject(testDir);
201
+ // Don't create CLAUDE.md
202
+
203
+ const result = await invokeTool("rebar_validate_config", {
204
+ project_path: testDir,
205
+ });
206
+
207
+ assertToolSuccess(result); // Tool succeeds but reports errors
208
+
209
+ const text = getToolResultText(result);
210
+ assert.ok(
211
+ text.includes("No CLAUDE.md") || text.includes("error"),
212
+ "Should report missing CLAUDE.md"
213
+ );
214
+ } finally {
215
+ await cleanup();
216
+ }
217
+ });
218
+
219
+ it("should report errors for invalid JSON in settings", async () => {
220
+ const { path: testDir, cleanup } = await createTestDir();
221
+
222
+ try {
223
+ await createMockProject(testDir, {
224
+ existingClaudeMd: createMinimalClaudeMd(),
225
+ });
226
+
227
+ // Create invalid JSON settings
228
+ await fs.mkdir(path.join(testDir, ".claude"), { recursive: true });
229
+ await fs.writeFile(
230
+ path.join(testDir, ".claude/settings.json"),
231
+ "{ invalid json }"
232
+ );
233
+
234
+ const result = await invokeTool("rebar_validate_config", {
235
+ project_path: testDir,
236
+ });
237
+
238
+ assertToolSuccess(result);
239
+
240
+ const text = getToolResultText(result);
241
+ assert.ok(
242
+ text.includes("FAILED") || text.includes("error") || text.includes("invalid"),
243
+ "Should report JSON error"
244
+ );
245
+ } finally {
246
+ await cleanup();
247
+ }
248
+ });
249
+
250
+ it("should return isError=true in CI mode when validation fails", async () => {
251
+ const { path: testDir, cleanup } = await createTestDir();
252
+
253
+ try {
254
+ await createMockProject(testDir);
255
+ // No CLAUDE.md = validation error
256
+
257
+ const result = await invokeTool("rebar_validate_config", {
258
+ project_path: testDir,
259
+ ci_mode: true,
260
+ });
261
+
262
+ // In CI mode with errors, should return isError: true
263
+ assert.ok(result.isError, "Should return isError=true in CI mode with failures");
264
+
265
+ // Should be JSON output
266
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
267
+ assert.strictEqual(parsed.success, false, "success should be false");
268
+ assert.ok(parsed.ci_mode || (parsed.data as Record<string, unknown>)?.ci_mode, "Should include ci_mode flag");
269
+ } finally {
270
+ await cleanup();
271
+ }
272
+ });
273
+
274
+ it("should not return isError in CI mode when validation passes", async () => {
275
+ const { path: testDir, cleanup } = await createTestDir();
276
+
277
+ try {
278
+ await createMockProject(testDir, {
279
+ existingClaudeMd: createMinimalClaudeMd(),
280
+ });
281
+
282
+ const result = await invokeTool("rebar_validate_config", {
283
+ project_path: testDir,
284
+ ci_mode: true,
285
+ });
286
+
287
+ // In CI mode without errors, should NOT have isError
288
+ assert.ok(!result.isError, "Should NOT return isError when validation passes");
289
+ } finally {
290
+ await cleanup();
291
+ }
292
+ });
293
+
294
+ it("should auto-fix trailing commas in JSON when fix_mode is auto_fix", async () => {
295
+ const { path: testDir, cleanup } = await createTestDir();
296
+
297
+ try {
298
+ await createMockProject(testDir, {
299
+ existingClaudeMd: createMinimalClaudeMd(),
300
+ });
301
+
302
+ // Create settings with trailing comma (fixable)
303
+ await fs.mkdir(path.join(testDir, ".claude"), { recursive: true });
304
+ await fs.writeFile(
305
+ path.join(testDir, ".claude/settings.json"),
306
+ '{ "permissions": { "allow": [], }, }'
307
+ );
308
+
309
+ const result = await invokeTool("rebar_validate_config", {
310
+ project_path: testDir,
311
+ fix_mode: "auto_fix",
312
+ });
313
+
314
+ assertToolSuccess(result);
315
+
316
+ const text = getToolResultText(result);
317
+ assert.ok(
318
+ text.includes("Fixed") || text.includes("fix"),
319
+ "Should indicate fix was applied"
320
+ );
321
+
322
+ // Verify file was fixed
323
+ const settings = await readTestFile(testDir, ".claude/settings.json");
324
+ assert.ok(settings, "Settings file should exist");
325
+ // Should be parseable now
326
+ assert.doesNotThrow(() => JSON.parse(settings!), "Fixed JSON should be valid");
327
+ } finally {
328
+ await cleanup();
329
+ }
330
+ });
331
+ });
332
+
333
+ describe("rebar_audit_context", () => {
334
+ it("should report context budget for configured project", async () => {
335
+ const { path: testDir, cleanup } = await createTestDir();
336
+
337
+ try {
338
+ await createMockProject(testDir, {
339
+ existingClaudeMd: createMinimalClaudeMd(),
340
+ });
341
+
342
+ const result = await invokeTool("rebar_audit_context", {
343
+ project_path: testDir,
344
+ });
345
+
346
+ assertToolSuccess(result);
347
+
348
+ const text = getToolResultText(result);
349
+ assert.ok(text.includes("Context Budget"), "Should show Context Budget header");
350
+ assert.ok(text.includes("CLAUDE.md"), "Should show CLAUDE.md tokens");
351
+ assert.ok(text.includes("tokens"), "Should show token counts");
352
+ assert.ok(text.includes("%"), "Should show percentage");
353
+ } finally {
354
+ await cleanup();
355
+ }
356
+ });
357
+
358
+ it("should warn about large skills", async () => {
359
+ const { path: testDir, cleanup } = await createTestDir();
360
+
361
+ try {
362
+ await createMockProject(testDir, {
363
+ existingClaudeMd: createMinimalClaudeMd(),
364
+ });
365
+
366
+ // Create a large skill (> 500 tokens)
367
+ const skillDir = path.join(testDir, ".claude/skills/large-skill");
368
+ await fs.mkdir(skillDir, { recursive: true });
369
+
370
+ // Generate content that's roughly 600+ tokens
371
+ const largeContent =
372
+ createMinimalSkill("large-skill", "A very large skill") +
373
+ "\n" +
374
+ "Instructions: ".repeat(200) +
375
+ "\nThis skill does many things.\n".repeat(100);
376
+
377
+ await fs.writeFile(path.join(skillDir, "SKILL.md"), largeContent);
378
+
379
+ const result = await invokeTool("rebar_audit_context", {
380
+ project_path: testDir,
381
+ });
382
+
383
+ assertToolSuccess(result);
384
+
385
+ const text = getToolResultText(result);
386
+ // Should warn about large skill
387
+ assert.ok(
388
+ text.includes("large-skill") && (text.includes("⚠") || text.includes("warning")),
389
+ "Should warn about large skill"
390
+ );
391
+ } finally {
392
+ await cleanup();
393
+ }
394
+ });
395
+
396
+ it("should return JSON output when requested", async () => {
397
+ const { path: testDir, cleanup } = await createTestDir();
398
+
399
+ try {
400
+ await createMockProject(testDir, {
401
+ existingClaudeMd: createMinimalClaudeMd(),
402
+ });
403
+
404
+ const result = await invokeTool("rebar_audit_context", {
405
+ project_path: testDir,
406
+ output_format: "json",
407
+ });
408
+
409
+ assertToolSuccess(result);
410
+
411
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
412
+ assert.strictEqual(parsed.success, true);
413
+ assert.strictEqual(parsed.operation, "audit_context");
414
+
415
+ const data = parsed.data as Record<string, unknown>;
416
+ assert.ok(data.tokens, "Should have tokens breakdown");
417
+ assert.ok(data.budget, "Should have budget info");
418
+
419
+ const tokens = data.tokens as Record<string, number>;
420
+ assert.ok(typeof tokens.claudemd === "number", "Should have claudemd token count");
421
+ assert.ok(typeof tokens.total === "number", "Should have total token count");
422
+ } finally {
423
+ await cleanup();
424
+ }
425
+ });
426
+
427
+ it("should report healthy budget for minimal config", async () => {
428
+ const { path: testDir, cleanup } = await createTestDir();
429
+
430
+ try {
431
+ await createMockProject(testDir, {
432
+ existingClaudeMd: createMinimalClaudeMd(),
433
+ });
434
+
435
+ const result = await invokeTool("rebar_audit_context", {
436
+ project_path: testDir,
437
+ output_format: "json",
438
+ });
439
+
440
+ assertToolSuccess(result);
441
+
442
+ const parsed = parseToolResultJson<Record<string, unknown>>(result);
443
+ const data = parsed.data as Record<string, unknown>;
444
+ const budget = data.budget as Record<string, unknown>;
445
+
446
+ assert.ok(budget.healthy === true, "Minimal config should be healthy");
447
+ } finally {
448
+ await cleanup();
449
+ }
450
+ });
451
+ });