@fission-ai/openspec 1.3.0 → 1.4.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 (179) hide show
  1. package/README.md +9 -6
  2. package/bin/openspec.js +3 -1
  3. package/dist/cli/index.d.ts +4 -1
  4. package/dist/cli/index.js +46 -2
  5. package/dist/commands/completion.js +7 -0
  6. package/dist/commands/config.js +64 -5
  7. package/dist/commands/context-store.d.ts +3 -0
  8. package/dist/commands/context-store.js +475 -0
  9. package/dist/commands/initiative.d.ts +13 -0
  10. package/dist/commands/initiative.js +318 -0
  11. package/dist/commands/workflow/index.d.ts +2 -0
  12. package/dist/commands/workflow/index.js +1 -0
  13. package/dist/commands/workflow/initiative-link.d.ts +24 -0
  14. package/dist/commands/workflow/initiative-link.js +47 -0
  15. package/dist/commands/workflow/instructions.d.ts +1 -1
  16. package/dist/commands/workflow/instructions.js +46 -83
  17. package/dist/commands/workflow/new-change.d.ts +6 -0
  18. package/dist/commands/workflow/new-change.js +114 -17
  19. package/dist/commands/workflow/set-change.d.ts +13 -0
  20. package/dist/commands/workflow/set-change.js +87 -0
  21. package/dist/commands/workflow/shared.d.ts +5 -3
  22. package/dist/commands/workflow/shared.js +6 -6
  23. package/dist/commands/workflow/status.js +24 -9
  24. package/dist/commands/workflow/templates.js +5 -4
  25. package/dist/commands/workspace/context-status.d.ts +4 -0
  26. package/dist/commands/workspace/context-status.js +59 -0
  27. package/dist/commands/workspace/open-target-selection.d.ts +13 -0
  28. package/dist/commands/workspace/open-target-selection.js +146 -0
  29. package/dist/commands/workspace/open-view.d.ts +62 -0
  30. package/dist/commands/workspace/open-view.js +249 -0
  31. package/dist/commands/workspace/open.d.ts +37 -0
  32. package/dist/commands/workspace/open.js +110 -0
  33. package/dist/commands/workspace/opener-selection.d.ts +11 -0
  34. package/dist/commands/workspace/opener-selection.js +98 -0
  35. package/dist/commands/workspace/operations.d.ts +29 -0
  36. package/dist/commands/workspace/operations.js +543 -0
  37. package/dist/commands/workspace/prompt-theme.d.ts +29 -0
  38. package/dist/commands/workspace/prompt-theme.js +24 -0
  39. package/dist/commands/workspace/registration.d.ts +13 -0
  40. package/dist/commands/workspace/registration.js +84 -0
  41. package/dist/commands/workspace/selection.d.ts +9 -0
  42. package/dist/commands/workspace/selection.js +115 -0
  43. package/dist/commands/workspace/setup-prompts.d.ts +13 -0
  44. package/dist/commands/workspace/setup-prompts.js +121 -0
  45. package/dist/commands/workspace/types.d.ts +103 -0
  46. package/dist/commands/workspace/types.js +36 -0
  47. package/dist/commands/workspace.d.ts +6 -0
  48. package/dist/commands/workspace.js +587 -0
  49. package/dist/core/artifact-graph/index.d.ts +3 -1
  50. package/dist/core/artifact-graph/index.js +1 -0
  51. package/dist/core/artifact-graph/instruction-loader.d.ts +41 -1
  52. package/dist/core/artifact-graph/instruction-loader.js +51 -9
  53. package/dist/core/artifact-graph/outputs.d.ts +14 -0
  54. package/dist/core/artifact-graph/outputs.js +39 -0
  55. package/dist/core/artifact-graph/state.js +2 -25
  56. package/dist/core/artifact-graph/types.d.ts +0 -5
  57. package/dist/core/artifact-graph/types.js +0 -14
  58. package/dist/core/change-metadata/index.d.ts +2 -0
  59. package/dist/core/change-metadata/index.js +2 -0
  60. package/dist/core/change-metadata/schema.d.ts +18 -0
  61. package/dist/core/change-metadata/schema.js +28 -0
  62. package/dist/core/change-status-policy.d.ts +50 -0
  63. package/dist/core/change-status-policy.js +70 -0
  64. package/dist/core/collections/index.d.ts +3 -0
  65. package/dist/core/collections/index.js +3 -0
  66. package/dist/core/collections/initiatives/collection.d.ts +4 -0
  67. package/dist/core/collections/initiatives/collection.js +17 -0
  68. package/dist/core/collections/initiatives/index.d.ts +6 -0
  69. package/dist/core/collections/initiatives/index.js +6 -0
  70. package/dist/core/collections/initiatives/operations.d.ts +49 -0
  71. package/dist/core/collections/initiatives/operations.js +175 -0
  72. package/dist/core/collections/initiatives/resolution.d.ts +87 -0
  73. package/dist/core/collections/initiatives/resolution.js +374 -0
  74. package/dist/core/collections/initiatives/schema.d.ts +41 -0
  75. package/dist/core/collections/initiatives/schema.js +134 -0
  76. package/dist/core/collections/initiatives/templates.d.ts +12 -0
  77. package/dist/core/collections/initiatives/templates.js +90 -0
  78. package/dist/core/collections/runtime.d.ts +46 -0
  79. package/dist/core/collections/runtime.js +194 -0
  80. package/dist/core/completions/command-registry.d.ts +1 -5
  81. package/dist/core/completions/command-registry.js +532 -32
  82. package/dist/core/completions/completion-provider.d.ts +11 -0
  83. package/dist/core/completions/completion-provider.js +27 -0
  84. package/dist/core/completions/generators/bash-generator.d.ts +3 -0
  85. package/dist/core/completions/generators/bash-generator.js +60 -4
  86. package/dist/core/completions/generators/fish-generator.js +3 -0
  87. package/dist/core/completions/generators/powershell-generator.d.ts +3 -0
  88. package/dist/core/completions/generators/powershell-generator.js +63 -4
  89. package/dist/core/completions/generators/zsh-generator.d.ts +3 -0
  90. package/dist/core/completions/generators/zsh-generator.js +46 -22
  91. package/dist/core/completions/installers/zsh-installer.d.ts +0 -8
  92. package/dist/core/completions/installers/zsh-installer.js +3 -31
  93. package/dist/core/completions/shared-flags.d.ts +12 -0
  94. package/dist/core/completions/shared-flags.js +28 -0
  95. package/dist/core/completions/templates/bash-templates.d.ts +1 -1
  96. package/dist/core/completions/templates/bash-templates.js +6 -0
  97. package/dist/core/completions/templates/fish-templates.d.ts +1 -1
  98. package/dist/core/completions/templates/fish-templates.js +6 -0
  99. package/dist/core/completions/templates/powershell-templates.d.ts +1 -1
  100. package/dist/core/completions/templates/powershell-templates.js +9 -0
  101. package/dist/core/completions/templates/zsh-templates.d.ts +1 -1
  102. package/dist/core/completions/templates/zsh-templates.js +9 -0
  103. package/dist/core/completions/types.d.ts +23 -1
  104. package/dist/core/config.js +3 -1
  105. package/dist/core/context-store/binding.d.ts +53 -0
  106. package/dist/core/context-store/binding.js +197 -0
  107. package/dist/core/context-store/errors.d.ts +20 -0
  108. package/dist/core/context-store/errors.js +22 -0
  109. package/dist/core/context-store/foundation.d.ts +55 -0
  110. package/dist/core/context-store/foundation.js +321 -0
  111. package/dist/core/context-store/index.d.ts +6 -0
  112. package/dist/core/context-store/index.js +6 -0
  113. package/dist/core/context-store/operations.d.ts +85 -0
  114. package/dist/core/context-store/operations.js +528 -0
  115. package/dist/core/context-store/registry.d.ts +45 -0
  116. package/dist/core/context-store/registry.js +229 -0
  117. package/dist/core/global-config.d.ts +6 -1
  118. package/dist/core/global-config.js +15 -16
  119. package/dist/core/index.d.ts +5 -1
  120. package/dist/core/index.js +4 -0
  121. package/dist/core/parsers/change-parser.js +7 -3
  122. package/dist/core/parsers/markdown-parser.d.ts +4 -0
  123. package/dist/core/parsers/markdown-parser.js +41 -1
  124. package/dist/core/parsers/requirement-blocks.js +5 -5
  125. package/dist/core/parsers/spec-structure.d.ts +9 -0
  126. package/dist/core/parsers/spec-structure.js +88 -0
  127. package/dist/core/planning-home.d.ts +21 -0
  128. package/dist/core/planning-home.js +108 -0
  129. package/dist/core/profiles.d.ts +1 -1
  130. package/dist/core/profiles.js +1 -1
  131. package/dist/core/specs-apply.js +9 -1
  132. package/dist/core/templates/workflows/apply-change.js +10 -4
  133. package/dist/core/templates/workflows/archive-change.js +21 -15
  134. package/dist/core/templates/workflows/bulk-archive-change.js +14 -10
  135. package/dist/core/templates/workflows/continue-change.js +6 -4
  136. package/dist/core/templates/workflows/explore.js +8 -10
  137. package/dist/core/templates/workflows/ff-change.js +8 -6
  138. package/dist/core/templates/workflows/new-change.js +6 -6
  139. package/dist/core/templates/workflows/onboard.js +13 -15
  140. package/dist/core/templates/workflows/propose.js +8 -6
  141. package/dist/core/templates/workflows/sync-specs.js +28 -10
  142. package/dist/core/templates/workflows/verify-change.js +16 -10
  143. package/dist/core/update.d.ts +5 -0
  144. package/dist/core/update.js +20 -0
  145. package/dist/core/validation/validator.d.ts +11 -0
  146. package/dist/core/validation/validator.js +28 -2
  147. package/dist/core/workspace/foundation.d.ts +67 -0
  148. package/dist/core/workspace/foundation.js +295 -0
  149. package/dist/core/workspace/index.d.ts +8 -0
  150. package/dist/core/workspace/index.js +8 -0
  151. package/dist/core/workspace/legacy-state.d.ts +28 -0
  152. package/dist/core/workspace/legacy-state.js +200 -0
  153. package/dist/core/workspace/link-input.d.ts +9 -0
  154. package/dist/core/workspace/link-input.js +32 -0
  155. package/dist/core/workspace/open-surface.d.ts +45 -0
  156. package/dist/core/workspace/open-surface.js +215 -0
  157. package/dist/core/workspace/openers.d.ts +21 -0
  158. package/dist/core/workspace/openers.js +124 -0
  159. package/dist/core/workspace/registry.d.ts +24 -0
  160. package/dist/core/workspace/registry.js +146 -0
  161. package/dist/core/workspace/skills.d.ts +57 -0
  162. package/dist/core/workspace/skills.js +334 -0
  163. package/dist/core/workspace/state-io.d.ts +10 -0
  164. package/dist/core/workspace/state-io.js +119 -0
  165. package/dist/telemetry/config.d.ts +7 -1
  166. package/dist/telemetry/config.js +87 -19
  167. package/dist/telemetry/index.js +19 -0
  168. package/dist/utils/change-metadata.d.ts +5 -2
  169. package/dist/utils/change-metadata.js +7 -13
  170. package/dist/utils/change-utils.d.ts +9 -0
  171. package/dist/utils/change-utils.js +7 -5
  172. package/dist/utils/file-system.d.ts +5 -0
  173. package/dist/utils/file-system.js +21 -1
  174. package/package.json +2 -1
  175. package/schemas/workspace-planning/schema.yaml +72 -0
  176. package/schemas/workspace-planning/templates/design.md +33 -0
  177. package/schemas/workspace-planning/templates/proposal.md +28 -0
  178. package/schemas/workspace-planning/templates/spec.md +9 -0
  179. package/schemas/workspace-planning/templates/tasks.md +15 -0
