kiro-spec-engine 1.36.0 → 1.38.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.
- package/CHANGELOG.md +27 -0
- package/lib/commands/scene.js +490 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.38.0] - 2026-02-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Scene Registry Statistics**: Dashboard for local scene package registry metrics
|
|
14
|
+
- `kse scene stats` show aggregate statistics (packages, versions, tags, ownership, deprecation, last publish)
|
|
15
|
+
- `--registry <dir>` custom registry directory
|
|
16
|
+
- `--json` structured JSON output
|
|
17
|
+
- **Scene Version Locking**: Protect specific package versions from accidental unpublish
|
|
18
|
+
- `kse scene lock set --name <pkg> --version <ver>` lock a version
|
|
19
|
+
- `kse scene lock rm --name <pkg> --version <ver>` unlock a version
|
|
20
|
+
- `kse scene lock ls --name <pkg>` list locked versions
|
|
21
|
+
- `--registry <dir>` custom registry directory
|
|
22
|
+
- `--json` structured JSON output
|
|
23
|
+
- Lock state stored as `locked: true` on version entries in `registry-index.json`
|
|
24
|
+
|
|
25
|
+
## [1.37.0] - 2026-02-10
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- **Scene Distribution Tags**: Manage distribution tags on scene packages in local registry
|
|
29
|
+
- `kse scene tag add --name <pkg> --tag <tag> --version <ver>` add a distribution tag
|
|
30
|
+
- `kse scene tag rm --name <pkg> --tag <tag>` remove a distribution tag
|
|
31
|
+
- `kse scene tag ls --name <pkg>` list all tags and latest version
|
|
32
|
+
- `--registry <dir>` custom registry directory
|
|
33
|
+
- `--json` structured JSON output
|
|
34
|
+
- Tags stored as `tags` object on package entry, separate from `latest` field
|
|
35
|
+
- "latest" tag is protected — managed automatically by publish
|
|
36
|
+
|
|
10
37
|
## [1.36.0] - 2026-02-10
|
|
11
38
|
|
|
12
39
|
### Added
|
package/lib/commands/scene.js
CHANGED
|
@@ -643,6 +643,91 @@ function registerSceneCommands(program) {
|
|
|
643
643
|
.action(async (options) => {
|
|
644
644
|
await runSceneOwnerCommand({ ...options, action: 'transfer' });
|
|
645
645
|
});
|
|
646
|
+
|
|
647
|
+
// ── scene tag ──
|
|
648
|
+
const tagCmd = sceneCmd
|
|
649
|
+
.command('tag')
|
|
650
|
+
.description('Manage distribution tags on scene packages');
|
|
651
|
+
|
|
652
|
+
tagCmd
|
|
653
|
+
.command('add')
|
|
654
|
+
.description('Add a distribution tag to a package version')
|
|
655
|
+
.requiredOption('-n, --name <name>', 'Package name')
|
|
656
|
+
.requiredOption('-t, --tag <tag>', 'Tag name')
|
|
657
|
+
.requiredOption('-v, --version <version>', 'Version to tag')
|
|
658
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
659
|
+
.option('--json', 'Print result as JSON')
|
|
660
|
+
.action(async (options) => {
|
|
661
|
+
await runSceneTagCommand({ ...options, action: 'add' });
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
tagCmd
|
|
665
|
+
.command('rm')
|
|
666
|
+
.description('Remove a distribution tag from a package')
|
|
667
|
+
.requiredOption('-n, --name <name>', 'Package name')
|
|
668
|
+
.requiredOption('-t, --tag <tag>', 'Tag name to remove')
|
|
669
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
670
|
+
.option('--json', 'Print result as JSON')
|
|
671
|
+
.action(async (options) => {
|
|
672
|
+
await runSceneTagCommand({ ...options, action: 'rm' });
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
tagCmd
|
|
676
|
+
.command('ls')
|
|
677
|
+
.description('List all distribution tags for a package')
|
|
678
|
+
.requiredOption('-n, --name <name>', 'Package name')
|
|
679
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
680
|
+
.option('--json', 'Print result as JSON')
|
|
681
|
+
.action(async (options) => {
|
|
682
|
+
await runSceneTagCommand({ ...options, action: 'ls' });
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
// ── scene stats ──
|
|
686
|
+
sceneCmd
|
|
687
|
+
.command('stats')
|
|
688
|
+
.description('Show aggregate statistics about the local scene package registry')
|
|
689
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
690
|
+
.option('--json', 'Print result as JSON')
|
|
691
|
+
.action(async (options) => {
|
|
692
|
+
await runSceneStatsCommand(options);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// ── scene lock ──
|
|
696
|
+
const lockCmd = sceneCmd
|
|
697
|
+
.command('lock')
|
|
698
|
+
.description('Manage version locks on scene packages');
|
|
699
|
+
|
|
700
|
+
lockCmd
|
|
701
|
+
.command('set')
|
|
702
|
+
.description('Lock a specific version of a package')
|
|
703
|
+
.requiredOption('-n, --name <name>', 'Package name')
|
|
704
|
+
.requiredOption('-v, --version <version>', 'Version to lock')
|
|
705
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
706
|
+
.option('--json', 'Print result as JSON')
|
|
707
|
+
.action(async (options) => {
|
|
708
|
+
await runSceneLockCommand({ ...options, action: 'set' });
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
lockCmd
|
|
712
|
+
.command('rm')
|
|
713
|
+
.description('Unlock a specific version of a package')
|
|
714
|
+
.requiredOption('-n, --name <name>', 'Package name')
|
|
715
|
+
.requiredOption('-v, --version <version>', 'Version to unlock')
|
|
716
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
717
|
+
.option('--json', 'Print result as JSON')
|
|
718
|
+
.action(async (options) => {
|
|
719
|
+
await runSceneLockCommand({ ...options, action: 'rm' });
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
lockCmd
|
|
723
|
+
.command('ls')
|
|
724
|
+
.description('List all locked versions for a package')
|
|
725
|
+
.requiredOption('-n, --name <name>', 'Package name')
|
|
726
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
727
|
+
.option('--json', 'Print result as JSON')
|
|
728
|
+
.action(async (options) => {
|
|
729
|
+
await runSceneLockCommand({ ...options, action: 'ls' });
|
|
730
|
+
});
|
|
646
731
|
}
|
|
647
732
|
|
|
648
733
|
function normalizeSourceOptions(options = {}) {
|
|
@@ -11516,6 +11601,398 @@ function printSceneOwnerSummary(options, payload) {
|
|
|
11516
11601
|
}
|
|
11517
11602
|
}
|
|
11518
11603
|
|
|
11604
|
+
// ── Scene Tag ──────────────────────────────────────────────────────────────
|
|
11605
|
+
|
|
11606
|
+
function normalizeSceneTagOptions(options = {}) {
|
|
11607
|
+
return {
|
|
11608
|
+
action: options.action ? String(options.action).trim() : undefined,
|
|
11609
|
+
name: options.name ? String(options.name).trim() : undefined,
|
|
11610
|
+
tag: options.tag ? String(options.tag).trim() : undefined,
|
|
11611
|
+
version: options.version ? String(options.version).trim() : undefined,
|
|
11612
|
+
registry: options.registry ? String(options.registry).trim() : '.kiro/registry',
|
|
11613
|
+
json: options.json === true
|
|
11614
|
+
};
|
|
11615
|
+
}
|
|
11616
|
+
|
|
11617
|
+
function validateSceneTagOptions(options) {
|
|
11618
|
+
if (!options.action) return '--action is required';
|
|
11619
|
+
const validActions = ['add', 'rm', 'ls'];
|
|
11620
|
+
if (!validActions.includes(options.action)) return `invalid action "${options.action}"`;
|
|
11621
|
+
|
|
11622
|
+
if (options.action === 'add') {
|
|
11623
|
+
if (!options.name) return '--name is required';
|
|
11624
|
+
if (!options.tag) return '--tag is required';
|
|
11625
|
+
if (!options.version) return '--version is required';
|
|
11626
|
+
if (options.tag === 'latest') return '"latest" tag is managed automatically by publish';
|
|
11627
|
+
}
|
|
11628
|
+
if (options.action === 'rm') {
|
|
11629
|
+
if (!options.name) return '--name is required';
|
|
11630
|
+
if (!options.tag) return '--tag is required';
|
|
11631
|
+
if (options.tag === 'latest') return '"latest" tag is managed automatically by publish';
|
|
11632
|
+
}
|
|
11633
|
+
if (options.action === 'ls') {
|
|
11634
|
+
if (!options.name) return '--name is required';
|
|
11635
|
+
}
|
|
11636
|
+
return null;
|
|
11637
|
+
}
|
|
11638
|
+
|
|
11639
|
+
async function runSceneTagCommand(rawOptions = {}, dependencies = {}) {
|
|
11640
|
+
const projectRoot = dependencies.projectRoot || process.cwd();
|
|
11641
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
11642
|
+
|
|
11643
|
+
const options = normalizeSceneTagOptions(rawOptions);
|
|
11644
|
+
const validationError = validateSceneTagOptions(options);
|
|
11645
|
+
|
|
11646
|
+
if (validationError) {
|
|
11647
|
+
console.error(chalk.red(`Scene tag failed: ${validationError}`));
|
|
11648
|
+
process.exitCode = 1;
|
|
11649
|
+
return null;
|
|
11650
|
+
}
|
|
11651
|
+
|
|
11652
|
+
try {
|
|
11653
|
+
const registryRoot = path.isAbsolute(options.registry)
|
|
11654
|
+
? options.registry
|
|
11655
|
+
: path.join(projectRoot, options.registry);
|
|
11656
|
+
|
|
11657
|
+
const index = await loadRegistryIndex(registryRoot, fileSystem);
|
|
11658
|
+
const packages = index.packages || {};
|
|
11659
|
+
let payload;
|
|
11660
|
+
|
|
11661
|
+
if (options.action === 'add') {
|
|
11662
|
+
if (!packages[options.name]) {
|
|
11663
|
+
throw new Error(`package "${options.name}" not found in registry`);
|
|
11664
|
+
}
|
|
11665
|
+
const pkg = packages[options.name];
|
|
11666
|
+
if (!pkg.versions || !pkg.versions[options.version]) {
|
|
11667
|
+
throw new Error(`version "${options.version}" not found for package "${options.name}"`);
|
|
11668
|
+
}
|
|
11669
|
+
if (!pkg.tags) pkg.tags = {};
|
|
11670
|
+
pkg.tags[options.tag] = options.version;
|
|
11671
|
+
await saveRegistryIndex(registryRoot, index, fileSystem);
|
|
11672
|
+
payload = {
|
|
11673
|
+
success: true,
|
|
11674
|
+
action: 'add',
|
|
11675
|
+
package: options.name,
|
|
11676
|
+
tag: options.tag,
|
|
11677
|
+
version: options.version,
|
|
11678
|
+
registry: options.registry
|
|
11679
|
+
};
|
|
11680
|
+
} else if (options.action === 'rm') {
|
|
11681
|
+
if (!packages[options.name]) {
|
|
11682
|
+
throw new Error(`package "${options.name}" not found in registry`);
|
|
11683
|
+
}
|
|
11684
|
+
const pkg = packages[options.name];
|
|
11685
|
+
if (!pkg.tags || !pkg.tags[options.tag]) {
|
|
11686
|
+
throw new Error(`tag "${options.tag}" not found for package "${options.name}"`);
|
|
11687
|
+
}
|
|
11688
|
+
delete pkg.tags[options.tag];
|
|
11689
|
+
await saveRegistryIndex(registryRoot, index, fileSystem);
|
|
11690
|
+
payload = {
|
|
11691
|
+
success: true,
|
|
11692
|
+
action: 'rm',
|
|
11693
|
+
package: options.name,
|
|
11694
|
+
tag: options.tag,
|
|
11695
|
+
registry: options.registry
|
|
11696
|
+
};
|
|
11697
|
+
} else if (options.action === 'ls') {
|
|
11698
|
+
if (!packages[options.name]) {
|
|
11699
|
+
throw new Error(`package "${options.name}" not found in registry`);
|
|
11700
|
+
}
|
|
11701
|
+
const pkg = packages[options.name];
|
|
11702
|
+
const tags = { ...(pkg.tags || {}) };
|
|
11703
|
+
payload = {
|
|
11704
|
+
success: true,
|
|
11705
|
+
action: 'ls',
|
|
11706
|
+
package: options.name,
|
|
11707
|
+
latest: pkg.latest || null,
|
|
11708
|
+
tags,
|
|
11709
|
+
registry: options.registry
|
|
11710
|
+
};
|
|
11711
|
+
}
|
|
11712
|
+
|
|
11713
|
+
printSceneTagSummary(options, payload);
|
|
11714
|
+
return payload;
|
|
11715
|
+
} catch (error) {
|
|
11716
|
+
console.error(chalk.red('Scene tag failed:'), error.message);
|
|
11717
|
+
process.exitCode = 1;
|
|
11718
|
+
return null;
|
|
11719
|
+
}
|
|
11720
|
+
}
|
|
11721
|
+
|
|
11722
|
+
function printSceneTagSummary(options, payload) {
|
|
11723
|
+
if (options.json) {
|
|
11724
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
11725
|
+
return;
|
|
11726
|
+
}
|
|
11727
|
+
|
|
11728
|
+
if (payload.action === 'add') {
|
|
11729
|
+
console.log(chalk.green(`Tag "${payload.tag}" set to version "${payload.version}" for package "${payload.package}"`));
|
|
11730
|
+
} else if (payload.action === 'rm') {
|
|
11731
|
+
console.log(chalk.green(`Tag "${payload.tag}" removed from package "${payload.package}"`));
|
|
11732
|
+
} else if (payload.action === 'ls') {
|
|
11733
|
+
const tagEntries = Object.entries(payload.tags);
|
|
11734
|
+
if (tagEntries.length === 0 && !payload.latest) {
|
|
11735
|
+
console.log(`No tags set for package "${payload.package}"`);
|
|
11736
|
+
} else {
|
|
11737
|
+
console.log(`Tags for package "${payload.package}":`);
|
|
11738
|
+
if (payload.latest) {
|
|
11739
|
+
console.log(` latest: ${payload.latest}`);
|
|
11740
|
+
}
|
|
11741
|
+
for (const [tag, version] of tagEntries) {
|
|
11742
|
+
console.log(` ${tag}: ${version}`);
|
|
11743
|
+
}
|
|
11744
|
+
}
|
|
11745
|
+
}
|
|
11746
|
+
}
|
|
11747
|
+
|
|
11748
|
+
// ── Scene Stats ───────────────────────────────────────────────────────────
|
|
11749
|
+
|
|
11750
|
+
function normalizeSceneStatsOptions(options = {}) {
|
|
11751
|
+
return {
|
|
11752
|
+
registry: options.registry ? String(options.registry).trim() : '.kiro/registry',
|
|
11753
|
+
json: options.json === true
|
|
11754
|
+
};
|
|
11755
|
+
}
|
|
11756
|
+
|
|
11757
|
+
function validateSceneStatsOptions(options) {
|
|
11758
|
+
return null;
|
|
11759
|
+
}
|
|
11760
|
+
|
|
11761
|
+
async function runSceneStatsCommand(rawOptions = {}, dependencies = {}) {
|
|
11762
|
+
const projectRoot = dependencies.projectRoot || process.cwd();
|
|
11763
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
11764
|
+
|
|
11765
|
+
const options = normalizeSceneStatsOptions(rawOptions);
|
|
11766
|
+
const validationError = validateSceneStatsOptions(options);
|
|
11767
|
+
|
|
11768
|
+
if (validationError) {
|
|
11769
|
+
console.error(chalk.red(`Scene stats failed: ${validationError}`));
|
|
11770
|
+
process.exitCode = 1;
|
|
11771
|
+
return null;
|
|
11772
|
+
}
|
|
11773
|
+
|
|
11774
|
+
try {
|
|
11775
|
+
const registryRoot = path.isAbsolute(options.registry)
|
|
11776
|
+
? options.registry
|
|
11777
|
+
: path.join(projectRoot, options.registry);
|
|
11778
|
+
|
|
11779
|
+
const index = await loadRegistryIndex(registryRoot, fileSystem);
|
|
11780
|
+
const packages = index.packages || {};
|
|
11781
|
+
const packageNames = Object.keys(packages);
|
|
11782
|
+
|
|
11783
|
+
let totalVersions = 0;
|
|
11784
|
+
let totalTags = 0;
|
|
11785
|
+
let packagesWithOwner = 0;
|
|
11786
|
+
let deprecatedPackages = 0;
|
|
11787
|
+
let mostRecent = null;
|
|
11788
|
+
|
|
11789
|
+
for (const name of packageNames) {
|
|
11790
|
+
const pkg = packages[name];
|
|
11791
|
+
const versions = pkg.versions || {};
|
|
11792
|
+
const versionKeys = Object.keys(versions);
|
|
11793
|
+
totalVersions += versionKeys.length;
|
|
11794
|
+
totalTags += Object.keys(pkg.tags || {}).length;
|
|
11795
|
+
|
|
11796
|
+
if (pkg.owner && String(pkg.owner).trim() !== '') {
|
|
11797
|
+
packagesWithOwner++;
|
|
11798
|
+
}
|
|
11799
|
+
if (pkg.deprecated) {
|
|
11800
|
+
deprecatedPackages++;
|
|
11801
|
+
}
|
|
11802
|
+
|
|
11803
|
+
for (const ver of versionKeys) {
|
|
11804
|
+
const publishedAt = versions[ver].published_at;
|
|
11805
|
+
if (publishedAt && (!mostRecent || publishedAt > mostRecent.publishedAt)) {
|
|
11806
|
+
mostRecent = { package: name, version: ver, publishedAt };
|
|
11807
|
+
}
|
|
11808
|
+
}
|
|
11809
|
+
}
|
|
11810
|
+
|
|
11811
|
+
const payload = {
|
|
11812
|
+
success: true,
|
|
11813
|
+
totalPackages: packageNames.length,
|
|
11814
|
+
totalVersions,
|
|
11815
|
+
totalTags,
|
|
11816
|
+
packagesWithOwner,
|
|
11817
|
+
packagesWithoutOwner: packageNames.length - packagesWithOwner,
|
|
11818
|
+
deprecatedPackages,
|
|
11819
|
+
mostRecentlyPublished: mostRecent,
|
|
11820
|
+
registry: options.registry
|
|
11821
|
+
};
|
|
11822
|
+
|
|
11823
|
+
printSceneStatsSummary(options, payload);
|
|
11824
|
+
return payload;
|
|
11825
|
+
} catch (error) {
|
|
11826
|
+
console.error(chalk.red('Scene stats failed:'), error.message);
|
|
11827
|
+
process.exitCode = 1;
|
|
11828
|
+
return null;
|
|
11829
|
+
}
|
|
11830
|
+
}
|
|
11831
|
+
|
|
11832
|
+
function printSceneStatsSummary(options, payload) {
|
|
11833
|
+
if (options.json) {
|
|
11834
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
11835
|
+
return;
|
|
11836
|
+
}
|
|
11837
|
+
|
|
11838
|
+
console.log(chalk.bold('Registry Statistics'));
|
|
11839
|
+
console.log(` Packages: ${payload.totalPackages}`);
|
|
11840
|
+
console.log(` Versions: ${payload.totalVersions}`);
|
|
11841
|
+
console.log(` Tags: ${payload.totalTags}`);
|
|
11842
|
+
console.log(` With owner: ${payload.packagesWithOwner}`);
|
|
11843
|
+
console.log(` No owner: ${payload.packagesWithoutOwner}`);
|
|
11844
|
+
console.log(` Deprecated: ${payload.deprecatedPackages}`);
|
|
11845
|
+
|
|
11846
|
+
if (payload.mostRecentlyPublished) {
|
|
11847
|
+
const mr = payload.mostRecentlyPublished;
|
|
11848
|
+
console.log(` Last publish: ${mr.package}@${mr.version} (${mr.publishedAt})`);
|
|
11849
|
+
} else {
|
|
11850
|
+
console.log(' Last publish: (none)');
|
|
11851
|
+
}
|
|
11852
|
+
}
|
|
11853
|
+
|
|
11854
|
+
// ── Scene Lock ────────────────────────────────────────────────────────────
|
|
11855
|
+
|
|
11856
|
+
function normalizeSceneLockOptions(options = {}) {
|
|
11857
|
+
return {
|
|
11858
|
+
action: options.action ? String(options.action).trim() : undefined,
|
|
11859
|
+
name: options.name ? String(options.name).trim() : undefined,
|
|
11860
|
+
version: options.version ? String(options.version).trim() : undefined,
|
|
11861
|
+
registry: options.registry ? String(options.registry).trim() : '.kiro/registry',
|
|
11862
|
+
json: options.json === true
|
|
11863
|
+
};
|
|
11864
|
+
}
|
|
11865
|
+
|
|
11866
|
+
function validateSceneLockOptions(options) {
|
|
11867
|
+
if (!options.action) return '--action is required';
|
|
11868
|
+
const validActions = ['set', 'rm', 'ls'];
|
|
11869
|
+
if (!validActions.includes(options.action)) return `invalid action "${options.action}"`;
|
|
11870
|
+
|
|
11871
|
+
if (options.action === 'set') {
|
|
11872
|
+
if (!options.name) return '--name is required';
|
|
11873
|
+
if (!options.version) return '--version is required';
|
|
11874
|
+
}
|
|
11875
|
+
if (options.action === 'rm') {
|
|
11876
|
+
if (!options.name) return '--name is required';
|
|
11877
|
+
if (!options.version) return '--version is required';
|
|
11878
|
+
}
|
|
11879
|
+
if (options.action === 'ls') {
|
|
11880
|
+
if (!options.name) return '--name is required';
|
|
11881
|
+
}
|
|
11882
|
+
return null;
|
|
11883
|
+
}
|
|
11884
|
+
|
|
11885
|
+
async function runSceneLockCommand(rawOptions = {}, dependencies = {}) {
|
|
11886
|
+
const projectRoot = dependencies.projectRoot || process.cwd();
|
|
11887
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
11888
|
+
|
|
11889
|
+
const options = normalizeSceneLockOptions(rawOptions);
|
|
11890
|
+
const validationError = validateSceneLockOptions(options);
|
|
11891
|
+
|
|
11892
|
+
if (validationError) {
|
|
11893
|
+
console.error(chalk.red(`Scene lock failed: ${validationError}`));
|
|
11894
|
+
process.exitCode = 1;
|
|
11895
|
+
return null;
|
|
11896
|
+
}
|
|
11897
|
+
|
|
11898
|
+
try {
|
|
11899
|
+
const registryRoot = path.isAbsolute(options.registry)
|
|
11900
|
+
? options.registry
|
|
11901
|
+
: path.join(projectRoot, options.registry);
|
|
11902
|
+
|
|
11903
|
+
const index = await loadRegistryIndex(registryRoot, fileSystem);
|
|
11904
|
+
const packages = index.packages || {};
|
|
11905
|
+
let payload;
|
|
11906
|
+
|
|
11907
|
+
if (options.action === 'set') {
|
|
11908
|
+
if (!packages[options.name]) {
|
|
11909
|
+
throw new Error(`package "${options.name}" not found in registry`);
|
|
11910
|
+
}
|
|
11911
|
+
const pkg = packages[options.name];
|
|
11912
|
+
if (!pkg.versions || !pkg.versions[options.version]) {
|
|
11913
|
+
throw new Error(`version "${options.version}" not found for package "${options.name}"`);
|
|
11914
|
+
}
|
|
11915
|
+
const versionEntry = pkg.versions[options.version];
|
|
11916
|
+
if (versionEntry.locked === true) {
|
|
11917
|
+
throw new Error(`version "${options.version}" of package "${options.name}" is already locked`);
|
|
11918
|
+
}
|
|
11919
|
+
versionEntry.locked = true;
|
|
11920
|
+
await saveRegistryIndex(registryRoot, index, fileSystem);
|
|
11921
|
+
payload = {
|
|
11922
|
+
success: true,
|
|
11923
|
+
action: 'set',
|
|
11924
|
+
package: options.name,
|
|
11925
|
+
version: options.version,
|
|
11926
|
+
registry: options.registry
|
|
11927
|
+
};
|
|
11928
|
+
} else if (options.action === 'rm') {
|
|
11929
|
+
if (!packages[options.name]) {
|
|
11930
|
+
throw new Error(`package "${options.name}" not found in registry`);
|
|
11931
|
+
}
|
|
11932
|
+
const pkg = packages[options.name];
|
|
11933
|
+
if (!pkg.versions || !pkg.versions[options.version]) {
|
|
11934
|
+
throw new Error(`version "${options.version}" not found for package "${options.name}"`);
|
|
11935
|
+
}
|
|
11936
|
+
const versionEntry = pkg.versions[options.version];
|
|
11937
|
+
if (!versionEntry.locked) {
|
|
11938
|
+
throw new Error(`version "${options.version}" of package "${options.name}" is not locked`);
|
|
11939
|
+
}
|
|
11940
|
+
delete versionEntry.locked;
|
|
11941
|
+
await saveRegistryIndex(registryRoot, index, fileSystem);
|
|
11942
|
+
payload = {
|
|
11943
|
+
success: true,
|
|
11944
|
+
action: 'rm',
|
|
11945
|
+
package: options.name,
|
|
11946
|
+
version: options.version,
|
|
11947
|
+
registry: options.registry
|
|
11948
|
+
};
|
|
11949
|
+
} else if (options.action === 'ls') {
|
|
11950
|
+
if (!packages[options.name]) {
|
|
11951
|
+
throw new Error(`package "${options.name}" not found in registry`);
|
|
11952
|
+
}
|
|
11953
|
+
const pkg = packages[options.name];
|
|
11954
|
+
const versions = pkg.versions || {};
|
|
11955
|
+
const lockedVersions = Object.keys(versions).filter(v => versions[v].locked === true);
|
|
11956
|
+
payload = {
|
|
11957
|
+
success: true,
|
|
11958
|
+
action: 'ls',
|
|
11959
|
+
package: options.name,
|
|
11960
|
+
lockedVersions,
|
|
11961
|
+
registry: options.registry
|
|
11962
|
+
};
|
|
11963
|
+
}
|
|
11964
|
+
|
|
11965
|
+
printSceneLockSummary(options, payload);
|
|
11966
|
+
return payload;
|
|
11967
|
+
} catch (error) {
|
|
11968
|
+
console.error(chalk.red('Scene lock failed:'), error.message);
|
|
11969
|
+
process.exitCode = 1;
|
|
11970
|
+
return null;
|
|
11971
|
+
}
|
|
11972
|
+
}
|
|
11973
|
+
|
|
11974
|
+
function printSceneLockSummary(options, payload) {
|
|
11975
|
+
if (options.json) {
|
|
11976
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
11977
|
+
return;
|
|
11978
|
+
}
|
|
11979
|
+
|
|
11980
|
+
if (payload.action === 'set') {
|
|
11981
|
+
console.log(chalk.green(`Version "${payload.version}" of package "${payload.package}" is now locked`));
|
|
11982
|
+
} else if (payload.action === 'rm') {
|
|
11983
|
+
console.log(chalk.green(`Version "${payload.version}" of package "${payload.package}" is now unlocked`));
|
|
11984
|
+
} else if (payload.action === 'ls') {
|
|
11985
|
+
if (payload.lockedVersions.length === 0) {
|
|
11986
|
+
console.log(`No locked versions for package "${payload.package}"`);
|
|
11987
|
+
} else {
|
|
11988
|
+
console.log(`Locked versions for package "${payload.package}":`);
|
|
11989
|
+
for (const version of payload.lockedVersions) {
|
|
11990
|
+
console.log(` ${version}`);
|
|
11991
|
+
}
|
|
11992
|
+
}
|
|
11993
|
+
}
|
|
11994
|
+
}
|
|
11995
|
+
|
|
11519
11996
|
module.exports = {
|
|
11520
11997
|
RUN_MODES,
|
|
11521
11998
|
SCAFFOLD_TYPES,
|
|
@@ -11696,5 +12173,17 @@ module.exports = {
|
|
|
11696
12173
|
normalizeSceneOwnerOptions,
|
|
11697
12174
|
validateSceneOwnerOptions,
|
|
11698
12175
|
runSceneOwnerCommand,
|
|
11699
|
-
printSceneOwnerSummary
|
|
12176
|
+
printSceneOwnerSummary,
|
|
12177
|
+
normalizeSceneTagOptions,
|
|
12178
|
+
validateSceneTagOptions,
|
|
12179
|
+
runSceneTagCommand,
|
|
12180
|
+
printSceneTagSummary,
|
|
12181
|
+
normalizeSceneStatsOptions,
|
|
12182
|
+
validateSceneStatsOptions,
|
|
12183
|
+
runSceneStatsCommand,
|
|
12184
|
+
printSceneStatsSummary,
|
|
12185
|
+
normalizeSceneLockOptions,
|
|
12186
|
+
validateSceneLockOptions,
|
|
12187
|
+
runSceneLockCommand,
|
|
12188
|
+
printSceneLockSummary
|
|
11700
12189
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kiro-spec-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.38.0",
|
|
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": {
|