@fission-ai/openspec 1.13.2 → 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 (99) 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 +106 -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 +92 -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 +32 -12
  31. package/dist/commands/workflow/shared.d.ts +3 -1
  32. package/dist/commands/workflow/shared.js +1 -1
  33. package/dist/commands/workflow/status.js +15 -2
  34. package/dist/commands/workset.d.ts +22 -2
  35. package/dist/commands/workset.js +1 -88
  36. package/dist/core/archive.js +28 -7
  37. package/dist/core/artifact-graph/instruction-loader.d.ts +7 -0
  38. package/dist/core/artifact-graph/instruction-loader.js +18 -1
  39. package/dist/core/change-metadata/schema.d.ts +3 -0
  40. package/dist/core/change-metadata/schema.js +10 -0
  41. package/dist/core/change-status-policy.d.ts +8 -0
  42. package/dist/core/change-status-policy.js +33 -2
  43. package/dist/core/command-generation/adapters/atomcode.d.ts +24 -0
  44. package/dist/core/command-generation/adapters/atomcode.js +46 -0
  45. package/dist/core/command-generation/adapters/codestudio.d.ts +13 -0
  46. package/dist/core/command-generation/adapters/codestudio.js +27 -0
  47. package/dist/core/command-generation/adapters/easycode.d.ts +17 -0
  48. package/dist/core/command-generation/adapters/easycode.js +28 -0
  49. package/dist/core/command-generation/adapters/gemini.js +1 -35
  50. package/dist/core/command-generation/adapters/gigacode.d.ts +17 -0
  51. package/dist/core/command-generation/adapters/gigacode.js +31 -0
  52. package/dist/core/command-generation/adapters/index.d.ts +4 -0
  53. package/dist/core/command-generation/adapters/index.js +4 -0
  54. package/dist/core/command-generation/registry.js +8 -0
  55. package/dist/core/command-generation/toml.d.ts +19 -0
  56. package/dist/core/command-generation/toml.js +39 -0
  57. package/dist/core/command-surface.js +1 -1
  58. package/dist/core/completion-tip.js +3 -1
  59. package/dist/core/completions/command-registry.js +19 -0
  60. package/dist/core/completions/installers/zsh-installer.js +4 -2
  61. package/dist/core/config-prompts.js +6 -6
  62. package/dist/core/config-schema.d.ts +1 -1
  63. package/dist/core/config.js +11 -1
  64. package/dist/core/init.js +35 -11
  65. package/dist/core/legacy-cleanup.js +11 -7
  66. package/dist/core/list.d.ts +2 -0
  67. package/dist/core/list.js +49 -23
  68. package/dist/core/parsers/markdown-parser.js +5 -0
  69. package/dist/core/parsers/requirement-blocks.d.ts +6 -0
  70. package/dist/core/parsers/requirement-blocks.js +9 -2
  71. package/dist/core/project-config.js +45 -1
  72. package/dist/core/references.js +3 -1
  73. package/dist/core/root-selection.d.ts +6 -0
  74. package/dist/core/root-selection.js +16 -0
  75. package/dist/core/schemas/base.schema.d.ts +3 -0
  76. package/dist/core/schemas/base.schema.js +6 -0
  77. package/dist/core/schemas/change.schema.d.ts +8 -0
  78. package/dist/core/schemas/spec.schema.d.ts +2 -0
  79. package/dist/core/templates/workflows/apply-change.js +9 -5
  80. package/dist/core/templates/workflows/archive-change.js +26 -4
  81. package/dist/core/templates/workflows/bulk-archive-change.js +6 -2
  82. package/dist/core/templates/workflows/continue-change.js +17 -0
  83. package/dist/core/templates/workflows/verify-change.js +12 -12
  84. package/dist/core/validation/constants.d.ts +1 -1
  85. package/dist/core/validation/constants.js +1 -1
  86. package/dist/core/validation/validator.js +21 -2
  87. package/dist/core/version-check.d.ts +23 -1
  88. package/dist/core/version-check.js +112 -26
  89. package/dist/core/view.d.ts +2 -0
  90. package/dist/core/view.js +36 -2
  91. package/dist/utils/change-metadata.d.ts +20 -1
  92. package/dist/utils/change-metadata.js +57 -1
  93. package/dist/utils/command-references.d.ts +1 -1
  94. package/dist/utils/command-references.js +2 -2
  95. package/dist/utils/index.d.ts +1 -1
  96. package/dist/utils/index.js +1 -1
  97. package/package.json +1 -1
  98. package/schemas/spec-driven/schema.yaml +29 -4
  99. package/schemas/spec-driven/templates/proposal.md +6 -3