@@ -0,0 +1,108 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { getWorkspaceChangesDir, readWorkspaceViewStateSync, workspaceStateFileExistsSync, } from './workspace/index.js';
4
+ import { FileSystemUtils } from '../utils/file-system.js';
5
+ const REPO_DEFAULT_SCHEMA = 'spec-driven';
6
+ const WORKSPACE_DEFAULT_SCHEMA = 'workspace-planning';
7
+ function pathExistsAsDirectory(candidatePath) {
8
+ try {
9
+ return fs.statSync(candidatePath).isDirectory();
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ function getSearchStartDirectory(startPath) {
16
+ const resolved = path.resolve(startPath);
17
+ try {
18
+ const stats = fs.statSync(resolved);
19
+ const searchStart = stats.isDirectory() ? resolved : path.dirname(resolved);
20
+ return FileSystemUtils.canonicalizeExistingPath(searchStart);
21
+ }
22
+ catch {
23
+ return resolved;
24
+ }
25
+ }
26
+ function findNearestAncestor(startPath, predicate) {
27
+ let currentDir = getSearchStartDirectory(startPath);
28
+ while (true) {
29
+ if (predicate(currentDir)) {
30
+ return FileSystemUtils.canonicalizeExistingPath(currentDir);
31
+ }
32
+ const parentDir = path.dirname(currentDir);
33
+ if (parentDir === currentDir) {
34
+ return null;
35
+ }
36
+ currentDir = parentDir;
37
+ }
38
+ }
39
+ export function findWorkspacePlanningRootSync(startPath = process.cwd()) {
40
+ return findNearestAncestor(startPath, workspaceStateFileExistsSync);
41
+ }
42
+ export function findRepoPlanningRootSync(startPath = process.cwd()) {
43
+ return findNearestAncestor(startPath, (dirPath) => pathExistsAsDirectory(path.join(dirPath, 'openspec')));
44
+ }
45
+ function isSameOrDescendant(rootPath, candidatePath) {
46
+ const relative = path.relative(rootPath, candidatePath);
47
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
48
+ }
49
+ function countPathSegments(candidatePath) {
50
+ return path.resolve(candidatePath).split(path.sep).filter(Boolean).length;
51
+ }
52
+ function isWindowsLikePath(candidatePath) {
53
+ return /^[A-Za-z]:[\\/]/.test(candidatePath) || candidatePath.startsWith('\\\\');
54
+ }
55
+ function relativePlanningPath(fromPath, toPath) {
56
+ if (isWindowsLikePath(fromPath) || isWindowsLikePath(toPath)) {
57
+ return path.win32.relative(path.win32.normalize(fromPath), path.win32.normalize(toPath));
58
+ }
59
+ return path.posix.relative(fromPath.replace(/\\/g, '/'), toPath.replace(/\\/g, '/'));
60
+ }
61
+ function workspacePlanningHome(workspaceRoot) {
62
+ const viewState = readWorkspaceViewStateSync(workspaceRoot);
63
+ return {
64
+ kind: 'workspace',
65
+ root: workspaceRoot,
66
+ changesDir: getWorkspaceChangesDir(workspaceRoot),
67
+ defaultSchema: WORKSPACE_DEFAULT_SCHEMA,
68
+ workspace: {
69
+ name: viewState?.name ?? path.basename(workspaceRoot),
70
+ links: Object.keys(viewState?.links ?? {}).sort((a, b) => a.localeCompare(b)),
71
+ },
72
+ };
73
+ }
74
+ function repoPlanningHome(repoRoot) {
75
+ return {
76
+ kind: 'repo',
77
+ root: repoRoot,
78
+ changesDir: path.join(repoRoot, 'openspec', 'changes'),
79
+ defaultSchema: REPO_DEFAULT_SCHEMA,
80
+ };
81
+ }
82
+ export function resolveCurrentPlanningHomeSync(options = {}) {
83
+ const startPath = options.startPath ?? process.cwd();
84
+ const searchStart = getSearchStartDirectory(startPath);
85
+ const workspaceRoot = findWorkspacePlanningRootSync(searchStart);
86
+ const repoRoot = findRepoPlanningRootSync(searchStart);
87
+ if (workspaceRoot && isSameOrDescendant(workspaceRoot, searchStart)) {
88
+ if (!repoRoot || countPathSegments(workspaceRoot) >= countPathSegments(repoRoot)) {
89
+ return workspacePlanningHome(workspaceRoot);
90
+ }
91
+ }
92
+ if (repoRoot) {
93
+ return repoPlanningHome(repoRoot);
94
+ }
95
+ if (options.allowImplicitRepoRoot === false) {
96
+ throw new Error('No OpenSpec planning home found from the current directory.');
97
+ }
98
+ return repoPlanningHome(FileSystemUtils.canonicalizeExistingPath(searchStart));
99
+ }
100
+ export function getChangeDir(planningHome, changeName) {
101
+ return FileSystemUtils.joinPath(planningHome.changesDir, changeName);
102
+ }
103
+ export function formatChangeLocation(planningHome, changeName) {
104
+ const changeDir = getChangeDir(planningHome, changeName);
105
+ const relative = relativePlanningPath(planningHome.root, changeDir);
106
+ return relative.length > 0 ? relative : changeDir;
107
+ }
108
+ //# sourceMappingURL=planning-home.js.map
@@ -9,7 +9,7 @@ import type { Profile } from './global-config.js';
9
9
  * Core workflows included in the 'core' profile.
10
10
  * These provide the streamlined experience for new users.
11
11
  */
12
- export declare const CORE_WORKFLOWS: readonly ["propose", "explore", "apply", "archive"];
12
+ export declare const CORE_WORKFLOWS: readonly ["propose", "explore", "apply", "sync", "archive"];
13
13
  /**
14
14
  * All available workflows in the system.
15
15
  */
@@ -8,7 +8,7 @@
8
8
  * Core workflows included in the 'core' profile.
9
9
  * These provide the streamlined experience for new users.
10
10
  */
11
- export const CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'archive'];
11
+ export const CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'sync', 'archive'];
12
12
  /**
13
13
  * All available workflows in the system.
14
14
  */
