kiro-spec-engine 1.28.0 → 1.29.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 CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.29.0] - 2026-02-10
11
+
12
+ ### Added
13
+ - **Scene Registry Query**: List and search scene packages in local registry
14
+ - `kse scene list` list all packages in registry
15
+ - `--registry <dir>` custom registry directory
16
+ - `--json` structured JSON output
17
+ - `kse scene search --query <term>` search packages by keyword
18
+ - Case-insensitive substring matching on name, description, and group
19
+ - `--registry <dir>` custom registry directory
20
+ - `--json` structured JSON output
21
+ - Shared helpers: `buildRegistryPackageList`, `filterRegistryPackages`
22
+ - Follows normalize → validate → run → print pattern
23
+ - Implements Spec 79-00-scene-registry-query
24
+
10
25
  ## [1.28.0] - 2026-02-10
11
26
 
12
27
  ### Added
@@ -517,6 +517,25 @@ function registerSceneCommands(program) {
517
517
  .action(async (options) => {
518
518
  await runSceneInstallCommand(options);
519
519
  });
520
+
521
+ sceneCmd
522
+ .command('list')
523
+ .description('List all packages in the local scene registry')
524
+ .option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
525
+ .option('--json', 'Print result as JSON')
526
+ .action(async (options) => {
527
+ await runSceneListCommand(options);
528
+ });
529
+
530
+ sceneCmd
531
+ .command('search')
532
+ .description('Search packages in the local scene registry')
533
+ .requiredOption('-q, --query <term>', 'Search term (substring match on name, description, group)')
534
+ .option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
535
+ .option('--json', 'Print result as JSON')
536
+ .action(async (options) => {
537
+ await runSceneSearchCommand(options);
538
+ });
520
539
  }
521
540
 
522
541
  function normalizeSourceOptions(options = {}) {
@@ -10062,6 +10081,157 @@ async function runSceneInstallCommand(rawOptions = {}, dependencies = {}) {
10062
10081
  }
10063
10082
  }
10064
10083
 
