@fission-ai/openspec 1.4.1 → 1.6.0-beta.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 (241) hide show
  1. package/README.md +37 -4
  2. package/dist/cli/index.d.ts +5 -0
  3. package/dist/cli/index.js +95 -95
  4. package/dist/commands/change.d.ts +5 -2
  5. package/dist/commands/change.js +23 -55
  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 +10 -0
  22. package/dist/commands/validate.js +88 -40
  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 +220 -68
  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.d.ts +1 -1
  50. package/dist/core/command-generation/adapters/claude.js +4 -15
  51. package/dist/core/command-generation/adapters/cursor.js +1 -14
  52. package/dist/core/command-generation/adapters/index.d.ts +2 -0
  53. package/dist/core/command-generation/adapters/index.js +2 -0
  54. package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
  55. package/dist/core/command-generation/adapters/oh-my-pi.js +44 -0
  56. package/dist/core/command-generation/adapters/pi.js +1 -14
  57. package/dist/core/command-generation/adapters/trae.d.ts +13 -0
  58. package/dist/core/command-generation/adapters/trae.js +48 -0
  59. package/dist/core/command-generation/adapters/windsurf.js +1 -14
  60. package/dist/core/command-generation/registry.js +4 -0
  61. package/dist/core/command-generation/yaml.d.ts +22 -0
  62. package/dist/core/command-generation/yaml.js +38 -0
  63. package/dist/core/completions/command-registry.js +87 -289
  64. package/dist/core/completions/generators/zsh-generator.js +3 -1
  65. package/dist/core/completions/installers/bash-installer.js +3 -0
  66. package/dist/core/completions/installers/fish-installer.js +8 -0
  67. package/dist/core/completions/installers/powershell-installer.js +30 -2
  68. package/dist/core/completions/installers/zsh-installer.js +3 -0
  69. package/dist/core/completions/shared-flags.d.ts +1 -0
  70. package/dist/core/completions/shared-flags.js +5 -0
  71. package/dist/core/config-schema.d.ts +2 -1
  72. package/dist/core/config-schema.js +26 -0
  73. package/dist/core/config.js +1 -0
  74. package/dist/core/file-state.d.ts +36 -0
  75. package/dist/core/file-state.js +112 -0
  76. package/dist/core/global-config.d.ts +2 -0
  77. package/dist/core/id.d.ts +17 -0
  78. package/dist/core/id.js +30 -0
  79. package/dist/core/index.d.ts +3 -3
  80. package/dist/core/index.js +3 -3
  81. package/dist/core/init.js +27 -6
  82. package/dist/core/list.d.ts +2 -0
  83. package/dist/core/list.js +37 -14
  84. package/dist/core/openers.d.ts +77 -0
  85. package/dist/core/openers.js +251 -0
  86. package/dist/core/openspec-root.d.ts +45 -0
  87. package/dist/core/openspec-root.js +195 -0
  88. package/dist/core/parsers/change-parser.js +2 -1
  89. package/dist/core/parsers/markdown-parser.d.ts +0 -3
  90. package/dist/core/parsers/markdown-parser.js +5 -59
  91. package/dist/core/parsers/requirement-blocks.d.ts +11 -0
  92. package/dist/core/parsers/requirement-blocks.js +42 -8
  93. package/dist/core/parsers/requirement-text.d.ts +55 -0
  94. package/dist/core/parsers/requirement-text.js +136 -0
  95. package/dist/core/planning-home.d.ts +1 -6
  96. package/dist/core/planning-home.js +2 -43
  97. package/dist/core/profile-sync-drift.js +1 -0
  98. package/dist/core/profiles.d.ts +2 -2
  99. package/dist/core/profiles.js +2 -1
  100. package/dist/core/project-config.d.ts +44 -1
  101. package/dist/core/project-config.js +160 -10
  102. package/dist/core/references.d.ts +63 -0
  103. package/dist/core/references.js +310 -0
  104. package/dist/core/relationship-health.d.ts +65 -0
  105. package/dist/core/relationship-health.js +64 -0
  106. package/dist/core/root-selection.d.ts +122 -0
  107. package/dist/core/root-selection.js +337 -0
  108. package/dist/core/schemas/base.schema.js +7 -2
  109. package/dist/core/shared/allowed-tools.d.ts +12 -0
  110. package/dist/core/shared/allowed-tools.js +12 -0
  111. package/dist/core/shared/skill-generation.js +5 -1
  112. package/dist/core/shared/tool-detection.d.ts +2 -2
  113. package/dist/core/shared/tool-detection.js +2 -0
  114. package/dist/core/specs-apply.d.ts +6 -1
  115. package/dist/core/specs-apply.js +41 -5
  116. package/dist/core/store/errors.d.ts +20 -0
  117. package/dist/core/{context-store → store}/errors.js +3 -3
  118. package/dist/core/store/foundation.d.ts +56 -0
  119. package/dist/core/store/foundation.js +251 -0
  120. package/dist/core/store/git.d.ts +23 -0
  121. package/dist/core/store/git.js +137 -0
  122. package/dist/core/{context-store → store}/index.d.ts +0 -1
  123. package/dist/core/{context-store → store}/index.js +0 -1
  124. package/dist/core/store/operations.d.ts +114 -0
  125. package/dist/core/store/operations.js +803 -0
  126. package/dist/core/store/registry.d.ts +58 -0
  127. package/dist/core/store/registry.js +275 -0
  128. package/dist/core/templates/skill-templates.d.ts +1 -0
  129. package/dist/core/templates/skill-templates.js +1 -0
  130. package/dist/core/templates/workflows/apply-change.js +5 -4
  131. package/dist/core/templates/workflows/archive-change.js +5 -4
  132. package/dist/core/templates/workflows/bulk-archive-change.js +5 -4
  133. package/dist/core/templates/workflows/continue-change.js +7 -2
  134. package/dist/core/templates/workflows/explore.js +5 -0
  135. package/dist/core/templates/workflows/ff-change.js +5 -0
  136. package/dist/core/templates/workflows/new-change.js +5 -0
  137. package/dist/core/templates/workflows/onboard.js +4 -1
  138. package/dist/core/templates/workflows/propose.js +5 -0
  139. package/dist/core/templates/workflows/store-selection.d.ts +8 -0
  140. package/dist/core/templates/workflows/store-selection.js +8 -0
  141. package/dist/core/templates/workflows/sync-specs.js +5 -4
  142. package/dist/core/templates/workflows/update-change.d.ts +10 -0
  143. package/dist/core/templates/workflows/update-change.js +167 -0
  144. package/dist/core/templates/workflows/verify-change.js +5 -4
  145. package/dist/core/update.js +4 -4
  146. package/dist/core/validation/validator.d.ts +7 -0
  147. package/dist/core/validation/validator.js +101 -38
  148. package/dist/core/view.js +1 -1
  149. package/dist/core/working-set.d.ts +47 -0
  150. package/dist/core/working-set.js +43 -0
  151. package/dist/core/worksets.d.ts +75 -0
  152. package/dist/core/worksets.js +245 -0
  153. package/dist/core/zod-issues.d.ts +4 -0
  154. package/dist/core/zod-issues.js +10 -0
  155. package/dist/utils/change-metadata.d.ts +1 -0
  156. package/dist/utils/change-metadata.js +1 -1
  157. package/dist/utils/change-utils.js +15 -0
  158. package/dist/utils/file-system.js +36 -17
  159. package/dist/utils/task-progress.d.ts +10 -1
  160. package/dist/utils/task-progress.js +66 -2
  161. package/package.json +26 -23
  162. package/dist/commands/context-store.d.ts +0 -3
  163. package/dist/commands/context-store.js +0 -475
  164. package/dist/commands/initiative.d.ts +0 -13
  165. package/dist/commands/initiative.js +0 -318
  166. package/dist/commands/workflow/initiative-link.d.ts +0 -24
  167. package/dist/commands/workflow/initiative-link.js +0 -47
  168. package/dist/commands/workflow/set-change.d.ts +0 -13
  169. package/dist/commands/workflow/set-change.js +0 -87
  170. package/dist/commands/workspace/context-status.d.ts +0 -4
  171. package/dist/commands/workspace/context-status.js +0 -59
  172. package/dist/commands/workspace/open-target-selection.d.ts +0 -13
  173. package/dist/commands/workspace/open-target-selection.js +0 -146
  174. package/dist/commands/workspace/open-view.d.ts +0 -62
  175. package/dist/commands/workspace/open-view.js +0 -249
  176. package/dist/commands/workspace/open.d.ts +0 -37
  177. package/dist/commands/workspace/open.js +0 -110
  178. package/dist/commands/workspace/opener-selection.d.ts +0 -11
  179. package/dist/commands/workspace/opener-selection.js +0 -98
  180. package/dist/commands/workspace/operations.d.ts +0 -29
  181. package/dist/commands/workspace/operations.js +0 -543
  182. package/dist/commands/workspace/prompt-theme.d.ts +0 -29
  183. package/dist/commands/workspace/prompt-theme.js +0 -24
  184. package/dist/commands/workspace/registration.d.ts +0 -13
  185. package/dist/commands/workspace/registration.js +0 -84
  186. package/dist/commands/workspace/selection.d.ts +0 -8
  187. package/dist/commands/workspace/selection.js +0 -104
  188. package/dist/commands/workspace/setup-prompts.d.ts +0 -13
  189. package/dist/commands/workspace/setup-prompts.js +0 -121
  190. package/dist/commands/workspace/types.d.ts +0 -103
  191. package/dist/commands/workspace/types.js +0 -36
  192. package/dist/commands/workspace.d.ts +0 -5
  193. package/dist/commands/workspace.js +0 -574
  194. package/dist/core/collections/index.d.ts +0 -3
  195. package/dist/core/collections/index.js +0 -3
  196. package/dist/core/collections/initiatives/collection.d.ts +0 -4
  197. package/dist/core/collections/initiatives/collection.js +0 -17
  198. package/dist/core/collections/initiatives/index.d.ts +0 -6
  199. package/dist/core/collections/initiatives/index.js +0 -6
  200. package/dist/core/collections/initiatives/operations.d.ts +0 -49
  201. package/dist/core/collections/initiatives/operations.js +0 -175
  202. package/dist/core/collections/initiatives/resolution.d.ts +0 -87
  203. package/dist/core/collections/initiatives/resolution.js +0 -374
  204. package/dist/core/collections/initiatives/schema.d.ts +0 -41
  205. package/dist/core/collections/initiatives/schema.js +0 -134
  206. package/dist/core/collections/initiatives/templates.d.ts +0 -12
  207. package/dist/core/collections/initiatives/templates.js +0 -90
  208. package/dist/core/collections/runtime.d.ts +0 -46
  209. package/dist/core/collections/runtime.js +0 -194
  210. package/dist/core/context-store/binding.d.ts +0 -53
  211. package/dist/core/context-store/binding.js +0 -197
  212. package/dist/core/context-store/errors.d.ts +0 -20
  213. package/dist/core/context-store/foundation.d.ts +0 -55
  214. package/dist/core/context-store/foundation.js +0 -321
  215. package/dist/core/context-store/operations.d.ts +0 -85
  216. package/dist/core/context-store/operations.js +0 -528
  217. package/dist/core/context-store/registry.d.ts +0 -45
  218. package/dist/core/context-store/registry.js +0 -229
  219. package/dist/core/workspace/foundation.d.ts +0 -67
  220. package/dist/core/workspace/foundation.js +0 -295
  221. package/dist/core/workspace/index.d.ts +0 -8
  222. package/dist/core/workspace/index.js +0 -8
  223. package/dist/core/workspace/legacy-state.d.ts +0 -28
  224. package/dist/core/workspace/legacy-state.js +0 -200
  225. package/dist/core/workspace/link-input.d.ts +0 -9
  226. package/dist/core/workspace/link-input.js +0 -32
  227. package/dist/core/workspace/open-surface.d.ts +0 -45
  228. package/dist/core/workspace/open-surface.js +0 -215
  229. package/dist/core/workspace/openers.d.ts +0 -21
  230. package/dist/core/workspace/openers.js +0 -124
  231. package/dist/core/workspace/registry.d.ts +0 -24
  232. package/dist/core/workspace/registry.js +0 -146
  233. package/dist/core/workspace/skills.d.ts +0 -57
  234. package/dist/core/workspace/skills.js +0 -334
  235. package/dist/core/workspace/state-io.d.ts +0 -10
  236. package/dist/core/workspace/state-io.js +0 -121
  237. package/schemas/workspace-planning/schema.yaml +0 -72
  238. package/schemas/workspace-planning/templates/design.md +0 -33
  239. package/schemas/workspace-planning/templates/proposal.md +0 -28
  240. package/schemas/workspace-planning/templates/spec.md +0 -9
  241. package/schemas/workspace-planning/templates/tasks.md +0 -15
