@vibe-validate/cli 0.18.4-rc.1 → 0.18.4
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.
- package/dist/bin.js +5 -3
- package/dist/bin.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +110 -89
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/generate-workflow.d.ts.map +1 -1
- package/dist/commands/generate-workflow.js +42 -32
- package/dist/commands/generate-workflow.js.map +1 -1
- package/dist/commands/run.js +3 -3
- package/dist/commands/run.js.map +1 -1
- package/dist/services/ci-providers/github-actions.d.ts.map +1 -1
- package/dist/services/ci-providers/github-actions.js +2 -0
- package/dist/services/ci-providers/github-actions.js.map +1 -1
- package/dist/services/history-summary-builder.d.ts.map +1 -1
- package/dist/services/history-summary-builder.js +9 -19
- package/dist/services/history-summary-builder.js.map +1 -1
- package/dist/utils/check-validation.d.ts.map +1 -1
- package/dist/utils/check-validation.js +7 -4
- package/dist/utils/check-validation.js.map +1 -1
- package/dist/utils/command-name.d.ts.map +1 -1
- package/dist/utils/command-name.js +6 -4
- package/dist/utils/command-name.js.map +1 -1
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-loader.js +5 -3
- package/dist/utils/config-loader.js.map +1 -1
- package/dist/utils/setup-checks/hooks-check.d.ts.map +1 -1
- package/dist/utils/setup-checks/hooks-check.js +14 -11
- package/dist/utils/setup-checks/hooks-check.js.map +1 -1
- package/dist/utils/setup-checks/workflow-check.d.ts.map +1 -1
- package/dist/utils/setup-checks/workflow-check.js +4 -3
- package/dist/utils/setup-checks/workflow-check.js.map +1 -1
- package/dist/utils/tree-hash-output.js +3 -3
- package/dist/utils/tree-hash-output.js.map +1 -1
- package/package.json +7 -7
package/dist/commands/doctor.js
CHANGED
|
@@ -28,6 +28,27 @@ import { formatTemplateList } from '../utils/template-discovery.js';
|
|
|
28
28
|
import { checkSync, ciConfigToWorkflowOptions } from './generate-workflow.js';
|
|
29
29
|
/** @deprecated State file deprecated in v0.12.0 - validation now uses git notes */
|
|
30
30
|
const DEPRECATED_STATE_FILE = '.vibe-validate-state.yaml';
|
|
31
|
+
// Check name constants (extracted to avoid duplication warnings)
|
|
32
|
+
const CHECK_CLI_BUILD_STATUS = 'CLI build status';
|
|
33
|
+
const CHECK_NODE_VERSION = 'Node.js version';
|
|
34
|
+
const CHECK_PACKAGE_MANAGER = 'Package manager';
|
|
35
|
+
const CHECK_GIT_REPOSITORY = 'Git repository';
|
|
36
|
+
const CHECK_GIT_MAIN_BRANCH = 'Git main branch';
|
|
37
|
+
const CHECK_GIT_REMOTE_ORIGIN = 'Git remote origin';
|
|
38
|
+
const CHECK_GIT_REMOTE_MAIN_BRANCH = 'Git remote main branch';
|
|
39
|
+
const CHECK_CONFIG_VALID = 'Configuration valid';
|
|
40
|
+
const CHECK_GITIGNORE_STATE_FILE = 'Gitignore state file';
|
|
41
|
+
const CHECK_VALIDATION_HISTORY = 'Validation history';
|
|
42
|
+
const CHECK_VALIDATION_HISTORY_MIGRATION = 'Validation history migration';
|
|
43
|
+
const CHECK_PRE_COMMIT_HOOK = 'Pre-commit hook';
|
|
44
|
+
const CHECK_GITHUB_ACTIONS_WORKFLOW = 'GitHub Actions workflow';
|
|
45
|
+
const CHECK_PRE_COMMIT_SECRET_SCANNING = 'Pre-commit secret scanning';
|
|
46
|
+
const CHECK_VIBE_VALIDATE_VERSION = 'vibe-validate version';
|
|
47
|
+
// Common message constants
|
|
48
|
+
const MSG_SKIPPED_NOT_IN_GIT = 'Skipped (not in git repository)';
|
|
49
|
+
const MSG_SKIPPED_NO_CONFIG = 'Skipped (no config)';
|
|
50
|
+
const SUGGESTION_INSTALL_NODE_JS = 'Install Node.js: https://nodejs.org/';
|
|
51
|
+
const GIT_REF_DEPRECATED_RUNS = 'refs/notes/vibe-validate/runs';
|
|
31
52
|
/**
|
|
32
53
|
* Check if CLI build is in sync with source code (development mode only)
|
|
33
54
|
*
|
|
@@ -42,16 +63,16 @@ function checkCliBuildSync() {
|
|
|
42
63
|
const gitRoot = findGitRoot();
|
|
43
64
|
if (!gitRoot) {
|
|
44
65
|
return {
|
|
45
|
-
name:
|
|
66
|
+
name: CHECK_CLI_BUILD_STATUS,
|
|
46
67
|
passed: true,
|
|
47
|
-
message:
|
|
68
|
+
message: MSG_SKIPPED_NOT_IN_GIT,
|
|
48
69
|
};
|
|
49
70
|
}
|
|
50
71
|
const sourcePackageJsonPath = join(gitRoot, 'packages/cli/package.json');
|
|
51
72
|
// Not in vibe-validate source tree
|
|
52
73
|
if (!existsSync(sourcePackageJsonPath)) {
|
|
53
74
|
return {
|
|
54
|
-
name:
|
|
75
|
+
name: CHECK_CLI_BUILD_STATUS,
|
|
55
76
|
passed: true,
|
|
56
77
|
message: 'Skipped (not in vibe-validate source tree)',
|
|
57
78
|
};
|
|
@@ -66,14 +87,14 @@ function checkCliBuildSync() {
|
|
|
66
87
|
// Compare versions - this is the confusing scenario that needs detection
|
|
67
88
|
if (runningVersion !== sourceVersion) {
|
|
68
89
|
return {
|
|
69
|
-
name:
|
|
90
|
+
name: CHECK_CLI_BUILD_STATUS,
|
|
70
91
|
passed: false,
|
|
71
92
|
message: `Build is stale: running v${runningVersion}, source v${sourceVersion}`,
|
|
72
93
|
suggestion: 'Rebuild packages: pnpm -r build',
|
|
73
94
|
};
|
|
74
95
|
}
|
|
75
96
|
return {
|
|
76
|
-
name:
|
|
97
|
+
name: CHECK_CLI_BUILD_STATUS,
|
|
77
98
|
passed: true,
|
|
78
99
|
message: `Build is up to date (v${runningVersion})`,
|
|
79
100
|
};
|
|
@@ -82,7 +103,7 @@ function checkCliBuildSync() {
|
|
|
82
103
|
catch (_error) {
|
|
83
104
|
// Any errors are non-critical - this is a development-only check
|
|
84
105
|
return {
|
|
85
|
-
name:
|
|
106
|
+
name: CHECK_CLI_BUILD_STATUS,
|
|
86
107
|
passed: true,
|
|
87
108
|
message: 'Skipped (could not determine build status)',
|
|
88
109
|
};
|
|
@@ -97,27 +118,27 @@ function checkNodeVersion() {
|
|
|
97
118
|
// Validate that we got a valid version number
|
|
98
119
|
if (!version) {
|
|
99
120
|
return {
|
|
100
|
-
name:
|
|
121
|
+
name: CHECK_NODE_VERSION,
|
|
101
122
|
passed: false,
|
|
102
123
|
message: 'Failed to detect Node.js version',
|
|
103
|
-
suggestion:
|
|
124
|
+
suggestion: SUGGESTION_INSTALL_NODE_JS,
|
|
104
125
|
};
|
|
105
126
|
}
|
|
106
127
|
const majorVersion = Number.parseInt(version.replace('v', '').split('.')[0]);
|
|
107
128
|
if (Number.isNaN(majorVersion) || majorVersion === 0) {
|
|
108
129
|
return {
|
|
109
|
-
name:
|
|
130
|
+
name: CHECK_NODE_VERSION,
|
|
110
131
|
passed: false,
|
|
111
132
|
message: `Failed to parse Node.js version from output: "${version}"`,
|
|
112
|
-
suggestion:
|
|
133
|
+
suggestion: SUGGESTION_INSTALL_NODE_JS,
|
|
113
134
|
};
|
|
114
135
|
}
|
|
115
136
|
return majorVersion >= 20 ? {
|
|
116
|
-
name:
|
|
137
|
+
name: CHECK_NODE_VERSION,
|
|
117
138
|
passed: true,
|
|
118
139
|
message: `${version} (meets requirement: >=20.0.0)`,
|
|
119
140
|
} : {
|
|
120
|
-
name:
|
|
141
|
+
name: CHECK_NODE_VERSION,
|
|
121
142
|
passed: false,
|
|
122
143
|
message: `${version} is too old. Node.js 20+ required.`,
|
|
123
144
|
suggestion: 'Upgrade Node.js: https://nodejs.org/ or use nvm',
|
|
@@ -126,7 +147,7 @@ function checkNodeVersion() {
|
|
|
126
147
|
catch (error) {
|
|
127
148
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
128
149
|
return {
|
|
129
|
-
name:
|
|
150
|
+
name: CHECK_NODE_VERSION,
|
|
130
151
|
passed: false,
|
|
131
152
|
message: `Failed to detect Node.js version: ${errorMessage}`,
|
|
132
153
|
suggestion: 'Install Node.js: https://nodejs.org/',
|
|
@@ -164,14 +185,14 @@ function checkGitRepository() {
|
|
|
164
185
|
const isGitRepo = isGitRepository();
|
|
165
186
|
if (isGitRepo) {
|
|
166
187
|
return {
|
|
167
|
-
name:
|
|
188
|
+
name: CHECK_GIT_REPOSITORY,
|
|
168
189
|
passed: true,
|
|
169
190
|
message: 'Current directory is a git repository',
|
|
170
191
|
};
|
|
171
192
|
}
|
|
172
193
|
else {
|
|
173
194
|
return {
|
|
174
|
-
name:
|
|
195
|
+
name: CHECK_GIT_REPOSITORY,
|
|
175
196
|
passed: false,
|
|
176
197
|
message: 'Current directory is not a git repository',
|
|
177
198
|
suggestion: 'Run: git init',
|
|
@@ -181,7 +202,7 @@ function checkGitRepository() {
|
|
|
181
202
|
catch (error) {
|
|
182
203
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
183
204
|
return {
|
|
184
|
-
name:
|
|
205
|
+
name: CHECK_GIT_REPOSITORY,
|
|
185
206
|
passed: false,
|
|
186
207
|
message: `Error checking git repository: ${errorMessage}`,
|
|
187
208
|
suggestion: 'Run: git init',
|
|
@@ -231,7 +252,7 @@ async function checkConfigValid(config, configWithErrors) {
|
|
|
231
252
|
errors: configWithErrors.errors
|
|
232
253
|
});
|
|
233
254
|
return {
|
|
234
|
-
name:
|
|
255
|
+
name: CHECK_CONFIG_VALID,
|
|
235
256
|
passed: false,
|
|
236
257
|
message,
|
|
237
258
|
suggestion,
|
|
@@ -243,7 +264,7 @@ async function checkConfigValid(config, configWithErrors) {
|
|
|
243
264
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Need to filter empty strings, not just null/undefined
|
|
244
265
|
const fileName = configPath.split('/').pop() || 'vibe-validate.config.yaml';
|
|
245
266
|
return {
|
|
246
|
-
name:
|
|
267
|
+
name: CHECK_CONFIG_VALID,
|
|
247
268
|
passed: false,
|
|
248
269
|
message: `Found ${fileName} but it contains validation errors`,
|
|
249
270
|
suggestion: [
|
|
@@ -258,7 +279,7 @@ async function checkConfigValid(config, configWithErrors) {
|
|
|
258
279
|
// No config file found
|
|
259
280
|
const templateList = formatTemplateList();
|
|
260
281
|
return {
|
|
261
|
-
name:
|
|
282
|
+
name: CHECK_CONFIG_VALID,
|
|
262
283
|
passed: false,
|
|
263
284
|
message: 'No configuration file found',
|
|
264
285
|
suggestion: [
|
|
@@ -279,14 +300,14 @@ async function checkConfigValid(config, configWithErrors) {
|
|
|
279
300
|
}
|
|
280
301
|
}
|
|
281
302
|
return {
|
|
282
|
-
name:
|
|
303
|
+
name: CHECK_CONFIG_VALID,
|
|
283
304
|
passed: true,
|
|
284
305
|
message: `Loaded successfully (${config.validation.phases.length} phases)`,
|
|
285
306
|
};
|
|
286
307
|
}
|
|
287
308
|
catch (error) {
|
|
288
309
|
return {
|
|
289
|
-
name:
|
|
310
|
+
name: CHECK_CONFIG_VALID,
|
|
290
311
|
passed: false,
|
|
291
312
|
message: `Invalid configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
292
313
|
suggestion: 'Check syntax in vibe-validate.config.yaml',
|
|
@@ -301,9 +322,9 @@ async function checkPackageManager(config) {
|
|
|
301
322
|
if (!config) {
|
|
302
323
|
// Config check will catch this
|
|
303
324
|
return {
|
|
304
|
-
name:
|
|
325
|
+
name: CHECK_PACKAGE_MANAGER,
|
|
305
326
|
passed: true,
|
|
306
|
-
message:
|
|
327
|
+
message: MSG_SKIPPED_NO_CONFIG,
|
|
307
328
|
};
|
|
308
329
|
}
|
|
309
330
|
// Detect package manager from config commands
|
|
@@ -312,7 +333,7 @@ async function checkPackageManager(config) {
|
|
|
312
333
|
const version = getToolVersion(pm);
|
|
313
334
|
if (version) {
|
|
314
335
|
return {
|
|
315
|
-
name:
|
|
336
|
+
name: CHECK_PACKAGE_MANAGER,
|
|
316
337
|
passed: true,
|
|
317
338
|
message: `${pm} ${version} is available`,
|
|
318
339
|
};
|
|
@@ -321,7 +342,7 @@ async function checkPackageManager(config) {
|
|
|
321
342
|
const currentPm = detectPackageManager(process.cwd());
|
|
322
343
|
const installCmd = getGlobalInstallCommand(currentPm, pm);
|
|
323
344
|
return {
|
|
324
|
-
name:
|
|
345
|
+
name: CHECK_PACKAGE_MANAGER,
|
|
325
346
|
passed: false,
|
|
326
347
|
message: `${pm} not found (required by config commands)`,
|
|
327
348
|
suggestion: pm === 'npm'
|
|
@@ -332,7 +353,7 @@ async function checkPackageManager(config) {
|
|
|
332
353
|
catch (error) {
|
|
333
354
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
334
355
|
return {
|
|
335
|
-
name:
|
|
356
|
+
name: CHECK_PACKAGE_MANAGER,
|
|
336
357
|
passed: true,
|
|
337
358
|
message: `Skipped (config check failed): ${errorMessage}`,
|
|
338
359
|
};
|
|
@@ -349,15 +370,15 @@ async function checkWorkflowSync(config) {
|
|
|
349
370
|
const gitRoot = findGitRoot();
|
|
350
371
|
if (!gitRoot) {
|
|
351
372
|
return {
|
|
352
|
-
name:
|
|
373
|
+
name: CHECK_GITHUB_ACTIONS_WORKFLOW,
|
|
353
374
|
passed: true,
|
|
354
|
-
message:
|
|
375
|
+
message: MSG_SKIPPED_NOT_IN_GIT,
|
|
355
376
|
};
|
|
356
377
|
}
|
|
357
378
|
const workflowPath = join(gitRoot, '.github/workflows/validate.yml');
|
|
358
379
|
if (!existsSync(workflowPath)) {
|
|
359
380
|
return {
|
|
360
|
-
name:
|
|
381
|
+
name: CHECK_GITHUB_ACTIONS_WORKFLOW,
|
|
361
382
|
passed: true,
|
|
362
383
|
message: 'No workflow file (optional)',
|
|
363
384
|
};
|
|
@@ -365,15 +386,15 @@ async function checkWorkflowSync(config) {
|
|
|
365
386
|
try {
|
|
366
387
|
if (!config) {
|
|
367
388
|
return {
|
|
368
|
-
name:
|
|
389
|
+
name: CHECK_GITHUB_ACTIONS_WORKFLOW,
|
|
369
390
|
passed: true,
|
|
370
|
-
message:
|
|
391
|
+
message: MSG_SKIPPED_NO_CONFIG,
|
|
371
392
|
};
|
|
372
393
|
}
|
|
373
394
|
// Check if workflow check is disabled in config
|
|
374
395
|
if (config.ci?.disableWorkflowCheck === true) {
|
|
375
396
|
return {
|
|
376
|
-
name:
|
|
397
|
+
name: CHECK_GITHUB_ACTIONS_WORKFLOW,
|
|
377
398
|
passed: true,
|
|
378
399
|
message: 'Workflow sync check disabled (ci.disableWorkflowCheck: true)',
|
|
379
400
|
};
|
|
@@ -385,11 +406,11 @@ async function checkWorkflowSync(config) {
|
|
|
385
406
|
};
|
|
386
407
|
const { inSync, diff } = checkSync(config, generateOptions, workflowPath);
|
|
387
408
|
return inSync ? {
|
|
388
|
-
name:
|
|
409
|
+
name: CHECK_GITHUB_ACTIONS_WORKFLOW,
|
|
389
410
|
passed: true,
|
|
390
411
|
message: 'Workflow is in sync with config',
|
|
391
412
|
} : {
|
|
392
|
-
name:
|
|
413
|
+
name: CHECK_GITHUB_ACTIONS_WORKFLOW,
|
|
393
414
|
passed: false,
|
|
394
415
|
message: `Workflow is out of sync: ${diff ?? 'differs from config'}`,
|
|
395
416
|
suggestion: [
|
|
@@ -404,7 +425,7 @@ async function checkWorkflowSync(config) {
|
|
|
404
425
|
}
|
|
405
426
|
catch (error) {
|
|
406
427
|
return {
|
|
407
|
-
name:
|
|
428
|
+
name: CHECK_GITHUB_ACTIONS_WORKFLOW,
|
|
408
429
|
passed: false,
|
|
409
430
|
message: `Failed to check workflow sync: ${error instanceof Error ? error.message : String(error)}`,
|
|
410
431
|
suggestion: 'Verify workflow file syntax',
|
|
@@ -423,7 +444,7 @@ async function checkPreCommitHook(config) {
|
|
|
423
444
|
// If user explicitly disabled, acknowledge their choice
|
|
424
445
|
if (!preCommitEnabled) {
|
|
425
446
|
return {
|
|
426
|
-
name:
|
|
447
|
+
name: CHECK_PRE_COMMIT_HOOK,
|
|
427
448
|
passed: true,
|
|
428
449
|
message: 'Pre-commit hook disabled in config (user preference)',
|
|
429
450
|
};
|
|
@@ -432,9 +453,9 @@ async function checkPreCommitHook(config) {
|
|
|
432
453
|
const gitRoot = findGitRoot();
|
|
433
454
|
if (!gitRoot) {
|
|
434
455
|
return {
|
|
435
|
-
name:
|
|
456
|
+
name: CHECK_PRE_COMMIT_HOOK,
|
|
436
457
|
passed: true,
|
|
437
|
-
message:
|
|
458
|
+
message: MSG_SKIPPED_NOT_IN_GIT,
|
|
438
459
|
};
|
|
439
460
|
}
|
|
440
461
|
const huskyPath = join(gitRoot, '.husky/pre-commit');
|
|
@@ -442,7 +463,7 @@ async function checkPreCommitHook(config) {
|
|
|
442
463
|
if (!existsSync(huskyPath)) {
|
|
443
464
|
const cmd = getCommandName();
|
|
444
465
|
return {
|
|
445
|
-
name:
|
|
466
|
+
name: CHECK_PRE_COMMIT_HOOK,
|
|
446
467
|
passed: false,
|
|
447
468
|
message: 'Pre-commit hook not installed',
|
|
448
469
|
suggestion: `Manual: npx husky init && echo "${cmd} pre-commit" > .husky/pre-commit\n 💡 Or run: ${cmd} init --setup-hooks\n 💡 Or disable: set hooks.preCommit.enabled=false in config`,
|
|
@@ -457,7 +478,7 @@ async function checkPreCommitHook(config) {
|
|
|
457
478
|
hookContent.includes('npx vibe-validate validate');
|
|
458
479
|
if (hasVibeValidate) {
|
|
459
480
|
return {
|
|
460
|
-
name:
|
|
481
|
+
name: CHECK_PRE_COMMIT_HOOK,
|
|
461
482
|
passed: true,
|
|
462
483
|
message: 'Pre-commit hook installed and runs vibe-validate',
|
|
463
484
|
};
|
|
@@ -467,7 +488,7 @@ async function checkPreCommitHook(config) {
|
|
|
467
488
|
const cmd = getCommandName();
|
|
468
489
|
const hookPreview = hookContent.split('\n').slice(0, 3).join('; ').trim();
|
|
469
490
|
return {
|
|
470
|
-
name:
|
|
491
|
+
name: CHECK_PRE_COMMIT_HOOK,
|
|
471
492
|
passed: false,
|
|
472
493
|
message: `Custom pre-commit hook detected: "${hookPreview}..."`,
|
|
473
494
|
suggestion: `Manual: Verify that .husky/pre-commit runs "${expectedCommand}"\n 💡 Or run: ${cmd} init --setup-hooks\n 💡 Or disable: set hooks.preCommit.enabled=false in config`,
|
|
@@ -477,7 +498,7 @@ async function checkPreCommitHook(config) {
|
|
|
477
498
|
catch (error) {
|
|
478
499
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
479
500
|
return {
|
|
480
|
-
name:
|
|
501
|
+
name: CHECK_PRE_COMMIT_HOOK,
|
|
481
502
|
passed: false,
|
|
482
503
|
message: `Pre-commit hook exists but unreadable: ${errorMessage}`,
|
|
483
504
|
suggestion: 'Fix file permissions or set hooks.preCommit.enabled=false',
|
|
@@ -536,7 +557,7 @@ async function checkVersion(versionChecker = defaultVersionChecker) {
|
|
|
536
557
|
const contextLabel = getContextLabel(context);
|
|
537
558
|
if (currentVersion === latestVersion) {
|
|
538
559
|
return {
|
|
539
|
-
name:
|
|
560
|
+
name: CHECK_VIBE_VALIDATE_VERSION,
|
|
540
561
|
passed: true,
|
|
541
562
|
message: `Current: ${currentVersion}${contextLabel} — up to date with npm`,
|
|
542
563
|
};
|
|
@@ -545,7 +566,7 @@ async function checkVersion(versionChecker = defaultVersionChecker) {
|
|
|
545
566
|
// Only show suggestion if current version is behind npm
|
|
546
567
|
const cmd = getCommandName();
|
|
547
568
|
return {
|
|
548
|
-
name:
|
|
569
|
+
name: CHECK_VIBE_VALIDATE_VERSION,
|
|
549
570
|
passed: true, // Warning only, not a failure
|
|
550
571
|
message: `Current: ${currentVersion}${contextLabel}, Latest: ${latestVersion} available`,
|
|
551
572
|
suggestion: `Upgrade: ${getUpgradeCommand(context)}\n 💡 After upgrade: Run '${cmd} doctor' to verify setup`,
|
|
@@ -554,7 +575,7 @@ async function checkVersion(versionChecker = defaultVersionChecker) {
|
|
|
554
575
|
else {
|
|
555
576
|
// Current version is ahead of npm (pre-release or unpublished)
|
|
556
577
|
return {
|
|
557
|
-
name:
|
|
578
|
+
name: CHECK_VIBE_VALIDATE_VERSION,
|
|
558
579
|
passed: true,
|
|
559
580
|
message: `Current: ${currentVersion}${contextLabel} (ahead of npm: ${latestVersion})`,
|
|
560
581
|
};
|
|
@@ -564,7 +585,7 @@ async function checkVersion(versionChecker = defaultVersionChecker) {
|
|
|
564
585
|
// npm registry unavailable - not a critical error
|
|
565
586
|
const errorMessage = npmError instanceof Error ? npmError.message : String(npmError);
|
|
566
587
|
return {
|
|
567
|
-
name:
|
|
588
|
+
name: CHECK_VIBE_VALIDATE_VERSION,
|
|
568
589
|
passed: true,
|
|
569
590
|
message: `Current version: ${currentVersion} (unable to check for updates: ${errorMessage})`,
|
|
570
591
|
};
|
|
@@ -573,7 +594,7 @@ async function checkVersion(versionChecker = defaultVersionChecker) {
|
|
|
573
594
|
catch (error) {
|
|
574
595
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
575
596
|
return {
|
|
576
|
-
name:
|
|
597
|
+
name: CHECK_VIBE_VALIDATE_VERSION,
|
|
577
598
|
passed: true,
|
|
578
599
|
message: `Unable to determine version: ${errorMessage}`,
|
|
579
600
|
};
|
|
@@ -592,16 +613,16 @@ function checkGitignoreStateFile() {
|
|
|
592
613
|
const gitRoot = findGitRoot();
|
|
593
614
|
if (!gitRoot) {
|
|
594
615
|
return {
|
|
595
|
-
name:
|
|
616
|
+
name: CHECK_GITIGNORE_STATE_FILE,
|
|
596
617
|
passed: true,
|
|
597
|
-
message:
|
|
618
|
+
message: MSG_SKIPPED_NOT_IN_GIT,
|
|
598
619
|
};
|
|
599
620
|
}
|
|
600
621
|
const gitignorePath = join(gitRoot, '.gitignore');
|
|
601
622
|
// Check if .gitignore exists
|
|
602
623
|
if (!existsSync(gitignorePath)) {
|
|
603
624
|
return {
|
|
604
|
-
name:
|
|
625
|
+
name: CHECK_GITIGNORE_STATE_FILE,
|
|
605
626
|
passed: true,
|
|
606
627
|
message: '.gitignore file not found (state file deprecated - using git notes)',
|
|
607
628
|
};
|
|
@@ -621,7 +642,7 @@ function checkGitignoreStateFile() {
|
|
|
621
642
|
}
|
|
622
643
|
else {
|
|
623
644
|
return {
|
|
624
|
-
name:
|
|
645
|
+
name: CHECK_GITIGNORE_STATE_FILE,
|
|
625
646
|
passed: true,
|
|
626
647
|
message: 'No deprecated state file entries in .gitignore',
|
|
627
648
|
};
|
|
@@ -630,7 +651,7 @@ function checkGitignoreStateFile() {
|
|
|
630
651
|
catch (error) {
|
|
631
652
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
632
653
|
return {
|
|
633
|
-
name:
|
|
654
|
+
name: CHECK_GITIGNORE_STATE_FILE,
|
|
634
655
|
passed: false,
|
|
635
656
|
message: `.gitignore exists but is unreadable: ${errorMessage}`,
|
|
636
657
|
suggestion: 'Fix file permissions: chmod 644 .gitignore',
|
|
@@ -676,13 +697,13 @@ function checkCacheMigration() {
|
|
|
676
697
|
try {
|
|
677
698
|
// Check if the EXACT old validation history ref exists (singular "runs", not "run/")
|
|
678
699
|
// Using git for-each-ref with exact ref name to avoid matching "run/" subdirectories
|
|
679
|
-
const result = executeGitCommand(['for-each-ref', '--format=%(refname)',
|
|
680
|
-
if (result.success && result.stdout.trim() ===
|
|
700
|
+
const result = executeGitCommand(['for-each-ref', '--format=%(refname)', GIT_REF_DEPRECATED_RUNS], { ignoreErrors: true, suppressStderr: true });
|
|
701
|
+
if (result.success && result.stdout.trim() === GIT_REF_DEPRECATED_RUNS) {
|
|
681
702
|
// Old validation history namespace exists - automatically clean it up
|
|
682
703
|
try {
|
|
683
|
-
executeGitCommand(['update-ref', '-d',
|
|
704
|
+
executeGitCommand(['update-ref', '-d', GIT_REF_DEPRECATED_RUNS]);
|
|
684
705
|
return {
|
|
685
|
-
name:
|
|
706
|
+
name: CHECK_VALIDATION_HISTORY_MIGRATION,
|
|
686
707
|
passed: true,
|
|
687
708
|
message: 'Automatically removed old validation history format (pre-v0.15.0)',
|
|
688
709
|
};
|
|
@@ -691,7 +712,7 @@ function checkCacheMigration() {
|
|
|
691
712
|
catch (_error) {
|
|
692
713
|
// Fallback to manual suggestion if auto-cleanup fails
|
|
693
714
|
return {
|
|
694
|
-
name:
|
|
715
|
+
name: CHECK_VALIDATION_HISTORY_MIGRATION,
|
|
695
716
|
passed: true, // Not a failure, just informational
|
|
696
717
|
message: 'Old validation history format detected (pre-v0.15.0)',
|
|
697
718
|
suggestion: `Manual cleanup:\n git update-ref -d refs/notes/vibe-validate/runs\n ℹ️ This removes the deprecated "runs" namespace`,
|
|
@@ -699,7 +720,7 @@ function checkCacheMigration() {
|
|
|
699
720
|
}
|
|
700
721
|
}
|
|
701
722
|
return {
|
|
702
|
-
name:
|
|
723
|
+
name: CHECK_VALIDATION_HISTORY_MIGRATION,
|
|
703
724
|
passed: true,
|
|
704
725
|
message: 'Using current validation history format',
|
|
705
726
|
};
|
|
@@ -708,7 +729,7 @@ function checkCacheMigration() {
|
|
|
708
729
|
catch (_error) {
|
|
709
730
|
// Git command failed or no git notes - that's fine
|
|
710
731
|
return {
|
|
711
|
-
name:
|
|
732
|
+
name: CHECK_VALIDATION_HISTORY_MIGRATION,
|
|
712
733
|
passed: true,
|
|
713
734
|
message: 'No validation history to migrate',
|
|
714
735
|
};
|
|
@@ -722,7 +743,7 @@ async function checkHistoryHealth() {
|
|
|
722
743
|
const health = await checkValidationHistoryHealth();
|
|
723
744
|
if (!health.shouldWarn) {
|
|
724
745
|
return {
|
|
725
|
-
name:
|
|
746
|
+
name: CHECK_VALIDATION_HISTORY,
|
|
726
747
|
passed: true,
|
|
727
748
|
message: `${health.totalNotes} tree hashes tracked, history is healthy`,
|
|
728
749
|
};
|
|
@@ -730,7 +751,7 @@ async function checkHistoryHealth() {
|
|
|
730
751
|
// Has warnings - advisory only, not a critical failure
|
|
731
752
|
const cmd = getCommandName();
|
|
732
753
|
return {
|
|
733
|
-
name:
|
|
754
|
+
name: CHECK_VALIDATION_HISTORY,
|
|
734
755
|
passed: true, // Advisory: large history is a warning, not a failure
|
|
735
756
|
message: `${health.totalNotes} tree hashes tracked (${health.oldNotesCount} older than 90 days)`,
|
|
736
757
|
suggestion: `Prune old history: ${cmd} history prune --older-than "90 days"`,
|
|
@@ -740,7 +761,7 @@ async function checkHistoryHealth() {
|
|
|
740
761
|
// Git notes not available or other error - not critical
|
|
741
762
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
742
763
|
return {
|
|
743
|
-
name:
|
|
764
|
+
name: CHECK_VALIDATION_HISTORY,
|
|
744
765
|
passed: true,
|
|
745
766
|
message: `History unavailable (not in git repo or no validation runs yet): ${errorMessage}`,
|
|
746
767
|
};
|
|
@@ -753,9 +774,9 @@ async function checkMainBranch(config) {
|
|
|
753
774
|
try {
|
|
754
775
|
if (!config) {
|
|
755
776
|
return {
|
|
756
|
-
name:
|
|
777
|
+
name: CHECK_GIT_MAIN_BRANCH,
|
|
757
778
|
passed: true,
|
|
758
|
-
message:
|
|
779
|
+
message: MSG_SKIPPED_NO_CONFIG,
|
|
759
780
|
};
|
|
760
781
|
}
|
|
761
782
|
const mainBranch = getMainBranch(config.git);
|
|
@@ -764,7 +785,7 @@ async function checkMainBranch(config) {
|
|
|
764
785
|
const localResult = verifyRef(mainBranch);
|
|
765
786
|
if (localResult) {
|
|
766
787
|
return {
|
|
767
|
-
name:
|
|
788
|
+
name: CHECK_GIT_MAIN_BRANCH,
|
|
768
789
|
passed: true,
|
|
769
790
|
message: `Branch '${mainBranch}' exists locally`,
|
|
770
791
|
};
|
|
@@ -773,13 +794,13 @@ async function checkMainBranch(config) {
|
|
|
773
794
|
const remoteResult = verifyRef(`${remoteOrigin}/${mainBranch}`);
|
|
774
795
|
if (remoteResult) {
|
|
775
796
|
return {
|
|
776
|
-
name:
|
|
797
|
+
name: CHECK_GIT_MAIN_BRANCH,
|
|
777
798
|
passed: true,
|
|
778
799
|
message: `Branch '${mainBranch}' exists on remote '${remoteOrigin}' (fetch-depth: 0 required in CI)`,
|
|
779
800
|
};
|
|
780
801
|
}
|
|
781
802
|
return {
|
|
782
|
-
name:
|
|
803
|
+
name: CHECK_GIT_MAIN_BRANCH,
|
|
783
804
|
passed: false,
|
|
784
805
|
message: `Configured main branch '${mainBranch}' does not exist locally or on remote '${remoteOrigin}'`,
|
|
785
806
|
suggestion: `Create branch: git checkout -b ${mainBranch} OR update config to use existing branch (e.g., 'master', 'develop')`,
|
|
@@ -788,7 +809,7 @@ async function checkMainBranch(config) {
|
|
|
788
809
|
catch (error) {
|
|
789
810
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
790
811
|
return {
|
|
791
|
-
name:
|
|
812
|
+
name: CHECK_GIT_MAIN_BRANCH,
|
|
792
813
|
passed: true,
|
|
793
814
|
message: `Skipped (config or git error): ${errorMessage}`,
|
|
794
815
|
};
|
|
@@ -801,9 +822,9 @@ async function checkRemoteOrigin(config) {
|
|
|
801
822
|
try {
|
|
802
823
|
if (!config) {
|
|
803
824
|
return {
|
|
804
|
-
name:
|
|
825
|
+
name: CHECK_GIT_REMOTE_ORIGIN,
|
|
805
826
|
passed: true,
|
|
806
|
-
message:
|
|
827
|
+
message: MSG_SKIPPED_NO_CONFIG,
|
|
807
828
|
};
|
|
808
829
|
}
|
|
809
830
|
const remoteOrigin = getRemoteOrigin(config.git);
|
|
@@ -814,7 +835,7 @@ async function checkRemoteOrigin(config) {
|
|
|
814
835
|
: [];
|
|
815
836
|
if (remotes.includes(remoteOrigin)) {
|
|
816
837
|
return {
|
|
817
|
-
name:
|
|
838
|
+
name: CHECK_GIT_REMOTE_ORIGIN,
|
|
818
839
|
passed: true,
|
|
819
840
|
message: `Remote '${remoteOrigin}' exists`,
|
|
820
841
|
};
|
|
@@ -822,7 +843,7 @@ async function checkRemoteOrigin(config) {
|
|
|
822
843
|
else {
|
|
823
844
|
const availableRemotes = remotes.join(', ') || '(none)';
|
|
824
845
|
return {
|
|
825
|
-
name:
|
|
846
|
+
name: CHECK_GIT_REMOTE_ORIGIN,
|
|
826
847
|
passed: false,
|
|
827
848
|
message: `Configured remote '${remoteOrigin}' does not exist. Available: ${availableRemotes}`,
|
|
828
849
|
suggestion: `Add remote: git remote add ${remoteOrigin} <url> OR update config to use existing remote (e.g., 'origin')`,
|
|
@@ -832,7 +853,7 @@ async function checkRemoteOrigin(config) {
|
|
|
832
853
|
catch (error) {
|
|
833
854
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
834
855
|
return {
|
|
835
|
-
name:
|
|
856
|
+
name: CHECK_GIT_REMOTE_ORIGIN,
|
|
836
857
|
passed: false,
|
|
837
858
|
message: `Failed to list git remotes: ${errorMessage}`,
|
|
838
859
|
suggestion: 'Verify git repository is initialized',
|
|
@@ -842,7 +863,7 @@ async function checkRemoteOrigin(config) {
|
|
|
842
863
|
catch (error) {
|
|
843
864
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
844
865
|
return {
|
|
845
|
-
name:
|
|
866
|
+
name: CHECK_GIT_REMOTE_ORIGIN,
|
|
846
867
|
passed: true,
|
|
847
868
|
message: `Skipped (config or git error): ${errorMessage}`,
|
|
848
869
|
};
|
|
@@ -855,9 +876,9 @@ async function checkRemoteMainBranch(config) {
|
|
|
855
876
|
try {
|
|
856
877
|
if (!config) {
|
|
857
878
|
return {
|
|
858
|
-
name:
|
|
879
|
+
name: CHECK_GIT_REMOTE_MAIN_BRANCH,
|
|
859
880
|
passed: true,
|
|
860
|
-
message:
|
|
881
|
+
message: MSG_SKIPPED_NO_CONFIG,
|
|
861
882
|
};
|
|
862
883
|
}
|
|
863
884
|
const mainBranch = getMainBranch(config.git);
|
|
@@ -870,7 +891,7 @@ async function checkRemoteMainBranch(config) {
|
|
|
870
891
|
: [];
|
|
871
892
|
if (!remotes.includes(remoteOrigin)) {
|
|
872
893
|
return {
|
|
873
|
-
name:
|
|
894
|
+
name: CHECK_GIT_REMOTE_MAIN_BRANCH,
|
|
874
895
|
passed: true,
|
|
875
896
|
message: `Skipped (remote '${remoteOrigin}' not configured)`,
|
|
876
897
|
};
|
|
@@ -881,7 +902,7 @@ async function checkRemoteMainBranch(config) {
|
|
|
881
902
|
throw new Error(`Failed to check remote branch: ${lsRemoteResult.stderr}`);
|
|
882
903
|
}
|
|
883
904
|
return {
|
|
884
|
-
name:
|
|
905
|
+
name: CHECK_GIT_REMOTE_MAIN_BRANCH,
|
|
885
906
|
passed: true,
|
|
886
907
|
message: `Branch '${mainBranch}' exists on remote '${remoteOrigin}'`,
|
|
887
908
|
};
|
|
@@ -889,7 +910,7 @@ async function checkRemoteMainBranch(config) {
|
|
|
889
910
|
catch (error) {
|
|
890
911
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
891
912
|
return {
|
|
892
|
-
name:
|
|
913
|
+
name: CHECK_GIT_REMOTE_MAIN_BRANCH,
|
|
893
914
|
passed: false,
|
|
894
915
|
message: `Branch '${mainBranch}' does not exist on remote '${remoteOrigin}': ${errorMessage}`,
|
|
895
916
|
suggestion: `Push branch: git push ${remoteOrigin} ${mainBranch} OR update config to match remote branch name`,
|
|
@@ -899,7 +920,7 @@ async function checkRemoteMainBranch(config) {
|
|
|
899
920
|
catch (error) {
|
|
900
921
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
901
922
|
return {
|
|
902
|
-
name:
|
|
923
|
+
name: CHECK_GIT_REMOTE_MAIN_BRANCH,
|
|
903
924
|
passed: true,
|
|
904
925
|
message: `Skipped (config or git error): ${errorMessage}`,
|
|
905
926
|
};
|
|
@@ -912,15 +933,15 @@ async function checkSecretScanning(config) {
|
|
|
912
933
|
try {
|
|
913
934
|
if (!config) {
|
|
914
935
|
return {
|
|
915
|
-
name:
|
|
936
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
916
937
|
passed: true,
|
|
917
|
-
message:
|
|
938
|
+
message: MSG_SKIPPED_NO_CONFIG,
|
|
918
939
|
};
|
|
919
940
|
}
|
|
920
941
|
const secretScanning = config.hooks?.preCommit?.secretScanning;
|
|
921
942
|
if (!secretScanning) {
|
|
922
943
|
return {
|
|
923
|
-
name:
|
|
944
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
924
945
|
passed: true,
|
|
925
946
|
message: 'Secret scanning not configured',
|
|
926
947
|
suggestion: 'Recommended: Enable secret scanning to prevent credential leaks\n • Add to config: hooks.preCommit.secretScanning.enabled=true\n • Install gitleaks: brew install gitleaks\n • Or add .secretlintrc.json for npm-based scanning',
|
|
@@ -928,7 +949,7 @@ async function checkSecretScanning(config) {
|
|
|
928
949
|
}
|
|
929
950
|
if (secretScanning.enabled === false) {
|
|
930
951
|
return {
|
|
931
|
-
name:
|
|
952
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
932
953
|
passed: true,
|
|
933
954
|
message: 'Secret scanning disabled in config (user preference)',
|
|
934
955
|
};
|
|
@@ -940,7 +961,7 @@ async function checkSecretScanning(config) {
|
|
|
940
961
|
const pm = detectPackageManager(process.cwd());
|
|
941
962
|
const secretlintInstall = getDevInstallCommand(pm, '@secretlint/secretlint-rule-preset-recommend secretlint');
|
|
942
963
|
return {
|
|
943
|
-
name:
|
|
964
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
944
965
|
passed: true,
|
|
945
966
|
message: 'Secret scanning enabled but no tools available',
|
|
946
967
|
suggestion: `Install a secret scanning tool:\n • gitleaks: brew install gitleaks\n • secretlint: ${secretlintInstall}\n • Or add config files: .gitleaks.toml or .secretlintrc.json`,
|
|
@@ -974,7 +995,7 @@ async function checkSecretScanning(config) {
|
|
|
974
995
|
const gitleaks = availableTools.find(t => t.tool === 'gitleaks');
|
|
975
996
|
if (gitleaks?.hasConfig && !gitleaks.available) {
|
|
976
997
|
return {
|
|
977
|
-
name:
|
|
998
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
978
999
|
passed: true,
|
|
979
1000
|
message,
|
|
980
1001
|
suggestion: 'Install gitleaks for better performance: brew install gitleaks',
|
|
@@ -984,14 +1005,14 @@ async function checkSecretScanning(config) {
|
|
|
984
1005
|
const hasGitleaksAvailable = availableTools.some(t => t.tool === 'gitleaks' && t.available);
|
|
985
1006
|
if (!hasGitleaksAvailable && toolsToRun.some(t => t.tool === 'secretlint')) {
|
|
986
1007
|
return {
|
|
987
|
-
name:
|
|
1008
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
988
1009
|
passed: true,
|
|
989
1010
|
message,
|
|
990
1011
|
suggestion: 'Consider installing gitleaks for faster scanning: brew install gitleaks',
|
|
991
1012
|
};
|
|
992
1013
|
}
|
|
993
1014
|
return {
|
|
994
|
-
name:
|
|
1015
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
995
1016
|
passed: true,
|
|
996
1017
|
message,
|
|
997
1018
|
};
|
|
@@ -999,7 +1020,7 @@ async function checkSecretScanning(config) {
|
|
|
999
1020
|
catch (error) {
|
|
1000
1021
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1001
1022
|
return {
|
|
1002
|
-
name:
|
|
1023
|
+
name: CHECK_PRE_COMMIT_SECRET_SCANNING,
|
|
1003
1024
|
passed: true,
|
|
1004
1025
|
message: `Skipped (config or execution error): ${errorMessage}`,
|
|
1005
1026
|
};
|