@fission-ai/openspec 1.4.0 → 1.5.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.
Files changed (206) hide show
  1. package/README.md +24 -3
  2. package/dist/cli/index.d.ts +5 -0
  3. package/dist/cli/index.js +94 -76
  4. package/dist/commands/change.d.ts +5 -1
  5. package/dist/commands/change.js +17 -15
  6. package/dist/commands/config.js +6 -65
  7. package/dist/commands/context.d.ts +3 -0
  8. package/dist/commands/context.js +155 -0
  9. package/dist/commands/doctor.d.ts +8 -0
  10. package/dist/commands/doctor.js +163 -0
  11. package/dist/commands/shared-gather.d.ts +14 -0
  12. package/dist/commands/shared-gather.js +31 -0
  13. package/dist/commands/shared-output.d.ts +18 -0
  14. package/dist/commands/shared-output.js +61 -0
  15. package/dist/commands/show.d.ts +11 -6
  16. package/dist/commands/show.js +70 -25
  17. package/dist/commands/spec.d.ts +5 -1
  18. package/dist/commands/spec.js +15 -4
  19. package/dist/commands/store.d.ts +3 -0
  20. package/dist/commands/store.js +547 -0
  21. package/dist/commands/validate.d.ts +2 -0
  22. package/dist/commands/validate.js +75 -39
  23. package/dist/commands/workflow/index.d.ts +0 -2
  24. package/dist/commands/workflow/index.js +0 -1
  25. package/dist/commands/workflow/instructions.d.ts +11 -1
  26. package/dist/commands/workflow/instructions.js +71 -22
  27. package/dist/commands/workflow/new-change.d.ts +6 -3
  28. package/dist/commands/workflow/new-change.js +42 -77
  29. package/dist/commands/workflow/shared.d.ts +15 -3
  30. package/dist/commands/workflow/shared.js +20 -3
  31. package/dist/commands/workflow/status.d.ts +2 -0
  32. package/dist/commands/workflow/status.js +19 -17
  33. package/dist/commands/workset-input.d.ts +19 -0
  34. package/dist/commands/workset-input.js +112 -0
  35. package/dist/commands/workset-prompts.d.ts +12 -0
  36. package/dist/commands/workset-prompts.js +143 -0
  37. package/dist/commands/workset.d.ts +25 -0
  38. package/dist/commands/workset.js +446 -0
  39. package/dist/core/archive.d.ts +17 -6
  40. package/dist/core/archive.js +209 -56
  41. package/dist/core/artifact-graph/index.d.ts +1 -1
  42. package/dist/core/artifact-graph/instruction-loader.d.ts +18 -13
  43. package/dist/core/artifact-graph/instruction-loader.js +8 -31
  44. package/dist/core/change-metadata/schema.d.ts +1 -0
  45. package/dist/core/change-metadata/schema.js +4 -2
  46. package/dist/core/change-status-policy.d.ts +5 -18
  47. package/dist/core/change-status-policy.js +2 -37
  48. package/dist/core/command-generation/adapters/bob.js +1 -14
  49. package/dist/core/command-generation/adapters/claude.js +1 -14
  50. package/dist/core/command-generation/adapters/cursor.js +1 -14
  51. package/dist/core/command-generation/adapters/pi.js +1 -14
  52. package/dist/core/command-generation/adapters/windsurf.js +1 -14
  53. package/dist/core/command-generation/yaml.d.ts +22 -0
  54. package/dist/core/command-generation/yaml.js +38 -0
  55. package/dist/core/completions/command-registry.js +87 -289
  56. package/dist/core/completions/generators/zsh-generator.js +3 -1
  57. package/dist/core/completions/installers/bash-installer.js +3 -0
  58. package/dist/core/completions/installers/fish-installer.js +8 -0
  59. package/dist/core/completions/installers/powershell-installer.js +30 -2
  60. package/dist/core/completions/installers/zsh-installer.js +3 -0
  61. package/dist/core/completions/shared-flags.d.ts +1 -0
  62. package/dist/core/completions/shared-flags.js +5 -0
  63. package/dist/core/config-schema.d.ts +2 -1
  64. package/dist/core/config-schema.js +26 -0
  65. package/dist/core/file-state.d.ts +36 -0
  66. package/dist/core/file-state.js +112 -0
  67. package/dist/core/global-config.d.ts +2 -0
  68. package/dist/core/id.d.ts +17 -0
  69. package/dist/core/id.js +30 -0
  70. package/dist/core/index.d.ts +3 -3
  71. package/dist/core/index.js +3 -3
  72. package/dist/core/init.js +24 -4
  73. package/dist/core/list.d.ts +2 -0
  74. package/dist/core/list.js +19 -5
  75. package/dist/core/openers.d.ts +77 -0
  76. package/dist/core/openers.js +251 -0
  77. package/dist/core/openspec-root.d.ts +45 -0
  78. package/dist/core/openspec-root.js +192 -0
  79. package/dist/core/planning-home.d.ts +1 -6
  80. package/dist/core/planning-home.js +2 -43
  81. package/dist/core/project-config.d.ts +44 -1
  82. package/dist/core/project-config.js +160 -10
  83. package/dist/core/references.d.ts +63 -0
  84. package/dist/core/references.js +310 -0
  85. package/dist/core/relationship-health.d.ts +65 -0
  86. package/dist/core/relationship-health.js +64 -0
  87. package/dist/core/root-selection.d.ts +122 -0
  88. package/dist/core/root-selection.js +337 -0
  89. package/dist/core/specs-apply.d.ts +6 -1
  90. package/dist/core/specs-apply.js +6 -4
  91. package/dist/core/store/errors.d.ts +20 -0
  92. package/dist/core/{context-store → store}/errors.js +3 -3
  93. package/dist/core/store/foundation.d.ts +56 -0
  94. package/dist/core/store/foundation.js +251 -0
  95. package/dist/core/store/git.d.ts +23 -0
  96. package/dist/core/store/git.js +137 -0
  97. package/dist/core/{context-store → store}/index.d.ts +0 -1
  98. package/dist/core/{context-store → store}/index.js +0 -1
  99. package/dist/core/store/operations.d.ts +114 -0
  100. package/dist/core/store/operations.js +783 -0
  101. package/dist/core/store/registry.d.ts +58 -0
  102. package/dist/core/store/registry.js +275 -0
  103. package/dist/core/templates/workflows/apply-change.js +5 -4
  104. package/dist/core/templates/workflows/archive-change.js +5 -4
  105. package/dist/core/templates/workflows/bulk-archive-change.js +5 -4
  106. package/dist/core/templates/workflows/continue-change.js +7 -2
  107. package/dist/core/templates/workflows/explore.js +5 -0
  108. package/dist/core/templates/workflows/ff-change.js +5 -0
  109. package/dist/core/templates/workflows/new-change.js +5 -0
  110. package/dist/core/templates/workflows/onboard.js +4 -1
  111. package/dist/core/templates/workflows/propose.js +5 -0
  112. package/dist/core/templates/workflows/store-selection.d.ts +8 -0
  113. package/dist/core/templates/workflows/store-selection.js +8 -0
  114. package/dist/core/templates/workflows/sync-specs.js +5 -4
  115. package/dist/core/templates/workflows/verify-change.js +5 -4
  116. package/dist/core/working-set.d.ts +47 -0
  117. package/dist/core/working-set.js +43 -0
  118. package/dist/core/worksets.d.ts +75 -0
  119. package/dist/core/worksets.js +245 -0
  120. package/dist/core/zod-issues.d.ts +4 -0
  121. package/dist/core/zod-issues.js +10 -0
  122. package/dist/utils/change-metadata.d.ts +1 -0
  123. package/dist/utils/change-metadata.js +1 -1
  124. package/dist/utils/change-utils.js +15 -0
  125. package/dist/utils/file-system.js +36 -17
  126. package/package.json +6 -6
  127. package/dist/commands/context-store.d.ts +0 -3
  128. package/dist/commands/context-store.js +0 -475
  129. package/dist/commands/initiative.d.ts +0 -13
  130. package/dist/commands/initiative.js +0 -318
  131. package/dist/commands/workflow/initiative-link.d.ts +0 -24
  132. package/dist/commands/workflow/initiative-link.js +0 -47
  133. package/dist/commands/workflow/set-change.d.ts +0 -13
  134. package/dist/commands/workflow/set-change.js +0 -87
  135. package/dist/commands/workspace/context-status.d.ts +0 -4
  136. package/dist/commands/workspace/context-status.js +0 -59
  137. package/dist/commands/workspace/open-target-selection.d.ts +0 -13
  138. package/dist/commands/workspace/open-target-selection.js +0 -146
  139. package/dist/commands/workspace/open-view.d.ts +0 -62
  140. package/dist/commands/workspace/open-view.js +0 -249
  141. package/dist/commands/workspace/open.d.ts +0 -37
  142. package/dist/commands/workspace/open.js +0 -110
  143. package/dist/commands/workspace/opener-selection.d.ts +0 -11
  144. package/dist/commands/workspace/opener-selection.js +0 -98
  145. package/dist/commands/workspace/operations.d.ts +0 -29
  146. package/dist/commands/workspace/operations.js +0 -543
  147. package/dist/commands/workspace/prompt-theme.d.ts +0 -29
  148. package/dist/commands/workspace/prompt-theme.js +0 -24
  149. package/dist/commands/workspace/registration.d.ts +0 -13
  150. package/dist/commands/workspace/registration.js +0 -84
  151. package/dist/commands/workspace/selection.d.ts +0 -9
  152. package/dist/commands/workspace/selection.js +0 -115
  153. package/dist/commands/workspace/setup-prompts.d.ts +0 -13
  154. package/dist/commands/workspace/setup-prompts.js +0 -121
  155. package/dist/commands/workspace/types.d.ts +0 -103
  156. package/dist/commands/workspace/types.js +0 -36
  157. package/dist/commands/workspace.d.ts +0 -6
  158. package/dist/commands/workspace.js +0 -587
  159. package/dist/core/collections/index.d.ts +0 -3
  160. package/dist/core/collections/index.js +0 -3
  161. package/dist/core/collections/initiatives/collection.d.ts +0 -4
  162. package/dist/core/collections/initiatives/collection.js +0 -17
  163. package/dist/core/collections/initiatives/index.d.ts +0 -6
  164. package/dist/core/collections/initiatives/index.js +0 -6
  165. package/dist/core/collections/initiatives/operations.d.ts +0 -49
  166. package/dist/core/collections/initiatives/operations.js +0 -175
  167. package/dist/core/collections/initiatives/resolution.d.ts +0 -87
  168. package/dist/core/collections/initiatives/resolution.js +0 -374
  169. package/dist/core/collections/initiatives/schema.d.ts +0 -41
  170. package/dist/core/collections/initiatives/schema.js +0 -134
  171. package/dist/core/collections/initiatives/templates.d.ts +0 -12
  172. package/dist/core/collections/initiatives/templates.js +0 -90
  173. package/dist/core/collections/runtime.d.ts +0 -46
  174. package/dist/core/collections/runtime.js +0 -194
  175. package/dist/core/context-store/binding.d.ts +0 -53
  176. package/dist/core/context-store/binding.js +0 -197
  177. package/dist/core/context-store/errors.d.ts +0 -20
  178. package/dist/core/context-store/foundation.d.ts +0 -55
  179. package/dist/core/context-store/foundation.js +0 -321
  180. package/dist/core/context-store/operations.d.ts +0 -85
  181. package/dist/core/context-store/operations.js +0 -528
  182. package/dist/core/context-store/registry.d.ts +0 -45
  183. package/dist/core/context-store/registry.js +0 -229
  184. package/dist/core/workspace/foundation.d.ts +0 -67
  185. package/dist/core/workspace/foundation.js +0 -295
  186. package/dist/core/workspace/index.d.ts +0 -8
  187. package/dist/core/workspace/index.js +0 -8
  188. package/dist/core/workspace/legacy-state.d.ts +0 -28
  189. package/dist/core/workspace/legacy-state.js +0 -200
  190. package/dist/core/workspace/link-input.d.ts +0 -9
  191. package/dist/core/workspace/link-input.js +0 -32
  192. package/dist/core/workspace/open-surface.d.ts +0 -45
  193. package/dist/core/workspace/open-surface.js +0 -215
  194. package/dist/core/workspace/openers.d.ts +0 -21
  195. package/dist/core/workspace/openers.js +0 -124
  196. package/dist/core/workspace/registry.d.ts +0 -24
  197. package/dist/core/workspace/registry.js +0 -146
  198. package/dist/core/workspace/skills.d.ts +0 -57
  199. package/dist/core/workspace/skills.js +0 -334
  200. package/dist/core/workspace/state-io.d.ts +0 -10
  201. package/dist/core/workspace/state-io.js +0 -119
  202. package/schemas/workspace-planning/schema.yaml +0 -72
  203. package/schemas/workspace-planning/templates/design.md +0 -33
  204. package/schemas/workspace-planning/templates/proposal.md +0 -28
  205. package/schemas/workspace-planning/templates/spec.md +0 -9
  206. package/schemas/workspace-planning/templates/tasks.md +0 -15
