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 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
- const pathValidation = this._validatePaths(repoPaths);
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
- // Map resolved paths back to original paths in error messages
414
+ // Categorize errors into duplicate and nested types
415
+ const duplicateErrors = [];
416
+ const nestedErrors = [];
417
+
412
418
  pathValidation.errors.forEach(error => {
413
- errors.push(error);
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 start or end with special characters
429
- const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/;
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
 
@@ -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
- const repoName = relativePath === '.' ? path.basename(rootPath) : relativePath.replace(/\//g, '-');
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: relativePath,
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.2",
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": {