@polymorphism-tech/morph-spec 7.2.0 → 7.3.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 (61) hide show
  1. package/README.md +2 -2
  2. package/claude-plugin.json +1 -1
  3. package/docs/CHEATSHEET.md +1 -1
  4. package/docs/QUICKSTART.md +1 -1
  5. package/framework/CLAUDE.md +1 -1
  6. package/framework/commands/morph-apply.md +1 -1
  7. package/framework/commands/morph-proposal.md +1 -1
  8. package/framework/hooks/README.md +1 -1
  9. package/framework/hooks/claude-code/core/validate-completion.js +136 -136
  10. package/framework/phases.json +1 -1
  11. package/framework/skills/README.md +1 -1
  12. package/framework/skills/core/morph-apply/SKILL.md +1 -1
  13. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  14. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  15. package/framework/skills/core/morph-implement/SKILL.md +1 -1
  16. package/framework/skills/core/morph-plan/SKILL.md +1 -1
  17. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  18. package/framework/skills/core/morph-review/SKILL.md +1 -1
  19. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  20. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  21. package/framework/templates/README.md +708 -709
  22. package/framework/templates/REGISTRY.json +1 -20
  23. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  24. package/framework/templates/docs/proposal.md +171 -171
  25. package/framework/templates/feature/recap.md +145 -145
  26. package/package.json +1 -1
  27. package/src/commands/project/doctor.js +941 -946
  28. package/src/commands/project/status.js +373 -376
  29. package/src/commands/validation/validate-feature.js +315 -287
  30. package/src/core/state/state-manager.js +900 -934
  31. package/src/dashboard/app/App.jsx +29 -31
  32. package/src/dashboard/app/components/Sidebar.jsx +44 -45
  33. package/src/dashboard/scanner/index.js +20 -21
  34. package/src/lib/context/context-builder.js +3 -3
  35. package/src/lib/generators/context-generator.js +2 -3
  36. package/src/lib/generators/mandate-generator.js +48 -92
  37. package/src/lib/generators/metadata-extractor.js +385 -386
  38. package/src/lib/generators/recap-generator.js +285 -285
  39. package/src/lib/validators/content/content-validator.js +89 -286
  40. package/src/lib/validators/content/index.js +0 -1
  41. package/framework/templates/feature/tasks.md +0 -199
  42. package/framework/workflows/README.md +0 -1041
  43. package/framework/workflows/configs/fast-track.json +0 -46
  44. package/framework/workflows/configs/full.json +0 -64
  45. package/framework/workflows/configs/quick.json +0 -32
  46. package/framework/workflows/configs/standard.json +0 -57
  47. package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +0 -392
  48. package/framework/workflows/docs/design-impl.md +0 -37
  49. package/framework/workflows/docs/enforcement-pipeline.md +0 -668
  50. package/framework/workflows/docs/fast-track.md +0 -29
  51. package/framework/workflows/docs/full-morph.md +0 -69
  52. package/framework/workflows/docs/standard.md +0 -49
  53. package/framework/workflows/docs/ui-refresh.md +0 -39
  54. package/src/commands/state/index.js +0 -7
  55. package/src/core/workflows/workflow-detector.js +0 -586
  56. package/src/core/workflows/workflow-router.js +0 -204
  57. package/src/dashboard/app/pages/Workflows.jsx +0 -346
  58. package/src/dashboard/scanner/workflows-scanner.js +0 -18
  59. package/src/lib/phase-chain/phase-validator.js +0 -359
  60. package/src/lib/validators/content/cross-phase-validator.js +0 -281
  61. package/src/lib/validators/spec-validator.js +0 -312