@@ -8,6 +8,7 @@ import { promises as fs } from 'fs';
8
8
  import path from 'path';
9
9
  import chalk from 'chalk';
10
10
  import { extractRequirementsSection, parseDeltaSpec, normalizeRequirementName, } from './parsers/requirement-blocks.js';
11
+ import { findMainSpecStructureIssues } from './parsers/spec-structure.js';
11
12
  import { Validator } from './validation/validator.js';
12
13
  // -----------------------------------------------------------------------------
13
14
  // Public API
@@ -153,6 +154,13 @@ export async function buildUpdatedSpec(update, changeName) {
153
154
  isNewSpec = true;
154
155
  targetContent = buildSpecSkeleton(specName, changeName);
155
156
  }
157
+ const structureIssues = findMainSpecStructureIssues(targetContent);
158
+ if (structureIssues.length > 0) {
159
+ const details = structureIssues
160
+ .map(issue => `line ${issue.line}: ${issue.message}`)
161
+ .join('\n');
162
+ throw new Error(`${specName}: target spec is structurally invalid and cannot be updated until fixed:\n${details}`);
163
+ }
156
164
  // Extract requirements section and build name->block map
157
165
  const parts = extractRequirementsSection(targetContent);
158
166
  const nameToBlock = new Map();
@@ -203,7 +211,7 @@ export async function buildUpdatedSpec(update, changeName) {
203
211
  throw new Error(`${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - not found`);
204
212
  }
