@eddacraft/anvil-runtime 0.1.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.
Files changed (170) hide show
  1. package/LICENSE +14 -0
  2. package/dist/cache/cache-key.d.ts +45 -0
  3. package/dist/cache/cache-key.d.ts.map +1 -0
  4. package/dist/cache/cache-key.js +135 -0
  5. package/dist/cache/index.d.ts +27 -0
  6. package/dist/cache/index.d.ts.map +1 -0
  7. package/dist/cache/index.js +38 -0
  8. package/dist/cache/providers/file-cache.d.ts +63 -0
  9. package/dist/cache/providers/file-cache.d.ts.map +1 -0
  10. package/dist/cache/providers/file-cache.js +369 -0
  11. package/dist/cache/providers/memory-cache.d.ts +52 -0
  12. package/dist/cache/providers/memory-cache.d.ts.map +1 -0
  13. package/dist/cache/providers/memory-cache.js +197 -0
  14. package/dist/cache/providers/null-cache.d.ts +26 -0
  15. package/dist/cache/providers/null-cache.d.ts.map +1 -0
  16. package/dist/cache/providers/null-cache.js +50 -0
  17. package/dist/cache/types.d.ts +114 -0
  18. package/dist/cache/types.d.ts.map +1 -0
  19. package/dist/cache/types.js +4 -0
  20. package/dist/concurrency/agent.d.ts +137 -0
  21. package/dist/concurrency/agent.d.ts.map +1 -0
  22. package/dist/concurrency/agent.js +440 -0
  23. package/dist/concurrency/atomic.d.ts +93 -0
  24. package/dist/concurrency/atomic.d.ts.map +1 -0
  25. package/dist/concurrency/atomic.js +281 -0
  26. package/dist/concurrency/git-agent.d.ts +114 -0
  27. package/dist/concurrency/git-agent.d.ts.map +1 -0
  28. package/dist/concurrency/git-agent.js +313 -0
  29. package/dist/concurrency/index.d.ts +95 -0
  30. package/dist/concurrency/index.d.ts.map +1 -0
  31. package/dist/concurrency/index.js +127 -0
  32. package/dist/concurrency/lock-manager.d.ts +170 -0
  33. package/dist/concurrency/lock-manager.d.ts.map +1 -0
  34. package/dist/concurrency/lock-manager.js +525 -0
  35. package/dist/concurrency/queue-manager.d.ts +166 -0
  36. package/dist/concurrency/queue-manager.d.ts.map +1 -0
  37. package/dist/concurrency/queue-manager.js +442 -0
  38. package/dist/concurrency/types.d.ts +382 -0
  39. package/dist/concurrency/types.d.ts.map +1 -0
  40. package/dist/concurrency/types.js +204 -0
  41. package/dist/export/constraint-collector.d.ts +175 -0
  42. package/dist/export/constraint-collector.d.ts.map +1 -0
  43. package/dist/export/constraint-collector.js +203 -0
  44. package/dist/export/formatters/llms-txt-formatter.d.ts +89 -0
  45. package/dist/export/formatters/llms-txt-formatter.d.ts.map +1 -0
  46. package/dist/export/formatters/llms-txt-formatter.js +249 -0
  47. package/dist/export/formatters/mcp-resource-formatter.d.ts +186 -0
  48. package/dist/export/formatters/mcp-resource-formatter.d.ts.map +1 -0
  49. package/dist/export/formatters/mcp-resource-formatter.js +139 -0
  50. package/dist/export/formatters/prompt-formatter.d.ts +83 -0
  51. package/dist/export/formatters/prompt-formatter.d.ts.map +1 -0
  52. package/dist/export/formatters/prompt-formatter.js +256 -0
  53. package/dist/export/index.d.ts +10 -0
  54. package/dist/export/index.d.ts.map +1 -0
  55. package/dist/export/index.js +9 -0
  56. package/dist/gate/check.interface.d.ts +15 -0
  57. package/dist/gate/check.interface.d.ts.map +1 -0
  58. package/dist/gate/check.interface.js +18 -0
  59. package/dist/gate/checks/antipattern.check.d.ts +27 -0
  60. package/dist/gate/checks/antipattern.check.d.ts.map +1 -0
  61. package/dist/gate/checks/antipattern.check.js +140 -0
  62. package/dist/gate/checks/architecture/circular-detector.d.ts +33 -0
  63. package/dist/gate/checks/architecture/circular-detector.d.ts.map +1 -0
  64. package/dist/gate/checks/architecture/circular-detector.js +71 -0
  65. package/dist/gate/checks/architecture/dependency-analyzer.d.ts +81 -0
  66. package/dist/gate/checks/architecture/dependency-analyzer.d.ts.map +1 -0
  67. package/dist/gate/checks/architecture/dependency-analyzer.js +136 -0
  68. package/dist/gate/checks/architecture/layer-validator.d.ts +75 -0
  69. package/dist/gate/checks/architecture/layer-validator.d.ts.map +1 -0
  70. package/dist/gate/checks/architecture/layer-validator.js +193 -0
  71. package/dist/gate/checks/architecture.check.d.ts +56 -0
  72. package/dist/gate/checks/architecture.check.d.ts.map +1 -0
  73. package/dist/gate/checks/architecture.check.js +394 -0
  74. package/dist/gate/checks/command-safety.check.d.ts +12 -0
  75. package/dist/gate/checks/command-safety.check.d.ts.map +1 -0
  76. package/dist/gate/checks/command-safety.check.js +230 -0
  77. package/dist/gate/checks/coverage.check.d.ts +9 -0
  78. package/dist/gate/checks/coverage.check.d.ts.map +1 -0
  79. package/dist/gate/checks/coverage.check.js +81 -0
  80. package/dist/gate/checks/dependency.check.d.ts +17 -0
  81. package/dist/gate/checks/dependency.check.d.ts.map +1 -0
  82. package/dist/gate/checks/dependency.check.js +342 -0
  83. package/dist/gate/checks/eslint.check.d.ts +14 -0
  84. package/dist/gate/checks/eslint.check.d.ts.map +1 -0
  85. package/dist/gate/checks/eslint.check.js +79 -0
  86. package/dist/gate/checks/policy.check.d.ts +78 -0
  87. package/dist/gate/checks/policy.check.d.ts.map +1 -0
  88. package/dist/gate/checks/policy.check.js +457 -0
  89. package/dist/gate/checks/secret/entropy-detector.d.ts +44 -0
  90. package/dist/gate/checks/secret/entropy-detector.d.ts.map +1 -0
  91. package/dist/gate/checks/secret/entropy-detector.js +76 -0
  92. package/dist/gate/checks/secret/git-scanner.d.ts +36 -0
  93. package/dist/gate/checks/secret/git-scanner.d.ts.map +1 -0
  94. package/dist/gate/checks/secret/git-scanner.js +90 -0
  95. package/dist/gate/checks/secret/secret-patterns.d.ts +42 -0
  96. package/dist/gate/checks/secret/secret-patterns.d.ts.map +1 -0
  97. package/dist/gate/checks/secret/secret-patterns.js +137 -0
  98. package/dist/gate/checks/secret.check.d.ts +56 -0
  99. package/dist/gate/checks/secret.check.d.ts.map +1 -0
  100. package/dist/gate/checks/secret.check.js +245 -0
  101. package/dist/gate/config/command-safety-config.d.ts +5 -0
  102. package/dist/gate/config/command-safety-config.d.ts.map +1 -0
  103. package/dist/gate/config/command-safety-config.js +69 -0
  104. package/dist/gate/config/index.d.ts +2 -0
  105. package/dist/gate/config/index.d.ts.map +1 -0
  106. package/dist/gate/config/index.js +1 -0
  107. package/dist/gate/formatters/command-safety-formatter.d.ts +10 -0
  108. package/dist/gate/formatters/command-safety-formatter.d.ts.map +1 -0
  109. package/dist/gate/formatters/command-safety-formatter.js +64 -0
  110. package/dist/gate/formatters/index.d.ts +2 -0
  111. package/dist/gate/formatters/index.d.ts.map +1 -0
  112. package/dist/gate/formatters/index.js +1 -0
  113. package/dist/gate/gate-config.d.ts +44 -0
  114. package/dist/gate/gate-config.d.ts.map +1 -0
  115. package/dist/gate/gate-config.js +334 -0
  116. package/dist/gate/gate-runner.d.ts +160 -0
  117. package/dist/gate/gate-runner.d.ts.map +1 -0
  118. package/dist/gate/gate-runner.js +531 -0
  119. package/dist/gate/index.d.ts +20 -0
  120. package/dist/gate/index.d.ts.map +1 -0
  121. package/dist/gate/index.js +14 -0
  122. package/dist/gate/parsers/command-parser.d.ts +18 -0
  123. package/dist/gate/parsers/command-parser.d.ts.map +1 -0
  124. package/dist/gate/parsers/command-parser.js +363 -0
  125. package/dist/gate/parsers/index.d.ts +2 -0
  126. package/dist/gate/parsers/index.d.ts.map +1 -0
  127. package/dist/gate/parsers/index.js +1 -0
  128. package/dist/gate/policy/index.d.ts +12 -0
  129. package/dist/gate/policy/index.d.ts.map +1 -0
  130. package/dist/gate/policy/index.js +10 -0
  131. package/dist/gate/rules/default-filesystem-rules.d.ts +3 -0
  132. package/dist/gate/rules/default-filesystem-rules.d.ts.map +1 -0
  133. package/dist/gate/rules/default-filesystem-rules.js +201 -0
  134. package/dist/gate/rules/default-git-rules.d.ts +3 -0
  135. package/dist/gate/rules/default-git-rules.d.ts.map +1 -0
  136. package/dist/gate/rules/default-git-rules.js +192 -0
  137. package/dist/gate/rules/index.d.ts +5 -0
  138. package/dist/gate/rules/index.d.ts.map +1 -0
  139. package/dist/gate/rules/index.js +3 -0
  140. package/dist/gate/rules/rule-matcher.d.ts +27 -0
  141. package/dist/gate/rules/rule-matcher.d.ts.map +1 -0
  142. package/dist/gate/rules/rule-matcher.js +228 -0
  143. package/dist/gate/rules/types.d.ts +250 -0
  144. package/dist/gate/rules/types.d.ts.map +1 -0
  145. package/dist/gate/rules/types.js +1 -0
  146. package/dist/index.d.ts +19 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +35 -0
  149. package/dist/types/gate.types.d.ts +42 -0
  150. package/dist/types/gate.types.d.ts.map +1 -0
  151. package/dist/types/gate.types.js +94 -0
  152. package/dist/watch/debouncer.d.ts +90 -0
  153. package/dist/watch/debouncer.d.ts.map +1 -0
  154. package/dist/watch/debouncer.js +135 -0
  155. package/dist/watch/file-watcher.d.ts +73 -0
  156. package/dist/watch/file-watcher.d.ts.map +1 -0
  157. package/dist/watch/file-watcher.js +121 -0
  158. package/dist/watch/git-status.d.ts +98 -0
  159. package/dist/watch/git-status.d.ts.map +1 -0
  160. package/dist/watch/git-status.js +266 -0
  161. package/dist/watch/index.d.ts +16 -0
  162. package/dist/watch/index.d.ts.map +1 -0
  163. package/dist/watch/index.js +15 -0
  164. package/dist/watch/orchestrator.d.ts +113 -0
  165. package/dist/watch/orchestrator.d.ts.map +1 -0
  166. package/dist/watch/orchestrator.js +409 -0
  167. package/dist/watch/types.d.ts +190 -0
  168. package/dist/watch/types.d.ts.map +1 -0
  169. package/dist/watch/types.js +76 -0
  170. package/package.json +60 -0
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Prompt Fragment Formatter
3
+ *
4
+ * Formats Anvil constraints as copy-paste system prompt text for manual
5
+ * AI tool configuration. Optimised for Claude, ChatGPT, and similar tools.
6
+ *
7
+ * @module export/formatters/prompt-formatter
8
+ */
9
+ // =============================================================================
10
+ // Prompt Formatter
11
+ // =============================================================================
12
+ /**
13
+ * Formats constraints as system prompt text
14
+ */
15
+ export class PromptFormatter {
16
+ options;
17
+ constructor(options = {}) {
18
+ this.options = {
19
+ includeBoundaries: true,
20
+ includeLayers: true,
21
+ includeAntiPatterns: true,
22
+ includeConventions: true,
23
+ includeSuppressions: true,
24
+ concise: false,
25
+ ...options,
26
+ };
27
+ }
28
+ /**
29
+ * Format constraints as prompt text
30
+ *
31
+ * @param constraints - Constraints to format
32
+ * @returns System prompt text
33
+ */
34
+ format(constraints) {
35
+ const sections = [];
36
+ // Opening instruction
37
+ sections.push(this.formatOpening());
38
+ // Architecture boundaries
39
+ if (this.options.includeBoundaries && constraints.boundaries.length > 0) {
40
+ sections.push(this.formatBoundaries(constraints));
41
+ }
42
+ // Layer definitions
43
+ if (this.options.includeLayers && constraints.layers.length > 0) {
44
+ sections.push(this.formatLayers(constraints));
45
+ }
46
+ // Anti-patterns
47
+ if (this.options.includeAntiPatterns && constraints.antiPatterns.length > 0) {
48
+ sections.push(this.formatAntiPatterns(constraints));
49
+ }
50
+ // Conventions
51
+ if (this.options.includeConventions && constraints.conventions.length > 0) {
52
+ sections.push(this.formatConventions(constraints));
53
+ }
54
+ // Suppressions
55
+ if (this.options.includeSuppressions && constraints.suppressions.length > 0) {
56
+ sections.push(this.formatSuppressions(constraints));
57
+ }
58
+ return sections.join('\n\n');
59
+ }
60
+ /**
61
+ * Format opening instruction
62
+ */
63
+ formatOpening() {
64
+ if (this.options.concise) {
65
+ return ('When working on this codebase, follow these architecture rules and conventions. ' +
66
+ 'Flag violations during code generation.');
67
+ }
68
+ return ('This codebase has specific architecture boundaries, anti-patterns, and conventions that must be followed. ' +
69
+ 'When generating or modifying code:\n\n' +
70
+ '1. Respect all architectural boundaries and layer dependencies\n' +
71
+ '2. Avoid all listed anti-patterns\n' +
72
+ '3. Follow project conventions consistently\n' +
73
+ '4. If a boundary violation or anti-pattern is necessary, explain why and suggest alternatives first');
74
+ }
75
+ /**
76
+ * Format architecture boundaries section
77
+ */
78
+ formatBoundaries(constraints) {
79
+ const lines = ['**Architecture Boundaries**'];
80
+ if (!this.options.concise) {
81
+ lines.push('These boundaries define which layers can depend on each other:');
82
+ }
83
+ lines.push('');
84
+ for (const boundary of constraints.boundaries) {
85
+ if (this.options.concise) {
86
+ lines.push(`- Layer "${boundary.from}" must not depend on "${boundary.to}"`);
87
+ }
88
+ else {
89
+ lines.push(`- **${boundary.name}**: Layer "${boundary.from}" must not depend on "${boundary.to}"`);
90
+ lines.push(` ${boundary.message}`);
91
+ lines.push(` Severity: ${boundary.severity}`);
92
+ }
93
+ }
94
+ return lines.join('\n');
95
+ }
96
+ /**
97
+ * Format layer definitions section
98
+ */
99
+ formatLayers(constraints) {
100
+ const lines = ['**Layer Definitions**'];
101
+ if (!this.options.concise) {
102
+ lines.push('The codebase is organised into these architectural layers:');
103
+ }
104
+ lines.push('');
105
+ for (const layer of constraints.layers) {
106
+ if (this.options.concise) {
107
+ lines.push(`- **${layer.name}**: ${layer.patterns.join(', ')}`);
108
+ if (layer.dependsOn.length > 0) {
109
+ lines.push(` Can depend on: ${layer.dependsOn.join(', ')}`);
110
+ }
111
+ }
112
+ else {
113
+ lines.push(`- **${layer.name}**`);
114
+ if (layer.description) {
115
+ lines.push(` ${layer.description}`);
116
+ }
117
+ lines.push(` Files: ${layer.patterns.join(', ')}`);
118
+ if (layer.dependsOn.length > 0) {
119
+ lines.push(` Allowed dependencies: ${layer.dependsOn.join(', ')}`);
120
+ }
121
+ else {
122
+ lines.push(` No dependencies (leaf layer)`);
123
+ }
124
+ lines.push('');
125
+ }
126
+ }
127
+ return lines.join('\n');
128
+ }
129
+ /**
130
+ * Format anti-patterns section
131
+ */
132
+ formatAntiPatterns(constraints) {
133
+ const lines = ['**Forbidden Anti-patterns**'];
134
+ if (!this.options.concise) {
135
+ lines.push('Never introduce these code patterns:');
136
+ }
137
+ lines.push('');
138
+ // Group by category for better organisation
139
+ const byCategory = new Map();
140
+ for (const pattern of constraints.antiPatterns) {
141
+ if (!byCategory.has(pattern.category)) {
142
+ byCategory.set(pattern.category, []);
143
+ }
144
+ byCategory.get(pattern.category).push(pattern);
145
+ }
146
+ for (const [category, patterns] of byCategory) {
147
+ if (!this.options.concise) {
148
+ lines.push(`${this.formatCategoryName(category)}:`);
149
+ }
150
+ for (const pattern of patterns) {
151
+ if (this.options.concise) {
152
+ lines.push(`- ${pattern.name}: ${pattern.suggestion}`);
153
+ }
154
+ else {
155
+ lines.push(`- **${pattern.name}** (${pattern.id})`);
156
+ lines.push(` Problem: ${pattern.explanation}`);
157
+ lines.push(` Instead: ${pattern.suggestion}`);
158
+ lines.push('');
159
+ }
160
+ }
161
+ }
162
+ return lines.join('\n');
163
+ }
164
+ /**
165
+ * Format conventions section
166
+ */
167
+ formatConventions(constraints) {
168
+ const lines = ['**Project Conventions**'];
169
+ if (!this.options.concise) {
170
+ lines.push('Follow these conventions for consistency:');
171
+ }
172
+ lines.push('');
173
+ for (const convention of constraints.conventions) {
174
+ lines.push(`- **${this.formatCategoryName(convention.category)}**: ${convention.description}`);
175
+ if (!this.options.concise && convention.examples && convention.examples.length > 0) {
176
+ for (const example of convention.examples) {
177
+ lines.push(` • ${example}`);
178
+ }
179
+ }
180
+ }
181
+ return lines.join('\n');
182
+ }
183
+ /**
184
+ * Format active suppressions section
185
+ */
186
+ formatSuppressions(constraints) {
187
+ const lines = ['**Active Suppressions**'];
188
+ if (!this.options.concise) {
189
+ lines.push('These violations are intentionally suppressed. Do not flag or fix them:');
190
+ }
191
+ lines.push('');
192
+ // Group by pattern ID
193
+ const byPattern = new Map();
194
+ for (const suppression of constraints.suppressions) {
195
+ if (!byPattern.has(suppression.patternId)) {
196
+ byPattern.set(suppression.patternId, []);
197
+ }
198
+ byPattern.get(suppression.patternId).push(suppression);
199
+ }
200
+ for (const [patternId, suppressions] of byPattern) {
201
+ if (this.options.concise) {
202
+ const files = suppressions.map((s) => s.file).join(', ');
203
+ lines.push(`- ${patternId}: suppressed in ${files}`);
204
+ }
205
+ else {
206
+ lines.push(`${patternId}:`);
207
+ for (const suppression of suppressions) {
208
+ lines.push(`- **${suppression.file}** (${suppression.scope}): ${suppression.reason}`);
209
+ if (suppression.expiresAt) {
210
+ const expiresAtDate = new Date(suppression.expiresAt);
211
+ if (!Number.isNaN(expiresAtDate.getTime())) {
212
+ lines.push(` Expires: ${expiresAtDate.toISOString().slice(0, 10)}`);
213
+ }
214
+ else {
215
+ lines.push(` Expires: ${suppression.expiresAt}`);
216
+ }
217
+ }
218
+ }
219
+ lines.push('');
220
+ }
221
+ }
222
+ return lines.join('\n');
223
+ }
224
+ /**
225
+ * Format category name for display
226
+ */
227
+ formatCategoryName(category) {
228
+ return category
229
+ .split('-')
230
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
231
+ .join(' ');
232
+ }
233
+ }
234
+ // =============================================================================
235
+ // Convenience Functions
236
+ // =============================================================================
237
+ /**
238
+ * Format constraints as prompt fragment with default options
239
+ *
240
+ * @param constraints - Constraints to format
241
+ * @returns Prompt text
242
+ */
243
+ export function formatAsPrompt(constraints) {
244
+ const formatter = new PromptFormatter();
245
+ return formatter.format(constraints);
246
+ }
247
+ /**
248
+ * Format constraints as concise prompt fragment
249
+ *
250
+ * @param constraints - Constraints to format
251
+ * @returns Concise prompt text
252
+ */
253
+ export function formatAsConcisePrompt(constraints) {
254
+ const formatter = new PromptFormatter({ concise: true });
255
+ return formatter.format(constraints);
256
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Export module
3
+ *
4
+ * Public API for constraint collection and formatting
5
+ */
6
+ export * from './constraint-collector.js';
7
+ export * from './formatters/llms-txt-formatter.js';
8
+ export * from './formatters/mcp-resource-formatter.js';
9
+ export * from './formatters/prompt-formatter.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wCAAwC,CAAC;AACvD,cAAc,kCAAkC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Export module
3
+ *
4
+ * Public API for constraint collection and formatting
5
+ */
6
+ export * from './constraint-collector.js';
7
+ export * from './formatters/llms-txt-formatter.js';
8
+ export * from './formatters/mcp-resource-formatter.js';
9
+ export * from './formatters/prompt-formatter.js';
@@ -0,0 +1,15 @@
1
+ import { CheckContext, GateResult } from '../types/gate.types.js';
2
+ export interface Check {
3
+ name: string;
4
+ description: string;
5
+ run(context: CheckContext): Promise<GateResult>;
6
+ }
7
+ export declare abstract class BaseCheck implements Check {
8
+ abstract name: string;
9
+ abstract description: string;
10
+ abstract run(context: CheckContext): Promise<GateResult>;
11
+ protected createResult(passed: boolean, message: string, score?: number, details?: Record<string, unknown>, error?: string): GateResult;
12
+ protected createSuccess(message: string, score?: number, details?: Record<string, unknown>): GateResult;
13
+ protected createFailure(message: string, error?: string, details?: Record<string, unknown>): GateResult;
14
+ }
15
+ //# sourceMappingURL=check.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.interface.d.ts","sourceRoot":"","sources":["../../src/gate/check.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAElE,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACjD;AAED,8BAAsB,SAAU,YAAW,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAExD,SAAS,CAAC,YAAY,CACpB,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,KAAK,CAAC,EAAE,MAAM,GACb,UAAU;IAWb,SAAS,CAAC,aAAa,CACrB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,UAAU;IAIb,SAAS,CAAC,aAAa,CACrB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,UAAU;CAGd"}
@@ -0,0 +1,18 @@
1
+ export class BaseCheck {
2
+ createResult(passed, message, score, details, error) {
3
+ return {
4
+ check: this.name,
5
+ passed,
6
+ score,
7
+ message,
8
+ details,
9
+ error,
10
+ };
11
+ }
12
+ createSuccess(message, score, details) {
13
+ return this.createResult(true, message, score, details);
14
+ }
15
+ createFailure(message, error, details) {
16
+ return this.createResult(false, message, undefined, details, error);
17
+ }
18
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Anti-pattern Check - Detect AI escape hatches and code quality issues
3
+ *
4
+ * Detects:
5
+ * - ESLint disable comments (broad and rule-specific)
6
+ * - Type escapes (any, @ts-ignore, @ts-expect-error)
7
+ * - Empty catch blocks
8
+ * - Console statements in production code
9
+ */
10
+ import { BaseCheck } from '../check.interface.js';
11
+ import { CheckContext, GateResult } from '../../types/gate.types.js';
12
+ export interface AntipatternCheckConfig {
13
+ patterns?: string[];
14
+ includeOptIn?: boolean;
15
+ extensions?: string[];
16
+ severityThreshold?: 'error' | 'warning' | 'info';
17
+ }
18
+ export declare class AntipatternCheck extends BaseCheck {
19
+ name: string;
20
+ description: string;
21
+ run(context: CheckContext): Promise<GateResult>;
22
+ private parseConfig;
23
+ private isScannableFile;
24
+ private calculateScore;
25
+ private buildMessage;
26
+ }
27
+ //# sourceMappingURL=antipattern.check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"antipattern.check.d.ts","sourceRoot":"","sources":["../../../src/gate/checks/antipattern.check.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAuB,MAAM,2BAA2B,CAAC;AAY1F,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CAClD;AAeD,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,IAAI,SAAiB;IACrB,WAAW,SAA6D;IAElE,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAuErD,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,cAAc;IA2BtB,OAAO,CAAC,YAAY;CAsBrB"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Anti-pattern Check - Detect AI escape hatches and code quality issues
3
+ *
4
+ * Detects:
5
+ * - ESLint disable comments (broad and rule-specific)
6
+ * - Type escapes (any, @ts-ignore, @ts-expect-error)
7
+ * - Empty catch blocks
8
+ * - Console statements in production code
9
+ */
10
+ import { readFileSync } from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import { BaseCheck } from '../check.interface.js';
13
+ import { getFilesFromContext } from '../../types/gate.types.js';
14
+ import { scanFile } from '@eddacraft/anvil-core/antipattern';
15
+ import { createWarningResult, } from '@eddacraft/anvil-core/antipattern';
16
+ import { parseSeverity, createDebugger } from '@eddacraft/anvil-core';
17
+ import { DEFAULT_ANALYSABLE_EXTENSIONS } from '@eddacraft/anvil-core';
18
+ const log = createDebugger('check');
19
+ const DEFAULT_CONFIG = {
20
+ patterns: [],
21
+ includeOptIn: false,
22
+ extensions: DEFAULT_ANALYSABLE_EXTENSIONS,
23
+ severityThreshold: 'error',
24
+ };
25
+ const SEVERITY_PENALTIES = {
26
+ error: 15,
27
+ warning: 5,
28
+ info: 1,
29
+ };
30
+ export class AntipatternCheck extends BaseCheck {
31
+ name = 'antipattern';
32
+ description = 'Detect AI escape hatches and code quality anti-patterns';
33
+ async run(context) {
34
+ log(`antipattern check starting, workspace=${context.workspace_root}`);
35
+ const config = this.parseConfig(context.check_config);
36
+ log(`antipattern config: severityThreshold=${config.severityThreshold}, patterns=${config.patterns.length}, includeOptIn=${config.includeOptIn}`);
37
+ try {
38
+ const files = getFilesFromContext(context, {
39
+ filter: (f) => this.isScannableFile(f, config),
40
+ });
41
+ if (files.length === 0) {
42
+ log('antipattern check: no scannable files found, returning success');
43
+ return this.createSuccess('No files to scan for anti-patterns', 100, {
44
+ warnings: createWarningResult([], []),
45
+ filesScanned: 0,
46
+ });
47
+ }
48
+ log(`antipattern check: scanning ${files.length} files`);
49
+ const scanOptions = {
50
+ patterns: config.patterns.length > 0 ? config.patterns : undefined,
51
+ includeOptIn: config.includeOptIn,
52
+ };
53
+ const allWarnings = [];
54
+ const allPatternsChecked = new Set();
55
+ let filesScanned = 0;
56
+ for (const filePath of files) {
57
+ let content;
58
+ try {
59
+ content = readFileSync(filePath, 'utf-8');
60
+ }
61
+ catch {
62
+ continue;
63
+ }
64
+ const relativePath = path.isAbsolute(filePath)
65
+ ? path.relative(context.workspace_root, filePath)
66
+ : filePath;
67
+ const result = scanFile(relativePath, content, scanOptions);
68
+ allWarnings.push(...result.warnings);
69
+ result.patternsChecked.forEach((p) => allPatternsChecked.add(p));
70
+ filesScanned++;
71
+ }
72
+ const warningResult = createWarningResult(allWarnings, Array.from(allPatternsChecked));
73
+ const { score, passed } = this.calculateScore(allWarnings, config);
74
+ const message = this.buildMessage(warningResult, filesScanned, passed);
75
+ log(`antipattern check result: passed=${passed}, score=${score}, warnings=${allWarnings.length}, filesScanned=${filesScanned}`);
76
+ return this.createResult(passed, message, score, {
77
+ warnings: warningResult,
78
+ filesScanned,
79
+ patternsChecked: Array.from(allPatternsChecked),
80
+ });
81
+ }
82
+ catch (error) {
83
+ log(`antipattern check error: ${error instanceof Error ? error.message : 'Unknown error'}`);
84
+ return this.createFailure('Anti-pattern check failed unexpectedly', error instanceof Error ? error.message : 'Unknown error');
85
+ }
86
+ }
87
+ parseConfig(checkConfig) {
88
+ return {
89
+ patterns: Array.isArray(checkConfig.patterns)
90
+ ? checkConfig.patterns.filter((p) => typeof p === 'string')
91
+ : DEFAULT_CONFIG.patterns,
92
+ includeOptIn: checkConfig.includeOptIn === true,
93
+ extensions: Array.isArray(checkConfig.extensions)
94
+ ? checkConfig.extensions.filter((e) => typeof e === 'string')
95
+ : DEFAULT_CONFIG.extensions,
96
+ severityThreshold: parseSeverity(checkConfig.severityThreshold, DEFAULT_CONFIG.severityThreshold),
97
+ };
98
+ }
99
+ isScannableFile(filePath, config) {
100
+ return config.extensions.some((ext) => filePath.endsWith(ext));
101
+ }
102
+ calculateScore(warnings, config) {
103
+ const severityLevels = { error: 3, warning: 2, info: 1 };
104
+ const threshold = severityLevels[config.severityThreshold];
105
+ let totalPenalty = 0;
106
+ let hasBlockingWarning = false;
107
+ for (const warning of warnings) {
108
+ if (warning.suppressed)
109
+ continue;
110
+ const warningSeverity = severityLevels[warning.severity];
111
+ if (warningSeverity >= threshold) {
112
+ hasBlockingWarning = true;
113
+ }
114
+ totalPenalty += SEVERITY_PENALTIES[warning.severity] || 0;
115
+ }
116
+ const score = Math.max(0, 100 - totalPenalty);
117
+ const passed = !hasBlockingWarning;
118
+ return { score, passed };
119
+ }
120
+ buildMessage(result, filesScanned, passed) {
121
+ if (result.warnings.length === 0) {
122
+ return `Anti-pattern check passed: ${filesScanned} files scanned, no issues found`;
123
+ }
124
+ const parts = [];
125
+ if (result.summary.errors > 0) {
126
+ parts.push(`${result.summary.errors} error${result.summary.errors > 1 ? 's' : ''}`);
127
+ }
128
+ if (result.summary.warnings > 0) {
129
+ parts.push(`${result.summary.warnings} warning${result.summary.warnings > 1 ? 's' : ''}`);
130
+ }
131
+ if (result.summary.info > 0) {
132
+ parts.push(`${result.summary.info} info`);
133
+ }
134
+ if (result.summary.suppressed > 0) {
135
+ parts.push(`${result.summary.suppressed} suppressed`);
136
+ }
137
+ const status = passed ? 'passed with issues' : 'failed';
138
+ return `Anti-pattern check ${status}: ${parts.join(', ')} (${filesScanned} files scanned)`;
139
+ }
140
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Circular Detector - Detect and categorize circular dependencies
3
+ *
4
+ * Handles detection and categorization of various violation types from dependency-cruiser.
5
+ */
6
+ import type { CruiserViolation } from './dependency-analyzer.js';
7
+ import type { ArchitectureBaseline } from '@eddacraft/anvil-core/architecture';
8
+ /**
9
+ * Categorized violation types
10
+ */
11
+ export type ViolationType = 'circular' | 'orphan' | 'layer' | 'other';
12
+ /**
13
+ * Circular detector for analyzing violations
14
+ */
15
+ export declare class CircularDetector {
16
+ /**
17
+ * Categorize a violation by type
18
+ */
19
+ categoriseViolation(violation: CruiserViolation): ViolationType;
20
+ /**
21
+ * Check if a violation is new compared to baseline
22
+ */
23
+ isNewViolation(violation: CruiserViolation, baseline: ArchitectureBaseline): boolean;
24
+ /**
25
+ * Count violations by type
26
+ */
27
+ countViolationsByType(violations: CruiserViolation[]): Record<string, number>;
28
+ /**
29
+ * Filter violations to only new ones based on baseline
30
+ */
31
+ filterNewViolations(violations: CruiserViolation[], baseline: ArchitectureBaseline | null): CruiserViolation[];
32
+ }
33
+ //# sourceMappingURL=circular-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circular-detector.d.ts","sourceRoot":"","sources":["../../../../src/gate/checks/architecture/circular-detector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAK/E;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAEtE;;GAEG;AACH,qBAAa,gBAAgB;IAC3B;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,gBAAgB,GAAG,aAAa;IAa/D;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,oBAAoB,GAAG,OAAO;IAYpF;;OAEG;IACH,qBAAqB,CAAC,UAAU,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAiB7E;;OAEG;IACH,mBAAmB,CACjB,UAAU,EAAE,gBAAgB,EAAE,EAC9B,QAAQ,EAAE,oBAAoB,GAAG,IAAI,GACpC,gBAAgB,EAAE;CAYtB"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Circular Detector - Detect and categorize circular dependencies
3
+ *
4
+ * Handles detection and categorization of various violation types from dependency-cruiser.
5
+ */
6
+ import { createDebugger } from '@eddacraft/anvil-core';
7
+ const log = createDebugger('check');
8
+ /**
9
+ * Circular detector for analyzing violations
10
+ */
11
+ export class CircularDetector {
12
+ /**
13
+ * Categorize a violation by type
14
+ */
15
+ categoriseViolation(violation) {
16
+ if (violation.cycle && violation.cycle.length > 0) {
17
+ return 'circular';
18
+ }
19
+ if (violation.rule.name.includes('orphan')) {
20
+ return 'orphan';
21
+ }
22
+ if (violation.rule.name.includes('layer') || violation.rule.name.includes('boundary')) {
23
+ return 'layer';
24
+ }
25
+ return 'other';
26
+ }
27
+ /**
28
+ * Check if a violation is new compared to baseline
29
+ */
30
+ isNewViolation(violation, baseline) {
31
+ const baselineViolations = baseline.baseline_snapshot.violations;
32
+ return !baselineViolations.some((bv) => {
33
+ const filesMatch = bv.from_file === violation.from && bv.to_file === violation.to;
34
+ if (!filesMatch)
35
+ return false;
36
+ if (bv.rule) {
37
+ return bv.rule === violation.rule.name;
38
+ }
39
+ return true;
40
+ });
41
+ }
42
+ /**
43
+ * Count violations by type
44
+ */
45
+ countViolationsByType(violations) {
46
+ const violationsByType = {
47
+ circular: 0,
48
+ orphan: 0,
49
+ layer: 0,
50
+ other: 0,
51
+ };
52
+ for (const v of violations) {
53
+ const type = this.categoriseViolation(v);
54
+ violationsByType[type]++;
55
+ }
56
+ log('circular-detector: violation counts by type', { violationsByType });
57
+ return violationsByType;
58
+ }
59
+ /**
60
+ * Filter violations to only new ones based on baseline
61
+ */
62
+ filterNewViolations(violations, baseline) {
63
+ if (!baseline) {
64
+ log(`circular-detector: no baseline, all ${violations.length} violations treated as new`);
65
+ return violations;
66
+ }
67
+ const newViolations = violations.filter((v) => this.isNewViolation(v, baseline));
68
+ log(`circular-detector: filtered ${violations.length} violations to ${newViolations.length} new (baseline has ${baseline.baseline_snapshot.violations.length})`);
69
+ return newViolations;
70
+ }
71
+ }