@@ -1,287 +1,315 @@
1
- /**
2
- * MORPH-SPEC Feature Validation Command
3
- *
4
- * Content-aware validation of feature outputs at each phase.
5
- * Checks section presence, schema consistency, and contract correctness.
6
- *
7
- * Usage:
8
- * morph-spec validate <feature-name> [--phase <phase>] [--json] [--verbose]
9
- *
10
- * Example:
11
- * morph-spec validate user-portal --phase design
12
- * morph-spec validate user-portal --json
13
- */
14
-
15
- import fs from 'fs';
16
- import path from 'path';
17
- import chalk from 'chalk';
18
- import { getFeature } from '../../core/state/state-manager.js';
19
-
20
- /**
21
- * Expected sections per output file
22
- */
23
- const OUTPUT_SECTIONS = {
24
- 'proposal.md': {
25
- required: ['Overview', 'Problem', 'Solution', 'Scope'],
26
- optional: ['Agents', 'Stack', 'Risks']
27
- },
28
- 'spec.md': {
29
- required: [
30
- 'Overview', 'Functional Requirements', 'Non-Functional Requirements',
31
- 'Technical Architecture', 'Data Model'
32
- ],
33
- optional: [
34
- 'Infrastructure', 'Testing Strategy', 'Security',
35
- 'Performance', 'Edge Cases', 'Clarifications'
36
- ]
37
- },
38
- 'mandate.md': {
39
- required: ['Stack', 'Standards', 'Done'],
40
- optional: ['Patterns proibidos', 'Riscos']
41
- },
42
- 'decisions.md': {
43
- required: ['ADR', 'Status', 'Context', 'Decision'],
44
- optional: ['Consequences', 'Alternatives', 'Costs']
45
- },
46
- 'tasks.md': {
47
- required: ['T001', 'category', 'dependencies'],
48
- optional: ['CHECKPOINT', 'estimatedMinutes']
49
- },
50
- 'review-report.md': {
51
- required: ['Scores', 'Recommendation'],
52
- optional: ['Blockers', 'Improvements']
53
- },
54
- };
55
-
56
- /**
57
- * Phase-to-output mapping (v5 NLH)
58
- */
59
- const PHASE_OUTPUTS = {
60
- proposal: ['proposal.md'],
61
- uiux: ['design-system.md', 'mockups.md', 'components.md', 'flows.md'],
62
- plan: ['spec.md', 'mandate.md', 'tasks.md'],
63
- implement: ['recap.md'],
64
- review: ['review-report.md'],
65
- };
66
-
67
- /**
68
- * Phase-to-folder mapping (v5 filesystem convention)
69
- */
70
- const PHASE_FOLDERS = {
71
- proposal: '0-proposal',
72
- uiux: '1-design',
73
- plan: '2-plan',
74
- implement: '3-implement',
75
- review: '4-review',
76
- };
77
-
78
- /**
79
- * Validate a single output file
80
- */
81
- function validateOutput(featurePath, outputFile) {
82
- const filePath = path.join(featurePath, outputFile);
83
- const result = {
84
- file: outputFile,
85
- exists: false,
86
- sections: { found: [], missing: [], optional: [] },
87
- warnings: [],
88
- errors: [],
89
- lineCount: 0
90
- };
91
-
92
- if (!fs.existsSync(filePath)) {
93
- result.errors.push(`File not found: ${outputFile}`);
94
- return result;
95
- }
96
-
97
- result.exists = true;
98
- const content = fs.readFileSync(filePath, 'utf8');
99
- result.lineCount = content.split('\n').length;
100
-
101
- // Skip section checks for files without defined sections
102
- const sections = OUTPUT_SECTIONS[outputFile];
103
- if (!sections) {
104
- return result;
105
- }
106
-
107
- // v5: only markdown outputs — contracts moved into spec.md
108
- validateMarkdownSections(content, sections, result);
109
-
110
- // Size warnings
111
- if (result.lineCount < 10) {
112
- result.warnings.push(`File is very short (${result.lineCount} lines) — may be incomplete`);
113
- }
114
-
115
- return result;
116
- }
117
-
118
- /**
119
- * Validate markdown sections
120
- */
121
- function validateMarkdownSections(content, sections, result) {
122
- const contentLower = content.toLowerCase();
123
-
124
- for (const section of sections.required) {
125
- if (contentLower.includes(section.toLowerCase())) {
126
- result.sections.found.push(section);
127
- } else {
128
- result.sections.missing.push(section);
129
- result.errors.push(`Missing required section: "${section}"`);
130
- }
131
- }
132
-
133
- for (const section of (sections.optional || [])) {
134
- if (contentLower.includes(section.toLowerCase())) {
135
- result.sections.optional.push(section);
136
- }
137
- }
138
- }
139
-
140
- /**
141
- * Main validate command
142
- */
143
- export async function validateFeatureCommand(featureName, options = {}) {
144
- if (!featureName) {
145
- console.error(chalk.red('Error: Feature name required'));
146
- console.error(chalk.gray('Usage: morph-spec validate <feature-name> [--phase <phase>]'));
147
- process.exit(1);
148
- }
149
-
150
- const featurePath = path.join(process.cwd(), '.morph/features', featureName);
151
-
152
- if (!fs.existsSync(featurePath)) {
153
- console.error(chalk.red(`Error: Feature directory not found: ${featurePath}`));
154
- process.exit(1);
155
- }
156
-
157
- // Determine which outputs to validate
158
- const phase = options.phase || null;
159
- let outputFiles;
160
-
161
- if (phase) {
162
- outputFiles = PHASE_OUTPUTS[phase];
163
- if (!outputFiles) {
164
- console.error(chalk.red(`Unknown phase: ${phase}`));
165
- console.error(chalk.gray(`Valid phases: ${Object.keys(PHASE_OUTPUTS).join(', ')}`));
166
- process.exit(1);
167
- }
168
- } else {
169
- // Validate all existing outputs across all phase folders
170
- outputFiles = [];
171
- const entries = fs.readdirSync(featurePath, { withFileTypes: true });
172
- for (const entry of entries) {
173
- if (entry.isDirectory()) {
174
- const subDir = path.join(featurePath, entry.name);
175
- const files = fs.readdirSync(subDir).filter(f => f.endsWith('.md') || f.endsWith('.cs'));
176
- outputFiles.push(...files.map(f => ({ file: f, dir: subDir })));
177
- } else if (entry.name.endsWith('.md') || entry.name.endsWith('.cs')) {
178
- outputFiles.push({ file: entry.name, dir: featurePath });
179
- }
180
- }
181
- }
182
-
183
- // Resolve phase folder for path lookup
184
- const phaseFolder = phase ? PHASE_FOLDERS[phase] : null;
185
- const searchPath = phaseFolder ? path.join(featurePath, phaseFolder) : featurePath;
186
-
187
- // Run validation
188
- const results = phase
189
- ? outputFiles.map(file => validateOutput(searchPath, file))
190
- : outputFiles.map(({ file, dir }) => validateOutput(dir, file));
191
- const schemaWarnings = []; // v5: cross-validation removed (contracts in spec.md)
192
-
193
- // Aggregate results
194
- const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
195
- const totalWarnings = results.reduce((sum, r) => sum + r.warnings.length, 0) + schemaWarnings.length;
196
- const totalFound = results.reduce((sum, r) => sum + r.sections.found.length, 0);
197
-
198
- // JSON output mode
199
- if (options.json) {
200
- const output = {
201
- feature: featureName,
202
- phase: phase || 'all',
203
- valid: totalErrors === 0,
204
- summary: {
205
- files: results.length,
206
- errors: totalErrors,
207
- warnings: totalWarnings,
208
- sectionsFound: totalFound
209
- },
210
- results: results.map(r => ({
211
- file: r.file,
212
- exists: r.exists,
213
- lineCount: r.lineCount,
214
- errors: r.errors,
215
- warnings: r.warnings,
216
- sectionsFound: r.sections.found,
217
- sectionsMissing: r.sections.missing
218
- })),
219
- schemaWarnings
220
- };
221
- console.log(JSON.stringify(output, null, 2));
222
- return;
223
- }
224
-
225
- // Visual output
226
- console.log(chalk.cyan('\n┌────────────────────────────────────────────────────────────┐'));
227
- console.log(chalk.cyan('│ MORPH-SPEC FEATURE VALIDATOR │'));
228
- console.log(chalk.cyan('└────────────────────────────────────────────────────────────┘\n'));
229
-
230
- console.log(chalk.white.bold(`Feature: ${featureName}`));
231
- if (phase) console.log(chalk.gray(`Phase: ${phase}`));
232
- console.log('');
233
-
234
- // File-by-file results
235
- for (const result of results) {
236
- const symbol = result.exists
237
- ? (result.errors.length === 0 ? '✅' : '❌')
238
- : '⬜';
239
- const name = result.exists
240
- ? (result.errors.length === 0 ? chalk.green(result.file) : chalk.red(result.file))
241
- : chalk.gray(result.file);
242
-
243
- console.log(`${symbol} ${name}${result.exists ? chalk.gray(` (${result.lineCount} lines)`) : ''}`);
244
-
245
- // Sections found
246
- if (result.sections.found.length > 0 && (options.verbose || result.errors.length > 0)) {
247
- result.sections.found.forEach(s => {
248
- console.log(chalk.green(` ✓ ${s}`));
249
- });
250
- }
251
-
252
- // Errors
253
- result.errors.forEach(err => {
254
- console.log(chalk.red(` ✗ ${err}`));
255
- });
256
-
257
- // Warnings
258
- result.warnings.forEach(warn => {
259
- console.log(chalk.yellow(` ⚠ ${warn}`));
260
- });
261
- }
262
-
263
- // Schema cross-validation
264
- if (schemaWarnings.length > 0) {
265
- console.log(chalk.yellow('\nSchema Cross-Validation:'));
266
- schemaWarnings.forEach(w => {
267
- console.log(chalk.yellow(` ⚠ ${w}`));
268
- });
269
- }
270
-
271
- // Summary
272
- console.log('');
273
- console.log(chalk.gray('─'.repeat(60)));
274
-
275
- if (totalErrors === 0) {
276
- console.log(chalk.green(`\n✓ VALIDATION PASSED — ${results.filter(r => r.exists).length} files validated`));
277
- if (totalWarnings > 0) {
278
- console.log(chalk.yellow(` ${totalWarnings} warning(s) — review recommended`));
279
- }
280
- } else {
281
- console.log(chalk.red(`\n✗ VALIDATION FAILED — ${totalErrors} error(s), ${totalWarnings} warning(s)`));
282
- console.log(chalk.yellow(' Fix errors before advancing to the next phase'));
283
- }
284
- console.log('');
285
- }
286
-
287
- export default validateFeatureCommand;
1
+ /**
2
+ * MORPH-SPEC Feature Validation Command
3
+ *
4
+ * Content-aware validation of feature outputs at each phase.
5
+ * Checks section presence, schema consistency, and contract correctness.
6
+ *
7
+ * Usage:
8
+ * morph-spec validate <feature-name> [--phase <phase>] [--json] [--verbose]
9
+ *
10
+ * Example:
11
+ * morph-spec validate user-portal --phase design
12
+ * morph-spec validate user-portal --json
13
+ */
14
+
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import chalk from 'chalk';
18
+ import { getFeature } from '../../core/state/state-manager.js';
19
+ import { validateTasksJson } from '../../lib/tasks/tasks-json-parser.js';
20
+
21
+ /**
22
+ * Expected sections per output file
23
+ */
24
+ const OUTPUT_SECTIONS = {
25
+ 'proposal.md': {
26
+ required: ['Overview', 'Problem', 'Solution', 'Scope'],
27
+ optional: ['Agents', 'Stack', 'Risks']
28
+ },
29
+ 'spec.md': {
30
+ required: [
31
+ 'Overview', 'Functional Requirements', 'Non-Functional Requirements',
32
+ 'Technical Architecture', 'Data Model'
33
+ ],
34
+ optional: [
35
+ 'Infrastructure', 'Testing Strategy', 'Security',
36
+ 'Performance', 'Edge Cases', 'Clarifications'
37
+ ]
38
+ },
39
+ 'mandate.md': {
40
+ required: ['Stack', 'Standards', 'Done'],
41
+ optional: ['Patterns proibidos', 'Riscos']
42
+ },
43
+ 'decisions.md': {
44
+ required: ['ADR', 'Status', 'Context', 'Decision'],
45
+ optional: ['Consequences', 'Alternatives', 'Costs']
46
+ },
47
+ 'review-report.md': {
48
+ required: ['Scores', 'Recommendation'],
49
+ optional: ['Blockers', 'Improvements']
50
+ },
51
+ };
52
+
53
+ /**
54
+ * Phase-to-output mapping (v5 NLH)
55
+ */
56
+ const PHASE_OUTPUTS = {
57
+ proposal: ['proposal.md'],
58
+ uiux: ['design-system.md', 'mockups.md', 'components.md', 'flows.md'],
59
+ plan: ['spec.md', 'mandate.md', 'tasks.json'],
60
+ implement: ['recap.md'],
61
+ review: ['review-report.md'],
62
+ };
63
+
64
+ /**
65
+ * Phase-to-folder mapping (v5 filesystem convention)
66
+ */
67
+ const PHASE_FOLDERS = {
68
+ proposal: '0-proposal',
69
+ uiux: '1-design',
70
+ plan: '2-plan',
71
+ implement: '3-implement',
72
+ review: '4-review',
73
+ };
74
+
75
+ /**
76
+ * Validate a single output file
77
+ */
78
+ function validateOutput(featurePath, outputFile) {
79
+ const filePath = path.join(featurePath, outputFile);
80
+ const result = {
81
+ file: outputFile,
82
+ exists: false,
83
+ sections: { found: [], missing: [], optional: [] },
84
+ warnings: [],
85
+ errors: [],
86
+ lineCount: 0
87
+ };
88
+
89
+ if (!fs.existsSync(filePath)) {
90
+ result.errors.push(`File not found: ${outputFile}`);
91
+ return result;
92
+ }
93
+
94
+ result.exists = true;
95
+ const content = fs.readFileSync(filePath, 'utf8');
96
+ result.lineCount = content.split('\n').length;
97
+
98
+ // tasks.json: structural validation against the schema, not markdown sections.
99
+ if (outputFile === 'tasks.json') {
100
+ validateTasksJsonOutput(content, result);
101
+ return result;
102
+ }
103
+
104
+ // Skip section checks for files without defined sections
105
+ const sections = OUTPUT_SECTIONS[outputFile];
106
+ if (!sections) {
107
+ return result;
108
+ }
109
+
110
+ // v5: only markdown outputs — contracts moved into spec.md
111
+ validateMarkdownSections(content, sections, result);
112
+
113
+ // Size warnings
114
+ if (result.lineCount < 10) {
115
+ result.warnings.push(`File is very short (${result.lineCount} lines) — may be incomplete`);
116
+ }
117
+
118
+ return result;
119
+ }
120
+
121
+ /**
122
+ * Validate tasks.json structurally: parseable JSON + conforms to the schema +
123
+ * has at least one task.
124
+ */
125
+ function validateTasksJsonOutput(content, result) {
126
+ let data;
127
+ try {
128
+ data = JSON.parse(content);
129
+ } catch (err) {
130
+ result.errors.push(`tasks.json is not valid JSON: ${err.message}`);
131
+ return;
132
+ }
133
+ const { valid, errors } = validateTasksJson(data);
134
+ if (!valid) {
135
+ for (const e of errors) result.errors.push(e);
136
+ return;
137
+ }
138
+ if (data.tasks.length === 0) {
139
+ result.errors.push('tasks.json has no task entries');
140
+ return;
141
+ }
142
+ result.sections.found.push(`${data.tasks.length} task(s)`);
143
+ }
144
+
145
+ /**
146
+ * Validate markdown sections
147
+ */
148
+ function validateMarkdownSections(content, sections, result) {
149
+ const contentLower = content.toLowerCase();
150
+
151
+ for (const section of sections.required) {
152
+ if (contentLower.includes(section.toLowerCase())) {
153
+ result.sections.found.push(section);
154
+ } else {
155
+ result.sections.missing.push(section);
156
+ result.errors.push(`Missing required section: "${section}"`);
157
+ }
158
+ }
159
+
160
+ for (const section of (sections.optional || [])) {
161
+ if (contentLower.includes(section.toLowerCase())) {
162
+ result.sections.optional.push(section);
163
+ }
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Main validate command
169
+ */
170
+ export async function validateFeatureCommand(featureName, options = {}) {
171
+ if (!featureName) {
172
+ console.error(chalk.red('Error: Feature name required'));
173
+ console.error(chalk.gray('Usage: morph-spec validate <feature-name> [--phase <phase>]'));
174
+ process.exit(1);
175
+ }
176
+
177
+ const featurePath = path.join(process.cwd(), '.morph/features', featureName);
178
+
179
+ if (!fs.existsSync(featurePath)) {
180
+ console.error(chalk.red(`Error: Feature directory not found: ${featurePath}`));
181
+ process.exit(1);
182
+ }
183
+
184
+ // Determine which outputs to validate
185
+ const phase = options.phase || null;
186
+ let outputFiles;
187
+
188
+ if (phase) {
189
+ outputFiles = PHASE_OUTPUTS[phase];
190
+ if (!outputFiles) {
191
+ console.error(chalk.red(`Unknown phase: ${phase}`));
192
+ console.error(chalk.gray(`Valid phases: ${Object.keys(PHASE_OUTPUTS).join(', ')}`));
193
+ process.exit(1);
194
+ }
195
+ } else {
196
+ // Validate all existing outputs across all phase folders
197
+ outputFiles = [];
198
+ const entries = fs.readdirSync(featurePath, { withFileTypes: true });
199
+ const isOutput = name => name.endsWith('.md') || name.endsWith('.cs') || name === 'tasks.json';
200
+ for (const entry of entries) {
201
+ if (entry.isDirectory()) {
202
+ const subDir = path.join(featurePath, entry.name);
203
+ const files = fs.readdirSync(subDir).filter(isOutput);
204
+ outputFiles.push(...files.map(f => ({ file: f, dir: subDir })));
205
+ } else if (isOutput(entry.name)) {
206
+ outputFiles.push({ file: entry.name, dir: featurePath });
207
+ }
208
+ }
209
+ }
210
+
211
+ // Resolve phase folder for path lookup
212
+ const phaseFolder = phase ? PHASE_FOLDERS[phase] : null;
213
+ const searchPath = phaseFolder ? path.join(featurePath, phaseFolder) : featurePath;
214
+
215
+ // Run validation
216
+ const results = phase
217
+ ? outputFiles.map(file => validateOutput(searchPath, file))
218
+ : outputFiles.map(({ file, dir }) => validateOutput(dir, file));
219
+ const schemaWarnings = []; // v5: cross-validation removed (contracts in spec.md)
220
+
221
+ // Aggregate results
222
+ const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
223
+ const totalWarnings = results.reduce((sum, r) => sum + r.warnings.length, 0) + schemaWarnings.length;
224
+ const totalFound = results.reduce((sum, r) => sum + r.sections.found.length, 0);
225
+
226
+ // JSON output mode
227
+ if (options.json) {
228
+ const output = {
229
+ feature: featureName,
230
+ phase: phase || 'all',
231
+ valid: totalErrors === 0,
232
+ summary: {
233
+ files: results.length,
234
+ errors: totalErrors,
235
+ warnings: totalWarnings,
236
+ sectionsFound: totalFound
237
+ },
238
+ results: results.map(r => ({
239
+ file: r.file,
240
+ exists: r.exists,
241
+ lineCount: r.lineCount,
242
+ errors: r.errors,
243
+ warnings: r.warnings,
244
+ sectionsFound: r.sections.found,
245
+ sectionsMissing: r.sections.missing
246
+ })),
247
+ schemaWarnings
248
+ };
249
+ console.log(JSON.stringify(output, null, 2));
250
+ return;
251
+ }
252
+
253
+ // Visual output
254
+ console.log(chalk.cyan('\n┌────────────────────────────────────────────────────────────┐'));
255
+ console.log(chalk.cyan('│ MORPH-SPEC FEATURE VALIDATOR │'));
256
+ console.log(chalk.cyan('└────────────────────────────────────────────────────────────┘\n'));
257
+
258
+ console.log(chalk.white.bold(`Feature: ${featureName}`));
259
+ if (phase) console.log(chalk.gray(`Phase: ${phase}`));
260
+ console.log('');
261
+
262
+ // File-by-file results
263
+ for (const result of results) {
264
+ const symbol = result.exists
265
+ ? (result.errors.length === 0 ? '✅' : '❌')
266
+ : '⬜';
267
+ const name = result.exists
268
+ ? (result.errors.length === 0 ? chalk.green(result.file) : chalk.red(result.file))
269
+ : chalk.gray(result.file);
270
+
271
+ console.log(`${symbol} ${name}${result.exists ? chalk.gray(` (${result.lineCount} lines)`) : ''}`);
272
+
273
+ // Sections found
274
+ if (result.sections.found.length > 0 && (options.verbose || result.errors.length > 0)) {
275
+ result.sections.found.forEach(s => {
276
+ console.log(chalk.green(` ✓ ${s}`));
277
+ });
278
+ }
279
+
280
+ // Errors
281
+ result.errors.forEach(err => {
282
+ console.log(chalk.red(` ✗ ${err}`));
283
+ });
284
+
285
+ // Warnings
286
+ result.warnings.forEach(warn => {
287
+ console.log(chalk.yellow(` ⚠ ${warn}`));
288
+ });
289
+ }
290
+
291
+ // Schema cross-validation
292
+ if (schemaWarnings.length > 0) {
293
+ console.log(chalk.yellow('\nSchema Cross-Validation:'));
294
+ schemaWarnings.forEach(w => {
295
+ console.log(chalk.yellow(` ⚠ ${w}`));
296
+ });
297
+ }
298
+
299
+ // Summary
300
+ console.log('');
301
+ console.log(chalk.gray('─'.repeat(60)));
302
+
303
+ if (totalErrors === 0) {
304
+ console.log(chalk.green(`\n✓ VALIDATION PASSED — ${results.filter(r => r.exists).length} files validated`));
305
+ if (totalWarnings > 0) {
306
+ console.log(chalk.yellow(` ${totalWarnings} warning(s) — review recommended`));
307
+ }
308
+ } else {
309
+ console.log(chalk.red(`\n✗ VALIDATION FAILED — ${totalErrors} error(s), ${totalWarnings} warning(s)`));
310
+ console.log(chalk.yellow(' Fix errors before advancing to the next phase'));
311
+ }
312
+ console.log('');
313
+ }
314
+
315
+ export default validateFeatureCommand;