guardrail-core 1.0.0 → 2.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 (74) hide show
  1. package/dist/__tests__/autopilot-enterprise.test.d.ts +7 -0
  2. package/dist/__tests__/autopilot-enterprise.test.d.ts.map +1 -0
  3. package/dist/__tests__/autopilot-enterprise.test.js +334 -0
  4. package/dist/autopilot/autopilot-runner.d.ts +9 -0
  5. package/dist/autopilot/autopilot-runner.d.ts.map +1 -1
  6. package/dist/autopilot/autopilot-runner.js +182 -1
  7. package/dist/autopilot/types.d.ts +18 -2
  8. package/dist/autopilot/types.d.ts.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/smells/index.d.ts +59 -0
  13. package/dist/smells/index.d.ts.map +1 -0
  14. package/dist/smells/index.js +251 -0
  15. package/package.json +19 -2
  16. package/src/__tests__/autopilot.test.ts +0 -196
  17. package/src/__tests__/tier-config.test.ts +0 -289
  18. package/src/__tests__/utils/hash-inline.test.ts +0 -76
  19. package/src/__tests__/utils/hash.test.ts +0 -119
  20. package/src/__tests__/utils/simple.test.ts +0 -10
  21. package/src/__tests__/utils/utils-simple.test.ts +0 -5
  22. package/src/__tests__/utils/utils.test.ts +0 -203
  23. package/src/autopilot/autopilot-runner.ts +0 -503
  24. package/src/autopilot/index.ts +0 -6
  25. package/src/autopilot/types.ts +0 -119
  26. package/src/cache/index.ts +0 -7
  27. package/src/cache/redis-cache.d.ts +0 -155
  28. package/src/cache/redis-cache.d.ts.map +0 -1
  29. package/src/cache/redis-cache.ts +0 -517
  30. package/src/ci/github-actions.ts +0 -335
  31. package/src/ci/index.ts +0 -12
  32. package/src/ci/pre-commit.ts +0 -338
  33. package/src/db/usage-schema.prisma +0 -114
  34. package/src/entitlements.ts +0 -570
  35. package/src/env.d.ts +0 -68
  36. package/src/env.d.ts.map +0 -1
  37. package/src/env.ts +0 -247
  38. package/src/fix-packs/__tests__/generate-fix-packs.test.ts +0 -317
  39. package/src/fix-packs/generate-fix-packs.ts +0 -577
  40. package/src/fix-packs/index.ts +0 -8
  41. package/src/fix-packs/types.ts +0 -206
  42. package/src/index.d.ts +0 -7
  43. package/src/index.d.ts.map +0 -1
  44. package/src/index.ts +0 -12
  45. package/src/metrics/prometheus.d.ts +0 -104
  46. package/src/metrics/prometheus.d.ts.map +0 -1
  47. package/src/metrics/prometheus.ts +0 -446
  48. package/src/quota-ledger.ts +0 -548
  49. package/src/rbac/__tests__/permissions.test.ts +0 -446
  50. package/src/rbac/index.ts +0 -46
  51. package/src/rbac/permissions.ts +0 -301
  52. package/src/rbac/types.ts +0 -298
  53. package/src/tier-config.json +0 -157
  54. package/src/tier-config.ts +0 -815
  55. package/src/types.d.ts +0 -365
  56. package/src/types.d.ts.map +0 -1
  57. package/src/types.ts +0 -441
  58. package/src/utils.d.ts +0 -36
  59. package/src/utils.d.ts.map +0 -1
  60. package/src/utils.ts +0 -140
  61. package/src/verified-autofix/__tests__/format-validator.test.ts +0 -335
  62. package/src/verified-autofix/__tests__/pipeline.test.ts +0 -419
  63. package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +0 -241
  64. package/src/verified-autofix/__tests__/workspace.test.ts +0 -373
  65. package/src/verified-autofix/format-validator.ts +0 -517
  66. package/src/verified-autofix/index.ts +0 -63
  67. package/src/verified-autofix/pipeline.ts +0 -403
  68. package/src/verified-autofix/repo-fingerprint.ts +0 -459
  69. package/src/verified-autofix/workspace.ts +0 -531
  70. package/src/verified-autofix.ts +0 -1187
  71. package/src/visualization/dependency-graph.d.ts +0 -85
  72. package/src/visualization/dependency-graph.d.ts.map +0 -1
  73. package/src/visualization/dependency-graph.ts +0 -495
  74. package/src/visualization/index.ts +0 -5
