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.
- package/.claude/agents/template-writer.md +43 -0
- package/.claude/agents/test-runner.md +47 -0
- package/.claude/mcp.json +9 -0
- package/.claude/settings.json +29 -0
- package/.claude/skills/ /SKILL.md +21 -0
- package/.claude/skills/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SKILL.md +21 -0
- package/.claude/skills/bmmibwetxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwjgvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwsesxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwxufxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibx3r9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmji0lrkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjiniphxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjio86zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjiolfbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjit1lvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjita1qxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibweu3/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwjh4/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwsey/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwxup/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibx3rg/SKILL.md +21 -0
- package/.claude/skills/bnd-mmji0lrp/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjinipm/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjio875/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjiolfg/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjit1m3/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjita1x/SKILL.md +21 -0
- package/.claude/skills/coercion-test/SKILL.md +50 -0
- package/.claude/skills/large-skill/SKILL.md +21 -0
- package/.claude/skills/long-desc-skill/SKILL.md +21 -0
- package/.claude/skills/mcp-dev/SKILL.md +61 -0
- package/.claude/skills/nl-mmibweus/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwjhf/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwsf7/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwxvq/SKILL.md +25 -0
- package/.claude/skills/nl-mmibx3rt/SKILL.md +25 -0
- package/.claude/skills/nl-mmji0lrz/SKILL.md +25 -0
- package/.claude/skills/nl-mmjinipx/SKILL.md +25 -0
- package/.claude/skills/nl-mmjio87f/SKILL.md +25 -0
- package/.claude/skills/nl-mmjiolfs/SKILL.md +25 -0
- package/.claude/skills/nl-mmjit1mc/SKILL.md +25 -0
- package/.claude/skills/nl-mmjita26/SKILL.md +25 -0
- package/.claude/skills/rapid-1/SKILL.md +21 -0
- package/.claude/skills/rapid-2/SKILL.md +21 -0
- package/.claude/skills/rapid-3/SKILL.md +21 -0
- package/.claude/skills/rapid-4/SKILL.md +21 -0
- package/.claude/skills/rapid-5/SKILL.md +21 -0
- package/.claude/skills/test/", /"malicious/": /"true/SKILL.md" +69 -0
- package/.claude/skills/test-emoji-/360/237/230/200-skill/SKILL.md +69 -0
- package/.claude/skills/test-skill/SKILL.md +69 -0
- package/.claude/skills/test; rm -rf /; skill/SKILL.md +69 -0
- package/.claude/skills/test<script>alert(1)</script>skill/SKILL.md +69 -0
- package/.claudeignore +5 -0
- package/.mcp.json +3 -0
- package/CHANGELOG.md +29 -0
- package/CLAUDE.md +76 -0
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/ROADMAP.md +526 -0
- package/ccboot-PRD-v1.0.docx.md +732 -0
- package/ccboot-v1.2.0-enforcement-spec.md +1272 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +674 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +25 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +118 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +62 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +15 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/scaffolding.d.ts +277 -0
- package/dist/schemas/scaffolding.d.ts.map +1 -0
- package/dist/schemas/scaffolding.js +133 -0
- package/dist/schemas/scaffolding.js.map +1 -0
- package/dist/services/claudemd-generator.d.ts +16 -0
- package/dist/services/claudemd-generator.d.ts.map +1 -0
- package/dist/services/claudemd-generator.js +426 -0
- package/dist/services/claudemd-generator.js.map +1 -0
- package/dist/services/codex-generator.d.ts +6 -0
- package/dist/services/codex-generator.d.ts.map +1 -0
- package/dist/services/codex-generator.js +35 -0
- package/dist/services/codex-generator.js.map +1 -0
- package/dist/services/cursor-generator.d.ts +15 -0
- package/dist/services/cursor-generator.d.ts.map +1 -0
- package/dist/services/cursor-generator.js +134 -0
- package/dist/services/cursor-generator.js.map +1 -0
- package/dist/services/file-ops.d.ts +48 -0
- package/dist/services/file-ops.d.ts.map +1 -0
- package/dist/services/file-ops.js +153 -0
- package/dist/services/file-ops.js.map +1 -0
- package/dist/services/output-formatter.d.ts +57 -0
- package/dist/services/output-formatter.d.ts.map +1 -0
- package/dist/services/output-formatter.js +88 -0
- package/dist/services/output-formatter.js.map +1 -0
- package/dist/services/platform-detect.d.ts +14 -0
- package/dist/services/platform-detect.d.ts.map +1 -0
- package/dist/services/platform-detect.js +63 -0
- package/dist/services/platform-detect.js.map +1 -0
- package/dist/services/project-analyzer.d.ts +71 -0
- package/dist/services/project-analyzer.d.ts.map +1 -0
- package/dist/services/project-analyzer.js +595 -0
- package/dist/services/project-analyzer.js.map +1 -0
- package/dist/services/rules-engine.d.ts +41 -0
- package/dist/services/rules-engine.d.ts.map +1 -0
- package/dist/services/rules-engine.js +304 -0
- package/dist/services/rules-engine.js.map +1 -0
- package/dist/services/strictness.d.ts +37 -0
- package/dist/services/strictness.d.ts.map +1 -0
- package/dist/services/strictness.js +182 -0
- package/dist/services/strictness.js.map +1 -0
- package/dist/services/template-engine.d.ts +16 -0
- package/dist/services/template-engine.d.ts.map +1 -0
- package/dist/services/template-engine.js +85 -0
- package/dist/services/template-engine.js.map +1 -0
- package/dist/services/validation.d.ts +41 -0
- package/dist/services/validation.d.ts.map +1 -0
- package/dist/services/validation.js +104 -0
- package/dist/services/validation.js.map +1 -0
- package/dist/services/windsurf-generator.d.ts +15 -0
- package/dist/services/windsurf-generator.d.ts.map +1 -0
- package/dist/services/windsurf-generator.js +127 -0
- package/dist/services/windsurf-generator.js.map +1 -0
- package/dist/tests/enforcement.test.d.ts +2 -0
- package/dist/tests/enforcement.test.d.ts.map +1 -0
- package/dist/tests/enforcement.test.js +541 -0
- package/dist/tests/enforcement.test.js.map +1 -0
- package/dist/tests/enterprise.test.d.ts +2 -0
- package/dist/tests/enterprise.test.d.ts.map +1 -0
- package/dist/tests/enterprise.test.js +353 -0
- package/dist/tests/enterprise.test.js.map +1 -0
- package/dist/tests/fuzzing.test.d.ts +2 -0
- package/dist/tests/fuzzing.test.d.ts.map +1 -0
- package/dist/tests/fuzzing.test.js +596 -0
- package/dist/tests/fuzzing.test.js.map +1 -0
- package/dist/tests/knowledge.test.d.ts +2 -0
- package/dist/tests/knowledge.test.d.ts.map +1 -0
- package/dist/tests/knowledge.test.js +292 -0
- package/dist/tests/knowledge.test.js.map +1 -0
- package/dist/tests/management.test.d.ts +2 -0
- package/dist/tests/management.test.d.ts.map +1 -0
- package/dist/tests/management.test.js +338 -0
- package/dist/tests/management.test.js.map +1 -0
- package/dist/tests/scaffolding.test.d.ts +2 -0
- package/dist/tests/scaffolding.test.d.ts.map +1 -0
- package/dist/tests/scaffolding.test.js +419 -0
- package/dist/tests/scaffolding.test.js.map +1 -0
- package/dist/tests/test-utils.d.ts +76 -0
- package/dist/tests/test-utils.d.ts.map +1 -0
- package/dist/tests/test-utils.js +171 -0
- package/dist/tests/test-utils.js.map +1 -0
- package/dist/tests/tool-harness.d.ts +18 -0
- package/dist/tests/tool-harness.d.ts.map +1 -0
- package/dist/tests/tool-harness.js +51 -0
- package/dist/tests/tool-harness.js.map +1 -0
- package/dist/tools/enterprise.d.ts +8 -0
- package/dist/tools/enterprise.d.ts.map +1 -0
- package/dist/tools/enterprise.js +571 -0
- package/dist/tools/enterprise.js.map +1 -0
- package/dist/tools/knowledge.d.ts +7 -0
- package/dist/tools/knowledge.d.ts.map +1 -0
- package/dist/tools/knowledge.js +120 -0
- package/dist/tools/knowledge.js.map +1 -0
- package/dist/tools/management.d.ts +10 -0
- package/dist/tools/management.d.ts.map +1 -0
- package/dist/tools/management.js +1541 -0
- package/dist/tools/management.js.map +1 -0
- package/dist/tools/scaffolding.d.ts +8 -0
- package/dist/tools/scaffolding.d.ts.map +1 -0
- package/dist/tools/scaffolding.js +736 -0
- package/dist/tools/scaffolding.js.map +1 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/landing/app/layout.tsx +30 -0
- package/landing/app/page.tsx +944 -0
- package/landing/next-env.d.ts +6 -0
- package/landing/next.config.js +6 -0
- package/landing/package-lock.json +896 -0
- package/landing/package.json +20 -0
- package/landing/tsconfig.json +40 -0
- package/package.json +49 -0
- package/rebar-v2.0.0-platform-spec.md +1567 -0
- package/server.json +20 -0
- package/src/cli.ts +735 -0
- package/src/constants.ts +131 -0
- package/src/index.ts +54 -0
- package/src/schemas/common.ts +22 -0
- package/src/schemas/scaffolding.ts +161 -0
- package/src/services/claudemd-generator.ts +481 -0
- package/src/services/codex-generator.ts +44 -0
- package/src/services/cursor-generator.ts +153 -0
- package/src/services/file-ops.ts +172 -0
- package/src/services/platform-detect.ts +80 -0
- package/src/services/project-analyzer.ts +690 -0
- package/src/services/rules-engine.ts +353 -0
- package/src/services/strictness.ts +202 -0
- package/src/services/template-engine.ts +119 -0
- package/src/services/validation.ts +138 -0
- package/src/services/windsurf-generator.ts +145 -0
- package/src/tests/enforcement.test.ts +794 -0
- package/src/tests/enterprise.test.ts +483 -0
- package/src/tests/fuzzing.test.ts +690 -0
- package/src/tests/knowledge.test.ts +371 -0
- package/src/tests/management.test.ts +451 -0
- package/src/tests/scaffolding.test.ts +575 -0
- package/src/tests/test-utils.ts +206 -0
- package/src/tests/tool-harness.ts +70 -0
- package/src/tools/enterprise.ts +666 -0
- package/src/tools/knowledge.ts +162 -0
- package/src/tools/management.ts +1706 -0
- package/src/tools/scaffolding.ts +909 -0
- package/src/types.ts +93 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/templates/agents/explore.md +41 -0
- package/templates/agents/plan.md +73 -0
- package/templates/agents/security-auditor.md +77 -0
- package/templates/agents/test-runner.md +60 -0
- package/templates/claudemd/fastapi.md +49 -0
- package/templates/claudemd/monorepo.md +48 -0
- package/templates/claudemd/nextjs.md +52 -0
- package/templates/claudemd/react-spa.md +50 -0
- package/templates/claudemd/springboot.md +50 -0
- package/templates/hooks/danger-blocker.json +11 -0
- package/templates/hooks/format-on-write.json +17 -0
- package/templates/hooks/lint-on-write.json +16 -0
- package/templates/hooks/secret-detector.json +11 -0
- package/templates/skills/code-review.md +68 -0
- package/templates/skills/documentation.md +62 -0
- package/templates/skills/performance-audit.md +80 -0
- package/templates/skills/security-scan.md +66 -0
- package/templates/skills/test-writer.md +56 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for scaffolding tools
|
|
3
|
+
* rebar_init_project, rebar_generate_claudemd, rebar_create_skill,
|
|
4
|
+
* rebar_create_agent, rebar_create_hook, rebar_create_command
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, before, after } from "node:test";
|
|
7
|
+
import * as assert from "node:assert";
|
|
8
|
+
import {
|
|
9
|
+
createTestDir,
|
|
10
|
+
createMockProject,
|
|
11
|
+
readTestFile,
|
|
12
|
+
testFileExists,
|
|
13
|
+
readTestJson,
|
|
14
|
+
assertToolSuccess,
|
|
15
|
+
assertToolError,
|
|
16
|
+
getToolResultText,
|
|
17
|
+
parseToolResultJson,
|
|
18
|
+
} from "./test-utils.js";
|
|
19
|
+
import { initializeTools, invokeTool } from "./tool-harness.js";
|
|
20
|
+
|
|
21
|
+
// Initialize tools before all tests
|
|
22
|
+
before(() => {
|
|
23
|
+
initializeTools();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("rebar_init_project", () => {
|
|
27
|
+
it("should initialize a new project with all required files", async () => {
|
|
28
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
await createMockProject(testDir);
|
|
32
|
+
|
|
33
|
+
const result = await invokeTool("rebar_init_project", {
|
|
34
|
+
project_path: testDir,
|
|
35
|
+
tech_stack: ["express"],
|
|
36
|
+
team_size: 5,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
assertToolSuccess(result);
|
|
40
|
+
|
|
41
|
+
// Check CLAUDE.md was created
|
|
42
|
+
assert.ok(
|
|
43
|
+
await testFileExists(testDir, "CLAUDE.md"),
|
|
44
|
+
"CLAUDE.md should be created"
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Check .claudeignore was created
|
|
48
|
+
assert.ok(
|
|
49
|
+
await testFileExists(testDir, ".claudeignore"),
|
|
50
|
+
".claudeignore should be created"
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Check .claude/settings.json was created
|
|
54
|
+
assert.ok(
|
|
55
|
+
await testFileExists(testDir, ".claude/settings.json"),
|
|
56
|
+
".claude/settings.json should be created"
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Check .mcp.json was created
|
|
60
|
+
assert.ok(
|
|
61
|
+
await testFileExists(testDir, ".mcp.json"),
|
|
62
|
+
".mcp.json should be created"
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Verify settings.json structure
|
|
66
|
+
const settings = await readTestJson<Record<string, unknown>>(
|
|
67
|
+
testDir,
|
|
68
|
+
".claude/settings.json"
|
|
69
|
+
);
|
|
70
|
+
assert.ok(settings, "settings.json should be valid JSON");
|
|
71
|
+
assert.ok(settings.permissions, "settings should have permissions");
|
|
72
|
+
} finally {
|
|
73
|
+
await cleanup();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should return JSON output when output_format is json", async () => {
|
|
78
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await createMockProject(testDir);
|
|
82
|
+
|
|
83
|
+
const result = await invokeTool("rebar_init_project", {
|
|
84
|
+
project_path: testDir,
|
|
85
|
+
tech_stack: ["express"],
|
|
86
|
+
team_size: 5,
|
|
87
|
+
output_format: "json",
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
assertToolSuccess(result);
|
|
91
|
+
|
|
92
|
+
const parsed = parseToolResultJson<Record<string, unknown>>(result);
|
|
93
|
+
assert.strictEqual(parsed.success, true);
|
|
94
|
+
assert.strictEqual(parsed.operation, "init_project");
|
|
95
|
+
assert.ok(parsed.data, "JSON should have data field");
|
|
96
|
+
} finally {
|
|
97
|
+
await cleanup();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should support dry_run mode", async () => {
|
|
102
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
await createMockProject(testDir);
|
|
106
|
+
|
|
107
|
+
const result = await invokeTool("rebar_init_project", {
|
|
108
|
+
project_path: testDir,
|
|
109
|
+
tech_stack: ["express"],
|
|
110
|
+
team_size: 5,
|
|
111
|
+
dry_run: true,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
assertToolSuccess(result);
|
|
115
|
+
|
|
116
|
+
const text = getToolResultText(result);
|
|
117
|
+
assert.ok(text.includes("DRY RUN"), "Should indicate dry run");
|
|
118
|
+
|
|
119
|
+
// CLAUDE.md should NOT be created in dry-run mode
|
|
120
|
+
assert.ok(
|
|
121
|
+
!(await testFileExists(testDir, "CLAUDE.md")),
|
|
122
|
+
"CLAUDE.md should NOT be created in dry-run mode"
|
|
123
|
+
);
|
|
124
|
+
} finally {
|
|
125
|
+
await cleanup();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("rebar_generate_claudemd", () => {
|
|
131
|
+
it("should generate CLAUDE.md from project analysis", async () => {
|
|
132
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
await createMockProject(testDir);
|
|
136
|
+
|
|
137
|
+
const result = await invokeTool("rebar_generate_claudemd", {
|
|
138
|
+
project_path: testDir,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
assertToolSuccess(result);
|
|
142
|
+
|
|
143
|
+
const claudemd = await readTestFile(testDir, "CLAUDE.md");
|
|
144
|
+
assert.ok(claudemd, "CLAUDE.md should be created");
|
|
145
|
+
assert.ok(claudemd.includes("#"), "Should have markdown headings");
|
|
146
|
+
} finally {
|
|
147
|
+
await cleanup();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should detect tech stack from package.json", async () => {
|
|
152
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
await createMockProject(testDir, {
|
|
156
|
+
packageJson: {
|
|
157
|
+
name: "nextjs-app",
|
|
158
|
+
dependencies: {
|
|
159
|
+
next: "^14.0.0",
|
|
160
|
+
react: "^18.0.0",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const result = await invokeTool("rebar_generate_claudemd", {
|
|
166
|
+
project_path: testDir,
|
|
167
|
+
output_format: "json",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
assertToolSuccess(result);
|
|
171
|
+
|
|
172
|
+
const parsed = parseToolResultJson<Record<string, unknown>>(result);
|
|
173
|
+
const data = parsed.data as Record<string, unknown>;
|
|
174
|
+
const analysis = data.analysis as Record<string, unknown>;
|
|
175
|
+
assert.ok(
|
|
176
|
+
(analysis.stacks as string[]).includes("nextjs") ||
|
|
177
|
+
(analysis.stacks as string[]).includes("react"),
|
|
178
|
+
"Should detect Next.js or React"
|
|
179
|
+
);
|
|
180
|
+
} finally {
|
|
181
|
+
await cleanup();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("should support dry_run mode", async () => {
|
|
186
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await createMockProject(testDir);
|
|
190
|
+
|
|
191
|
+
const result = await invokeTool("rebar_generate_claudemd", {
|
|
192
|
+
project_path: testDir,
|
|
193
|
+
dry_run: true,
|
|
194
|
+
output_format: "json",
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
assertToolSuccess(result);
|
|
198
|
+
|
|
199
|
+
const parsed = parseToolResultJson<Record<string, unknown>>(result);
|
|
200
|
+
assert.strictEqual(parsed.dry_run, true);
|
|
201
|
+
|
|
202
|
+
// File should not exist
|
|
203
|
+
assert.ok(
|
|
204
|
+
!(await testFileExists(testDir, "CLAUDE.md")),
|
|
205
|
+
"CLAUDE.md should NOT be created in dry-run"
|
|
206
|
+
);
|
|
207
|
+
} finally {
|
|
208
|
+
await cleanup();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("rebar_create_skill", () => {
|
|
214
|
+
it("should create a new skill directory with SKILL.md", async () => {
|
|
215
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
// Change to test directory for process.cwd() usage
|
|
219
|
+
const originalCwd = process.cwd();
|
|
220
|
+
process.chdir(testDir);
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
await createMockProject(testDir);
|
|
224
|
+
|
|
225
|
+
const result = await invokeTool("rebar_create_skill", {
|
|
226
|
+
name: "test-skill",
|
|
227
|
+
description: "A test skill for unit testing",
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
assertToolSuccess(result);
|
|
231
|
+
|
|
232
|
+
assert.ok(
|
|
233
|
+
await testFileExists(testDir, ".claude/skills/test-skill/SKILL.md"),
|
|
234
|
+
"SKILL.md should be created"
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const skillContent = await readTestFile(
|
|
238
|
+
testDir,
|
|
239
|
+
".claude/skills/test-skill/SKILL.md"
|
|
240
|
+
);
|
|
241
|
+
assert.ok(skillContent, "Skill file should have content");
|
|
242
|
+
assert.ok(skillContent.includes("name: test-skill"), "Should have name in frontmatter");
|
|
243
|
+
assert.ok(
|
|
244
|
+
skillContent.includes("A test skill for unit testing"),
|
|
245
|
+
"Should have description"
|
|
246
|
+
);
|
|
247
|
+
} finally {
|
|
248
|
+
process.chdir(originalCwd);
|
|
249
|
+
}
|
|
250
|
+
} finally {
|
|
251
|
+
await cleanup();
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should fail if skill already exists", async () => {
|
|
256
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const originalCwd = process.cwd();
|
|
260
|
+
process.chdir(testDir);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
await createMockProject(testDir);
|
|
264
|
+
|
|
265
|
+
// Create first skill
|
|
266
|
+
await invokeTool("rebar_create_skill", {
|
|
267
|
+
name: "existing-skill",
|
|
268
|
+
description: "First skill",
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Try to create again
|
|
272
|
+
const result = await invokeTool("rebar_create_skill", {
|
|
273
|
+
name: "existing-skill",
|
|
274
|
+
description: "Duplicate skill",
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
assertToolError(result);
|
|
278
|
+
const text = getToolResultText(result);
|
|
279
|
+
assert.ok(text.includes("already exists"), "Should mention already exists");
|
|
280
|
+
} finally {
|
|
281
|
+
process.chdir(originalCwd);
|
|
282
|
+
}
|
|
283
|
+
} finally {
|
|
284
|
+
await cleanup();
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("should support dry_run mode", async () => {
|
|
289
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const originalCwd = process.cwd();
|
|
293
|
+
process.chdir(testDir);
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
await createMockProject(testDir);
|
|
297
|
+
|
|
298
|
+
const result = await invokeTool("rebar_create_skill", {
|
|
299
|
+
name: "dry-run-skill",
|
|
300
|
+
description: "Should not be created",
|
|
301
|
+
dry_run: true,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
assertToolSuccess(result);
|
|
305
|
+
|
|
306
|
+
assert.ok(
|
|
307
|
+
!(await testFileExists(testDir, ".claude/skills/dry-run-skill/SKILL.md")),
|
|
308
|
+
"Skill should NOT be created in dry-run"
|
|
309
|
+
);
|
|
310
|
+
} finally {
|
|
311
|
+
process.chdir(originalCwd);
|
|
312
|
+
}
|
|
313
|
+
} finally {
|
|
314
|
+
await cleanup();
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe("rebar_create_agent", () => {
|
|
320
|
+
it("should create a new agent file", async () => {
|
|
321
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
const originalCwd = process.cwd();
|
|
325
|
+
process.chdir(testDir);
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
await createMockProject(testDir);
|
|
329
|
+
|
|
330
|
+
const result = await invokeTool("rebar_create_agent", {
|
|
331
|
+
name: "test-agent",
|
|
332
|
+
description: "A test agent for unit testing",
|
|
333
|
+
role: "explore",
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
assertToolSuccess(result);
|
|
337
|
+
|
|
338
|
+
assert.ok(
|
|
339
|
+
await testFileExists(testDir, ".claude/agents/test-agent.md"),
|
|
340
|
+
"Agent file should be created"
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const agentContent = await readTestFile(
|
|
344
|
+
testDir,
|
|
345
|
+
".claude/agents/test-agent.md"
|
|
346
|
+
);
|
|
347
|
+
assert.ok(agentContent, "Agent file should have content");
|
|
348
|
+
assert.ok(agentContent.includes("test-agent"), "Should include agent name");
|
|
349
|
+
} finally {
|
|
350
|
+
process.chdir(originalCwd);
|
|
351
|
+
}
|
|
352
|
+
} finally {
|
|
353
|
+
await cleanup();
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("should fail if agent already exists", async () => {
|
|
358
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
const originalCwd = process.cwd();
|
|
362
|
+
process.chdir(testDir);
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
await createMockProject(testDir);
|
|
366
|
+
|
|
367
|
+
// Create first agent
|
|
368
|
+
await invokeTool("rebar_create_agent", {
|
|
369
|
+
name: "existing-agent",
|
|
370
|
+
description: "First agent",
|
|
371
|
+
role: "explore",
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Try to create again
|
|
375
|
+
const result = await invokeTool("rebar_create_agent", {
|
|
376
|
+
name: "existing-agent",
|
|
377
|
+
description: "Duplicate agent",
|
|
378
|
+
role: "plan",
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
assertToolError(result);
|
|
382
|
+
} finally {
|
|
383
|
+
process.chdir(originalCwd);
|
|
384
|
+
}
|
|
385
|
+
} finally {
|
|
386
|
+
await cleanup();
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
describe("rebar_create_hook", () => {
|
|
392
|
+
it("should add a hook to settings.json", async () => {
|
|
393
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const originalCwd = process.cwd();
|
|
397
|
+
process.chdir(testDir);
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
await createMockProject(testDir);
|
|
401
|
+
|
|
402
|
+
const result = await invokeTool("rebar_create_hook", {
|
|
403
|
+
event: "PreToolCall",
|
|
404
|
+
command: "echo test",
|
|
405
|
+
description: "Test hook",
|
|
406
|
+
exit_behavior: "notify",
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
assertToolSuccess(result);
|
|
410
|
+
|
|
411
|
+
const settings = await readTestJson<Record<string, unknown>>(
|
|
412
|
+
testDir,
|
|
413
|
+
".claude/settings.json"
|
|
414
|
+
);
|
|
415
|
+
assert.ok(settings, "settings.json should exist");
|
|
416
|
+
assert.ok(settings.hooks, "Should have hooks");
|
|
417
|
+
|
|
418
|
+
const hooks = settings.hooks as Record<string, unknown[]>;
|
|
419
|
+
assert.ok(hooks.PreToolCall, "Should have PreToolCall hooks");
|
|
420
|
+
assert.ok(
|
|
421
|
+
hooks.PreToolCall.some(
|
|
422
|
+
(h: unknown) => (h as Record<string, unknown>).command === "echo test"
|
|
423
|
+
),
|
|
424
|
+
"Should contain the new hook"
|
|
425
|
+
);
|
|
426
|
+
} finally {
|
|
427
|
+
process.chdir(originalCwd);
|
|
428
|
+
}
|
|
429
|
+
} finally {
|
|
430
|
+
await cleanup();
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("should update existing hook with same command and matcher", async () => {
|
|
435
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const originalCwd = process.cwd();
|
|
439
|
+
process.chdir(testDir);
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
await createMockProject(testDir);
|
|
443
|
+
|
|
444
|
+
// Create first hook
|
|
445
|
+
await invokeTool("rebar_create_hook", {
|
|
446
|
+
event: "PreToolCall",
|
|
447
|
+
command: "echo test",
|
|
448
|
+
matcher: "Bash",
|
|
449
|
+
description: "First description",
|
|
450
|
+
exit_behavior: "notify",
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Update with same command and matcher but different description and exit_behavior
|
|
454
|
+
const result = await invokeTool("rebar_create_hook", {
|
|
455
|
+
event: "PreToolCall",
|
|
456
|
+
command: "echo test",
|
|
457
|
+
matcher: "Bash",
|
|
458
|
+
description: "Updated description",
|
|
459
|
+
exit_behavior: "block",
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
assertToolSuccess(result);
|
|
463
|
+
|
|
464
|
+
const text = getToolResultText(result);
|
|
465
|
+
assert.ok(text.includes("updated"), "Should indicate update");
|
|
466
|
+
|
|
467
|
+
const settings = await readTestJson<Record<string, unknown>>(
|
|
468
|
+
testDir,
|
|
469
|
+
".claude/settings.json"
|
|
470
|
+
);
|
|
471
|
+
const hooks = settings?.hooks as Record<string, unknown[]>;
|
|
472
|
+
const preToolCallHooks = hooks?.PreToolCall ?? [];
|
|
473
|
+
|
|
474
|
+
// Should only have one hook with that command
|
|
475
|
+
const matchingHooks = preToolCallHooks.filter(
|
|
476
|
+
(h: unknown) =>
|
|
477
|
+
(h as Record<string, unknown>).command === "echo test"
|
|
478
|
+
);
|
|
479
|
+
assert.strictEqual(matchingHooks.length, 1, "Should have exactly one hook");
|
|
480
|
+
assert.strictEqual(
|
|
481
|
+
(matchingHooks[0] as Record<string, unknown>).description,
|
|
482
|
+
"Updated description",
|
|
483
|
+
"Hook description should be updated"
|
|
484
|
+
);
|
|
485
|
+
assert.strictEqual(
|
|
486
|
+
(matchingHooks[0] as Record<string, unknown>).exit_behavior,
|
|
487
|
+
"block",
|
|
488
|
+
"Hook exit_behavior should be updated"
|
|
489
|
+
);
|
|
490
|
+
} finally {
|
|
491
|
+
process.chdir(originalCwd);
|
|
492
|
+
}
|
|
493
|
+
} finally {
|
|
494
|
+
await cleanup();
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
describe("rebar_create_command", () => {
|
|
500
|
+
it("should create a command skill", async () => {
|
|
501
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
const originalCwd = process.cwd();
|
|
505
|
+
process.chdir(testDir);
|
|
506
|
+
|
|
507
|
+
try {
|
|
508
|
+
await createMockProject(testDir);
|
|
509
|
+
|
|
510
|
+
const result = await invokeTool("rebar_create_command", {
|
|
511
|
+
name: "test-cmd",
|
|
512
|
+
description: "A test command",
|
|
513
|
+
prompt_body: "Do something useful",
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
assertToolSuccess(result);
|
|
517
|
+
|
|
518
|
+
assert.ok(
|
|
519
|
+
await testFileExists(testDir, ".claude/skills/test-cmd/SKILL.md"),
|
|
520
|
+
"Command skill should be created"
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
const skillContent = await readTestFile(
|
|
524
|
+
testDir,
|
|
525
|
+
".claude/skills/test-cmd/SKILL.md"
|
|
526
|
+
);
|
|
527
|
+
assert.ok(skillContent, "Skill file should have content");
|
|
528
|
+
assert.ok(skillContent.includes("invocation: user"), "Should be user-invoked");
|
|
529
|
+
assert.ok(skillContent.includes("Do something useful"), "Should include prompt");
|
|
530
|
+
} finally {
|
|
531
|
+
process.chdir(originalCwd);
|
|
532
|
+
}
|
|
533
|
+
} finally {
|
|
534
|
+
await cleanup();
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("should include arguments in command", async () => {
|
|
539
|
+
const { path: testDir, cleanup } = await createTestDir();
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
const originalCwd = process.cwd();
|
|
543
|
+
process.chdir(testDir);
|
|
544
|
+
|
|
545
|
+
try {
|
|
546
|
+
await createMockProject(testDir);
|
|
547
|
+
|
|
548
|
+
const result = await invokeTool("rebar_create_command", {
|
|
549
|
+
name: "cmd-with-args",
|
|
550
|
+
description: "Command with arguments",
|
|
551
|
+
prompt_body: "Process the input",
|
|
552
|
+
arguments: [
|
|
553
|
+
{ name: "target", description: "Target to process", required: true },
|
|
554
|
+
{ name: "mode", description: "Processing mode", required: false },
|
|
555
|
+
],
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
assertToolSuccess(result);
|
|
559
|
+
|
|
560
|
+
const skillContent = await readTestFile(
|
|
561
|
+
testDir,
|
|
562
|
+
".claude/skills/cmd-with-args/SKILL.md"
|
|
563
|
+
);
|
|
564
|
+
assert.ok(skillContent, "Skill file should have content");
|
|
565
|
+
assert.ok(skillContent.includes("**target**"), "Should include target arg");
|
|
566
|
+
assert.ok(skillContent.includes("(required)"), "Should mark required");
|
|
567
|
+
assert.ok(skillContent.includes("(optional)"), "Should mark optional");
|
|
568
|
+
} finally {
|
|
569
|
+
process.chdir(originalCwd);
|
|
570
|
+
}
|
|
571
|
+
} finally {
|
|
572
|
+
await cleanup();
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
});
|