@rigour-labs/core 2.21.2 → 3.0.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 (101) hide show
  1. package/README.md +58 -0
  2. package/dist/context.test.js +2 -3
  3. package/dist/environment.test.js +2 -1
  4. package/dist/gates/agent-team.d.ts +2 -1
  5. package/dist/gates/agent-team.js +1 -0
  6. package/dist/gates/base.d.ts +4 -2
  7. package/dist/gates/base.js +5 -1
  8. package/dist/gates/checkpoint.d.ts +2 -1
  9. package/dist/gates/checkpoint.js +3 -2
  10. package/dist/gates/content.js +1 -1
  11. package/dist/gates/context-window-artifacts.d.ts +34 -0
  12. package/dist/gates/context-window-artifacts.js +214 -0
  13. package/dist/gates/context.d.ts +2 -1
  14. package/dist/gates/context.js +4 -3
  15. package/dist/gates/coverage.js +3 -1
  16. package/dist/gates/dependency.js +5 -5
  17. package/dist/gates/duplication-drift.d.ts +33 -0
  18. package/dist/gates/duplication-drift.js +190 -0
  19. package/dist/gates/environment.js +4 -4
  20. package/dist/gates/file.js +1 -1
  21. package/dist/gates/hallucinated-imports.d.ts +63 -0
  22. package/dist/gates/hallucinated-imports.js +406 -0
  23. package/dist/gates/inconsistent-error-handling.d.ts +39 -0
  24. package/dist/gates/inconsistent-error-handling.js +236 -0
  25. package/dist/gates/promise-safety.d.ts +68 -0
  26. package/dist/gates/promise-safety.js +509 -0
  27. package/dist/gates/retry-loop-breaker.d.ts +2 -1
  28. package/dist/gates/retry-loop-breaker.js +2 -1
  29. package/dist/gates/runner.js +62 -1
  30. package/dist/gates/safety.d.ts +2 -1
  31. package/dist/gates/safety.js +2 -1
  32. package/dist/gates/security-patterns.d.ts +2 -1
  33. package/dist/gates/security-patterns.js +2 -1
  34. package/dist/gates/structure.js +1 -1
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +1 -0
  37. package/dist/services/fix-packet-service.d.ts +0 -1
  38. package/dist/services/fix-packet-service.js +9 -14
  39. package/dist/services/score-history.d.ts +54 -0
  40. package/dist/services/score-history.js +122 -0
  41. package/dist/templates/index.js +195 -0
  42. package/dist/types/fix-packet.d.ts +5 -5
  43. package/dist/types/fix-packet.js +1 -1
  44. package/dist/types/index.d.ts +430 -0
  45. package/dist/types/index.js +57 -0
  46. package/package.json +21 -1
  47. package/src/context.test.ts +0 -256
  48. package/src/discovery.test.ts +0 -88
  49. package/src/discovery.ts +0 -112
  50. package/src/environment.test.ts +0 -115
  51. package/src/gates/agent-team.test.ts +0 -134
  52. package/src/gates/agent-team.ts +0 -210
  53. package/src/gates/ast-handlers/base.ts +0 -13
  54. package/src/gates/ast-handlers/python.ts +0 -145
  55. package/src/gates/ast-handlers/python_parser.py +0 -181
  56. package/src/gates/ast-handlers/typescript.ts +0 -264
  57. package/src/gates/ast-handlers/universal.ts +0 -184
  58. package/src/gates/ast.ts +0 -54
  59. package/src/gates/base.ts +0 -27
  60. package/src/gates/checkpoint.test.ts +0 -135
  61. package/src/gates/checkpoint.ts +0 -311
  62. package/src/gates/content.ts +0 -50
  63. package/src/gates/context.ts +0 -267
  64. package/src/gates/coverage.ts +0 -74
  65. package/src/gates/dependency.ts +0 -108
  66. package/src/gates/environment.ts +0 -94
  67. package/src/gates/file.ts +0 -42
  68. package/src/gates/retry-loop-breaker.ts +0 -151
  69. package/src/gates/runner.ts +0 -156
  70. package/src/gates/safety.ts +0 -56
  71. package/src/gates/security-patterns.test.ts +0 -162
  72. package/src/gates/security-patterns.ts +0 -305
  73. package/src/gates/structure.ts +0 -36
  74. package/src/index.ts +0 -13
  75. package/src/pattern-index/embeddings.ts +0 -84
  76. package/src/pattern-index/index.ts +0 -59
  77. package/src/pattern-index/indexer.test.ts +0 -276
  78. package/src/pattern-index/indexer.ts +0 -1023
  79. package/src/pattern-index/matcher.test.ts +0 -293
  80. package/src/pattern-index/matcher.ts +0 -493
  81. package/src/pattern-index/overrides.ts +0 -235
  82. package/src/pattern-index/security.ts +0 -151
  83. package/src/pattern-index/staleness.test.ts +0 -313
  84. package/src/pattern-index/staleness.ts +0 -568
  85. package/src/pattern-index/types.ts +0 -339
  86. package/src/safety.test.ts +0 -53
  87. package/src/services/adaptive-thresholds.test.ts +0 -189
  88. package/src/services/adaptive-thresholds.ts +0 -275
  89. package/src/services/context-engine.ts +0 -104
  90. package/src/services/fix-packet-service.ts +0 -42
  91. package/src/services/state-service.ts +0 -138
  92. package/src/smoke.test.ts +0 -18
  93. package/src/templates/index.ts +0 -312
  94. package/src/types/fix-packet.ts +0 -32
  95. package/src/types/index.ts +0 -159
  96. package/src/utils/logger.ts +0 -43
  97. package/src/utils/scanner.test.ts +0 -37
  98. package/src/utils/scanner.ts +0 -43
  99. package/tsconfig.json +0 -10
  100. package/vitest.config.ts +0 -7
  101. package/vitest.setup.ts +0 -30