@@ -56,6 +56,7 @@ export const COMMAND_REGISTRY = [
56
56
  values: ['recent', 'name'],
57
57
  },
58
58
  COMMON_FLAGS.json,
59
+ COMMON_FLAGS.store,
59
60
  ],
60
61
  },
61
62
  {
@@ -91,6 +92,7 @@ export const COMMAND_REGISTRY = [
91
92
  takesValue: true,
92
93
  },
93
94
  COMMON_FLAGS.noInteractive,
95
+ COMMON_FLAGS.store,
94
96
  ],
95
97
  },
96
98
  {
@@ -125,6 +127,7 @@ export const COMMAND_REGISTRY = [
125
127
  description: 'Show specific requirement by ID (JSON only, spec-specific)',
126
128
  takesValue: true,
127
129
  },
130
+ COMMON_FLAGS.store,
128
131
  ],
129
132
  },
130
133
  {
@@ -147,6 +150,11 @@ export const COMMAND_REGISTRY = [
147
150
  name: 'no-validate',
148
151
  description: 'Skip validation (not recommended)',
149
152
  },
153
+ {
154
+ name: 'json',
155
+ description: 'Output as JSON (non-interactive)',
156
+ },
157
+ COMMON_FLAGS.store,
150
158
  ],
151
159
  },
