@rigour-labs/core 5.0.0 → 5.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 (139) hide show
  1. package/README.md +9 -1
  2. package/dist/gates/agent-team.d.ts +0 -1
  3. package/dist/gates/agent-team.js +0 -1
  4. package/dist/gates/checkpoint.d.ts +0 -2
  5. package/dist/gates/checkpoint.js +0 -2
  6. package/dist/gates/context-window-artifacts.d.ts +6 -2
  7. package/dist/gates/context-window-artifacts.js +107 -31
  8. package/dist/gates/deep-analysis.d.ts +2 -0
  9. package/dist/gates/deep-analysis.js +41 -11
  10. package/dist/gates/dependency.d.ts +0 -2
  11. package/dist/gates/dependency.js +23 -5
  12. package/dist/gates/deprecated-apis.d.ts +0 -2
  13. package/dist/gates/deprecated-apis.js +33 -20
  14. package/dist/gates/duplication-drift/index.d.ts +61 -0
  15. package/dist/gates/duplication-drift/index.js +240 -0
  16. package/dist/gates/duplication-drift/similarity.d.ts +68 -0
  17. package/dist/gates/duplication-drift/similarity.js +177 -0
  18. package/dist/gates/duplication-drift/tokenizer.d.ts +55 -0
  19. package/dist/gates/duplication-drift/tokenizer.js +195 -0
  20. package/dist/gates/frontend-secret-exposure.d.ts +0 -3
  21. package/dist/gates/frontend-secret-exposure.js +1 -114
  22. package/dist/gates/frontend-secret-patterns.d.ts +33 -0
  23. package/dist/gates/frontend-secret-patterns.js +119 -0
  24. package/dist/gates/{hallucinated-imports.d.ts → hallucinated-imports/index.d.ts} +2 -29
  25. package/dist/gates/hallucinated-imports/index.js +174 -0
  26. package/dist/gates/hallucinated-imports/js-resolver.d.ts +45 -0
  27. package/dist/gates/hallucinated-imports/js-resolver.js +320 -0
  28. package/dist/gates/hallucinated-imports/manifest-discovery.d.ts +28 -0
  29. package/dist/gates/hallucinated-imports/manifest-discovery.js +114 -0
  30. package/dist/gates/hallucinated-imports/python-resolver.d.ts +24 -0
  31. package/dist/gates/hallucinated-imports/python-resolver.js +306 -0
  32. package/dist/gates/hallucinated-imports-lang.d.ts +2 -2
  33. package/dist/gates/hallucinated-imports-lang.js +269 -34
  34. package/dist/gates/hallucinated-imports.test.js +1 -2
  35. package/dist/gates/inconsistent-error-handling.d.ts +0 -5
  36. package/dist/gates/inconsistent-error-handling.js +15 -144
  37. package/dist/gates/language-adapters/csharp-adapter.d.ts +16 -0
  38. package/dist/gates/language-adapters/csharp-adapter.js +211 -0
  39. package/dist/gates/language-adapters/go-adapter.d.ts +26 -0
  40. package/dist/gates/language-adapters/go-adapter.js +195 -0
  41. package/dist/gates/language-adapters/index.d.ts +15 -0
  42. package/dist/gates/language-adapters/index.js +16 -0
  43. package/dist/gates/language-adapters/java-adapter.d.ts +16 -0
  44. package/dist/gates/language-adapters/java-adapter.js +237 -0
  45. package/dist/gates/language-adapters/js-adapter.d.ts +26 -0
  46. package/dist/gates/language-adapters/js-adapter.js +279 -0
  47. package/dist/gates/language-adapters/python-adapter.d.ts +25 -0
  48. package/dist/gates/language-adapters/python-adapter.js +183 -0
  49. package/dist/gates/language-adapters/registry.d.ts +26 -0
  50. package/dist/gates/language-adapters/registry.js +65 -0
  51. package/dist/gates/language-adapters/ruby-adapter.d.ts +25 -0
  52. package/dist/gates/language-adapters/ruby-adapter.js +217 -0
  53. package/dist/gates/language-adapters/rust-adapter.d.ts +27 -0
  54. package/dist/gates/language-adapters/rust-adapter.js +235 -0
  55. package/dist/gates/language-adapters/types.d.ts +60 -0
  56. package/dist/gates/language-adapters/types.js +22 -0
  57. package/dist/gates/logic-drift-extractors.d.ts +15 -0
  58. package/dist/gates/logic-drift-extractors.js +34 -0
  59. package/dist/gates/logic-drift.d.ts +0 -30
  60. package/dist/gates/logic-drift.js +39 -129
  61. package/dist/gates/phantom-apis.d.ts +0 -2
  62. package/dist/gates/phantom-apis.js +49 -20
  63. package/dist/gates/promise-safety.d.ts +0 -1
  64. package/dist/gates/promise-safety.js +14 -2
  65. package/dist/gates/runner.js +51 -22
  66. package/dist/gates/security-patterns-data.d.ts +14 -0
  67. package/dist/gates/security-patterns-data.js +235 -0
  68. package/dist/gates/security-patterns.d.ts +17 -3
  69. package/dist/gates/security-patterns.js +80 -211
  70. package/dist/gates/side-effect-analysis/categorizer.d.ts +32 -0
  71. package/dist/gates/side-effect-analysis/categorizer.js +83 -0
  72. package/dist/gates/{side-effect-analysis.d.ts → side-effect-analysis/index.d.ts} +3 -5
  73. package/dist/gates/{side-effect-analysis.js → side-effect-analysis/index.js} +33 -45
  74. package/dist/gates/side-effect-analysis/scope-tracker.d.ts +37 -0
  75. package/dist/gates/side-effect-analysis/scope-tracker.js +40 -0
  76. package/dist/gates/side-effect-helpers/index.d.ts +4 -0
  77. package/dist/gates/side-effect-helpers/index.js +4 -0
  78. package/dist/gates/side-effect-helpers/pattern-detection.d.ts +123 -0
  79. package/dist/gates/{side-effect-helpers.js → side-effect-helpers/pattern-detection.js} +22 -468
  80. package/dist/gates/side-effect-helpers/resource-tracking.d.ts +80 -0
  81. package/dist/gates/side-effect-helpers/resource-tracking.js +281 -0
  82. package/dist/gates/side-effect-helpers/scope-analysis.d.ts +21 -0
  83. package/dist/gates/side-effect-helpers/scope-analysis.js +146 -0
  84. package/dist/gates/side-effect-helpers/types.d.ts +38 -0
  85. package/dist/gates/side-effect-helpers/types.js +41 -0
  86. package/dist/gates/side-effect-rules.d.ts +0 -1
  87. package/dist/gates/side-effect-rules.js +0 -1
  88. package/dist/gates/style-drift-rules.d.ts +86 -0
  89. package/dist/gates/style-drift-rules.js +103 -0
  90. package/dist/gates/style-drift.d.ts +7 -16
  91. package/dist/gates/style-drift.js +101 -119
  92. package/dist/gates/test-quality-matchers.d.ts +53 -0
  93. package/dist/gates/test-quality-matchers.js +86 -0
  94. package/dist/gates/test-quality.d.ts +0 -3
  95. package/dist/gates/test-quality.js +47 -44
  96. package/dist/hooks/checker.d.ts +0 -1
  97. package/dist/hooks/checker.js +1 -3
  98. package/dist/hooks/dlp-templates.d.ts +0 -1
  99. package/dist/hooks/dlp-templates.js +0 -4
  100. package/dist/hooks/index.d.ts +0 -2
  101. package/dist/hooks/index.js +0 -2
  102. package/dist/hooks/input-validator.d.ts +0 -1
  103. package/dist/hooks/input-validator.js +0 -1
  104. package/dist/hooks/input-validator.test.js +0 -1
  105. package/dist/hooks/standalone-checker.d.ts +0 -1
  106. package/dist/hooks/standalone-checker.js +0 -1
  107. package/dist/hooks/standalone-dlp-checker.d.ts +0 -1
  108. package/dist/hooks/standalone-dlp-checker.js +0 -1
  109. package/dist/hooks/templates.d.ts +6 -1
  110. package/dist/hooks/templates.js +6 -1
  111. package/dist/hooks/types.d.ts +1 -2
  112. package/dist/hooks/types.js +1 -1
  113. package/dist/index.d.ts +1 -1
  114. package/dist/index.js +1 -1
  115. package/dist/services/adaptive-thresholds.d.ts +0 -2
  116. package/dist/services/adaptive-thresholds.js +0 -2
  117. package/dist/services/filesystem-cache.d.ts +0 -1
  118. package/dist/services/filesystem-cache.js +0 -1
  119. package/dist/services/score-history.d.ts +0 -1
  120. package/dist/services/score-history.js +0 -1
  121. package/dist/services/temporal-drift.d.ts +1 -2
  122. package/dist/services/temporal-drift.js +7 -8
  123. package/dist/storage/db.d.ts +23 -7
  124. package/dist/storage/db.js +116 -55
  125. package/dist/storage/findings.d.ts +4 -3
  126. package/dist/storage/findings.js +13 -20
  127. package/dist/storage/local-memory.d.ts +4 -4
  128. package/dist/storage/local-memory.js +20 -22
  129. package/dist/storage/patterns.d.ts +5 -5
  130. package/dist/storage/patterns.js +20 -26
  131. package/dist/storage/scans.d.ts +6 -6
  132. package/dist/storage/scans.js +12 -21
  133. package/dist/types/index.d.ts +1 -0
  134. package/dist/utils/scanner.js +1 -1
  135. package/package.json +7 -8
  136. package/dist/gates/duplication-drift.d.ts +0 -128
  137. package/dist/gates/duplication-drift.js +0 -585
  138. package/dist/gates/hallucinated-imports.js +0 -641
  139. package/dist/gates/side-effect-helpers.d.ts +0 -260
