@gobing-ai/ts-rule-engine 0.3.1 → 0.3.3

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 (86) hide show
  1. package/README.md +328 -58
  2. package/dist/config/extensions.d.ts +10 -7
  3. package/dist/config/extensions.d.ts.map +1 -1
  4. package/dist/config/extensions.js +48 -23
  5. package/dist/config/loader.d.ts +7 -0
  6. package/dist/config/loader.d.ts.map +1 -1
  7. package/dist/config/loader.js +17 -12
  8. package/dist/engine.d.ts +13 -2
  9. package/dist/engine.d.ts.map +1 -1
  10. package/dist/engine.js +107 -45
  11. package/dist/evaluators/agent-detection-evaluator.d.ts.map +1 -1
  12. package/dist/evaluators/agent-detection-evaluator.js +2 -9
  13. package/dist/evaluators/coverage-gate-evaluator.d.ts.map +1 -1
  14. package/dist/evaluators/coverage-gate-evaluator.js +4 -5
  15. package/dist/evaluators/exit-code-evaluator.d.ts.map +1 -1
  16. package/dist/evaluators/exit-code-evaluator.js +6 -23
  17. package/dist/evaluators/file-utils.d.ts +25 -1
  18. package/dist/evaluators/file-utils.d.ts.map +1 -1
  19. package/dist/evaluators/file-utils.js +48 -8
  20. package/dist/evaluators/forbidden-import-evaluator.js +2 -10
  21. package/dist/evaluators/path-evaluator.d.ts.map +1 -1
  22. package/dist/evaluators/path-evaluator.js +5 -18
  23. package/dist/evaluators/regex-evaluator.js +3 -11
  24. package/dist/evaluators/ripgrep-evaluator.d.ts +50 -0
  25. package/dist/evaluators/ripgrep-evaluator.d.ts.map +1 -0
  26. package/dist/evaluators/ripgrep-evaluator.js +145 -0
  27. package/dist/evaluators/schema-artifact-evaluator.d.ts.map +1 -1
  28. package/dist/evaluators/schema-artifact-evaluator.js +3 -7
  29. package/dist/evaluators/sg-evaluator.d.ts +10 -2
  30. package/dist/evaluators/sg-evaluator.d.ts.map +1 -1
  31. package/dist/evaluators/sg-evaluator.js +21 -4
  32. package/dist/evaluators/test-location-evaluator.d.ts +2 -2
  33. package/dist/evaluators/test-location-evaluator.d.ts.map +1 -1
  34. package/dist/evaluators/test-location-evaluator.js +2 -15
  35. package/dist/events.d.ts +33 -0
  36. package/dist/events.d.ts.map +1 -0
  37. package/dist/events.js +0 -0
  38. package/dist/fixers/fixers.d.ts +1 -1
  39. package/dist/fixers/fixers.d.ts.map +1 -1
  40. package/dist/fixers/fixers.js +4 -5
  41. package/dist/fixers/test-stub-fixer.d.ts +1 -1
  42. package/dist/fixers/test-stub-fixer.d.ts.map +1 -1
  43. package/dist/fixers/test-stub-fixer.js +3 -4
  44. package/dist/host/builtins.d.ts.map +1 -1
  45. package/dist/host/builtins.js +5 -3
  46. package/dist/host/bundled-rules.d.ts +1 -1
  47. package/dist/host/bundled-rules.js +1 -1
  48. package/dist/host/rule-engine-host.d.ts +1 -1
  49. package/dist/host/rule-engine-host.d.ts.map +1 -1
  50. package/dist/host/rule-engine-host.js +1 -1
  51. package/dist/index.d.ts +3 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +7 -1
  54. package/dist/types.d.ts +2 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/types.js +6 -0
  57. package/package.json +4 -5
  58. package/rules/example.yaml +13 -0
  59. package/src/config/extensions.ts +58 -29
  60. package/src/config/loader.ts +27 -12
  61. package/src/engine.ts +132 -47
  62. package/src/evaluators/agent-detection-evaluator.ts +2 -8
  63. package/src/evaluators/coverage-gate-evaluator.ts +4 -5
  64. package/src/evaluators/exit-code-evaluator.ts +6 -23
  65. package/src/evaluators/file-utils.ts +70 -8
  66. package/src/evaluators/forbidden-import-evaluator.ts +2 -9
  67. package/src/evaluators/path-evaluator.ts +5 -18
  68. package/src/evaluators/regex-evaluator.ts +4 -11
  69. package/src/evaluators/ripgrep-evaluator.ts +167 -0
  70. package/src/evaluators/schema-artifact-evaluator.ts +3 -8
  71. package/src/evaluators/sg-evaluator.ts +21 -4
  72. package/src/evaluators/test-location-evaluator.ts +3 -16
  73. package/src/events.ts +13 -0
  74. package/src/fixers/fixers.ts +12 -6
  75. package/src/fixers/test-stub-fixer.ts +4 -5
  76. package/src/host/builtins.ts +5 -3
  77. package/src/host/bundled-rules.ts +1 -1
  78. package/src/host/rule-engine-host.ts +1 -1
  79. package/src/index.ts +8 -1
  80. package/src/types.ts +7 -0
  81. package/dist/host/capability-registry.d.ts +0 -10
  82. package/dist/host/capability-registry.d.ts.map +0 -1
  83. package/dist/host/capability-registry.js +0 -9
  84. package/rules/recommended.yaml +0 -10
  85. package/rules/spur-dev.yaml +0 -6
  86. package/src/host/capability-registry.ts +0 -9