152
160
  {
@@ -164,6 +172,7 @@ export const COMMAND_REGISTRY = [
164
172
  takesValue: true,
165
173
  },
166
174
  COMMON_FLAGS.json,
175
+ COMMON_FLAGS.store,
167
176
  ],
168
177
  },
169
178
  {
@@ -183,6 +192,7 @@ export const COMMAND_REGISTRY = [
183
192
  takesValue: true,
184
193
  },
185
194
  COMMON_FLAGS.json,
195
+ COMMON_FLAGS.store,
186
196
  ],
187
197
  },
188
198
  {
@@ -222,27 +232,7 @@ export const COMMAND_REGISTRY = [
222
232
  },
223
233
  {
224
234
  name: 'goal',
225
- description: 'Workspace product goal to store with the change',
226
- takesValue: true,
227
- },
228
- {
229
- name: 'areas',
230
- description: 'Comma-separated affected workspace link names',
231
- takesValue: true,
232
- },
233
- {
234
- name: 'initiative',
235
- description: 'Link the repo-local change to an initiative',
236
- takesValue: true,
237
- },
238
- {
239
- name: 'store',
240
- description: 'Context store id for --initiative',
241
- takesValue: true,
242
- },
243
- {
244
- name: 'store-path',
245
- description: 'Existing local context store root for --initiative',
235
+ description: 'Optional goal metadata to store with the change',
246
236
  takesValue: true,
247
237
  },
248
238
  {
@@ -251,254 +241,64 @@ export const COMMAND_REGISTRY = [
251
241
  takesValue: true,
252
242
  },
253
243
  COMMON_FLAGS.json,
244
+ COMMON_FLAGS.store,
254
245
  ],
255
246
  },