@@ -19,9 +19,6 @@
19
19
  * Go — testing package (t.Run, table-driven tests)
20
20
  * Java — JUnit 4/5, TestNG
21
21
  * Kotlin — JUnit 5, kotlin.test
22
- *
23
- * @since v3.0.0
24
- * @since v3.0.3 — Go, Java, Kotlin support added
25
22
  */
26
23
  import { Gate, GateContext } from './base.js';
27
24
  import { Failure, Provenance } from '../types/index.js';
@@ -19,14 +19,13 @@
19
19
  * Go — testing package (t.Run, table-driven tests)
20
20
  * Java — JUnit 4/5, TestNG
21
21
  * Kotlin — JUnit 5, kotlin.test
22
- *
23
- * @since v3.0.0
24
- * @since v3.0.3 — Go, Java, Kotlin support added
25
22
  */
26
23
  import { Gate } from './base.js';
27
24
  import { FileScanner } from '../utils/scanner.js';
28
25
  import { Logger } from '../utils/logger.js';
26
+ import { languageAdapters } from './language-adapters/index.js';
29
27
  import { checkGoTestQuality, checkJavaKotlinTestQuality } from './test-quality-lang.js';
28
+ import { JS_TEST_START_PATTERN, JS_ASSERTION_PATTERNS, JS_MOCK_PATTERNS, JS_TAUTOLOGICAL_PATTERNS, JS_VAR_TAUTOLOGY_PATTERN, SNAPSHOT_PATTERNS, PYTHON_TEST_FUNC_PATTERN, PYTHON_ASSERTION_PATTERNS, PYTHON_MOCK_PATTERNS, PYTHON_TAUTOLOGICAL_PATTERNS, PYTHON_FIXTURE_PATTERN, PYTHON_CONFTEST_NAME, } from './test-quality-matchers.js';
30
29
  import fs from 'fs-extra';