@@ -1,339 +0,0 @@
1
- /**
2
- * Pattern Index - Types
3
- *
4
- * Core type definitions for the Pattern Index system.
5
- * Rigour's Pattern Index prevents AI from reinventing existing code.
6
- */
7
-
8
- /**
9
- * All supported pattern types that can be indexed.
10
- * Organized by category for clarity.
11
- */
12
- export type PatternType =
13
- // === CODE PATTERNS ===
14
- | 'function' // Standalone functions, utilities
15
- | 'class' // Classes
16
- | 'method' // Class methods (indexed separately for reuse)
17
- | 'component' // React/Vue/Svelte components
18
- | 'hook' // React hooks (useX)
19
- | 'decorator' // Python/TS decorators
20
- | 'middleware' // Express/FastAPI middleware
21
-
22
- // === DATA PATTERNS ===
23
- | 'type' // TypeScript types
24
- | 'interface' // TypeScript interfaces
25
- | 'schema' // Zod, Yup, JSON Schema validators
26
- | 'model' // Database models (Prisma, SQLAlchemy, etc.)
27
- | 'enum' // Enumerations
28
-
29
- // === CONFIGURATION ===
30
- | 'constant' // Constants, magic values
31
- | 'config' // Configuration objects
32
- | 'env' // Environment variable patterns
33
-
34
- // === API PATTERNS ===
35
- | 'route' // API routes/endpoints
36
- | 'handler' // Route handlers
37
- | 'resolver' // GraphQL resolvers
38
- | 'rpc' // tRPC procedures
39
-
40
- // === STATE PATTERNS ===
41
- | 'store' // State stores (Zustand, Redux)
42
- | 'reducer' // Redux reducers
43
- | 'action' // Actions/mutations
44
- | 'selector' // State selectors
45
-
46
- // === ERROR HANDLING ===
47
- | 'error' // Custom error classes
48
- | 'exception' // Exception types
49
-
50
- // === TESTING ===
51
- | 'mock' // Mock objects/functions
52
- | 'fixture' // Test fixtures
53
- | 'factory' // Test factories
54
-
55
- // === INFRASTRUCTURE ===
56
- | 'command' // CLI commands
57
- | 'task' // Background tasks/jobs
58
- | 'event' // Event types/handlers
59
- | 'protocol'; // Python protocols, abstract classes
60
-
61
- /**
62
- * A single indexed pattern entry.
63
- */
64
- export interface PatternEntry {
65
- /** Unique identifier (hash of file + name) */
66
- id: string;
67
-
68
- /** The type of pattern */
69
- type: PatternType;
70
-
71
- /** Name of the pattern (e.g., "formatDate") */
72
- name: string;
73
-
74
- /** Relative path to the file */
75
- file: string;
76
-
77
- /** Line number where the pattern is defined */
78
- line: number;
79
-
80
- /** End line number */
81
- endLine: number;
82
-
83
- /** Function/method signature if applicable */
84
- signature: string;
85
-
86
- /** Description from JSDoc/docstring */
87
- description: string;
88
-
89
- /** Extracted semantic keywords for matching */
90
- keywords: string[];
91
-
92
- /** Content hash for change detection */
93
- hash: string;
94
-
95
- /** Is this pattern exported? */
96
- exported: boolean;
97
-
98
- /** How many files import this pattern */
99
- usageCount: number; // How many files import this?
100
-
101
- /** User-defined category/grouping */
102
- category?: string; // User-defined grouping
103
- embedding?: number[]; // Vector embedding for semantic search
104
-
105
- /** Last indexed timestamp */
106
- indexedAt: string;
107
- }
108
-
109
- /**
110
- * The complete pattern index structure.
111
- */
112
- export interface PatternIndex {
113
- /** Index format version */
114
- version: string;
115
-
116
- /** When the index was last updated */
117
- lastUpdated: string;
118
-
119
- /** Root directory that was indexed */
120
- rootDir: string;
121
-
122
- /** All indexed patterns */
123
- patterns: PatternEntry[];
124
-
125
- /** Index statistics */
126
- stats: PatternIndexStats;
127
-
128
- /** Files that were indexed */
129
- files: IndexedFile[];
130
- }
131
-
132
- /**
133
- * Statistics about the pattern index.
134
- */
135
- export interface PatternIndexStats {
136
- totalPatterns: number;
137
- totalFiles: number;
138
- byType: Record<PatternType, number>;
139
- indexDurationMs: number;
140
- }
141
-
142
- /**
143
- * Information about an indexed file.
144
- */
145
- export interface IndexedFile {
146
- path: string;
147
- hash: string;
148
- patternCount: number;
149
- indexedAt: string;
150
- }
151
-
152
- /**
153
- * Configuration for the pattern indexer.
154
- */
155
- export interface PatternIndexConfig {
156
- /** Directories to index (defaults to src/) */
157
- include: string[];
158
-
159
- /** Directories to exclude */
160
- exclude: string[];
161
-
162
- /** File extensions to index */
163
- extensions: string[];
164
-
165
- /** Whether to index test files */
166
- indexTests: boolean;
167
-
168
- /** Whether to index node_modules */
169
- indexNodeModules: boolean;
170
-
171
- /** Minimum pattern name length to index */
172
- minNameLength: number;
173
-
174
- /** Custom categories for patterns */
175
- categories: Record<string, string[]>;
176
-
177
- /** Whether to generate semantic embeddings for patterns */
178
- useEmbeddings?: boolean;
179
- }
180
-
181
- /**
182
- * Result from matching against the pattern index.
183
- */
184
- export interface PatternMatchResult {
185
- /** The query that was matched */
186
- query: string;
187
-
188
- /** All matches found */
189
- matches: PatternMatch[];
190
-
191
- /** Suggestion for what to do */
192
- suggestion: string;
193
-
194
- /** Whether human override is available */
195
- canOverride: boolean;
196
-
197
- /** Overall status */
198
- status: 'FOUND_SIMILAR' | 'NO_MATCH' | 'OVERRIDE_ALLOWED';
199
-
200
- /** Recommended action */
201
- action: 'BLOCK' | 'WARN' | 'ALLOW';
202
- }
203
-
204
- /**
205
- * A single pattern match.
206
- */
207
- export interface PatternMatch {
208
- /** The matched pattern */
209
- pattern: PatternEntry;
210
-
211
- /** How the match was determined */
212
- matchType: 'exact' | 'fuzzy' | 'signature' | 'semantic';
213
-
214
- /** Confidence score 0-100 */
215
- confidence: number;
216
-
217
- /** Human-readable reason for the match */
218
- reason: string;
219
- }
220
-
221
- /**
222
- * Human override entry.
223
- */
224
- export interface PatternOverride {
225
- /** Pattern name or glob */
226
- pattern: string;
227
-
228
- /** Why the override was granted */
229
- reason: string;
230
-
231
- /** When the override expires */
232
- expiresAt?: string;
233
-
234
- /** Who approved the override */
235
- approvedBy?: string;
236
-
237
- /** When the override was created */
238
- createdAt: string;
239
- }
240
-
241
- /**
242
- * Staleness detection result.
243
- */
244
- export interface StalenessResult {
245
- /** Overall status */
246
- status: 'FRESH' | 'STALE' | 'DEPRECATED';
247
-
248
- /** All staleness issues found */
249
- issues: StalenessIssue[];
250
-
251
- /** Project context (versions) */
252
- projectContext: Record<string, string>;
253
- }
254
-
255
- /**
256
- * A single staleness issue.
257
- */
258
- export interface StalenessIssue {
259
- /** Line number */
260
- line: number;
261
-
262
- /** The stale pattern */
263
- pattern: string;
264
-
265
- /** Severity */
266
- severity: 'error' | 'warning' | 'info';
267
-
268
- /** Why it's stale */
269
- reason: string;
270
-
271
- /** What to use instead */
272
- replacement: string;
273
-
274
- /** Link to documentation */
275
- docs?: string;
276
- }
277
-
278
- /**
279
- * Security/CVE entry for a package.
280
- */
281
- export interface SecurityEntry {
282
- /** CVE Identifier (e.g., CVE-2021-1234) */
283
- cveId: string;
284
-
285
- /** Package name */
286
- packageName: string;
287
-
288
- /** Vulnerable version range (semver) */
289
- vulnerableRange: string;
290
-
291
- /** Severity level */
292
- severity: 'critical' | 'high' | 'moderate' | 'low';
293
-
294
- /** Brief description of the vulnerability */
295
- title: string;
296
-
297
- /** Link to advisory */
298
- url: string;
299
-
300
- /** The version of the package in the current project */
301
- currentVersion?: string;
302
- }
303
-
304
- /**
305
- * Result from security check.
306
- */
307
- export interface SecurityResult {
308
- /** Overall security status */
309
- status: 'SECURE' | 'VULNERABLE';
310
-
311
- /** All CVEs/vulnerabilities found */
312
- vulnerabilities: SecurityEntry[];
313
- }
314
-
315
- /**
316
- * Deprecation entry in the deprecation database.
317
- */
318
- export interface DeprecationEntry {
319
- /** Pattern to match (can be regex) */
320
- pattern: string;
321
-
322
- /** Library this belongs to */
323
- library?: string;
324
-
325
- /** Version when deprecated */
326
- deprecatedIn: string;
327
-
328
- /** Suggested replacement */
329
- replacement: string;
330
-
331
- /** Severity level */
332
- severity: 'error' | 'warning' | 'info';
333
-
334
- /** Additional context */
335
- reason?: string;
336
-
337
- /** Documentation link */
338
- docs?: string;
339
- }
@@ -1,53 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { FileGuardGate } from './gates/safety.js';
3
- import { Gates } from './types/index.js';
4
- import { execa } from 'execa';
5
-
6
- vi.mock('execa');
7
-
8
- describe('FileGuardGate', () => {
9
- const config: Gates = {
10
- safety: {
11
- protected_paths: ['docs/'],
12
- max_files_changed_per_cycle: 10
13
- }
14
- } as any;
15
-
16
- it('should flag modified (M) protected files', async () => {
17
- const gate = new FileGuardGate(config);
18
- vi.mocked(execa).mockResolvedValueOnce({ stdout: ' M docs/SPEC.md\n' } as any);
19
-
20
- const failures = await gate.run({ cwd: '/test', record: {} as any });
21
- expect(failures).toHaveLength(1);
22
- expect(failures[0].title).toContain("Protected file 'docs/SPEC.md' was modified.");
23
- });
24
-
25
- it('should flag added (A) protected files', async () => {
26
- const gate = new FileGuardGate(config);
27
- vi.mocked(execa).mockResolvedValueOnce({ stdout: 'A docs/NEW.md\n' } as any);
28
-
29
- const failures = await gate.run({ cwd: '/test', record: {} as any });
30
- expect(failures).toHaveLength(1);
31
- expect(failures[0].title).toContain("Protected file 'docs/NEW.md' was modified.");
32
- });
33
-
34
- it('should NOT flag untracked (??) protected files', async () => {
35
- const gate = new FileGuardGate(config);
36
- vi.mocked(execa).mockResolvedValueOnce({ stdout: '?? docs/UNTRAKED.md\n' } as any);
37
-
38
- const failures = await gate.run({ cwd: '/test', record: {} as any });
39
- expect(failures).toHaveLength(0);
40
- });
41
-
42
- it('should correctly handle multiple mixed statuses', async () => {
43
- const gate = new FileGuardGate(config);
44
- vi.mocked(execa).mockResolvedValueOnce({
45
- stdout: ' M docs/MODIFIED.md\n?? docs/NEW_UNTRACKED.md\n D docs/DELETED.md\n'
46
- } as any);
47
-
48
- const failures = await gate.run({ cwd: '/test', record: {} as any });
49
- expect(failures).toHaveLength(2);
50
- expect(failures.map(f => f.title)).toContain("Protected file 'docs/MODIFIED.md' was modified.");
51
- expect(failures.map(f => f.title)).toContain("Protected file 'docs/DELETED.md' was modified.");
52
- });
53
- });
@@ -1,189 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import {
3
- detectComplexityTier,
4
- calculateAdaptiveThresholds,
5
- recordGateRun,
6
- getQualityTrend,
7
- clearAdaptiveHistory,
8
- getAdaptiveSummary,
9
- } from './adaptive-thresholds.js';
10
- import * as fs from 'fs';
11
- import * as path from 'path';
12
- import * as os from 'os';
13
-
14
- describe('AdaptiveThresholds', () => {
15
- let testDir: string;
16
-
17
- beforeEach(() => {
18
- testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'adaptive-test-'));
19
- });
20
-
21
- afterEach(() => {
22
- clearAdaptiveHistory(testDir);
23
- fs.rmSync(testDir, { recursive: true, force: true });
24
- });
25
-
26
- describe('detectComplexityTier', () => {
27
- it('should detect hobby tier for small projects', () => {
28
- const tier = detectComplexityTier({ fileCount: 20 });
29
- expect(tier).toBe('hobby');
30
- });
31
-
32
- it('should detect startup tier for medium projects', () => {
33
- const tier = detectComplexityTier({ fileCount: 100 });
34
- expect(tier).toBe('startup');
35
- });
36
-
37
- it('should detect enterprise tier for large projects', () => {
38
- const tier = detectComplexityTier({ fileCount: 600 });
39
- expect(tier).toBe('enterprise');
40
- });
41
-
42
- it('should consider commit count for tier detection', () => {
43
- const tier = detectComplexityTier({ fileCount: 50, commitCount: 1500 });
44
- expect(tier).toBe('enterprise');
45
- });
46
- });
47
-
48
- describe('calculateAdaptiveThresholds', () => {
49
- it('should return lenient thresholds for hobby tier', () => {
50
- const adjustments = calculateAdaptiveThresholds(
51
- testDir,
52
- { fileCount: 20 }
53
- );
54
-
55
- expect(adjustments.tier).toBe('hobby');
56
- expect(adjustments.coverageThreshold).toBeLessThan(80);
57
- expect(adjustments.securityBlockLevel).toBe('critical');
58
- expect(adjustments.leniencyFactor).toBeGreaterThan(0.5);
59
- });
60
-
61
- it('should return strict thresholds for enterprise tier', () => {
62
- const adjustments = calculateAdaptiveThresholds(
63
- testDir,
64
- { fileCount: 600 }
65
- );
66
-
67
- expect(adjustments.tier).toBe('enterprise');
68
- expect(adjustments.coverageThreshold).toBe(80);
69
- expect(adjustments.securityBlockLevel).toBe('medium');
70
- expect(adjustments.leniencyFactor).toBeLessThan(0.5);
71
- });
72
-
73
- it('should respect forced_tier config', () => {
74
- const adjustments = calculateAdaptiveThresholds(
75
- testDir,
76
- { fileCount: 20 },
77
- { forced_tier: 'enterprise' }
78
- );
79
-
80
- expect(adjustments.tier).toBe('enterprise');
81
- });
82
-
83
- it('should include reasoning for adjustments', () => {
84
- const adjustments = calculateAdaptiveThresholds(
85
- testDir,
86
- { fileCount: 100 }
87
- );
88
-
89
- expect(adjustments.reasoning.length).toBeGreaterThan(0);
90
- expect(adjustments.reasoning.some(r => r.includes('tier'))).toBe(true);
91
- });
92
- });
93
-
94
- describe('historical tracking', () => {
95
- it('should record gate runs', () => {
96
- recordGateRun(testDir, 5, 2, 10);
97
- recordGateRun(testDir, 6, 1, 5);
98
-
99
- const historyPath = path.join(testDir, '.rigour', 'adaptive-history.json');
100
- expect(fs.existsSync(historyPath)).toBe(true);
101
-
102
- const history = JSON.parse(fs.readFileSync(historyPath, 'utf-8'));
103
- expect(history.runs).toHaveLength(2);
104
- });
105
-
106
- it('should return stable trend for new projects', () => {
107
- const trend = getQualityTrend(testDir);
108
- expect(trend).toBe('stable');
109
- });
110
-
111
- it('should detect improving trend', () => {
112
- // Record 20 runs: older ones with high failures, recent with low
113
- for (let i = 0; i < 10; i++) {
114
- recordGateRun(testDir, 3, 5, 20);
115
- }
116
- for (let i = 0; i < 10; i++) {
117
- recordGateRun(testDir, 7, 1, 5);
118
- }
119
-
120
- const trend = getQualityTrend(testDir);
121
- expect(trend).toBe('improving');
122
- });
123
-
124
- it('should detect degrading trend', () => {
125
- // Record 20 runs: older ones with low failures, recent with high
126
- for (let i = 0; i < 10; i++) {
127
- recordGateRun(testDir, 7, 1, 3);
128
- }
129
- for (let i = 0; i < 10; i++) {
130
- recordGateRun(testDir, 3, 5, 20);
131
- }
132
-
133
- const trend = getQualityTrend(testDir);
134
- expect(trend).toBe('degrading');
135
- });
136
- });
137
-
138
- describe('trend-based adjustments', () => {
139
- it('should relax thresholds for improving trend', () => {
140
- // Create improving history
141
- for (let i = 0; i < 10; i++) {
142
- recordGateRun(testDir, 3, 5, 20);
143
- }
144
- for (let i = 0; i < 10; i++) {
145
- recordGateRun(testDir, 7, 1, 5);
146
- }
147
-
148
- const adjustments = calculateAdaptiveThresholds(
149
- testDir,
150
- { fileCount: 100 }
151
- );
152
-
153
- expect(adjustments.trend).toBe('improving');
154
- expect(adjustments.reasoning.some(r => r.includes('bonus'))).toBe(true);
155
- });
156
-
157
- it('should tighten thresholds for degrading trend', () => {
158
- // Create degrading history
159
- for (let i = 0; i < 10; i++) {
160
- recordGateRun(testDir, 7, 1, 3);
161
- }
162
- for (let i = 0; i < 10; i++) {
163
- recordGateRun(testDir, 3, 5, 20);
164
- }
165
-
166
- const adjustments = calculateAdaptiveThresholds(
167
- testDir,
168
- { fileCount: 100 }
169
- );
170
-
171
- expect(adjustments.trend).toBe('degrading');
172
- expect(adjustments.reasoning.some(r => r.includes('tightened'))).toBe(true);
173
- });
174
- });
175
-
176
- describe('getAdaptiveSummary', () => {
177
- it('should return formatted summary string', () => {
178
- const adjustments = calculateAdaptiveThresholds(
179
- testDir,
180
- { fileCount: 100 }
181
- );
182
-
183
- const summary = getAdaptiveSummary(adjustments);
184
- expect(summary).toContain('STARTUP');
185
- expect(summary).toContain('Coverage:');
186
- expect(summary).toContain('Quality:');
187
- });
188
- });
189
- });