@@ -1,3 +1,4 @@
1
+ import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
1
2
  export function getSyncSpecsSkillTemplate() {
2
3
  return {
3
4
  name: 'openspec-sync-specs',
@@ -6,6 +7,8 @@ export function getSyncSpecsSkillTemplate() {
6
7
 
7
8
  This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
8
9
 
10
+ ${STORE_SELECTION_GUIDANCE}
11
+
9
12
  **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
10
13
 
11
14
  **Steps**
@@ -25,8 +28,6 @@ This is an **agent-driven** operation - you will read delta specs and directly e
25
28
  openspec status --change "<name>" --json
26
29
  \`\`\`
27
30
 
28
- If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace spec sync is not supported in this slice and STOP. Do not fall back to repo-local paths or edit linked repos.
29
-
30
31
  3. **Find delta specs**
31
32
 
32
33
  Use \`artifactPaths.specs.existingOutputPaths\` from the status JSON as the list of delta spec files.
@@ -153,6 +154,8 @@ export function getOpsxSyncCommandTemplate() {
153
154
 
154
155
  This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
155
156
 
157
+ ${STORE_SELECTION_GUIDANCE}
158
+
156
159
  **Input**: Optionally specify a change name after \`/opsx:sync\` (e.g., \`/opsx:sync add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
157
160
 
158
161
  **Steps**
@@ -172,8 +175,6 @@ This is an **agent-driven** operation - you will read delta specs and directly e
172
175
  openspec status --change "<name>" --json
173
176
  \`\`\`
174
177
 
175
- If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace spec sync is not supported in this slice and STOP. Do not fall back to repo-local paths or edit linked repos.
176
-
177
178
  3. **Find delta specs**
178
179
 
179
180
  Use \`artifactPaths.specs.existingOutputPaths\` from the status JSON as the list of delta spec files.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Skill Template Workflow Modules
3
+ *
4
+ * This file is generated by splitting the legacy monolithic
5
+ * templates file into workflow-focused modules.
6
+ */
7
+ import type { SkillTemplate, CommandTemplate } from '../types.js';
8
+ export declare function getUpdateChangeSkillTemplate(): SkillTemplate;
9
+ export declare function getOpsxUpdateCommandTemplate(): CommandTemplate;
10
+ //# sourceMappingURL=update-change.d.ts.map
@@ -0,0 +1,167 @@
1
+ import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
2
+ export function getUpdateChangeSkillTemplate() {
3
+ return {
4
+ name: 'openspec-update-change',
5
+ description: "Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.",
6
+ instructions: `Revise a change's existing planning artifacts and keep them coherent. Never edit code.
7
+
8
+ ${STORE_SELECTION_GUIDANCE}
9
+
10
+ **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
11
+
12
+ **Steps**
13
+
14
+ 1. **If no change name provided, prompt for selection**
15
+
16
+ Run \`openspec list --json\` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.
17
+
18
+ Present the top 3-4 most recently modified changes as options, showing:
19
+ - Change name
20
+ - Schema (from \`schema\` field if present, otherwise "spec-driven")
21
+ - Status (e.g., "0/5 tasks", "complete", "no tasks")
22
+ - How recently it was modified (from \`lastModified\` field)
23
+
24
+ Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to update.
25
+
26
+ **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
27
+
28
+ 2. **Get the change's artifacts**
29
+ \`\`\`bash
30
+ openspec status --change "<name>" --json
31
+ \`\`\`
32
+ Parse the JSON to understand current state. The response includes:
33
+ - \`schemaName\`: The workflow schema being used (e.g., "spec-driven")
34
+ - \`artifacts\`: Array of artifacts with their status ("done", "ready", "blocked")
35
+ - \`isComplete\`: Boolean indicating if all artifacts are complete
36
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
37
+
38
+ The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.
39
+
40
+ The files to edit are \`artifactPaths.<id>.existingOutputPaths\` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. \`specs/**/*.md\`). Do NOT write to \`resolvedOutputPath\`: for a glob artifact it is still the glob pattern, not a real file.
41
+
42
+ 3. **Understand the request**
43
+ - If the user asked for a specific revision ("the design now uses X"), that is the starting edit.
44
+ - If they only said "update" / "make this coherent", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.
45
+
46
+ 4. **Read and reconcile**
47
+ - Read the artifact(s) the request touches and the change's other existing artifacts.
48
+ - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
49
+ - Note everything that is now inconsistent, missing, or contradictory.
50
+ - Revise only files that already exist (\`existingOutputPaths\`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to \`/opsx:continue\` to create them.
51
+ - If the change is already coherent, say so and make no edits.
52
+
53
+ 5. **Confirm and apply, one artifact at a time**
54
+ - Show each proposed revision and why. Write only after the user confirms.
55
+ - If the user rejects a revision, do not write it - leave that artifact unchanged.
56
+ - When a substantial rewrite is needed, get that artifact's rules and template first:
57
+ \`\`\`bash
58
+ openspec instructions <artifact-id> --change "<name>" --json
59
+ \`\`\`
60
+
61
+ 6. **Point to the next step (guidance only - NEVER act on it)**
62
+ - Artifacts still missing -> suggest \`/opsx:continue\` to create them.
63
+ - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest \`/opsx:apply\` to carry the delta into code.
64
+ - Everything done and implemented -> suggest \`/opsx:archive\`.
65
+
66
+ **Output**
67
+
68
+ After each invocation, show:
69
+ - Which artifacts were revised (and which proposed revisions were rejected)
70
+ - Anything deferred to \`/opsx:continue\` (not-yet-created artifacts or files)
71
+ - Where the change stands and the recommended next command
72
+
73
+ **Guardrails**
74
+ - Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to \`/opsx:apply\`.
75
+ - Use the artifact ids and paths reported by \`openspec status\`; never branch on hardcoded artifact names.
76
+ - Edit only the concrete files in \`existingOutputPaths\`; never write to a glob \`resolvedOutputPath\`.
77
+ - Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is \`/opsx:continue\`'s job.
78
+ - Confirm every edit with the user before writing.
79
+ - If the request changes the change's *intent* rather than refining it, recommend starting fresh with \`/opsx:new\` (the "Update vs. Start Fresh" heuristic).`,
80
+ license: 'MIT',
81
+ compatibility: 'Requires openspec CLI.',
82
+ metadata: { author: 'openspec', version: '1.0' },
83
+ };
84
+ }
85
+ export function getOpsxUpdateCommandTemplate() {
86
+ return {
87
+ name: 'OPSX: Update',
88
+ description: "Update a change - revise existing planning artifacts and keep them coherent (Experimental)",
89
+ category: 'Workflow',
90
+ tags: ['workflow', 'artifacts', 'experimental'],
91
+ content: `Revise a change's existing planning artifacts and keep them coherent. Never edit code.
92
+
93
+ ${STORE_SELECTION_GUIDANCE}
94
+
95
+ **Input**: Optionally specify a change name after \`/opsx:update\` (e.g., \`/opsx:update add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
96
+
97
+ **Steps**
98
+
99
+ 1. **If no change name provided, prompt for selection**
100
+
101
+ Run \`openspec list --json\` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.
102
+
103
+ Present the top 3-4 most recently modified changes as options, showing:
104
+ - Change name
105
+ - Schema (from \`schema\` field if present, otherwise "spec-driven")
106
+ - Status (e.g., "0/5 tasks", "complete", "no tasks")
107
+ - How recently it was modified (from \`lastModified\` field)
108
+
109
+ Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to update.
110
+
111
+ **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
112
+
113
+ 2. **Get the change's artifacts**
114
+ \`\`\`bash
115
+ openspec status --change "<name>" --json
116
+ \`\`\`
117
+ Parse the JSON to understand current state. The response includes:
118
+ - \`schemaName\`: The workflow schema being used (e.g., "spec-driven")
119
+ - \`artifacts\`: Array of artifacts with their status ("done", "ready", "blocked")
120
+ - \`isComplete\`: Boolean indicating if all artifacts are complete
121
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
122
+
123
+ The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.
124
+
125
+ The files to edit are \`artifactPaths.<id>.existingOutputPaths\` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. \`specs/**/*.md\`). Do NOT write to \`resolvedOutputPath\`: for a glob artifact it is still the glob pattern, not a real file.
126
+
127
+ 3. **Understand the request**
128
+ - If the user asked for a specific revision ("the design now uses X"), that is the starting edit.
129
+ - If they only said "update" / "make this coherent", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.
130
+
131
+ 4. **Read and reconcile**
132
+ - Read the artifact(s) the request touches and the change's other existing artifacts.
133
+ - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
134
+ - Note everything that is now inconsistent, missing, or contradictory.
135
+ - Revise only files that already exist (\`existingOutputPaths\`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to \`/opsx:continue\` to create them.
136
+ - If the change is already coherent, say so and make no edits.
137
+
138
+ 5. **Confirm and apply, one artifact at a time**
139
+ - Show each proposed revision and why. Write only after the user confirms.
140
+ - If the user rejects a revision, do not write it - leave that artifact unchanged.
141
+ - When a substantial rewrite is needed, get that artifact's rules and template first:
142
+ \`\`\`bash
143
+ openspec instructions <artifact-id> --change "<name>" --json
144
+ \`\`\`
145
+
146
+ 6. **Point to the next step (guidance only - NEVER act on it)**
147
+ - Artifacts still missing -> suggest \`/opsx:continue\` to create them.
148
+ - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest \`/opsx:apply\` to carry the delta into code.
149
+ - Everything done and implemented -> suggest \`/opsx:archive\`.
150
+
151
+ **Output**
152
+
153
+ After each invocation, show:
154
+ - Which artifacts were revised (and which proposed revisions were rejected)
155
+ - Anything deferred to \`/opsx:continue\` (not-yet-created artifacts or files)
156
+ - Where the change stands and the recommended next command
157
+
158
+ **Guardrails**
159
+ - Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to \`/opsx:apply\`.
160
+ - Use the artifact ids and paths reported by \`openspec status\`; never branch on hardcoded artifact names.
161
+ - Edit only the concrete files in \`existingOutputPaths\`; never write to a glob \`resolvedOutputPath\`.
162
+ - Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is \`/opsx:continue\`'s job.
163
+ - Confirm every edit with the user before writing.
164
+ - If the request changes the change's *intent* rather than refining it, recommend starting fresh with \`/opsx:new\` (the "Update vs. Start Fresh" heuristic).`
165
+ };
166
+ }
167
+ //# sourceMappingURL=update-change.js.map
@@ -1,9 +1,12 @@
1
+ import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
1
2
  export function getVerifyChangeSkillTemplate() {
2
3
  return {
3
4
  name: 'openspec-verify-change',
4
5
  description: 'Verify implementation matches change artifacts. Use when the user wants to validate that implementation is complete, correct, and coherent before archiving.',
5
6
  instructions: `Verify that an implementation matches the change artifacts (specs, tasks, design).
6
7
 
8
+ ${STORE_SELECTION_GUIDANCE}
9
+
7
10
  **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
8
11
 
9
12
  **Steps**
@@ -27,8 +30,6 @@ export function getVerifyChangeSkillTemplate() {
27
30
  - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
28
31
  - Which artifacts exist for this change
29
32
 
30
- If status reports \`actionContext.mode: "workspace-planning"\`, explain that full workspace implementation verification is not supported in this slice and STOP. Do not infer repo-local implementation ownership or edit linked repos.
31
-
32
33
  3. **Get planning context and load artifacts**
33
34
 
34
35
  \`\`\`bash
@@ -175,6 +176,8 @@ export function getOpsxVerifyCommandTemplate() {
175
176
  tags: ['workflow', 'verify', 'experimental'],
176
177
  content: `Verify that an implementation matches the change artifacts (specs, tasks, design).
177
178
 
179
+ ${STORE_SELECTION_GUIDANCE}
180
+
178
181
  **Input**: Optionally specify a change name after \`/opsx:verify\` (e.g., \`/opsx:verify add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
179
182
 
180
183
  **Steps**
@@ -198,8 +201,6 @@ export function getOpsxVerifyCommandTemplate() {
198
201
  - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
199
202
  - Which artifacts exist for this change
200
203
 
201
- If status reports \`actionContext.mode: "workspace-planning"\`, explain that full workspace implementation verification is not supported in this slice and STOP. Do not infer repo-local implementation ownership or edit linked repos.
202
-
203
204
  3. **Get planning context and load artifacts**
204
205
 
205
206
  \`\`\`bash
@@ -130,8 +130,8 @@ export class UpdateCommand {
130
130
  for (const { template, dirName } of skillTemplates) {
131
131
  const skillDir = path.join(skillsDir, dirName);
132
132
  const skillFile = path.join(skillDir, 'SKILL.md');
133
- // Use hyphen-based command references for OpenCode
134
- const transformer = (tool.value === 'opencode' || tool.value === 'pi') ? transformToHyphenCommands : undefined;
133
+ // Use hyphen-based command references for tools where filename === command name (oh-my-pi, opencode, pi)
134
+ const transformer = (tool.value === 'opencode' || tool.value === 'pi' || tool.value === 'oh-my-pi') ? transformToHyphenCommands : undefined;
135
135
  const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
136
136
  await FileSystemUtils.writeFile(skillFile, skillContent);
137
137
  }
@@ -523,8 +523,8 @@ export class UpdateCommand {
523
523
  for (const { template, dirName } of skillTemplates) {
524
524
  const skillDir = path.join(skillsDir, dirName);
525
525
  const skillFile = path.join(skillDir, 'SKILL.md');
526
- // Use hyphen-based command references for OpenCode
527
- const transformer = (tool.value === 'opencode' || tool.value === 'pi') ? transformToHyphenCommands : undefined;
526
+ // Use hyphen-based command references for tools where filename === command name (oh-my-pi, opencode, pi)
527
+ const transformer = (tool.value === 'opencode' || tool.value === 'pi' || tool.value === 'oh-my-pi') ? transformToHyphenCommands : undefined;
528
528
  const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
529
529
  await FileSystemUtils.writeFile(skillFile, skillContent);
530
530
  }
@@ -18,6 +18,13 @@ export declare class Validator {
18
18
  * - No duplicates within sections; no cross-section conflicts per spec
19
19
  */
20
20
  validateChangeDeltaSpecs(changeDir: string): Promise<ValidationReport>;
21
+ /**
22
+ * Recursively collect every delta `spec.md` under a change's specs directory,
23
+ * so both the one-level (specs/<capability>/spec.md) and nested multi-area
24
+ * (specs/<area>/<capability>/spec.md) layouts are discovered (#1182b).
25
+ * Returns absolute paths, sorted for deterministic issue ordering.
26
+ */
27
+ private findDeltaSpecFiles;
21
28
  private convertZodErrors;
22
29
  private applySpecRules;
23
30
  private applyChangeRules;
@@ -4,7 +4,8 @@ import { SpecSchema, ChangeSchema } from '../schemas/index.js';
4
4
  import { MarkdownParser } from '../parsers/markdown-parser.js';
5
5
  import { ChangeParser } from '../parsers/change-parser.js';
6
6
  import { MIN_PURPOSE_LENGTH, MAX_REQUIREMENT_TEXT_LENGTH, VALIDATION_MESSAGES } from './constants.js';
7
- import { parseDeltaSpec, normalizeRequirementName } from '../parsers/requirement-blocks.js';
7
+ import { parseDeltaSpec, normalizeRequirementName, extractRequirementsSection } from '../parsers/requirement-blocks.js';
8
+ import { extractRequirementBody as extractRequirementBodyShared, containsShallOrMust as containsShallOrMustShared, countScenarios as countScenariosShared, } from '../parsers/requirement-text.js';
8
9
  import { findMainSpecStructureIssues } from '../parsers/spec-structure.js';
9
10
  import { FileSystemUtils } from '../../utils/file-system.js';
10
11
  export class Validator {
@@ -98,12 +99,12 @@ export class Validator {
98
99
  const missingHeaderSpecs = [];
99
100
  const emptySectionSpecs = [];
100
101
  try {
101
- const entries = await fs.readdir(specsDir, { withFileTypes: true });
102
- for (const entry of entries) {
103
- if (!entry.isDirectory())
104
- continue;
105
- const specName = entry.name;
106
- const specFile = path.join(specsDir, specName, 'spec.md');
102
+ // Discover delta specs at any depth so the nested multi-area layout
103
+ // (specs/<area>/<capability>/spec.md) is validated, not just the
104
+ // one-level specs/<capability>/spec.md layout (#1182b). The spec-driven
105
+ // specs glob is specs/**/*.md; delta files are always named spec.md.
106
+ const specFiles = await this.findDeltaSpecFiles(specsDir);
107
+ for (const specFile of specFiles) {
107
108
  let content;
108
109
  try {
109
110
  content = await fs.readFile(specFile, 'utf-8');
@@ -112,7 +113,24 @@ export class Validator {
112
113
  continue;
113
114
  }
114
115
  const plan = parseDeltaSpec(content);
115
- const entryPath = `${specName}/spec.md`;
116
+ const entryPath = FileSystemUtils.toPosixPath(path.relative(specsDir, specFile));
117
+ // Surface (as INFO, never a failure) the non-canonical level-3 headers
118
+ // the delta reader skipped while parsing ADDED/MODIFIED sections —
119
+ // without this note a stray divider like "### Documentation
120
+ // Requirements" would pass validate <change> while failing
121
+ // archive/validate <spec>. The list comes from the parse itself, so it
122
+ // reflects exactly what the reader skipped.
123
+ for (const stray of plan.skippedHeaders) {
124
+ const nameless = /^requirement:?$/i.test(stray.header);
125
+ issues.push({
126
+ level: 'INFO',
127
+ path: entryPath,
128
+ line: stray.line,
129
+ message: nameless
130
+ ? `Header "### ${stray.header}" in ${stray.section} is missing a requirement name and is ignored by validation. Add a name, e.g. "### Requirement: <name>".`
131
+ : `Header "### ${stray.header}" in ${stray.section} is not a "### Requirement:" header and is ignored by validation. Use "### Requirement: ${stray.header}" if it should be validated as a requirement.`,
132
+ });
133
+ }
116
134
  const sectionNames = [];
117
135
  if (plan.sectionPresence.added)
118
136
  sectionNames.push('## ADDED Requirements');
@@ -147,10 +165,16 @@ export class Validator {
147
165
  }
148
166
  const requirementText = this.extractRequirementText(block.raw);
149
167
  if (!requirementText) {
150
- issues.push({ level: 'ERROR', path: entryPath, message: `ADDED "${block.name}" is missing requirement text` });
168
+ issues.push({
169
+ level: 'ERROR',
170
+ path: entryPath,
171
+ message: this.containsShallOrMust(block.name)
172
+ ? this.buildMissingShallOrMustMessage(`ADDED "${block.name}"`, block.name)
173
+ : `ADDED "${block.name}" is missing requirement text`,
174
+ });
151
175
  }
152
176
  else if (!this.containsShallOrMust(requirementText)) {
153
- issues.push({ level: 'ERROR', path: entryPath, message: this.buildMissingShallOrMustMessage('ADDED', block.name) });
177
+ issues.push({ level: 'ERROR', path: entryPath, message: this.buildMissingShallOrMustMessage(`ADDED "${block.name}"`, block.name) });
154
178
  }
155
179
  const scenarioCount = this.countScenarios(block.raw);
156
180
  if (scenarioCount < 1) {
@@ -169,10 +193,16 @@ export class Validator {
169
193
  }
170
194
  const requirementText = this.extractRequirementText(block.raw);
171
195
  if (!requirementText) {
172
- issues.push({ level: 'ERROR', path: entryPath, message: `MODIFIED "${block.name}" is missing requirement text` });
196
+ issues.push({
197
+ level: 'ERROR',
198
+ path: entryPath,
199
+ message: this.containsShallOrMust(block.name)
200
+ ? this.buildMissingShallOrMustMessage(`MODIFIED "${block.name}"`, block.name)
201
+ : `MODIFIED "${block.name}" is missing requirement text`,
202
+ });
173
203
  }
174
204
  else if (!this.containsShallOrMust(requirementText)) {
175
- issues.push({ level: 'ERROR', path: entryPath, message: this.buildMissingShallOrMustMessage('MODIFIED', block.name) });
205
+ issues.push({ level: 'ERROR', path: entryPath, message: this.buildMissingShallOrMustMessage(`MODIFIED "${block.name}"`, block.name) });
176
206
  }
177
207
  const scenarioCount = this.countScenarios(block.raw);
178
208
  if (scenarioCount < 1) {
@@ -256,6 +286,35 @@ export class Validator {
256
286
  }
257
287
  return this.createReport(issues);
258
288
  }
289
+ /**
290
+ * Recursively collect every delta `spec.md` under a change's specs directory,
291
+ * so both the one-level (specs/<capability>/spec.md) and nested multi-area
292
+ * (specs/<area>/<capability>/spec.md) layouts are discovered (#1182b).
293
+ * Returns absolute paths, sorted for deterministic issue ordering.
294
+ */
295
+ async findDeltaSpecFiles(specsDir) {
296
+ const results = [];
297
+ const walk = async (dir) => {
298
+ let entries;
299
+ try {
300
+ entries = await fs.readdir(dir, { withFileTypes: true });
301
+ }
302
+ catch {
303
+ return;
304
+ }
305
+ for (const entry of entries) {
306
+ const full = path.join(dir, entry.name);
307
+ if (entry.isDirectory()) {
308
+ await walk(full);
309
+ }
310
+ else if (entry.isFile() && entry.name === 'spec.md') {
311
+ results.push(full);
312
+ }
313
+ }
314
+ };
315
+ await walk(specsDir);
316
+ return results.sort();
317
+ }
259
318
  convertZodErrors(error) {
260
319
  return error.issues.map(err => {
261
320
  let message = err.message;
@@ -302,6 +361,23 @@ export class Validator {
302
361
  });
303
362
  }
304
363
  });
364
+ // SHALL/MUST body-keyword enforcement for main specs (#1156). The main-spec
365
+ // parser collapses the requirement header into `text`, so we recover the
366
+ // header+body pairs here (the same source the delta path trusts) and reuse
367
+ // the delta detection: a body that omits the keyword errors, with the
368
+ // targeted "move it to the body line" hint when the keyword is in the header
369
+ // only and the generic message otherwise. Emitted exactly once per
370
+ // requirement (the Zod refine that used to emit a generic error is removed).
371
+ extractRequirementsSection(content).bodyBlocks.forEach((block, index) => {
372
+ const requirementText = this.extractRequirementText(block.raw);
373
+ if (!requirementText || !this.containsShallOrMust(requirementText)) {
374
+ issues.push({
375
+ level: 'ERROR',
376
+ path: `requirements[${index}]`,
377
+ message: this.buildMissingShallOrMustMessage(`Requirement "${block.name}"`, block.name),
378
+ });
379
+ }
380
+ });
305
381
  return issues;
306
382
  }
307
383
  applyChangeRules(change, content) {
@@ -376,30 +452,16 @@ export class Validator {
376
452
  return report.valid;
377
453
  }
378
454
  extractRequirementText(blockRaw) {
379
- const lines = blockRaw.split('\n');
380
- // Skip header line (index 0)
381
- let i = 1;
382
- // Find the first substantial text line, skipping metadata and blank lines
383
- for (; i < lines.length; i++) {
384
- const line = lines[i];
385
- // Stop at scenario headers
386
- if (/^####\s+/.test(line))
387
- break;
388
- const trimmed = line.trim();
389
- // Skip blank lines
390
- if (trimmed.length === 0)
391
- continue;
392
- // Skip metadata lines (lines starting with ** like **ID**, **Priority**, etc.)
393
- if (/^\*\*[^*]+\*\*:/.test(trimmed))
394
- continue;
395
- // Found first non-metadata, non-blank line - this is the requirement text
396
- return trimmed;
397
- }
398
- // No requirement text found
399
- return undefined;
455
+ // Delegate to the shared, fence-/metadata-/multi-line-aware body reader.
456
+ // Validation intentionally does not use the parser/display header-title
457
+ // fallback for canonical `### Requirement:` blocks: #1280 requires a
458
+ // SHALL/MUST that appears only in the header to receive the body-keyword
459
+ // hint. Line 0 is the `### Requirement: ...` header.
460
+ const [, ...bodyLines] = blockRaw.split('\n');
461
+ return extractRequirementBodyShared(bodyLines) || undefined;
400
462
  }
401
463
  containsShallOrMust(text) {
402
- return /\b(SHALL|MUST)\b/.test(text);
464
+ return containsShallOrMustShared(text);
403
465
  }
404
466
  /**
405
467
  * Build an error message for a requirement block whose body lacks SHALL/MUST.
@@ -411,16 +473,17 @@ export class Validator {
411
473
  * on the requirement body line (the line right after the header), so we point
412
474
  * the author at that exact fix when the keyword is found in the header only.
413
475
  */
414
- buildMissingShallOrMustMessage(action, blockName) {
415
- const base = `${action} "${blockName}" must contain SHALL or MUST`;
476
+ buildMissingShallOrMustMessage(prefix, blockName) {
477
+ const base = `${prefix} must contain SHALL or MUST`;
416
478
  if (this.containsShallOrMust(blockName)) {
417
479
  return `${base} in the requirement body, not only in the header. Move the SHALL/MUST statement to the line immediately after the "### Requirement: ..." header.`;
418
480
  }
419
481
  return base;
420
482
  }
421
483
  countScenarios(blockRaw) {
422
- const matches = blockRaw.match(/^####\s+/gm);
423
- return matches ? matches.length : 0;
484
+ // Fence-aware count via the shared reader: a `#### Scenario:` inside a fenced
485
+ // example is not a real scenario. Drop the header line (index 0).
486
+ return countScenariosShared(blockRaw.split('\n').slice(1));
424
487
  }
425
488
  formatSectionList(sections) {
426
489
  if (sections.length === 0)
package/dist/core/view.js CHANGED
@@ -70,7 +70,7 @@ export class ViewCommand {
70
70
  const entries = fs.readdirSync(changesDir, { withFileTypes: true });
71
71
  for (const entry of entries) {
72
72
  if (entry.isDirectory() && entry.name !== 'archive') {
73
- const progress = await getTaskProgressForChange(changesDir, entry.name);
73
+ const progress = await getTaskProgressForChange(changesDir, entry.name, path.dirname(openspecDir));
74
74
  if (progress.total === 0) {
75
75
  // No tasks defined yet - still in planning/draft phase
76
76
  draft.push({ name: entry.name });
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Working-set assembly (slice 4.1): the full set a root's declarations
3
+ * describe — the OpenSpec root and its referenced stores — as an
4
+ * agent-consumable brief. A local convenience
5
+ * computed from declared relationships, never a planning system; no
6
+ * clone/sync/launch machinery. Unresolvable members are reported, not
7
+ * guessed.
8
+ */
9
+ import type { StoreDiagnostic } from './store/errors.js';
10
+ import { type ReferenceIndexEntry } from './references.js';
11
+ import { type ResolvedOpenSpecRoot } from './root-selection.js';
12
+ export type WorkingSetRole = 'referenced_store';
13
+ export interface WorkingSetMember {
14
+ role: WorkingSetRole;
15
+ id: string;
16
+ path?: string;
17
+ remote?: string;
18
+ fetch?: string;
19
+ status: StoreDiagnostic[];
20
+ }
21
+ export interface WorkingSet {
22
+ root: {
23
+ path: string;
24
+ source: ResolvedOpenSpecRoot['source'];
25
+ store_id?: string;
26
+ role: 'openspec_root';
27
+ };
28
+ members: WorkingSetMember[];
29
+ status: StoreDiagnostic[];
30
+ }
31
+ export interface AssembleWorkingSetInput {
32
+ root: ResolvedOpenSpecRoot;
33
+ referenceEntries: ReferenceIndexEntry[];
34
+ /** The composition's top-level status; the working set keeps only
35
+ * the registry-unreadable degradation (selected by code, never by
36
+ * position). */
37
+ topLevelStatus?: StoreDiagnostic[];
38
+ }
39
+ /** AVAILABLE = path present AND per-entry status empty. */
40
+ export declare function isAvailableMember(member: WorkingSetMember): boolean;
41
+ export declare function assembleWorkingSet(input: AssembleWorkingSetInput): WorkingSet;
42
+ /**
43
+ * Pure builder for the `.code-workspace` editor view — one consumer of
44
+ * assembly, not the feature. Available members only.
45
+ */
46
+ export declare function buildCodeWorkspaceJson(workingSet: WorkingSet, rootName: string): string;
47
+ //# sourceMappingURL=working-set.d.ts.map
@@ -0,0 +1,43 @@
1
+ import { fetchRecipe } from './references.js';
2
+ import { toRootOutput } from './root-selection.js';
3
+ /** AVAILABLE = path present AND per-entry status empty. */
4
+ export function isAvailableMember(member) {
5
+ return member.path !== undefined && member.status.length === 0;
6
+ }
7
+ export function assembleWorkingSet(input) {
8
+ const members = [];
9
+ for (const entry of input.referenceEntries) {
10
+ members.push({
11
+ role: 'referenced_store',
12
+ id: entry.store_id,
13
+ ...(entry.root !== undefined ? { path: entry.root } : {}),
14
+ ...(entry.root !== undefined && entry.status.length === 0
15
+ ? { fetch: fetchRecipe(entry.store_id) }
16
+ : {}),
17
+ status: entry.status,
18
+ });
19
+ }
20
+ const status = (input.topLevelStatus ?? []).filter((entry) => entry.code === 'relationship_registry_unreadable');
21
+ return {
22
+ root: { ...toRootOutput(input.root), role: 'openspec_root' },
23
+ members,
24
+ status,
25
+ };
26
+ }
27
+ /**
28
+ * Pure builder for the `.code-workspace` editor view — one consumer of
29
+ * assembly, not the feature. Available members only.
30
+ */
31
+ export function buildCodeWorkspaceJson(workingSet, rootName) {
32
+ const folders = [
33
+ { name: rootName, path: workingSet.root.path },
34
+ ];
35
+ for (const member of workingSet.members) {
36
+ if (!isAvailableMember(member)) {
37
+ continue;
38
+ }
39
+ folders.push({ name: `ref:${member.id}`, path: member.path });
40
+ }
41
+ return JSON.stringify({ folders }, null, 2) + '\n';
42
+ }
43
+ //# sourceMappingURL=working-set.js.map