kiro-spec-engine 1.2.0 → 1.2.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.2] - 2026-01-23
11
+
12
+ ### Added
13
+ - **User Documentation**: Comprehensive guides for adoption and upgrade workflows
14
+ - `docs/adoption-guide.md`: Complete guide for adopting existing projects
15
+ - `docs/upgrade-guide.md`: Complete guide for upgrading project versions
16
+ - Step-by-step instructions with examples
17
+ - Troubleshooting sections for common issues
18
+ - Best practices and recommendations
19
+
20
+ ### Enhanced
21
+ - Improved documentation structure for better user experience
22
+ - Added practical examples for all adoption modes
23
+ - Added detailed upgrade scenarios with migration examples
24
+
25
+ ## [1.2.1] - 2026-01-23
26
+
27
+ ### Added
28
+ - **Validation System**: Comprehensive project validation
29
+ - `validateProjectStructure()`: Check required files and directories
30
+ - `validateVersionFile()`: Verify version.json structure
31
+ - `validateDependencies()`: Check Node.js and Python versions
32
+ - `validateProject()`: Complete project validation
33
+ - **Automatic Version Checking**: Detect version mismatches
34
+ - VersionChecker class for automatic version detection
35
+ - Warning display when project version differs from installed kse
36
+ - `--no-version-check` flag to suppress warnings
37
+ - `kse version-info` command for detailed version information
38
+ - **Enhanced Testing**: Added tests for validation and version checking
39
+ - 7 new unit tests for validation system
40
+ - 4 new unit tests for version checker
41
+ - Total: 25 tests passing
42
+
43
+ ### Enhanced
44
+ - CLI now checks for version mismatches before command execution
45
+ - Better error messages for validation failures
46
+ - Improved user experience with version information display
47
+
10
48
  ## [1.2.0] - 2026-01-23
11
49
 
12
50
  ### Added
@@ -11,6 +11,7 @@ const doctorCommand = require('../lib/commands/doctor');
11
11
  const adoptCommand = require('../lib/commands/adopt');
12
12
  const upgradeCommand = require('../lib/commands/upgrade');
13
13
  const rollbackCommand = require('../lib/commands/rollback');
14
+ const VersionChecker = require('../lib/version/version-checker');
14
15
 
15
16
  const i18n = getI18n();
16
17
  const t = (key, params) => i18n.t(key, params);
@@ -18,6 +19,19 @@ const t = (key, params) => i18n.t(key, params);
18
19
  // Read version from package.json
19
20
  const packageJson = require('../package.json');
20
21
 
22
+ // Create version checker instance
23
+ const versionChecker = new VersionChecker();
24
+
25
+ // Helper function to check version before command execution
26
+ async function checkVersionBeforeCommand(options = {}) {
27
+ const projectPath = process.cwd();
28
+ const noVersionCheck = options.noVersionCheck || false;
29
+
30
+ if (!noVersionCheck) {
31
+ await versionChecker.checkVersion(projectPath, { noVersionCheck });
32
+ }
33
+ }
34
+
21
35
  const program = new Command();
22
36
 
23
37
  // 版本和基本信息
@@ -27,7 +41,8 @@ program
27
41
  .version(packageJson.version, '-v, --version', 'Display version number')
28
42
  .option('-l, --lang <locale>', 'Set language (en/zh)', (locale) => {
29
43
  i18n.setLocale(locale);
30
- });
44
+ })
45
+ .option('--no-version-check', 'Suppress version mismatch warnings');
31
46
 
32
47
  // 初始化项目命令
33
48
  program
@@ -240,6 +255,15 @@ program
240
255
  }
241
256
  });
242
257
 
258
+ // 版本信息命令
259
+ program
260
+ .command('version-info')
261
+ .description('Display detailed version information')
262
+ .action(async () => {
263
+ const projectPath = process.cwd();
264
+ await versionChecker.displayVersionInfo(projectPath);
265
+ });
266
+
243
267
  // 更新项目配置的辅助函数
