@schilling.mark.a/atdd-guardian 1.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/CHANGELOG.md +37 -0
- package/CONTRIBUTING.md +154 -0
- package/DISTRIBUTION.md +366 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +194 -0
- package/README.md +406 -0
- package/SHARING.md +223 -0
- package/dist/constants.d.ts +19 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +38 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/tool-schemas.d.ts +404 -0
- package/dist/schemas/tool-schemas.d.ts.map +1 -0
- package/dist/schemas/tool-schemas.js +267 -0
- package/dist/schemas/tool-schemas.js.map +1 -0
- package/dist/services/context-review.d.ts +65 -0
- package/dist/services/context-review.d.ts.map +1 -0
- package/dist/services/context-review.js +300 -0
- package/dist/services/context-review.js.map +1 -0
- package/dist/services/formatter.d.ts +10 -0
- package/dist/services/formatter.d.ts.map +1 -0
- package/dist/services/formatter.js +173 -0
- package/dist/services/formatter.js.map +1 -0
- package/dist/services/phase-gate.d.ts +38 -0
- package/dist/services/phase-gate.d.ts.map +1 -0
- package/dist/services/phase-gate.js +309 -0
- package/dist/services/phase-gate.js.map +1 -0
- package/dist/services/review-engine.d.ts +26 -0
- package/dist/services/review-engine.d.ts.map +1 -0
- package/dist/services/review-engine.js +235 -0
- package/dist/services/review-engine.js.map +1 -0
- package/dist/services/rule-loader.d.ts +19 -0
- package/dist/services/rule-loader.d.ts.map +1 -0
- package/dist/services/rule-loader.js +91 -0
- package/dist/services/rule-loader.js.map +1 -0
- package/dist/services/session-manager.d.ts +75 -0
- package/dist/services/session-manager.d.ts.map +1 -0
- package/dist/services/session-manager.js +244 -0
- package/dist/services/session-manager.js.map +1 -0
- package/dist/services/test-runner.d.ts +53 -0
- package/dist/services/test-runner.d.ts.map +1 -0
- package/dist/services/test-runner.js +199 -0
- package/dist/services/test-runner.js.map +1 -0
- package/dist/tools/review-tools.d.ts +26 -0
- package/dist/tools/review-tools.d.ts.map +1 -0
- package/dist/tools/review-tools.js +904 -0
- package/dist/tools/review-tools.js.map +1 -0
- package/dist/types.d.ts +339 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/examples/README.md +119 -0
- package/examples/mcp-config.json +11 -0
- package/examples/test-config.json +7 -0
- package/package.json +48 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase Gate Enforcement Service
|
|
3
|
+
*
|
|
4
|
+
* Implements the process constraints from the ATDD requirements:
|
|
5
|
+
* PC-1: No production code without a failing unit test
|
|
6
|
+
* PC-2: Minimal implementation principle
|
|
7
|
+
* PC-3: Test independence
|
|
8
|
+
*
|
|
9
|
+
* The key idea: advance_phase is no longer a free pass.
|
|
10
|
+
* The system validates that the ATDD discipline was followed
|
|
11
|
+
* before allowing phase transitions. Violations are either
|
|
12
|
+
* blocked (hard gate) or warned (soft gate), depending on
|
|
13
|
+
* severity and team configuration.
|
|
14
|
+
*
|
|
15
|
+
* Gate strictness can be configured:
|
|
16
|
+
* "strict" — blocks on any violation
|
|
17
|
+
* "guided" — blocks on errors, warns on warnings
|
|
18
|
+
* "lenient" — warns on everything, never blocks
|
|
19
|
+
*/
|
|
20
|
+
// ─── Valid Phase Transitions ─────────────────────────────────────
|
|
21
|
+
const VALID_TRANSITIONS = {
|
|
22
|
+
requirements: ["test-design"],
|
|
23
|
+
"test-design": ["implementation"],
|
|
24
|
+
implementation: ["refactor"],
|
|
25
|
+
refactor: ["pre-pr-review", "implementation"], // back to impl if more ACs
|
|
26
|
+
"pre-pr-review": ["pr-submitted", "refactor"], // back to refactor if issues
|
|
27
|
+
"pr-submitted": ["pr-feedback", "done"],
|
|
28
|
+
"pr-feedback": ["refactor", "done"], // back to refactor to fix
|
|
29
|
+
done: [],
|
|
30
|
+
};
|
|
31
|
+
// ─── Main Gate Check ─────────────────────────────────────────────
|
|
32
|
+
/**
|
|
33
|
+
* Validate whether a phase transition is allowed.
|
|
34
|
+
* Checks TDD discipline, test state, and workflow sequence.
|
|
35
|
+
*/
|
|
36
|
+
export function checkPhaseGate(session, targetPhase, strictness = "guided") {
|
|
37
|
+
const feature = session.activeFeature;
|
|
38
|
+
if (!feature) {
|
|
39
|
+
return {
|
|
40
|
+
allowed: false,
|
|
41
|
+
violations: [{
|
|
42
|
+
type: "phase-skipped",
|
|
43
|
+
message: "No active feature. Use start_feature first.",
|
|
44
|
+
guidance: "Load a story context before advancing phases.",
|
|
45
|
+
level: "block",
|
|
46
|
+
}],
|
|
47
|
+
summary: "No active feature loaded.",
|
|
48
|
+
nextSteps: ["Call start_feature with your story context."],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const currentPhase = feature.currentPhase;
|
|
52
|
+
const violations = [];
|
|
53
|
+
// 1. Check valid transition path
|
|
54
|
+
const validTargets = VALID_TRANSITIONS[currentPhase] ?? [];
|
|
55
|
+
if (!validTargets.includes(targetPhase)) {
|
|
56
|
+
violations.push({
|
|
57
|
+
type: "phase-skipped",
|
|
58
|
+
message: `Cannot go from '${currentPhase}' to '${targetPhase}'. Valid transitions: ${validTargets.join(", ") || "none"}.`,
|
|
59
|
+
guidance: `Follow the ATDD sequence. From '${currentPhase}', you can go to: ${validTargets.join(", ")}.`,
|
|
60
|
+
level: "block",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// 2. Phase-specific gates
|
|
64
|
+
switch (targetPhase) {
|
|
65
|
+
case "implementation":
|
|
66
|
+
violations.push(...checkTestDesignToImplementation(feature));
|
|
67
|
+
break;
|
|
68
|
+
case "refactor":
|
|
69
|
+
violations.push(...checkImplementationToRefactor(feature, session));
|
|
70
|
+
break;
|
|
71
|
+
case "pre-pr-review":
|
|
72
|
+
violations.push(...checkRefactorToPrePr(feature, session));
|
|
73
|
+
break;
|
|
74
|
+
case "pr-submitted":
|
|
75
|
+
violations.push(...checkPrePrToSubmit(feature, session));
|
|
76
|
+
break;
|
|
77
|
+
case "done":
|
|
78
|
+
violations.push(...checkDone(feature));
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
// 3. Determine if allowed based on strictness
|
|
82
|
+
const blockers = violations.filter((v) => v.level === "block");
|
|
83
|
+
const warnings = violations.filter((v) => v.level === "warn");
|
|
84
|
+
let allowed;
|
|
85
|
+
switch (strictness) {
|
|
86
|
+
case "strict":
|
|
87
|
+
allowed = violations.length === 0;
|
|
88
|
+
break;
|
|
89
|
+
case "guided":
|
|
90
|
+
allowed = blockers.length === 0;
|
|
91
|
+
break;
|
|
92
|
+
case "lenient":
|
|
93
|
+
allowed = true;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
// 4. Build summary
|
|
97
|
+
const summary = violations.length === 0
|
|
98
|
+
? `✅ Phase gate passed. Ready to advance to '${targetPhase}'.`
|
|
99
|
+
: `${blockers.length > 0 ? "❌ BLOCKED" : "⚠️ WARNINGS"}: ` +
|
|
100
|
+
`${blockers.length} blockers, ${warnings.length} warnings for transition to '${targetPhase}'.`;
|
|
101
|
+
const nextSteps = violations.map((v) => v.guidance);
|
|
102
|
+
return { allowed, violations, summary, nextSteps };
|
|
103
|
+
}
|
|
104
|
+
// ─── Gate: test-design → implementation ──────────────────────────
|
|
105
|
+
/**
|
|
106
|
+
* Before entering implementation:
|
|
107
|
+
* - Every AC must have at least one acceptance test (status = "red")
|
|
108
|
+
* - No AC should still be "no-test"
|
|
109
|
+
*/
|
|
110
|
+
function checkTestDesignToImplementation(feature) {
|
|
111
|
+
const violations = [];
|
|
112
|
+
const noTest = feature.acceptanceCriteria.filter((ac) => ac.testStatus === "no-test");
|
|
113
|
+
if (noTest.length > 0) {
|
|
114
|
+
violations.push({
|
|
115
|
+
type: "no-red-test",
|
|
116
|
+
message: `${noTest.length} acceptance criteria have no test: ${noTest.map((ac) => ac.id).join(", ")}`,
|
|
117
|
+
guidance: "Write a failing Playwright test for each AC before implementing. " +
|
|
118
|
+
"Use update_criterion to mark them as 'red' once tests are created.",
|
|
119
|
+
level: "block",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const notRed = feature.acceptanceCriteria.filter((ac) => ac.testStatus !== "red" && ac.testStatus !== "no-test");
|
|
123
|
+
// ACs that are already green before implementation haven't gone through red
|
|
124
|
+
const alreadyGreen = feature.acceptanceCriteria.filter((ac) => ac.testStatus === "green");
|
|
125
|
+
if (alreadyGreen.length > 0) {
|
|
126
|
+
violations.push({
|
|
127
|
+
type: "skipped-red",
|
|
128
|
+
message: `${alreadyGreen.length} ACs are already green before implementation: ${alreadyGreen.map((ac) => ac.id).join(", ")}. Tests should be RED at this stage.`,
|
|
129
|
+
guidance: "Acceptance tests should fail (red) before you start implementation. " +
|
|
130
|
+
"If they pass without implementation, the test may not be testing the right thing.",
|
|
131
|
+
level: "warn",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return violations;
|
|
135
|
+
}
|
|
136
|
+
// ─── Gate: implementation → refactor ─────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* Before entering refactor:
|
|
139
|
+
* - All ACs should have green acceptance tests
|
|
140
|
+
* - Each AC that went red→green should have unit TDD cycles
|
|
141
|
+
* - No incomplete unit cycles (stuck in red without green)
|
|
142
|
+
*/
|
|
143
|
+
function checkImplementationToRefactor(feature, session) {
|
|
144
|
+
const violations = [];
|
|
145
|
+
// Check all ACs are green
|
|
146
|
+
const notGreen = feature.acceptanceCriteria.filter((ac) => ac.testStatus !== "green" && ac.testStatus !== "skipped");
|
|
147
|
+
if (notGreen.length > 0) {
|
|
148
|
+
violations.push({
|
|
149
|
+
type: "ac-still-red",
|
|
150
|
+
message: `${notGreen.length} acceptance criteria are not green: ${notGreen.map((ac) => `${ac.id}[${ac.testStatus}]`).join(", ")}`,
|
|
151
|
+
guidance: "All acceptance tests must pass before refactoring. " +
|
|
152
|
+
"Continue implementation until all ACs are green.",
|
|
153
|
+
level: "block",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Check that unit TDD cycles were used (PC-1 enforcement)
|
|
157
|
+
const greenWithoutUnits = feature.acceptanceCriteria.filter((ac) => ac.testStatus === "green" && ac.unitCycles.length === 0);
|
|
158
|
+
if (greenWithoutUnits.length > 0) {
|
|
159
|
+
violations.push({
|
|
160
|
+
type: "no-unit-tests",
|
|
161
|
+
message: `${greenWithoutUnits.length} ACs went green without any unit TDD cycles: ${greenWithoutUnits.map((ac) => ac.id).join(", ")}`,
|
|
162
|
+
guidance: "PC-1: No production code without a failing unit test. " +
|
|
163
|
+
"Use start_unit_cycle and advance_unit_cycle to track the unit RED→GREEN→REFACTOR loops " +
|
|
164
|
+
"that drove each AC to green.",
|
|
165
|
+
level: "block",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// Check for incomplete unit cycles (stuck in red)
|
|
169
|
+
for (const ac of feature.acceptanceCriteria) {
|
|
170
|
+
const incomplete = ac.unitCycles.filter((uc) => uc.state === "red");
|
|
171
|
+
if (incomplete.length > 0) {
|
|
172
|
+
violations.push({
|
|
173
|
+
type: "incomplete-unit-cycle",
|
|
174
|
+
message: `AC ${ac.id} has ${incomplete.length} unit cycle(s) stuck in RED state (cycle ${incomplete.map((u) => u.cycleNumber).join(", ")})`,
|
|
175
|
+
guidance: "Each unit TDD cycle must complete RED→GREEN→REFACTOR. " +
|
|
176
|
+
"Either make the failing unit test pass, or abandon the cycle with a reason.",
|
|
177
|
+
level: "warn",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return violations;
|
|
182
|
+
}
|
|
183
|
+
// ─── Gate: refactor → pre-pr-review ──────────────────────────────
|
|
184
|
+
/**
|
|
185
|
+
* Before entering pre-PR review:
|
|
186
|
+
* - All ACs still green (refactoring didn't break anything)
|
|
187
|
+
* - Unit cycles should be in "refactored" state, not just "green"
|
|
188
|
+
* - Most recent test run should show all passing
|
|
189
|
+
*/
|
|
190
|
+
function checkRefactorToPrePr(feature, session) {
|
|
191
|
+
const violations = [];
|
|
192
|
+
// All ACs still green
|
|
193
|
+
const notGreen = feature.acceptanceCriteria.filter((ac) => ac.testStatus !== "green" && ac.testStatus !== "skipped");
|
|
194
|
+
if (notGreen.length > 0) {
|
|
195
|
+
violations.push({
|
|
196
|
+
type: "tests-regressed",
|
|
197
|
+
message: `Acceptance tests regressed during refactoring: ${notGreen.map((ac) => `${ac.id}[${ac.testStatus}]`).join(", ")}`,
|
|
198
|
+
guidance: "Refactoring must not break tests. Fix the regression before proceeding.",
|
|
199
|
+
level: "block",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Check that unit cycles went through refactor
|
|
203
|
+
let unrefactored = 0;
|
|
204
|
+
for (const ac of feature.acceptanceCriteria) {
|
|
205
|
+
const greenNotRefactored = ac.unitCycles.filter((uc) => uc.state === "green");
|
|
206
|
+
unrefactored += greenNotRefactored.length;
|
|
207
|
+
}
|
|
208
|
+
if (unrefactored > 0) {
|
|
209
|
+
violations.push({
|
|
210
|
+
type: "no-refactor",
|
|
211
|
+
message: `${unrefactored} unit cycle(s) completed GREEN but were not refactored.`,
|
|
212
|
+
guidance: "Each unit TDD cycle should include a REFACTOR step. " +
|
|
213
|
+
"Review the green code for duplication, naming, SOLID principles. " +
|
|
214
|
+
"Use advance_unit_cycle to mark cycles as refactored.",
|
|
215
|
+
level: "warn",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// Check most recent test run
|
|
219
|
+
const recentRun = session.testRunHistory[session.testRunHistory.length - 1];
|
|
220
|
+
if (!recentRun) {
|
|
221
|
+
violations.push({
|
|
222
|
+
type: "no-red-test",
|
|
223
|
+
message: "No test runs recorded. Run tests to verify everything passes.",
|
|
224
|
+
guidance: "Use run_tests to execute your test suite and capture results.",
|
|
225
|
+
level: "warn",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
else if (recentRun.failed > 0) {
|
|
229
|
+
violations.push({
|
|
230
|
+
type: "tests-regressed",
|
|
231
|
+
message: `Most recent test run had ${recentRun.failed} failures (${recentRun.timestamp}).`,
|
|
232
|
+
guidance: "Fix failing tests before submitting for review.",
|
|
233
|
+
level: "block",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return violations;
|
|
237
|
+
}
|
|
238
|
+
// ─── Gate: pre-pr-review → pr-submitted ──────────────────────────
|
|
239
|
+
function checkPrePrToSubmit(feature, session) {
|
|
240
|
+
const violations = [];
|
|
241
|
+
// Must have at least one passing review
|
|
242
|
+
const passingReview = session.reviewHistory.find((r) => r.phase === "pre-pr-review" && (r.verdict === "pass" || r.verdict === "warnings-only"));
|
|
243
|
+
if (!passingReview) {
|
|
244
|
+
violations.push({
|
|
245
|
+
type: "phase-skipped",
|
|
246
|
+
message: "No passing pre-PR review found. Run review_code first.",
|
|
247
|
+
guidance: "Run review_code and fix all errors before submitting the PR.",
|
|
248
|
+
level: "block",
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// DoD should be mostly complete
|
|
252
|
+
const incomplete = feature.definitionOfDone.filter((d) => !d.completed);
|
|
253
|
+
if (incomplete.length > 0) {
|
|
254
|
+
violations.push({
|
|
255
|
+
type: "phase-skipped",
|
|
256
|
+
message: `${incomplete.length} Definition of Done items incomplete: ${incomplete.map((d) => d.id).join(", ")}`,
|
|
257
|
+
guidance: "Complete all DoD items, or document why they're deferred.",
|
|
258
|
+
level: "warn",
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return violations;
|
|
262
|
+
}
|
|
263
|
+
// ─── Gate: → done ────────────────────────────────────────────────
|
|
264
|
+
function checkDone(feature) {
|
|
265
|
+
const violations = [];
|
|
266
|
+
const notGreen = feature.acceptanceCriteria.filter((ac) => ac.testStatus !== "green" && ac.testStatus !== "skipped");
|
|
267
|
+
if (notGreen.length > 0) {
|
|
268
|
+
violations.push({
|
|
269
|
+
type: "ac-still-red",
|
|
270
|
+
message: `Cannot mark as done — ${notGreen.length} ACs are not green.`,
|
|
271
|
+
guidance: "All acceptance criteria must have passing tests to be considered done.",
|
|
272
|
+
level: "block",
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return violations;
|
|
276
|
+
}
|
|
277
|
+
// ─── Format Gate Result ──────────────────────────────────────────
|
|
278
|
+
export function formatGateResult(result) {
|
|
279
|
+
const lines = [result.summary, ""];
|
|
280
|
+
if (result.violations.length === 0) {
|
|
281
|
+
return result.summary;
|
|
282
|
+
}
|
|
283
|
+
const blockers = result.violations.filter((v) => v.level === "block");
|
|
284
|
+
const warnings = result.violations.filter((v) => v.level === "warn");
|
|
285
|
+
if (blockers.length > 0) {
|
|
286
|
+
lines.push("## 🚫 Blockers (must resolve)");
|
|
287
|
+
for (const v of blockers) {
|
|
288
|
+
lines.push(`- **${v.type}**: ${v.message}`);
|
|
289
|
+
lines.push(` → ${v.guidance}`);
|
|
290
|
+
}
|
|
291
|
+
lines.push("");
|
|
292
|
+
}
|
|
293
|
+
if (warnings.length > 0) {
|
|
294
|
+
lines.push("## ⚠️ Warnings (should resolve)");
|
|
295
|
+
for (const v of warnings) {
|
|
296
|
+
lines.push(`- **${v.type}**: ${v.message}`);
|
|
297
|
+
lines.push(` → ${v.guidance}`);
|
|
298
|
+
}
|
|
299
|
+
lines.push("");
|
|
300
|
+
}
|
|
301
|
+
if (result.nextSteps.length > 0) {
|
|
302
|
+
lines.push("## Next Steps");
|
|
303
|
+
result.nextSteps.forEach((step, i) => {
|
|
304
|
+
lines.push(`${i + 1}. ${step}`);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return lines.join("\n");
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=phase-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-gate.js","sourceRoot":"","sources":["../../src/services/phase-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review Engine Service
|
|
3
|
+
*
|
|
4
|
+
* Core logic for applying review rules against source files.
|
|
5
|
+
* Separated from MCP tool definitions so it can be tested
|
|
6
|
+
* independently and reused across tools.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReviewFinding, ReviewSummary, RuleSet, Severity, AtddPhase } from "../types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Recursively find all reviewable files under a root directory.
|
|
11
|
+
* Respects default excludes and reviewable extensions.
|
|
12
|
+
*/
|
|
13
|
+
export declare function discoverFiles(rootDir: string, additionalExcludes?: string[]): Promise<string[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Run a full review of a directory against a rule set.
|
|
16
|
+
*/
|
|
17
|
+
export declare function reviewDirectory(rootDir: string, ruleSet: RuleSet, options?: {
|
|
18
|
+
excludePatterns?: string[];
|
|
19
|
+
includePatterns?: string[];
|
|
20
|
+
severityFilter?: Severity[];
|
|
21
|
+
categoryFilter?: string[];
|
|
22
|
+
/** Only apply rules that match this ATDD phase */
|
|
23
|
+
phase?: AtddPhase;
|
|
24
|
+
}): Promise<ReviewSummary>;
|
|
25
|
+
export declare function reviewSingleFile(filePath: string, rootDir: string, ruleSet: RuleSet): Promise<ReviewFinding[]>;
|
|
26
|
+
//# sourceMappingURL=review-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-engine.d.ts","sourceRoot":"","sources":["../../src/services/review-engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAEV,aAAa,EACb,aAAa,EACb,OAAO,EACP,QAAQ,EACR,SAAS,EACV,MAAM,aAAa,CAAC;AAQrB;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,kBAAkB,GAAE,MAAM,EAAO,GAChC,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BnB;AAuGD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,kDAAkD;IAClD,KAAK,CAAC,EAAE,SAAS,CAAC;CACd,GACL,OAAO,CAAC,aAAa,CAAC,CA2HxB;AAID,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,EAAE,CAAC,CAc1B"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review Engine Service
|
|
3
|
+
*
|
|
4
|
+
* Core logic for applying review rules against source files.
|
|
5
|
+
* Separated from MCP tool definitions so it can be tested
|
|
6
|
+
* independently and reused across tools.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
9
|
+
import { join, relative, extname } from "node:path";
|
|
10
|
+
import { minimatch } from "minimatch";
|
|
11
|
+
import { DEFAULT_EXCLUDES, REVIEWABLE_EXTENSIONS, } from "../constants.js";
|
|
12
|
+
// ─── File Discovery ──────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Recursively find all reviewable files under a root directory.
|
|
15
|
+
* Respects default excludes and reviewable extensions.
|
|
16
|
+
*/
|
|
17
|
+
export async function discoverFiles(rootDir, additionalExcludes = []) {
|
|
18
|
+
const allExcludes = [...DEFAULT_EXCLUDES, ...additionalExcludes];
|
|
19
|
+
const results = [];
|
|
20
|
+
async function walk(dir) {
|
|
21
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const fullPath = join(dir, entry.name);
|
|
24
|
+
const relPath = relative(rootDir, fullPath);
|
|
25
|
+
// Check if path matches any exclude pattern
|
|
26
|
+
const isExcluded = allExcludes.some((pattern) => minimatch(relPath, pattern, { dot: true }));
|
|
27
|
+
if (isExcluded)
|
|
28
|
+
continue;
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
await walk(fullPath);
|
|
31
|
+
}
|
|
32
|
+
else if (REVIEWABLE_EXTENSIONS.includes(extname(entry.name))) {
|
|
33
|
+
results.push(fullPath);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
await walk(rootDir);
|
|
38
|
+
return results;
|
|
39
|
+
}
|
|
40
|
+
// ─── Rule Matching ───────────────────────────────────────────────
|
|
41
|
+
/**
|
|
42
|
+
* Check whether a rule applies to a given file path.
|
|
43
|
+
*/
|
|
44
|
+
function ruleAppliesToFile(rule, relPath) {
|
|
45
|
+
const matchesInclude = rule.appliesTo.some((pattern) => minimatch(relPath, pattern, { dot: true }));
|
|
46
|
+
if (!matchesInclude)
|
|
47
|
+
return false;
|
|
48
|
+
if (rule.excludes && rule.excludes.length > 0) {
|
|
49
|
+
const matchesExclude = rule.excludes.some((pattern) => minimatch(relPath, pattern, { dot: true }));
|
|
50
|
+
if (matchesExclude)
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Apply a single rule against file content.
|
|
57
|
+
* Returns findings (possibly empty).
|
|
58
|
+
*/
|
|
59
|
+
function applyRule(rule, filePath, relPath, content, lines) {
|
|
60
|
+
const findings = [];
|
|
61
|
+
// Pattern-based rules
|
|
62
|
+
if (rule.pattern) {
|
|
63
|
+
const regex = new RegExp(rule.pattern, "gm");
|
|
64
|
+
let match;
|
|
65
|
+
while ((match = regex.exec(content)) !== null) {
|
|
66
|
+
if (rule.matchIsViolation) {
|
|
67
|
+
// Find line number
|
|
68
|
+
const upToMatch = content.substring(0, match.index);
|
|
69
|
+
const lineNumber = upToMatch.split("\n").length;
|
|
70
|
+
findings.push({
|
|
71
|
+
ruleId: rule.id,
|
|
72
|
+
ruleName: rule.name,
|
|
73
|
+
category: rule.category,
|
|
74
|
+
severity: rule.severity,
|
|
75
|
+
filePath: relPath,
|
|
76
|
+
line: lineNumber,
|
|
77
|
+
matchedText: match[0].substring(0, 100),
|
|
78
|
+
message: rule.description,
|
|
79
|
+
suggestion: rule.suggestion,
|
|
80
|
+
goodExample: rule.goodExample,
|
|
81
|
+
badExample: rule.badExample,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// For "require" rules (matchIsViolation = false), flag if NOT found
|
|
86
|
+
if (!rule.matchIsViolation) {
|
|
87
|
+
const hasMatch = regex.test(content);
|
|
88
|
+
if (!hasMatch) {
|
|
89
|
+
findings.push({
|
|
90
|
+
ruleId: rule.id,
|
|
91
|
+
ruleName: rule.name,
|
|
92
|
+
category: rule.category,
|
|
93
|
+
severity: rule.severity,
|
|
94
|
+
filePath: relPath,
|
|
95
|
+
message: `Required pattern not found: ${rule.description}`,
|
|
96
|
+
suggestion: rule.suggestion,
|
|
97
|
+
goodExample: rule.goodExample,
|
|
98
|
+
badExample: rule.badExample,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Structural rules (no pattern, custom logic)
|
|
104
|
+
if (rule.id === "arch-002" && !rule.pattern) {
|
|
105
|
+
// Component file length check
|
|
106
|
+
if (lines.length > 150) {
|
|
107
|
+
findings.push({
|
|
108
|
+
ruleId: rule.id,
|
|
109
|
+
ruleName: rule.name,
|
|
110
|
+
category: rule.category,
|
|
111
|
+
severity: rule.severity,
|
|
112
|
+
filePath: relPath,
|
|
113
|
+
message: `Component has ${lines.length} lines (limit: 150). Likely contains business logic that should be in a service.`,
|
|
114
|
+
suggestion: rule.suggestion,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return findings;
|
|
119
|
+
}
|
|
120
|
+
// ─── Main Review Function ────────────────────────────────────────
|
|
121
|
+
/**
|
|
122
|
+
* Run a full review of a directory against a rule set.
|
|
123
|
+
*/
|
|
124
|
+
export async function reviewDirectory(rootDir, ruleSet, options = {}) {
|
|
125
|
+
const files = await discoverFiles(rootDir, options.excludePatterns);
|
|
126
|
+
// Optionally filter to only requested files
|
|
127
|
+
const targetFiles = options.includePatterns
|
|
128
|
+
? files.filter((f) => {
|
|
129
|
+
const rel = relative(rootDir, f);
|
|
130
|
+
return options.includePatterns.some((p) => minimatch(rel, p, { dot: true }));
|
|
131
|
+
})
|
|
132
|
+
: files;
|
|
133
|
+
// Filter rules by severity/category/phase if requested
|
|
134
|
+
let activeRules = ruleSet.rules;
|
|
135
|
+
if (options.phase) {
|
|
136
|
+
activeRules = activeRules.filter((r) => !r.applicablePhases ||
|
|
137
|
+
r.applicablePhases.length === 0 ||
|
|
138
|
+
r.applicablePhases.includes(options.phase));
|
|
139
|
+
}
|
|
140
|
+
if (options.severityFilter?.length) {
|
|
141
|
+
activeRules = activeRules.filter((r) => options.severityFilter.includes(r.severity));
|
|
142
|
+
}
|
|
143
|
+
if (options.categoryFilter?.length) {
|
|
144
|
+
activeRules = activeRules.filter((r) => options.categoryFilter.includes(r.category));
|
|
145
|
+
}
|
|
146
|
+
const allFindings = [];
|
|
147
|
+
let filesReviewed = 0;
|
|
148
|
+
let filesSkipped = 0;
|
|
149
|
+
for (const filePath of targetFiles) {
|
|
150
|
+
const relPath = relative(rootDir, filePath);
|
|
151
|
+
// Find rules that apply to this file
|
|
152
|
+
const applicableRules = activeRules.filter((rule) => ruleAppliesToFile(rule, relPath));
|
|
153
|
+
if (applicableRules.length === 0) {
|
|
154
|
+
filesSkipped++;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
filesReviewed++;
|
|
158
|
+
try {
|
|
159
|
+
const content = await readFile(filePath, "utf-8");
|
|
160
|
+
const lines = content.split("\n");
|
|
161
|
+
for (const rule of applicableRules) {
|
|
162
|
+
const findings = applyRule(rule, filePath, relPath, content, lines);
|
|
163
|
+
allFindings.push(...findings);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
const message = error instanceof Error ? error.message : "Unknown read error";
|
|
168
|
+
allFindings.push({
|
|
169
|
+
ruleId: "system-error",
|
|
170
|
+
ruleName: "File Read Error",
|
|
171
|
+
category: "custom",
|
|
172
|
+
severity: "warning",
|
|
173
|
+
filePath: relPath,
|
|
174
|
+
message: `Could not read file: ${message}`,
|
|
175
|
+
suggestion: "Check file permissions and encoding.",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Build summary
|
|
180
|
+
const errorCount = allFindings.filter((f) => f.severity === "error").length;
|
|
181
|
+
const warningCount = allFindings.filter((f) => f.severity === "warning").length;
|
|
182
|
+
const infoCount = allFindings.filter((f) => f.severity === "info").length;
|
|
183
|
+
const findingsByCategory = {};
|
|
184
|
+
const findingsByFile = {};
|
|
185
|
+
for (const finding of allFindings) {
|
|
186
|
+
if (!findingsByCategory[finding.category]) {
|
|
187
|
+
findingsByCategory[finding.category] = [];
|
|
188
|
+
}
|
|
189
|
+
findingsByCategory[finding.category].push(finding);
|
|
190
|
+
if (!findingsByFile[finding.filePath]) {
|
|
191
|
+
findingsByFile[finding.filePath] = [];
|
|
192
|
+
}
|
|
193
|
+
findingsByFile[finding.filePath].push(finding);
|
|
194
|
+
}
|
|
195
|
+
let verdict;
|
|
196
|
+
if (errorCount > 0) {
|
|
197
|
+
verdict = "fail";
|
|
198
|
+
}
|
|
199
|
+
else if (warningCount > 0) {
|
|
200
|
+
verdict = "warnings-only";
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
verdict = "pass";
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
verdict,
|
|
207
|
+
phase: options.phase ?? "pre-pr-review",
|
|
208
|
+
totalFiles: targetFiles.length,
|
|
209
|
+
filesReviewed,
|
|
210
|
+
filesSkipped,
|
|
211
|
+
totalFindings: allFindings.length,
|
|
212
|
+
errorCount,
|
|
213
|
+
warningCount,
|
|
214
|
+
infoCount,
|
|
215
|
+
findingsByCategory,
|
|
216
|
+
findingsByFile,
|
|
217
|
+
findings: allFindings,
|
|
218
|
+
rulesApplied: activeRules.length,
|
|
219
|
+
reviewedAt: new Date().toISOString(),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
// ─── Review Single File (for quick checks) ───────────────────────
|
|
223
|
+
export async function reviewSingleFile(filePath, rootDir, ruleSet) {
|
|
224
|
+
const relPath = relative(rootDir, filePath);
|
|
225
|
+
const content = await readFile(filePath, "utf-8");
|
|
226
|
+
const lines = content.split("\n");
|
|
227
|
+
const findings = [];
|
|
228
|
+
for (const rule of ruleSet.rules) {
|
|
229
|
+
if (ruleAppliesToFile(rule, relPath)) {
|
|
230
|
+
findings.push(...applyRule(rule, filePath, relPath, content, lines));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return findings;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=review-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-engine.js","sourceRoot":"","sources":["../../src/services/review-engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAQ,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAStC,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAEzB,oEAAoE;AAEpE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAe,EACf,qBAA+B,EAAE;IAEjC,MAAM,WAAW,GAAG,CAAC,GAAG,gBAAgB,EAAE,GAAG,kBAAkB,CAAC,CAAC;IACjE,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,UAAU,IAAI,CAAC,GAAW;QAC7B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAE5D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAE5C,4CAA4C;YAC5C,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC9C,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAC3C,CAAC;YACF,IAAI,UAAU;gBAAE,SAAS;YAEzB,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;iBAAM,IAAI,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,oEAAoE;AAEpE;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAAgB,EAAE,OAAe;IAC1D,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CACrD,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAC3C,CAAC;IACF,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAElC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CACpD,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAC3C,CAAC;QACF,IAAI,cAAc;YAAE,OAAO,KAAK,CAAC;IACnC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAChB,IAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,OAAe,EACf,KAAe;IAEf,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,sBAAsB;IACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC7C,IAAI,KAA6B,CAAC;QAElC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC9C,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,mBAAmB;gBACnB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBACpD,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;gBAEhD,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,UAAU;oBAChB,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;oBACvC,OAAO,EAAE,IAAI,CAAC,WAAW;oBACzB,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,oEAAoE;QACpE,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,QAAQ,CAAC,IAAI,CAAC;oBACZ,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,+BAA+B,IAAI,CAAC,WAAW,EAAE;oBAC1D,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,8CAA8C;IAC9C,IAAI,IAAI,CAAC,EAAE,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5C,8BAA8B;QAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,iBAAiB,KAAK,CAAC,MAAM,kFAAkF;gBACxH,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,oEAAoE;AAEpE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAe,EACf,OAAgB,EAChB,UAOI,EAAE;IAEN,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAEpE,4CAA4C;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe;QACzC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACjC,OAAO,OAAO,CAAC,eAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACzC,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CACjC,CAAC;QACJ,CAAC,CAAC;QACJ,CAAC,CAAC,KAAK,CAAC;IAEV,uDAAuD;IACvD,IAAI,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;IAChC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,WAAW,GAAG,WAAW,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,CAAC,gBAAgB;YACnB,CAAC,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC;YAC/B,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAM,CAAC,CAC9C,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;QACnC,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACrC,OAAO,CAAC,cAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAC7C,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;QACnC,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACrC,OAAO,CAAC,cAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAC7C,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAoB,EAAE,CAAC;IACxC,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,KAAK,MAAM,QAAQ,IAAI,WAAW,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE5C,qCAAqC;QACrC,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAClD,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CACjC,CAAC;QAEF,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,YAAY,EAAE,CAAC;YACf,SAAS;QACX,CAAC;QAED,aAAa,EAAE,CAAC;QAEhB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAElC,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;gBACnC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;gBACpE,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,OAAO,GACX,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;YAChE,WAAW,CAAC,IAAI,CAAC;gBACf,MAAM,EAAE,cAAc;gBACtB,QAAQ,EAAE,iBAAiB;gBAC3B,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,wBAAwB,OAAO,EAAE;gBAC1C,UAAU,EAAE,sCAAsC;aACnD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC5E,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CACrC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAChC,CAAC,MAAM,CAAC;IACT,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAE1E,MAAM,kBAAkB,GAAoC,EAAE,CAAC;IAC/D,MAAM,cAAc,GAAoC,EAAE,CAAC;IAE3D,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QAClC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1C,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QAC5C,CAAC;QACD,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QACxC,CAAC;QACD,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,OAA0C,CAAC;IAC/C,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;SAAM,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,eAAe,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IAED,OAAO;QACL,OAAO;QACP,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,eAAe;QACvC,UAAU,EAAE,WAAW,CAAC,MAAM;QAC9B,aAAa;QACb,YAAY;QACZ,aAAa,EAAE,WAAW,CAAC,MAAM;QACjC,UAAU;QACV,YAAY;QACZ,SAAS;QACT,kBAAkB;QAClB,cAAc;QACd,QAAQ,EAAE,WAAW;QACrB,YAAY,EAAE,WAAW,CAAC,MAAM;QAChC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;AACJ,CAAC;AAED,oEAAoE;AAEpE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,OAAe,EACf,OAAgB;IAEhB,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YACrC,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule Loader Service
|
|
3
|
+
*
|
|
4
|
+
* Loads review rules from JSON config files.
|
|
5
|
+
* Supports loading from a default bundled ruleset
|
|
6
|
+
* or from a team's custom rules file.
|
|
7
|
+
*/
|
|
8
|
+
import type { RuleSet } from "../types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Load rules from a JSON file path.
|
|
11
|
+
* Falls back to bundled defaults if no path provided.
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadRuleSet(customPath?: string): Promise<RuleSet>;
|
|
14
|
+
/**
|
|
15
|
+
* Merge multiple rule sets (e.g., base + team overrides).
|
|
16
|
+
* Later rules with the same ID override earlier ones.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mergeRuleSets(...sets: RuleSet[]): RuleSet;
|
|
19
|
+
//# sourceMappingURL=rule-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-loader.d.ts","sourceRoot":"","sources":["../../src/services/rule-loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAavD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CA8BlB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAezD"}
|