31
30
  import path from 'path';
32
31
  export class TestQualityGate extends Gate {
@@ -51,12 +50,14 @@ export class TestQualityGate extends Gate {
51
50
  const failures = [];
52
51
  const issues = [];
53
52
  const defaultPatterns = [
54
- '**/*.test.{ts,js,tsx,jsx}', '**/*.spec.{ts,js,tsx,jsx}',
55
- '**/__tests__/**/*.{ts,js,tsx,jsx}',
53
+ '**/*.test.{ts,js,tsx,jsx,mjs,cjs}', '**/*.spec.{ts,js,tsx,jsx,mjs,cjs}',
54
+ '**/__tests__/**/*.{ts,js,tsx,jsx,mjs,cjs}',
56
55
  '**/test_*.py', '**/*_test.py', '**/tests/**/*.py',
57
56
  '**/*_test.go',
58
57
  '**/*Test.java', '**/*Tests.java', '**/src/test/**/*.java',
59
58
  '**/*Test.kt', '**/*Tests.kt', '**/src/test/**/*.kt',
59
+ '**/*_test.rs', '**/tests/**/*.rs',
60
+ '**/*_spec.rb', '**/spec/**/*.rb',
60
61
  ];
61
62
  const scanPatterns = context.patterns || defaultPatterns;
62
63
  const files = await FileScanner.findFiles({
@@ -72,27 +73,28 @@ export class TestQualityGate extends Gate {
72
73
  const fullPath = path.join(context.cwd, file);
73
74
  const content = await fs.readFile(fullPath, 'utf-8');
74
75
  const ext = path.extname(file);
75
- if (['.ts', '.js', '.tsx', '.jsx'].includes(ext)) {
76
- this.checkJSTestQuality(content, file, issues);
77
- }
78
- else if (ext === '.py') {
79
- this.checkPythonTestQuality(content, file, issues);
80
- }
81
- else if (ext === '.go') {
82
- checkGoTestQuality(content, file, issues, {
83
- check_empty_tests: this.config.check_empty_tests,
84
- check_tautological: this.config.check_tautological,
85
- check_mock_heavy: this.config.check_mock_heavy,
86
- max_mocks_per_test: this.config.max_mocks_per_test,
87
- });
88
- }
89
- else if (ext === '.java' || ext === '.kt') {
90
- checkJavaKotlinTestQuality(content, file, ext, issues, {
91
- check_empty_tests: this.config.check_empty_tests,
92
- check_tautological: this.config.check_tautological,
93
- check_mock_heavy: this.config.check_mock_heavy,
94
- max_mocks_per_test: this.config.max_mocks_per_test,
95
- });
76
+ const adapter = languageAdapters.getAdapter(file);
77
+ if (!adapter)
78
+ continue;
79
+ const langConfig = {
80
+ check_empty_tests: this.config.check_empty_tests,
81
+ check_tautological: this.config.check_tautological,
82
+ check_mock_heavy: this.config.check_mock_heavy,
83
+ max_mocks_per_test: this.config.max_mocks_per_test,
84
+ };
85
+ switch (adapter.id) {
86
+ case 'js':
87
+ this.checkJSTestQuality(content, file, issues);
88
+ break;
89
+ case 'python':
90
+ this.checkPythonTestQuality(content, file, issues);
91
+ break;
92
+ case 'go':
93
+ checkGoTestQuality(content, file, issues, langConfig);
94
+ break;
95
+ case 'java':
96
+ checkJavaKotlinTestQuality(content, file, ext, issues, langConfig);
97
+ break;
96
98
  }
97
99
  }
98
100
  catch { /* skip */ }
@@ -125,7 +127,7 @@ export class TestQualityGate extends Gate {
125
127
  const line = lines[i];
126
128
  const trimmed = line.trim();
127
129
  // Detect test block start: it('...', () => { or test('...', async () => {
128
- const testStart = trimmed.match(/^(?:it|test)\s*\(\s*['"`].*['"`]\s*,\s*(async\s+)?(?:\(\s*\)|function\s*\(\s*\)|\(\s*\{[^}]*\}\s*\))\s*(?:=>)?\s*\{/);
130
+ const testStart = trimmed.match(JS_TEST_START_PATTERN);
129
131
  if (testStart && !inTestBlock) {
130
132
  inTestBlock = true;
131
133
  testStartLine = i + 1;
@@ -159,12 +161,11 @@ export class TestQualityGate extends Gate {
159
161
  braceDepth--;
160
162
  }
161
163
  // Check for assertions
162
- if (/expect\s*\(/.test(line) || /assert\s*[.(]/.test(line) ||
163
- /\.toEqual|\.toBe|\.toContain|\.toMatch|\.toThrow|\.toHaveBeenCalled|\.toHaveLength|\.toBeTruthy|\.toBeFalsy|\.toBeDefined|\.toBeNull|\.toBeUndefined|\.toBeGreaterThan|\.toBeLessThan|\.toHaveProperty|\.toStrictEqual|\.rejects|\.resolves/.test(line)) {
164
+ if (JS_ASSERTION_PATTERNS.some(p => p.test(line))) {
164
165
  hasAssertion = true;
165
166
  }
166
167
  // Check for mocks
167
- if (/jest\.fn\(|vi\.fn\(|jest\.mock\(|vi\.mock\(|jest\.spyOn\(|vi\.spyOn\(|sinon\.(stub|mock|spy)\(/.test(line)) {
168
+ if (JS_MOCK_PATTERNS.some(p => p.test(line))) {
168
169
  mockCount++;
169
170
  }
170
171
  // Check for await
@@ -173,19 +174,24 @@ export class TestQualityGate extends Gate {
173
174
  }
174
175
  // Check for tautological assertions
175
176
  if (this.config.check_tautological) {
176
- if (/expect\s*\(\s*true\s*\)\s*\.toBe\s*\(\s*true\s*\)/.test(line) ||
177
- /expect\s*\(\s*false\s*\)\s*\.toBe\s*\(\s*false\s*\)/.test(line) ||
178
- /expect\s*\(\s*1\s*\)\s*\.toBe\s*\(\s*1\s*\)/.test(line) ||
179
- /expect\s*\(\s*['"].*['"]\s*\)\s*\.toBe\s*\(\s*['"].*['"]\s*\)/.test(line) && line.match(/expect\s*\(\s*(['"].*?['"])\s*\)\s*\.toBe\s*\(\s*\1\s*\)/)) {
177
+ if (JS_TAUTOLOGICAL_PATTERNS.some(p => p.test(line))) {
180
178
  issues.push({
181
179
  file, line: i + 1, pattern: 'tautological-assertion',
182
180
  reason: 'Tautological assertion — comparing a literal to itself proves nothing',
183
181
  });
184
182
  }
183
+ // Variable tautology: expect(x).toBe(x), expect(foo).toEqual(foo)
184
+ const varTautology = line.match(JS_VAR_TAUTOLOGY_PATTERN);
185
+ if (varTautology && varTautology[1] === varTautology[2]) {
186
+ issues.push({
187
+ file, line: i + 1, pattern: 'tautological-assertion',
188
+ reason: `Tautological assertion — expect(${varTautology[1]}).toBe(${varTautology[2]}) compares variable to itself`,
189
+ });
190
+ }
185
191
  }
186
192
  // Check for snapshot-only tests
187
193
  if (this.config.check_snapshot_abuse) {
188
- if (/\.toMatchSnapshot\s*\(/.test(line) || /\.toMatchInlineSnapshot\s*\(/.test(line)) {
194
+ if (SNAPSHOT_PATTERNS.some(p => p.test(line))) {
189
195
  // This is fine IF there are also semantic assertions
190
196
  // We'll check when the block ends
191
197
  }
@@ -240,7 +246,7 @@ export class TestQualityGate extends Gate {
240
246
  const lines = content.split('\n');
241
247
  const basename = path.basename(file);
242
248
  // Skip conftest.py entirely — these contain fixtures, not tests
243
- if (basename === 'conftest.py')
249
+ if (basename === PYTHON_CONFTEST_NAME)
244
250
  return;
245
251
  let inTestFunc = false;
246
252
  let testStartLine = 0;
@@ -253,7 +259,7 @@ export class TestQualityGate extends Gate {
253
259
  const line = lines[i];
254
260
  const trimmed = line.trim();
255
261
  // Detect test function start
256
- const testFuncMatch = line.match(/^(\s*)(?:def|async\s+def)\s+(test_\w+)\s*\(/);
262
+ const testFuncMatch = line.match(PYTHON_TEST_FUNC_PATTERN);
257
263
  if (testFuncMatch) {
258
264
  // Check if this function has a @pytest.fixture decorator (not a real test)
259
265
  hasFixtureDecorator = false;
@@ -263,7 +269,7 @@ export class TestQualityGate extends Gate {
263
269
  continue;
264
270
  if (!prevLine.startsWith('@'))
265
271
  break;
266
- if (/^@pytest\.fixture/.test(prevLine)) {
272
+ if (PYTHON_FIXTURE_PATTERN.test(prevLine)) {
267
273
  hasFixtureDecorator = true;
268
274
  break;
269
275
  }
@@ -301,19 +307,16 @@ export class TestQualityGate extends Gate {
301
307
  }
302
308
  testContent += line + '\n';
303
309
  // Check for assertions
304
- if (/\bassert\s+/.test(trimmed) || /self\.assert\w+\s*\(/.test(trimmed) ||
305
- /pytest\.raises\s*\(/.test(trimmed) || /\.assert_called|\.assert_any_call/.test(trimmed)) {
310
+ if (PYTHON_ASSERTION_PATTERNS.some(p => p.test(trimmed))) {
306
311
  hasAssertion = true;
307
312
  }
308
313
  // Check for mocks
309
- if (/mock\.|Mock\(|patch\(|MagicMock\(/.test(trimmed)) {
314
+ if (PYTHON_MOCK_PATTERNS.some(p => p.test(trimmed))) {
310
315
  mockCount++;
311
316
  }
312
317
  // Tautological assertions
313
318
  if (this.config.check_tautological) {
314
- if (/\bassert\s+True\s*$/.test(trimmed) || /\bassert\s+1\s*==\s*1/.test(trimmed) ||
315
- /self\.assertTrue\s*\(\s*True\s*\)/.test(trimmed) ||
316
- /self\.assertEqual\s*\(\s*(\d+|['"][^'"]*['"])\s*,\s*\1\s*\)/.test(trimmed)) {
319
+ if (PYTHON_TAUTOLOGICAL_PATTERNS.some(p => p.test(trimmed))) {
317
320
  issues.push({
318
321
  file, line: i + 1, pattern: 'tautological-assertion',
319
322
  reason: 'Tautological assertion — comparing a constant to itself proves nothing',
@@ -6,7 +6,6 @@
6
6
  *
7
7
  * Used by all tool-specific hooks (Claude, Cursor, Cline, Windsurf).
8
8
  *
9
- * @since v3.0.0
10
9
  */
11
10
  import type { HookCheckerResult } from './types.js';
12
11
  interface CheckerOptions {
@@ -6,7 +6,6 @@
6
6
  *
7
7
  * Used by all tool-specific hooks (Claude, Cursor, Cline, Windsurf).
8
8
  *
9
- * @since v3.0.0
10
9
  */
11
10
  import fs from 'fs-extra';
12
11
  import path from 'path';
@@ -258,7 +257,6 @@ function simpleGlob(filePath, pattern) {
258
257
  * enforce_memory: false # allow native memory, still enforce skills
259
258
  * enforce_skills: false # allow native skills, still enforce memory
260
259
  *
261
- * @since v4.2.0
262
260
  */
263
261
  function checkGovernance(content, relPath, config, failures) {
264
262
  const gov = config.gates.governance;
@@ -275,7 +273,7 @@ function checkGovernance(content, relPath, config, failures) {
275
273
  const isMemoryPath = memoryPaths.some(pattern => simpleGlob(normalizedPath, pattern));
276
274
  // ── Check skills paths ──
277
275
  const skillsPaths = gov.protected_skills_paths ?? [];
278
- const isSkillsPath = skillsPaths.some((pattern) => simpleGlob(normalizedPath, pattern));
276
+ const isSkillsPath = skillsPaths.some(pattern => simpleGlob(normalizedPath, pattern));
279
277
  if (!isMemoryPath && !isSkillsPath)
280
278
  return;
281
279
  // ── Enforcement: block memory writes ──
@@ -11,7 +11,6 @@
11
11
  * - Cline: PreToolUse executable script
12
12
  * - Windsurf: pre_write_code event
13
13
  *
14
- * @since v4.2.0 — AI Agent DLP layer
15
14
  */
16
15
  import type { HookTool } from './types.js';
17
16
  export interface GeneratedDLPHookFile {
@@ -11,7 +11,6 @@
11
11
  * - Cline: PreToolUse executable script
12
12
  * - Windsurf: pre_write_code event
13
13
  *
14
- * @since v4.2.0 — AI Agent DLP layer
15
14
  */
16
15
  /**
17
16
  * Generate DLP (pre-input) hook config files for a specific tool.
@@ -75,7 +74,6 @@ function generateCursorDLPHooks(checkerCommand) {
75
74
  * Receives { file_path, new_content } on stdin.
76
75
  * Runs Rigour credential scanner on the content.
77
76
  *
78
- * @since v4.2.0 — AI Agent DLP
79
77
  */
80
78
 
81
79
  let data = '';
@@ -142,7 +140,6 @@ function generateClineDLPHooks(checkerCommand) {
142
140
  * Receives JSON on stdin with { toolName, toolInput }.
143
141
  * Scans tool input for credentials BEFORE execution.
144
142
  *
145
- * @since v4.2.0 — AI Agent DLP
146
143
  */
147
144
 
148
145
  let data = '';
@@ -225,7 +222,6 @@ function generateWindsurfDLPHooks(checkerCommand) {
225
222
  * Windsurf DLP hook — scans input for credentials before Cascade agent processing.
226
223
  * Receives { file_path, content } on stdin.
227
224
  *
228
- * @since v4.2.0 — AI Agent DLP
229
225
  */
230
226
 
231
227
  let data = '';
@@ -1,8 +1,6 @@
1
1
  /**
2
2
  * Hooks module — multi-tool hook integration for Rigour.
3
3
  *
4
- * @since v3.0.0
5
- * @since v4.2.0 — AI Agent DLP (Data Loss Prevention)
6
4
  */
7
5
  export { runHookChecker } from './checker.js';
8
6
  export { generateHookFiles } from './templates.js';
@@ -1,8 +1,6 @@
1
1
  /**
2
2
  * Hooks module — multi-tool hook integration for Rigour.
3
3
  *
4
- * @since v3.0.0
5
- * @since v4.2.0 — AI Agent DLP (Data Loss Prevention)
6
4
  */
7
5
  export { runHookChecker } from './checker.js';
8
6
  export { generateHookFiles } from './templates.js';
@@ -15,7 +15,6 @@
15
15
  * - .env format strings (KEY=value)
16
16
  * - Azure / GCP / Stripe / Twilio keys
17
17
  *
18
- * @since v4.2.0 — AI Agent DLP layer
19
18
  */
20
19
  export interface CredentialDetection {
21
20
  type: string;
@@ -15,7 +15,6 @@
15
15
  * - .env format strings (KEY=value)
16
16
  * - Azure / GCP / Stripe / Twilio keys
17
17
  *
18
- * @since v4.2.0 — AI Agent DLP layer
19
18
  */
20
19
  const CREDENTIAL_PATTERNS = [
21
20
  // ── Cloud Provider Keys ───────────────────────────────────
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Tests for Input Validation Gate — AI Agent DLP (Data Loss Prevention)
3
3
  *
4
- * @since v4.2.0
5
4
  */
6
5
  import { describe, it, expect } from 'vitest';
7
6
  import { scanInputForCredentials, formatDLPAlert, createDLPAuditEntry, } from './input-validator.js';
@@ -10,6 +10,5 @@
10
10
  * 0 = pass (or warn-only mode)
11
11
  * 2 = block (fail + block_on_failure enabled)
12
12
  *
13
- * @since v3.0.0
14
13
  */
15
14
  export {};
@@ -10,7 +10,6 @@
10
10
  * 0 = pass (or warn-only mode)
11
11
  * 2 = block (fail + block_on_failure enabled)
12
12
  *
13
- * @since v3.0.0
14
13
  */
15
14
  import { runHookChecker } from './checker.js';
16
15
  const EMPTY_RESULT = JSON.stringify({ status: 'pass', failures: [], duration_ms: 0 });
@@ -13,6 +13,5 @@
13
13
  * echo "my api_key = sk-abc123..." | node standalone-dlp-checker.js
14
14
  * echo '{"content":"..."}' | node standalone-dlp-checker.js --json
15
15
  *
16
- * @since v4.2.0 — AI Agent DLP
17
16
  */
18
17
  export {};
@@ -13,7 +13,6 @@
13
13
  * echo "my api_key = sk-abc123..." | node standalone-dlp-checker.js
14
14
  * echo '{"content":"..."}' | node standalone-dlp-checker.js --json
15
15
  *
16
- * @since v4.2.0 — AI Agent DLP
17
16
  */
18
17
  import { scanInputForCredentials, formatDLPAlert, createDLPAuditEntry } from './input-validator.js';
19
18
  import fs from 'fs-extra';
@@ -7,7 +7,12 @@
7
7
  * - Cline: .clinerules/hooks/PostToolUse (executable script)
8
8
  * - Windsurf: .windsurf/hooks.json (post_write_code event)
9
9
  *
10
- * @since v3.0.0
10
+ * NOTE: These templates use `require('@rigour-labs/core/...')` for direct library
11
+ * invocation (used by MCP `handleHooksInit`). The CLI `rigour hooks init` has its
12
+ * own generators in `packages/rigour-cli/src/commands/hooks.ts` that shell out to
13
+ * `rigour hooks check` instead — more portable but requires the CLI binary.
14
+ * Both produce functionally equivalent hook configs for each tool.
15
+ *
11
16
  */
12
17
  import type { HookTool } from './types.js';
13
18
  export interface GeneratedHookFile {
@@ -7,7 +7,12 @@
7
7
  * - Cline: .clinerules/hooks/PostToolUse (executable script)
8
8
  * - Windsurf: .windsurf/hooks.json (post_write_code event)
9
9
  *
10
- * @since v3.0.0
10
+ * NOTE: These templates use `require('@rigour-labs/core/...')` for direct library
11
+ * invocation (used by MCP `handleHooksInit`). The CLI `rigour hooks init` has its
12
+ * own generators in `packages/rigour-cli/src/commands/hooks.ts` that shell out to
13
+ * `rigour hooks check` instead — more portable but requires the CLI binary.
14
+ * Both produce functionally equivalent hook configs for each tool.
15
+ *
11
16
  */
12
17
  /**
13
18
  * Generate hook config files for a specific tool.
@@ -4,7 +4,6 @@
4
4
  * Each AI coding tool (Claude Code, Cursor, Cline, Windsurf)
5
5
  * has its own hook format. These types unify the config generation.
6
6
  *
7
- * @since v3.0.0
8
7
  */
9
8
  export type HookTool = 'claude' | 'cursor' | 'cline' | 'windsurf';
10
9
  export interface HookConfig {
@@ -18,7 +17,7 @@ export interface HookConfig {
18
17
  block_on_failure: boolean;
19
18
  }
20
19
  /** The fast gates that can run per-file in <200ms */
21
- export declare const FAST_GATE_IDS: readonly ["hallucinated-imports", "promise-safety", "security-patterns", "file-size"];
20
+ export declare const FAST_GATE_IDS: readonly ["governance", "hallucinated-imports", "promise-safety", "security-patterns", "file-size"];
22
21
  export declare const DEFAULT_HOOK_CONFIG: HookConfig;
23
22
  export type FastGateId = typeof FAST_GATE_IDS[number];
24
23
  export interface HookCheckerResult {
@@ -4,10 +4,10 @@
4
4
  * Each AI coding tool (Claude Code, Cursor, Cline, Windsurf)
5
5
  * has its own hook format. These types unify the config generation.
6
6
  *
7
- * @since v3.0.0
8
7
  */
9
8
  /** The fast gates that can run per-file in <200ms */
10
9
  export const FAST_GATE_IDS = [
10
+ 'governance',
11
11
  'hallucinated-imports',
12
12
  'promise-safety',
13
13
  'security-patterns',
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export * from './templates/index.js';
6
6
  export * from './types/fix-packet.js';
7
7
  export { Gate, GateContext } from './gates/base.js';
8
8
  export { RetryLoopBreakerGate } from './gates/retry-loop-breaker.js';
9
- export { SideEffectAnalysisGate } from './gates/side-effect-analysis.js';
9
+ export { SideEffectAnalysisGate } from './gates/side-effect-analysis/index.js';
10
10
  export { FrontendSecretExposureGate } from './gates/frontend-secret-exposure.js';
11
11
  export * from './utils/logger.js';
12
12
  export * from './services/score-history.js';
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ export * from './templates/index.js';
6
6
  export * from './types/fix-packet.js';
7
7
  export { Gate } from './gates/base.js';
8
8
  export { RetryLoopBreakerGate } from './gates/retry-loop-breaker.js';
9
- export { SideEffectAnalysisGate } from './gates/side-effect-analysis.js';
9
+ export { SideEffectAnalysisGate } from './gates/side-effect-analysis/index.js';
10
10
  export { FrontendSecretExposureGate } from './gates/frontend-secret-exposure.js';
11
11
  export * from './utils/logger.js';
12
12
  export * from './services/score-history.js';
@@ -12,8 +12,6 @@
12
12
  * - Per-provenance failure tracking (AI drift vs structural vs security)
13
13
  * - Statistical anomaly detection normalizes across project sizes
14
14
  *
15
- * @since v2.14.0 (original)
16
- * @since v5.0.0 (Z-score + provenance-aware trends)
17
15
  */
18
16
  export type ComplexityTier = 'hobby' | 'startup' | 'enterprise';
19
17
  export type QualityTrend = 'improving' | 'stable' | 'degrading';
@@ -12,8 +12,6 @@
12
12
  * - Per-provenance failure tracking (AI drift vs structural vs security)
13
13
  * - Statistical anomaly detection normalizes across project sizes
14
14
  *
15
- * @since v2.14.0 (original)
16
- * @since v5.0.0 (Z-score + provenance-aware trends)
17
15
  */
18
16
  import * as fs from 'fs';
19
17
  import * as path from 'path';
@@ -8,7 +8,6 @@
8
8
  * Now: one cache per scan, all gates share it. LRU eviction keeps
9
9
  * memory bounded. Hit rate >70% by the 2nd gate.
10
10
  *
11
- * @since v5.0.0
12
11
  */
13
12
  export interface CacheStats {
14
13
  hits: number;
@@ -8,7 +8,6 @@
8
8
  * Now: one cache per scan, all gates share it. LRU eviction keeps
9
9
  * memory bounded. Hit rate >70% by the 2nd gate.
10
10
  *
11
- * @since v5.0.0
12
11
  */
13
12
  import fs from 'fs-extra';
14
13
  import path from 'path';
@@ -7,7 +7,6 @@
7
7
  * Uses JSONL (not JSON) to avoid read-modify-write race conditions
8
8
  * when multiple agents run checks concurrently.
9
9
  *
10
- * @since v2.17.0
11
10
  */
12
11
  export interface ScoreEntry {
13
12
  timestamp: string;
@@ -7,7 +7,6 @@
7
7
  * Uses JSONL (not JSON) to avoid read-modify-write race conditions
8
8
  * when multiple agents run checks concurrently.
9
9
  *
10
- * @since v2.17.0
11
10
  */
12
11
  import * as fs from 'fs';
13
12
  import * as path from 'path';
@@ -15,7 +15,6 @@
15
15
  * Data source: ~/.rigour/rigour.db (scans + findings tables)
16
16
  * All computation is read-only — no writes to DB.
17
17
  *
18
- * @since v5.0.0
19
18
  */
20
19
  export type DriftDirection = 'improving' | 'stable' | 'degrading';
21
20
  /** A single data point in a time series. */
@@ -94,7 +93,7 @@ export interface TemporalDriftReport {
94
93
  * @param cwd - Project root path (used to derive repo name)
95
94
  * @param maxScans - Max scans to analyze (default 200)
96
95
  */
97
- export declare function generateTemporalDriftReport(cwd: string, maxScans?: number): TemporalDriftReport | null;
96
+ export declare function generateTemporalDriftReport(cwd: string, maxScans?: number): Promise<TemporalDriftReport | null>;
98
97
  /**
99
98
  * Get a formatted summary string for CLI/MCP output.
100
99
  */
@@ -15,7 +15,6 @@
15
15
  * Data source: ~/.rigour/rigour.db (scans + findings tables)
16
16
  * All computation is read-only — no writes to DB.
17
17
  *
18
- * @since v5.0.0
19
18
  */
20
19
  import { openDatabase } from '../storage/db.js';
21
20
  import { Logger } from '../utils/logger.js';
@@ -89,8 +88,8 @@ function toWeekKey(timestamp) {
89
88
  * @param cwd - Project root path (used to derive repo name)
90
89
  * @param maxScans - Max scans to analyze (default 200)
91
90
  */
92
- export function generateTemporalDriftReport(cwd, maxScans = 200) {
93
- const db = openDatabase();
91
+ export async function generateTemporalDriftReport(cwd, maxScans = 200) {
92
+ const db = await openDatabase();
94
93
  if (!db) {
95
94
  Logger.warn('Temporal drift: SQLite not available');
96
95
  return null;
@@ -98,20 +97,20 @@ export function generateTemporalDriftReport(cwd, maxScans = 200) {
98
97
  const repo = path.basename(cwd);
99
98
  try {
100
99
  // 1. Load all scans (oldest first)
101
- const scans = db.db.prepare(`
100
+ const scans = await db.all(`
102
101
  SELECT * FROM scans WHERE repo = ?
103
102
  ORDER BY timestamp ASC LIMIT ?
104
- `).all(repo, maxScans);
103
+ `, repo, maxScans);
105
104
  if (scans.length < 3) {
106
105
  return createEmptyReport(repo, scans.length);
107
106
  }
108
107
  // 2. Load per-scan provenance failure counts
109
108
  const provenanceByScan = new Map();
110
109
  for (const scan of scans) {
111
- const counts = db.db.prepare(`
110
+ const counts = await db.all(`
112
111
  SELECT provenance, COUNT(*) as cnt FROM findings
113
112
  WHERE scan_id = ? GROUP BY provenance
114
- `).all(scan.id);
113
+ `, scan.id);
115
114
  const breakdown = { aiDrift: 0, structural: 0, security: 0 };
116
115
  for (const row of counts) {
117
116
  if (row.provenance === 'ai-drift')
@@ -233,7 +232,7 @@ export function generateTemporalDriftReport(cwd, maxScans = 200) {
233
232
  return null;
234
233
  }
235
234
  finally {
236
- db.close();
235
+ await db.close();
237
236
  }
238
237
  }
239
238
  // ─── Helpers ────────────────────────────────────────────────────────
@@ -1,19 +1,35 @@
1
1
  declare const RIGOUR_DIR: string;
2
2
  declare const DB_PATH: string;
3
+ /**
4
+ * Promisified wrapper around a node-sqlite3 Database instance.
5
+ * Provides run/get/all/exec methods that return Promises.
6
+ */
3
7
  export interface RigourDB {
4
- db: any;
5
- close(): void;
8
+ /** Execute a write statement (INSERT/UPDATE/DELETE). Returns { changes }. */
9
+ run(sql: string, ...params: any[]): Promise<{
10
+ changes: number;
11
+ lastID: number;
12
+ }>;
13
+ /** Fetch a single row. */
14
+ get(sql: string, ...params: any[]): Promise<any>;
15
+ /** Fetch all rows. */
16
+ all(sql: string, ...params: any[]): Promise<any[]>;
17
+ /** Execute raw SQL (multi-statement OK). */
18
+ exec(sql: string): Promise<void>;
19
+ /** Close the database connection. */
20
+ close(): Promise<void>;
21
+ /** Run multiple operations atomically via BEGIN/COMMIT/ROLLBACK. */
22
+ transaction<T>(fn: (db: RigourDB) => Promise<T>): Promise<T>;
6
23
  }
7
24
  /**
8
25
  * Open (or create) the Rigour SQLite database.
9
- * Returns null if better-sqlite3 is not available.
26
+ * Returns null if sqlite3 is not available.
10
27
  */
11
- export declare function openDatabase(dbPath?: string): RigourDB | null;
28
+ export declare function openDatabase(dbPath?: string): Promise<RigourDB | null>;
12
29
  /**
13
30
  * Compact the database — prune old data, reclaim disk space.
14
- * Retention policy: keep last `retainDays` of findings, merge old patterns.
15
31
  */
16
- export declare function compactDatabase(retainDays?: number): CompactResult;
32
+ export declare function compactDatabase(retainDays?: number): Promise<CompactResult>;
17
33
  export interface CompactResult {
18
34
  pruned: number;
19
35
  patternsDecayed: number;
@@ -29,7 +45,7 @@ export declare function getDatabaseSize(): number;
29
45
  */
30
46
  export declare function resetDatabase(): void;
31
47
  /**
32
- * Check if SQLite is available (better-sqlite3 installed)
48
+ * Check if SQLite is available (sqlite3 installed)
33
49
  */
34
50
  export declare function isSQLiteAvailable(): boolean;
35
51
  export { RIGOUR_DIR, DB_PATH };