244
268
  async function updateProjectConfig(projectName) {
245
269
  const envPath = path.join(process.cwd(), '.kiro/steering/ENVIRONMENT.md');
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Validation Utilities
3
+ *
4
+ * Provides validation functions for project structure, version files,
5
+ * and dependencies. Used for post-operation validation.
6
+ */
7
+
8
+ const path = require('path');
9
+ const { pathExists, readJSON } = require('./fs-utils');
10
+ const { spawn } = require('child_process');
11
+
12
+ /**
13
+ * Validates project structure
14
+ * Checks if all required files and directories exist
15
+ *
16
+ * @param {string} projectPath - Absolute path to project root
17
+ * @returns {Promise<ValidationResult>}
18
+ */
19
+ async function validateProjectStructure(projectPath) {
20
+ const errors = [];
21
+ const warnings = [];
22
+
23
+ try {
24
+ const kiroPath = path.join(projectPath, '.kiro');
25
+
26
+ // Check if .kiro/ directory exists
27
+ const kiroExists = await pathExists(kiroPath);
28
+ if (!kiroExists) {
29
+ errors.push('.kiro/ directory not found');
30
+ return { success: false, errors, warnings };
31
+ }
32
+
33
+ // Check required directories
34
+ const requiredDirs = [
35
+ { path: 'specs', required: false },
36
+ { path: 'steering', required: true },
37
+ { path: 'tools', required: true },
38
+ { path: 'backups', required: false }
39
+ ];
40
+
41
+ for (const dir of requiredDirs) {
42
+ const dirPath = path.join(kiroPath, dir.path);
43
+ const exists = await pathExists(dirPath);
44
+
45
+ if (!exists) {
46
+ if (dir.required) {
47
+ errors.push(`Required directory not found: ${dir.path}/`);
48
+ } else {
49
+ warnings.push(`Optional directory not found: ${dir.path}/`);
50
+ }
51
+ }
52
+ }
53
+
54
+ // Check required steering files
55
+ const requiredSteeringFiles = [
56
+ { path: 'steering/CORE_PRINCIPLES.md', required: true },
57
+ { path: 'steering/ENVIRONMENT.md', required: true },
58
+ { path: 'steering/CURRENT_CONTEXT.md', required: true },
59
+ { path: 'steering/RULES_GUIDE.md', required: true }
60
+ ];
61
+
62
+ for (const file of requiredSteeringFiles) {
63
+ const filePath = path.join(kiroPath, file.path);
64
+ const exists = await pathExists(filePath);
65
+
66
+ if (!exists) {
67
+ if (file.required) {
68
+ errors.push(`Required file not found: ${file.path}`);
69
+ } else {
70
+ warnings.push(`Optional file not found: ${file.path}`);
71
+ }
72
+ }
73
+ }
74
+
75
+ // Check required tool files
76
+ const requiredToolFiles = [
77
+ { path: 'tools/ultrawork_enhancer.py', required: true }
78
+ ];
79
+
80
+ for (const file of requiredToolFiles) {
81
+ const filePath = path.join(kiroPath, file.path);
82
+ const exists = await pathExists(filePath);
83
+
84
+ if (!exists) {
85
+ if (file.required) {
86
+ warnings.push(`Tool file not found: ${file.path}`);
87
+ }
88
+ }
89
+ }
90
+
91
+ return {
92
+ success: errors.length === 0,
93
+ errors,
94
+ warnings
95
+ };
96
+ } catch (error) {
97
+ errors.push(`Validation failed: ${error.message}`);
98
+ return { success: false, errors, warnings };
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Validates version.json file structure
104
+ *
105
+ * @param {string} projectPath - Absolute path to project root
106
+ * @returns {Promise<ValidationResult>}
107
+ */
108
+ async function validateVersionFile(projectPath) {
109
+ const errors = [];
110
+ const warnings = [];
111
+
112
+ try {
113
+ const versionPath = path.join(projectPath, '.kiro', 'version.json');
114
+
115
+ // Check if version.json exists
116
+ const exists = await pathExists(versionPath);
117
+ if (!exists) {
118
+ errors.push('version.json not found');
119
+ return { success: false, errors, warnings };
120
+ }
121
+
122
+ // Read and validate structure
123
+ const versionInfo = await readJSON(versionPath);
124
+
125
+ // Check required fields
126
+ const requiredFields = [
127
+ 'kse-version',
128
+ 'template-version',
129
+ 'created',
130
+ 'last-upgraded',
131
+ 'upgrade-history'
132
+ ];
133
+
134
+ for (const field of requiredFields) {
135
+ if (!(field in versionInfo)) {
136
+ errors.push(`Missing required field in version.json: ${field}`);
137
+ }
138
+ }
139
+
140
+ // Validate field types
141
+ if (versionInfo['kse-version'] && typeof versionInfo['kse-version'] !== 'string') {
142
+ errors.push('kse-version must be a string');
143
+ }
144
+
145
+ if (versionInfo['template-version'] && typeof versionInfo['template-version'] !== 'string') {
146
+ errors.push('template-version must be a string');
147
+ }
148
+
149
+ if (versionInfo['upgrade-history'] && !Array.isArray(versionInfo['upgrade-history'])) {
150
+ errors.push('upgrade-history must be an array');
151
+ }
152
+
153
+ // Validate upgrade history entries
154
+ if (Array.isArray(versionInfo['upgrade-history'])) {
155
+ versionInfo['upgrade-history'].forEach((entry, index) => {
156
+ if (!entry.from || !entry.to || !entry.date || typeof entry.success !== 'boolean') {
157
+ warnings.push(`Invalid upgrade history entry at index ${index}`);
158
+ }
159
+ });
160
+ }
161
+
162
+ return {
163
+ success: errors.length === 0,
164
+ errors,
165
+ warnings
166
+ };
167
+ } catch (error) {
168
+ errors.push(`Failed to validate version.json: ${error.message}`);
169
+ return { success: false, errors, warnings };
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Validates dependencies (Node.js and Python versions)
175
+ *
176
+ * @param {string} projectPath - Absolute path to project root
177
+ * @returns {Promise<ValidationResult>}
178
+ */
179
+ async function validateDependencies(projectPath) {
180
+ const errors = [];
181
+ const warnings = [];
182
+
183
+ try {
184
+ // Check Node.js version
185
+ const nodeVersion = process.version;
186
+ const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0]);
187
+
188
+ if (nodeMajor < 16) {
189
+ errors.push(`Node.js version ${nodeVersion} is not supported. Requires Node.js 16+`);
190
+ }
191
+
192
+ // Check Python version (if Ultrawork tools are present)
193
+ const toolPath = path.join(projectPath, '.kiro/tools/ultrawork_enhancer.py');
194
+ const toolExists = await pathExists(toolPath);
195
+
196
+ if (toolExists) {
197
+ try {
198
+ const pythonVersion = await checkPythonVersion();
199
+
200
+ if (!pythonVersion) {
201
+ warnings.push('Python not found. Ultrawork tools require Python 3.8+');
202
+ } else {
203
+ const [major, minor] = pythonVersion.split('.').map(Number);
204
+
205
+ if (major < 3 || (major === 3 && minor < 8)) {
206
+ warnings.push(`Python ${pythonVersion} found. Ultrawork tools require Python 3.8+`);
207
+ }
208
+ }
209
+ } catch (error) {
210
+ warnings.push('Could not check Python version');
211
+ }
212
+ }
213
+
214
+ return {
215
+ success: errors.length === 0,
216
+ errors,
217
+ warnings
218
+ };
219
+ } catch (error) {
220
+ errors.push(`Failed to validate dependencies: ${error.message}`);
221
+ return { success: false, errors, warnings };
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Checks Python version
227
+ *
228
+ * @returns {Promise<string|null>} - Python version string or null if not found
229
+ */
230
+ function checkPythonVersion() {
231
+ return new Promise((resolve) => {
232
+ const python = spawn('python', ['--version']);
233
+ let output = '';
234
+
235
+ python.stdout.on('data', (data) => {
236
+ output += data.toString();
237
+ });
238
+
239
+ python.stderr.on('data', (data) => {
240
+ output += data.toString();
241
+ });
242
+
243
+ python.on('close', (code) => {
244
+ if (code === 0 && output) {
245
+ // Extract version number from "Python 3.x.x"
246
+ const match = output.match(/Python (\d+\.\d+\.\d+)/);
247
+ if (match) {
248
+ resolve(match[1]);
249
+ } else {
250
+ resolve(null);
251
+ }
252
+ } else {
253
+ resolve(null);
254
+ }
255
+ });
256
+
257
+ python.on('error', () => {
258
+ resolve(null);
259
+ });
260
+
261
+ // Timeout after 5 seconds
262
+ setTimeout(() => {
263
+ python.kill();
264
+ resolve(null);
265
+ }, 5000);
266
+ });
267
+ }
268
+
269
+ /**
270
+ * Validates complete project (structure + version + dependencies)
271
+ *
272
+ * @param {string} projectPath - Absolute path to project root
273
+ * @returns {Promise<ValidationResult>}
274
+ */
275
+ async function validateProject(projectPath) {
276
+ const allErrors = [];
277
+ const allWarnings = [];
278
+
279
+ // Run all validations
280
+ const structureResult = await validateProjectStructure(projectPath);
281
+ const versionResult = await validateVersionFile(projectPath);
282
+ const depsResult = await validateDependencies(projectPath);
283
+
284
+ // Combine results
285
+ allErrors.push(...structureResult.errors);
286
+ allErrors.push(...versionResult.errors);
287
+ allErrors.push(...depsResult.errors);
288
+
289
+ allWarnings.push(...structureResult.warnings);
290
+ allWarnings.push(...versionResult.warnings);
291
+ allWarnings.push(...depsResult.warnings);
292
+
293
+ return {
294
+ success: allErrors.length === 0,
295
+ errors: allErrors,
296
+ warnings: allWarnings
297
+ };
298
+ }
299
+
300
+ module.exports = {
301
+ validateProjectStructure,
302
+ validateVersionFile,
303
+ validateDependencies,
304
+ validateProject,
305
+ checkPythonVersion
306
+ };
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Version Checker
3
+ *
4
+ * Automatically detects version mismatches between project and installed kse.
5
+ * Displays warnings and upgrade suggestions.
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+ const VersionManager = require('./version-manager');
10
+
11
+ class VersionChecker {
12
+ constructor() {
13
+ this.versionManager = new VersionManager();
14
+ this.suppressWarnings = false;
15
+ }
16
+
17
+ /**
18
+ * Checks for version mismatch and displays warning if needed
19
+ *
20
+ * @param {string} projectPath - Absolute path to project root
21
+ * @param {Object} options - Check options
22
+ * @param {boolean} options.noVersionCheck - Suppress warnings
23
+ * @returns {Promise<VersionCheckResult>}
24
+ */
25
+ async checkVersion(projectPath, options = {}) {
26
+ const { noVersionCheck = false } = options;
27
+
28
+ if (noVersionCheck || this.suppressWarnings) {
29
+ return { mismatch: false, shouldUpgrade: false };
30
+ }
31
+
32
+ try {
33
+ // Read project version
34
+ const projectVersionInfo = await this.versionManager.readVersion(projectPath);
35
+
36
+ if (!projectVersionInfo) {
37
+ // No version.json - project might not be initialized
38
+ return { mismatch: false, shouldUpgrade: false };
39
+ }
40
+
41
+ const projectVersion = projectVersionInfo['kse-version'];
42
+
43
+ // Get current kse version
44
+ const packageJson = require('../../package.json');
45
+ const kseVersion = packageJson.version;
46
+
47
+ // Check if upgrade is needed
48
+ const needsUpgrade = this.versionManager.needsUpgrade(projectVersion, kseVersion);
49
+
50
+ if (needsUpgrade) {
51
+ this.displayWarning(projectVersion, kseVersion);
52
+ }
53
+
54
+ return {
55
+ mismatch: needsUpgrade,
56
+ shouldUpgrade: needsUpgrade,
57
+ projectVersion,
58
+ kseVersion
59
+ };
60
+ } catch (error) {
61
+ // Silently fail - don't block commands if version check fails
62
+ return { mismatch: false, shouldUpgrade: false, error: error.message };
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Displays version mismatch warning
68
+ *
69
+ * @param {string} projectVersion - Project version
70
+ * @param {string} kseVersion - Current kse version
71
+ */
72
+ displayWarning(projectVersion, kseVersion) {
73
+ console.log();
74
+ console.log(chalk.yellow('⚠️ Version Mismatch Detected'));
75
+ console.log(chalk.gray(' Project initialized with kse'), chalk.cyan(`v${projectVersion}`));
76
+ console.log(chalk.gray(' Current kse version:'), chalk.cyan(`v${kseVersion}`));
77
+ console.log();
78
+ console.log(chalk.blue('💡 Tip:'), chalk.gray('Run'), chalk.cyan('kse upgrade'), chalk.gray('to update project templates'));
79
+ console.log(chalk.gray(' Or use'), chalk.cyan('--no-version-check'), chalk.gray('to suppress this warning'));
80
+ console.log();
81
+ }
82
+
83
+ /**
84
+ * Checks version and displays detailed information
85
+ *
86
+ * @param {string} projectPath - Absolute path to project root
87
+ * @returns {Promise<void>}
88
+ */
89
+ async displayVersionInfo(projectPath) {
90
+ try {
91
+ const projectVersionInfo = await this.versionManager.readVersion(projectPath);
92
+
93
+ if (!projectVersionInfo) {
94
+ console.log(chalk.yellow('⚠️ No version information found'));
95
+ console.log(chalk.gray(' This project may not be initialized with kse'));
96
+ console.log(chalk.gray(' Run'), chalk.cyan('kse adopt'), chalk.gray('to adopt this project'));
97
+ return;
98
+ }
99
+
100
+ const packageJson = require('../../package.json');
101
+ const kseVersion = packageJson.version;
102
+
103
+ console.log(chalk.blue('📦 Version Information'));
104
+ console.log();
105
+ console.log(chalk.gray('Project:'));
106
+ console.log(` kse version: ${chalk.cyan(projectVersionInfo['kse-version'])}`);
107
+ console.log(` Template version: ${chalk.cyan(projectVersionInfo['template-version'])}`);
108
+ console.log(` Created: ${chalk.gray(new Date(projectVersionInfo.created).toLocaleString())}`);
109
+ console.log(` Last upgraded: ${chalk.gray(new Date(projectVersionInfo['last-upgraded']).toLocaleString())}`);
110
+
111
+ console.log();
112
+ console.log(chalk.gray('Installed:'));
113
+ console.log(` kse version: ${chalk.cyan(kseVersion)}`);
114
+
115
+ if (projectVersionInfo['upgrade-history'].length > 0) {
116
+ console.log();
117
+ console.log(chalk.gray('Upgrade History:'));
118
+ projectVersionInfo['upgrade-history'].forEach((entry, index) => {
119
+ const icon = entry.success ? chalk.green('✅') : chalk.red('❌');
120
+ const date = new Date(entry.date).toLocaleString();
121
+ console.log(` ${icon} ${entry.from} → ${entry.to} (${date})`);
122
+ if (entry.error) {
123
+ console.log(` ${chalk.red('Error:')} ${entry.error}`);
124
+ }
125
+ });
126
+ }
127
+
128
+ console.log();
129
+
130
+ const needsUpgrade = this.versionManager.needsUpgrade(
131
+ projectVersionInfo['kse-version'],
132
+ kseVersion
133
+ );
134
+
135
+ if (needsUpgrade) {
136
+ console.log(chalk.yellow('⚠️ Upgrade available'));
137
+ console.log(chalk.gray(' Run'), chalk.cyan('kse upgrade'), chalk.gray('to update to'), chalk.cyan(`v${kseVersion}`));
138
+ } else {
139
+ console.log(chalk.green('✅ Project is up to date'));
140
+ }
141
+ } catch (error) {
142
+ console.log(chalk.red('❌ Error:'), error.message);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Suppresses version check warnings
148
+ *
149
+ * @param {boolean} suppress - Whether to suppress warnings
150
+ */
151
+ setSuppressWarnings(suppress) {
152
+ this.suppressWarnings = suppress;
153
+ }
154
+ }
155
+
156
+ module.exports = VersionChecker;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiro-spec-engine",
3
- "version": "1.2.0",
3
+ "version": "1.2.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": {