@vibe-validate/cli 0.10.3 → 0.12.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 (108) hide show
  1. package/README.md +84 -92
  2. package/dist/bin.js +137 -20
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/cleanup.d.ts +4 -0
  5. package/dist/commands/cleanup.d.ts.map +1 -1
  6. package/dist/commands/cleanup.js +96 -15
  7. package/dist/commands/cleanup.js.map +1 -1
  8. package/dist/commands/config.d.ts +4 -0
  9. package/dist/commands/config.d.ts.map +1 -1
  10. package/dist/commands/config.js +83 -15
  11. package/dist/commands/config.js.map +1 -1
  12. package/dist/commands/doctor.d.ts +4 -0
  13. package/dist/commands/doctor.d.ts.map +1 -1
  14. package/dist/commands/doctor.js +385 -82
  15. package/dist/commands/doctor.js.map +1 -1
  16. package/dist/commands/generate-workflow.d.ts +6 -2
  17. package/dist/commands/generate-workflow.d.ts.map +1 -1
  18. package/dist/commands/generate-workflow.js +188 -33
  19. package/dist/commands/generate-workflow.js.map +1 -1
  20. package/dist/commands/history.d.ts +13 -0
  21. package/dist/commands/history.d.ts.map +1 -0
  22. package/dist/commands/history.js +415 -0
  23. package/dist/commands/history.js.map +1 -0
  24. package/dist/commands/init.d.ts +4 -0
  25. package/dist/commands/init.d.ts.map +1 -1
  26. package/dist/commands/init.js +252 -109
  27. package/dist/commands/init.js.map +1 -1
  28. package/dist/commands/pre-commit.d.ts +4 -0
  29. package/dist/commands/pre-commit.d.ts.map +1 -1
  30. package/dist/commands/pre-commit.js +158 -7
  31. package/dist/commands/pre-commit.js.map +1 -1
  32. package/dist/commands/state.d.ts +5 -1
  33. package/dist/commands/state.d.ts.map +1 -1
  34. package/dist/commands/state.js +192 -23
  35. package/dist/commands/state.js.map +1 -1
  36. package/dist/commands/sync-check.d.ts +4 -0
  37. package/dist/commands/sync-check.d.ts.map +1 -1
  38. package/dist/commands/sync-check.js +101 -14
  39. package/dist/commands/sync-check.js.map +1 -1
  40. package/dist/commands/validate.d.ts +5 -1
  41. package/dist/commands/validate.d.ts.map +1 -1
  42. package/dist/commands/validate.js +184 -28
  43. package/dist/commands/validate.js.map +1 -1
  44. package/dist/commands/watch-pr.d.ts +10 -0
  45. package/dist/commands/watch-pr.d.ts.map +1 -0
  46. package/dist/commands/watch-pr.js +443 -0
  47. package/dist/commands/watch-pr.js.map +1 -0
  48. package/dist/schemas/watch-pr-schema.d.ts +261 -0
  49. package/dist/schemas/watch-pr-schema.d.ts.map +1 -0
  50. package/dist/schemas/watch-pr-schema.js +58 -0
  51. package/dist/schemas/watch-pr-schema.js.map +1 -0
  52. package/dist/scripts/generate-watch-pr-schema.d.ts +12 -0
  53. package/dist/scripts/generate-watch-pr-schema.d.ts.map +1 -0
  54. package/dist/scripts/generate-watch-pr-schema.js +35 -0
  55. package/dist/scripts/generate-watch-pr-schema.js.map +1 -0
  56. package/dist/services/ci-provider-registry.d.ts +38 -0
  57. package/dist/services/ci-provider-registry.d.ts.map +1 -0
  58. package/dist/services/ci-provider-registry.js +53 -0
  59. package/dist/services/ci-provider-registry.js.map +1 -0
  60. package/dist/services/ci-provider.d.ts +165 -0
  61. package/dist/services/ci-provider.d.ts.map +1 -0
  62. package/dist/services/ci-provider.js +11 -0
  63. package/dist/services/ci-provider.js.map +1 -0
  64. package/dist/services/ci-providers/github-actions.d.ts +41 -0
  65. package/dist/services/ci-providers/github-actions.d.ts.map +1 -0
  66. package/dist/services/ci-providers/github-actions.js +314 -0
  67. package/dist/services/ci-providers/github-actions.js.map +1 -0
  68. package/dist/utils/check-validation.d.ts +7 -4
  69. package/dist/utils/check-validation.d.ts.map +1 -1
  70. package/dist/utils/check-validation.js +129 -48
  71. package/dist/utils/check-validation.js.map +1 -1
  72. package/dist/utils/config-loader.d.ts +15 -3
  73. package/dist/utils/config-loader.d.ts.map +1 -1
  74. package/dist/utils/config-loader.js +61 -17
  75. package/dist/utils/config-loader.js.map +1 -1
  76. package/dist/utils/context-detector.d.ts +1 -1
  77. package/dist/utils/context-detector.js +1 -1
  78. package/dist/utils/normalize-line-endings.d.ts +53 -0
  79. package/dist/utils/normalize-line-endings.d.ts.map +1 -0
  80. package/dist/utils/normalize-line-endings.js +57 -0
  81. package/dist/utils/normalize-line-endings.js.map +1 -0
  82. package/dist/utils/run-validation-with-cache.d.ts +48 -0
  83. package/dist/utils/run-validation-with-cache.d.ts.map +1 -0
  84. package/dist/utils/run-validation-with-cache.js +123 -0
  85. package/dist/utils/run-validation-with-cache.js.map +1 -0
  86. package/dist/utils/runner-adapter.d.ts +1 -0
  87. package/dist/utils/runner-adapter.d.ts.map +1 -1
  88. package/dist/utils/runner-adapter.js +25 -17
  89. package/dist/utils/runner-adapter.js.map +1 -1
  90. package/dist/utils/setup-checks/gitignore-check.d.ts +10 -15
  91. package/dist/utils/setup-checks/gitignore-check.d.ts.map +1 -1
  92. package/dist/utils/setup-checks/gitignore-check.js +20 -138
  93. package/dist/utils/setup-checks/gitignore-check.js.map +1 -1
  94. package/dist/utils/template-discovery.d.ts +40 -0
  95. package/dist/utils/template-discovery.d.ts.map +1 -0
  96. package/dist/utils/template-discovery.js +136 -0
  97. package/dist/utils/template-discovery.js.map +1 -0
  98. package/dist/utils/validate-workflow.d.ts +28 -0
  99. package/dist/utils/validate-workflow.d.ts.map +1 -0
  100. package/dist/utils/validate-workflow.js +247 -0
  101. package/dist/utils/validate-workflow.js.map +1 -0
  102. package/dist/utils/validation-cache.d.ts +30 -0
  103. package/dist/utils/validation-cache.d.ts.map +1 -0
  104. package/dist/utils/validation-cache.js +57 -0
  105. package/dist/utils/validation-cache.js.map +1 -0
  106. package/package.json +19 -16
  107. package/watch-pr-result.schema.json +204 -0
  108. package/LICENSE +0 -21