@@ -1,419 +0,0 @@
1
- /**
2
- * Pipeline Tests - Verified AutoFix Pipeline
3
- *
4
- * Tests for the full verification pipeline:
5
- * - Format validation stage
6
- * - Diff application stage
7
- * - Verification stage (typecheck, build, tests)
8
- * - Top 3 failure context extraction
9
- */
10
-
11
- import * as fs from 'fs';
12
- import * as path from 'path';
13
- import * as os from 'os';
14
- import {
15
- VerifiedAutofixPipeline,
16
- formatPipelineResult,
17
- formatPipelineResultJson,
18
- type PipelineResult,
19
- } from '../pipeline';
20
- import { validateAgentOutput } from '../format-validator';
21
-
22
- describe('VerifiedAutofixPipeline', () => {
23
- let tempDir: string;
24
- let pipeline: VerifiedAutofixPipeline;
25
-
26
- beforeEach(async () => {
27
- tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pipeline-test-'));
28
- pipeline = new VerifiedAutofixPipeline();
29
-
30
- // Create minimal project structure
31
- await fs.promises.writeFile(
32
- path.join(tempDir, 'package.json'),
33
- JSON.stringify({
34
- name: 'test-project',
35
- version: '1.0.0',
36
- scripts: {
37
- build: 'echo "build"',
38
- test: 'echo "test"',
39
- },
40
- })
41
- );
42
- });
43
-
44
- afterEach(async () => {
45
- await fs.promises.rm(tempDir, { recursive: true, force: true });
46
- });
47
-
48
- describe('validation stage', () => {
49
- it('rejects missing agent output file', async () => {
50
- const result = await pipeline.run({
51
- projectPath: tempDir,
52
- agentOutputFile: '/nonexistent/file.json',
53
- skipEntitlements: true,
54
- });
55
-
56
- expect(result.success).toBe(false);
57
- expect(result.stage).toBe('validate');
58
- expect(result.errors.some(e => e.includes('not found'))).toBe(true);
59
- });
60
-
61
- it('rejects invalid JSON in agent output', async () => {
62
- const outputFile = path.join(tempDir, 'output.json');
63
- await fs.promises.writeFile(outputFile, 'not valid json');
64
-
65
- const result = await pipeline.run({
66
- projectPath: tempDir,
67
- agentOutputFile: outputFile,
68
- skipEntitlements: true,
69
- });
70
-
71
- expect(result.success).toBe(false);
72
- expect(result.errors.some(e => e.includes('Invalid JSON'))).toBe(true);
73
- });
74
-
75
- it('rejects wrong format field', async () => {
76
- const outputFile = path.join(tempDir, 'output.json');
77
- await fs.promises.writeFile(outputFile, JSON.stringify({
78
- format: 'wrong-format',
79
- diff: '',
80
- commands: [],
81
- tests: [],
82
- notes: '',
83
- }));
84
-
85
- const result = await pipeline.run({
86
- projectPath: tempDir,
87
- agentOutputFile: outputFile,
88
- skipEntitlements: true,
89
- });
90
-
91
- expect(result.success).toBe(false);
92
- expect(result.errors.some(e => e.includes('guardrail-v1'))).toBe(true);
93
- });
94
-
95
- it('accepts valid guardrail-v1 output with empty diff', async () => {
96
- const outputFile = path.join(tempDir, 'output.json');
97
- await fs.promises.writeFile(outputFile, JSON.stringify({
98
- format: 'guardrail-v1',
99
- diff: '',
100
- commands: [],
101
- tests: [],
102
- notes: 'No changes needed',
103
- }));
104
-
105
- const result = await pipeline.run({
106
- projectPath: tempDir,
107
- agentOutputFile: outputFile,
108
- skipEntitlements: true,
109
- });
110
-
111
- expect(result.success).toBe(true);
112
- expect(result.stage).toBe('done');
113
- });
114
-
115
- it('strips markdown fences and warns', async () => {
116
- const outputFile = path.join(tempDir, 'output.json');
117
- await fs.promises.writeFile(outputFile, '```json\n' + JSON.stringify({
118
- format: 'guardrail-v1',
119
- diff: '',
120
- commands: [],
121
- tests: [],
122
- notes: '',
123
- }) + '\n```');
124
-
125
- const result = await pipeline.run({
126
- projectPath: tempDir,
127
- agentOutputFile: outputFile,
128
- skipEntitlements: true,
129
- });
130
-
131
- expect(result.success).toBe(true);
132
- expect(result.warnings.some(w => w.includes('markdown'))).toBe(true);
133
- });
134
- });
135
-
136
- describe('stub detection', () => {
137
- it('blocks TODO comments in diff', async () => {
138
- const outputFile = path.join(tempDir, 'output.json');
139
- await fs.promises.writeFile(outputFile, JSON.stringify({
140
- format: 'guardrail-v1',
141
- diff: `--- a/src/file.ts
142
- +++ b/src/file.ts
143
- @@ -1,1 +1,2 @@
144
- const x = 1;
145
- +// TODO: implement this`,
146
- commands: [],
147
- tests: [],
148
- notes: '',
149
- }));
150
-
151
- const result = await pipeline.run({
152
- projectPath: tempDir,
153
- agentOutputFile: outputFile,
154
- skipEntitlements: true,
155
- });
156
-
157
- expect(result.success).toBe(false);
158
- expect(result.errors.some(e => e.includes('Stub'))).toBe(true);
159
- });
160
-
161
- it('blocks placeholder text in diff', async () => {
162
- const outputFile = path.join(tempDir, 'output.json');
163
- await fs.promises.writeFile(outputFile, JSON.stringify({
164
- format: 'guardrail-v1',
165
- diff: `--- a/src/file.ts
166
- +++ b/src/file.ts
167
- @@ -1,1 +1,2 @@
168
- const x = 1;
169
- +const message = "placeholder content here";`,
170
- commands: [],
171
- tests: [],
172
- notes: '',
173
- }));
174
-
175
- const result = await pipeline.run({
176
- projectPath: tempDir,
177
- agentOutputFile: outputFile,
178
- skipEntitlements: true,
179
- });
180
-
181
- expect(result.success).toBe(false);
182
- expect(result.errors.some(e => e.includes('Stub') || e.includes('placeholder'))).toBe(true);
183
- });
184
-
185
- it('allows clean code without stubs', async () => {
186
- const outputFile = path.join(tempDir, 'output.json');
187
- await fs.promises.writeFile(outputFile, JSON.stringify({
188
- format: 'guardrail-v1',
189
- diff: `--- a/src/file.ts
190
- +++ b/src/file.ts
191
- @@ -1,1 +1,2 @@
192
- const x = 1;
193
- +const y = x + 1;`,
194
- commands: [],
195
- tests: [],
196
- notes: '',
197
- }));
198
-
199
- // Create the source file
200
- await fs.promises.mkdir(path.join(tempDir, 'src'), { recursive: true });
201
- await fs.promises.writeFile(path.join(tempDir, 'src', 'file.ts'), 'const x = 1;\n');
202
-
203
- const result = await pipeline.run({
204
- projectPath: tempDir,
205
- agentOutputFile: outputFile,
206
- dryRun: true,
207
- skipEntitlements: true,
208
- });
209
-
210
- // Should pass validation (stub detection at least)
211
- expect(result.validation?.stubDetection?.hasStubs).toBe(false);
212
- });
213
- });
214
-
215
- describe('path safety', () => {
216
- it('blocks parent directory traversal', async () => {
217
- const outputFile = path.join(tempDir, 'output.json');
218
- await fs.promises.writeFile(outputFile, JSON.stringify({
219
- format: 'guardrail-v1',
220
- diff: `--- a/../../../etc/passwd
221
- +++ b/../../../etc/passwd
222
- @@ -1,1 +1,1 @@
223
- -root
224
- +hacked`,
225
- commands: [],
226
- tests: [],
227
- notes: '',
228
- }));
229
-
230
- const result = await pipeline.run({
231
- projectPath: tempDir,
232
- agentOutputFile: outputFile,
233
- skipEntitlements: true,
234
- });
235
-
236
- expect(result.success).toBe(false);
237
- expect(result.errors.some(e => e.includes('Unsafe') || e.includes('path'))).toBe(true);
238
- });
239
-
240
- it('blocks node_modules modifications', async () => {
241
- const outputFile = path.join(tempDir, 'output.json');
242
- await fs.promises.writeFile(outputFile, JSON.stringify({
243
- format: 'guardrail-v1',
244
- diff: `--- a/node_modules/lodash/index.js
245
- +++ b/node_modules/lodash/index.js
246
- @@ -1,1 +1,1 @@
247
- -module.exports = {};
248
- +module.exports = { hacked: true };`,
249
- commands: [],
250
- tests: [],
251
- notes: '',
252
- }));
253
-
254
- const result = await pipeline.run({
255
- projectPath: tempDir,
256
- agentOutputFile: outputFile,
257
- skipEntitlements: true,
258
- });
259
-
260
- expect(result.success).toBe(false);
261
- });
262
- });
263
-
264
- describe('dry run mode', () => {
265
- it('validates without applying changes', async () => {
266
- const outputFile = path.join(tempDir, 'output.json');
267
- await fs.promises.writeFile(outputFile, JSON.stringify({
268
- format: 'guardrail-v1',
269
- diff: `--- a/src/file.ts
270
- +++ b/src/file.ts
271
- @@ -1,1 +1,2 @@
272
- const x = 1;
273
- +const y = 2;`,
274
- commands: ['npm test'],
275
- tests: ['test/file.test.ts'],
276
- notes: 'Added y constant',
277
- }));
278
-
279
- // Create source file
280
- await fs.promises.mkdir(path.join(tempDir, 'src'), { recursive: true });
281
- await fs.promises.writeFile(path.join(tempDir, 'src', 'file.ts'), 'const x = 1;\n');
282
-
283
- const result = await pipeline.run({
284
- projectPath: tempDir,
285
- agentOutputFile: outputFile,
286
- dryRun: true,
287
- skipEntitlements: true,
288
- });
289
-
290
- expect(result.success).toBe(true);
291
- expect(result.stage).toBe('done');
292
-
293
- // File should NOT be modified in dry run
294
- const content = await fs.promises.readFile(path.join(tempDir, 'src', 'file.ts'), 'utf8');
295
- expect(content).toBe('const x = 1;\n');
296
- });
297
- });
298
-
299
- describe('progress callbacks', () => {
300
- it('calls onProgress for each stage', async () => {
301
- const outputFile = path.join(tempDir, 'output.json');
302
- await fs.promises.writeFile(outputFile, JSON.stringify({
303
- format: 'guardrail-v1',
304
- diff: '',
305
- commands: [],
306
- tests: [],
307
- notes: '',
308
- }));
309
-
310
- const stages: string[] = [];
311
- await pipeline.run({
312
- projectPath: tempDir,
313
- agentOutputFile: outputFile,
314
- skipEntitlements: true,
315
- onProgress: (stage) => {
316
- stages.push(stage);
317
- },
318
- });
319
-
320
- expect(stages).toContain('init');
321
- expect(stages).toContain('validate');
322
- expect(stages).toContain('done');
323
- });
324
- });
325
- });
326
-
327
- describe('validateAgentOutput strictMarkdown option', () => {
328
- const projectRoot = '/tmp/project';
329
-
330
- it('rejects markdown in strict mode', () => {
331
- const raw = '```json\n{"format":"guardrail-v1","diff":"","commands":[],"tests":[],"notes":""}\n```';
332
- const result = validateAgentOutput(raw, projectRoot, { strictMarkdown: true });
333
-
334
- expect(result.valid).toBe(false);
335
- expect(result.wasMarkdownWrapped).toBe(true);
336
- expect(result.errors[0]).toContain('markdown');
337
- });
338
-
339
- it('accepts and strips markdown in forgiving mode', () => {
340
- const raw = '```json\n{"format":"guardrail-v1","diff":"","commands":[],"tests":[],"notes":""}\n```';
341
- const result = validateAgentOutput(raw, projectRoot);
342
-
343
- expect(result.valid).toBe(true);
344
- expect(result.wasMarkdownWrapped).toBe(true);
345
- expect(result.warnings.some(w => w.includes('markdown'))).toBe(true);
346
- });
347
-
348
- it('accepts raw JSON without warnings', () => {
349
- const raw = '{"format":"guardrail-v1","diff":"","commands":[],"tests":[],"notes":""}';
350
- const result = validateAgentOutput(raw, projectRoot);
351
-
352
- expect(result.valid).toBe(true);
353
- expect(result.wasMarkdownWrapped).toBe(false);
354
- expect(result.warnings.filter(w => w.includes('markdown'))).toHaveLength(0);
355
- });
356
- });
357
-
358
- describe('formatPipelineResult', () => {
359
- it('formats successful result', () => {
360
- const result: PipelineResult = {
361
- success: true,
362
- stage: 'done',
363
- duration: 1234,
364
- filesModified: ['src/file.ts'],
365
- errors: [],
366
- warnings: [],
367
- failureContext: [],
368
- };
369
-
370
- const output = formatPipelineResult(result);
371
- expect(output).toContain('SUCCESSFUL');
372
- expect(output).toContain('src/file.ts');
373
- expect(output).toContain('1234');
374
- });
375
-
376
- it('formats failed result with top 3 failures', () => {
377
- const result: PipelineResult = {
378
- success: false,
379
- stage: 'typecheck',
380
- duration: 500,
381
- filesModified: [],
382
- errors: ['TypeScript error'],
383
- warnings: [],
384
- failureContext: [
385
- 'error TS2322: Type mismatch',
386
- 'error TS2345: Argument type',
387
- 'error TS2339: Property does not exist',
388
- 'error TS2304: Cannot find name', // This 4th one should be cut off
389
- ],
390
- };
391
-
392
- const output = formatPipelineResult(result);
393
- expect(output).toContain('FAILED');
394
- expect(output).toContain('TS2322');
395
- expect(output).toContain('TS2345');
396
- expect(output).toContain('TS2339');
397
- });
398
- });
399
-
400
- describe('formatPipelineResultJson', () => {
401
- it('outputs valid JSON', () => {
402
- const result: PipelineResult = {
403
- success: true,
404
- stage: 'done',
405
- duration: 100,
406
- filesModified: ['a.ts'],
407
- errors: [],
408
- warnings: ['warn1'],
409
- failureContext: [],
410
- };
411
-
412
- const json = formatPipelineResultJson(result);
413
- const parsed = JSON.parse(json);
414
-
415
- expect(parsed.success).toBe(true);
416
- expect(parsed.filesModified).toEqual(['a.ts']);
417
- expect(parsed.warnings).toEqual(['warn1']);
418
- });
419
- });
@@ -1,241 +0,0 @@
1
- /**
2
- * Repo Fingerprint Tests
3
- *
4
- * Tests for project detection and configuration:
5
- * - Package manager detection
6
- * - Build tool detection
7
- * - Framework detection
8
- * - Test runner detection
9
- */
10
-
11
- import * as fs from 'fs';
12
- import * as path from 'path';
13
- import * as os from 'os';
14
- import {
15
- fingerprintRepo,
16
- getInstallCommand,
17
- getBuildCommand,
18
- getTestCommand,
19
- getTypecheckCommand,
20
- } from '../repo-fingerprint';
21
-
22
- describe('fingerprintRepo', () => {
23
- let tempDir: string;
24
-
25
- beforeEach(async () => {
26
- tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'fingerprint-test-'));
27
- });
28
-
29
- afterEach(async () => {
30
- await fs.promises.rm(tempDir, { recursive: true, force: true });
31
- });
32
-
33
- it('detects pnpm from lock file', async () => {
34
- await fs.promises.writeFile(path.join(tempDir, 'pnpm-lock.yaml'), '');
35
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
36
- name: 'test',
37
- dependencies: {},
38
- }));
39
-
40
- const { fingerprint } = fingerprintRepo(tempDir);
41
- expect(fingerprint.packageManager).toBe('pnpm');
42
- });
43
-
44
- it('detects yarn from lock file', async () => {
45
- await fs.promises.writeFile(path.join(tempDir, 'yarn.lock'), '');
46
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
47
- name: 'test',
48
- }));
49
-
50
- const { fingerprint } = fingerprintRepo(tempDir);
51
- expect(fingerprint.packageManager).toBe('yarn');
52
- });
53
-
54
- it('detects npm from lock file', async () => {
55
- await fs.promises.writeFile(path.join(tempDir, 'package-lock.json'), '{}');
56
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
57
- name: 'test',
58
- }));
59
-
60
- const { fingerprint } = fingerprintRepo(tempDir);
61
- expect(fingerprint.packageManager).toBe('npm');
62
- });
63
-
64
- it('detects Next.js framework', async () => {
65
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
66
- name: 'test',
67
- dependencies: { next: '^14.0.0', react: '^18.0.0' },
68
- }));
69
-
70
- const { fingerprint } = fingerprintRepo(tempDir);
71
- expect(fingerprint.framework).toBe('next');
72
- });
73
-
74
- it('detects Vite framework', async () => {
75
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
76
- name: 'test',
77
- devDependencies: { vite: '^5.0.0' },
78
- }));
79
-
80
- const { fingerprint } = fingerprintRepo(tempDir);
81
- expect(fingerprint.framework).toBe('vite');
82
- });
83
-
84
- it('detects Jest test runner', async () => {
85
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
86
- name: 'test',
87
- devDependencies: { jest: '^29.0.0' },
88
- }));
89
-
90
- const { fingerprint } = fingerprintRepo(tempDir);
91
- expect(fingerprint.testRunner).toBe('jest');
92
- });
93
-
94
- it('detects Vitest test runner', async () => {
95
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
96
- name: 'test',
97
- devDependencies: { vitest: '^1.0.0' },
98
- }));
99
-
100
- const { fingerprint } = fingerprintRepo(tempDir);
101
- expect(fingerprint.testRunner).toBe('vitest');
102
- });
103
-
104
- it('detects TypeScript', async () => {
105
- await fs.promises.writeFile(path.join(tempDir, 'tsconfig.json'), '{}');
106
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
107
- name: 'test',
108
- }));
109
-
110
- const { fingerprint } = fingerprintRepo(tempDir);
111
- expect(fingerprint.hasTypeScript).toBe(true);
112
- });
113
-
114
- it('detects Turbo build tool', async () => {
115
- await fs.promises.writeFile(path.join(tempDir, 'turbo.json'), '{}');
116
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
117
- name: 'test',
118
- }));
119
-
120
- const { fingerprint } = fingerprintRepo(tempDir);
121
- expect(fingerprint.buildTool).toBe('turbo');
122
- });
123
-
124
- it('detects monorepo from workspaces', async () => {
125
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
126
- name: 'monorepo',
127
- workspaces: ['packages/*'],
128
- }));
129
-
130
- const { fingerprint } = fingerprintRepo(tempDir);
131
- expect(fingerprint.isMonorepo).toBe(true);
132
- expect(fingerprint.workspaces).toContain('packages/*');
133
- });
134
-
135
- it('detects build script', async () => {
136
- await fs.promises.writeFile(path.join(tempDir, 'package.json'), JSON.stringify({
137
- name: 'test',
138
- scripts: { build: 'tsc' },
139
- }));
140
-
141
- const { fingerprint } = fingerprintRepo(tempDir);
142
- expect(fingerprint.hasBuildScript).toBe(true);
143
- });
144
-
145
- it('handles missing package.json gracefully', () => {
146
- const { fingerprint, confidence, detectionNotes } = fingerprintRepo(tempDir);
147
- expect(fingerprint.packageManager).toBe('unknown');
148
- expect(confidence).toBeLessThan(100);
149
- expect(detectionNotes).toContain('No package.json found');
150
- });
151
- });
152
-
153
- describe('command helpers', () => {
154
- it('returns correct install command for pnpm', () => {
155
- expect(getInstallCommand('pnpm')).toBe('pnpm install --frozen-lockfile');
156
- });
157
-
158
- it('returns correct install command for yarn', () => {
159
- expect(getInstallCommand('yarn')).toBe('yarn install --frozen-lockfile');
160
- });
161
-
162
- it('returns correct install command for npm', () => {
163
- expect(getInstallCommand('npm')).toBe('npm ci');
164
- });
165
-
166
- it('returns turbo build command when turbo detected', () => {
167
- const fingerprint = {
168
- buildTool: 'turbo' as const,
169
- hasBuildScript: true,
170
- hasTypeScript: true,
171
- packageManager: 'pnpm' as const,
172
- framework: 'next' as const,
173
- testRunner: 'jest' as const,
174
- hasESLint: true,
175
- hasPrettier: true,
176
- hasTestScript: true,
177
- isMonorepo: true,
178
- workspaces: ['packages/*'],
179
- dependencies: {},
180
- devDependencies: {},
181
- };
182
- expect(getBuildCommand(fingerprint)).toBe('npx turbo run build');
183
- });
184
-
185
- it('returns vitest command for vitest runner', () => {
186
- const fingerprint = {
187
- testRunner: 'vitest' as const,
188
- hasTestScript: true,
189
- buildTool: 'none' as const,
190
- hasBuildScript: false,
191
- hasTypeScript: false,
192
- packageManager: 'npm' as const,
193
- framework: 'none' as const,
194
- hasESLint: false,
195
- hasPrettier: false,
196
- isMonorepo: false,
197
- workspaces: [],
198
- dependencies: {},
199
- devDependencies: {},
200
- };
201
- expect(getTestCommand(fingerprint)).toBe('npx vitest run');
202
- });
203
-
204
- it('returns typecheck command when TypeScript present', () => {
205
- const fingerprint = {
206
- hasTypeScript: true,
207
- testRunner: 'none' as const,
208
- hasTestScript: false,
209
- buildTool: 'none' as const,
210
- hasBuildScript: false,
211
- packageManager: 'npm' as const,
212
- framework: 'none' as const,
213
- hasESLint: false,
214
- hasPrettier: false,
215
- isMonorepo: false,
216
- workspaces: [],
217
- dependencies: {},
218
- devDependencies: {},
219
- };
220
- expect(getTypecheckCommand(fingerprint)).toBe('npx tsc --noEmit');
221
- });
222
-
223
- it('returns null typecheck command when no TypeScript', () => {
224
- const fingerprint = {
225
- hasTypeScript: false,
226
- testRunner: 'none' as const,
227
- hasTestScript: false,
228
- buildTool: 'none' as const,
229
- hasBuildScript: false,
230
- packageManager: 'npm' as const,
231
- framework: 'none' as const,
232
- hasESLint: false,
233
- hasPrettier: false,
234
- isMonorepo: false,
235
- workspaces: [],
236
- dependencies: {},
237
- devDependencies: {},
238
- };
239
- expect(getTypecheckCommand(fingerprint)).toBeNull();
240
- });
241
- });