@fission-ai/openspec 1.14.0 → 1.14.1

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.
Files changed (44) hide show
  1. package/dist/cli/commands/config.d.ts +8 -0
  2. package/dist/cli/commands/config.js +88 -0
  3. package/dist/cli/commands/context.d.ts +3 -0
  4. package/dist/cli/commands/context.js +20 -0
  5. package/dist/cli/commands/doctor.d.ts +3 -0
  6. package/dist/cli/commands/doctor.js +18 -0
  7. package/dist/cli/commands/schema.d.ts +6 -0
  8. package/dist/cli/commands/schema.js +57 -0
  9. package/dist/cli/commands/spec.d.ts +3 -0
  10. package/dist/cli/commands/spec.js +42 -0
  11. package/dist/cli/commands/store.d.ts +3 -0
  12. package/dist/cli/commands/store.js +130 -0
  13. package/dist/cli/commands/workset.d.ts +3 -0
  14. package/dist/cli/commands/workset.js +96 -0
  15. package/dist/cli/index.js +68 -43
  16. package/dist/commands/config.d.ts +16 -7
  17. package/dist/commands/config.js +342 -395
  18. package/dist/commands/context.d.ts +8 -2
  19. package/dist/commands/context.js +22 -37
  20. package/dist/commands/doctor.d.ts +6 -7
  21. package/dist/commands/doctor.js +14 -27
  22. package/dist/commands/schema.d.ts +19 -5
  23. package/dist/commands/schema.js +662 -702
  24. package/dist/commands/spec.d.ts +13 -4
  25. package/dist/commands/spec.js +91 -120
  26. package/dist/commands/store.d.ts +27 -2
  27. package/dist/commands/store.js +1 -120
  28. package/dist/commands/workflow/default-schema.d.ts +2 -0
  29. package/dist/commands/workflow/default-schema.js +4 -0
  30. package/dist/commands/workflow/instructions.js +1 -1
  31. package/dist/commands/workflow/shared.d.ts +1 -1
  32. package/dist/commands/workflow/shared.js +1 -1
  33. package/dist/commands/workset.d.ts +22 -2
  34. package/dist/commands/workset.js +1 -88
  35. package/dist/core/archive.js +13 -5
  36. package/dist/core/completion-tip.js +3 -1
  37. package/dist/core/config-schema.d.ts +1 -1
  38. package/dist/core/templates/workflows/apply-change.js +4 -3
  39. package/dist/core/templates/workflows/continue-change.js +17 -0
  40. package/dist/core/templates/workflows/verify-change.js +12 -12
  41. package/dist/core/validation/validator.js +12 -1
  42. package/dist/core/view.js +2 -26
  43. package/package.json +1 -1
  44. package/schemas/spec-driven/schema.yaml +28 -4
package/dist/cli/index.js CHANGED
@@ -1,34 +1,22 @@
1
- import { asStatus } from '../commands/shared-output.js';
2
1
  import { Command, Option } from 'commander';
3
2
  import { createRequire } from 'module';
4
- import ora from 'ora';
5
3
  import path from 'path';
6
4
  import { fileURLToPath } from 'url';
7
5
  import { existsSync, promises as fs } from 'fs';
8
6
  import { AI_TOOLS, TOOL_ID_ALIASES } from '../core/config.js';