@@ -1,156 +1,38 @@
1
1
  /**
2
- * Gitignore Setup Check
2
+ * Gitignore Setup Check (DEPRECATED)
3
3
  *
4
- * Ensures that .vibe-validate-state.yaml is listed in .gitignore
5
- * to prevent committing validation state files.
4
+ * @deprecated Since v0.12.0 - State file (.vibe-validate-state.yaml) is deprecated.
5
+ * Validation history is now stored in git notes instead of a state file.
6
+ * This check always passes and does not modify .gitignore.
7
+ *
8
+ * Use `vibe-validate doctor` to detect and remove deprecated state file entries.
6
9
  */
7
- import { readFile, writeFile } from 'fs/promises';
8
- import { existsSync } from 'fs';
9
- import { join } from 'path';
10
- const STATE_FILE_ENTRY = '.vibe-validate-state.yaml';
11
10
  export class GitignoreSetupCheck {
12
11
  id = 'gitignore';
13
- name = 'Gitignore Setup';
14
- async check(options) {
15
- const cwd = options?.cwd ?? process.cwd();
16
- const gitignorePath = join(cwd, '.gitignore');
17
- // Check if .gitignore exists
18
- if (!existsSync(gitignorePath)) {
19
- return {
20
- passed: false,
21
- message: '.gitignore not found',
22
- suggestion: `Create .gitignore and add ${STATE_FILE_ENTRY}`,
23
- };
24
- }
25
- // Read .gitignore content
26
- const content = await readFile(gitignorePath, 'utf-8');
27
- // Check if state file entry exists (with flexible whitespace)
28
- const hasEntry = content
29
- .split('\n')
30
- .some(line => line.trim() === STATE_FILE_ENTRY);
31
- if (!hasEntry) {
32
- return {
33
- passed: false,
34
- message: `.gitignore missing ${STATE_FILE_ENTRY} entry`,
35
- suggestion: `Add ${STATE_FILE_ENTRY} to .gitignore`,
36
- };
37
- }
12
+ name = 'Gitignore Setup (deprecated)';
13
+ async check(_options) {
14
+ // DEPRECATED: State file is no longer used (git notes replaced it in v0.12.0)
15
+ // Always return passed - no .gitignore modifications needed
38
16
  return {
39
17
  passed: true,
40
- message: '.gitignore correctly configured with state file entry',
18
+ message: '.gitignore check skipped (state file deprecated in v0.12.0)',
41
19
  };
42
20
  }
43
- async preview(options) {
44
- const cwd = options?.cwd ?? process.cwd();
45
- const gitignorePath = join(cwd, '.gitignore');
46
- // Check current state
47
- const checkResult = await this.check(options);
48
- if (checkResult.passed) {
49
- return {
50
- description: '.gitignore already configured correctly',
51
- filesAffected: [],
52
- changes: [],
53
- };
54
- }
55
- // If .gitignore doesn't exist
56
- if (!existsSync(gitignorePath)) {
57
- const content = this.generateNewGitignoreContent();
58
- return {
59
- description: 'Create new .gitignore with vibe-validate state file entry',
60
- filesAffected: ['.gitignore'],
61
- changes: [
62
- {
63
- file: '.gitignore',
64
- action: 'create',
65
- content,
66
- },
67
- ],
68
- };
69
- }
70
- // If .gitignore exists but missing entry
21
+ async preview(_options) {
22
+ // DEPRECATED: No .gitignore modifications needed (state file deprecated)
71
23
  return {
72
- description: `Add ${STATE_FILE_ENTRY} entry to existing .gitignore`,
73
- filesAffected: ['.gitignore'],
74
- changes: [
75
- {
76
- file: '.gitignore',
77
- action: 'modify',
78
- },
79
- ],
24
+ description: 'Gitignore check deprecated (state file no longer used)',
25
+ filesAffected: [],
26
+ changes: [],
80
27
  };
81
28
  }
82
- async fix(options) {
83
- const cwd = options?.cwd ?? process.cwd();
84
- const gitignorePath = join(cwd, '.gitignore');
85
- const dryRun = options?.dryRun ?? false;
86
- // Check current state
87
- const checkResult = await this.check(options);
88
- if (checkResult.passed && !options?.force) {
89
- return {
90
- success: true,
91
- message: '.gitignore already configured correctly',
92
- filesChanged: [],
93
- };
94
- }
95
- if (dryRun) {
96
- return {
97
- success: true,
98
- message: '[dry-run] Would update .gitignore',
99
- filesChanged: [],
100
- };
101
- }
102
- // If .gitignore doesn't exist, create it
103
- if (!existsSync(gitignorePath)) {
104
- const content = this.generateNewGitignoreContent();
105
- await writeFile(gitignorePath, content, 'utf-8');
106
- return {
107
- success: true,
108
- message: 'Created .gitignore with state file entry',
109
- filesChanged: ['.gitignore'],
110
- };
111
- }
112
- // If .gitignore exists, add entry if missing
113
- const content = await readFile(gitignorePath, 'utf-8');
114
- const hasEntry = content
115
- .split('\n')
116
- .some(line => line.trim() === STATE_FILE_ENTRY);
117
- if (hasEntry) {
118
- // Entry already exists (idempotent)
119
- return {
120
- success: true,
121
- message: '.gitignore already contains state file entry',
122
- filesChanged: [],
123
- };
124
- }
125
- // Add entry to existing .gitignore
126
- const updatedContent = this.addEntryToGitignore(content);
127
- await writeFile(gitignorePath, updatedContent, 'utf-8');
29
+ async fix(_options) {
30
+ // DEPRECATED: No .gitignore modifications needed (state file deprecated)
128
31
  return {
129
32
  success: true,
130
- message: `Added ${STATE_FILE_ENTRY} to .gitignore`,
131
- filesChanged: ['.gitignore'],
33
+ message: 'Gitignore check deprecated (state file no longer used)',
34
+ filesChanged: [],
132
35
  };
133
36
  }
134
- /**
135
- * Generate content for a new .gitignore file
136
- */
137
- generateNewGitignoreContent() {
138
- return `# vibe-validate
139
- ${STATE_FILE_ENTRY}
140
- `;
141
- }
142
- /**
143
- * Add state file entry to existing .gitignore content
144
- */
145
- addEntryToGitignore(content) {
146
- // Ensure content ends with newline
147
- let updatedContent = content;
148
- if (!content.endsWith('\n')) {
149
- updatedContent += '\n';
150
- }
151
- // Add vibe-validate section
152
- updatedContent += `\n# vibe-validate\n${STATE_FILE_ENTRY}\n`;
153
- return updatedContent;
154
- }
155
37
  }
156
38
  //# sourceMappingURL=gitignore-check.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gitignore-check.js","sourceRoot":"","sources":["../../../src/utils/setup-checks/gitignore-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gitignore-check.js","sourceRoot":"","sources":["../../../src/utils/setup-checks/gitignore-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,MAAM,OAAO,mBAAmB;IACrB,EAAE,GAAG,WAAW,CAAC;IACjB,IAAI,GAAG,8BAA8B,CAAC;IAE/C,KAAK,CAAC,KAAK,CAAC,QAAqB;QAC/B,8EAA8E;QAC9E,4DAA4D;QAC5D,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,6DAA6D;SACvE,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAqB;QACjC,yEAAyE;QACzE,OAAO;YACL,WAAW,EAAE,wDAAwD;YACrE,aAAa,EAAE,EAAE;YACjB,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,QAAqB;QAC7B,yEAAyE;QACzE,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,wDAAwD;YACjE,YAAY,EAAE,EAAE;SACjB,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Template Discovery Utility
3
+ *
4
+ * Discovers and reads metadata from config templates in the config-templates/ directory.
5
+ */
6
+ /**
7
+ * Metadata for a config template
8
+ */
9
+ export interface TemplateMetadata {
10
+ /** Template filename (e.g., "typescript-nodejs.yaml") */
11
+ filename: string;
12
+ /** Display name extracted from header comment (e.g., "TypeScript for Node.js") */
13
+ displayName: string;
14
+ /** Short description extracted from template (e.g., "Node.js apps, APIs, and backend services") */
15
+ description: string;
16
+ }
17
+ /**
18
+ * Discover all available config templates
19
+ *
20
+ * Scans the config-templates/ directory and extracts metadata from each template.
21
+ * Results are sorted alphabetically by filename.
22
+ *
23
+ * @returns Array of template metadata
24
+ */
25
+ export declare function discoverTemplates(): TemplateMetadata[];
26
+ /**
27
+ * Format template list for CLI output
28
+ *
29
+ * Creates a human-readable list of templates with descriptions.
30
+ *
31
+ * Example output:
32
+ * ```
33
+ * • typescript-library.yaml - TypeScript libraries and npm packages
34
+ * • typescript-nodejs.yaml - Node.js apps, APIs, and backend services
35
+ * ```
36
+ *
37
+ * @returns Formatted template list (one template per line)
38
+ */
39
+ export declare function formatTemplateList(): string[];
40
+ //# sourceMappingURL=template-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-discovery.d.ts","sourceRoot":"","sources":["../../src/utils/template-discovery.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,WAAW,EAAE,MAAM,CAAC;CACrB;AAoFD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,gBAAgB,EAAE,CAsBtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAa7C"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Template Discovery Utility
3
+ *
4
+ * Discovers and reads metadata from config templates in the config-templates/ directory.
5
+ */
6
+ import { readdirSync, readFileSync, existsSync } from 'fs';
7
+ import { join, dirname } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import { splitLines } from './normalize-line-endings.js';
10
+ /**
11
+ * Get the absolute path to the config-templates directory
12
+ *
13
+ * This function works both in development (from source) and when installed as npm package.
14
+ *
15
+ * @returns Absolute path to config-templates directory
16
+ */
17
+ function getTemplatesDir() {
18
+ // In production (npm package), templates are at <package-root>/config-templates
19
+ // In development, templates are at <repo-root>/config-templates
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+ // Try paths in order:
23
+ // 1. Development: packages/cli/src/utils/../../../config-templates
24
+ const devPath = join(__dirname, '../../../../config-templates');
25
+ if (existsSync(devPath)) {
26
+ return devPath;
27
+ }
28
+ // 2. Production: packages/cli/dist/utils/../../config-templates
29
+ const prodPath = join(__dirname, '../../../config-templates');
30
+ if (existsSync(prodPath)) {
31
+ return prodPath;
32
+ }
33
+ // 3. Fallback: assume monorepo root
34
+ const fallbackPath = join(process.cwd(), 'config-templates');
35
+ return fallbackPath;
36
+ }
37
+ /**
38
+ * Extract template metadata from YAML file
39
+ *
40
+ * Parses the header comment block to extract display name and description.
41
+ *
42
+ * Expected format:
43
+ * ```yaml
44
+ * # ============================================================================
45
+ * # CONFIGURATION TEMPLATE - vibe-validate for TypeScript Libraries
46
+ * # ============================================================================
47
+ * # ...
48
+ * # This template is optimized for TypeScript libraries and npm packages.
49
+ * ```
50
+ *
51
+ * @param filename - Template filename
52
+ * @param content - Template file content
53
+ * @returns Template metadata
54
+ */
55
+ function parseTemplateMetadata(filename, content) {
56
+ const lines = splitLines(content);
57
+ // Find the title line (line 2, format: "# CONFIGURATION TEMPLATE - <title>")
58
+ let displayName = filename.replace('.yaml', '');
59
+ const titleLine = lines.find(line => line.includes('CONFIGURATION TEMPLATE -'));
60
+ if (titleLine) {
61
+ const match = titleLine.match(/CONFIGURATION TEMPLATE\s*-\s*(.+)/);
62
+ if (match) {
63
+ displayName = match[1].trim();
64
+ // Remove "vibe-validate for " prefix if present
65
+ displayName = displayName.replace(/^vibe-validate for\s+/i, '');
66
+ }
67
+ }
68
+ // Find the description (first line starting with "# This template is")
69
+ let description = '';
70
+ const descLine = lines.find(line => line.trim().startsWith('# This template is'));
71
+ if (descLine) {
72
+ description = descLine.replace(/^#\s*This template is\s+/, '').replace(/\.$/, '').trim();
73
+ // Capitalize first letter
74
+ if (description.length > 0) {
75
+ description = description.charAt(0).toUpperCase() + description.slice(1);
76
+ }
77
+ }
78
+ return {
79
+ filename,
80
+ displayName,
81
+ description,
82
+ };
83
+ }
84
+ /**
85
+ * Discover all available config templates
86
+ *
87
+ * Scans the config-templates/ directory and extracts metadata from each template.
88
+ * Results are sorted alphabetically by filename.
89
+ *
90
+ * @returns Array of template metadata
91
+ */
92
+ export function discoverTemplates() {
93
+ const templatesDir = getTemplatesDir();
94
+ // Check if directory exists
95
+ if (!existsSync(templatesDir)) {
96
+ return [];
97
+ }
98
+ // Read all .yaml files
99
+ const files = readdirSync(templatesDir)
100
+ .filter(file => file.endsWith('.yaml'))
101
+ .sort();
102
+ // Parse metadata from each template
103
+ const templates = [];
104
+ for (const file of files) {
105
+ const filePath = join(templatesDir, file);
106
+ const content = readFileSync(filePath, 'utf-8');
107
+ templates.push(parseTemplateMetadata(file, content));
108
+ }
109
+ return templates;
110
+ }
111
+ /**
112
+ * Format template list for CLI output
113
+ *
114
+ * Creates a human-readable list of templates with descriptions.
115
+ *
116
+ * Example output:
117
+ * ```
118
+ * • typescript-library.yaml - TypeScript libraries and npm packages
119
+ * • typescript-nodejs.yaml - Node.js apps, APIs, and backend services
120
+ * ```
121
+ *
122
+ * @returns Formatted template list (one template per line)
123
+ */
124
+ export function formatTemplateList() {
125
+ const templates = discoverTemplates();
126
+ if (templates.length === 0) {
127
+ return ['No templates found'];
128
+ }
129
+ return templates.map(t => {
130
+ if (t.description) {
131
+ return `• ${t.filename} - ${t.description}`;
132
+ }
133
+ return `• ${t.filename}`;
134
+ });
135
+ }
136
+ //# sourceMappingURL=template-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-discovery.js","sourceRoot":"","sources":["../../src/utils/template-discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Shared Validation Workflow
3
+ *
4
+ * Core validation logic used by both validate and pre-commit commands.
5
+ * Handles caching, history recording, and output formatting.
6
+ */
7
+ import type { VibeValidateConfig } from '@vibe-validate/config';
8
+ import type { ValidationResult } from '@vibe-validate/core';
9
+ import type { AgentContext } from './context-detector.js';
10
+ export interface ValidateWorkflowOptions {
11
+ force?: boolean;
12
+ verbose?: boolean;
13
+ yaml?: boolean;
14
+ check?: boolean;
15
+ context: AgentContext;
16
+ }
17
+ /**
18
+ * Execute validation workflow with caching, history recording, and output formatting.
19
+ *
20
+ * This is the shared implementation used by both `validate` and `pre-commit` commands.
21
+ *
22
+ * @param config - Loaded vibe-validate configuration
23
+ * @param options - Validation options (force, verbose, yaml, check, context)
24
+ * @returns Validation result
25
+ * @throws Error if validation encounters fatal error
26
+ */
27
+ export declare function runValidateWorkflow(config: VibeValidateConfig, options: ValidateWorkflowOptions): Promise<ValidationResult>;
28
+ //# sourceMappingURL=validate-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-workflow.d.ts","sourceRoot":"","sources":["../../src/utils/validate-workflow.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAS5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAK1D,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CAmP3B"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Shared Validation Workflow
3
+ *
4
+ * Core validation logic used by both validate and pre-commit commands.
5
+ * Handles caching, history recording, and output formatting.
6
+ */
7
+ import { runValidation } from '@vibe-validate/core';
8
+ import { getGitTreeHash } from '@vibe-validate/git';
9
+ import { recordValidationHistory, checkWorktreeStability, checkHistoryHealth, readHistoryNote, } from '@vibe-validate/history';
10
+ import { createRunnerConfig } from './runner-adapter.js';
11
+ import chalk from 'chalk';
12
+ import { stringify as yamlStringify } from 'yaml';
13
+ /**
14
+ * Execute validation workflow with caching, history recording, and output formatting.
15
+ *
16
+ * This is the shared implementation used by both `validate` and `pre-commit` commands.
17
+ *
18
+ * @param config - Loaded vibe-validate configuration
19
+ * @param options - Validation options (force, verbose, yaml, check, context)
20
+ * @returns Validation result
21
+ * @throws Error if validation encounters fatal error
22
+ */
23
+ export async function runValidateWorkflow(config, options) {
24
+ try {
25
+ // If --check flag is used, only check validation state without running
26
+ if (options.check) {
27
+ const yaml = options.yaml ?? false;
28
+ const { checkValidationStatus } = await import('./check-validation.js');
29
+ await checkValidationStatus(config, yaml);
30
+ // checkValidationStatus calls process.exit, so this line never executes
31
+ // But TypeScript doesn't know that, so we need to satisfy the return type
32
+ throw new Error('checkValidationStatus should have exited');
33
+ }
34
+ const verbose = options.verbose ?? false;
35
+ const yaml = options.yaml ?? false;
36
+ // Create runner config
37
+ const runnerConfig = createRunnerConfig(config, {
38
+ force: options.force,
39
+ verbose,
40
+ yaml,
41
+ context: options.context,
42
+ });
43
+ // Get tree hash BEFORE validation (for caching and stability check)
44
+ let treeHashBefore = null;
45
+ try {
46
+ treeHashBefore = await getGitTreeHash();
47
+ }
48
+ catch (_error) {
49
+ // Not in git repo or git command failed - continue without history
50
+ if (verbose) {
51
+ console.warn(chalk.yellow('āš ļø Could not get git tree hash - history recording disabled'));
52
+ }
53
+ }
54
+ // Check cache: if validation already passed for this tree hash, skip re-running
55
+ if (treeHashBefore && !options.force) {
56
+ try {
57
+ const historyNote = await readHistoryNote(treeHashBefore);
58
+ if (historyNote && historyNote.runs.length > 0) {
59
+ // Find most recent passing run
60
+ const passingRun = [...historyNote.runs]
61
+ .reverse()
62
+ .find(run => run.passed);
63
+ if (passingRun) {
64
+ if (yaml) {
65
+ // YAML mode: Output cached result as YAML to stdout
66
+ await new Promise(resolve => setTimeout(resolve, 10));
67
+ // Output YAML document separator (RFC 4627)
68
+ process.stdout.write('---\n');
69
+ process.stdout.write(yamlStringify(passingRun.result));
70
+ // Wait for stdout to flush before exiting
71
+ await new Promise(resolve => {
72
+ if (process.stdout.write('')) {
73
+ resolve();
74
+ }
75
+ else {
76
+ process.stdout.once('drain', resolve);
77
+ }
78
+ });
79
+ }
80
+ else {
81
+ // Human-readable mode: Display cache hit message
82
+ const durationSecs = (passingRun.duration / 1000).toFixed(1);
83
+ console.log(chalk.green('āœ… Validation already passed for current working tree'));
84
+ console.log(chalk.gray(` Tree hash: ${treeHashBefore.substring(0, 12)}...`));
85
+ console.log(chalk.gray(` Last validated: ${passingRun.timestamp}`));
86
+ console.log(chalk.gray(` Duration: ${durationSecs}s`));
87
+ console.log(chalk.gray(` Branch: ${passingRun.branch}`));
88
+ if (passingRun.result?.phases) {
89
+ const totalSteps = passingRun.result.phases.reduce((sum, phase) => sum + (phase.steps?.length || 0), 0);
90
+ console.log(chalk.gray(` Phases: ${passingRun.result.phases.length}, Steps: ${totalSteps}`));
91
+ }
92
+ }
93
+ // Cache hit - return early without calling process.exit
94
+ // This allows tests to complete without throwing process.exit errors
95
+ // In production, Commander will exit with code 0
96
+ // Mark result as from cache so caller knows not to call process.exit
97
+ const result = passingRun.result;
98
+ result._fromCache = true;
99
+ return result;
100
+ }
101
+ }
102
+ }
103
+ catch (_error) {
104
+ // Cache check failed - proceed with validation
105
+ // This is expected for first-time validation
106
+ }
107
+ }
108
+ // Display tree hash before running validation (debugging/transparency aid)
109
+ // This goes to stderr, so it's visible even in YAML mode
110
+ if (treeHashBefore) {
111
+ console.error(chalk.gray(`🌳 Working tree: ${treeHashBefore.slice(0, 12)}...`));
112
+ if (!yaml) {
113
+ console.log(''); // Blank line for readability (human mode only)
114
+ }
115
+ }
116
+ // Run validation
117
+ const result = await runValidation(runnerConfig);
118
+ // Record validation history (if in git repo and stability check passes)
119
+ if (treeHashBefore) {
120
+ try {
121
+ // Check if worktree changed during validation
122
+ const stability = await checkWorktreeStability(treeHashBefore);
123
+ if (!stability.stable) {
124
+ console.warn(chalk.yellow('\nāš ļø Worktree changed during validation'));
125
+ console.warn(chalk.yellow(` Before: ${stability.treeHashBefore.slice(0, 12)}...`));
126
+ console.warn(chalk.yellow(` After: ${stability.treeHashAfter.slice(0, 12)}...`));
127
+ console.warn(chalk.yellow(' Results valid but history not recorded (unstable state)'));
128
+ }
129
+ else {
130
+ // Record to git notes
131
+ const recordResult = await recordValidationHistory(treeHashBefore, result);
132
+ if (recordResult.recorded) {
133
+ if (verbose) {
134
+ console.log(chalk.gray(`\nšŸ“ History recorded (tree: ${treeHashBefore.slice(0, 12)})`));
135
+ }
136
+ }
137
+ else if (verbose) {
138
+ console.warn(chalk.yellow(`āš ļø History recording failed: ${recordResult.reason}`));
139
+ }
140
+ }
141
+ }
142
+ catch (error) {
143
+ // Silent failure - don't block validation
144
+ if (verbose) {
145
+ const errorMessage = error instanceof Error ? error.message : String(error);
146
+ console.warn(chalk.yellow(`āš ļø History recording error: ${errorMessage}`));
147
+ }
148
+ }
149
+ }
150
+ // Proactive health check (non-blocking)
151
+ try {
152
+ const health = await checkHistoryHealth();
153
+ if (health.shouldWarn) {
154
+ console.log('');
155
+ console.log(chalk.blue(health.warningMessage));
156
+ }
157
+ }
158
+ catch {
159
+ // Silent failure - don't block validation
160
+ }
161
+ // If validation failed, show agent-friendly error details
162
+ if (!result.passed) {
163
+ console.error(chalk.blue('\nšŸ“‹ View error details:'), chalk.white('vibe-validate state'));
164
+ if (result.rerunCommand) {
165
+ console.error(chalk.blue('šŸ”„ To retry:'), chalk.white(result.rerunCommand));
166
+ }
167
+ if (result.fullLogFile) {
168
+ console.error(chalk.blue('šŸ“„ Full log:'), chalk.gray(result.fullLogFile));
169
+ }
170
+ // Context-aware extraction quality feedback (only when developerFeedback is enabled)
171
+ if (config.developerFeedback) {
172
+ // Check if any steps had poor extraction quality
173
+ const poorExtractionSteps = result.phases
174
+ ?.flatMap(phase => phase.steps || [])
175
+ .filter(step => !step.passed && step.extractionQuality && step.extractionQuality.score < 50);
176
+ if (poorExtractionSteps && poorExtractionSteps.length > 0) {
177
+ // Detect if we're dogfooding (in the vibe-validate project itself)
178
+ const isDogfooding = process.cwd().includes('vibe-validate');
179
+ console.error('');
180
+ console.error(chalk.yellow('āš ļø Poor extraction quality detected'));
181
+ if (isDogfooding) {
182
+ // Developing vibe-validate itself: direct contributor call-to-action
183
+ console.error(chalk.yellow(' šŸ’” vibe-validate improvement opportunity: Improve extractors in packages/extractors/'));
184
+ console.error(chalk.gray(' See packages/extractors/test/samples/ for how to add test cases'));
185
+ }
186
+ else {
187
+ // External project: user feedback to improve vibe-validate
188
+ console.error(chalk.yellow(' šŸ’” Help improve vibe-validate by reporting this extraction issue'));
189
+ console.error(chalk.gray(' https://github.com/anthropics/vibe-validate/issues/new?template=extractor-improvement.yml'));
190
+ }
191
+ }
192
+ }
193
+ }
194
+ // Output YAML validation result if --yaml flag is set
195
+ if (yaml) {
196
+ // Small delay to ensure stderr is flushed before writing to stdout
197
+ await new Promise(resolve => setTimeout(resolve, 10));
198
+ // Output YAML document separator (RFC 4627) to mark transition from stderr to stdout
199
+ process.stdout.write('---\n');
200
+ // Output pure YAML without headers (workflow provides display framing)
201
+ process.stdout.write(yamlStringify(result));
202
+ // CRITICAL: Wait for stdout to flush before exiting
203
+ // When stdout is redirected to a file (CI), process.exit() can kill the process
204
+ // before the write buffer is flushed, causing truncated output
205
+ await new Promise(resolve => {
206
+ if (process.stdout.write('')) {
207
+ // Write buffer is empty, can exit immediately
208
+ resolve();
209
+ }
210
+ else {
211
+ // Wait for drain event
212
+ process.stdout.once('drain', resolve);
213
+ }
214
+ });
215
+ }
216
+ return result;
217
+ }
218
+ catch (error) {
219
+ console.error(chalk.red('āŒ Validation failed with error:'), error);
220
+ // If YAML mode, output error as YAML to stdout for CI extraction
221
+ if (options.yaml) {
222
+ const errorResult = {
223
+ passed: false,
224
+ timestamp: new Date().toISOString(),
225
+ error: error instanceof Error ? error.message : String(error),
226
+ errorStack: error instanceof Error ? error.stack : undefined,
227
+ };
228
+ // Small delay to ensure stderr is flushed before writing to stdout
229
+ await new Promise(resolve => setTimeout(resolve, 10));
230
+ // Output YAML document separator
231
+ process.stdout.write('---\n');
232
+ process.stdout.write(yamlStringify(errorResult));
233
+ // Wait for stdout to flush before exiting
234
+ await new Promise(resolve => {
235
+ if (process.stdout.write('')) {
236
+ resolve();
237
+ }
238
+ else {
239
+ process.stdout.once('drain', resolve);
240
+ }
241
+ });
242
+ }
243
+ // Re-throw to allow caller to handle exit
244
+ throw error;
245
+ }
246
+ }
247
+ //# sourceMappingURL=validate-workflow.js.map