kiro-spec-engine 1.4.4 → 1.5.2

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.
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Validation Engine
3
+ *
4
+ * Validates project documentation structure
5
+ */
6
+
7
+ const path = require('path');
8
+ const FileScanner = require('./file-scanner');
9
+
10
+ class ValidationEngine {
11
+ constructor(projectPath, config) {
12
+ this.projectPath = projectPath;
13
+ this.config = config;
14
+ this.scanner = new FileScanner(projectPath);
15
+ this.validationErrors = [];
16
+ this.validationWarnings = [];
17
+ }
18
+
19
+ /**
20
+ * Validate project structure
21
+ *
22
+ * @param {Object} options - Validation options
23
+ * @param {string} options.spec - Specific Spec to validate
24
+ * @param {boolean} options.all - Validate all Specs
25
+ * @returns {Promise<ValidationReport>}
26
+ */
27
+ async validate(options = {}) {
28
+ this.validationErrors = []; // Reset
29
+ this.validationWarnings = []; // Reset
30
+
31
+ await this.validateRootDirectory();
32
+
33
+ if (options.spec) {
34
+ await this.validateSpec(options.spec);
35
+ } else if (options.all) {
36
+ await this.validateAllSpecs();
37
+ }
38
+
39
+ return this.generateReport();
40
+ }
41
+
42
+ /**
43
+ * Validate root directory
44
+ *
45
+ * @returns {Promise<void>}
46
+ */
47
+ async validateRootDirectory() {
48
+ const mdFiles = await this.scanner.findMarkdownFiles(this.projectPath);
49
+ const allowedFiles = this.config.rootAllowedFiles || [];
50
+
51
+ for (const filePath of mdFiles) {
52
+ const basename = path.basename(filePath);
53
+
54
+ if (!allowedFiles.includes(basename)) {
55
+ this.validationErrors.push({
56
+ type: 'root_violation',
57
+ path: filePath,
58
+ message: `Unexpected markdown file in root: ${basename}`,
59
+ recommendation: 'Move to appropriate location or delete if temporary'
60
+ });
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Validate all Spec directories
67
+ *
68
+ * @returns {Promise<void>}
69
+ */
70
+ async validateAllSpecs() {
71
+ const specDirs = await this.scanner.findSpecDirectories();
72
+
73
+ for (const specDir of specDirs) {
74
+ const specName = path.basename(specDir);
75
+ await this.validateSpec(specName);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Validate a Spec directory
81
+ *
82
+ * @param {string} specName - Spec name
83
+ * @returns {Promise<void>}
84
+ */
85
+ async validateSpec(specName) {
86
+ const specPath = this.scanner.getSpecDirectory(specName);
87
+
88
+ // Check if Spec directory exists
89
+ if (!await this.scanner.exists(specPath)) {
90
+ this.validationErrors.push({
91
+ type: 'missing_spec',
92
+ path: specPath,
93
+ message: `Spec directory does not exist: ${specName}`,
94
+ recommendation: `Create Spec directory at ${specPath}`
95
+ });
96
+ return;
97
+ }
98
+
99
+ // Check required files
100
+ const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
101
+ for (const file of requiredFiles) {
102
+ const filePath = path.join(specPath, file);
103
+ if (!await this.scanner.exists(filePath)) {
104
+ this.validationErrors.push({
105
+ type: 'missing_required_file',
106
+ path: filePath,
107
+ message: `Missing required file: ${file}`,
108
+ recommendation: `Create ${file} in ${specName}`
109
+ });
110
+ }
111
+ }
112
+
113
+ // Check for misplaced artifacts
114
+ const files = await this.scanner.getFiles(specPath);
115
+ const allowedSubdirs = this.config.specSubdirs || [];
116
+
117
+ for (const filePath of files) {
118
+ const basename = path.basename(filePath);
119
+
120
+ // Skip required files
121
+ if (requiredFiles.includes(basename)) {
122
+ continue;
123
+ }
124
+
125
+ this.validationWarnings.push({
126
+ type: 'misplaced_artifact',
127
+ path: filePath,
128
+ message: `Artifact not in subdirectory: ${basename}`,
129
+ recommendation: `Move to appropriate subdirectory (${allowedSubdirs.join(', ')})`
130
+ });
131
+ }
132
+
133
+ // Check subdirectory naming
134
+ const subdirs = await this.scanner.getSubdirectories(specPath);
135
+
136
+ for (const subdirPath of subdirs) {
137
+ const subdirName = path.basename(subdirPath);
138
+
139
+ // Skip hidden directories
140
+ if (subdirName.startsWith('.')) {
141
+ continue;
142
+ }
143
+
144
+ if (!allowedSubdirs.includes(subdirName)) {
145
+ this.validationWarnings.push({
146
+ type: 'invalid_subdirectory',
147
+ path: subdirPath,
148
+ message: `Non-standard subdirectory: ${subdirName}`,
149
+ recommendation: `Rename to one of: ${allowedSubdirs.join(', ')}, or remove if not needed`
150
+ });
151
+ }
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Generate validation report
157
+ *
158
+ * @returns {ValidationReport}
159
+ */
160
+ generateReport() {
161
+ return {
162
+ valid: this.validationErrors.length === 0,
163
+ errors: this.validationErrors,
164
+ warnings: this.validationWarnings,
165
+ summary: {
166
+ totalErrors: this.validationErrors.length,
167
+ totalWarnings: this.validationWarnings.length
168
+ }
169
+ };
170
+ }
171
+ }
172
+
173
+ /**
174
+ * @typedef {Object} ValidationReport
175
+ * @property {boolean} valid - Whether validation passed
176
+ * @property {Object[]} errors - Validation errors
177
+ * @property {Object[]} warnings - Validation warnings
178
+ * @property {Object} summary - Summary statistics
179
+ */
180
+
181
+ module.exports = ValidationEngine;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiro-spec-engine",
3
- "version": "1.4.4",
3
+ "version": "1.5.2",
4
4
  "description": "Kiro Spec Engine - A spec-driven development engine with steering rules and quality enhancement powered by Ultrawork spirit",
5
5
  "main": "index.js",
6
6
  "bin": {