@fission-ai/openspec 1.6.0-beta.1 → 1.7.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 (175) hide show
  1. package/README.md +28 -1
  2. package/dist/cli/index.js +73 -7
  3. package/dist/commands/change.d.ts +0 -1
  4. package/dist/commands/change.js +90 -50
  5. package/dist/commands/config.js +14 -8
  6. package/dist/commands/doctor.js +9 -4
  7. package/dist/commands/feedback.js +74 -23
  8. package/dist/commands/schema.js +21 -11
  9. package/dist/commands/show.js +5 -2
  10. package/dist/commands/spec.js +20 -24
  11. package/dist/commands/validate.js +18 -3
  12. package/dist/commands/workflow/index.d.ts +1 -1
  13. package/dist/commands/workflow/index.js +1 -1
  14. package/dist/commands/workflow/instructions.d.ts +7 -1
  15. package/dist/commands/workflow/instructions.js +89 -6
  16. package/dist/commands/workflow/shared.d.ts +13 -2
  17. package/dist/commands/workflow/shared.js +34 -4
  18. package/dist/commands/workflow/status.js +7 -2
  19. package/dist/core/archive.d.ts +0 -1
  20. package/dist/core/archive.js +96 -33
  21. package/dist/core/artifact-graph/graph.d.ts +16 -0
  22. package/dist/core/artifact-graph/graph.js +30 -6
  23. package/dist/core/artifact-graph/instruction-loader.d.ts +27 -2
  24. package/dist/core/artifact-graph/instruction-loader.js +55 -7
  25. package/dist/core/artifact-graph/resolver.d.ts +14 -0
  26. package/dist/core/artifact-graph/resolver.js +34 -6
  27. package/dist/core/change-metadata/schema.d.ts +1 -0
  28. package/dist/core/change-metadata/schema.js +6 -0
  29. package/dist/core/change-status-policy.d.ts +1 -1
  30. package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
  31. package/dist/core/command-generation/adapters/amazon-q.js +7 -1
  32. package/dist/core/command-generation/adapters/antigravity.js +2 -1
  33. package/dist/core/command-generation/adapters/auggie.js +2 -1
  34. package/dist/core/command-generation/adapters/bob.d.ts +5 -1
  35. package/dist/core/command-generation/adapters/bob.js +6 -5
  36. package/dist/core/command-generation/adapters/claude.js +1 -8
  37. package/dist/core/command-generation/adapters/codebuddy.js +3 -2
  38. package/dist/core/command-generation/adapters/continue.js +3 -2
  39. package/dist/core/command-generation/adapters/costrict.js +2 -1
  40. package/dist/core/command-generation/adapters/crush.js +5 -5
  41. package/dist/core/command-generation/adapters/cursor.js +2 -2
  42. package/dist/core/command-generation/adapters/devin.d.ts +19 -0
  43. package/dist/core/command-generation/adapters/devin.js +36 -0
  44. package/dist/core/command-generation/adapters/factory.js +2 -1
  45. package/dist/core/command-generation/adapters/gemini.js +37 -2
  46. package/dist/core/command-generation/adapters/github-copilot.js +2 -1
  47. package/dist/core/command-generation/adapters/iflow.js +5 -4
  48. package/dist/core/command-generation/adapters/index.d.ts +2 -2
  49. package/dist/core/command-generation/adapters/index.js +2 -2
  50. package/dist/core/command-generation/adapters/junie.js +2 -1
  51. package/dist/core/command-generation/adapters/kiro.js +2 -1
  52. package/dist/core/command-generation/adapters/lingma.js +5 -5
  53. package/dist/core/command-generation/adapters/oh-my-pi.d.ts +2 -2
  54. package/dist/core/command-generation/adapters/oh-my-pi.js +3 -5
  55. package/dist/core/command-generation/adapters/opencode.js +3 -5
  56. package/dist/core/command-generation/adapters/pi.d.ts +2 -2
  57. package/dist/core/command-generation/adapters/pi.js +3 -6
  58. package/dist/core/command-generation/adapters/qoder.js +5 -5
  59. package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
  60. package/dist/core/command-generation/adapters/qwen.js +12 -7
  61. package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
  62. package/dist/core/command-generation/adapters/roocode.js +4 -4
  63. package/dist/core/command-generation/adapters/trae.js +1 -21
  64. package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
  65. package/dist/core/command-generation/adapters/zcode.js +33 -0
  66. package/dist/core/command-generation/generator.d.ts +8 -0
  67. package/dist/core/command-generation/generator.js +15 -1
  68. package/dist/core/command-generation/index.d.ts +1 -1
  69. package/dist/core/command-generation/index.js +1 -1
  70. package/dist/core/command-generation/invocation.d.ts +73 -0
  71. package/dist/core/command-generation/invocation.js +77 -0
  72. package/dist/core/command-generation/registry.js +4 -4
  73. package/dist/core/command-generation/types.d.ts +9 -2
  74. package/dist/core/command-generation/yaml.d.ts +20 -6
  75. package/dist/core/command-generation/yaml.js +31 -21
  76. package/dist/core/command-surface.d.ts +15 -0
  77. package/dist/core/command-surface.js +33 -0
  78. package/dist/core/completions/command-registry.js +8 -2
  79. package/dist/core/completions/generators/powershell-generator.js +15 -8
  80. package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
  81. package/dist/core/completions/installers/zsh-installer.js +20 -4
  82. package/dist/core/config-prompts.d.ts +1 -1
  83. package/dist/core/config-prompts.js +14 -1
  84. package/dist/core/config-schema.d.ts +7 -1
  85. package/dist/core/config-schema.js +40 -1
  86. package/dist/core/config.d.ts +12 -0
  87. package/dist/core/config.js +21 -3
  88. package/dist/core/global-config.d.ts +5 -0
  89. package/dist/core/init.d.ts +35 -0
  90. package/dist/core/init.js +225 -39
  91. package/dist/core/legacy-cleanup.d.ts +59 -0
  92. package/dist/core/legacy-cleanup.js +249 -22
  93. package/dist/core/list.js +5 -7
  94. package/dist/core/migration.d.ts +71 -1
  95. package/dist/core/migration.js +294 -1
  96. package/dist/core/onboarding-commands.d.ts +30 -0
  97. package/dist/core/onboarding-commands.js +38 -0
  98. package/dist/core/parsers/change-parser.d.ts +17 -2
  99. package/dist/core/parsers/change-parser.js +33 -20
  100. package/dist/core/parsers/code-fence.d.ts +15 -0
  101. package/dist/core/parsers/code-fence.js +49 -0
  102. package/dist/core/parsers/markdown-parser.js +2 -1
  103. package/dist/core/parsers/requirement-blocks.d.ts +8 -0
  104. package/dist/core/parsers/requirement-blocks.js +60 -35
  105. package/dist/core/parsers/requirement-text.d.ts +1 -8
  106. package/dist/core/parsers/requirement-text.js +4 -42
  107. package/dist/core/parsers/spec-structure.js +3 -29
  108. package/dist/core/profile-sync-drift.d.ts +0 -8
  109. package/dist/core/profile-sync-drift.js +9 -45
  110. package/dist/core/project-config.d.ts +25 -6
  111. package/dist/core/project-config.js +77 -7
  112. package/dist/core/references.js +42 -4
  113. package/dist/core/relationship-health.d.ts +8 -0
  114. package/dist/core/relationship-health.js +13 -0
  115. package/dist/core/root-selection.d.ts +9 -6
  116. package/dist/core/root-selection.js +42 -5
  117. package/dist/core/shared/tool-detection.d.ts +25 -5
  118. package/dist/core/shared/tool-detection.js +105 -6
  119. package/dist/core/specs-apply.d.ts +8 -33
  120. package/dist/core/specs-apply.js +252 -157
  121. package/dist/core/store/git.d.ts +13 -0
  122. package/dist/core/store/git.js +24 -0
  123. package/dist/core/templates/workflows/apply-change.js +54 -4
  124. package/dist/core/templates/workflows/archive-change.js +166 -42
  125. package/dist/core/templates/workflows/bulk-archive-change.js +240 -63
  126. package/dist/core/templates/workflows/continue-change.js +32 -40
  127. package/dist/core/templates/workflows/explore.js +12 -0
  128. package/dist/core/templates/workflows/ff-change.js +46 -30
  129. package/dist/core/templates/workflows/new-change.js +2 -2
  130. package/dist/core/templates/workflows/onboard.js +4 -4
  131. package/dist/core/templates/workflows/propose.js +50 -32
  132. package/dist/core/templates/workflows/store-selection.d.ts +1 -1
  133. package/dist/core/templates/workflows/store-selection.js +1 -1
  134. package/dist/core/templates/workflows/sync-specs.js +172 -22
  135. package/dist/core/templates/workflows/update-change.js +20 -12
  136. package/dist/core/templates/workflows/verify-change.js +16 -10
  137. package/dist/core/update.d.ts +39 -5
  138. package/dist/core/update.js +288 -71
  139. package/dist/core/validation/constants.d.ts +4 -1
  140. package/dist/core/validation/constants.js +4 -1
  141. package/dist/core/validation/validator.d.ts +1 -7
  142. package/dist/core/validation/validator.js +92 -36
  143. package/dist/core/version-check.d.ts +152 -0
  144. package/dist/core/version-check.js +736 -0
  145. package/dist/core/view.js +12 -17
  146. package/dist/prompts/searchable-multi-select.js +1 -1
  147. package/dist/telemetry/index.d.ts +1 -1
  148. package/dist/telemetry/index.js +55 -33
  149. package/dist/ui/welcome-screen.d.ts +9 -1
  150. package/dist/ui/welcome-screen.js +91 -38
  151. package/dist/utils/change-metadata.d.ts +30 -0
  152. package/dist/utils/change-metadata.js +101 -7
  153. package/dist/utils/change-utils.d.ts +10 -4
  154. package/dist/utils/change-utils.js +20 -13
  155. package/dist/utils/command-references.d.ts +80 -6
  156. package/dist/utils/command-references.js +138 -7
  157. package/dist/utils/date.d.ts +8 -0
  158. package/dist/utils/date.js +13 -0
  159. package/dist/utils/index.d.ts +1 -1
  160. package/dist/utils/index.js +1 -1
  161. package/dist/utils/item-discovery.d.ts +19 -0
  162. package/dist/utils/item-discovery.js +30 -48
  163. package/dist/utils/shell-detection.d.ts +1 -1
  164. package/dist/utils/shell-detection.js +64 -11
  165. package/dist/utils/spec-discovery.d.ts +40 -0
  166. package/dist/utils/spec-discovery.js +102 -0
  167. package/package.json +29 -30
  168. package/schemas/spec-driven/schema.yaml +60 -5
  169. package/schemas/spec-driven/templates/design.md +2 -2
  170. package/schemas/spec-driven/templates/proposal.md +5 -1
  171. package/schemas/spec-driven/templates/spec.md +3 -0
  172. package/dist/core/command-generation/adapters/codex.d.ts +0 -16
  173. package/dist/core/command-generation/adapters/codex.js +0 -39
  174. package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
  175. package/dist/core/command-generation/adapters/windsurf.js +0 -38