9
- import { UpdateCommand } from '../core/update.js';
10
- import { getAvailableCliUpdate, displayCliUpdateNote, shouldOfferUpgrade, getInstallDir, offerCliUpgrade, rerunUpdateWithUpgradedCli, displayUpgradeCommand, isSourceCheckout, checkForCliUpdate, getCliInstallInfo, getCliUpdateCommand, canSelfUpgrade, buildVersionReportLines, } from '../core/version-check.js';
11
- import { ListCommand } from '../core/list.js';
12
- import { ArchiveCommand } from '../core/archive.js';
13
- import { ViewCommand } from '../core/view.js';
14
- import { resolveRootForCommand, toRootOutput } from '../core/root-selection.js';
15
- import { registerSpecCommand } from '../commands/spec.js';
16
- import { ChangeCommand } from '../commands/change.js';
17
- import { ValidateCommand } from '../commands/validate.js';
18
- import { ShowCommand } from '../commands/show.js';
19
- import { CompletionCommand } from '../commands/completion.js';
20
- import { FeedbackCommand } from '../commands/feedback.js';
21
- import { registerConfigCommand } from '../commands/config.js';
22
- import { registerSchemaCommand } from '../commands/schema.js';
23
- import { registerStoreCommand } from '../commands/store.js';
24
- import { registerDoctorCommand } from '../commands/doctor.js';
25
- import { registerContextCommand } from '../commands/context.js';
26
- import { registerWorksetCommand } from '../commands/workset.js';
27
- import { statusCommand, BATCH_STATUS_FAILURE_PAYLOAD, instructionsCommand, applyInstructionsCommand, archiveInstructionsCommand, templatesCommand, schemasCommand, newChangeCommand, DEFAULT_SCHEMA, } from '../commands/workflow/index.js';
28
- import { maybeShowTelemetryNotice, trackCommand, shutdown } from '../telemetry/index.js';
29
- import { maybeShowCompletionTip } from '../core/completion-tip.js';
7
+ import { registerSpecCommand } from './commands/spec.js';
8
+ import { registerConfigCommand } from './commands/config.js';
9
+ import { registerSchemaCommand } from './commands/schema.js';
10
+ import { registerStoreCommand } from './commands/store.js';
11
+ import { registerDoctorCommand } from './commands/doctor.js';
12
+ import { registerContextCommand } from './commands/context.js';
13
+ import { registerWorksetCommand } from './commands/workset.js';
14
+ import { DEFAULT_SCHEMA } from '../commands/workflow/default-schema.js';
30
15
  import { COMMON_FLAGS } from '../core/completions/shared-flags.js';
31
- import { isInteractive } from '../utils/interactive.js';
16
+ // Startup cost: every command's implementation, and the packages it uses,
17
+ // loads with `await import()` inside its action. This module and
18
+ // ./commands/ only define commands (names, options, help), so `--version`,
19
+ // `--help`, and each command load no other command's implementation.
32
20
  const STORE_OPTION_DESCRIPTION = COMMON_FLAGS.store.description;
33
21
  // Deliberate rejection path: --store-path stays registered (hidden) so the
34
22
  // resolver can explain that registering the path is the supported route,
@@ -37,14 +25,16 @@ const STORE_OPTION_DESCRIPTION = COMMON_FLAGS.store.description;
37
25
  function hiddenStorePathOption() {
38
26
  return new Option('--store-path <path>', 'Not supported; register the path with "openspec store register <path>" and use --store <id>').hideHelp();
39
27
  }
