@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,531 @@
1
+ import { createDebugger } from '@eddacraft/anvil-core';
2
+ import { createProvenanceRecord, createProvenanceStore, } from '@eddacraft/anvil-core';
3
+ const debug = createDebugger('gate');
4
+ import { ESLintCheck } from './checks/eslint.check.js';
5
+ import { CoverageCheck } from './checks/coverage.check.js';
6
+ import { SecretCheck } from './checks/secret.check.js';
7
+ import { DependencyCheck } from './checks/dependency.check.js';
8
+ import { PolicyCheck } from './checks/policy.check.js';
9
+ import { ArchitectureCheck } from './checks/architecture.check.js';
10
+ import { AntipatternCheck } from './checks/antipattern.check.js';
11
+ import { CommandSafetyCheck } from './checks/command-safety.check.js';
12
+ import { NullCacheProvider } from '../cache/providers/null-cache.js';
13
+ import { generateCacheKey, hashCheckConfig, generateInputHash } from '../cache/cache-key.js';
14
+ import { createWarningResult } from '@eddacraft/anvil-core/antipattern';
15
+ import { SuppressionService, } from '@eddacraft/anvil-core/suppression';
16
+ /**
17
+ * Creates a minimal valid PlanData object for use in planless/full-codebase
18
+ * gate runs where no real plan exists. Avoids unsafe double type-casts.
19
+ */
20
+ export function createEmptyPlan() {
21
+ return {
22
+ schema_version: '0.1.0',
23
+ id: 'aps-00000000',
24
+ hash: '0'.repeat(64),
25
+ intent: 'Full codebase quality gate run (no plan)',
26
+ proposed_changes: [],
27
+ provenance: {
28
+ timestamp: new Date().toISOString(),
29
+ source: 'automation',
30
+ version: '0.0.1',
31
+ },
32
+ validations: {
33
+ required_checks: [],
34
+ skip_checks: [],
35
+ },
36
+ };
37
+ }
38
+ export class GateRunner {
39
+ checks = new Map();
40
+ defaultCache = new NullCacheProvider();
41
+ constructor() {
42
+ this.registerDefaultChecks();
43
+ }
44
+ /**
45
+ * Set default cache provider
46
+ */
47
+ setDefaultCache(cache) {
48
+ this.defaultCache = cache;
49
+ }
50
+ registerCheck(check) {
51
+ this.checks.set(check.name, check);
52
+ }
53
+ unregisterCheck(name) {
54
+ this.checks.delete(name);
55
+ }
56
+ getAvailableChecks() {
57
+ return Array.from(this.checks.keys());
58
+ }
59
+ async analyzeFiles(files, workspaceRoot, options) {
60
+ const startTime = Date.now();
61
+ const cache = options?.noCache
62
+ ? new NullCacheProvider()
63
+ : (options?.cache ?? this.defaultCache);
64
+ const checksToRun = options?.checks ?? ['architecture', 'antipattern'];
65
+ const allWarnings = [];
66
+ const patternsChecked = [];
67
+ const checksRun = [];
68
+ const checkResults = [];
69
+ const minimalConfig = {
70
+ version: 1,
71
+ checks: [],
72
+ thresholds: { overall_score: 0 },
73
+ };
74
+ const context = {
75
+ workspace_root: workspaceRoot,
76
+ config: minimalConfig,
77
+ check_config: options?.checkConfig ?? {},
78
+ targetFiles: files,
79
+ };
80
+ const availableChecks = checksToRun.filter((name) => this.checks.has(name));
81
+ const runCheck = async (checkName) => {
82
+ const check = this.checks.get(checkName);
83
+ if (!check)
84
+ return;
85
+ const cacheKeyInput = {
86
+ check_name: checkName,
87
+ plan_hash: `files:${[...files].sort().join(',')}`,
88
+ config_hash: hashCheckConfig(options?.checkConfig ?? {}),
89
+ workspace_root: workspaceRoot,
90
+ };
91
+ const cacheKey = generateCacheKey(cacheKeyInput);
92
+ const inputHash = generateInputHash(cacheKeyInput);
93
+ let result;
94
+ try {
95
+ const cached = await cache.get(cacheKey);
96
+ if (cached && cached.input_hash === inputHash) {
97
+ result = cached.value;
98
+ }
99
+ }
100
+ catch (error) {
101
+ debug('Cache read failed during file analysis, continuing without cache', error);
102
+ }
103
+ if (!result) {
104
+ try {
105
+ result = await check.run(context);
106
+ if (!result.error) {
107
+ try {
108
+ await cache.set(cacheKey, result, { input_hash: inputHash });
109
+ }
110
+ catch (error) {
111
+ debug('Cache write failed during file analysis, continuing', error);
112
+ }
113
+ }
114
+ }
115
+ catch (error) {
116
+ result = {
117
+ check: checkName,
118
+ passed: false,
119
+ message: `Check '${checkName}' failed with error`,
120
+ error: error instanceof Error ? error.message : 'Unknown error',
121
+ };
122
+ }
123
+ }
124
+ // Only include checks that passed - failed checks don't contribute valid warnings
125
+ // Use `!result.error` instead if you need "completed" (regardless of pass/fail)
126
+ if (result && result.passed) {
127
+ checksRun.push(checkName);
128
+ }
129
+ // Track all check outcomes for provenance (passed and failed)
130
+ if (result) {
131
+ checkResults.push({ name: checkName, passed: result.passed, error: result.error });
132
+ }
133
+ const checkWarnings = result.details?.warnings;
134
+ if (checkWarnings) {
135
+ allWarnings.push(...checkWarnings.warnings);
136
+ // @ts-expect-error patterns_checked may exist on extended warning types
137
+ if (checkWarnings.patterns_checked)
138
+ patternsChecked.push(...checkWarnings.patterns_checked);
139
+ if (options?.onWarning) {
140
+ for (const warning of checkWarnings.warnings) {
141
+ options.onWarning(warning);
142
+ }
143
+ }
144
+ }
145
+ };
146
+ const parallelLimit = Math.max(1, options?.parallelLimit ?? availableChecks.length);
147
+ const queue = [...availableChecks];
148
+ const workers = Array.from({ length: Math.min(parallelLimit, queue.length) }, async () => {
149
+ while (queue.length > 0) {
150
+ const next = queue.shift();
151
+ if (!next)
152
+ break;
153
+ await runCheck(next);
154
+ }
155
+ });
156
+ await Promise.all(workers);
157
+ let processedWarnings = allWarnings;
158
+ let suppressionStats;
159
+ const suppressionsEnabled = options?.suppressions !== false;
160
+ if (suppressionsEnabled && files.length > 0) {
161
+ const suppressionService = new SuppressionService(workspaceRoot, options?.suppressionStore);
162
+ await suppressionService.initialize();
163
+ await suppressionService.processFiles(files);
164
+ processedWarnings = suppressionService.applyToAllWarnings(allWarnings);
165
+ suppressionStats = suppressionService.getStats(processedWarnings);
166
+ }
167
+ const warningResult = createWarningResult(processedWarnings, [...new Set(patternsChecked)]);
168
+ const hasBlocking = processedWarnings.some((w) => w.severity === 'error' && !w.suppressed);
169
+ const result = {
170
+ warnings: warningResult,
171
+ executionTimeMs: Date.now() - startTime,
172
+ checksRun,
173
+ hasBlockingWarnings: hasBlocking,
174
+ suppressionStats,
175
+ };
176
+ // Record provenance if enabled
177
+ if (options?.provenance?.enabled) {
178
+ try {
179
+ const passedCount = checkResults.filter((c) => c.passed).length;
180
+ const failedCount = checkResults.filter((c) => !c.passed).length;
181
+ const gateRunResult = {
182
+ overall: !hasBlocking,
183
+ score: hasBlocking ? 0 : 100,
184
+ checks: checkResults.map((c) => ({
185
+ check: c.name,
186
+ passed: c.passed,
187
+ message: c.error ?? `${c.name} analysis completed`,
188
+ })),
189
+ summary: {
190
+ total: checkResults.length,
191
+ passed: passedCount,
192
+ failed: failedCount,
193
+ skipped: 0,
194
+ },
195
+ };
196
+ const provenanceRecord = await createProvenanceRecord({
197
+ workspaceRoot,
198
+ filesChecked: files,
199
+ scope: options.provenance.scope,
200
+ results: gateRunResult,
201
+ trigger: options.provenance.trigger,
202
+ startTime,
203
+ });
204
+ const store = createProvenanceStore(workspaceRoot);
205
+ store.save(provenanceRecord);
206
+ result.provenance_id = provenanceRecord.id;
207
+ debug('Provenance record saved (analyze): %s', provenanceRecord.id);
208
+ }
209
+ catch (provError) {
210
+ debug('Failed to save provenance record in analyzeFiles (non-fatal)', provError);
211
+ }
212
+ }
213
+ options?.onResult?.(result);
214
+ return result;
215
+ }
216
+ async runGate(plan, config, workspaceRoot, options) {
217
+ const startTime = Date.now();
218
+ const cache = options?.noCache
219
+ ? new NullCacheProvider()
220
+ : (options?.cache ?? this.defaultCache);
221
+ const parallelLimit = options?.parallelLimit;
222
+ // Collect checks to run
223
+ const checksToRun = [];
224
+ const skippedResults = [];
225
+ for (const checkConfig of config.checks) {
226
+ // Apply skip/only filters
227
+ if (options?.skipChecks?.includes(checkConfig.name)) {
228
+ skippedResults.push({
229
+ check: checkConfig.name,
230
+ passed: true,
231
+ message: 'Check skipped via --skip-checks',
232
+ skipped: true,
233
+ });
234
+ continue;
235
+ }
236
+ if (options?.onlyChecks && !options.onlyChecks.includes(checkConfig.name)) {
237
+ skippedResults.push({
238
+ check: checkConfig.name,
239
+ passed: true,
240
+ message: 'Check not in --only-checks filter',
241
+ skipped: true,
242
+ });
243
+ continue;
244
+ }
245
+ if (!checkConfig.enabled) {
246
+ skippedResults.push({
247
+ check: checkConfig.name,
248
+ passed: true,
249
+ message: 'Check disabled',
250
+ skipped: true,
251
+ });
252
+ continue;
253
+ }
254
+ const check = this.checks.get(checkConfig.name);
255
+ if (!check) {
256
+ skippedResults.push({
257
+ check: checkConfig.name,
258
+ passed: false,
259
+ message: `Check '${checkConfig.name}' not found`,
260
+ error: 'Unknown check',
261
+ });
262
+ continue;
263
+ }
264
+ checksToRun.push({ checkConfig, check });
265
+ }
266
+ // Execute checks (parallel or sequential)
267
+ const { results, cacheStats, timing } = await this.executeChecks(checksToRun, plan, config, workspaceRoot, cache, parallelLimit, options?.failFast, options?.onProgress, options?.fullScan);
268
+ // Combine skipped and executed results
269
+ const allResults = [...skippedResults, ...results];
270
+ // Calculate scores
271
+ let totalScore = 0;
272
+ let validChecks = 0;
273
+ for (const result of allResults) {
274
+ if (result.score !== undefined && !result.skipped) {
275
+ totalScore += result.score;
276
+ validChecks++;
277
+ }
278
+ }
279
+ const overallScore = validChecks > 0 ? totalScore / validChecks : 100;
280
+ const passed = allResults.every((r) => r.passed || r.skipped);
281
+ const overallPassed = passed && (validChecks === 0 || overallScore >= (config.thresholds.overall_score || 80));
282
+ const summary = {
283
+ total: allResults.length,
284
+ passed: allResults.filter((r) => r.passed && !r.skipped).length,
285
+ failed: allResults.filter((r) => !r.passed && !r.skipped).length,
286
+ skipped: allResults.filter((r) => r.skipped).length,
287
+ };
288
+ const result = {
289
+ overall: overallPassed,
290
+ score: overallScore,
291
+ checks: allResults,
292
+ summary,
293
+ cacheStats,
294
+ timing: {
295
+ totalMs: Date.now() - startTime,
296
+ checks: timing,
297
+ },
298
+ };
299
+ // Record provenance if enabled
300
+ if (options?.provenance?.enabled) {
301
+ try {
302
+ const provenanceRecord = await createProvenanceRecord({
303
+ workspaceRoot,
304
+ filesChecked: options.provenance.filesChecked ?? [],
305
+ scope: options.provenance.scope,
306
+ results: result,
307
+ trigger: options.provenance.trigger,
308
+ startTime,
309
+ planId: options.provenance.planId,
310
+ });
311
+ const store = createProvenanceStore(workspaceRoot);
312
+ store.save(provenanceRecord);
313
+ result.provenance_id = provenanceRecord.id;
314
+ result.provenanceRecord = provenanceRecord;
315
+ debug(`Provenance record saved: ${provenanceRecord.id}`);
316
+ }
317
+ catch (provError) {
318
+ debug('Failed to save provenance record (non-fatal)', provError);
319
+ }
320
+ }
321
+ return result;
322
+ }
323
+ async executeChecks(checksToRun, plan, config, workspaceRoot, cache, parallelLimit, failFast, onProgress, fullScan) {
324
+ const results = [];
325
+ const cacheStats = { hits: 0, misses: 0, timeSavedMs: 0 };
326
+ const timing = {};
327
+ const totalChecks = checksToRun.length;
328
+ let completedCount = 0;
329
+ let architectureContext;
330
+ const archCheckIndex = checksToRun.findIndex((c) => c.checkConfig.name === 'architecture');
331
+ if (archCheckIndex > 0) {
332
+ const [archCheck] = checksToRun.splice(archCheckIndex, 1);
333
+ checksToRun.unshift(archCheck);
334
+ }
335
+ if (failFast || parallelLimit === 0) {
336
+ for (let i = 0; i < checksToRun.length; i++) {
337
+ const { checkConfig, check } = checksToRun[i];
338
+ onProgress?.({
339
+ type: 'check:start',
340
+ checkName: checkConfig.name,
341
+ current: i + 1,
342
+ total: totalChecks,
343
+ });
344
+ const { result, cached, executionTimeMs } = await this.runCheckWithCache(check, checkConfig, plan, config, workspaceRoot, cache, fullScan, architectureContext);
345
+ if (checkConfig.name === 'architecture' && result.details?.architectureContext) {
346
+ architectureContext = result.details.architectureContext;
347
+ }
348
+ results.push(result);
349
+ timing[checkConfig.name] = executionTimeMs;
350
+ completedCount++;
351
+ if (cached) {
352
+ cacheStats.hits++;
353
+ cacheStats.timeSavedMs += executionTimeMs;
354
+ }
355
+ else {
356
+ cacheStats.misses++;
357
+ }
358
+ onProgress?.({
359
+ type: result.error ? 'check:error' : 'check:complete',
360
+ checkName: checkConfig.name,
361
+ current: completedCount,
362
+ total: totalChecks,
363
+ result,
364
+ cached,
365
+ executionTimeMs,
366
+ });
367
+ if (failFast && !result.passed && !result.skipped) {
368
+ break;
369
+ }
370
+ }
371
+ return { results, cacheStats, timing };
372
+ }
373
+ const runCheckWithProgress = async (item, index) => {
374
+ onProgress?.({
375
+ type: 'check:start',
376
+ checkName: item.checkConfig.name,
377
+ current: index + 1,
378
+ total: totalChecks,
379
+ });
380
+ const { result, cached, executionTimeMs } = await this.runCheckWithCache(item.check, item.checkConfig, plan, config, workspaceRoot, cache, fullScan, architectureContext);
381
+ completedCount++;
382
+ onProgress?.({
383
+ type: result.error ? 'check:error' : 'check:complete',
384
+ checkName: item.checkConfig.name,
385
+ current: completedCount,
386
+ total: totalChecks,
387
+ result,
388
+ cached,
389
+ executionTimeMs,
390
+ });
391
+ return { checkConfig: item.checkConfig, result, cached, executionTimeMs };
392
+ };
393
+ const hasArchCheck = checksToRun.length > 0 && checksToRun[0].checkConfig.name === 'architecture';
394
+ let remainingChecks = checksToRun;
395
+ if (hasArchCheck) {
396
+ const archItem = checksToRun[0];
397
+ const archResult = await runCheckWithProgress(archItem, 0);
398
+ results.push(archResult.result);
399
+ timing[archResult.checkConfig.name] = archResult.executionTimeMs;
400
+ if (archResult.cached) {
401
+ cacheStats.hits++;
402
+ cacheStats.timeSavedMs += archResult.executionTimeMs;
403
+ }
404
+ else {
405
+ cacheStats.misses++;
406
+ }
407
+ if (archResult.result.details?.architectureContext) {
408
+ architectureContext = archResult.result.details.architectureContext;
409
+ }
410
+ remainingChecks = checksToRun.slice(1);
411
+ }
412
+ if (remainingChecks.length === 0) {
413
+ return { results, cacheStats, timing };
414
+ }
415
+ const startIndex = hasArchCheck ? 1 : 0;
416
+ if (parallelLimit === undefined || parallelLimit >= remainingChecks.length) {
417
+ const checkResults = await Promise.all(remainingChecks.map((item, index) => runCheckWithProgress(item, startIndex + index)));
418
+ for (const { checkConfig, result, cached, executionTimeMs } of checkResults) {
419
+ results.push(result);
420
+ timing[checkConfig.name] = executionTimeMs;
421
+ if (cached) {
422
+ cacheStats.hits++;
423
+ cacheStats.timeSavedMs += executionTimeMs;
424
+ }
425
+ else {
426
+ cacheStats.misses++;
427
+ }
428
+ }
429
+ }
430
+ else {
431
+ let batchStartIndex = startIndex;
432
+ for (let i = 0; i < remainingChecks.length; i += parallelLimit) {
433
+ const batch = remainingChecks.slice(i, i + parallelLimit);
434
+ const batchResults = await Promise.all(batch.map((item, batchIndex) => runCheckWithProgress(item, batchStartIndex + batchIndex)));
435
+ for (const { checkConfig, result, cached, executionTimeMs } of batchResults) {
436
+ results.push(result);
437
+ timing[checkConfig.name] = executionTimeMs;
438
+ if (cached) {
439
+ cacheStats.hits++;
440
+ cacheStats.timeSavedMs += executionTimeMs;
441
+ }
442
+ else {
443
+ cacheStats.misses++;
444
+ }
445
+ }
446
+ batchStartIndex += batch.length;
447
+ }
448
+ }
449
+ return { results, cacheStats, timing };
450
+ }
451
+ async runCheckWithCache(check, checkConfig, plan, gateConfig, workspaceRoot, cache, fullScan, architectureContext) {
452
+ const startTime = Date.now();
453
+ // Generate cache key
454
+ const cacheKeyInput = {
455
+ check_name: checkConfig.name,
456
+ plan_hash: plan.hash || 'no-hash',
457
+ config_hash: hashCheckConfig(checkConfig.config || {}),
458
+ workspace_root: workspaceRoot,
459
+ };
460
+ const cacheKey = generateCacheKey(cacheKeyInput);
461
+ const inputHash = generateInputHash(cacheKeyInput);
462
+ // Try to get from cache
463
+ try {
464
+ const cached = await cache.get(cacheKey);
465
+ if (cached && cached.input_hash === inputHash) {
466
+ return {
467
+ result: {
468
+ ...cached.value,
469
+ details: {
470
+ ...cached.value.details,
471
+ cached: true,
472
+ cached_at: cached.created_at,
473
+ },
474
+ },
475
+ cached: true,
476
+ executionTimeMs: Date.now() - startTime,
477
+ };
478
+ }
479
+ }
480
+ catch (error) {
481
+ debug('Cache read failed during gate run, continuing without cache', error);
482
+ }
483
+ // Run the check
484
+ try {
485
+ const context = {
486
+ plan,
487
+ workspace_root: workspaceRoot,
488
+ config: gateConfig,
489
+ check_config: checkConfig.config || {},
490
+ fullScan,
491
+ // Cast to base type - runtime ArchitectureContext is compatible
492
+ architectureContext: architectureContext,
493
+ };
494
+ const result = await check.run(context);
495
+ const executionTimeMs = Date.now() - startTime;
496
+ // Cache the result (only cache successful or failed results, not errors)
497
+ if (!result.error) {
498
+ try {
499
+ await cache.set(cacheKey, result, { input_hash: inputHash });
500
+ }
501
+ catch (error) {
502
+ debug('Cache write failed during gate run, continuing without caching', error);
503
+ }
504
+ }
505
+ return { result, cached: false, executionTimeMs };
506
+ }
507
+ catch (error) {
508
+ const errorResult = {
509
+ check: checkConfig.name,
510
+ passed: false,
511
+ message: `Check '${checkConfig.name}' failed with error`,
512
+ error: error instanceof Error ? error.message : 'Unknown error',
513
+ };
514
+ return {
515
+ result: errorResult,
516
+ cached: false,
517
+ executionTimeMs: Date.now() - startTime,
518
+ };
519
+ }
520
+ }
521
+ registerDefaultChecks() {
522
+ this.registerCheck(new ESLintCheck());
523
+ this.registerCheck(new CoverageCheck());
524
+ this.registerCheck(new SecretCheck());
525
+ this.registerCheck(new DependencyCheck());
526
+ this.registerCheck(new PolicyCheck());
527
+ this.registerCheck(new ArchitectureCheck());
528
+ this.registerCheck(new AntipatternCheck());
529
+ this.registerCheck(new CommandSafetyCheck());
530
+ }
531
+ }
@@ -0,0 +1,20 @@
1
+ export { GateRunner, createEmptyPlan } from './gate-runner.js';
2
+ export type { AnalyzeOptions, AnalyzeResult, GateRunOptions, GateRunResultWithCache, ProvenanceOptions, ProgressCallback, ProgressEvent, ProgressEventType, } from './gate-runner.js';
3
+ export { GateConfigManager } from './gate-config.js';
4
+ export type { ConfigLoadResult } from './gate-config.js';
5
+ export type { Check } from './check.interface.js';
6
+ export { BaseCheck } from './check.interface.js';
7
+ export { ESLintCheck } from './checks/eslint.check.js';
8
+ export { CoverageCheck } from './checks/coverage.check.js';
9
+ export { SecretCheck } from './checks/secret.check.js';
10
+ export { PolicyCheck } from './checks/policy.check.js';
11
+ export type { PolicyCheckConfig } from './checks/policy.check.js';
12
+ export { ArchitectureCheck } from './checks/architecture.check.js';
13
+ export type { ArchitectureCheckConfig } from './checks/architecture.check.js';
14
+ export { CommandSafetyCheck } from './checks/command-safety.check.js';
15
+ export type { CommandRule, CommandSafetyConfig, CommandSafetyFinding, CommandAnalysisSummary, ResolvedCommandSafetyConfig, } from './rules/types.js';
16
+ export { loadCommandSafetyRules, resolveCommandSafetyConfig, DEFAULT_COMMAND_SAFETY_CONFIG, } from './config/index.js';
17
+ export { formatBlockedCommands, formatWarningCommands, formatSummary } from './formatters/index.js';
18
+ export * from './policy/index.js';
19
+ export * from '../types/gate.types.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC/D,YAAY,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,YAAY,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGpG,cAAc,mBAAmB,CAAC;AAElC,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export { GateRunner, createEmptyPlan } from './gate-runner.js';
2
+ export { GateConfigManager } from './gate-config.js';
3
+ export { BaseCheck } from './check.interface.js';
4
+ export { ESLintCheck } from './checks/eslint.check.js';
5
+ export { CoverageCheck } from './checks/coverage.check.js';
6
+ export { SecretCheck } from './checks/secret.check.js';
7
+ export { PolicyCheck } from './checks/policy.check.js';
8
+ export { ArchitectureCheck } from './checks/architecture.check.js';
9
+ export { CommandSafetyCheck } from './checks/command-safety.check.js';
10
+ export { loadCommandSafetyRules, resolveCommandSafetyConfig, DEFAULT_COMMAND_SAFETY_CONFIG, } from './config/index.js';
11
+ export { formatBlockedCommands, formatWarningCommands, formatSummary } from './formatters/index.js';
12
+ // Policy module exports
13
+ export * from './policy/index.js';
14
+ export * from '../types/gate.types.js';
@@ -0,0 +1,18 @@
1
+ import type { ParsedCommand } from '../rules/types.js';
2
+ export declare function parseCommand(cmd: string): ParsedCommand;
3
+ export interface CompoundCommandResult {
4
+ isCompound: boolean;
5
+ commands: ParsedCommand[];
6
+ operators: string[];
7
+ }
8
+ export declare function parseCompoundCommand(cmd: string): CompoundCommandResult;
9
+ export declare class CommandParser {
10
+ parse(cmd: string): ParsedCommand;
11
+ parseCompound(cmd: string): CompoundCommandResult;
12
+ parseMultiple(commands: string[]): ParsedCommand[];
13
+ parseAllCommands(cmd: string): ParsedCommand[];
14
+ isWrapped(cmd: string): boolean;
15
+ isCompound(cmd: string): boolean;
16
+ getWrappers(cmd: string): string[];
17
+ }
18
+ //# sourceMappingURL=command-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-parser.d.ts","sourceRoot":"","sources":["../../../src/gate/parsers/command-parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AA2UvD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CA0CvD;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAkCvE;AAED,qBAAa,aAAa;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAIjC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB;IAIjD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE;IAIlD,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE;IAK9C,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAK/B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKhC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;CAInC"}