256
247
  ],
257
248
  },
258
249
  {
259
- name: 'set',
260
- description: 'Set checked-in OpenSpec metadata',
261
- flags: [],
262
- subcommands: [
263
- {
264
- name: 'change',
265
- description: 'Set repo-local change metadata',
266
- acceptsPositional: true,
267
- positionalType: 'change-id',
268
- positionals: [{ name: 'name', type: 'change-id' }],
269
- flags: [
270
- {
271
- name: 'initiative',
272
- description: 'Link the repo-local change to an initiative',
273
- takesValue: true,
274
- },
275
- {
276
- name: 'store',
277
- description: 'Context store id for --initiative',
278
- takesValue: true,
279
- },
280
- {
281
- name: 'store-path',
282
- description: 'Existing local context store root for --initiative',
283
- takesValue: true,
284
- },
285
- COMMON_FLAGS.json,
286
- ],
287
- },
288
- ],
289
- },
290
- {
291
- name: 'workspace',
292
- description: 'Set up and inspect coordination workspaces',
293
- flags: [],
294
- subcommands: [
295
- {
296
- name: 'setup',
297
- description: 'Set up a workspace and link existing repos or folders',
298
- flags: [
299
- {
300
- name: 'name',
301
- description: 'Workspace name',
302
- takesValue: true,
303
- },
304
- {
305
- name: 'link',
306
- description: 'Repo or folder link. Use <path> or <name>=<path>',
307
- takesValue: true,
308
- },
309
- {
310
- name: 'opener',
311
- description: 'Preferred opener: codex-cli, claude, github-copilot, or editor',
312
- takesValue: true,
313
- values: ['codex-cli', 'claude', 'github-copilot', 'editor'],
314
- },
315
- {
316
- name: 'tools',
317
- description: 'Install OpenSpec skills for agents (all, none, or comma-separated tool IDs)',
318
- takesValue: true,
319
- },
320
- COMMON_FLAGS.json,
321
- COMMON_FLAGS.noInteractive,
322
- ],
323
- },
324
- {
325
- name: 'list',
326
- description: 'List known OpenSpec workspaces',
327
- flags: [
328
- COMMON_FLAGS.json,
329
- ],
330
- },
331
- {
332
- name: 'ls',
333
- description: 'List known OpenSpec workspaces',
334
- flags: [
335
- COMMON_FLAGS.json,
336
- ],
337
- },
338
- {
339
- name: 'link',
340
- description: 'Link an existing repo or folder to a workspace',
341
- acceptsPositional: true,
342
- positionals: [
343
- { name: 'name-or-path', type: 'path', optional: true },
344
- { name: 'path', type: 'path', optional: true },
345
- ],
346
- flags: [
347
- {
348
- name: 'workspace',
349
- description: 'Workspace name from local workspace views',
350
- takesValue: true,
351
- },
352
- COMMON_FLAGS.json,
353
- COMMON_FLAGS.noInteractive,
354
- ],
355
- },
356
- {
357
- name: 'relink',
358
- description: 'Update the local path for an existing workspace link',
359
- acceptsPositional: true,
360
- positionals: [
361
- { name: 'name' },
362
- { name: 'path', type: 'path' },
363
- ],
364
- flags: [
365
- {
366
- name: 'workspace',
367
- description: 'Workspace name from local workspace views',
368
- takesValue: true,
369
- },
370
- COMMON_FLAGS.json,
371
- COMMON_FLAGS.noInteractive,
372
- ],
373
- },
374
- {
375
- name: 'doctor',
376
- description: 'Check what a workspace can resolve on this machine',
377
- flags: [
378
- {
379
- name: 'workspace',
380
- description: 'Workspace name from local workspace views',
381
- takesValue: true,
382
- },
383
- COMMON_FLAGS.json,
384
- COMMON_FLAGS.noInteractive,
385
- ],
386
- },
387
- {
388
- name: 'update',
389
- description: 'Refresh workspace-local OpenSpec guidance and agent skills',
390
- acceptsPositional: true,
391
- positionals: [{ name: 'name', optional: true }],
392
- flags: [
393
- {
394
- name: 'workspace',
395
- description: 'Workspace name from local workspace views',
396
- takesValue: true,
397
- },
398
- {
399
- name: 'tools',
400
- description: 'Select agents for workspace skills-only delivery; global profile selects workflows',
401
- takesValue: true,
402
- },
403
- COMMON_FLAGS.json,
404
- COMMON_FLAGS.noInteractive,
405
- ],
406
- },
407
- {
408
- name: 'open',
409
- description: 'Open a workspace in an agent or VS Code editor',
410
- acceptsPositional: true,
411
- positionals: [{ name: 'name', optional: true }],
412
- flags: [
413
- {
414
- name: 'workspace',
415
- description: 'Workspace name from local workspace views',
416
- takesValue: true,
417
- },
418
- {
419
- name: 'initiative',
420
- description: 'Open an initiative as a local workspace view',
421
- takesValue: true,
422
- },
423
- {
424
- name: 'store',
425
- description: 'Context store id for --initiative',
426
- takesValue: true,
427
- },
428
- {
429
- name: 'store-path',
430
- description: 'Existing local context store root for --initiative',
431
- takesValue: true,
432
- },
433
- {
434
- name: 'agent',
435
- description: 'Use an agent for this session: codex-cli, claude, or github-copilot',
436
- takesValue: true,
437
- values: ['codex-cli', 'claude', 'github-copilot'],
438
- },
439
- {
440
- name: 'editor',
441
- description: 'Open the workspace in VS Code editor mode',
442
- },
443
- {
444
- name: 'prepare-only',
445
- description: 'Unsupported: preview surfaces belong to a future context/query command',
446
- },
447
- COMMON_FLAGS.json,
448
- {
449
- name: 'change',
450
- description: 'Unsupported: change-scoped open belongs to future workspace change planning',
451
- takesValue: true,
452
- },
453
- COMMON_FLAGS.noInteractive,
454
- ],
455
- },
456
- ],
457
- },
458
- {
459
- name: 'context-store',
460
- description: 'Set up and inspect context stores',
250
+ name: 'store',
251
+ description: 'Create and manage stores - standalone OpenSpec repos you register on this machine',
461
252
  flags: [],
462
253
  subcommands: [
463
254
  {
464
255
  name: 'setup',
465
- description: 'Create or register a local context store',
256
+ description: 'Create or register a local store',
466
257
  acceptsPositional: true,
467
258
  positionals: [{ name: 'id', optional: true }],
468
259
  flags: [
469
260
  {
470
261
  name: 'path',
471
- description: 'Directory to use for the context store',
262
+ description: 'Directory to use for the store',
472
263
  takesValue: true,
473
264
  },
474
265
  {
475
266
  name: 'init-git',
476
- description: 'Initialize a Git repository in the context store',
267
+ description: 'Initialize a Git repository in the store',
477
268
  },
478
269
  {
479
270
  name: 'no-init-git',
480
271
  description: 'Skip Git repository initialization',
481
272
  },
273
+ {
274
+ name: 'remote',
275
+ description: 'Canonical clone source recorded in store.yaml',
276
+ takesValue: true,
277
+ },
482
278
  COMMON_FLAGS.json,
483
279
  ],
484
280
  },
485
281
  {
486
282
  name: 'register',
487
- description: 'Register an existing context store directory',
283
+ description: 'Register an existing store directory',
488
284
  acceptsPositional: true,
489
285
  positionals: [{ name: 'path', type: 'path', optional: true }],
490
286
  flags: [
491
287
  {
492
288
  name: 'id',
493
- description: 'Context store id',
289
+ description: 'Store id',
494
290
  takesValue: true,
495
291
  },
292
+ {
293
+ name: 'yes',
294
+ description: 'Confirm creating store identity metadata',
295
+ },
496
296
  COMMON_FLAGS.json,
497
297
  ],
498
298
  },
499
299
  {
500
300
  name: 'unregister',
501
- description: 'Forget a local context-store registration without deleting files',
301
+ description: 'Forget a local store registration without deleting files',
502
302
  acceptsPositional: true,
503
303
  positionals: [{ name: 'id' }],
504
304
  flags: [
@@ -507,34 +307,34 @@ export const COMMAND_REGISTRY = [
507
307
  },
508
308
  {
509
309
  name: 'remove',
510
- description: 'Forget a local context-store registration and delete its local folder',
310
+ description: 'Forget a local store registration and delete its local folder',
511
311
  acceptsPositional: true,
512
312
  positionals: [{ name: 'id' }],
513
313
  flags: [
514
314
  {
515
315
  name: 'yes',
516
- description: 'Confirm local context-store folder deletion',
316
+ description: 'Confirm local store folder deletion',
517
317
  },
518
318
  COMMON_FLAGS.json,
519
319
  ],
520
320
  },
521
321
  {
522
322
  name: 'list',
523
- description: 'List registered context stores',
323
+ description: 'List registered stores',
524
324
  flags: [
525
325
  COMMON_FLAGS.json,
526
326
  ],
527
327
  },
528
328
  {
529
329
  name: 'ls',
530
- description: 'List registered context stores',
330
+ description: 'List registered stores',
531
331
  flags: [
532
332
  COMMON_FLAGS.json,
533
333
  ],
534
334
  },
535
335
  {
536
336
  name: 'doctor',
537
- description: 'Check local context-store registration and metadata',
337
+ description: 'Check local store registration and metadata',
538
338
  acceptsPositional: true,
539
339
  positionals: [{ name: 'id', optional: true }],
540
340
  flags: [
@@ -544,88 +344,86 @@ export const COMMAND_REGISTRY = [
544
344
  ],
545
345
  },
546
346
  {
547
- name: 'initiative',
548
- description: 'Create and list coordinated initiatives',
347
+ name: 'context',
348
+ description: 'Print the working context for the resolved OpenSpec root',
349
+ flags: [
350
+ COMMON_FLAGS.json,
351
+ COMMON_FLAGS.store,
352
+ {
353
+ name: 'code-workspace',
354
+ description: 'Also write a VS Code workspace file for the set',
355
+ takesValue: true,
356
+ },
357
+ {
358
+ name: 'force',
359
+ description: 'Overwrite an existing --code-workspace file',
360
+ },
361
+ ],
362
+ },
363
+ {
364
+ name: 'doctor',
365
+ description: 'Report relationship health for the resolved OpenSpec root',
366
+ flags: [
367
+ COMMON_FLAGS.json,
368
+ COMMON_FLAGS.store,
369
+ ],
370
+ },
371
+ {
372
+ name: 'workset',
373
+ description: 'Compose, keep, and open personal working views (purely local)',
549
374
  flags: [],
550
375
  subcommands: [
551
376
  {
552
377
  name: 'create',
553
- description: 'Create an initiative in a context store',
378
+ description: 'Compose and save a named working view of folders you choose',
554
379
  acceptsPositional: true,
555
- positionals: [{ name: 'id', optional: true }],
380
+ positionals: [{ name: 'name', optional: true }],
556
381
  flags: [
557
382
  {
558
- name: 'store',
559
- description: 'Context store id from the local context-store registry',
383
+ name: 'member',
384
+ description: 'Member folder as <path> or <name>=<path>; repeatable, first is the primary',
560
385
  takesValue: true,
561
386
  },
562
387
  {
563
- name: 'store-path',
564
- description: 'Existing local context store root',
565
- takesValue: true,
566
- },
567
- {
568
- name: 'title',
569
- description: 'Initiative title',
570
- takesValue: true,
571
- },
572
- {
573
- name: 'summary',
574
- description: 'Initiative summary',
388
+ name: 'tool',
389
+ description: 'Preferred tool to open this workset with',
575
390
  takesValue: true,
576
391
  },
577
392
  COMMON_FLAGS.json,
578
393
  ],
579
394
  },
580
395
  {
581
- name: 'show',
582
- description: 'Show where an initiative lives and how to read it',
583
- acceptsPositional: true,
584
- positionals: [{ name: 'id' }],
585
- flags: [
586
- {
587
- name: 'store',
588
- description: 'Context store id from the local context-store registry',
589
- takesValue: true,
590
- },
591
- {
592
- name: 'store-path',
593
- description: 'Existing local context store root',
594
- takesValue: true,
595
- },
596
- COMMON_FLAGS.json,
597
- ],
396
+ name: 'list',
397
+ description: 'Show saved worksets with their members',
398
+ flags: [COMMON_FLAGS.json],
598
399
  },
599
400
  {
600
- name: 'list',
601
- description: 'List initiatives across registered context stores',
401
+ name: 'ls',
402
+ description: 'Show saved worksets with their members',
403
+ flags: [COMMON_FLAGS.json],
404
+ },
405
+ {
406
+ name: 'open',
407
+ description: 'Open a saved workset in your tool (editor window or agent session)',
408
+ acceptsPositional: true,
409
+ positionals: [{ name: 'name' }],
602
410
  flags: [
603
411
  {
604
- name: 'store',
605
- description: 'Context store id from the local context-store registry',
412
+ name: 'tool',
413
+ description: 'Open with this tool just this once',
606
414
  takesValue: true,
607
415
  },
608
- {
609
- name: 'store-path',
610
- description: 'Existing local context store root',
611
- takesValue: true,
612
- },
613
- COMMON_FLAGS.json,
614
416
  ],
615
417
  },
616
418
  {
617
- name: 'ls',
618
- description: 'List initiatives across registered context stores',
419
+ name: 'remove',
420
+ description: 'Delete a saved workset (member folders are never touched)',
421
+ acceptsPositional: true,
422
+ positionals: [{ name: 'name' }],
619
423
  flags: [
620
424
  {
621
- name: 'store',
622
- description: 'Context store id from the local context-store registry',
623
- takesValue: true,
624
- },
625
- {
626
- name: 'store-path',
627
- description: 'Existing local context store root',
628
- takesValue: true,
425
+ name: 'yes',
426
+ description: 'Confirm removal non-interactively',
629
427
  },
630
428
  COMMON_FLAGS.json,
631
429
  ],
@@ -250,7 +250,9 @@ compdef _openspec openspec
250
250
  escapeDescription(desc) {
251
251
  return desc
252
252
  .replace(/\\/g, '\\\\')
253
- .replace(/'/g, "\\'")
253
+ // Inside zsh single quotes, backslash-quote does NOT escape; the
254
+ // idiom is close-quote, literal quote, reopen: '\''
255
+ .replace(/'/g, "'\\''")
254
256
  .replace(/\[/g, '\\[')
255
257
  .replace(/]/g, '\\]')
256
258
  .replace(/:/g, '\\:');
@@ -210,6 +210,9 @@ export class BashInstaller {
210
210
  // File doesn't exist or can't be read, proceed with installation
211
211
  console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
212
212
  }
213
+ if (!(await FileSystemUtils.canWriteFile(targetPath))) {
214
+ throw new Error(`Path is not writable: ${targetPath}`);
215
+ }
213
216
  // Ensure the directory exists
214
217
  const targetDir = path.dirname(targetPath);
215
218
  await fs.mkdir(targetDir, { recursive: true });
@@ -1,6 +1,7 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { FileSystemUtils } from '../../../utils/file-system.js';
4
5
  /**
5
6
  * Installer for Fish completion scripts.
6
7
  * Fish automatically loads completions from ~/.config/fish/completions/
@@ -70,6 +71,9 @@ export class FishInstaller {
70
71
  // File doesn't exist or can't be read, proceed with installation
71
72
  console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
72
73
  }
74
+ if (!(await FileSystemUtils.canWriteFile(targetPath))) {
75
+ throw new Error(`Path is not writable: ${targetPath}`);
76
+ }
73
77
  // Ensure the directory exists
74
78
  const targetDir = path.dirname(targetPath);
75
79
  await fs.mkdir(targetDir, { recursive: true });
@@ -125,6 +129,10 @@ export class FishInstaller {
125
129
  message: 'Completion script is not installed',
126
130
  };
127
131
  }
132
+ const targetDir = path.dirname(targetPath);
133
+ if (!(await FileSystemUtils.canWriteFile(targetDir))) {
134
+ throw new Error(`Path is not writable: ${targetDir}`);
135
+ }
128
136
  // Remove the completion script
129
137
  await fs.unlink(targetPath);
130
138
  return {
@@ -1,6 +1,7 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { FileSystemUtils } from '../../../utils/file-system.js';
4
5
  /**
5
6
  * Installer for PowerShell completion scripts.
6
7
  * Works with both Windows PowerShell 5.1 and PowerShell Core 7+
@@ -154,9 +155,23 @@ export class PowerShellInstaller {
154
155
  let anyConfigured = false;
155
156
  for (const profilePath of profilePaths) {
156
157
  try {
157
- // Create profile file if it doesn't exist
158
158
  const profileDir = path.dirname(profilePath);
159
- await fs.mkdir(profileDir, { recursive: true });
159
+ let profileExists = false;
160
+ try {
161
+ await fs.access(profilePath);
162
+ profileExists = true;
163
+ }
164
+ catch (err) {
165
+ if (err?.code !== 'ENOENT') {
166
+ throw err;
167
+ }
168
+ }
169
+ if (!profileExists) {
170
+ if (!(await FileSystemUtils.canWriteFile(profilePath))) {
171
+ throw new Error(`Path is not writable: ${profilePath}`);
172
+ }
173
+ await fs.mkdir(profileDir, { recursive: true });
174
+ }
160
175
  let profileContent = '';
161
176
  let fileEncoding = 'utf-8';
162
177
  let fileBom = Buffer.alloc(0);
@@ -191,6 +206,9 @@ export class PowerShellInstaller {
191
206
  '',
192
207
  ].join('\n');
193
208
  const newContent = profileContent + openspecBlock;
209
+ if (!(await FileSystemUtils.canWriteFile(profilePath))) {
210
+ throw new Error(`Path is not writable: ${profilePath}`);
211
+ }
194
212
  await this.writeProfileFile(profilePath, newContent, fileEncoding, fileBom);
195
213
  anyConfigured = true;
196
214
  }
@@ -246,6 +264,9 @@ export class PowerShellInstaller {
246
264
  const afterBlock = profileContent.substring(endIndex + endMarker.length);
247
265
  // Clean up extra newlines
248
266
  const newContent = (beforeBlock.trimEnd() + '\n' + afterBlock.trimStart()).trim() + '\n';
267
+ if (!(await FileSystemUtils.canWriteFile(profilePath))) {
268
+ throw new Error(`Path is not writable: ${profilePath}`);
269
+ }
249
270
  await this.writeProfileFile(profilePath, newContent, fileEncoding, fileBom);
250
271
  anyRemoved = true;
251
272
  }
@@ -287,6 +308,9 @@ export class PowerShellInstaller {
287
308
  // File doesn't exist or can't be read, proceed with installation
288
309
  console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
289
310
  }
311
+ if (!(await FileSystemUtils.canWriteFile(targetPath))) {
312
+ throw new Error(`Path is not writable: ${targetPath}`);
313
+ }
290
314
  // Ensure the directory exists
291
315
  const targetDir = path.dirname(targetPath);
292
316
  await fs.mkdir(targetDir, { recursive: true });
@@ -367,6 +391,10 @@ export class PowerShellInstaller {
367
391
  message: 'Completion script is not installed',
368
392
  };
369
393
  }
394
+ const targetDir = path.dirname(targetPath);
395
+ if (!(await FileSystemUtils.canWriteFile(targetDir))) {
396
+ throw new Error(`Path is not writable: ${targetDir}`);
397
+ }
370
398
  // Remove the completion script
371
399
  await fs.unlink(targetPath);
372
400
  // Remove profile configuration
@@ -228,6 +228,9 @@ export class ZshInstaller {
228
228
  // File doesn't exist or can't be read, proceed with installation
229
229
  console.debug(`Unable to read existing completion file at ${targetPath}: ${error.message}`);
230
230
  }
231
+ if (!(await FileSystemUtils.canWriteFile(targetPath))) {
232
+ throw new Error(`Path is not writable: ${targetPath}`);
233
+ }
231
234
  // Ensure the directory exists
232
235
  const targetDir = path.dirname(targetPath);
233
236
  await fs.mkdir(targetDir, { recursive: true });
@@ -8,5 +8,6 @@ export declare const COMMON_FLAGS: {
8
8
  readonly strict: FlagDefinition;
9
9
  readonly noInteractive: FlagDefinition;
10
10
  readonly type: FlagDefinition;
11
+ readonly store: FlagDefinition;
11
12
  };
12
13
  //# sourceMappingURL=shared-flags.d.ts.map