10084
+ // --- Scene Registry Query Helpers ---
10085
+
10086
+ function buildRegistryPackageList(registryPackages) {
10087
+ return Object.values(registryPackages || {})
10088
+ .map(pkg => ({
10089
+ name: pkg.name || '',
10090
+ group: pkg.group || '',
10091
+ description: pkg.description || '',
10092
+ latest: pkg.latest || '',
10093
+ version_count: Object.keys(pkg.versions || {}).length
10094
+ }))
10095
+ .sort((a, b) => a.name.localeCompare(b.name));
10096
+ }
10097
+
10098
+ function filterRegistryPackages(packageList, query) {
10099
+ if (!query) return packageList;
10100
+ const lowerQuery = query.toLowerCase();
10101
+ return packageList.filter(pkg =>
10102
+ pkg.name.toLowerCase().includes(lowerQuery) ||
10103
+ pkg.description.toLowerCase().includes(lowerQuery) ||
10104
+ pkg.group.toLowerCase().includes(lowerQuery)
10105
+ );
10106
+ }
10107
+
10108
+ // --- Scene List Command ---
10109
+
10110
+ function normalizeSceneListOptions(options = {}) {
10111
+ return {
10112
+ registry: options.registry ? String(options.registry).trim() : '.kiro/registry',
10113
+ json: options.json === true
10114
+ };
10115
+ }
10116
+
10117
+ function validateSceneListOptions(options) {
10118
+ return null;
10119
+ }
10120
+
10121
+ async function runSceneListCommand(rawOptions = {}, dependencies = {}) {
10122
+ const projectRoot = dependencies.projectRoot || process.cwd();
10123
+ const fileSystem = dependencies.fileSystem || fs;
10124
+
10125
+ const options = normalizeSceneListOptions(rawOptions);
10126
+ const validationError = validateSceneListOptions(options);
10127
+
10128
+ if (validationError) {
10129
+ console.error(chalk.red(`Scene list failed: ${validationError}`));
10130
+ process.exitCode = 1;
10131
+ return null;
10132
+ }
10133
+
10134
+ try {
10135
+ const registryRoot = path.isAbsolute(options.registry)
10136
+ ? options.registry
10137
+ : path.join(projectRoot, options.registry);
10138
+
10139
+ const index = await loadRegistryIndex(registryRoot, fileSystem);
10140
+ const packages = buildRegistryPackageList(index.packages);
10141
+ const payload = { packages, total: packages.length };
10142
+
10143
+ printSceneListSummary(options, payload, projectRoot);
10144
+ return payload;
10145
+ } catch (error) {
10146
+ console.error(chalk.red('Scene list failed:'), error.message);
10147
+ process.exitCode = 1;
10148
+ return null;
10149
+ }
10150
+ }
10151
+
10152
+ function printSceneListSummary(options, payload, projectRoot = process.cwd()) {
10153
+ if (options.json) {
10154
+ console.log(JSON.stringify(payload, null, 2));
10155
+ return;
10156
+ }
10157
+
10158
+ if (payload.total === 0) {
10159
+ console.log('No packages found');
10160
+ return;
10161
+ }
10162
+
10163
+ console.log(chalk.blue(`Scene Registry (${payload.total} package${payload.total !== 1 ? 's' : ''})`));
10164
+ for (const pkg of payload.packages) {
10165
+ console.log(` ${pkg.name.padEnd(20)} ${pkg.latest.padEnd(10)} ${String(pkg.version_count).padEnd(10)} ${pkg.description}`);
10166
+ }
10167
+ }
10168
+
10169
+ function normalizeSceneSearchOptions(options = {}) {
10170
+ return {
10171
+ query: options.query ? String(options.query).trim() : '',
10172
+ registry: options.registry ? String(options.registry).trim() : '.kiro/registry',
10173
+ json: options.json === true
10174
+ };
10175
+ }
10176
+
10177
+ function validateSceneSearchOptions(options) {
10178
+ return null; // Empty query is valid (returns all)
10179
+ }
10180
+
10181
+ async function runSceneSearchCommand(rawOptions = {}, dependencies = {}) {
10182
+ const projectRoot = dependencies.projectRoot || process.cwd();
10183
+ const fileSystem = dependencies.fileSystem || fs;
10184
+
10185
+ const options = normalizeSceneSearchOptions(rawOptions);
10186
+ const validationError = validateSceneSearchOptions(options);
10187
+
10188
+ if (validationError) {
10189
+ console.error(chalk.red(`Scene search failed: ${validationError}`));
10190
+ process.exitCode = 1;
10191
+ return null;
10192
+ }
10193
+
10194
+ try {
10195
+ const registryRoot = path.isAbsolute(options.registry)
10196
+ ? options.registry
10197
+ : path.join(projectRoot, options.registry);
10198
+
10199
+ const index = await loadRegistryIndex(registryRoot, fileSystem);
10200
+ const allPackages = buildRegistryPackageList(index.packages);
10201
+ const packages = filterRegistryPackages(allPackages, options.query);
10202
+ const payload = { query: options.query, packages, total: packages.length };
10203
+
10204
+ printSceneSearchSummary(options, payload, projectRoot);
10205
+ return payload;
10206
+ } catch (error) {
10207
+ console.error(chalk.red('Scene search failed:'), error.message);
10208
+ process.exitCode = 1;
10209
+ return null;
10210
+ }
10211
+ }
10212
+
10213
+ function printSceneSearchSummary(options, payload, projectRoot = process.cwd()) {
10214
+ if (options.json) {
10215
+ console.log(JSON.stringify(payload, null, 2));
10216
+ return;
10217
+ }
10218
+
10219
+ if (payload.total === 0) {
10220
+ if (payload.query) {
10221
+ console.log(`No packages matching '${payload.query}'`);
10222
+ } else {
10223
+ console.log('No packages found');
10224
+ }
10225
+ return;
10226
+ }
10227
+
10228
+ const matchLabel = payload.total === 1 ? 'match' : 'matches';
10229
+ console.log(chalk.blue(`Scene Search: "${payload.query}" (${payload.total} ${matchLabel})`));
10230
+ for (const pkg of payload.packages) {
10231
+ console.log(` ${pkg.name.padEnd(20)} ${pkg.latest.padEnd(10)} ${String(pkg.version_count).padEnd(10)} ${pkg.description}`);
10232
+ }
10233
+ }
10234
+
10065
10235
  function printSceneUnpublishSummary(options, payload, projectRoot = process.cwd()) {
10066
10236
  if (options.json) {
10067
10237
  console.log(JSON.stringify(payload, null, 2));
@@ -10290,5 +10460,15 @@ module.exports = {
10290
10460
  normalizeSceneInstallOptions,
10291
10461
  printSceneInstallSummary,
10292
10462
  runSceneInstallCommand,
10293
- validateSceneInstallOptions
10463
+ validateSceneInstallOptions,
10464
+ buildRegistryPackageList,
10465
+ filterRegistryPackages,
10466
+ normalizeSceneListOptions,
10467
+ validateSceneListOptions,
10468
+ runSceneListCommand,
10469
+ printSceneListSummary,
10470
+ normalizeSceneSearchOptions,
10471
+ validateSceneSearchOptions,
10472
+ runSceneSearchCommand,
10473
+ printSceneSearchSummary
10294
10474
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiro-spec-engine",
3
- "version": "1.28.0",
3
+ "version": "1.29.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": {