@boyingliu01/xp-gate 0.8.8 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/bin/xp-gate.js +18 -0
  2. package/hooks/pre-commit +565 -120
  3. package/lib/arch.js +49 -0
  4. package/lib/check.js +50 -0
  5. package/lib/init.js +36 -0
  6. package/lib/principles.js +48 -0
  7. package/mock-policy/AGENTS.md +94 -0
  8. package/mock-policy/__tests__/config.test.ts +292 -0
  9. package/mock-policy/__tests__/integration.test.ts +600 -0
  10. package/mock-policy/__tests__/mock-decision-engine.test.ts +231 -0
  11. package/mock-policy/__tests__/scope-scanner.test.ts +535 -0
  12. package/mock-policy/config.ts +106 -0
  13. package/mock-policy/gate-m3.ts +233 -0
  14. package/mock-policy/mock-decision-engine.ts +155 -0
  15. package/mock-policy/schema.ts +22 -0
  16. package/mock-policy/scope-scanner.ts +288 -0
  17. package/mock-policy/types.ts +65 -0
  18. package/mutation/AGENTS.md +73 -0
  19. package/mutation/__tests__/detect-ai-test.test.ts +152 -0
  20. package/mutation/__tests__/gate-m.test.ts +194 -0
  21. package/mutation/detect-ai-test.ts +128 -0
  22. package/mutation/gate-m.ts +619 -0
  23. package/mutation/init-baseline.ts +192 -0
  24. package/mutation/stryker-types.ts +14 -0
  25. package/mutation/types.ts +70 -0
  26. package/mutation/update-baseline.ts +160 -0
  27. package/package.json +5 -2
  28. package/plugins/claude-code/.claude-plugin/plugin.json +2 -2
  29. package/plugins/claude-code/skills/delphi-review/AGENTS.md +7 -7
  30. package/plugins/claude-code/skills/delphi-review/INSTALL.md +1 -1
  31. package/plugins/claude-code/skills/delphi-review/SKILL.md +12 -12
  32. package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +1 -1
  33. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +83 -36
  34. package/plugins/claude-code/skills/sprint-flow/SKILL.md +1 -1
  35. package/plugins/claude-code/skills/sprint-flow/references/force-levels.md +1 -1
  36. package/plugins/claude-code/skills/sprint-flow/references/phase-3-review.md +2 -2
  37. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  38. package/plugins/opencode/README.md +23 -7
  39. package/plugins/opencode/index.ts +36 -21
  40. package/plugins/opencode/package.json +1 -1
  41. package/plugins/opencode/skills/delphi-review/AGENTS.md +7 -7
  42. package/plugins/opencode/skills/delphi-review/INSTALL.md +1 -1
  43. package/plugins/opencode/skills/delphi-review/SKILL.md +12 -12
  44. package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +1 -1
  45. package/plugins/opencode/skills/sprint-flow/AGENTS.md +83 -36
  46. package/plugins/opencode/skills/sprint-flow/SKILL.md +1 -1
  47. package/plugins/opencode/skills/sprint-flow/references/force-levels.md +1 -1
  48. package/plugins/opencode/skills/sprint-flow/references/phase-3-review.md +2 -2
  49. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  50. package/plugins/qoder/plugin.json +20 -0
  51. package/plugins/qoder/skills/delphi-review/AGENTS.md +7 -7
  52. package/plugins/qoder/skills/delphi-review/INSTALL.md +1 -1
  53. package/plugins/qoder/skills/delphi-review/SKILL.md +12 -12
  54. package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +1 -1
  55. package/plugins/qoder/skills/sprint-flow/AGENTS.md +83 -36
  56. package/plugins/qoder/skills/sprint-flow/SKILL.md +51 -1
  57. package/plugins/qoder/skills/sprint-flow/references/force-levels.md +1 -1
  58. package/plugins/qoder/skills/sprint-flow/references/phase-3-review.md +2 -2
  59. package/plugins/qoder/skills/test-driven-development/SKILL.md +371 -0
  60. package/plugins/qoder/skills/test-driven-development/testing-anti-patterns.md +299 -0
  61. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  62. package/principles/AGENTS.md +111 -0
  63. package/principles/__tests__/analyzer.test.ts +343 -0
  64. package/principles/__tests__/baseline-storage.test.ts +187 -0
  65. package/principles/__tests__/baseline.test.ts +328 -0
  66. package/principles/__tests__/boy-scout-integration.test.ts +264 -0
  67. package/principles/__tests__/boy-scout.test.ts +646 -0
  68. package/principles/__tests__/config.test.ts +78 -0
  69. package/principles/__tests__/index.test.ts +81 -0
  70. package/principles/__tests__/lint-baseline.test.ts +314 -0
  71. package/principles/__tests__/reporter.test.ts +832 -0
  72. package/principles/__tests__/types.test.ts +101 -0
  73. package/principles/adapters/__tests__/base.test.ts +95 -0
  74. package/principles/adapters/__tests__/cpp.test.ts +290 -0
  75. package/principles/adapters/__tests__/dart.test.ts +84 -0
  76. package/principles/adapters/__tests__/go.test.ts +68 -0
  77. package/principles/adapters/__tests__/java.test.ts +68 -0
  78. package/principles/adapters/__tests__/kotlin.test.ts +76 -0
  79. package/principles/adapters/__tests__/objectivec.test.ts +453 -0
  80. package/principles/adapters/__tests__/python.test.ts +84 -0
  81. package/principles/adapters/__tests__/swift.test.ts +68 -0
  82. package/principles/adapters/__tests__/typescript.test.ts +116 -0
  83. package/principles/adapters/base.ts +124 -0
  84. package/principles/adapters/cpp.ts +115 -0
  85. package/principles/adapters/dart.ts +44 -0
  86. package/principles/adapters/go.ts +55 -0
  87. package/principles/adapters/java.ts +56 -0
  88. package/principles/adapters/kotlin.ts +54 -0
  89. package/principles/adapters/objectivec.ts +82 -0
  90. package/principles/adapters/python.ts +83 -0
  91. package/principles/adapters/swift.ts +40 -0
  92. package/principles/adapters/typescript.ts +66 -0
  93. package/principles/analyzer.ts +190 -0
  94. package/principles/baseline.ts +273 -0
  95. package/principles/boy-scout.ts +416 -0
  96. package/principles/config.ts +118 -0
  97. package/principles/index.ts +87 -0
  98. package/principles/lint-baseline.ts +385 -0
  99. package/principles/reporter.ts +188 -0
  100. package/principles/rules/__tests__/clean-code/code-duplication.test.ts +84 -0
  101. package/principles/rules/__tests__/clean-code/deep-nesting.test.ts +74 -0
  102. package/principles/rules/__tests__/clean-code/god-class.test.ts +111 -0
  103. package/principles/rules/__tests__/clean-code/large-file.test.ts +73 -0
  104. package/principles/rules/__tests__/clean-code/long-function.test.ts +83 -0
  105. package/principles/rules/__tests__/clean-code/magic-numbers.test.ts +68 -0
  106. package/principles/rules/__tests__/clean-code/many-exports.test.ts +118 -0
  107. package/principles/rules/__tests__/clean-code/missing-error-handling.test.ts +173 -0
  108. package/principles/rules/__tests__/clean-code/too-many-params.test.ts +60 -0
  109. package/principles/rules/__tests__/clean-code/unused-imports.test.ts +107 -0
  110. package/principles/rules/__tests__/index.test.ts +49 -0
  111. package/principles/rules/__tests__/solid/dip.test.ts +121 -0
  112. package/principles/rules/__tests__/solid/isp.test.ts +60 -0
  113. package/principles/rules/__tests__/solid/lsp.test.ts +184 -0
  114. package/principles/rules/__tests__/solid/ocp.test.ts +69 -0
  115. package/principles/rules/__tests__/solid/srp.test.ts +89 -0
  116. package/principles/rules/clean-code/code-duplication.ts +31 -0
  117. package/principles/rules/clean-code/deep-nesting.ts +38 -0
  118. package/principles/rules/clean-code/god-class.ts +54 -0
  119. package/principles/rules/clean-code/large-file.ts +34 -0
  120. package/principles/rules/clean-code/long-function.ts +47 -0
  121. package/principles/rules/clean-code/magic-numbers.ts +59 -0
  122. package/principles/rules/clean-code/many-exports.ts +40 -0
  123. package/principles/rules/clean-code/missing-error-handling.ts +33 -0
  124. package/principles/rules/clean-code/too-many-params.ts +35 -0
  125. package/principles/rules/clean-code/unused-imports.ts +33 -0
  126. package/principles/rules/index.ts +61 -0
  127. package/principles/rules/solid/dip.ts +47 -0
  128. package/principles/rules/solid/isp.ts +37 -0
  129. package/principles/rules/solid/lsp.ts +79 -0
  130. package/principles/rules/solid/ocp.ts +40 -0
  131. package/principles/rules/solid/srp.ts +44 -0
  132. package/principles/types.ts +27 -0
  133. package/skills/delphi-review/AGENTS.md +7 -7
  134. package/skills/delphi-review/INSTALL.md +1 -1
  135. package/skills/delphi-review/SKILL.md +12 -12
  136. package/skills/delphi-review/references/code-walkthrough.md +1 -1
  137. package/skills/sprint-flow/AGENTS.md +83 -36
  138. package/skills/sprint-flow/SKILL.md +1 -1
  139. package/skills/sprint-flow/references/force-levels.md +1 -1
  140. package/skills/sprint-flow/references/phase-3-review.md +2 -2
  141. package/skills/test-specification-alignment/AGENTS.md +3 -3
