@boyingliu01/xp-gate 0.8.9 → 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 (132) hide show
  1. package/lib/init.js +36 -0
  2. package/mock-policy/AGENTS.md +94 -0
  3. package/mock-policy/__tests__/config.test.ts +292 -0
  4. package/mock-policy/__tests__/integration.test.ts +600 -0
  5. package/mock-policy/__tests__/mock-decision-engine.test.ts +231 -0
  6. package/mock-policy/__tests__/scope-scanner.test.ts +535 -0
  7. package/mock-policy/config.ts +106 -0
  8. package/mock-policy/gate-m3.ts +233 -0
  9. package/mock-policy/mock-decision-engine.ts +155 -0
  10. package/mock-policy/schema.ts +22 -0
  11. package/mock-policy/scope-scanner.ts +288 -0
  12. package/mock-policy/types.ts +65 -0
  13. package/mutation/AGENTS.md +73 -0
  14. package/mutation/__tests__/detect-ai-test.test.ts +152 -0
  15. package/mutation/__tests__/gate-m.test.ts +194 -0
  16. package/mutation/detect-ai-test.ts +128 -0
  17. package/mutation/gate-m.ts +619 -0
  18. package/mutation/init-baseline.ts +192 -0
  19. package/mutation/stryker-types.ts +14 -0
  20. package/mutation/types.ts +70 -0
  21. package/mutation/update-baseline.ts +160 -0
  22. package/package.json +5 -2
  23. package/plugins/claude-code/.claude-plugin/plugin.json +2 -2
  24. package/plugins/claude-code/skills/delphi-review/AGENTS.md +7 -7
  25. package/plugins/claude-code/skills/delphi-review/INSTALL.md +1 -1
  26. package/plugins/claude-code/skills/delphi-review/SKILL.md +12 -12
  27. package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +1 -1
  28. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +7 -7
  29. package/plugins/claude-code/skills/sprint-flow/SKILL.md +1 -1
  30. package/plugins/claude-code/skills/sprint-flow/references/force-levels.md +1 -1
  31. package/plugins/claude-code/skills/sprint-flow/references/phase-3-review.md +2 -2
  32. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  33. package/plugins/opencode/package.json +1 -1
  34. package/plugins/opencode/skills/delphi-review/AGENTS.md +7 -7
  35. package/plugins/opencode/skills/delphi-review/INSTALL.md +1 -1
  36. package/plugins/opencode/skills/delphi-review/SKILL.md +12 -12
  37. package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +1 -1
  38. package/plugins/opencode/skills/sprint-flow/AGENTS.md +7 -7
  39. package/plugins/opencode/skills/sprint-flow/SKILL.md +1 -1
  40. package/plugins/opencode/skills/sprint-flow/references/force-levels.md +1 -1
  41. package/plugins/opencode/skills/sprint-flow/references/phase-3-review.md +2 -2
  42. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  43. package/plugins/qoder/plugin.json +1 -1
  44. package/plugins/qoder/skills/delphi-review/AGENTS.md +7 -7
  45. package/plugins/qoder/skills/delphi-review/INSTALL.md +1 -1
  46. package/plugins/qoder/skills/delphi-review/SKILL.md +12 -12
  47. package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +1 -1
  48. package/plugins/qoder/skills/sprint-flow/AGENTS.md +7 -7
  49. package/plugins/qoder/skills/sprint-flow/SKILL.md +51 -1
  50. package/plugins/qoder/skills/sprint-flow/references/force-levels.md +1 -1
  51. package/plugins/qoder/skills/sprint-flow/references/phase-3-review.md +2 -2
  52. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  53. package/principles/AGENTS.md +111 -0
  54. package/principles/__tests__/analyzer.test.ts +343 -0
  55. package/principles/__tests__/baseline-storage.test.ts +187 -0
  56. package/principles/__tests__/baseline.test.ts +328 -0
  57. package/principles/__tests__/boy-scout-integration.test.ts +264 -0
  58. package/principles/__tests__/boy-scout.test.ts +646 -0
  59. package/principles/__tests__/config.test.ts +78 -0
  60. package/principles/__tests__/index.test.ts +81 -0
  61. package/principles/__tests__/lint-baseline.test.ts +314 -0
  62. package/principles/__tests__/reporter.test.ts +832 -0
  63. package/principles/__tests__/types.test.ts +101 -0
  64. package/principles/adapters/__tests__/base.test.ts +95 -0
  65. package/principles/adapters/__tests__/cpp.test.ts +290 -0
  66. package/principles/adapters/__tests__/dart.test.ts +84 -0
  67. package/principles/adapters/__tests__/go.test.ts +68 -0
  68. package/principles/adapters/__tests__/java.test.ts +68 -0
  69. package/principles/adapters/__tests__/kotlin.test.ts +76 -0
  70. package/principles/adapters/__tests__/objectivec.test.ts +453 -0
  71. package/principles/adapters/__tests__/python.test.ts +84 -0
  72. package/principles/adapters/__tests__/swift.test.ts +68 -0
  73. package/principles/adapters/__tests__/typescript.test.ts +116 -0
  74. package/principles/adapters/base.ts +124 -0
  75. package/principles/adapters/cpp.ts +115 -0
  76. package/principles/adapters/dart.ts +44 -0
  77. package/principles/adapters/go.ts +55 -0
  78. package/principles/adapters/java.ts +56 -0
  79. package/principles/adapters/kotlin.ts +54 -0
  80. package/principles/adapters/objectivec.ts +82 -0
  81. package/principles/adapters/python.ts +83 -0
  82. package/principles/adapters/swift.ts +40 -0
  83. package/principles/adapters/typescript.ts +66 -0
  84. package/principles/analyzer.ts +190 -0
  85. package/principles/baseline.ts +273 -0
  86. package/principles/boy-scout.ts +416 -0
  87. package/principles/config.ts +118 -0
  88. package/principles/index.ts +87 -0
  89. package/principles/lint-baseline.ts +385 -0
  90. package/principles/reporter.ts +188 -0
  91. package/principles/rules/__tests__/clean-code/code-duplication.test.ts +84 -0
  92. package/principles/rules/__tests__/clean-code/deep-nesting.test.ts +74 -0
  93. package/principles/rules/__tests__/clean-code/god-class.test.ts +111 -0
  94. package/principles/rules/__tests__/clean-code/large-file.test.ts +73 -0
  95. package/principles/rules/__tests__/clean-code/long-function.test.ts +83 -0
  96. package/principles/rules/__tests__/clean-code/magic-numbers.test.ts +68 -0
  97. package/principles/rules/__tests__/clean-code/many-exports.test.ts +118 -0
  98. package/principles/rules/__tests__/clean-code/missing-error-handling.test.ts +173 -0
  99. package/principles/rules/__tests__/clean-code/too-many-params.test.ts +60 -0
  100. package/principles/rules/__tests__/clean-code/unused-imports.test.ts +107 -0
  101. package/principles/rules/__tests__/index.test.ts +49 -0
  102. package/principles/rules/__tests__/solid/dip.test.ts +121 -0
  103. package/principles/rules/__tests__/solid/isp.test.ts +60 -0
  104. package/principles/rules/__tests__/solid/lsp.test.ts +184 -0
  105. package/principles/rules/__tests__/solid/ocp.test.ts +69 -0
  106. package/principles/rules/__tests__/solid/srp.test.ts +89 -0
  107. package/principles/rules/clean-code/code-duplication.ts +31 -0
  108. package/principles/rules/clean-code/deep-nesting.ts +38 -0
  109. package/principles/rules/clean-code/god-class.ts +54 -0
  110. package/principles/rules/clean-code/large-file.ts +34 -0
  111. package/principles/rules/clean-code/long-function.ts +47 -0
  112. package/principles/rules/clean-code/magic-numbers.ts +59 -0
  113. package/principles/rules/clean-code/many-exports.ts +40 -0
  114. package/principles/rules/clean-code/missing-error-handling.ts +33 -0
  115. package/principles/rules/clean-code/too-many-params.ts +35 -0
  116. package/principles/rules/clean-code/unused-imports.ts +33 -0
  117. package/principles/rules/index.ts +61 -0
  118. package/principles/rules/solid/dip.ts +47 -0
  119. package/principles/rules/solid/isp.ts +37 -0
  120. package/principles/rules/solid/lsp.ts +79 -0
  121. package/principles/rules/solid/ocp.ts +40 -0
  122. package/principles/rules/solid/srp.ts +44 -0
  123. package/principles/types.ts +27 -0
  124. package/skills/delphi-review/AGENTS.md +7 -7
  125. package/skills/delphi-review/INSTALL.md +1 -1
  126. package/skills/delphi-review/SKILL.md +12 -12
  127. package/skills/delphi-review/references/code-walkthrough.md +1 -1
  128. package/skills/sprint-flow/AGENTS.md +7 -7
  129. package/skills/sprint-flow/SKILL.md +1 -1
  130. package/skills/sprint-flow/references/force-levels.md +1 -1
  131. package/skills/sprint-flow/references/phase-3-review.md +2 -2
  132. package/skills/test-specification-alignment/AGENTS.md +3 -3