@@ -15,6 +15,7 @@ export interface RuleLoaderOptions {
15
15
  /** Optional fetch implementation for remote HTTP(S) schema refs. */
16
16
  fetch?: (input: string) => Promise<Response>;
17
17
  }
18
+ /** Options for loading a single rule file directly from disk. */
18
19
  export interface RuleFileLoadOptions {
19
20
  /** When true, honor top-level `$schema` refs. Defaults to true. */
20
21
  validateSchema?: boolean;
@@ -36,6 +37,12 @@ export interface LoadedPreset {
36
37
  * the rest of a preset's categories from the lower-priority roots.
37
38
  */
38
39
  export declare function loadPreset(name: string, options: RuleLoaderOptions): Promise<LoadedPreset>;
40
+ /**
41
+ * Load a preset's resolved rules (convenience wrapper).
42
+ *
43
+ * Shortcut for `(await loadPreset(name, options)).rules` — returns only the
44
+ * normalized rule set without the extension module refs.
45
+ */
39
46
  export declare function loadPresetRules(name: string, options: RuleLoaderOptions): Promise<ConstraintRule[]>;
40
47
  /**
41
48
  * Load a direct rule file from disk.
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,cAAc,EAOtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEpE,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAC9B;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4FAA4F;IAC5F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oEAAoE;IACpE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,mBAAmB;IAChC,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oEAAoE;IACpE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD;AAED,mFAAmF;AACnF,MAAM,WAAW,YAAY;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC;IACjC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC;CACvC;AAUD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAahG;AAyCD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAEzG;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC,CAS7G"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAUA,OAAO,EACH,KAAK,cAAc,EAOtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEpE,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAC9B;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4FAA4F;IAC5F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oEAAoE;IACpE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD;AAED,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAChC,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oEAAoE;IACpE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD;AAED,mFAAmF;AACnF,MAAM,WAAW,YAAY;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC;IACjC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC;CACvC;AAUD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAahG;AAyCD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAEzG;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC,CAS7G"}
@@ -1,5 +1,4 @@
1
- import { basename, dirname, join, relative, resolve, sep } from 'node:path';
2
- import { loadStructuredConfig, NodeFileSystem } from '@gobing-ai/ts-runtime';
1
+ import { basenamePath, dirnamePath, joinPath, loadStructuredConfig, NodeFileSystem, relativePath, resolvePath, SEP, } from '@gobing-ai/ts-runtime';
3
2
  import { ConstraintRuleFileSchema, ConstraintRuleSchema, PresetDefinitionSchema, } from '../types.js';
4
3
  import { collectExtensions } from './extensions.js';
5
4
  /**
@@ -10,13 +9,13 @@ import { collectExtensions } from './extensions.js';
10
9
  * the rest of a preset's categories from the lower-priority roots.
11
10
  */
12
11
  export async function loadPreset(name, options) {
13
- const merged = await buildMergedRoots(options.roots.map((root) => resolve(root)));
12
+ const merged = await buildMergedRoots(options.roots.map((root) => resolvePath(root)));
14
13
  const presetPath = findMergedPreset(merged, name);
15
14
  if (presetPath === null)
16
15
  return { rules: [], extensions: [] };
17
16
  const preset = PresetDefinitionSchema.parse(await readStructuredFile(presetPath, options));
18
17
  const rules = [];
19
- const extensions = collectExtensions(preset.name, dirname(presetPath), preset.extensions);
18
+ const extensions = collectExtensions(preset.name, dirnamePath(presetPath), preset.extensions);
20
19
  for (const entry of preset.extends) {
21
20
  const loaded = await loadPresetEntry(merged, entry, new Set([name]), options);
22
21
  rules.push(...loaded.rules);
@@ -53,6 +52,12 @@ function assertFixModeNotPromoted(presetName, rule, overrideMode) {
53
52
  throw new Error(`Preset "${presetName}" override for rule "${rule.id}" raises fix mode from "${ruleMode}" to "${overrideMode}"; overrides may only lower fix authority`);
54
53
  }
55
54
  }
55
+ /**
56
+ * Load a preset's resolved rules (convenience wrapper).
57
+ *
58
+ * Shortcut for `(await loadPreset(name, options)).rules` — returns only the
59
+ * normalized rule set without the extension module refs.
60
+ */
56
61
  export async function loadPresetRules(name, options) {
57
62
  return (await loadPreset(name, options)).rules;
58
63
  }
@@ -66,12 +71,12 @@ export async function loadPresetRules(name, options) {
66
71
  * object, not a `rules:` array) cannot declare extensions and yields `extensions: []`.
67
72
  */
68
73
  export async function loadRuleFile(filePath, options = {}) {
69
- const resolved = resolve(filePath);
74
+ const resolved = resolvePath(filePath);
70
75
  const raw = await readStructuredFile(resolved, options);
71
76
  const rules = normalizeRuleFile(raw, resolved);
72
77
  const parsed = ConstraintRuleFileSchema.safeParse(raw);
73
78
  const extensions = parsed.success
74
- ? collectExtensions(basename(resolved), dirname(resolved), parsed.data.extensions)
79
+ ? collectExtensions(basenamePath(resolved), dirnamePath(resolved), parsed.data.extensions)
75
80
  : [];
76
81
  return { rules, extensions };
77
82
  }
@@ -86,7 +91,7 @@ async function loadPresetEntry(merged, entry, seen, options) {
86
91
  const preset = PresetDefinitionSchema.safeParse(await readStructuredFile(presetPath, options));
87
92
  if (preset.success) {
88
93
  const rules = [];
89
- const extensions = collectExtensions(preset.data.name, dirname(presetPath), preset.data.extensions);
94
+ const extensions = collectExtensions(preset.data.name, dirnamePath(presetPath), preset.data.extensions);
90
95
  for (const child of preset.data.extends) {
91
96
  const loaded = await loadPresetEntry(merged, child, nextSeen, options);
92
97
  rules.push(...loaded.rules);
@@ -127,7 +132,7 @@ async function buildMergedRoots(roots) {
127
132
  const categories = new Set();
128
133
  for (const root of roots) {
129
134
  for (const absPath of await walkYamlFiles(fs, root)) {
130
- const relPath = relative(root, absPath).split(sep).join('/');
135
+ const relPath = relativePath(root, absPath).split(SEP).join('/');
131
136
  const slashIdx = relPath.indexOf('/');
132
137
  if (slashIdx > 0)
133
138
  categories.add(relPath.slice(0, slashIdx));
@@ -181,7 +186,7 @@ async function walkYamlFiles(fs, dir, depth = 0) {
181
186
  for (const entry of (await fs.readDir(dir)).sort()) {
182
187
  if (depth === 0 && entry === 'presets')
183
188
  continue;
184
- const fullPath = join(dir, entry);
189
+ const fullPath = joinPath(dir, entry);
185
190
  const entryStat = await fs.stat(fullPath);
186
191
  if (entryStat?.isDirectory()) {
187
192
  acc.push(...(await walkYamlFiles(fs, fullPath, depth + 1)));
@@ -201,7 +206,7 @@ async function listImmediateDirs(fs, dir) {
201
206
  for (const entry of await fs.readDir(dir)) {
202
207
  if (entry === 'presets')
203
208
  continue;
204
- const entryStat = await fs.stat(join(dir, entry));
209
+ const entryStat = await fs.stat(joinPath(dir, entry));
205
210
  if (entryStat?.isDirectory())
206
211
  dirs.push(entry);
207
212
  }
@@ -214,7 +219,7 @@ async function readStructuredFile(path, options = {}) {
214
219
  });
215
220
  }
216
221
  function normalizeRuleFile(raw, filePath) {
217
- const sourceDir = dirname(filePath);
222
+ const sourceDir = dirnamePath(filePath);
218
223
  const maybeFile = ConstraintRuleFileSchema.safeParse(raw);
219
224
  if (maybeFile.success)
220
225
  return normalizeFileRules(maybeFile.data, sourceDir);
@@ -226,7 +231,7 @@ function normalizeRuleFile(raw, filePath) {
226
231
  // single-rule schema. Include field paths so the offending key is obvious.
227
232
  const isRuleFileShape = typeof raw === 'object' && raw !== null && 'rules' in raw;
228
233
  const issues = (isRuleFileShape ? maybeFile.error : maybeRule.error).issues;
229
- throw new Error(`Invalid rule file "${basename(filePath)}": ${formatIssues(issues)}`);
234
+ throw new Error(`Invalid rule file "${basenamePath(filePath)}": ${formatIssues(issues)}`);
230
235
  }
231
236
  /** Render Zod issues as `path: message` fragments for actionable diagnostics. */
232
237
  function formatIssues(issues) {
package/dist/engine.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import type { EventBus, Logger } from '@gobing-ai/ts-infra';
1
2
  import type { ProcessExecutor } from '@gobing-ai/ts-runtime';
3
+ import type { RuleEngineEvents } from './events';
2
4
  import { type FixApplicationResult } from './fixers/fixers';
3
5
  import { RuleEngineHost } from './host/rule-engine-host';
4
6
  import type { ConstraintRule, Fix, FixMode, RuleEngineResult, RuleEvaluator } from './types';
@@ -8,6 +10,10 @@ export interface RuleEngineOptions {
8
10
  processExecutor?: ProcessExecutor;
9
11
  /** Optional preconfigured host. */
10
12
  host?: RuleEngineHost;
13
+ /** Optional event bus for structured run observability (R-A4). */
14
+ events?: EventBus<RuleEngineEvents>;
15
+ /** Optional logger; defaults to the shared `rule-engine` category logger. */
16
+ logger?: Logger;
11
17
  }
12
18
  /** Orchestrates enabled constraint rules through a typed evaluator host. */
13
19
  export declare class RuleEngine {
@@ -15,6 +21,8 @@ export declare class RuleEngine {
15
21
  readonly host: RuleEngineHost;
16
22
  /** Fixer providers keyed by evaluator type. */
17
23
  private readonly fixers;
24
+ private readonly events;
25
+ private readonly logger;
18
26
  constructor(options?: RuleEngineOptions);
19
27
  /** Register or replace an evaluator. */
20
28
  registerEvaluator(type: string, evaluator: RuleEvaluator): void;
@@ -26,7 +34,7 @@ export declare class RuleEngine {
26
34
  * findings, never auto-generated fixes. Keeps the rule loop and error-finding
27
35
  * semantics in one place.
28
36
  */
29
- evaluate(rules: ConstraintRule[], workdir: string): Promise<RuleEngineResult>;
37
+ evaluate(rules: ConstraintRule[], workdir: string, stopOnFirst?: 'error' | 'warning' | 'info'): Promise<RuleEngineResult>;
30
38
  /**
31
39
  * Evaluate all enabled rules and collect candidate fixes.
32
40
  *
@@ -37,9 +45,12 @@ export declare class RuleEngine {
37
45
  * @param rules - Normalized rule definitions to evaluate.
38
46
  * @param workdir - Working directory to scan.
39
47
  * @param maxFixMode - Highest fix authority requested by the caller.
48
+ * @param stopOnFirst - When set, stop evaluating rules after the first rule
49
+ * whose findings meet/exceed this severity threshold. Undefined = exhaustive
50
+ * (today's behavior, zero breaking change).
40
51
  * @returns Findings plus fixes allowed by the requested authority.
41
52
  */
42
- evaluateWithFixes(rules: ConstraintRule[], workdir: string, maxFixMode?: FixMode): Promise<RuleEngineResult>;
53
+ evaluateWithFixes(rules: ConstraintRule[], workdir: string, maxFixMode?: FixMode, stopOnFirst?: 'error' | 'warning' | 'info'): Promise<RuleEngineResult>;
43
54
  /**
44
55
  * Apply or preview candidate byte-range fixes.
45
56
  *
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAKH,KAAK,oBAAoB,EAE5B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAqB,cAAc,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGhH,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAC9B,+DAA+D;IAC/D,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,mCAAmC;IACnC,IAAI,CAAC,EAAE,cAAc,CAAC;CACzB;AAED,4EAA4E;AAC5E,qBAAa,UAAU;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAE9B,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAE5C,OAAO,GAAE,iBAAsB;IAM3C,wCAAwC;IACxC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI;IAI/D;;;;;;;OAOG;IACG,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAInF;;;;;;;;;;;OAWG;IACG,iBAAiB,CACnB,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,OAAgB,GAC7B,OAAO,CAAC,gBAAgB,CAAC;IAkD5B;;;;;;;OAOG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAG1G"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAKH,KAAK,oBAAoB,EAE5B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAqB,cAAc,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGhH,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAC9B,+DAA+D;IAC/D,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,mCAAmC;IACnC,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,kEAAkE;IAClE,MAAM,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACpC,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,4EAA4E;AAC5E,qBAAa,UAAU;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAE9B,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAChE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,iBAAsB;IAQ3C,wCAAwC;IACxC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI;IAI/D;;;;;;;OAOG;IACG,QAAQ,CACV,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAC3C,OAAO,CAAC,gBAAgB,CAAC;IAI5B;;;;;;;;;;;;;;OAcG;IACG,iBAAiB,CACnB,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,OAAgB,EAC5B,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAC3C,OAAO,CAAC,gBAAgB,CAAC;IAoH5B;;;;;;;OAOG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAG1G"}
package/dist/engine.js CHANGED
@@ -1,17 +1,22 @@
1
+ import { addSpanEvent, getLogger, traceAsync } from '@gobing-ai/ts-infra';
1
2
  import { applyFixes as applyFixesImpl, builtInFixers, FIX_MODE_RANK, } from './fixers/fixers.js';
2
3
  import { registerBuiltins } from './host/builtins.js';
3
4
  import { RuleEngineHost } from './host/rule-engine-host.js';
4
- import { createFinding } from './types.js';
5
+ import { createFinding, SEVERITY_RANK } from './types.js';
5
6
  /** Orchestrates enabled constraint rules through a typed evaluator host. */
6
7
  export class RuleEngine {
7
8
  /** Capability host used by this engine. */
8
9
  host;
9
10
  /** Fixer providers keyed by evaluator type. */
10
11
  fixers;
12
+ events;
13
+ logger;
11
14
  constructor(options = {}) {
12
15
  this.host = options.host ?? new RuleEngineHost();
13
16
  registerBuiltins(this.host, options.processExecutor);
14
17
  this.fixers = builtInFixers(this.host, options.processExecutor);
18
+ this.events = options.events;
19
+ this.logger = options.logger ?? getLogger('rule-engine');
15
20
  }
16
21
  /** Register or replace an evaluator. */
17
22
  registerEvaluator(type, evaluator) {
@@ -25,8 +30,8 @@ export class RuleEngine {
25
30
  * findings, never auto-generated fixes. Keeps the rule loop and error-finding
26
31
  * semantics in one place.
27
32
  */
28
- async evaluate(rules, workdir) {
29
- return this.evaluateWithFixes(rules, workdir, 'none');
33
+ async evaluate(rules, workdir, stopOnFirst) {
34
+ return this.evaluateWithFixes(rules, workdir, 'none', stopOnFirst);
30
35
  }
31
36
  /**
32
37
  * Evaluate all enabled rules and collect candidate fixes.
@@ -38,52 +43,109 @@ export class RuleEngine {
38
43
  * @param rules - Normalized rule definitions to evaluate.
39
44
  * @param workdir - Working directory to scan.
40
45
  * @param maxFixMode - Highest fix authority requested by the caller.
46
+ * @param stopOnFirst - When set, stop evaluating rules after the first rule
47
+ * whose findings meet/exceed this severity threshold. Undefined = exhaustive
48
+ * (today's behavior, zero breaking change).
41
49
  * @returns Findings plus fixes allowed by the requested authority.
42
50
  */
43
- async evaluateWithFixes(rules, workdir, maxFixMode = 'auto') {
44
- const findings = [];
45
- const fixes = [];
46
- for (const rule of rules) {
47
- if (rule.enabled === false)
48
- continue;
49
- let ruleFindings = [];
50
- let ruleEvalFixes = [];
51
- try {
52
- const result = await this.host.evaluators.get(rule.evaluator.type).evaluate(rule, { rule, workdir });
53
- ruleFindings = result.findings;
54
- ruleEvalFixes = result.fixes;
55
- }
56
- catch (error) {
57
- ruleFindings = [
58
- createFinding(rule, error instanceof Error ? error.message : String(error), null, {
59
- code: `evaluator:${rule.evaluator.type}`,
60
- kind: 'error',
61
- }),
62
- ];
63
- }
64
- findings.push(...ruleFindings);
65
- fixes.push(...ruleEvalFixes);
66
- const ruleMode = rule.fix?.mode ?? 'none';
67
- const effectiveMode = effectiveFixMode(ruleMode, maxFixMode);
68
- if (effectiveMode !== 'none' && ruleFindings.length > 0) {
69
- const provider = this.fixers.get(rule.evaluator.type);
70
- if (provider) {
71
- const effectiveFix = {
72
- mode: effectiveMode,
73
- ...(rule.fix?.replacement !== undefined ? { replacement: rule.fix.replacement } : {}),
74
- ...(rule.fix?.params !== undefined ? { params: rule.fix.params } : {}),
75
- };
76
- const providerFixes = await provider.createFixes({
77
- rule,
78
- context: { rule, workdir },
79
- findings: ruleFindings,
80
- fix: effectiveFix,
81
- });
82
- fixes.push(...providerFixes);
51
+ async evaluateWithFixes(rules, workdir, maxFixMode = 'auto', stopOnFirst) {
52
+ const enabledRules = rules.filter((r) => r.enabled !== false);
53
+ const runStartMs = Date.now();
54
+ return await traceAsync('rule.run', async () => {
55
+ this.logger.info('rule run started', { enabled: enabledRules.length, total: rules.length });
56
+ addSpanEvent('rule.run.start', { rules: enabledRules.length, total: rules.length });
57
+ void this.events?.emit('rule.run.start', { rules: enabledRules.length, total: rules.length });
58
+ const findings = [];
59
+ const fixes = [];
60
+ let index = 0;
61
+ let stoppedEarlyLocal = false;
62
+ for (const rule of rules) {
63
+ if (rule.enabled === false)
64
+ continue;
65
+ index++;
66
+ const evalStartMs = Date.now();
67
+ this.logger.debug('eval start', { ruleId: rule.id, index, total: enabledRules.length });
68
+ addSpanEvent('rule.eval.start', { ruleId: rule.id, index, total: enabledRules.length });
69
+ void this.events?.emit('rule.eval.start', { ruleId: rule.id, index, total: enabledRules.length });
70
+ let ruleFindings = [];
71
+ let ruleEvalFixes = [];
72
+ try {
73
+ const result = await this.host.evaluators
74
+ .get(rule.evaluator.type)
75
+ .evaluate(rule, { rule, workdir });
76
+ ruleFindings = result.findings;
77
+ ruleEvalFixes = result.fixes;
78
+ }
79
+ catch (error) {
80
+ const message = error instanceof Error ? error.message : String(error);
81
+ ruleFindings = [
82
+ createFinding(rule, message, null, {
83
+ code: `evaluator:${rule.evaluator.type}`,
84
+ kind: 'error',
85
+ }),
86
+ ];
87
+ addSpanEvent('rule.eval.error', { ruleId: rule.id, error: message });
88
+ void this.events?.emit('rule.eval.error', { ruleId: rule.id, error: message });
89
+ }
90
+ const durationMs = Date.now() - evalStartMs;
91
+ addSpanEvent('rule.eval.done', {
92
+ ruleId: rule.id,
93
+ findings: ruleFindings.length,
94
+ durationMs,
95
+ });
96
+ void this.events?.emit('rule.eval.done', {
97
+ ruleId: rule.id,
98
+ findings: ruleFindings.length,
99
+ durationMs,
100
+ });
101
+ findings.push(...ruleFindings);
102
+ fixes.push(...ruleEvalFixes);
103
+ const ruleMode = rule.fix?.mode ?? 'none';
104
+ const effectiveMode = effectiveFixMode(ruleMode, maxFixMode);
105
+ if (effectiveMode !== 'none' && ruleFindings.length > 0) {
106
+ const provider = this.fixers.get(rule.evaluator.type);
107
+ if (provider) {
108
+ const effectiveFix = {
109
+ mode: effectiveMode,
110
+ ...(rule.fix?.replacement !== undefined ? { replacement: rule.fix.replacement } : {}),
111
+ ...(rule.fix?.params !== undefined ? { params: rule.fix.params } : {}),
112
+ };
113
+ const providerFixes = await provider.createFixes({
114
+ rule,
115
+ context: { rule, workdir },
116
+ findings: ruleFindings,
117
+ fix: effectiveFix,
118
+ });
119
+ fixes.push(...providerFixes);
120
+ }
121
+ }
122
+ if (stopOnFirst &&
123
+ ruleFindings.some((f) => SEVERITY_RANK[f.severity] >= SEVERITY_RANK[stopOnFirst])) {
124
+ stoppedEarlyLocal = true;
125
+ break;
83
126
  }
84
127
  }
85
- }
86
- return { findings, fixes };
128
+ const runDurationMs = Date.now() - runStartMs;
129
+ this.logger.info('rule run done', {
130
+ rules: enabledRules.length,
131
+ findings: findings.length,
132
+ durationMs: runDurationMs,
133
+ stoppedEarly: stoppedEarlyLocal,
134
+ });
135
+ addSpanEvent('rule.run.done', {
136
+ rules: enabledRules.length,
137
+ findings: findings.length,
138
+ durationMs: runDurationMs,
139
+ stoppedEarly: stoppedEarlyLocal,
140
+ });
141
+ void this.events?.emit('rule.run.done', {
142
+ rules: enabledRules.length,
143
+ findings: findings.length,
144
+ durationMs: runDurationMs,
145
+ stoppedEarly: stoppedEarlyLocal,
146
+ });
147
+ return { findings, fixes };
148
+ }, { attributes: { 'rule.count': enabledRules.length } });
87
149
  }
88
150
  /**
89
151
  * Apply or preview candidate byte-range fixes.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-detection-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/agent-detection-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA+B,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAElB,gEAAgE;AAChE,qBAAa,uBAAwB,YAAW,aAAa;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,gBAAsB;IAE3D,gEAAgE;IAC1D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAe7F"}
1
+ {"version":3,"file":"agent-detection-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/agent-detection-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA+B,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB,gEAAgE;AAChE,qBAAa,uBAAwB,YAAW,aAAa;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,gBAAsB;IAE3D,gEAAgE;IAC1D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAe7F"}
@@ -1,5 +1,6 @@
1
1
  import { AgentDetector, isAgentName } from '@gobing-ai/ts-ai-runner';
2
2
  import { createFinding, } from '../types.js';
3
+ import { configArray } from './file-utils.js';
3
4
  /** Evaluates local availability of configured coding agents. */
4
5
  export class AgentDetectionEvaluator {
5
6
  detector;
@@ -8,7 +9,7 @@ export class AgentDetectionEvaluator {
8
9
  }
9
10
  /** Probe required agents and emit findings for missing CLIs. */
10
11
  async evaluate(rule, _context) {
11
- const agents = arrayConfig(rule.evaluator.config ?? {}, 'agents');
12
+ const agents = configArray(rule.evaluator.config ?? {}, 'agents', undefined, { evaluator: 'agent-detection' });
12
13
  const findings = [];
13
14
  for (const agent of agents) {
14
15
  if (!isAgentName(agent)) {
@@ -23,11 +24,3 @@ export class AgentDetectionEvaluator {
23
24
  return { findings, fixes: [] };
24
25
  }
25
26
  }
26
- function arrayConfig(config, key) {
27
- const value = config[key];
28
- if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
29
- return value;
30
- if (typeof value === 'string')
31
- return [value];
32
- throw new Error(`agent-detection evaluator requires string[] config "${key}"`);
33
- }
@@ -1 +1 @@
1
- {"version":3,"file":"coverage-gate-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/coverage-gate-evaluator.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAyBlB;;;;;;;;;;;;GAYG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;;IAMpC,mEAAmE;IAC7D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA0C5F"}
1
+ {"version":3,"file":"coverage-gate-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/coverage-gate-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAyBlB;;;;;;;;;;;;GAYG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;;IAMpC,mEAAmE;IAC7D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA0C5F"}
@@ -1,5 +1,4 @@
1
- import { isAbsolute, relative, resolve } from 'node:path';
2
- import { NodeFileSystem } from '@gobing-ai/ts-runtime';
1
+ import { isAbsolutePath, NodeFileSystem, relativePath, resolvePath } from '@gobing-ai/ts-runtime';
3
2
  import { createFinding, } from '../types.js';
4
3
  import { matchesGlob } from './file-utils.js';
5
4
  /**
@@ -24,8 +23,8 @@ export class CoverageGateEvaluator {
24
23
  async evaluate(rule, context) {
25
24
  const config = (rule.evaluator.config ?? {});
26
25
  const lcovPath = config.lcovPath
27
- ? resolve(context.workdir, config.lcovPath)
28
- : resolve(context.workdir, 'coverage', 'lcov.info');
26
+ ? resolvePath(context.workdir, config.lcovPath)
27
+ : resolvePath(context.workdir, 'coverage', 'lcov.info');
29
28
  if (!(await this.fs.exists(lcovPath))) {
30
29
  return {
31
30
  findings: [
@@ -107,6 +106,6 @@ function isAlwaysExcluded(filePath) {
107
106
  }
108
107
  /** Normalize an lcov `SF:` path to a workdir-relative forward-slash path. */
109
108
  function normalizeLcovSourcePath(workdir, filePath) {
110
- const normalized = isAbsolute(filePath) ? relative(workdir, filePath) : filePath;
109
+ const normalized = isAbsolutePath(filePath) ? relativePath(workdir, filePath) : filePath;
111
110
  return normalized.replaceAll('\\', '/');
112
111
  }
@@ -1 +1 @@
1
- {"version":3,"file":"exit-code-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/exit-code-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAElB,2EAA2E;AAC3E,qBAAa,iBAAkB,YAAW,aAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,GAAE,eAA2C;IAElF,4FAA4F;IACtF,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA2B5F"}
1
+ {"version":3,"file":"exit-code-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/exit-code-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB,2EAA2E;AAC3E,qBAAa,iBAAkB,YAAW,aAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,GAAE,eAA2C;IAElF,4FAA4F;IACtF,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA2B5F"}
@@ -1,5 +1,6 @@
1
1
  import { NodeProcessExecutor } from '@gobing-ai/ts-runtime';
2
2
  import { createFinding, } from '../types.js';
3
+ import { configArray, configNumber, configString } from './file-utils.js';
3
4
  /** Evaluates a rule by running a subprocess and checking its exit code. */
4
5
  export class ExitCodeEvaluator {
5
6
  executor;
@@ -9,10 +10,10 @@ export class ExitCodeEvaluator {
9
10
  /** Run configured command and emit a finding unless the exit code matches `successCode`. */
10
11
  async evaluate(rule, context) {
11
12
  const config = rule.evaluator.config ?? {};
12
- const command = stringConfig(config, 'command');
13
- const args = arrayConfig(config, 'args', []);
14
- const successCode = numberConfig(config, 'successCode', 0);
15
- const timeout = numberConfig(config, 'timeout', 60_000);
13
+ const command = configString(config, 'command', undefined, { evaluator: 'exit-code' });
14
+ const args = configArray(config, 'args', []);
15
+ const successCode = configNumber(config, 'successCode', 0);
16
+ const timeout = configNumber(config, 'timeout', 60_000);
16
17
  const result = await this.executor.run({
17
18
  command,
18
19
  args,
@@ -23,7 +24,7 @@ export class ExitCodeEvaluator {
23
24
  });
24
25
  if (result.exitCode === successCode)
25
26
  return { findings: [], fixes: [] };
26
- const template = stringConfig(config, 'message', `Command failed (exit {code}): ${command} ${args.join(' ')}`.trim());
27
+ const template = configString(config, 'message', `Command failed (exit {code}): ${command} ${args.join(' ')}`.trim());
27
28
  const message = template.replaceAll('{code}', String(result.exitCode));
28
29
  return {
29
30
  findings: [createFinding(rule, message, null, { code: 'exit-code:failed' })],
@@ -31,21 +32,3 @@ export class ExitCodeEvaluator {
31
32
  };
32
33
  }
33
34
  }
34
- function numberConfig(config, key, fallback) {
35
- const value = config[key];
36
- return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
37
- }
38
- function stringConfig(config, key, fallback) {
39
- const value = config[key];
40
- if (typeof value === 'string')
41
- return value;
42
- if (fallback !== undefined)
43
- return fallback;
44
- throw new Error(`exit-code evaluator requires string config "${key}"`);
45
- }
46
- function arrayConfig(config, key, fallback) {
47
- const value = config[key];
48
- if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
49
- return value;
50
- return fallback;
51
- }
@@ -1,4 +1,4 @@
1
- import { type FileSystem, NodeFileSystem } from '@gobing-ai/ts-runtime';
1
+ import { type LegacyFileSystem as FileSystem, NodeFileSystem } from '@gobing-ai/ts-runtime';
2
2
  /** Options for source-file discovery. */
3
3
  export interface SourceDiscoveryOptions {
4
4
  /** Working directory. */
@@ -10,6 +10,12 @@ export interface SourceDiscoveryOptions {
10
10
  /** Filesystem adapter. */
11
11
  fs?: FileSystem;
12
12
  }
13
+ /**
14
+ * Directory names pruned from every file walk — heavy or generated trees that no rule
15
+ * should scan. Shared so subprocess-backed evaluators (e.g. `sg`) can forward the same
16
+ * skip-list to the external tool instead of relying on each rule to remember it.
17
+ */
18
+ export declare const DEFAULT_EXCLUDES: Set<string>;
13
19
  /** Resolve source files for evaluators using conservative path-fragment matching. */
14
20
  export declare function discoverFiles(options: SourceDiscoveryOptions): Promise<string[]>;
15
21
  /** Read a file from a workdir-relative path. */
@@ -72,6 +78,24 @@ export declare function matchesGlob(path: string, pattern: string): boolean;
72
78
  export declare function escapeRegExp(value: string): string;
73
79
  /** Return the value as a `string[]` when every item is a string, otherwise undefined. */
74
80
  export declare function stringArray(value: unknown): string[] | undefined;
81
+ /** Optional accessor context — names the evaluator in "required config" errors for rule authors. */
82
+ export interface ConfigAccessorOptions {
83
+ /** Evaluator name surfaced in the error, e.g. `"regex evaluator requires string config \"pattern\""`. */
84
+ evaluator?: string;
85
+ }
86
+ /**
87
+ * Read a string entry from a rule's evaluator config. Returns the value when it is a string,
88
+ * the `fallback` when one is supplied, otherwise throws — so required keys fail loudly.
89
+ */
90
+ export declare function configString(config: Record<string, unknown>, key: string, fallback?: string, options?: ConfigAccessorOptions): string;
91
+ /**
92
+ * Read a string-array entry from a rule's evaluator config. A bare string is coerced to a
93
+ * single-element array. Returns the `fallback` when one is supplied and the value is absent;
94
+ * otherwise throws — so required keys fail loudly.
95
+ */
96
+ export declare function configArray(config: Record<string, unknown>, key: string, fallback?: string[], options?: ConfigAccessorOptions): string[];
97
+ /** Read a finite-number entry from a rule's evaluator config, falling back when absent or invalid. */
98
+ export declare function configNumber(config: Record<string, unknown>, key: string, fallback: number): number;
75
99
  /**
76
100
  * Split a leading ripgrep/PCRE-style `(?flags)` inline group off a regex source.
77
101
  *
@@ -1 +1 @@
1
- {"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/evaluators/file-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAE,cAAc,EAAW,MAAM,uBAAuB,CAAC;AAEjF,yCAAyC;AACzC,MAAM,WAAW,sBAAsB;IACnC,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,EAAE,CAAC,EAAE,UAAU,CAAC;CACnB;AAID,qFAAqF;AACrF,wBAAsB,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWtF;AAED,gDAAgD;AAChD,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,iBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnH;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IACxB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B;AAED,8EAA8E;AAC9E,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC7B,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;;OAQG;IACH,SAAS,EAAE,aAAa,CAAC;IACzB,0BAA0B;IAC1B,EAAE,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAWjF;AAeD,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,CAMhF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAUlE;AAqBD,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAKhF"}
1
+ {"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/evaluators/file-utils.ts"],"names":[],"mappings":"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"}