40
- function failWithError(error, json) {
28
+ async function failWithError(error, json) {
41
29
  // The agent contract: every --json failure leaves exactly one JSON
42
30
  // document on stdout (the command's null-shape plus a status array).
43
31
  if (json?.enabled) {
32
+ const { asStatus } = await import('../commands/shared-output.js');
44
33
  console.log(JSON.stringify({ ...(json.payload ?? {}), status: [asStatus(error, json.fallbackCode ?? 'command_error')] }, null, 2));
45
34
  process.exitCode = 1;
46
35
  return;
47
36
  }
37
+ const { default: ora } = await import('ora');
48
38
  ora().fail(`Error: ${error.message}`);
49
39
  // Resolution and store errors carry a pasteable fix - never drop it.
50
40
  const fix = error.diagnostic?.fix;
@@ -124,6 +114,7 @@ program
124
114
  .option('--json', 'Output as JSON')
125
115
  .option('--check', 'Check the registry for a newer version')
126
116
  .action(async (options) => {
117
+ const { getCliInstallInfo, checkForCliUpdate, getCliUpdateCommand, canSelfUpgrade, buildVersionReportLines, } = await import('../core/version-check.js');
127
118
  const install = getCliInstallInfo();
128
119
  const update = options.check ? await checkForCliUpdate() : undefined;
129
120
  const command = update?.status === 'available' ? getCliUpdateCommand(install) : null;
@@ -161,6 +152,7 @@ program.hook('preAction', async (thisCommand, actionCommand) => {
161
152
  // Show first-run telemetry notice (if not seen). It's written to stderr, so it
162
153
  // never pollutes stdout — but --json runs still defer it (see isJsonRun) so the
163
154
  // very first invocation stays free of any incidental output on either stream.
155
+ const { maybeShowTelemetryNotice, trackCommand } = await import('../telemetry/index.js');
164
156
  await maybeShowTelemetryNotice({ silent: isJsonRun(actionCommand) });
165
157
  // Track command execution (use actionCommand to get the actual subcommand)
166
158
  const commandPath = getCommandPath(actionCommand);
@@ -175,6 +167,7 @@ program.hook('postAction', async (_thisCommand, actionCommand) => {
175
167
  // `openspec completion ...`, and a stderr that is not a terminal (agents and
176
168
  // pipes would otherwise silently burn the user's one-shot tip).
177
169
  try {
170
+ const { maybeShowCompletionTip } = await import('../core/completion-tip.js');
178
171
  await maybeShowCompletionTip({
179
172
  silent: shouldDeferCompletionTip(actionCommand, Boolean(process.stderr.isTTY)),
180
173
  });
@@ -182,6 +175,7 @@ program.hook('postAction', async (_thisCommand, actionCommand) => {
182
175
  finally {
183
176
  // The flush runs even if the hint throws: parse() is synchronous, so a
184
177
  // rejection here has no catch anywhere above it.
178
+ const { shutdown } = await import('../telemetry/index.js');
185
179
  await shutdown();
186
180
  }
187
181
  });
@@ -236,7 +230,7 @@ program
236
230
  await initCommand.execute(targetPath);
237
231
  }
238
232
  catch (error) {
239
- failWithError(error);
233
+ await failWithError(error);
240
234
  process.exit(1);
241
235
  }
242
236
  });
@@ -257,7 +251,7 @@ program
257
251
  await initCommand.execute('.');
258
252
  }
259
253
  catch (error) {
260
- failWithError(error);
254
+ await failWithError(error);
261
255
  process.exit(1);
262
256
  }
263
257
  });
@@ -267,6 +261,11 @@ program
267
261
  .option('--force', 'Force update even when tools are up to date')
268
262
  .action(async (targetPath = '.', options) => {
269
263
  try {
264
+ const [{ getInstallDir, isSourceCheckout, getAvailableCliUpdate, shouldOfferUpgrade, displayCliUpdateNote, offerCliUpgrade, rerunUpdateWithUpgradedCli, displayUpgradeCommand, }, { isInteractive }, { UpdateCommand },] = await Promise.all([
265
+ import('../core/version-check.js'),
266
+ import('../utils/interactive.js'),
267
+ import('../core/update.js'),
268
+ ]);
270
269
  const installDir = getInstallDir();
271
270
  // Running from a clone: the version is whatever the branch says, so any
272
271
  // upgrade advice would be noise. Decided before the request, so a
@@ -318,7 +317,7 @@ program
318
317
  }
319
318
  }
320
319
  catch (error) {
321
- failWithError(error);
320
+ await failWithError(error);
322
321
  process.exit(1);
323
322
  }
324
323
  });
@@ -338,6 +337,10 @@ program
338
337
  if (options?.specs && (options.archived || options.all)) {
339
338
  throw new Error('--archived and --all can only be used when listing changes.');
340
339
  }
340
+ const [{ resolveRootForCommand, toRootOutput }, { ListCommand }] = await Promise.all([
341
+ import('../core/root-selection.js'),
342
+ import('../core/list.js'),
343
+ ]);
341
344
  const root = await resolveRootForCommand(options ?? {}, {
342
345
  json: options?.json,
343
346
  failurePayload: options?.specs ? { specs: [], root: null } : { changes: [], root: null },
@@ -360,7 +363,7 @@ program
360
363
  });
361
364
  }
362
365
  catch (error) {
363
- failWithError(error, {
366
+ await failWithError(error, {
364
367
  enabled: options?.json,
365
368
  payload: options?.specs ? { specs: [], root: null } : { changes: [], root: null },
366
369
  fallbackCode: 'list_error',
@@ -378,6 +381,10 @@ program
378
381
  // Implicit cwd fallback stays enabled so `view` keeps accepting the same
379
382
  // directories as `list`/`status` — notably pre-config.yaml `openspec/`
380
383
  // dirs. ViewCommand still reports a missing openspec/ directory itself.
384
+ const [{ resolveRootForCommand }, { ViewCommand }] = await Promise.all([
385
+ import('../core/root-selection.js'),
386
+ import('../core/view.js'),
387
+ ]);
381
388
  const root = await resolveRootForCommand(options ?? {});
382
389
  if (!root) {
383
390
  return;
@@ -386,7 +393,7 @@ program
386
393
  await viewCommand.execute(root.path);
387
394
  }
388
395
  catch (error) {
389
- failWithError(error);
396
+ await failWithError(error);
390
397
  process.exit(1);
391
398
  }
392
399
  });
@@ -408,6 +415,7 @@ changeCmd
408
415
  .option('--no-interactive', 'Disable interactive prompts')
409
416
  .action(async (changeName, options) => {
410
417
  try {
418
+ const { ChangeCommand } = await import('../commands/change.js');
411
419
  const changeCommand = new ChangeCommand();
412
420
  await changeCommand.show(changeName, options);
413
421
  }
@@ -424,6 +432,7 @@ changeCmd
424
432
  .action(async (options) => {
425
433
  try {
426
434
  console.error('Warning: "openspec change list" is deprecated. Use "openspec list".');
435
+ const { ChangeCommand } = await import('../commands/change.js');
427
436
  const changeCommand = new ChangeCommand();
428
437
  await changeCommand.list(options);
429
438
  }
@@ -440,6 +449,7 @@ changeCmd
440
449
  .option('--no-interactive', 'Disable interactive prompts')
441
450
  .action(async (changeName, options) => {
442
451
  try {
452
+ const { ChangeCommand } = await import('../commands/change.js');
443
453
  const changeCommand = new ChangeCommand();
444
454
  // validate() already sets process.exitCode, and Node honours it at
445
455
  // natural exit. Calling process.exit() here would skip commander's
@@ -464,11 +474,12 @@ program
464
474
  .addOption(hiddenStorePathOption())
465
475
  .action(async (changeName, options) => {
466
476
  try {
477
+ const { ArchiveCommand } = await import('../core/archive.js');
467
478
  const archiveCommand = new ArchiveCommand();
468
479
  await archiveCommand.execute(changeName, options);
469
480
  }
470
481
  catch (error) {
471
- failWithError(error);
482
+ await failWithError(error);
472
483
  process.exit(1);
473
484
  }
474
485
  });
@@ -497,11 +508,12 @@ program
497
508
  .addOption(hiddenStorePathOption())
498
509
  .action(async (itemName, options) => {
499
510
  try {
511
+ const { ValidateCommand } = await import('../commands/validate.js');
500
512
  const validateCommand = new ValidateCommand();
501
513
  await validateCommand.execute(itemName, options);
502
514
  }
503
515
  catch (error) {
504
- failWithError(error, { enabled: options?.json, fallbackCode: 'validate_error' });
516
+ await failWithError(error, { enabled: options?.json, fallbackCode: 'validate_error' });
505
517
  process.exit(1);
506
518
  }
507
519
  });
@@ -528,11 +540,12 @@ program
528
540
  .allowUnknownOption(true)
529
541
  .action(async (itemName, options) => {
530
542
  try {
543
+ const { ShowCommand } = await import('../commands/show.js');
531
544
  const showCommand = new ShowCommand();
532
545
  await showCommand.execute(itemName, options ?? {});
533
546
  }
534
547
  catch (error) {
535
- failWithError(error, { enabled: options?.json, fallbackCode: 'show_error' });
548
+ await failWithError(error, { enabled: options?.json, fallbackCode: 'show_error' });
536
549
  process.exit(1);
537
550
  }
538
551
  });
@@ -543,11 +556,12 @@ program
543
556
  .option('--body <text>', 'Detailed description for the feedback')
544
557
  .action(async (message, options) => {
545
558
  try {
559
+ const { FeedbackCommand } = await import('../commands/feedback.js');
546
560
  const feedbackCommand = new FeedbackCommand();
547
561
  await feedbackCommand.execute(message, options);
548
562
  }
549
563
  catch (error) {
550
- failWithError(error);
564
+ await failWithError(error);
551
565
  process.exit(1);
552
566
  }
553
567
  });
@@ -560,11 +574,12 @@ completionCmd
560
574
  .description('Generate completion script for a shell (outputs to stdout)')
561
575
  .action(async (shell) => {
562
576
  try {
577
+ const { CompletionCommand } = await import('../commands/completion.js');
563
578
  const completionCommand = new CompletionCommand();
564
579
  await completionCommand.generate({ shell });
565
580
  }
566
581
  catch (error) {
567
- failWithError(error);
582
+ await failWithError(error);
568
583
  process.exit(1);
569
584
  }
570
585
  });
@@ -574,11 +589,12 @@ completionCmd
574
589
  .option('--verbose', 'Show detailed installation output')
575
590
  .action(async (shell, options) => {
576
591
  try {
592
+ const { CompletionCommand } = await import('../commands/completion.js');
577
593
  const completionCommand = new CompletionCommand();
578
594
  await completionCommand.install({ shell, verbose: options?.verbose });
579
595
  }
580
596
  catch (error) {
581
- failWithError(error);
597
+ await failWithError(error);
582
598
  process.exit(1);
583
599
  }
584
600
  });
@@ -588,11 +604,12 @@ completionCmd
588
604
  .option('-y, --yes', 'Skip confirmation prompts')
589
605
  .action(async (shell, options) => {
590
606
  try {
607
+ const { CompletionCommand } = await import('../commands/completion.js');
591
608
  const completionCommand = new CompletionCommand();
592
609
  await completionCommand.uninstall({ shell, yes: options?.yes });
593
610
  }
594
611
  catch (error) {
595
- failWithError(error);
612
+ await failWithError(error);
596
613
  process.exit(1);
597
614
  }
598
615
  });
@@ -602,6 +619,7 @@ program
602
619
  .description('Output completion data in machine-readable format (internal use)')
603
620
  .action(async (type) => {
604
621
  try {
622
+ const { CompletionCommand } = await import('../commands/completion.js');
605
623
  const completionCommand = new CompletionCommand();
606
624
  await completionCommand.complete({ type });
607
625
  }
@@ -625,14 +643,17 @@ program
625
643
  .addOption(hiddenStorePathOption())
626
644
  .action(async (options) => {
627
645
  try {
646
+ const { statusCommand } = await import('../commands/workflow/status.js');
628
647
  await statusCommand(options);
629
648
  }
630
649
  catch (error) {
631
- failWithError(error, {
650
+ await failWithError(error, {
632
651
  enabled: options.json,
633
652
  // The batch null-shape; the single-change failure shape is
634
653
  // pre-existing contract and stays payload-free.
635
- payload: options.all ? BATCH_STATUS_FAILURE_PAYLOAD : undefined,
654
+ payload: options.all
655
+ ? (await import('../commands/workflow/status.js')).BATCH_STATUS_FAILURE_PAYLOAD
656
+ : undefined,
636
657
  fallbackCode: 'change_error',
637
658
  });
638
659
  process.exit(1);
@@ -649,6 +670,7 @@ program
649
670
  .addOption(hiddenStorePathOption())
650
671
  .action(async (artifactId, options) => {
651
672
  try {
673
+ const { applyInstructionsCommand, archiveInstructionsCommand, instructionsCommand } = await import('../commands/workflow/instructions.js');
652
674
  // Workflow instruction surfaces are reserved command branches, not artifacts.
653
675
  if (artifactId === 'apply') {
654
676
  await applyInstructionsCommand(options);
@@ -661,7 +683,7 @@ program
661
683
  }
662
684
  }
663
685
  catch (error) {
664
- failWithError(error, { enabled: options.json, fallbackCode: 'change_error' });
686
+ await failWithError(error, { enabled: options.json, fallbackCode: 'change_error' });
665
687
  process.exit(1);
666
688
  }
667
689
  });
@@ -673,10 +695,11 @@ program
673
695
  .option('--json', 'Output as JSON mapping artifact IDs to template paths')
674
696
  .action(async (options) => {
675
697
  try {
698
+ const { templatesCommand } = await import('../commands/workflow/templates.js');
676
699
  await templatesCommand(options);
677
700
  }
678
701
  catch (error) {
679
- failWithError(error);
702
+ await failWithError(error);
680
703
  process.exit(1);
681
704
  }
682
705
  });
@@ -689,10 +712,11 @@ program
689
712
  .addOption(hiddenStorePathOption())
690
713
  .action(async (options) => {
691
714
  try {
715
+ const { schemasCommand } = await import('../commands/workflow/schemas.js');
692
716
  await schemasCommand(options);
693
717
  }
694
718
  catch (error) {
695
- failWithError(error, {
719
+ await failWithError(error, {
696
720
  enabled: options.json,
697
721
  payload: { schemas: [], root: null },
698
722
  fallbackCode: 'schemas_error',
@@ -717,10 +741,11 @@ newCmd
717
741
  .addOption(new Option('--areas <names>', 'No longer supported').hideHelp())
718
742
  .action(async (name, options) => {
719
743
  try {
744
+ const { newChangeCommand } = await import('../commands/workflow/new-change.js');
720
745
  await newChangeCommand(name, options);
721
746
  }
722
747
  catch (error) {
723
- failWithError(error);
748
+ await failWithError(error);
724
749
  process.exit(1);
725
750
  }
726
751
  });
@@ -1,4 +1,3 @@
1
- import { Command } from 'commander';
2
1
  import { GlobalConfig } from '../core/global-config.js';
3
2
  import type { Profile, Delivery } from '../core/global-config.js';
4
3
  /**
@@ -40,11 +39,21 @@ export declare function formatWorkflowSummary(workflows: readonly string[], prof
40
39
  * Build a user-facing diff summary between two profile states.
41
40
  */
42
41
  export declare function diffProfileState(before: ProfileState, after: ProfileState): ProfileStateDiff;
43
- /**
44
- * Register the config command and all its subcommands.
45
- *
46
- * @param program - The Commander program instance
47
- */
48
- export declare function registerConfigCommand(program: Command): void;
42
+ export declare function configPathCommand(): void;
43
+ export declare function configListCommand(options: {
44
+ json?: boolean;
45
+ }): void;
46
+ export declare function configGetCommand(key: string): void;
47
+ export declare function configSetCommand(key: string, value: string, options: {
48
+ string?: boolean;
49
+ allowUnknown?: boolean;
50
+ }): void;
51
+ export declare function configUnsetCommand(key: string): void;
52
+ export declare function configResetCommand(options: {
53
+ all?: boolean;
54
+ yes?: boolean;
55
+ }): Promise<void>;
56
+ export declare function configEditCommand(): Promise<void>;
57
+ export declare function configProfileCommand(preset?: string): Promise<void>;
49
58
  export {};
50
59
  //# sourceMappingURL=config.d.ts.map