@@ -0,0 +1,184 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { lspRule } from '../../solid/lsp';
3
+
4
+ const mockAdapter = {
5
+ detectLanguage: () => 'typescript',
6
+ parseAST: () => undefined,
7
+ extractFunctions: () => [],
8
+ extractClasses: () => [],
9
+ countLines: () => 0
10
+ };
11
+
12
+ describe('lsp.ts - Liskov Substitution Principle Rule', () => {
13
+ it('should detect parameter type changes in override', () => {
14
+ const mockAdapterWithViolation = {
15
+ ...mockAdapter,
16
+ extractClasses: () => [{
17
+ name: 'DerivedRepo',
18
+ line: 1,
19
+ code: `class DerivedRepo extends BaseRepo { findById(id: UserId): Entity {} }`
20
+ }]
21
+ };
22
+
23
+ const violations = lspRule.check('test.ts', mockAdapterWithViolation as never);
24
+
25
+ expect(violations.length).toBeGreaterThan(0);
26
+ expect(violations[0].ruleId).toBe('solid.lsp');
27
+ });
28
+
29
+ it('should pass for proper override', () => {
30
+ const mockAdapterWithProperOverride = {
31
+ ...mockAdapter,
32
+ extractClasses: () => [{
33
+ name: 'DerivedRepo',
34
+ line: 1,
35
+ code: `
36
+ class DerivedRepo extends BaseRepo {
37
+ findById(id: string): Entity | null {}
38
+ }
39
+ `
40
+ }]
41
+ };
42
+
43
+ const violations = lspRule.check('test.ts', mockAdapterWithProperOverride as never);
44
+
45
+ expect(violations.length).toBe(0);
46
+ });
47
+
48
+ it('should use severity from config', () => {
49
+ expect(lspRule.severity).toBe('info');
50
+ });
51
+
52
+ it('should return empty violations when adapter throws error', () => {
53
+ const mockAdapterThatThrows = {
54
+ ...mockAdapter,
55
+ extractClasses: () => { throw new Error('Adapter failed'); }
56
+ };
57
+
58
+ const violations = lspRule.check('test.ts', mockAdapterThatThrows as never);
59
+
60
+ expect(violations.length).toBe(0);
61
+ });
62
+
63
+ /**
64
+ * @test REQ-174 Branch coverage for lsp rule
65
+ * @intent Cover uncovered branches: missing class.code, no extends keyword,
66
+ * non-class extractClasses returning null, params without ':',
67
+ * params with ':' but no type word, cls.line missing fallback,
68
+ * className startsWith paramType branch.
69
+ * @covers AC-174-05, AC-174-06, AC-174-07
70
+ */
71
+ it('returns no violations when extractClasses returns null (|| [] branch)', () => {
72
+ const adapter = {
73
+ ...mockAdapter,
74
+ extractClasses: () => null as unknown as never[],
75
+ };
76
+ const violations = lspRule.check('test.ts', adapter as never);
77
+ expect(violations).toHaveLength(0);
78
+ });
79
+
80
+ it('returns no violations when class has no code property', () => {
81
+ const adapter = {
82
+ ...mockAdapter,
83
+ extractClasses: () => [{ name: 'X', line: 1 }],
84
+ };
85
+ const violations = lspRule.check('test.ts', adapter as never);
86
+ expect(violations).toHaveLength(0);
87
+ });
88
+
89
+ it('returns no violations when class does not extend anything', () => {
90
+ const adapter = {
91
+ ...mockAdapter,
92
+ extractClasses: () => [{
93
+ name: 'Plain',
94
+ line: 1,
95
+ code: `class Plain { method(id: Foo) {} }`,
96
+ }],
97
+ };
98
+ const violations = lspRule.check('test.ts', adapter as never);
99
+ expect(violations).toHaveLength(0);
100
+ });
101
+
102
+ it('ignores params with no type annotation (no colon branch)', () => {
103
+ // foo(id) — no colon → extractParamType returns null
104
+ const adapter = {
105
+ ...mockAdapter,
106
+ extractClasses: () => [{
107
+ name: 'D',
108
+ line: 1,
109
+ code: `class D extends B { foo(id) {} }`,
110
+ }],
111
+ };
112
+ const violations = lspRule.check('test.ts', adapter as never);
113
+ expect(violations).toHaveLength(0);
114
+ });
115
+
116
+ it('ignores params with colon but no matchable type word', () => {
117
+ // foo(id: ) — colon present but typeMatch fails → returns null
118
+ const adapter = {
119
+ ...mockAdapter,
120
+ extractClasses: () => [{
121
+ name: 'D',
122
+ line: 1,
123
+ code: `class D extends B { foo(id: ) {} }`,
124
+ }],
125
+ };
126
+ const violations = lspRule.check('test.ts', adapter as never);
127
+ expect(violations).toHaveLength(0);
128
+ });
129
+
130
+ it('treats primitive parameter types as compatible', () => {
131
+ const adapter = {
132
+ ...mockAdapter,
133
+ extractClasses: () => [{
134
+ name: 'D',
135
+ line: 1,
136
+ code: `class D extends B { foo(s: string, n: number, b: boolean, a: any, v: void, x: Object) {} }`,
137
+ }],
138
+ };
139
+ const violations = lspRule.check('test.ts', adapter as never);
140
+ expect(violations).toHaveLength(0);
141
+ });
142
+
143
+ it('treats parameter types that start with className as compatible', () => {
144
+ // className = 'User', paramType = 'UserId' (startsWith match)
145
+ const adapter = {
146
+ ...mockAdapter,
147
+ extractClasses: () => [{
148
+ name: 'User',
149
+ line: 1,
150
+ code: `class User extends Base { findById(id: UserId) {} }`,
151
+ }],
152
+ };
153
+ const violations = lspRule.check('test.ts', adapter as never);
154
+ expect(violations).toHaveLength(0);
155
+ });
156
+
157
+ it('reports violation with className undefined (className && ... false branch)', () => {
158
+ // cls.name undefined → className && ... short-circuits to false → incompatible
159
+ const adapter = {
160
+ ...mockAdapter,
161
+ extractClasses: () => [{
162
+ line: 1,
163
+ code: `class X extends B { foo(id: CustomId) {} }`,
164
+ }],
165
+ };
166
+ const violations = lspRule.check('test.ts', adapter as never);
167
+ expect(violations.length).toBeGreaterThan(0);
168
+ expect(violations[0].ruleId).toBe('solid.lsp');
169
+ });
170
+
171
+ it('falls back to line 0 when cls.line is missing', () => {
172
+ const adapter = {
173
+ ...mockAdapter,
174
+ extractClasses: () => [{
175
+ name: 'D',
176
+ // no line
177
+ code: `class D extends B { foo(id: CustomId) {} }`,
178
+ }],
179
+ };
180
+ const violations = lspRule.check('test.ts', adapter as never);
181
+ expect(violations.length).toBeGreaterThan(0);
182
+ expect(violations[0].line).toBe(0);
183
+ });
184
+ });
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ocpRule } from '../../solid/ocp';
3
+
4
+ const mockAdapter = {
5
+ detectLanguage: () => 'typescript',
6
+ parseAST: () => undefined,
7
+ extractFunctions: () => [],
8
+ extractClasses: () => [],
9
+ countLines: () => 0
10
+ };
11
+
12
+ describe('ocp.ts - Open/Closed Principle Rule', () => {
13
+ it('should detect possible base class modification', () => {
14
+ const mockAdapterWithModification = {
15
+ ...mockAdapter,
16
+ extractClasses: () => [{
17
+ name: 'DerivedClass',
18
+ line: 1,
19
+ code: `
20
+ class BaseClass {
21
+ method() {}
22
+ }
23
+ class DerivedClass extends BaseClass {
24
+ method() {}
25
+ }
26
+ `
27
+ }]
28
+ };
29
+
30
+ const violations = ocpRule.check('test.ts', mockAdapterWithModification as never);
31
+
32
+ expect(violations.length).toBeGreaterThan(0);
33
+ expect(violations[0].ruleId).toBe('solid.ocp');
34
+ });
35
+
36
+ it('should pass for proper extension', () => {
37
+ const mockAdapterWithProperExtension = {
38
+ ...mockAdapter,
39
+ extractClasses: () => [{
40
+ name: 'DerivedClass',
41
+ line: 1,
42
+ code: `
43
+ class DerivedClass extends BaseClass {
44
+ newMethod() {}
45
+ }
46
+ `
47
+ }]
48
+ };
49
+
50
+ const violations = ocpRule.check('test.ts', mockAdapterWithProperExtension as never);
51
+
52
+ expect(violations.length).toBe(0);
53
+ });
54
+
55
+ it('should use severity from config', () => {
56
+ expect(ocpRule.severity).toBe('info');
57
+ });
58
+
59
+ it('should return empty violations when adapter throws error', () => {
60
+ const mockAdapterThatThrows = {
61
+ ...mockAdapter,
62
+ extractClasses: () => { throw new Error('Adapter failed'); }
63
+ };
64
+
65
+ const violations = ocpRule.check('test.ts', mockAdapterThatThrows as never);
66
+
67
+ expect(violations.length).toBe(0);
68
+ });
69
+ });
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { srpRule } from '../../solid/srp';
3
+
4
+ const mockAdapter = {
5
+ detectLanguage: () => 'typescript',
6
+ parseAST: () => undefined,
7
+ extractFunctions: () => [],
8
+ extractClasses: () => [],
9
+ countLines: () => 0
10
+ };
11
+
12
+ describe('srp.ts - Single Responsibility Principle Rule', () => {
13
+ it('should detect class with too many methods (>15)', () => {
14
+ const mockAdapterWithManyMethods = {
15
+ ...mockAdapter,
16
+ extractClasses: () => [{
17
+ name: 'MultiResponsibilityClass',
18
+ line: 1,
19
+ methodCount: 18,
20
+ imports: {
21
+ ui: ['Component', 'Styles'],
22
+ data: ['Repository', 'Model'],
23
+ business: ['Service', 'Utils']
24
+ }
25
+ }]
26
+ };
27
+
28
+ const violations = srpRule.check('test.ts', mockAdapterWithManyMethods as never);
29
+
30
+ expect(violations.length).toBeGreaterThan(0);
31
+ expect(violations[0].ruleId).toBe('solid.srp');
32
+ });
33
+
34
+ it('should detect import diversity (>3 categories)', () => {
35
+ const mockAdapterWithImportDiversity = {
36
+ ...mockAdapter,
37
+ extractClasses: () => [{
38
+ name: 'CrossDomainClass',
39
+ line: 1,
40
+ methodCount: 8,
41
+ imports: {
42
+ ui: ['Component'],
43
+ data: ['Repository'],
44
+ business: ['Service'],
45
+ infrastructure: ['Config']
46
+ }
47
+ }]
48
+ };
49
+
50
+ const violations = srpRule.check('test.ts', mockAdapterWithImportDiversity as never);
51
+
52
+ expect(violations.length).toBeGreaterThan(0);
53
+ expect(violations[0].message).toContain('import');
54
+ });
55
+
56
+ it('should pass for focused class', () => {
57
+ const mockAdapterWithFocusedClass = {
58
+ ...mockAdapter,
59
+ extractClasses: () => [{
60
+ name: 'FocusedClass',
61
+ line: 1,
62
+ methodCount: 8,
63
+ imports: {
64
+ data: ['Repository', 'Model']
65
+ }
66
+ }]
67
+ };
68
+
69
+ const violations = srpRule.check('test.ts', mockAdapterWithFocusedClass as never);
70
+
71
+ expect(violations.length).toBe(0);
72
+ });
73
+
74
+ it('should use threshold from config', () => {
75
+ expect(srpRule.threshold).toBe(15);
76
+ expect(srpRule.severity).toBe('warning');
77
+ });
78
+
79
+ it('should return empty violations when adapter throws error', () => {
80
+ const mockAdapterThatThrows = {
81
+ ...mockAdapter,
82
+ extractClasses: () => { throw new Error('Adapter failed'); }
83
+ };
84
+
85
+ const violations = srpRule.check('test.ts', mockAdapterThatThrows as never);
86
+
87
+ expect(violations.length).toBe(0);
88
+ });
89
+ });
@@ -0,0 +1,31 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ export const codeDuplicationRule: Rule = {
7
+ id: 'clean-code.code-duplication',
8
+ name: 'Code Duplication Rule',
9
+ threshold: config.rules['clean-code']['code-duplication'].threshold ?? 15,
10
+ severity: config.rules['clean-code']['code-duplication'].severity as Severity,
11
+ check: (file: string, adapter: unknown): Violation[] => {
12
+ const violations: Violation[] = [];
13
+
14
+ try {
15
+ const typedAdapter = adapter as { duplicationPercentage?: number };
16
+ const duplicationPercentage = typedAdapter.duplicationPercentage;
17
+
18
+ if (duplicationPercentage && duplicationPercentage > (config.rules['clean-code']['code-duplication'].threshold as number)) {
19
+ violations.push({
20
+ file,
21
+ line: 1,
22
+ ruleId: 'clean-code.code-duplication',
23
+ message: `Code duplication detected: ${duplicationPercentage}% (threshold: ${config.rules['clean-code']['code-duplication'].threshold}%). Consider refactoring duplicated code.`,
24
+ severity: config.rules['clean-code']['code-duplication'].severity as Severity
25
+ });
26
+ }
27
+ } catch { }
28
+
29
+ return violations;
30
+ }
31
+ };
@@ -0,0 +1,38 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ export const deepNestingRule: Rule = {
7
+ id: 'clean-code.deep-nesting',
8
+ name: 'Deep Nesting Rule',
9
+ threshold: config.rules['clean-code']['deep-nesting'].threshold ?? 4,
10
+ severity: config.rules['clean-code']['deep-nesting'].severity as Severity,
11
+ check: (file: string, adapter: unknown): Violation[] => {
12
+ const violations: Violation[] = [];
13
+
14
+ try {
15
+ interface TypedAdapter {
16
+ extractFunctions?: () => Array<{name?: string, nestingDepth?: number, startLine?: number, line?: number}> | undefined;
17
+ }
18
+ const typedAdapter = adapter as TypedAdapter;
19
+ const functions = typedAdapter.extractFunctions?.() || [];
20
+
21
+ for (const func of functions) {
22
+ if (func.nestingDepth && func.nestingDepth > (config.rules['clean-code']['deep-nesting'].threshold as number)) {
23
+ violations.push({
24
+ file,
25
+ line: func.startLine ?? func.line ?? 1,
26
+ ruleId: 'clean-code.deep-nesting',
27
+ message: `Function "${func.name}" has deep nesting: ${func.nestingDepth} levels (maximum: ${
28
+ config.rules['clean-code']['deep-nesting'].threshold
29
+ })`,
30
+ severity: config.rules['clean-code']['deep-nesting'].severity as Severity
31
+ });
32
+ }
33
+ }
34
+ } catch { }
35
+
36
+ return violations;
37
+ }
38
+ };
@@ -0,0 +1,54 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ export const godClassRule: Rule = {
7
+ id: 'clean-code.god-class',
8
+ name: 'God Class Rule',
9
+ threshold: config.rules['clean-code']['god-class'].threshold ?? 15,
10
+ severity: config.rules['clean-code']['god-class'].severity as Severity,
11
+ check: (file: string, adapter: unknown): Violation[] => {
12
+ const violations: Violation[] = [];
13
+
14
+ try {
15
+ interface ClassObject {
16
+ code?: string;
17
+ line: number;
18
+ name: string;
19
+ }
20
+
21
+ interface TypedAdapter {
22
+ extractClasses?: () => ClassObject[] | undefined;
23
+ }
24
+
25
+ const typedAdapter = adapter as TypedAdapter;
26
+ const classes = typedAdapter.extractClasses?.() || [];
27
+
28
+ for (const cls of classes) {
29
+ const methodMatches = cls.code?.match(/(get|set)\s+\w+\s*\(|\w+\s*\([^)]*\)\s*{/g) || [];
30
+
31
+ const methodCount = methodMatches.filter((m: string) => {
32
+ if (m.startsWith('get ') || m.startsWith('set ')) {
33
+ return false;
34
+ }
35
+ return true;
36
+ }).length;
37
+
38
+ if (methodCount > (config.rules['clean-code']['god-class'].threshold as number)) {
39
+ violations.push({
40
+ file,
41
+ line: cls.line,
42
+ ruleId: 'clean-code.god-class',
43
+ message: `Class "${cls.name}" has too many methods: ${methodCount} (maximum: ${
44
+ config.rules['clean-code']['god-class'].threshold
45
+ })`,
46
+ severity: config.rules['clean-code']['god-class'].severity as Severity
47
+ });
48
+ }
49
+ }
50
+ } catch { }
51
+
52
+ return violations;
53
+ }
54
+ };
@@ -0,0 +1,34 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ export const largeFileRule: Rule = {
7
+ id: 'clean-code.large-file',
8
+ name: 'Large File Rule',
9
+ threshold: config.rules['clean-code']['large-file'].threshold ?? 500,
10
+ severity: config.rules['clean-code']['large-file'].severity as Severity,
11
+ check: (file: string, adapter: unknown): Violation[] => {
12
+ const violations: Violation[] = [];
13
+
14
+ try {
15
+ interface TypedAdapter {
16
+ countLines?: (file: string) => number | undefined;
17
+ }
18
+ const typedAdapter = adapter as TypedAdapter;
19
+ const lineCount = typedAdapter.countLines?.(file) ?? 0;
20
+
21
+ if (lineCount > (config.rules['clean-code']['large-file'].threshold as number)) {
22
+ violations.push({
23
+ file,
24
+ line: 1,
25
+ ruleId: 'clean-code.large-file',
26
+ message: `File is too large: ${lineCount} lines (maximum: ${config.rules['clean-code']['large-file'].threshold})`,
27
+ severity: config.rules['clean-code']['large-file'].severity as Severity
28
+ });
29
+ }
30
+ } catch { }
31
+
32
+ return violations;
33
+ }
34
+ };
@@ -0,0 +1,47 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ export const longFunctionRule: Rule = {
7
+ id: 'clean-code.long-function',
8
+ name: 'Long Function Rule',
9
+ threshold: config.rules['clean-code']['long-function'].threshold ?? 50,
10
+ severity: config.rules['clean-code']['long-function'].severity as Severity,
11
+ check: (file: string, adapter: unknown): Violation[] => {
12
+ const violations: Violation[] = [];
13
+
14
+ try {
15
+ interface FunctionObj {
16
+ name: string;
17
+ startLine: number;
18
+ length: number;
19
+ }
20
+
21
+ interface TypedAdapter {
22
+ extractFunctions?: () => FunctionObj[] | undefined;
23
+ }
24
+
25
+ const typedAdapter = adapter as TypedAdapter;
26
+ const functions = typedAdapter.extractFunctions?.() || [];
27
+
28
+ for (const func of functions) {
29
+ const { name, startLine, length } = func;
30
+
31
+ if (length > (config.rules['clean-code']['long-function'].threshold as number)) {
32
+ violations.push({
33
+ file,
34
+ line: startLine ?? 1,
35
+ ruleId: 'clean-code.long-function',
36
+ message: `Function "${name}" is too long: ${length} lines (maximum: ${
37
+ config.rules['clean-code']['long-function'].threshold
38
+ })`,
39
+ severity: config.rules['clean-code']['long-function'].severity as Severity
40
+ });
41
+ }
42
+ }
43
+ } catch { }
44
+
45
+ return violations;
46
+ }
47
+ };
@@ -0,0 +1,59 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ const EXCLUDED_NUMBERS = config.rules['clean-code']['magic-numbers'].exclude || [0, 1, -1, 2, 10, 100, 1000, 60, 24, 7, 30, 365, 256, 1024];
7
+
8
+ export const magicNumbersRule: Rule = {
9
+ id: 'clean-code.magic-numbers',
10
+ name: 'Magic Numbers Rule',
11
+ threshold: 10,
12
+ severity: config.rules['clean-code']['magic-numbers'].severity as Severity,
13
+ check: (file: string, adapter: unknown): Violation[] => {
14
+ const violations: Violation[] = [];
15
+
16
+ try {
17
+ interface NumberObject {
18
+ value: number;
19
+ line: number;
20
+ }
21
+
22
+ interface TypedAdapter {
23
+ parseAST?: () => unknown | undefined;
24
+ extract?: () => NumberObject[] | undefined;
25
+ }
26
+
27
+ const typedAdapter = adapter as TypedAdapter;
28
+
29
+ let magicNumbers: NumberObject[] = [];
30
+
31
+ try {
32
+ if (typeof typedAdapter.extract !== 'undefined') {
33
+ const literals = typedAdapter.extract?.();
34
+ if (literals && Array.isArray(literals)) {
35
+ magicNumbers = literals;
36
+ }
37
+ }
38
+ } catch { }
39
+
40
+ const filteredNumbers = magicNumbers.filter(numObj => {
41
+ const numValue = numObj.value;
42
+ return !EXCLUDED_NUMBERS.includes(numValue);
43
+ });
44
+
45
+ filteredNumbers.forEach(numObj => {
46
+ violations.push({
47
+ file,
48
+ line: numObj.line,
49
+ ruleId: 'clean-code.magic-numbers',
50
+ message: `Potential magic number detected: ${numObj.value}. Consider using a named constant instead.`,
51
+ severity: config.rules['clean-code']['magic-numbers'].severity as Severity
52
+ });
53
+ });
54
+
55
+ } catch { }
56
+
57
+ return violations;
58
+ }
59
+ };
@@ -0,0 +1,40 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ export const manyExportsRule: Rule = {
7
+ id: 'clean-code.many-exports',
8
+ name: 'Many Exports Rule',
9
+ threshold: config.rules['clean-code']['many-exports'].threshold ?? 10,
10
+ severity: config.rules['clean-code']['many-exports'].severity as Severity,
11
+ check: (file: string, adapter: unknown): Violation[] => {
12
+ const violations: Violation[] = [];
13
+
14
+ try {
15
+ interface ExportObj {
16
+ line: number;
17
+ }
18
+
19
+ interface TypedAdapter {
20
+ extractExports?: () => ExportObj[] | undefined;
21
+ }
22
+
23
+ const typedAdapter = adapter as TypedAdapter;
24
+ const exports = typedAdapter.extractExports ? typedAdapter.extractExports() : [];
25
+ const threshold = config.rules['clean-code']['many-exports'].threshold ?? 10;
26
+
27
+ if (exports && exports.length > threshold) {
28
+ violations.push({
29
+ file,
30
+ line: exports[0]?.line || 1,
31
+ ruleId: 'clean-code.many-exports',
32
+ message: `Module has too many exports: ${exports.length} (maximum: ${threshold}). Consider splitting into focused sub-modules.`,
33
+ severity: config.rules['clean-code']['many-exports'].severity as Severity
34
+ });
35
+ }
36
+ } catch { }
37
+
38
+ return violations;
39
+ }
40
+ };
@@ -0,0 +1,33 @@
1
+ import { Rule, Violation, Severity } from '../../types';
2
+ import { getDefaultConfig } from '../../config';
3
+
4
+ const config = getDefaultConfig();
5
+
6
+ export const missingErrorHandlingRule: Rule = {
7
+ id: 'clean-code.missing-error-handling',
8
+ name: 'Missing Error Handling Rule',
9
+ threshold: 1,
10
+ severity: config.rules['clean-code']['missing-error-handling'].severity as Severity,
11
+ check: (file: string, adapter: unknown): Violation[] => {
12
+ const violations: Violation[] = [];
13
+
14
+ try {
15
+ const typedAdapter = adapter as { extractFunctions?: () => Array<{name?: string; startLine?: number; line?: number; ioOperations?: string[]; hasTryCatch?: boolean;}> | undefined };
16
+ const functions = typedAdapter.extractFunctions?.() || [];
17
+
18
+ for (const func of functions) {
19
+ if (func.ioOperations && func.ioOperations.length > 0 && !func.hasTryCatch) {
20
+ violations.push({
21
+ file,
22
+ line: func.startLine ?? func.line ?? 1,
23
+ ruleId: 'clean-code.missing-error-handling',
24
+ message: `Function "${func.name}" has IO operations (${func.ioOperations.join(', ')}) without error handling`,
25
+ severity: config.rules['clean-code']['missing-error-handling'].severity as Severity
26
+ });
27
+ }
28
+ }
29
+ } catch { }
30
+
31
+ return violations;
32
+ }
33
+ };