@@ -0,0 +1,82 @@
1
+ import { BaseAdapter } from './base';
2
+ import { Adapter } from '../types';
3
+
4
+ /**
5
+ * Objective-C Adapter
6
+ * Supports .m (Objective-C) and .mm (Objective-C++) files
7
+ */
8
+ export class ObjectiveCAdapter extends BaseAdapter implements Adapter {
9
+ detectLanguage(): string {
10
+ const ext = this.filePath.toLowerCase();
11
+ if (ext.endsWith('.m') || ext.endsWith('.mm')) {
12
+ return 'objectivec';
13
+ }
14
+ return super.detectLanguage();
15
+ }
16
+
17
+ parseAST(): unknown {
18
+ return {
19
+ content: this.fileContent,
20
+ language: 'objectivec',
21
+ filePath: this.filePath
22
+ };
23
+ }
24
+
25
+ extractFunctions(): unknown[] {
26
+ const functionMatches = [];
27
+ const methodRegex = /[-+]\s*\([^)]+\)\s*(\w+)\s*(?::[^;{]+)?/g;
28
+ let match;
29
+
30
+ while ((match = methodRegex.exec(this.fileContent)) !== null) {
31
+ functionMatches.push({
32
+ name: match[1],
33
+ type: 'method',
34
+ line: this.getLineNumber(match.index),
35
+ code: this.extractCodeBlock(match.index)
36
+ });
37
+ }
38
+
39
+ const cFunctionRegex = /^(?:static\s+)?(?:inline\s+)?(?:\w+\s+)+(\w+)\s*\([^)]*\)\s*\{/gm;
40
+ while ((match = cFunctionRegex.exec(this.fileContent)) !== null) {
41
+ if (this.fileContent.substring(match.index).startsWith('-') ||
42
+ this.fileContent.substring(match.index).startsWith('+')) {
43
+ continue;
44
+ }
45
+ functionMatches.push({
46
+ name: match[1],
47
+ type: 'function',
48
+ line: this.getLineNumber(match.index),
49
+ code: this.extractCodeBlock(match.index)
50
+ });
51
+ }
52
+
53
+ return functionMatches;
54
+ }
55
+
56
+ extractClasses(): unknown[] {
57
+ const classMatches = [];
58
+ const implRegex = /@implementation\s+(\w+)/g;
59
+ let match;
60
+
61
+ while ((match = implRegex.exec(this.fileContent)) !== null) {
62
+ classMatches.push({
63
+ name: match[1],
64
+ type: 'implementation',
65
+ line: this.getLineNumber(match.index),
66
+ code: this.extractCodeBlock(match.index)
67
+ });
68
+ }
69
+
70
+ const interfaceRegex = /@interface\s+(\w+)/g;
71
+ while ((match = interfaceRegex.exec(this.fileContent)) !== null) {
72
+ classMatches.push({
73
+ name: match[1],
74
+ type: 'interface',
75
+ line: this.getLineNumber(match.index),
76
+ code: this.extractCodeBlock(match.index)
77
+ });
78
+ }
79
+
80
+ return classMatches;
81
+ }
82
+ }
@@ -0,0 +1,83 @@
1
+ import { BaseAdapter } from './base';
2
+ import { Adapter } from '../types';
3
+
4
+ export class PythonAdapter extends BaseAdapter implements Adapter {
5
+ detectLanguage(): string {
6
+ const ext = this.filePath.toLowerCase();
7
+ if (ext.endsWith('.py')) {
8
+ return 'python';
9
+ }
10
+ return super.detectLanguage();
11
+ }
12
+
13
+ parseAST(): unknown {
14
+ return {
15
+ content: this.fileContent,
16
+ language: 'python',
17
+ filePath: this.filePath
18
+ };
19
+ }
20
+
21
+ extractFunctions(): unknown[] {
22
+ const functionMatches = [];
23
+ const fnRegex = /(async\s+)?def\s+(\w+)\s*\([^)]*\)\s*:/g;
24
+ let match;
25
+
26
+ while ((match = fnRegex.exec(this.fileContent)) !== null) {
27
+ functionMatches.push({
28
+ name: match[2],
29
+ type: match[1] ? 'async_function' : 'function',
30
+ line: this.getLineNumber(match.index),
31
+ code: this.getCodeBlock(match.index)
32
+ });
33
+ }
34
+
35
+ return functionMatches;
36
+ }
37
+
38
+ extractClasses(): unknown[] {
39
+ const classMatches = [];
40
+ const classRegex = /class\s+(\w+)(\s*\([^)]*\))?\s*:/g;
41
+ let match;
42
+
43
+ while ((match = classRegex.exec(this.fileContent)) !== null) {
44
+ classMatches.push({
45
+ name: match[1],
46
+ type: 'class',
47
+ line: this.getLineNumber(match.index),
48
+ code: this.getCodeBlock(match.index)
49
+ });
50
+ }
51
+
52
+ return classMatches;
53
+ }
54
+
55
+ private getCodeBlock(startPos: number): string {
56
+ const lines = this.fileContent.split('\n');
57
+ const startLine = this.getLineNumber(startPos) - 1;
58
+
59
+ const codeLines = [];
60
+ let indentLevel = -1;
61
+
62
+ for (let i = startLine; i < lines.length; i++) {
63
+ const line = lines[i];
64
+
65
+ if (i === startLine) {
66
+ indentLevel = line.search(/\S/);
67
+ codeLines.push(line);
68
+ } else {
69
+ const currentIndent = line.search(/\S/);
70
+
71
+ if (line.trim() === '' || currentIndent > indentLevel) {
72
+ codeLines.push(line);
73
+ } else if (currentIndent >= 0 && currentIndent <= indentLevel) {
74
+ break;
75
+ }
76
+ }
77
+
78
+ if (codeLines.length > 50) break;
79
+ }
80
+
81
+ return codeLines.join('\n');
82
+ }
83
+ }
@@ -0,0 +1,40 @@
1
+ import { BaseAdapter } from './base';
2
+ import { Adapter } from '../types';
3
+
4
+ export class SwiftAdapter extends BaseAdapter implements Adapter {
5
+ detectLanguage(): string {
6
+ const ext = this.filePath.toLowerCase();
7
+ if (ext.endsWith('.swift')) {
8
+ return 'swift';
9
+ }
10
+ return super.detectLanguage();
11
+ }
12
+
13
+ parseAST(): unknown {
14
+ return this.createParseResult('swift');
15
+ }
16
+
17
+ extractFunctions(): unknown[] {
18
+ const functionMatches = [];
19
+ const fnRegex = /func\s+(\w+)\s*\([^)]*\)/g;
20
+ let match;
21
+
22
+ while ((match = fnRegex.exec(this.fileContent)) !== null) {
23
+ functionMatches.push(this.createCodeMatch(match[1], 'function', match.index));
24
+ }
25
+
26
+ return functionMatches;
27
+ }
28
+
29
+ extractClasses(): unknown[] {
30
+ const classMatches = [];
31
+ const classRegex = /class\s+(\w+)/g;
32
+ let match;
33
+
34
+ while ((match = classRegex.exec(this.fileContent)) !== null) {
35
+ classMatches.push(this.createCodeMatch(match[1], 'class', match.index));
36
+ }
37
+
38
+ return classMatches;
39
+ }
40
+ }
@@ -0,0 +1,66 @@
1
+ import { BaseAdapter } from './base';
2
+ import { Adapter } from '../types';
3
+
4
+ export class TypeScriptAdapter extends BaseAdapter implements Adapter {
5
+ detectLanguage(): string {
6
+ const ext = this.filePath.toLowerCase();
7
+ if (ext.endsWith('.ts') || ext.endsWith('.tsx')) {
8
+ return 'typescript';
9
+ }
10
+ return super.detectLanguage();
11
+ }
12
+
13
+ parseAST(): unknown {
14
+ return this.createParseResult('typescript');
15
+ }
16
+
17
+ extractFunctions(): unknown[] {
18
+ const functionMatches = [];
19
+ const fnRegex = /(export\s+)?(async\s+)?function\s+(\w+)\s*\([^)]*\)\s*[:\w\s]*{/g;
20
+ let match;
21
+
22
+ while ((match = fnRegex.exec(this.fileContent)) !== null) {
23
+ functionMatches.push(this.createCodeMatch(match[3], 'function', match.index));
24
+ }
25
+
26
+ return functionMatches;
27
+ }
28
+
29
+ extractClasses(): unknown[] {
30
+ const classMatches = [];
31
+ const classRegex = /(export\s+)?class\s+(\w+)\s*(extends\s+[\w.]+)?\s*{/g;
32
+ let match;
33
+
34
+ while ((match = classRegex.exec(this.fileContent)) !== null) {
35
+ classMatches.push(this.createCodeMatch(match[2], 'class', match.index));
36
+ }
37
+
38
+ return classMatches;
39
+ }
40
+
41
+ extractExports(): unknown[] {
42
+ const exportMatches = [];
43
+ const exportRegex = /^(export\s+(default\s+)?(async\s+)?(function|const|class|let|var|type|interface|enum)\s+\w+)/gm;
44
+ let match;
45
+
46
+ while ((match = exportRegex.exec(this.fileContent)) !== null) {
47
+ exportMatches.push({
48
+ name: match[0],
49
+ type: 'export',
50
+ line: this.getLineNumber(match.index)
51
+ });
52
+ }
53
+
54
+ const reExportRegex = /^export\s*{/gm;
55
+ let reMatch;
56
+ while ((reMatch = reExportRegex.exec(this.fileContent)) !== null) {
57
+ exportMatches.push({
58
+ name: reMatch[0],
59
+ type: 're-export',
60
+ line: this.getLineNumber(reMatch.index)
61
+ });
62
+ }
63
+
64
+ return exportMatches;
65
+ }
66
+ }
@@ -0,0 +1,190 @@
1
+ import { Rule, Violation, Adapter } from './types';
2
+ import { TypeScriptAdapter } from './adapters/typescript';
3
+ import { PythonAdapter } from './adapters/python';
4
+ import { GoAdapter } from './adapters/go';
5
+ import { JavaAdapter } from './adapters/java';
6
+ import { KotlinAdapter } from './adapters/kotlin';
7
+ import { DartAdapter } from './adapters/dart';
8
+ import { SwiftAdapter } from './adapters/swift';
9
+ import { ObjectiveCAdapter } from './adapters/objectivec';
10
+ import { CppAdapter } from './adapters/cpp';
11
+ import { extname } from 'path';
12
+
13
+ export type { Violation } from './types';
14
+ export interface AnalysisOptions {
15
+ enabledRules?: string[];
16
+ }
17
+
18
+ export interface FileResult {
19
+ violations: Violation[];
20
+ ruleIds: string[];
21
+ }
22
+
23
+ export interface RuleResult {
24
+ violationCount: number;
25
+ filesChecked: number;
26
+ }
27
+
28
+ export interface AnalysisSummary {
29
+ totalViolations: number;
30
+ errorCount: number;
31
+ warningCount: number;
32
+ infoCount: number;
33
+ filesChecked: number;
34
+ rulesRun: number;
35
+ }
36
+
37
+ export interface AnalysisResult {
38
+ violations: Violation[];
39
+ summary: AnalysisSummary;
40
+ fileResults: Record<string, FileResult>;
41
+ ruleResults: Record<string, RuleResult>;
42
+ executionTimeMs: number;
43
+ errors: string[];
44
+ }
45
+
46
+ export type AdapterFactory = (filePath: string) => Adapter | null;
47
+
48
+ export function getAdapterForFile(filePath: string): Adapter | null {
49
+ const ext = extname(filePath).toLowerCase();
50
+
51
+ const adapterMap: Record<string, new (filePath: string) => Adapter> = {
52
+ '.ts': TypeScriptAdapter,
53
+ '.tsx': TypeScriptAdapter,
54
+ '.js': TypeScriptAdapter,
55
+ '.jsx': TypeScriptAdapter,
56
+ '.py': PythonAdapter,
57
+ '.go': GoAdapter,
58
+ '.java': JavaAdapter,
59
+ '.kt': KotlinAdapter,
60
+ '.kts': KotlinAdapter,
61
+ '.dart': DartAdapter,
62
+ '.swift': SwiftAdapter,
63
+ '.m': ObjectiveCAdapter,
64
+ '.mm': ObjectiveCAdapter,
65
+ '.cpp': CppAdapter,
66
+ '.cxx': CppAdapter,
67
+ '.cc': CppAdapter,
68
+ '.c': CppAdapter,
69
+ '.hpp': CppAdapter,
70
+ '.h': CppAdapter,
71
+ };
72
+
73
+ const AdapterClass = adapterMap[ext];
74
+ if (!AdapterClass) {
75
+ return null;
76
+ }
77
+
78
+ return new AdapterClass(filePath);
79
+ }
80
+
81
+ function resolveAdapter(
82
+ file: string,
83
+ adapterOrFactory: Adapter | AdapterFactory,
84
+ ): Adapter | null {
85
+ if (typeof adapterOrFactory === 'function') {
86
+ return adapterOrFactory(file);
87
+ }
88
+ return adapterOrFactory;
89
+ }
90
+
91
+ function computeSummary(
92
+ violations: Violation[],
93
+ fileResults: Record<string, FileResult>,
94
+ rulesToRun: Rule[],
95
+ ): AnalysisSummary {
96
+ let errorCount = 0;
97
+ let warningCount = 0;
98
+ let infoCount = 0;
99
+ for (const v of violations) {
100
+ if (v.severity === 'error') errorCount++;
101
+ else if (v.severity === 'warning') warningCount++;
102
+ else infoCount++;
103
+ }
104
+ return {
105
+ totalViolations: violations.length,
106
+ errorCount,
107
+ warningCount,
108
+ infoCount,
109
+ filesChecked: Object.keys(fileResults).length,
110
+ rulesRun: rulesToRun.length,
111
+ };
112
+ }
113
+
114
+ function runRuleOnFile(ctx: RunRuleContext): void {
115
+ const { file, rule, adapter, violations, fileResult, ruleResult, errors } = ctx;
116
+ ruleResult.filesChecked++;
117
+ try {
118
+ const ruleViolations = rule.check(file, adapter);
119
+ if (ruleViolations.length > 0) {
120
+ violations.push(...ruleViolations);
121
+ fileResult.violations.push(...ruleViolations);
122
+ fileResult.ruleIds.push(rule.id);
123
+ ruleResult.violationCount += ruleViolations.length;
124
+ }
125
+ } catch (err) {
126
+ const msg = err instanceof Error ? err.message : String(err);
127
+ errors.push(`Rule ${rule.id} failed on ${file}: ${msg}`);
128
+ }
129
+ }
130
+
131
+ interface RunRuleContext {
132
+ file: string;
133
+ rule: Rule;
134
+ adapter: Adapter;
135
+ violations: Violation[];
136
+ fileResult: FileResult;
137
+ ruleResult: RuleResult;
138
+ errors: string[];
139
+ }
140
+
141
+ export async function analyze(
142
+ files: string[],
143
+ rules: Rule[],
144
+ adapterOrFactory: Adapter | AdapterFactory,
145
+ options?: AnalysisOptions
146
+ ): Promise<AnalysisResult> {
147
+ const startTime = Date.now();
148
+ const errors: string[] = [];
149
+ const violations: Violation[] = [];
150
+ const fileResults: Record<string, FileResult> = {};
151
+ const ruleResults: Record<string, RuleResult> = {};
152
+
153
+ const enabledRules = options?.enabledRules ?? rules.map(r => r.id);
154
+ const rulesToRun = rules.filter(r => enabledRules.includes(r.id));
155
+
156
+ for (const rule of rulesToRun) {
157
+ ruleResults[rule.id] = { violationCount: 0, filesChecked: 0 };
158
+ }
159
+
160
+ for (const file of files) {
161
+ const adapter = resolveAdapter(file, adapterOrFactory);
162
+ if (!adapter || adapter.detectLanguage() === 'unknown') continue;
163
+
164
+ const fileResult: FileResult = { violations: [], ruleIds: [] };
165
+ fileResults[file] = fileResult;
166
+
167
+ for (const rule of rulesToRun) {
168
+ runRuleOnFile({
169
+ file,
170
+ rule,
171
+ adapter,
172
+ violations,
173
+ fileResult,
174
+ ruleResult: ruleResults[rule.id],
175
+ errors,
176
+ });
177
+ }
178
+ }
179
+
180
+ const summary = computeSummary(violations, fileResults, rulesToRun);
181
+
182
+ return {
183
+ violations,
184
+ summary,
185
+ fileResults,
186
+ ruleResults,
187
+ executionTimeMs: Date.now() - startTime,
188
+ errors,
189
+ };
190
+ }