205
213
  // Replace block with provided raw (ensure header line matches key)
206
- const modHeaderMatch = mod.raw.split('\n')[0].match(/^###\s*Requirement:\s*(.+)\s*$/);
214
+ const modHeaderMatch = mod.raw.split('\n')[0].match(/^###\s*Requirement:\s*(.+)\s*$/i);
207
215
  if (!modHeaderMatch || normalizeRequirementName(modHeaderMatch[1]) !== key) {
208
216
  throw new Error(`${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - header mismatch in content`);
209
217
  }
@@ -23,6 +23,7 @@ export function getApplyChangeSkillTemplate() {
23
23
  \`\`\`
24
24
  Parse the JSON to understand:
25
25
  - \`schemaName\`: The workflow being used (e.g., "spec-driven")
26
+ - \`planningHome\`, \`changeRoot\`, and \`actionContext\`: planning scope and edit constraints
26
27
  - Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
27
28
 
28
29
  3. **Get apply instructions**
@@ -32,7 +33,7 @@ export function getApplyChangeSkillTemplate() {
32
33
  \`\`\`
33
34
 
34
35
  This returns:
35
- - Context file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
36
+ - \`contextFiles\`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
36
37
  - Progress (total, complete, remaining)
37
38
  - Task list with status
38
39
  - Dynamic instruction based on current state
@@ -42,9 +43,11 @@ export function getApplyChangeSkillTemplate() {
42
43
  - If \`state: "all_done"\`: congratulate, suggest archive
43
44
  - Otherwise: proceed to implementation
44
45
 
46
+ **Workspace guard:** If status JSON reports \`actionContext.mode: "workspace-planning"\` and \`allowedEditRoots\` is empty, explain that full workspace apply is not supported in this slice. Treat linked repos and folders as read-only context, ask the user to select an affected area through an explicit implementation workflow, and STOP before editing files.
47
+
45
48
  4. **Read context files**
46
49
 
47
- Read the files listed in \`contextFiles\` from the apply instructions output.
50
+ Read every file path listed under \`contextFiles\` from the apply instructions output.
48
51
  The files depend on the schema being used:
49
52
  - **spec-driven**: proposal, specs, design, tasks
50
53
  - Other schemas: follow the contextFiles from CLI output
@@ -179,6 +182,7 @@ export function getOpsxApplyCommandTemplate() {
179
182
  \`\`\`
180
183
  Parse the JSON to understand:
181
184
  - \`schemaName\`: The workflow being used (e.g., "spec-driven")
185
+ - \`planningHome\`, \`changeRoot\`, and \`actionContext\`: planning scope and edit constraints
182
186
  - Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
183
187
 
184
188
  3. **Get apply instructions**
@@ -188,7 +192,7 @@ export function getOpsxApplyCommandTemplate() {
188
192
  \`\`\`
189
193
 
190
194
  This returns:
191
- - Context file paths (varies by schema)
195
+ - \`contextFiles\`: artifact ID -> array of concrete file paths (varies by schema)
192
196
  - Progress (total, complete, remaining)
193
197
  - Task list with status
194
198
  - Dynamic instruction based on current state
@@ -198,9 +202,11 @@ export function getOpsxApplyCommandTemplate() {
198
202
  - If \`state: "all_done"\`: congratulate, suggest archive
199
203
  - Otherwise: proceed to implementation
200
204
 
205
+ **Workspace guard:** If status JSON reports \`actionContext.mode: "workspace-planning"\` and \`allowedEditRoots\` is empty, explain that full workspace apply is not supported in this slice. Treat linked repos and folders as read-only context, ask the user to select an affected area through an explicit implementation workflow, and STOP before editing files.
206
+
201
207
  4. **Read context files**
202
208
 
203
- Read the files listed in \`contextFiles\` from the apply instructions output.
209
+ Read every file path listed under \`contextFiles\` from the apply instructions output.
204
210
  The files depend on the schema being used:
205
211
  - **spec-driven**: proposal, specs, design, tasks
206
212
  - Other schemas: follow the contextFiles from CLI output
@@ -23,8 +23,11 @@ export function getArchiveChangeSkillTemplate() {
23
23
 
24
24
  Parse the JSON to understand:
25
25
  - \`schemaName\`: The workflow being used
26
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
26
27
  - \`artifacts\`: List of artifacts with their status (\`done\` or other)
27
28
 
29
+ If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace archive is not supported in this slice and STOP. Do not move workspace changes into repo-local archives or edit linked repos.
30
+
28
31
  **If any artifacts are not \`done\`:**
29
32
  - Display warning listing incomplete artifacts
30
33
  - Use **AskUserQuestion tool** to confirm user wants to proceed
@@ -45,7 +48,7 @@ export function getArchiveChangeSkillTemplate() {
45
48
 
46
49
  4. **Assess delta spec sync state**
47
50
 
48
- Check for delta specs at \`openspec/changes/<name>/specs/\`. If none exist, proceed without sync prompt.
51
+ Use \`artifactPaths.specs.existingOutputPaths\` from status JSON to check for delta specs. If none exist, proceed without sync prompt.
49
52
 
50
53
  **If delta specs exist:**
51
54
  - Compare each delta spec with its corresponding main spec at \`openspec/specs/<capability>/spec.md\`
@@ -60,19 +63,19 @@ export function getArchiveChangeSkillTemplate() {
60
63
 
61
64
  5. **Perform the archive**
62
65
 
63
- Create the archive directory if it doesn't exist:
66
+ Create an \`archive\` directory under \`planningHome.changesDir\` if it doesn't exist:
64
67
  \`\`\`bash
65
- mkdir -p openspec/changes/archive
68
+ mkdir -p "<planningHome.changesDir>/archive"
66
69
  \`\`\`
67
70
 
68
71
  Generate target name using current date: \`YYYY-MM-DD-<change-name>\`
69
72
 
70
73
  **Check if target already exists:**
71
74
  - If yes: Fail with error, suggest renaming existing archive or using different date
72
- - If no: Move the change directory to archive
75
+ - If no: Move \`changeRoot\` to the archive directory
73
76
 
74
77
  \`\`\`bash
75
- mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
78
+ mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
76
79
  \`\`\`
77
80
 
78
81
  6. **Display summary**
@@ -91,7 +94,7 @@ export function getArchiveChangeSkillTemplate() {
91
94
 
92
95
  **Change:** <change-name>
93
96
  **Schema:** <schema-name>
94
- **Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
97
+ **Archived to:** the archive path derived from \`planningHome.changesDir\`/YYYY-MM-DD-<name>/
95
98
  **Specs:** ✓ Synced to main specs (or "No delta specs" or "Sync skipped")
96
99
 
97
100
  All artifacts complete. All tasks complete.
@@ -137,8 +140,11 @@ export function getOpsxArchiveCommandTemplate() {
137
140
 
138
141
  Parse the JSON to understand:
139
142
  - \`schemaName\`: The workflow being used
143
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
140
144
  - \`artifacts\`: List of artifacts with their status (\`done\` or other)
141
145
 
146
+ If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace archive is not supported in this slice and STOP. Do not move workspace changes into repo-local archives or edit linked repos.
147
+
142
148
  **If any artifacts are not \`done\`:**
143
149
  - Display warning listing incomplete artifacts
144
150
  - Prompt user for confirmation to continue
@@ -159,7 +165,7 @@ export function getOpsxArchiveCommandTemplate() {
159
165
 
160
166
  4. **Assess delta spec sync state**
161
167
 
162
- Check for delta specs at \`openspec/changes/<name>/specs/\`. If none exist, proceed without sync prompt.
168
+ Use \`artifactPaths.specs.existingOutputPaths\` from status JSON to check for delta specs. If none exist, proceed without sync prompt.
163
169
 
164
170
  **If delta specs exist:**
165
171
  - Compare each delta spec with its corresponding main spec at \`openspec/specs/<capability>/spec.md\`
@@ -174,19 +180,19 @@ export function getOpsxArchiveCommandTemplate() {
174
180
 
175
181
  5. **Perform the archive**
176
182
 
177
- Create the archive directory if it doesn't exist:
183
+ Create an \`archive\` directory under \`planningHome.changesDir\` if it doesn't exist:
178
184
  \`\`\`bash
179
- mkdir -p openspec/changes/archive
185
+ mkdir -p "<planningHome.changesDir>/archive"
180
186
  \`\`\`
181
187
 
182
188
  Generate target name using current date: \`YYYY-MM-DD-<change-name>\`
183
189
 
184
190
  **Check if target already exists:**
185
191
  - If yes: Fail with error, suggest renaming existing archive or using different date
186
- - If no: Move the change directory to archive
192
+ - If no: Move \`changeRoot\` to the archive directory
187
193
 
188
194
  \`\`\`bash
189
- mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
195
+ mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
190
196
  \`\`\`
191
197
 
192
198
  6. **Display summary**
@@ -205,7 +211,7 @@ export function getOpsxArchiveCommandTemplate() {
205
211
 
206
212
  **Change:** <change-name>
207
213
  **Schema:** <schema-name>
208
- **Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
214
+ **Archived to:** the archive path derived from \`planningHome.changesDir\`/YYYY-MM-DD-<name>/
209
215
  **Specs:** ✓ Synced to main specs
210
216
 
211
217
  All artifacts complete. All tasks complete.
@@ -218,7 +224,7 @@ All artifacts complete. All tasks complete.
218
224
 
219
225
  **Change:** <change-name>
220
226
  **Schema:** <schema-name>
221
- **Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
227
+ **Archived to:** the archive path derived from \`planningHome.changesDir\`/YYYY-MM-DD-<name>/
222
228
  **Specs:** No delta specs
223
229
 
224
230
  All artifacts complete. All tasks complete.
@@ -231,7 +237,7 @@ All artifacts complete. All tasks complete.
231
237
 
232
238
  **Change:** <change-name>
233
239
  **Schema:** <schema-name>
234
- **Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
240
+ **Archived to:** the archive path derived from \`planningHome.changesDir\`/YYYY-MM-DD-<name>/
235
241
  **Specs:** Sync skipped (user chose to skip)
236
242
 
237
243
  **Warnings:**
@@ -248,7 +254,7 @@ Review the archive if this was not intentional.
248
254
  ## Archive Failed
249
255
 
250
256
  **Change:** <change-name>
251
- **Target:** openspec/changes/archive/YYYY-MM-DD-<name>/
257
+ **Target:** the archive path derived from \`planningHome.changesDir\`/YYYY-MM-DD-<name>/
252
258
 
253
259
  Target archive directory already exists.
254
260
 
@@ -30,14 +30,16 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
30
30
  For each selected change, collect:
31
31
 
32
32
  a. **Artifact status** - Run \`openspec status --change "<name>" --json\`
33
- - Parse \`schemaName\` and \`artifacts\` list
33
+ - Parse \`schemaName\`, \`artifacts\`, \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`
34
34
  - Note which artifacts are \`done\` vs other states
35
35
 
36
- b. **Task completion** - Read \`openspec/changes/<name>/tasks.md\`
36
+ If any selected change reports \`actionContext.mode: "workspace-planning"\`, explain that workspace bulk archive is not supported in this slice and STOP before syncing specs or moving changes. Do not fall back to repo-local paths or edit linked repos.
37
+
38
+ b. **Task completion** - Read \`artifactPaths.tasks.existingOutputPaths\` from status JSON
37
39
  - Count \`- [ ]\` (incomplete) vs \`- [x]\` (complete)
38
40
  - If no tasks file exists, note as "No tasks"
39
41
 
40
- c. **Delta specs** - Check \`openspec/changes/<name>/specs/\` directory
42
+ c. **Delta specs** - Check \`artifactPaths.specs.existingOutputPaths\` from status JSON
41
43
  - List which capability specs exist
42
44
  - For each, extract requirement names (lines matching \`### Requirement: <name>\`)
43
45
 
@@ -120,8 +122,8 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
120
122
 
121
123
  b. **Perform the archive**:
122
124
  \`\`\`bash
123
- mkdir -p openspec/changes/archive
124
- mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
125
+ mkdir -p "<planningHome.changesDir>/archive"
126
+ mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
125
127
  \`\`\`
126
128
 
127
129
  c. **Track outcome** for each change:
@@ -276,14 +278,16 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
276
278
  For each selected change, collect:
277
279
 
278
280
  a. **Artifact status** - Run \`openspec status --change "<name>" --json\`
279
- - Parse \`schemaName\` and \`artifacts\` list
281
+ - Parse \`schemaName\`, \`artifacts\`, \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`
280
282
  - Note which artifacts are \`done\` vs other states
281
283
 
282
- b. **Task completion** - Read \`openspec/changes/<name>/tasks.md\`
284
+ If any selected change reports \`actionContext.mode: "workspace-planning"\`, explain that workspace bulk archive is not supported in this slice and STOP before syncing specs or moving changes. Do not fall back to repo-local paths or edit linked repos.
285
+
286
+ b. **Task completion** - Read \`artifactPaths.tasks.existingOutputPaths\` from status JSON
283
287
  - Count \`- [ ]\` (incomplete) vs \`- [x]\` (complete)
284
288
  - If no tasks file exists, note as "No tasks"
285
289
 
286
- c. **Delta specs** - Check \`openspec/changes/<name>/specs/\` directory
290
+ c. **Delta specs** - Check \`artifactPaths.specs.existingOutputPaths\` from status JSON
287
291
  - List which capability specs exist
288
292
  - For each, extract requirement names (lines matching \`### Requirement: <name>\`)
289
293
 
@@ -366,8 +370,8 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
366
370
 
367
371
  b. **Perform the archive**:
368
372
  \`\`\`bash
369
- mkdir -p openspec/changes/archive
370
- mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
373
+ mkdir -p "<planningHome.changesDir>/archive"
374
+ mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
371
375
  \`\`\`
372
376
 
373
377
  c. **Track outcome** for each change:
@@ -30,6 +30,7 @@ export function getContinueChangeSkillTemplate() {
30
30
  - \`schemaName\`: The workflow schema being used (e.g., "spec-driven")
31
31
  - \`artifacts\`: Array of artifacts with their status ("done", "ready", "blocked")
32
32
  - \`isComplete\`: Boolean indicating if all artifacts are complete
33
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
33
34
 
34
35
  3. **Act based on status**:
35
36
 
@@ -54,13 +55,13 @@ export function getContinueChangeSkillTemplate() {
54
55
  - \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
55
56
  - \`template\`: The structure to use for your output file
56
57
  - \`instruction\`: Schema-specific guidance
57
- - \`outputPath\`: Where to write the artifact
58
+ - \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
58
59
  - \`dependencies\`: Completed artifacts to read for context
59
60
  - **Create the artifact file**:
60
61
  - Read any completed dependency files for context
61
62
  - Use \`template\` as the structure - fill in its sections
62
63
  - Apply \`context\` and \`rules\` as constraints when writing - but do NOT copy them into the file
63
- - Write to the output path specified in instructions
64
+ - Write to the \`resolvedOutputPath\` specified in instructions. If it is a glob pattern, choose the concrete file path using the schema instruction and workspace planning context
64
65
  - Show what was created and what's now unlocked
65
66
  - STOP after creating ONE artifact
66
67
 
@@ -148,6 +149,7 @@ export function getOpsxContinueCommandTemplate() {
148
149
  - \`schemaName\`: The workflow schema being used (e.g., "spec-driven")
149
150
  - \`artifacts\`: Array of artifacts with their status ("done", "ready", "blocked")
150
151
  - \`isComplete\`: Boolean indicating if all artifacts are complete
152
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
151
153
 
152
154
  3. **Act based on status**:
153
155
 
@@ -172,13 +174,13 @@ export function getOpsxContinueCommandTemplate() {
172
174
  - \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
173
175
  - \`template\`: The structure to use for your output file
174
176
  - \`instruction\`: Schema-specific guidance
175
- - \`outputPath\`: Where to write the artifact
177
+ - \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
176
178
  - \`dependencies\`: Completed artifacts to read for context
177
179
  - **Create the artifact file**:
178
180
  - Read any completed dependency files for context
179
181
  - Use \`template\` as the structure - fill in its sections
180
182
  - Apply \`context\` and \`rules\` as constraints when writing - but do NOT copy them into the file
181
- - Write to the output path specified in instructions
183
+ - Write to the \`resolvedOutputPath\` specified in instructions. If it is a glob pattern, choose the concrete file path using the schema instruction and workspace planning context
182
184
  - Show what was created and what's now unlocked
183
185
  - STOP after creating ONE artifact
184
186
 
@@ -95,11 +95,10 @@ Think freely. When insights crystallize, you might offer:
95
95
 
96
96
  If the user mentions a change or you detect one is relevant:
97
97
 
98
- 1. **Read existing artifacts for context**
99
- - \`openspec/changes/<name>/proposal.md\`
100
- - \`openspec/changes/<name>/design.md\`
101
- - \`openspec/changes/<name>/tasks.md\`
102
- - etc.
98
+ 1. **Resolve and read existing artifacts for context**
99
+ - Run \`openspec status --change "<name>" --json\`.
100
+ - Use \`changeRoot\`, \`artifactPaths\`, and \`actionContext\` from the status JSON.
101
+ - Read existing files from \`artifactPaths.<artifact>.existingOutputPaths\`.
103
102
 
104
103
  2. **Reference them naturally in conversation**
105
104
  - "Your design mentions using Redis, but we just realized SQLite fits better..."
@@ -392,11 +391,10 @@ Think freely. When insights crystallize, you might offer:
392
391
 
393
392
  If the user mentions a change or you detect one is relevant:
394
393
 
395
- 1. **Read existing artifacts for context**
396
- - \`openspec/changes/<name>/proposal.md\`
397
- - \`openspec/changes/<name>/design.md\`
398
- - \`openspec/changes/<name>/tasks.md\`
399
- - etc.
394
+ 1. **Resolve and read existing artifacts for context**
395
+ - Run \`openspec status --change "<name>" --json\`.
396
+ - Use \`changeRoot\`, \`artifactPaths\`, and \`actionContext\` from the status JSON.
397
+ - Read existing files from \`artifactPaths.<artifact>.existingOutputPaths\`.
400
398
 
401
399
  2. **Reference them naturally in conversation**
402
400
  - "Your design mentions using Redis, but we just realized SQLite fits better..."
@@ -21,7 +21,7 @@ export function getFfChangeSkillTemplate() {
21
21
  \`\`\`bash
22
22
  openspec new change "<name>"
23
23
  \`\`\`
24
- This creates a scaffolded change at \`openspec/changes/<name>/\`.
24
+ This creates a scaffolded change in the planning home resolved by the CLI.
25
25
 
26
26
  3. **Get the artifact build order**
27
27
  \`\`\`bash
@@ -30,6 +30,7 @@ export function getFfChangeSkillTemplate() {
30
30
  Parse the JSON to get:
31
31
  - \`applyRequires\`: array of artifact IDs needed before implementation (e.g., \`["tasks"]\`)
32
32
  - \`artifacts\`: list of all artifacts with their status and dependencies
33
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
33
34
 
34
35
  4. **Create artifacts in sequence until apply-ready**
35
36
 
@@ -47,10 +48,10 @@ export function getFfChangeSkillTemplate() {
47
48
  - \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
48
49
  - \`template\`: The structure to use for your output file
49
50
  - \`instruction\`: Schema-specific guidance for this artifact type
50
- - \`outputPath\`: Where to write the artifact
51
+ - \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
51
52
  - \`dependencies\`: Completed artifacts to read for context
52
53
  - Read any completed dependency files for context
53
- - Create the artifact file using \`template\` as the structure
54
+ - Create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`
54
55
  - Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
55
56
  - Show brief progress: "✓ Created <artifact-id>"
56
57
 
@@ -122,7 +123,7 @@ export function getOpsxFfCommandTemplate() {
122
123
  \`\`\`bash
123
124
  openspec new change "<name>"
124
125
  \`\`\`
125
- This creates a scaffolded change at \`openspec/changes/<name>/\`.
126
+ This creates a scaffolded change in the planning home resolved by the CLI.
126
127
 
127
128
  3. **Get the artifact build order**
128
129
  \`\`\`bash
@@ -131,6 +132,7 @@ export function getOpsxFfCommandTemplate() {
131
132
  Parse the JSON to get:
132
133
  - \`applyRequires\`: array of artifact IDs needed before implementation (e.g., \`["tasks"]\`)
133
134
  - \`artifacts\`: list of all artifacts with their status and dependencies
135
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
134
136
 
135
137
  4. **Create artifacts in sequence until apply-ready**
136
138
 
@@ -148,10 +150,10 @@ export function getOpsxFfCommandTemplate() {
148
150
  - \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
149
151
  - \`template\`: The structure to use for your output file
150
152
  - \`instruction\`: Schema-specific guidance for this artifact type
151
- - \`outputPath\`: Where to write the artifact
153
+ - \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
152
154
  - \`dependencies\`: Completed artifacts to read for context
153
155
  - Read any completed dependency files for context
154
- - Create the artifact file using \`template\` as the structure
156
+ - Create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`
155
157
  - Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
156
158
  - Show brief progress: "✓ Created <artifact-id>"
157
159
 
@@ -32,13 +32,13 @@ export function getNewChangeSkillTemplate() {
32
32
  openspec new change "<name>"
33
33
  \`\`\`
34
34
  Add \`--schema <name>\` only if the user requested a specific workflow.
35
- This creates a scaffolded change at \`openspec/changes/<name>/\` with the selected schema.
35
+ This creates a scaffolded change in the planning home resolved by the CLI.
36
36
 
37
37
  4. **Show the artifact status**
38
38
  \`\`\`bash
39
- openspec status --change "<name>"
39
+ openspec status --change "<name>" --json
40
40
  \`\`\`
41
- This shows which artifacts need to be created and which are ready (dependencies satisfied).
41
+ Use the returned \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`nextSteps\` instead of assuming repo-local paths.
42
42
 
43
43
  5. **Get instructions for the first artifact**
44
44
  The first artifact depends on the schema (e.g., \`proposal\` for spec-driven).
@@ -106,13 +106,13 @@ export function getOpsxNewCommandTemplate() {
106
106
  openspec new change "<name>"
107
107
  \`\`\`
108
108
  Add \`--schema <name>\` only if the user requested a specific workflow.
109
- This creates a scaffolded change at \`openspec/changes/<name>/\` with the selected schema.
109
+ This creates a scaffolded change in the planning home resolved by the CLI.
110
110
 
111
111
  4. **Show the artifact status**
112
112
  \`\`\`bash
113
- openspec status --change "<name>"
113
+ openspec status --change "<name>" --json
114
114
  \`\`\`
115
- This shows which artifacts need to be created and which are ready (dependencies satisfied).
115
+ Use the returned \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`nextSteps\` instead of assuming repo-local paths.
116
116
 
117
117
  5. **Get instructions for the first artifact**
118
118
  The first artifact depends on the schema. Check the status output to find the first artifact with status "ready".