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