@@ -1,6 +1,5 @@
1
- import { program } from 'commander';
2
1
  import type { RootOutput } from '../core/root-selection.js';
3
- interface ShowOptions {
2
+ export interface ShowOptions {
4
3
  json?: boolean;
5
4
  requirements?: boolean;
6
5
  scenarios?: boolean;
@@ -14,6 +13,16 @@ export declare class SpecCommand {
14
13
  constructor(rootPath?: string);
15
14
  show(specId?: string, options?: ShowOptions): Promise<void>;
16
15
  }
17
- export declare function registerSpecCommand(rootProgram: typeof program): import("commander").Command;
18
- export {};
16
+ export declare function specShowCommand(specId: string | undefined, options: ShowOptions & {
17
+ noInteractive?: boolean;
18
+ }): Promise<void>;
19
+ export declare function specListCommand(options: {
20
+ json?: boolean;
21
+ long?: boolean;
22
+ }): Promise<void>;
23
+ export declare function specValidateCommand(specId: string | undefined, options: {
24
+ strict?: boolean;
25
+ json?: boolean;
26
+ noInteractive?: boolean;
27
+ }): Promise<void>;
19
28
  //# sourceMappingURL=spec.d.ts.map
@@ -45,6 +45,7 @@ function filterSpec(spec, options) {
45
45
  const filteredRequirements = (requirementIndex !== undefined
46
46
  ? [spec.requirements[requirementIndex]]
47
47
  : spec.requirements).map(req => ({
48
+ name: req.name,
48
49
  text: req.text,
49
50
  scenarios: includeScenarios ? req.scenarios : [],
50
51
  }));
@@ -118,139 +119,110 @@ export class SpecCommand {
118
119
  printSpecTextRaw(this.specsDir, specPath);
119
120
  }
120
121
  }
121
- export function registerSpecCommand(rootProgram) {
122
- const specCommand = rootProgram
123
- .command('spec')
124
- .description('Manage and view OpenSpec specifications');
125
- // Deprecation notice for noun-based commands
126
- specCommand.hook('preAction', () => {
127
- console.error('Warning: The "openspec spec ..." commands are deprecated. Prefer verb-first commands (e.g., "openspec show", "openspec validate --specs").');
128
- });
129
- specCommand
130
- .command('show [spec-id]')
131
- .description('Display a specific specification')
132
- .option('--json', 'Output as JSON')
133
- .option('--requirements', 'JSON only: Show only requirements (exclude scenarios)')
134
- .option('--no-scenarios', 'JSON only: Exclude scenario content')
135
- .option('-r, --requirement <id>', 'JSON only: Show specific requirement by ID (1-based)')
136
- .option('--no-interactive', 'Disable interactive prompts')
137
- .action(async (specId, options) => {
138
- try {
139
- const cmd = new SpecCommand();
140
- await cmd.show(specId, options);
122
+ export async function specShowCommand(specId, options) {
123
+ try {
124
+ const cmd = new SpecCommand();
125
+ await cmd.show(specId, options);
126
+ }
127
+ catch (error) {
128
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
129
+ process.exitCode = 1;
130
+ }
131
+ }
132
+ export async function specListCommand(options) {
133
+ try {
134
+ if (!existsSync(SPECS_DIR)) {
135
+ console.log('No items found');
136
+ return;
141
137
  }
142
- catch (error) {
143
- console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
144
- process.exitCode = 1;
138
+ const discovered = await discoverSpecFiles(SPECS_DIR);
139
+ const specs = discovered
140
+ .map(({ id, specFile }) => {
141
+ try {
142
+ assertSpecPath(SPECS_DIR, specFile);
143
+ const spec = parseSpecFromFile(SPECS_DIR, specFile, id);
144
+ return {
145
+ id,
146
+ title: spec.name,
147
+ requirementCount: spec.requirements.length
148
+ };
149
+ }
150
+ catch {
151
+ return {
152
+ id,
153
+ title: id,
154
+ requirementCount: 0
155
+ };
156
+ }
157
+ })
158
+ .sort((a, b) => a.id.localeCompare(b.id));
159
+ if (options.json) {
160
+ console.log(JSON.stringify(specs, null, 2));
145
161
  }
146
- });
147
- specCommand
148
- .command('list')
149
- .description('List all available specifications')
150
- .option('--json', 'Output as JSON')
151
- .option('--long', 'Show id and title with counts')
152
- .action(async (options) => {
153
- try {
154
- if (!existsSync(SPECS_DIR)) {
162
+ else {
163
+ if (specs.length === 0) {
155
164
  console.log('No items found');
156
165
  return;
157
166
  }
158
- const discovered = await discoverSpecFiles(SPECS_DIR);
159
- const specs = discovered
160
- .map(({ id, specFile }) => {
161
- try {
162
- assertSpecPath(SPECS_DIR, specFile);
163
- const spec = parseSpecFromFile(SPECS_DIR, specFile, id);
164
- return {
165
- id,
166
- title: spec.name,
167
- requirementCount: spec.requirements.length
168
- };
169
- }
170
- catch {
171
- return {
172
- id,
173
- title: id,
174
- requirementCount: 0
175
- };
176
- }
177
- })
178
- .sort((a, b) => a.id.localeCompare(b.id));
179
- if (options.json) {
180
- console.log(JSON.stringify(specs, null, 2));
167
+ if (!options.long) {
168
+ specs.forEach(spec => console.log(spec.id));
169
+ return;
181
170
  }
182
- else {
183
- if (specs.length === 0) {
184
- console.log('No items found');
185
- return;
186
- }
187
- if (!options.long) {
188
- specs.forEach(spec => console.log(spec.id));
189
- return;
190
- }
191
- specs.forEach(spec => {
192
- console.log(`${spec.id}: ${spec.title} [requirements ${spec.requirementCount}]`);
171
+ specs.forEach(spec => {
172
+ console.log(`${spec.id}: ${spec.title} [requirements ${spec.requirementCount}]`);
173
+ });
174
+ }
175
+ }
176
+ catch (error) {
177
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
178
+ process.exitCode = 1;
179
+ }
180
+ }
181
+ export async function specValidateCommand(specId, options) {
182
+ try {
183
+ if (!specId) {
184
+ const canPrompt = isInteractive(options);
185
+ const specIds = await getSpecIds();
186
+ if (canPrompt && specIds.length > 0) {
187
+ const { select } = await import('@inquirer/prompts');
188
+ specId = await select({
189
+ message: 'Select a spec to validate',
190
+ choices: specIds.map(id => ({ name: id, value: id })),
193
191
  });
194
192
  }
193
+ else {
194
+ throw new Error('Missing required argument <spec-id>');
195
+ }
195
196
  }
196
- catch (error) {
197
- console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
198
- process.exitCode = 1;
197
+ const specPath = join(SPECS_DIR, specId, 'spec.md');
198
+ assertSpecPath(SPECS_DIR, specPath);
199
+ if (!existsSync(specPath)) {
200
+ throw new Error(`Spec '${specId}' not found at openspec/specs/${specId}/spec.md`);
199
201
  }
200
- });
201
- specCommand
202
- .command('validate [spec-id]')
203
- .description('Validate a specification structure')
204
- .option('--strict', 'Enable strict validation mode')
205
- .option('--json', 'Output validation report as JSON')
206
- .option('--no-interactive', 'Disable interactive prompts')
207
- .action(async (specId, options) => {
208
- try {
209
- if (!specId) {
210
- const canPrompt = isInteractive(options);
211
- const specIds = await getSpecIds();
212
- if (canPrompt && specIds.length > 0) {
213
- const { select } = await import('@inquirer/prompts');
214
- specId = await select({
215
- message: 'Select a spec to validate',
216
- choices: specIds.map(id => ({ name: id, value: id })),
217
- });
218
- }
219
- else {
220
- throw new Error('Missing required argument <spec-id>');
221
- }
222
- }
223
- const specPath = join(SPECS_DIR, specId, 'spec.md');
224
- assertSpecPath(SPECS_DIR, specPath);
225
- if (!existsSync(specPath)) {
226
- throw new Error(`Spec '${specId}' not found at openspec/specs/${specId}/spec.md`);
227
- }
228
- const validator = new Validator(options.strict);
229
- assertSpecPath(SPECS_DIR, specPath);
230
- const report = await validator.validateSpec(specPath);
231
- if (options.json) {
232
- console.log(JSON.stringify(report, null, 2));
202
+ const validator = new Validator(options.strict);
203
+ assertSpecPath(SPECS_DIR, specPath);
204
+ const report = await validator.validateSpec(specPath);
205
+ if (options.json) {
206
+ console.log(JSON.stringify(report, null, 2));
207
+ }
208
+ else {
209
+ if (report.valid) {
210
+ console.log(`Specification '${specId}' is valid`);
233
211
  }
234
212
  else {
235
- if (report.valid) {
236
- console.log(`Specification '${specId}' is valid`);
237
- }
238
- else {
239
- console.error(`Specification '${specId}' has issues`);
240
- report.issues.forEach(issue => {
241
- const label = issue.level === 'ERROR' ? 'ERROR' : issue.level;
242
- const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ';
243
- console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
244
- });
245
- }
213
+ console.error(`Specification '${specId}' has issues`);
214
+ report.issues.forEach(issue => {
215
+ const label = issue.level === 'ERROR' ? 'ERROR' : issue.level;
216
+ const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ';
217
+ console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
218
+ });
246
219
  }
247
- process.exitCode = report.valid ? 0 : 1;
248
- }
249
- catch (error) {
250
- console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
251
- process.exitCode = 1;
252
220
  }
253
- });
254
- return specCommand;
221
+ process.exitCode = report.valid ? 0 : 1;
222
+ }
223
+ catch (error) {
224
+ console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
225
+ process.exitCode = 1;
226
+ }
255
227
  }
256
228
  //# sourceMappingURL=spec.js.map
@@ -1,3 +1,28 @@
1
- import { Command } from 'commander';
2
- export declare function registerStoreCommand(program: Command): void;
1
+ export interface StoreSetupOptions {
2
+ path?: string;
3
+ initGit?: boolean;
4
+ json?: boolean;
5
+ remote?: string;
6
+ }
7
+ export interface StoreRegisterOptions {
8
+ id?: string;
9
+ yes?: boolean;
10
+ json?: boolean;
11
+ }
12
+ export interface StoreRemoveOptions {
13
+ yes?: boolean;
14
+ json?: boolean;
15
+ }
16
+ export interface StoreJsonOptions {
17
+ json?: boolean;
18
+ }
19
+ export declare class StoreCommand {
20
+ setup(id: string | undefined, options?: StoreSetupOptions): Promise<void>;
21
+ register(inputPath: string | undefined, options?: StoreRegisterOptions): Promise<void>;
22
+ unregister(id: string, options?: StoreJsonOptions): Promise<void>;
23
+ remove(id: string, options?: StoreRemoveOptions): Promise<void>;
24
+ list(options?: StoreJsonOptions): Promise<void>;
25
+ doctor(id: string | undefined, options?: StoreJsonOptions): Promise<void>;
26
+ private handleFailure;
27
+ }
3
28
  //# sourceMappingURL=store.d.ts.map
@@ -1,7 +1,6 @@
1
1
  import * as os from 'node:os';
2
2
  import { asErrorMessage, emitFailure, printJson } from './shared-output.js';
3
3
  import * as path from 'node:path';
4
- import { COMMAND_REGISTRY } from '../core/completions/command-registry.js';
5
4
  import { StoreError, doctorStores, listStores, prepareStoreSetup, prepareStoreCleanup, registerExistingStore, removeStore, resolveSetupGitEnabled, setupPreparedStore, unregisterStore, validateStoreId, } from '../core/store/index.js';
6
5
  import { isInteractive } from '../utils/interactive.js';
7
6
  function toStoreOutput(store) {
@@ -318,7 +317,7 @@ function printDoctorHuman(payload) {
318
317
  }
319
318
  }
320
319
  }
321
- class StoreCommand {
320
+ export class StoreCommand {
322
321
  async setup(id, options = {}) {
323
322
  try {
324
323
  const setupInput = await resolveSetupInput(id, options);
@@ -429,122 +428,4 @@ class StoreCommand {
429
428
  emitFailure(json, payload, error, 'store_error');
430
429
  }
431
430
  }
432
- export function registerStoreCommand(program) {
433
- const storeCommand = new StoreCommand();
434
- // One source for the locked group one-liner: the completions registry
435
- // entry, which shell completion scripts also consume.
436
- const storeGroupDescription = COMMAND_REGISTRY.find((entry) => entry.name === 'store')?.description ??
437
- 'Create and manage stores - standalone OpenSpec repos you register on this machine';
438
- const store = program.command('store').description(storeGroupDescription);
439
- store
440
- .command('setup [id]')
441
- .description('Create and register a local store')
442
- .option('--path <path>', 'Folder where the store should live (for example ~/openspec/<id>)')
443
- .option('--init-git', 'Initialize a Git repository with an initial commit (default)')
444
- .option('--no-init-git', 'Skip every Git action: no init, no initial commit')
445
- .option('--remote <url>', 'Canonical clone source recorded in store.yaml')
446
- .option('--json', 'Output as JSON')
447
- .action(async (id, options) => {
448
- await storeCommand.setup(id, options);
449
- });
450
- store
451
- .command('register [path]')
452
- .description('Register an existing local store')
453
- .option('--id <id>', 'Store id; defaults to metadata or folder name')
454
- .option('--yes', 'Confirm creating store identity metadata for a healthy OpenSpec root')
455
- .option('--json', 'Output as JSON')
456
- .action(async (inputPath, options) => {
457
- await storeCommand.register(inputPath, options);
458
- });
459
- store
460
- .command('unregister <id>')
461
- .description('Forget a local store registration without deleting files')
462
- .option('--json', 'Output as JSON')
463
- .action(async (id, options) => {
464
- await storeCommand.unregister(id, options);
465
- });
466
- store
467
- .command('remove <id>')
468
- .description('Forget a local store registration and delete its local folder')
469
- .option('--yes', 'Confirm local store folder deletion')
470
- .option('--json', 'Output as JSON')
471
- .action(async (id, options) => {
472
- await storeCommand.remove(id, options);
473
- });
474
- store
475
- .command('list')
476
- .alias('ls')
477
- .description('List locally registered stores')
478
- .option('--json', 'Output as JSON')
479
- .action(async (options) => {
480
- await storeCommand.list(options);
481
- });
482
- store
483
- .command('doctor [id]')
484
- .description('Check local store registration and metadata')
485
- .option('--json', 'Output as JSON')
486
- .action(async (id, options) => {
487
- await storeCommand.doctor(id, options);
488
- });
489
- const lifecycleRedirects = new Set(COMMAND_REGISTRY.filter((entry) => entry.flags.some((flag) => flag.name === 'store') ||
490
- (entry.subcommands ?? []).some((subcommand) => subcommand.flags.some((flag) => flag.name === 'store'))).map((entry) => entry.name));
491
- const storeSubcommandsLine = store.commands
492
- .map((subcommand) => {
493
- const aliases = subcommand.aliases();
494
- return aliases.length > 0 ? `${subcommand.name()} (${aliases.join(', ')})` : subcommand.name();
495
- })
496
- .join(', ');
497
- // One group action owns missing AND unknown subcommands. Known
498
- // subcommands dispatch above; everything else — including a bare
499
- // `store --json` with no operand — lands here, so the handler owns the
500
- // entire message and exit path (same text for human and --json). The
501
- // permissive flags route unknown operands/options here instead of
502
- // letting Commander emit a raw error before the action runs. We detect
503
- // `--json` in the residual args rather than declaring a group option,
504
- // which would otherwise shadow each subcommand's own `--json` flag.
505
- store.allowExcessArguments(true);
506
- store.allowUnknownOption(true);
507
- store.action(() => {
508
- const operands = store.args;
509
- // Flag values are indistinguishable from operands without a full
510
- // parse, so the verbatim echo only applies to plain-operand input.
511
- const attempted = operands.filter((operand) => !operand.startsWith('-'));
512
- const hasFlagLikeToken = operands.some((operand) => operand.startsWith('-'));
513
- // The agent contract: --json failures emit one JSON document.
514
- if (operands.includes('--json')) {
515
- const message = attempted.length > 0
516
- ? `Unknown command '${attempted[0]}' for 'openspec store'. Store subcommands: ${storeSubcommandsLine}.`
517
- : `Missing subcommand for 'openspec store'. Store subcommands: ${storeSubcommandsLine}.`;
518
- printJson({
519
- status: [
520
- {
521
- severity: 'error',
522
- code: 'unknown_store_subcommand',
523
- message,
524
- fix: 'Run a store subcommand, or use the lifecycle command with --store <id>.',
525
- },
526
- ],
527
- });
528
- process.exitCode = 1;
529
- return;
530
- }
531
- let example = 'openspec new change <change-id> --store <id>';
532
- if (!hasFlagLikeToken && attempted.length > 0 && lifecycleRedirects.has(attempted[0])) {
533
- if (attempted[0] === 'new') {
534
- const changeId = attempted[1] === 'change' && attempted[2] ? attempted[2] : '<change-id>';
535
- example = `openspec new change ${changeId} --store <id>`;
536
- }
537
- else {
538
- example = `openspec ${attempted.join(' ')} --store <id>`;
539
- }
540
- }
541
- console.error(attempted.length > 0
542
- ? `Error: unknown command '${attempted[0]}' for 'openspec store'.`
543
- : "Error: missing subcommand for 'openspec store'.");
544
- console.error(`Store subcommands manage store registration: ${storeSubcommandsLine}.`);
545
- console.error('To create or work on a change in a store, use the normal command with --store, for example:');
546
- console.error(` ${example}`);
547
- process.exitCode = 1;
548
- });
549
- }
550
431
  //# sourceMappingURL=store.js.map
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_SCHEMA = "spec-driven";
2
+ //# sourceMappingURL=default-schema.d.ts.map
@@ -0,0 +1,4 @@
1
+ // Its own module so the CLI's command definitions can show it in help text
2
+ // without loading the workflow implementation.
3
+ export const DEFAULT_SCHEMA = 'spec-driven';
4
+ //# sourceMappingURL=default-schema.js.map
@@ -103,6 +103,14 @@ export function printInstructionsText(instructions, isBlocked) {
103
103
  ` change="${escapeEnvelopeAttribute(changeName)}"` +
104
104
  ` schema="${escapeEnvelopeAttribute(schemaName)}">`);
105
105
  console.log();
106
+ if (instructions.warnings) {
107
+ for (const warning of instructions.warnings) {
108
+ console.log('<warning>');
109
+ console.log(escapeEnvelopeTags(warning));
110
+ console.log('</warning>');
111
+ console.log();
112
+ }
113
+ }
106
114
  // Artifacts skipped via skip_specs get no creation directive: emitting the
107
115
  // task/template anyway would prompt an agent to write spec files that
108
116
  // validate then rejects as conflicting with the marker.
@@ -215,9 +223,16 @@ export function printInstructionsText(instructions, isBlocked) {
215
223
  // Closing tag
216
224
  console.log('</artifact>');
217
225
  }
218
- // -----------------------------------------------------------------------------
219
- // Apply Instructions Command
220
- // -----------------------------------------------------------------------------
226
+ /** Adds one-based source locations to parsed tasks without changing task parsing. */
227
+ function parseLocatedTasks(content, sourcePath) {
228
+ const tasks = [];
229
+ for (const [index, line] of content.split('\n').entries()) {
230
+ const [task] = parseTaskLines(line);
231
+ if (task)
232
+ tasks.push({ ...task, sourcePath, line: index + 1 });
233
+ }
234
+ return tasks;
235
+ }
221
236
  /**
222
237
  * Turns parsed task lines into the listed task items.
223
238
  *
@@ -238,6 +253,8 @@ function toTaskItems(parsed) {
238
253
  id: `${tasks.length + 1}`,
239
254
  description: task.description,
240
255
  done: task.done,
256
+ sourcePath: task.sourcePath,
257
+ line: task.line,
241
258
  });
242
259
  }
243
260
  return tasks;
@@ -406,7 +423,7 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
406
423
  for (const tracksPath of tracksPaths) {
407
424
  try {
408
425
  const tasksContent = await fs.promises.readFile(tracksPath, 'utf-8');
409
- parsedTasks.push(...parseTaskLines(tasksContent));
426
+ parsedTasks.push(...parseLocatedTasks(tasksContent, tracksPath));
410
427
  }
411
428
  catch (error) {
412
429
  const code = error?.code;
@@ -466,7 +483,7 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
466
483
  remaining === 0 &&
467
484
  total > 0) {
468
485
  state = 'all_done';
469
- instruction = 'All tasks are complete! This change is ready to be archived.\nConsider running tests and reviewing the changes before archiving.';
486
+ instruction = 'All tracked tasks are complete.\nReview or verify the change as appropriate before archiving.';
470
487
  }
471
488
  else if (!tracksFile) {
472
489
  // No tracking file configured in schema - ready to apply
@@ -483,13 +500,16 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
483
500
  .join('\n');
484
501
  instruction += `\nTask completion is not verified because tracking evidence was unavailable:\n${unavailableDetails}`;
485
502
  }
486
- const warnings = await collectApplyWarnings({
487
- state,
488
- schema,
489
- changeDir,
490
- changeName,
491
- skippedArtifacts: context.skippedArtifacts,
492
- });
503
+ const warnings = [
504
+ ...(context.warnings ?? []),
505
+ ...(await collectApplyWarnings({
506
+ state,
507
+ schema,
508
+ changeDir,
509
+ changeName,
510
+ skippedArtifacts: context.skippedArtifacts,
511
+ })),
512
+ ];
493
513
  return {
494
514
  changeName,
495
515
  changeDir,
@@ -16,6 +16,8 @@ export interface TaskItem {
16
16
  id: string;
17
17
  description: string;
18
18
  done: boolean;
19
+ sourcePath: string;
20
+ line: number;
19
21
  }
20
22
  export interface ApplyInstructions {
21
23
  changeName: string;
@@ -58,7 +60,7 @@ export interface ArchiveInstructions {
58
60
  /** Current advisory guidance for archive. */
59
61
  operationGuidance?: string[];
60
62
  }
61
- export declare const DEFAULT_SCHEMA = "spec-driven";
63
+ export { DEFAULT_SCHEMA } from './default-schema.js';
62
64
  export declare function printJson(payload: unknown): void;
63
65
  export declare function statusFromError(error: unknown): ChangeCommandStatus;
64
66
  /**
@@ -13,7 +13,7 @@ import { isRootSelectionError } from '../../core/root-selection.js';
13
13
  // -----------------------------------------------------------------------------
14
14
  // Constants
15
15
  // -----------------------------------------------------------------------------
16
- export const DEFAULT_SCHEMA = 'spec-driven';
16
+ export { DEFAULT_SCHEMA } from './default-schema.js';
17
17
  // -----------------------------------------------------------------------------
18
18
  // Utility Functions
19
19
  // -----------------------------------------------------------------------------
@@ -6,7 +6,7 @@
6
6
  import ora from 'ora';
7
7
  import chalk from 'chalk';
8
8
  import { getChangeDir } from '../../core/planning-home.js';
9
- import { resolveRootForCommand, toPlanningHome, toRootOutput, withStoreFlag, isStoreSelectedRoot, } from '../../core/root-selection.js';
9
+ import { resolveRootForCommand, toPlanningHome, toRootOutput, withStoreFlag, isStoreSelectedRoot, findDeclaringProjectRoot, } from '../../core/root-selection.js';
10
10
  import { loadChangeContext, formatChangeStatus, } from '../../core/artifact-graph/index.js';
11
11
  import { resolveNextStep } from '../../core/change-status-policy.js';
12
12
  import { asStatus } from '../shared-output.js';
@@ -43,12 +43,20 @@ export async function statusCommand(options) {
43
43
  // `Next:` line, so a store-selected root can never carry `--store` in one
44
44
  // and drop it from the other.
45
45
  const storeOptions = isStoreSelectedRoot(root) ? { storeId: root.storeId } : {};
46
+ // A store holds planning artifacts only; the project declaring it is
47
+ // where implementation edits go (#2013).
48
+ const implementationRoot = isStoreSelectedRoot(root)
49
+ ? findDeclaringProjectRoot(root.storeId)
50
+ : null;
51
+ const statusOptions = implementationRoot
52
+ ? { ...storeOptions, implementationRoot }
53
+ : storeOptions;
46
54
  // Single definition of "load one change's status" so the batch and
47
55
  // single-change payloads can never drift apart.
48
56
  const loadStatus = (changeName) => formatChangeStatus(loadChangeContext(projectRoot, changeName, options.schema, {
49
57
  changeDir: getChangeDir(planningHome, changeName),
50
58
  planningHome,
51
- }), storeOptions);
59
+ }), statusOptions);
52
60
  // Handle no-changes case gracefully — status is informational,
53
61
  // so "no changes" is a valid state, not an error.
54
62
  if (!options.change) {
@@ -157,6 +165,11 @@ export function printStatusText(status, options = {}) {
157
165
  const total = status.artifacts.length - skippedCount;
158
166
  console.log(`Change: ${status.changeName}`);
159
167
  console.log(`Schema: ${status.schemaName}`);
168
+ if (status.warnings) {
169
+ for (const warning of status.warnings) {
170
+ console.log(chalk.yellow(`Warning: ${warning}`));
171
+ }
172
+ }
160
173
  if (status.changeRoot) {
161
174
  console.log(`Change root: ${status.changeRoot}`);
162
175
  }
@@ -1,6 +1,18 @@
1
1
  import type { spawn as nodeSpawn } from 'node:child_process';
2
- import { Command } from 'commander';
3
2
  import { type LaunchCommand } from '../core/openers.js';
3
+ export interface WorksetCreateOptions {
4
+ member?: string[];
5
+ tool?: string;
6
+ json?: boolean;
7
+ }
8
+ export interface WorksetOpenOptions {
9
+ tool?: string;
10
+ json?: boolean;
11
+ }
12
+ export interface WorksetRemoveOptions {
13
+ yes?: boolean;
14
+ json?: boolean;
15
+ }
4
16
  interface LaunchResult {
5
17
  code: number | null;
6
18
  signal: NodeJS.Signals | null;
@@ -20,6 +32,14 @@ export interface LaunchOptions {
20
32
  export declare function launchOpenerCommand(command: LaunchCommand, options?: LaunchOptions): Promise<LaunchResult>;
21
33
  /** 130 for SIGINT, 143 for SIGTERM - the shell's 128+n convention. */
22
34
  export declare function exitCodeForLaunch(result: LaunchResult): number;
23
- export declare function registerWorksetCommand(program: Command): void;
35
+ export declare class WorksetCommand {
36
+ create(name: string | undefined, options?: WorksetCreateOptions): Promise<void>;
37
+ private composeFromFlags;
38
+ list(options?: {
39
+ json?: boolean;
40
+ }): Promise<void>;
41
+ open(name: string, options?: WorksetOpenOptions): Promise<void>;
42
+ remove(name: string, options?: WorksetRemoveOptions): Promise<void>;
43
+ }
24
44
  export {};
25
45
  //# sourceMappingURL=workset.d.ts.map