@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.
- package/README.md +28 -1
- package/dist/cli/index.js +73 -7
- package/dist/commands/change.d.ts +0 -1
- package/dist/commands/change.js +90 -50
- package/dist/commands/config.js +14 -8
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/feedback.js +74 -23
- package/dist/commands/schema.js +21 -11
- package/dist/commands/show.js +5 -2
- package/dist/commands/spec.js +20 -24
- package/dist/commands/validate.js +18 -3
- package/dist/commands/workflow/index.d.ts +1 -1
- package/dist/commands/workflow/index.js +1 -1
- package/dist/commands/workflow/instructions.d.ts +7 -1
- package/dist/commands/workflow/instructions.js +89 -6
- package/dist/commands/workflow/shared.d.ts +13 -2
- package/dist/commands/workflow/shared.js +34 -4
- package/dist/commands/workflow/status.js +7 -2
- package/dist/core/archive.d.ts +0 -1
- package/dist/core/archive.js +96 -33
- package/dist/core/artifact-graph/graph.d.ts +16 -0
- package/dist/core/artifact-graph/graph.js +30 -6
- package/dist/core/artifact-graph/instruction-loader.d.ts +27 -2
- package/dist/core/artifact-graph/instruction-loader.js +55 -7
- package/dist/core/artifact-graph/resolver.d.ts +14 -0
- package/dist/core/artifact-graph/resolver.js +34 -6
- package/dist/core/change-metadata/schema.d.ts +1 -0
- package/dist/core/change-metadata/schema.js +6 -0
- package/dist/core/change-status-policy.d.ts +1 -1
- package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
- package/dist/core/command-generation/adapters/amazon-q.js +7 -1
- package/dist/core/command-generation/adapters/antigravity.js +2 -1
- package/dist/core/command-generation/adapters/auggie.js +2 -1
- package/dist/core/command-generation/adapters/bob.d.ts +5 -1
- package/dist/core/command-generation/adapters/bob.js +6 -5
- package/dist/core/command-generation/adapters/claude.js +1 -8
- package/dist/core/command-generation/adapters/codebuddy.js +3 -2
- package/dist/core/command-generation/adapters/continue.js +3 -2
- package/dist/core/command-generation/adapters/costrict.js +2 -1
- package/dist/core/command-generation/adapters/crush.js +5 -5
- package/dist/core/command-generation/adapters/cursor.js +2 -2
- package/dist/core/command-generation/adapters/devin.d.ts +19 -0
- package/dist/core/command-generation/adapters/devin.js +36 -0
- package/dist/core/command-generation/adapters/factory.js +2 -1
- package/dist/core/command-generation/adapters/gemini.js +37 -2
- package/dist/core/command-generation/adapters/github-copilot.js +2 -1
- package/dist/core/command-generation/adapters/iflow.js +5 -4
- package/dist/core/command-generation/adapters/index.d.ts +2 -2
- package/dist/core/command-generation/adapters/index.js +2 -2
- package/dist/core/command-generation/adapters/junie.js +2 -1
- package/dist/core/command-generation/adapters/kiro.js +2 -1
- package/dist/core/command-generation/adapters/lingma.js +5 -5
- package/dist/core/command-generation/adapters/oh-my-pi.d.ts +2 -2
- package/dist/core/command-generation/adapters/oh-my-pi.js +3 -5
- package/dist/core/command-generation/adapters/opencode.js +3 -5
- package/dist/core/command-generation/adapters/pi.d.ts +2 -2
- package/dist/core/command-generation/adapters/pi.js +3 -6
- package/dist/core/command-generation/adapters/qoder.js +5 -5
- package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
- package/dist/core/command-generation/adapters/qwen.js +12 -7
- package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
- package/dist/core/command-generation/adapters/roocode.js +4 -4
- package/dist/core/command-generation/adapters/trae.js +1 -21
- package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
- package/dist/core/command-generation/adapters/zcode.js +33 -0
- package/dist/core/command-generation/generator.d.ts +8 -0
- package/dist/core/command-generation/generator.js +15 -1
- package/dist/core/command-generation/index.d.ts +1 -1
- package/dist/core/command-generation/index.js +1 -1
- package/dist/core/command-generation/invocation.d.ts +73 -0
- package/dist/core/command-generation/invocation.js +77 -0
- package/dist/core/command-generation/registry.js +4 -4
- package/dist/core/command-generation/types.d.ts +9 -2
- package/dist/core/command-generation/yaml.d.ts +20 -6
- package/dist/core/command-generation/yaml.js +31 -21
- package/dist/core/command-surface.d.ts +15 -0
- package/dist/core/command-surface.js +33 -0
- package/dist/core/completions/command-registry.js +8 -2
- package/dist/core/completions/generators/powershell-generator.js +15 -8
- package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
- package/dist/core/completions/installers/zsh-installer.js +20 -4
- package/dist/core/config-prompts.d.ts +1 -1
- package/dist/core/config-prompts.js +14 -1
- package/dist/core/config-schema.d.ts +7 -1
- package/dist/core/config-schema.js +40 -1
- package/dist/core/config.d.ts +12 -0
- package/dist/core/config.js +21 -3
- package/dist/core/global-config.d.ts +5 -0
- package/dist/core/init.d.ts +35 -0
- package/dist/core/init.js +225 -39
- package/dist/core/legacy-cleanup.d.ts +59 -0
- package/dist/core/legacy-cleanup.js +249 -22
- package/dist/core/list.js +5 -7
- package/dist/core/migration.d.ts +71 -1
- package/dist/core/migration.js +294 -1
- package/dist/core/onboarding-commands.d.ts +30 -0
- package/dist/core/onboarding-commands.js +38 -0
- package/dist/core/parsers/change-parser.d.ts +17 -2
- package/dist/core/parsers/change-parser.js +33 -20
- package/dist/core/parsers/code-fence.d.ts +15 -0
- package/dist/core/parsers/code-fence.js +49 -0
- package/dist/core/parsers/markdown-parser.js +2 -1
- package/dist/core/parsers/requirement-blocks.d.ts +8 -0
- package/dist/core/parsers/requirement-blocks.js +60 -35
- package/dist/core/parsers/requirement-text.d.ts +1 -8
- package/dist/core/parsers/requirement-text.js +4 -42
- package/dist/core/parsers/spec-structure.js +3 -29
- package/dist/core/profile-sync-drift.d.ts +0 -8
- package/dist/core/profile-sync-drift.js +9 -45
- package/dist/core/project-config.d.ts +25 -6
- package/dist/core/project-config.js +77 -7
- package/dist/core/references.js +42 -4
- package/dist/core/relationship-health.d.ts +8 -0
- package/dist/core/relationship-health.js +13 -0
- package/dist/core/root-selection.d.ts +9 -6
- package/dist/core/root-selection.js +42 -5
- package/dist/core/shared/tool-detection.d.ts +25 -5
- package/dist/core/shared/tool-detection.js +105 -6
- package/dist/core/specs-apply.d.ts +8 -33
- package/dist/core/specs-apply.js +252 -157
- package/dist/core/store/git.d.ts +13 -0
- package/dist/core/store/git.js +24 -0
- package/dist/core/templates/workflows/apply-change.js +54 -4
- package/dist/core/templates/workflows/archive-change.js +166 -42
- package/dist/core/templates/workflows/bulk-archive-change.js +240 -63
- package/dist/core/templates/workflows/continue-change.js +32 -40
- package/dist/core/templates/workflows/explore.js +12 -0
- package/dist/core/templates/workflows/ff-change.js +46 -30
- package/dist/core/templates/workflows/new-change.js +2 -2
- package/dist/core/templates/workflows/onboard.js +4 -4
- package/dist/core/templates/workflows/propose.js +50 -32
- package/dist/core/templates/workflows/store-selection.d.ts +1 -1
- package/dist/core/templates/workflows/store-selection.js +1 -1
- package/dist/core/templates/workflows/sync-specs.js +172 -22
- package/dist/core/templates/workflows/update-change.js +20 -12
- package/dist/core/templates/workflows/verify-change.js +16 -10
- package/dist/core/update.d.ts +39 -5
- package/dist/core/update.js +288 -71
- package/dist/core/validation/constants.d.ts +4 -1
- package/dist/core/validation/constants.js +4 -1
- package/dist/core/validation/validator.d.ts +1 -7
- package/dist/core/validation/validator.js +92 -36
- package/dist/core/version-check.d.ts +152 -0
- package/dist/core/version-check.js +736 -0
- package/dist/core/view.js +12 -17
- package/dist/prompts/searchable-multi-select.js +1 -1
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.js +55 -33
- package/dist/ui/welcome-screen.d.ts +9 -1
- package/dist/ui/welcome-screen.js +91 -38
- package/dist/utils/change-metadata.d.ts +30 -0
- package/dist/utils/change-metadata.js +101 -7
- package/dist/utils/change-utils.d.ts +10 -4
- package/dist/utils/change-utils.js +20 -13
- package/dist/utils/command-references.d.ts +80 -6
- package/dist/utils/command-references.js +138 -7
- package/dist/utils/date.d.ts +8 -0
- package/dist/utils/date.js +13 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/item-discovery.d.ts +19 -0
- package/dist/utils/item-discovery.js +30 -48
- package/dist/utils/shell-detection.d.ts +1 -1
- package/dist/utils/shell-detection.js +64 -11
- package/dist/utils/spec-discovery.d.ts +40 -0
- package/dist/utils/spec-discovery.js +102 -0
- package/package.json +29 -30
- package/schemas/spec-driven/schema.yaml +60 -5
- package/schemas/spec-driven/templates/design.md +2 -2
- package/schemas/spec-driven/templates/proposal.md +5 -1
- package/schemas/spec-driven/templates/spec.md +3 -0
- package/dist/core/command-generation/adapters/codex.d.ts +0 -16
- package/dist/core/command-generation/adapters/codex.js +0 -39
- package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
- 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.
|
|
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**:
|
|
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**:
|
|
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
|
-
|
|
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
|
-
<!--
|
|
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
|
|
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
|
|
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,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
|