@@ -0,0 +1,102 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ /**
4
+ * Recursively discover every `spec.md` under a specs root, so both the flat
5
+ * `specs/<id>/spec.md` layout and nested `specs/<area>/<id>/spec.md` layouts
6
+ * are found (#1353). A `spec.md` sitting directly in the root is ignored,
7
+ * matching the historical requirement that specs live in a capability folder.
8
+ * Dot-directories are skipped and symlinked directories are not followed.
9
+ * A symlinked `spec.md` IS resolved: `hasAnyFileUnder` and the artifact
10
+ * graph's globs both count it as content, so dropping it here would silently
11
+ * lose the delta on archive; a dangling link is skipped. Results are sorted
12
+ * by id for deterministic output.
13
+ *
14
+ * A missing root (ENOENT) yields an empty list, but any other read failure
15
+ * (EACCES, EIO, ...) is thrown rather than swallowed: since this feeds the
16
+ * archive/apply merge path, silently dropping an unreadable capability would
17
+ * recreate the exact data-loss class #1353 is closing.
18
+ */
19
+ export async function discoverSpecFiles(specsRoot) {
20
+ const results = [];
21
+ const walk = async (dir, segments) => {
22
+ let entries;
23
+ try {
24
+ entries = await fs.readdir(dir, { withFileTypes: true });
25
+ }
26
+ catch (err) {
27
+ if (err?.code === 'ENOENT')
28
+ return;
29
+ throw err;
30
+ }
31
+ for (const entry of entries) {
32
+ if (entry.name.startsWith('.'))
33
+ continue;
34
+ if (entry.isDirectory()) {
35
+ await walk(path.join(dir, entry.name), [...segments, entry.name]);
36
+ }
37
+ else if (entry.name === 'spec.md' && segments.length > 0) {
38
+ if (entry.isFile()) {
39
+ results.push({ id: segments.join('/'), specFile: path.join(dir, entry.name) });
40
+ }
41
+ else if (entry.isSymbolicLink()) {
42
+ const specFile = path.join(dir, entry.name);
43
+ try {
44
+ if ((await fs.stat(specFile)).isFile()) {
45
+ results.push({ id: segments.join('/'), specFile });
46
+ }
47
+ }
48
+ catch (err) {
49
+ // A dangling link is not content; anything else fails loudly.
50
+ if (err?.code !== 'ENOENT')
51
+ throw err;
52
+ }
53
+ }
54
+ }
55
+ }
56
+ };
57
+ await walk(specsRoot, []);
58
+ // Plain code-point comparison, not localeCompare: the latter follows the
59
+ // process's ICU locale, so ordering could vary by OS/CI. Code-point ordering
60
+ // guarantees the deterministic output the docstring promises.
61
+ return results.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
62
+ }
63
+ /**
64
+ * True when any regular non-dot file exists anywhere under the given
65
+ * directory. Used by validate/archive to detect content under a change's
66
+ * specs/ that contradicts a declared skip_specs marker - including files that
67
+ * discoverSpecFiles ignores (a root spec.md, stray non-spec.md notes), since
68
+ * anything there would be silently dropped or misread while the change claims
69
+ * to have nothing. Dot entries (.DS_Store, .gitkeep, dot-directories) are
70
+ * skipped to match discoverSpecFiles - they are invisible to every other
71
+ * code path, so they must not count as spec content. Symlinks DO count
72
+ * (without being followed): the artifact graph's globs follow them, so a
73
+ * symlinked spec would read as existing content while the change claims to
74
+ * have none - it contradicts the marker like any regular file. A missing
75
+ * directory returns false; other read failures are thrown for the caller to
76
+ * decide.
77
+ */
78
+ export async function hasAnyFileUnder(dirPath) {
79
+ let entries;
80
+ try {
81
+ entries = await fs.readdir(dirPath, { withFileTypes: true });
82
+ }
83
+ catch (err) {
84
+ if (err?.code === 'ENOENT') {
85
+ return false;
86
+ }
87
+ throw err;
88
+ }
89
+ for (const entry of entries) {
90
+ if (entry.name.startsWith('.')) {
91
+ continue;
92
+ }
93
+ if (entry.isFile() || entry.isSymbolicLink()) {
94
+ return true;
95
+ }
96
+ if (entry.isDirectory() && (await hasAnyFileUnder(path.join(dirPath, entry.name)))) {
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+ //# sourceMappingURL=spec-discovery.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fission-ai/openspec",
3
- "version": "1.6.0-beta.1",
3
+ "version": "1.7.0",
4
4
  "description": "AI-native system for spec-driven development",
5
5
  "keywords": [
6
6
  "openspec",
@@ -17,7 +17,6 @@
17
17
  "license": "MIT",
18
18
  "author": "OpenSpec Contributors",
19
19
  "type": "module",
20
- "packageManager": "pnpm@9.15.9",
21
20
  "publishConfig": {
22
21
  "access": "public"
23
22
  },
@@ -39,9 +38,36 @@
39
38
  "!dist/**/__tests__",
40
39
  "!dist/**/*.map"
41
40
  ],
41
+ "engines": {
42
+ "node": ">=20.19.0"
43
+ },
44
+ "devDependencies": {
45
+ "@changesets/changelog-github": "^0.7.0",
46
+ "@changesets/cli": "^2.31.1",
47
+ "@types/node": "^20.19.43",
48
+ "@vitest/ui": "^3.2.6",
49
+ "eslint": "^10.5.0",
50
+ "smol-toml": "^1.7.1",
51
+ "typescript": "^6.0.3",
52
+ "typescript-eslint": "^8.65.0",
53
+ "vitest": "^3.2.6"
54
+ },
55
+ "dependencies": {
56
+ "@inquirer/core": "^10.3.2",
57
+ "@inquirer/prompts": "^7.10.1",
58
+ "chalk": "^5.6.2",
59
+ "commander": "^14.0.0",
60
+ "cross-spawn": "7.0.6",
61
+ "fast-glob": "^3.3.3",
62
+ "ora": "^9.4.1",
63
+ "yaml": "^2.8.3",
64
+ "zod": "^4.4.3"
65
+ },
42
66
  "scripts": {
43
67
  "lint": "eslint src/",
44
68
  "build": "node build.js",
69
+ "generate:skills": "node scripts/generate-skillssh.mjs",
70
+ "regen:parity-hashes": "node scripts/regen-parity-hashes.mjs",
45
71
  "dev": "tsc --watch",
46
72
  "dev:cli": "pnpm build && node bin/openspec.js",
47
73
  "test": "vitest run",
@@ -49,37 +75,10 @@
49
75
  "test:ui": "vitest --ui",
50
76
  "test:coverage": "vitest --coverage",
51
77
  "test:postinstall": "node scripts/postinstall.js",
52
- "prepare": "pnpm run build",
53
- "prepublishOnly": "pnpm run build",
54
78
  "postinstall": "node scripts/postinstall.js",
55
79
  "check:pack-version": "node scripts/pack-version-check.mjs",
56
80
  "release": "pnpm run release:ci",
57
81
  "release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
58
82
  "changeset": "changeset"
59
- },
60
- "engines": {
61
- "node": ">=20.19.0"
62
- },
63
- "devDependencies": {
64
- "@changesets/changelog-github": "^0.5.2",
65
- "@changesets/cli": "^2.27.7",
66
- "@types/node": "^24.2.0",
67
- "@vitest/ui": "^3.2.6",
68
- "eslint": "^9.39.2",
69
- "typescript": "^5.9.3",
70
- "typescript-eslint": "^8.62.0",
71
- "vitest": "^3.2.6"
72
- },
73
- "dependencies": {
74
- "@inquirer/core": "^10.3.2",
75
- "@inquirer/prompts": "^7.10.1",
76
- "chalk": "^5.5.0",
77
- "commander": "^14.0.0",
78
- "cross-spawn": "7.0.6",
79
- "fast-glob": "^3.3.3",
80
- "ora": "^8.2.0",
81
- "posthog-node": "^5.20.0",
82
- "yaml": "^2.8.2",
83
- "zod": "^4.0.17"
84
83
  }
85
- }
84
+ }
@@ -21,6 +21,14 @@ artifacts:
21
21
  proposal and specs phases. Research existing specs before filling this in.
22
22
  Each capability listed here will need a corresponding spec file.
23
23
 
24
+ Every change must either declare at least one capability (new or
25
+ modified) or explicitly opt out of specs: `openspec validate` rejects a
26
+ change with zero deltas unless the change's `.openspec.yaml` sets
27
+ `skip_specs: true`. Use `skip_specs: true` only when no spec-level
28
+ behavior changes (pure refactor, tooling, docs) - specs describe
29
+ behavior, so if behavior does not change, no spec should change either.
30
+ Do not invent a requirement just to satisfy validation.
31
+
24
32
  Keep it concise (1-2 pages). Focus on the "why" not the "how" -
25
33
  implementation details belong in design.md.
26
34
 
@@ -34,10 +42,32 @@ artifacts:
34
42
  instruction: |
35
43
  Create specification files that define WHAT the system should do.
36
44
 
45
+ A spec is a behavior contract, not an implementation plan.
46
+
47
+ Good spec content:
48
+ - Observable behavior users or downstream systems rely on
49
+ - Inputs, outputs, and error conditions
50
+ - External constraints (security, privacy, reliability, compatibility)
51
+ - Scenarios that can be tested or explicitly validated
52
+
53
+ Avoid in specs:
54
+ - Internal class/function names
55
+ - Library or framework choices
56
+ - Step-by-step implementation details
57
+ - Detailed execution plans (those belong in design.md or tasks.md)
58
+
59
+ Quick test: if the implementation can change without changing externally
60
+ visible behavior, it likely does not belong in the spec.
61
+
37
62
  Create one spec file per capability listed in the proposal's Capabilities section.
38
63
  - New capabilities: use the exact kebab-case name from the proposal (specs/<capability>/spec.md).
39
64
  - Modified capabilities: use the existing spec folder name from openspec/specs/<capability>/ when creating the delta spec at specs/<capability>/spec.md.
40
65
 
66
+ There must be at least one spec file unless the change's `.openspec.yaml`
67
+ sets `skip_specs: true` (no spec-level behavior change) - `openspec validate`
68
+ rejects a zero-delta change without that marker. If the proposal lists no
69
+ capabilities and `skip_specs` is not set, revisit the proposal first.
70
+
41
71
  Delta operations (use ## headers):
42
72
  - **ADDED Requirements**: New capabilities
43
73
  - **MODIFIED Requirements**: Changed behavior - MUST include full updated content
@@ -51,6 +81,16 @@ artifacts:
51
81
  - **CRITICAL**: Scenarios MUST use exactly 4 hashtags (`####`). Using 3 hashtags or bullets will fail silently.
52
82
  - Every requirement MUST have at least one scenario.
53
83
 
84
+ New capabilities only: start the delta spec with a `## Purpose` section -
85
+ one or two sentences (50+ characters, or `openspec validate --strict`
86
+ reports it as too brief) describing what the capability is for. Archive
87
+ copies it into the main spec it creates; without it the new main spec is
88
+ left with a `TBD ... Update Purpose after archive` placeholder to fill in
89
+ by hand. Do NOT add `## Purpose` to a delta for an existing capability -
90
+ that spec already has one and the delta's is ignored. To change an
91
+ existing capability's Purpose - including a leftover `TBD` placeholder -
92
+ edit `openspec/specs/<capability>/spec.md` directly.
93
+
54
94
  MODIFIED requirements workflow:
55
95
  1. Locate the existing requirement in openspec/specs/<capability>/spec.md
56
96
  2. Copy the ENTIRE requirement block (from `### Requirement:` through all scenarios)
@@ -60,8 +100,12 @@ artifacts:
60
100
  Common pitfall: Using MODIFIED with partial content loses detail at archive time.
61
101
  If adding new concerns without changing existing behavior, use ADDED instead.
62
102
 
63
- Example:
103
+ Example (a new capability, so it opens with `## Purpose`):
64
104
  ```
105
+ ## Purpose
106
+
107
+ Lets users take their data out of the product in a portable format.
108
+
65
109
  ## ADDED Requirements
66
110
 
67
111
  ### Requirement: User can export data
@@ -96,15 +140,22 @@ artifacts:
96
140
  - Ambiguity that benefits from technical decisions before coding
97
141
 
98
142
  Sections:
99
- - **Context**: Background, current state, constraints, stakeholders
100
- - **Goals / Non-Goals**: What this design achieves and explicitly excludes
143
+ - **Context**: Only the current state and constraints needed to explain the approach. Reference the proposal for motivation instead of restating it (e.g., "See proposal.md - Why").
144
+ - **Goals / Non-Goals**: What this design achieves and explicitly excludes. Don't restate the proposal's scope - add only design-level boundaries.
101
145
  - **Decisions**: Key technical choices with rationale (why X over Y?). Include alternatives considered for each decision.
102
146
  - **Risks / Trade-offs**: Known limitations, things that could go wrong. Format: [Risk] → Mitigation
103
147
  - **Migration Plan**: Steps to deploy, rollback strategy (if applicable)
104
- - **Open Questions**: Outstanding decisions or unknowns to resolve
148
+ - **Open Questions**: Unknowns that can safely be answered later without
149
+ changing the specs, the approach, or the task breakdown. Omit if none.
150
+
151
+ Open questions are for genuinely deferrable unknowns, not decisions you
152
+ skipped. If a question would change the specs, the chosen approach, or
153
+ the task breakdown, resolve it now - ask the user instead of guessing.
105
154
 
106
155
  Focus on architecture and approach, not line-by-line implementation.
107
- Reference the proposal for motivation and specs for requirements.
156
+ The proposal covers why and what; design covers how. Reference the
157
+ proposal for motivation and, once written, the specs for requirements -
158
+ if a section would only restate them, point to them instead.
108
159
 
109
160
  Good design docs explain the "why" behind technical decisions.
110
161
  requires:
@@ -117,6 +168,10 @@ artifacts:
117
168
  instruction: |
118
169
  Create the task list that breaks down the implementation work.
119
170
 
171
+ Before writing tasks, check design.md for Open Questions. If any of them
172
+ would change what gets built, resolve them with the user first - do not
173
+ bake an unstated assumption into the task list.
174
+
120
175
  **IMPORTANT: Follow the template below exactly.** The apply phase parses
121
176
  checkbox format to track progress. Tasks not using `- [ ]` won't be tracked.
122
177
 
@@ -1,6 +1,6 @@
1
1
  ## Context
2
2
 
3
- <!-- Background and current state -->
3
+ <!-- Current state and constraints that shape the approach. See proposal.md for motivation - don't restate it -->
4
4
 
5
5
  ## Goals / Non-Goals
6
6
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  ## Decisions
14
14
 
15
- <!-- Key design decisions and rationale -->
15
+ <!-- Key design decisions with rationale and alternatives considered -->
16
16
 
17
17
  ## Risks / Trade-offs
18
18
 
@@ -15,7 +15,11 @@
15
15
  ### Modified Capabilities
16
16
  <!-- Existing capabilities whose REQUIREMENTS are changing (not just implementation).
17
17
  Only list here if spec-level behavior changes. Each needs a delta spec file.
18
- Use existing spec names from openspec/specs/. Leave empty if no requirement changes. -->
18
+ Use existing spec names from openspec/specs/. Leave empty if no requirement
19
+ changes. A change with no capabilities at all (pure refactor, tooling, docs)
20
+ must set `skip_specs: true` in its .openspec.yaml - openspec validate rejects
21
+ a zero-delta change without that marker. Do not invent a requirement just to
22
+ satisfy validation. -->
19
23
  - `<existing-name>`: <what requirement is changing>
20
24
 
21
25
  ## Impact
@@ -1,3 +1,6 @@
1
+ ## Purpose
2
+ <!-- New capabilities only: one or two sentences (50+ characters) on what this capability is for. Delete this section for an existing capability. -->
3
+
1
4
  ## ADDED Requirements
2
5
 
3
6
  ### Requirement: <!-- requirement name -->
@@ -1,16 +0,0 @@
1
- /**
2
- * Codex Command Adapter
3
- *
4
- * Formats commands for Codex following its frontmatter specification.
5
- * Codex custom prompts live in the global home directory (~/.codex/prompts/)
6
- * and are not shared through the repository. The CODEX_HOME env var can
7
- * override the default ~/.codex location.
8
- */
9
- import type { ToolCommandAdapter } from '../types.js';
10
- /**
11
- * Codex adapter for command generation.
12
- * File path: <CODEX_HOME>/prompts/opsx-<id>.md (absolute, global)
13
- * Frontmatter: description, argument-hint
14
- */
15
- export declare const codexAdapter: ToolCommandAdapter;
16
- //# sourceMappingURL=codex.d.ts.map
@@ -1,39 +0,0 @@
1
- /**
2
- * Codex Command Adapter
3
- *
4
- * Formats commands for Codex following its frontmatter specification.
5
- * Codex custom prompts live in the global home directory (~/.codex/prompts/)
6
- * and are not shared through the repository. The CODEX_HOME env var can
7
- * override the default ~/.codex location.
8
- */
9
- import os from 'os';
10
- import path from 'path';
11
- /**
12
- * Returns the Codex home directory.
13
- * Respects the CODEX_HOME env var, defaulting to ~/.codex.
14
- */
15
- function getCodexHome() {
16
- const envHome = process.env.CODEX_HOME?.trim();
17
- return path.resolve(envHome ? envHome : path.join(os.homedir(), '.codex'));
18
- }
19
- /**
20
- * Codex adapter for command generation.
21
- * File path: <CODEX_HOME>/prompts/opsx-<id>.md (absolute, global)
22
- * Frontmatter: description, argument-hint
23
- */
24
- export const codexAdapter = {
25
- toolId: 'codex',
26
- getFilePath(commandId) {
27
- return path.join(getCodexHome(), 'prompts', `opsx-${commandId}.md`);
28
- },
29
- formatFile(content) {
30
- return `---
31
- description: ${content.description}
32
- argument-hint: command arguments
33
- ---
34
-
35
- ${content.body}
36
- `;
37
- },
38
- };
39
- //# sourceMappingURL=codex.js.map
@@ -1,14 +0,0 @@
1
- /**
2
- * Windsurf Command Adapter
3
- *
4
- * Formats commands for Windsurf following its frontmatter specification.
5
- * Windsurf uses a similar format to Claude but may have different conventions.
6
- */
7
- import type { ToolCommandAdapter } from '../types.js';
8
- /**
9
- * Windsurf adapter for command generation.
10
- * File path: .windsurf/workflows/opsx-<id>.md
11
- * Frontmatter: name, description, category, tags
12
- */
13
- export declare const windsurfAdapter: ToolCommandAdapter;
14
- //# sourceMappingURL=windsurf.d.ts.map
@@ -1,38 +0,0 @@
1
- /**
2
- * Windsurf Command Adapter
3
- *
4
- * Formats commands for Windsurf following its frontmatter specification.
5
- * Windsurf uses a similar format to Claude but may have different conventions.
6
- */
7
- import path from 'path';
8
- import { escapeYamlValue } from '../yaml.js';
9
- /**
10
- * Formats a tags array as a YAML array with proper escaping.
11
- */
12
- function formatTagsArray(tags) {
13
- const escapedTags = tags.map((tag) => escapeYamlValue(tag));
14
- return `[${escapedTags.join(', ')}]`;
15
- }
16
- /**
17
- * Windsurf adapter for command generation.
18
- * File path: .windsurf/workflows/opsx-<id>.md
19
- * Frontmatter: name, description, category, tags
20
- */
21
- export const windsurfAdapter = {
22
- toolId: 'windsurf',
23
- getFilePath(commandId) {
24
- return path.join('.windsurf', 'workflows', `opsx-${commandId}.md`);
25
- },
26
- formatFile(content) {
27
- return `---
28
- name: ${escapeYamlValue(content.name)}
29
- description: ${escapeYamlValue(content.description)}
30
- category: ${escapeYamlValue(content.category)}
31
- tags: ${formatTagsArray(content.tags)}
32
- ---
33
-
34
- ${content.body}
35
- `;
36
- },
37
- };
38
- //# sourceMappingURL=windsurf.js.map