kiro-spec-engine 1.20.2 → 1.20.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/CHANGELOG.md +39 -0
- package/lib/repo/config-manager.js +31 -7
- package/lib/repo/handlers/init-handler.js +13 -1
- package/lib/repo/repo-manager.js +4 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.20.4] - 2026-02-01 🔥 HOTFIX
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Multi-Repository Validation Bug**: Fixed critical validation logic that incorrectly rejected valid multi-repository configurations
|
|
14
|
+
- Independent repositories (non-overlapping paths) now pass validation regardless of `nestedMode` setting
|
|
15
|
+
- Validation now correctly distinguishes between duplicate paths (always invalid) and nested paths (invalid only without `nestedMode`)
|
|
16
|
+
- Enhanced error messages with actionable hints: suggests enabling `nestedMode` when nested paths detected
|
|
17
|
+
- User-reported test cases now pass:
|
|
18
|
+
- ✅ Two independent repositories (`backend/`, `frontend/`)
|
|
19
|
+
- ✅ Eight independent repositories
|
|
20
|
+
- ✅ Nested repositories with `nestedMode: true`
|
|
21
|
+
- ❌ Nested repositories without `nestedMode` (correctly fails with helpful hint)
|
|
22
|
+
|
|
23
|
+
### Technical Details
|
|
24
|
+
- Enhanced `_validatePaths()` method to categorize errors into duplicate and nested types
|
|
25
|
+
- Duplicate path errors always reported (always invalid)
|
|
26
|
+
- Nested path errors only reported when `nestedMode` is false or undefined
|
|
27
|
+
- Added hint message: "Enable nestedMode in settings to allow nested repositories: { \"settings\": { \"nestedMode\": true } }"
|
|
28
|
+
- Root cause: Previous logic didn't distinguish between independent and nested repositories
|
|
29
|
+
- Reference: User bug report with 4 test cases demonstrating the issue
|
|
30
|
+
|
|
31
|
+
## [1.20.3] - 2026-02-01
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- **Nested Scanning Validation**: Fixed three critical validation issues preventing nested repository configuration saves
|
|
35
|
+
- Repository names starting with dots (`.github`, `.kiro`) now accepted as valid
|
|
36
|
+
- Path overlap validation now context-aware: allows overlapping paths in nested mode, rejects in non-nested mode
|
|
37
|
+
- Fixed empty name/path bug for root directory repositories (now normalized to '.')
|
|
38
|
+
- Added `settings.nestedMode` field to track scanning mode in configuration
|
|
39
|
+
- Successfully tested with 104 nested repositories in real-world project
|
|
40
|
+
|
|
41
|
+
### Technical Details
|
|
42
|
+
- Updated `_isValidRepoName()` regex to allow names starting with dots: `/^\.?[a-zA-Z0-9][a-zA-Z0-9._-]*$/`
|
|
43
|
+
- Modified `_validatePaths()` to accept `allowNested` parameter and skip overlap errors in nested mode
|
|
44
|
+
- Updated `validateConfig()` to pass `settings.nestedMode` to path validation
|
|
45
|
+
- Fixed `discoverRepositories()` to normalize empty relativePath to '.' instead of empty string
|
|
46
|
+
- Added detailed error reporting in `init-handler.js` to show validation errors during scanning
|
|
47
|
+
- All 1686 tests passing
|
|
48
|
+
|
|
10
49
|
## [1.20.2] - 2026-02-01
|
|
11
50
|
|
|
12
51
|
### Fixed
|
|
@@ -187,7 +187,9 @@ class ConfigManager {
|
|
|
187
187
|
|
|
188
188
|
// Validate no duplicate or overlapping paths
|
|
189
189
|
if (repoPaths.length > 0) {
|
|
190
|
-
|
|
190
|
+
// Check if nested mode is enabled in settings
|
|
191
|
+
const nestedMode = config.settings && config.settings.nestedMode === true;
|
|
192
|
+
const pathValidation = this._validatePaths(repoPaths, nestedMode);
|
|
191
193
|
errors.push(...pathValidation.errors);
|
|
192
194
|
}
|
|
193
195
|
|
|
@@ -388,9 +390,10 @@ class ConfigManager {
|
|
|
388
390
|
* Validate repository paths for duplicates and overlaps
|
|
389
391
|
* @private
|
|
390
392
|
* @param {string[]} paths - Array of repository paths
|
|
393
|
+
* @param {boolean} allowNested - Whether to allow nested paths (for nested mode)
|
|
391
394
|
* @returns {{errors: string[]}} Validation result
|
|
392
395
|
*/
|
|
393
|
-
_validatePaths(paths) {
|
|
396
|
+
_validatePaths(paths, allowNested = false) {
|
|
394
397
|
const errors = [];
|
|
395
398
|
|
|
396
399
|
// Resolve all paths to absolute for comparison
|
|
@@ -408,10 +411,30 @@ class ConfigManager {
|
|
|
408
411
|
const pathValidation = this.pathResolver.validateNoOverlap(resolvedPaths);
|
|
409
412
|
|
|
410
413
|
if (!pathValidation.valid) {
|
|
411
|
-
//
|
|
414
|
+
// Categorize errors into duplicate and nested types
|
|
415
|
+
const duplicateErrors = [];
|
|
416
|
+
const nestedErrors = [];
|
|
417
|
+
|
|
412
418
|
pathValidation.errors.forEach(error => {
|
|
413
|
-
|
|
419
|
+
if (error.includes('Duplicate path found')) {
|
|
420
|
+
duplicateErrors.push(error);
|
|
421
|
+
} else if (error.includes('nested within')) {
|
|
422
|
+
nestedErrors.push(error);
|
|
423
|
+
}
|
|
414
424
|
});
|
|
425
|
+
|
|
426
|
+
// Always report duplicate paths (always invalid)
|
|
427
|
+
errors.push(...duplicateErrors);
|
|
428
|
+
|
|
429
|
+
// Report nested paths only if nestedMode is not enabled
|
|
430
|
+
if (nestedErrors.length > 0 && !allowNested) {
|
|
431
|
+
errors.push(...nestedErrors);
|
|
432
|
+
// Add helpful hint about enabling nestedMode
|
|
433
|
+
errors.push(
|
|
434
|
+
'Hint: Enable nestedMode in settings to allow nested repositories: ' +
|
|
435
|
+
'{ "settings": { "nestedMode": true } }'
|
|
436
|
+
);
|
|
437
|
+
}
|
|
415
438
|
}
|
|
416
439
|
|
|
417
440
|
return { errors };
|
|
@@ -424,9 +447,10 @@ class ConfigManager {
|
|
|
424
447
|
* @returns {boolean} True if name is valid
|
|
425
448
|
*/
|
|
426
449
|
_isValidRepoName(name) {
|
|
427
|
-
// Allow alphanumeric, hyphens, underscores, and dots
|
|
428
|
-
// Must not
|
|
429
|
-
|
|
450
|
+
// Allow alphanumeric, hyphens, underscores, dots, and names starting with dots (hidden directories)
|
|
451
|
+
// Must not contain spaces or special characters like @, #, $, etc.
|
|
452
|
+
// Examples: "backend", ".github", ".kiro", "my-repo", "repo_name", "repo.name"
|
|
453
|
+
const validPattern = /^\.?[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
430
454
|
return validPattern.test(name);
|
|
431
455
|
}
|
|
432
456
|
|
|
@@ -76,7 +76,10 @@ class InitHandler {
|
|
|
76
76
|
remote: repo.remote,
|
|
77
77
|
defaultBranch: repo.branch,
|
|
78
78
|
parent: repo.parent || null // Include parent field
|
|
79
|
-
}))
|
|
79
|
+
})),
|
|
80
|
+
settings: {
|
|
81
|
+
nestedMode: nested // Store nested mode setting
|
|
82
|
+
}
|
|
80
83
|
};
|
|
81
84
|
|
|
82
85
|
// Save configuration
|
|
@@ -84,6 +87,15 @@ class InitHandler {
|
|
|
84
87
|
await this.configManager.saveConfig(config);
|
|
85
88
|
} catch (error) {
|
|
86
89
|
console.log(this.formatter.error(`Failed to save configuration: ${error.message}`));
|
|
90
|
+
|
|
91
|
+
// Display detailed validation errors if available
|
|
92
|
+
if (error.details && error.details.errors && Array.isArray(error.details.errors)) {
|
|
93
|
+
console.log(this.formatter.error('\nValidation errors:'));
|
|
94
|
+
error.details.errors.forEach((err, index) => {
|
|
95
|
+
console.log(this.formatter.error(` ${index + 1}. ${err}`));
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
throw error;
|
|
88
100
|
}
|
|
89
101
|
|
package/lib/repo/repo-manager.js
CHANGED
|
@@ -159,10 +159,12 @@ class RepoManager {
|
|
|
159
159
|
|
|
160
160
|
// Generate repository name from path
|
|
161
161
|
const relativePath = this.pathResolver.toRelative(currentPath, rootPath);
|
|
162
|
-
|
|
162
|
+
// Handle empty path (root directory) or '.' (current directory)
|
|
163
|
+
const normalizedPath = (!relativePath || relativePath === '.') ? '.' : relativePath;
|
|
164
|
+
const repoName = normalizedPath === '.' ? path.basename(rootPath) : normalizedPath.replace(/\//g, '-');
|
|
163
165
|
|
|
164
166
|
discovered.push({
|
|
165
|
-
path:
|
|
167
|
+
path: normalizedPath,
|
|
166
168
|
name: repoName,
|
|
167
169
|
remote: remoteUrl,
|
|
168
170
|
branch: branch,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kiro-spec-engine",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.4",
|
|
4
4
|
"description": "kiro-spec-engine (kse) - A CLI tool and npm package for spec-driven development with AI coding assistants. NOT the Kiro IDE desktop application.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|