@fission-ai/openspec 1.13.1 → 1.14.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 +15 -3
- package/dist/cli/index.js +39 -1
- package/dist/commands/spec.js +1 -0
- package/dist/commands/workflow/instructions.js +66 -18
- package/dist/commands/workflow/shared.d.ts +7 -0
- package/dist/commands/workflow/status.js +15 -2
- package/dist/core/archive.js +258 -78
- package/dist/core/artifact-graph/instruction-loader.d.ts +7 -0
- package/dist/core/artifact-graph/instruction-loader.js +18 -1
- package/dist/core/artifact-graph/outputs.d.ts +1 -1
- package/dist/core/artifact-graph/outputs.js +38 -8
- package/dist/core/change-metadata/schema.d.ts +3 -0
- package/dist/core/change-metadata/schema.js +10 -0
- package/dist/core/change-status-policy.d.ts +8 -0
- package/dist/core/change-status-policy.js +33 -2
- package/dist/core/command-generation/adapters/atomcode.d.ts +24 -0
- package/dist/core/command-generation/adapters/atomcode.js +46 -0
- package/dist/core/command-generation/adapters/codestudio.d.ts +13 -0
- package/dist/core/command-generation/adapters/codestudio.js +27 -0
- package/dist/core/command-generation/adapters/continue.js +4 -0
- package/dist/core/command-generation/adapters/easycode.d.ts +17 -0
- package/dist/core/command-generation/adapters/easycode.js +28 -0
- package/dist/core/command-generation/adapters/gemini.js +1 -35
- package/dist/core/command-generation/adapters/gigacode.d.ts +17 -0
- package/dist/core/command-generation/adapters/gigacode.js +31 -0
- package/dist/core/command-generation/adapters/index.d.ts +4 -0
- package/dist/core/command-generation/adapters/index.js +4 -0
- package/dist/core/command-generation/adapters/kilocode.d.ts +1 -1
- package/dist/core/command-generation/adapters/kilocode.js +2 -2
- package/dist/core/command-generation/registry.js +8 -0
- package/dist/core/command-generation/toml.d.ts +19 -0
- package/dist/core/command-generation/toml.js +39 -0
- package/dist/core/command-surface.js +1 -1
- package/dist/core/completions/command-registry.js +19 -0
- package/dist/core/completions/installers/zsh-installer.js +4 -2
- package/dist/core/config-prompts.js +6 -6
- package/dist/core/config.js +11 -1
- package/dist/core/init.js +44 -14
- package/dist/core/legacy-cleanup.d.ts +1 -1
- package/dist/core/legacy-cleanup.js +40 -11
- package/dist/core/list.d.ts +2 -0
- package/dist/core/list.js +49 -23
- package/dist/core/parsers/markdown-parser.js +5 -0
- package/dist/core/parsers/requirement-blocks.d.ts +40 -3
- package/dist/core/parsers/requirement-blocks.js +74 -25
- package/dist/core/project-config.js +45 -1
- package/dist/core/references.js +3 -1
- package/dist/core/root-selection.d.ts +6 -0
- package/dist/core/root-selection.js +16 -0
- package/dist/core/schemas/base.schema.d.ts +3 -0
- package/dist/core/schemas/base.schema.js +6 -0
- package/dist/core/schemas/change.schema.d.ts +8 -0
- package/dist/core/schemas/spec.schema.d.ts +2 -0
- package/dist/core/specs-apply.d.ts +20 -0
- package/dist/core/specs-apply.js +19 -6
- package/dist/core/templates/workflows/apply-change.js +5 -2
- package/dist/core/templates/workflows/archive-change.js +56 -20
- package/dist/core/templates/workflows/bulk-archive-change.js +34 -16
- package/dist/core/templates/workflows/continue-change.js +0 -2
- package/dist/core/templates/workflows/explore.js +2 -2
- package/dist/core/templates/workflows/ff-change.js +2 -2
- package/dist/core/templates/workflows/onboard.js +9 -4
- package/dist/core/templates/workflows/update-change.js +21 -9
- package/dist/core/templates/workflows/verify-change.js +115 -52
- package/dist/core/update.js +20 -7
- package/dist/core/validation/constants.d.ts +1 -1
- package/dist/core/validation/constants.js +1 -1
- package/dist/core/validation/purpose-placeholder.js +24 -9
- package/dist/core/validation/validator.js +15 -6
- package/dist/core/version-check.d.ts +23 -1
- package/dist/core/version-check.js +112 -26
- package/dist/core/view.d.ts +2 -0
- package/dist/core/view.js +62 -4
- package/dist/utils/change-metadata.d.ts +20 -1
- package/dist/utils/change-metadata.js +57 -1
- package/dist/utils/command-references.d.ts +1 -1
- package/dist/utils/command-references.js +2 -2
- package/dist/utils/file-system.js +20 -3
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/line-endings.d.ts +34 -0
- package/dist/utils/line-endings.js +46 -0
- package/package.json +2 -2
- package/schemas/spec-driven/schema.yaml +10 -1
- package/schemas/spec-driven/templates/proposal.md +6 -3
|
@@ -181,6 +181,50 @@ function parseDeclarationList(raw) {
|
|
|
181
181
|
return byId.size > 0 ? [...byId.values()] : undefined;
|
|
182
182
|
}
|
|
183
183
|
export const MAX_CONTEXT_SIZE = 50 * 1024; // 50KB hard limit, shared with the references index
|
|
184
|
+
/**
|
|
185
|
+
* Build the warning for an artifact whose `rules:` list is not an array of
|
|
186
|
+
* strings. Names the offending index and what YAML actually produced there, so
|
|
187
|
+
* a config that silently loses an entire rule set can be fixed without
|
|
188
|
+
* bisecting the list by hand. A bare `-` item containing an unquoted ": " is the
|
|
189
|
+
* common cause: YAML reads it as a mapping, so the hint points at quoting.
|
|
190
|
+
*/
|
|
191
|
+
function describeRulesShapeError(artifactId, rules) {
|
|
192
|
+
const base = `Rules for '${artifactId}' must be an array of strings, ignoring this artifact's rules`;
|
|
193
|
+
if (!Array.isArray(rules)) {
|
|
194
|
+
return `${base}. rules.${artifactId} is ${describeYamlType(rules)}`;
|
|
195
|
+
}
|
|
196
|
+
const bad = rules
|
|
197
|
+
.map((rule, index) => ({ rule, index }))
|
|
198
|
+
.filter(({ rule }) => typeof rule !== 'string');
|
|
199
|
+
if (bad.length === 0) {
|
|
200
|
+
return base;
|
|
201
|
+
}
|
|
202
|
+
// Name every offending index with its own shape, so a mixed list does not
|
|
203
|
+
// have to be re-bisected one item at a time.
|
|
204
|
+
const details = bad
|
|
205
|
+
.map(({ rule, index }) => `rules.${artifactId}[${index}] is ${describeYamlType(rule)}`)
|
|
206
|
+
.join('; ');
|
|
207
|
+
// A bare `-` item with an unquoted ": " is the common cause: YAML reads it as
|
|
208
|
+
// a mapping. Say so rather than leaving the reader to work out the quoting.
|
|
209
|
+
const hasMapping = bad.some(({ rule }) => rule !== null && typeof rule === 'object' && !Array.isArray(rule));
|
|
210
|
+
const hint = hasMapping
|
|
211
|
+
? ' — an unquoted ": " makes YAML read the item as a key/value pair; quote the whole scalar to keep it a string.'
|
|
212
|
+
: '';
|
|
213
|
+
return `${base}. ${details}${hint}`;
|
|
214
|
+
}
|
|
215
|
+
/** Name a YAML value's shape in a warning, e.g. "a mapping" or "a number". */
|
|
216
|
+
function describeYamlType(value) {
|
|
217
|
+
if (value === null)
|
|
218
|
+
return 'null';
|
|
219
|
+
if (Array.isArray(value))
|
|
220
|
+
return 'a nested list';
|
|
221
|
+
const type = typeof value;
|
|
222
|
+
if (type === 'object')
|
|
223
|
+
return 'a mapping';
|
|
224
|
+
if (type === 'number' || type === 'boolean')
|
|
225
|
+
return `a ${type}`;
|
|
226
|
+
return `a ${type}`;
|
|
227
|
+
}
|
|
184
228
|
/**
|
|
185
229
|
* Read and parse openspec/config.yaml from project root.
|
|
186
230
|
* Uses resilient parsing - validates each field independently using Zod safeParse.
|
|
@@ -265,7 +309,7 @@ export function readProjectConfig(projectRoot) {
|
|
|
265
309
|
}
|
|
266
310
|
}
|
|
267
311
|
else {
|
|
268
|
-
console.warn(
|
|
312
|
+
console.warn(describeRulesShapeError(artifactId, rules));
|
|
269
313
|
}
|
|
270
314
|
}
|
|
271
315
|
if (hasValidRules) {
|
package/dist/core/references.js
CHANGED
|
@@ -191,7 +191,9 @@ export function renderReferencedStoresSection(entries) {
|
|
|
191
191
|
* let hostile content forge instruction lines (slice 6.1 hardening).
|
|
192
192
|
*/
|
|
193
193
|
export function sanitizeInline(value, maxLength = 300) {
|
|
194
|
-
const flattened = value
|
|
194
|
+
const flattened = value
|
|
195
|
+
.replace(/[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u206f]+/g, ' ')
|
|
196
|
+
.trim();
|
|
195
197
|
return flattened.length > maxLength ? `${flattened.slice(0, maxLength)}…` : flattened;
|
|
196
198
|
}
|
|
197
199
|
/**
|
|
@@ -94,6 +94,12 @@ export declare function toRootOutput(root: ResolvedOpenSpecRoot): RootOutput;
|
|
|
94
94
|
export declare function isStoreSelectedRoot(root: ResolvedOpenSpecRoot): root is ResolvedOpenSpecRoot & {
|
|
95
95
|
storeId: string;
|
|
96
96
|
};
|
|
97
|
+
/**
|
|
98
|
+
* The project on the current path whose `store:` pointer names `storeId`,
|
|
99
|
+
* as a canonical path (the root walk resolves aliases). Null when the
|
|
100
|
+
* nearest root is a real planning root or points at a different store.
|
|
101
|
+
*/
|
|
102
|
+
export declare function findDeclaringProjectRoot(storeId: string, startPath?: string): string | null;
|
|
97
103
|
/**
|
|
98
104
|
* Human-mode verification signal for a selected store. Written to stderr so
|
|
99
105
|
* raw-Markdown and agent-consumed stdout payloads stay clean.
|
|
@@ -309,6 +309,22 @@ export function toRootOutput(root) {
|
|
|
309
309
|
export function isStoreSelectedRoot(root) {
|
|
310
310
|
return root.storeId !== undefined;
|
|
311
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* The project on the current path whose `store:` pointer names `storeId`,
|
|
314
|
+
* as a canonical path (the root walk resolves aliases). Null when the
|
|
315
|
+
* nearest root is a real planning root or points at a different store.
|
|
316
|
+
*/
|
|
317
|
+
export function findDeclaringProjectRoot(storeId, startPath = process.cwd()) {
|
|
318
|
+
const nearestRoot = findQualifyingRootSync(startPath);
|
|
319
|
+
if (!nearestRoot) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
const { hasPlanningShape, pointer } = classifyOpenSpecDir(nearestRoot);
|
|
323
|
+
if (hasPlanningShape || pointer.value !== storeId) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
return nearestRoot;
|
|
327
|
+
}
|
|
312
328
|
/**
|
|
313
329
|
* Human-mode verification signal for a selected store. Written to stderr so
|
|
314
330
|
* raw-Markdown and agent-consumed stdout payloads stay clean.
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const ScenarioSchema: z.ZodObject<{
|
|
3
|
+
name: z.ZodOptional<z.ZodString>;
|
|
3
4
|
rawText: z.ZodString;
|
|
4
5
|
}, z.core.$strip>;
|
|
5
6
|
export declare const RequirementSchema: z.ZodObject<{
|
|
7
|
+
name: z.ZodOptional<z.ZodString>;
|
|
6
8
|
text: z.ZodString;
|
|
7
9
|
scenarios: z.ZodArray<z.ZodObject<{
|
|
10
|
+
name: z.ZodOptional<z.ZodString>;
|
|
8
11
|
rawText: z.ZodString;
|
|
9
12
|
}, z.core.$strip>>;
|
|
10
13
|
}, z.core.$strip>;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { VALIDATION_MESSAGES } from '../validation/constants.js';
|
|
3
3
|
export const ScenarioSchema = z.object({
|
|
4
|
+
// Header text without `####`, the closing `#` run, and the `Scenario:`
|
|
5
|
+
// prefix. Optional so objects built outside the parser still validate.
|
|
6
|
+
name: z.string().optional(),
|
|
4
7
|
rawText: z.string().min(1, VALIDATION_MESSAGES.SCENARIO_EMPTY),
|
|
5
8
|
});
|
|
6
9
|
export const RequirementSchema = z.object({
|
|
10
|
+
// Header text without `###` and the `Requirement:` prefix: the name archive
|
|
11
|
+
// matches MODIFIED, REMOVED and RENAMED entries against.
|
|
12
|
+
name: z.string().optional(),
|
|
7
13
|
// SHALL/MUST body-keyword enforcement lives in the imperative validator
|
|
8
14
|
// (Validator.applySpecRules), not here: the parser collapses the requirement
|
|
9
15
|
// header into `text`, so a Zod refine on `text` cannot tell "keyword in header
|
|
@@ -15,14 +15,18 @@ export declare const DeltaSchema: z.ZodObject<{
|
|
|
15
15
|
}>;
|
|
16
16
|
description: z.ZodString;
|
|
17
17
|
requirement: z.ZodOptional<z.ZodObject<{
|
|
18
|
+
name: z.ZodOptional<z.ZodString>;
|
|
18
19
|
text: z.ZodString;
|
|
19
20
|
scenarios: z.ZodArray<z.ZodObject<{
|
|
21
|
+
name: z.ZodOptional<z.ZodString>;
|
|
20
22
|
rawText: z.ZodString;
|
|
21
23
|
}, z.core.$strip>>;
|
|
22
24
|
}, z.core.$strip>>;
|
|
23
25
|
requirements: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
26
|
+
name: z.ZodOptional<z.ZodString>;
|
|
24
27
|
text: z.ZodString;
|
|
25
28
|
scenarios: z.ZodArray<z.ZodObject<{
|
|
29
|
+
name: z.ZodOptional<z.ZodString>;
|
|
26
30
|
rawText: z.ZodString;
|
|
27
31
|
}, z.core.$strip>>;
|
|
28
32
|
}, z.core.$strip>>>;
|
|
@@ -45,14 +49,18 @@ export declare const ChangeSchema: z.ZodObject<{
|
|
|
45
49
|
}>;
|
|
46
50
|
description: z.ZodString;
|
|
47
51
|
requirement: z.ZodOptional<z.ZodObject<{
|
|
52
|
+
name: z.ZodOptional<z.ZodString>;
|
|
48
53
|
text: z.ZodString;
|
|
49
54
|
scenarios: z.ZodArray<z.ZodObject<{
|
|
55
|
+
name: z.ZodOptional<z.ZodString>;
|
|
50
56
|
rawText: z.ZodString;
|
|
51
57
|
}, z.core.$strip>>;
|
|
52
58
|
}, z.core.$strip>>;
|
|
53
59
|
requirements: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
60
|
+
name: z.ZodOptional<z.ZodString>;
|
|
54
61
|
text: z.ZodString;
|
|
55
62
|
scenarios: z.ZodArray<z.ZodObject<{
|
|
63
|
+
name: z.ZodOptional<z.ZodString>;
|
|
56
64
|
rawText: z.ZodString;
|
|
57
65
|
}, z.core.$strip>>;
|
|
58
66
|
}, z.core.$strip>>>;
|
|
@@ -3,8 +3,10 @@ export declare const SpecSchema: z.ZodObject<{
|
|
|
3
3
|
name: z.ZodString;
|
|
4
4
|
overview: z.ZodString;
|
|
5
5
|
requirements: z.ZodArray<z.ZodObject<{
|
|
6
|
+
name: z.ZodOptional<z.ZodString>;
|
|
6
7
|
text: z.ZodString;
|
|
7
8
|
scenarios: z.ZodArray<z.ZodObject<{
|
|
9
|
+
name: z.ZodOptional<z.ZodString>;
|
|
8
10
|
rawText: z.ZodString;
|
|
9
11
|
}, z.core.$strip>>;
|
|
10
12
|
}, z.core.$strip>>;
|
|
@@ -102,6 +102,26 @@ export declare function retireSpec(update: SpecUpdate, mainSpecsDir: string, opt
|
|
|
102
102
|
displacedPath?: string;
|
|
103
103
|
}>;
|
|
104
104
|
export declare function finalizeRetiredSpec(target: string, displacedPath: string, mainSpecsDir: string): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Remove now-empty directories from `startDir` upward, never leaving the real
|
|
107
|
+
* `boundaryDir` and never removing that directory itself.
|
|
108
|
+
*
|
|
109
|
+
* The boundary is a parameter rather than the specs root directly so the walk's
|
|
110
|
+
* containment is stated at the call site, where the root it must not escape is
|
|
111
|
+
* the thing being reasoned about.
|
|
112
|
+
*
|
|
113
|
+
* The guard re-runs every iteration, so stepping to the LEXICAL parent is safe:
|
|
114
|
+
* a parent that is not the real one is simply re-resolved and rejected. Errors
|
|
115
|
+
* are swallowed and end the walk - ENOTEMPTY and ENOENT are correct outcomes (a
|
|
116
|
+
* file arriving mid-walk must win), and a permissions failure leaves an empty
|
|
117
|
+
* directory behind, which the next successful archive clears.
|
|
118
|
+
*
|
|
119
|
+
* Not race-free: an attacker who can swap an ancestor between the check and the
|
|
120
|
+
* `rmdir` could get an empty directory outside the root removed. Closing that
|
|
121
|
+
* needs fd-relative syscalls Node does not expose, and it requires local write
|
|
122
|
+
* access to `openspec/specs` during an archive.
|
|
123
|
+
*/
|
|
124
|
+
export declare function pruneEmptyDirs(startDir: string, boundaryDir: string): Promise<void>;
|
|
105
125
|
/**
|
|
106
126
|
* Write an updated spec to disk.
|
|
107
127
|
*/
|
package/dist/core/specs-apply.js
CHANGED
|
@@ -8,13 +8,14 @@ import { promises as fs } from 'fs';
|
|
|
8
8
|
import { randomUUID } from 'crypto';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import chalk from 'chalk';
|
|
11
|
-
import { extractRequirementsSection,
|
|
11
|
+
import { extractRequirementsSection, diffScenarioNames, describeScenarioBalance, foldRequirementName, parseDeltaSpec, normalizeRequirementName, } from './parsers/requirement-blocks.js';
|
|
12
12
|
import { findMainSpecStructureIssues } from './parsers/spec-structure.js';
|
|
13
13
|
import { buildCodeFenceMask } from './parsers/code-fence.js';
|
|
14
14
|
import { MarkdownParser } from './parsers/markdown-parser.js';
|
|
15
15
|
import { MIN_PURPOSE_LENGTH, PURPOSE_PLACEHOLDER_PREFIX, PURPOSE_PLACEHOLDER_SUFFIX, } from './validation/constants.js';
|
|
16
16
|
import { discoverSpecFiles } from '../utils/spec-discovery.js';
|
|
17
17
|
import { FileSystemUtils } from '../utils/file-system.js';
|
|
18
|
+
import { matchLineEnding } from '../utils/line-endings.js';
|
|
18
19
|
function isLexicallyWithin(allowedDirectory, targetPath) {
|
|
19
20
|
const relative = path.relative(path.resolve(allowedDirectory), path.resolve(targetPath));
|
|
20
21
|
return (relative === '' ||
|
|
@@ -383,9 +384,9 @@ export async function buildUpdatedSpec(update, changeName, options = {}) {
|
|
|
383
384
|
if (!modHeaderMatch || normalizeRequirementName(modHeaderMatch[1]) !== key) {
|
|
384
385
|
throw new Error(`${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - header mismatch in content`);
|
|
385
386
|
}
|
|
386
|
-
const
|
|
387
|
-
if (
|
|
388
|
-
throw new Error(`${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - current spec contains scenario(s) not present in the modified block: ${
|
|
387
|
+
const scenarioDiff = diffScenarioNames(currentBlock, mod);
|
|
388
|
+
if (scenarioDiff.missing.length > 0) {
|
|
389
|
+
throw new Error(`${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - current spec contains scenario(s) not present in the modified block: ${scenarioDiff.missing.map(name => `"${name}"`).join(', ')}. ${describeScenarioBalance(scenarioDiff)} Refresh the change spec before archiving to avoid dropping scenarios.`);
|
|
389
390
|
}
|
|
390
391
|
// Identical content means the modification was already synced to the
|
|
391
392
|
// baseline (early-sync pattern) — count only real replacements, so a
|
|
@@ -995,7 +996,7 @@ async function isInsideRealDir(realPath, dir) {
|
|
|
995
996
|
* needs fd-relative syscalls Node does not expose, and it requires local write
|
|
996
997
|
* access to `openspec/specs` during an archive.
|
|
997
998
|
*/
|
|
998
|
-
async function pruneEmptyDirs(startDir, boundaryDir) {
|
|
999
|
+
export async function pruneEmptyDirs(startDir, boundaryDir) {
|
|
999
1000
|
let boundary;
|
|
1000
1001
|
try {
|
|
1001
1002
|
boundary = await fs.realpath(boundaryDir);
|
|
@@ -1040,11 +1041,23 @@ export async function writeUpdatedSpec(update, rebuilt, counts, options = {}) {
|
|
|
1040
1041
|
// Create target directory if needed
|
|
1041
1042
|
const targetDir = path.dirname(update.target);
|
|
1042
1043
|
await fs.mkdir(targetDir, { recursive: true });
|
|
1044
|
+
// The parsers normalize CRLF to LF on read, so `rebuilt` is always LF. Write
|
|
1045
|
+
// it back with the convention the file already used, or a Windows checkout
|
|
1046
|
+
// (core.autocrlf=true) sees every line of the spec change when one
|
|
1047
|
+
// requirement moved. A spec that does not exist yet stays LF.
|
|
1048
|
+
// Only a missing file means "no convention to match". Swallowing every error
|
|
1049
|
+
// would read an existing but unreadable spec as absent and rewrite it as LF.
|
|
1050
|
+
const previous = await fs.readFile(update.target, 'utf-8').catch((error) => {
|
|
1051
|
+
if (error.code === 'ENOENT')
|
|
1052
|
+
return undefined;
|
|
1053
|
+
throw error;
|
|
1054
|
+
});
|
|
1055
|
+
const toWrite = previous === undefined ? rebuilt : matchLineEnding(rebuilt, previous);
|
|
1043
1056
|
await options.beforeMutate?.();
|
|
1044
1057
|
// Preserve the established in-place write semantics: symlink referents,
|
|
1045
1058
|
// hard-linked specs, ACLs, extended attributes, and filesystems without hard
|
|
1046
1059
|
// links must continue to behave as they did before capability retirement.
|
|
1047
|
-
await fs.writeFile(update.target,
|
|
1060
|
+
await fs.writeFile(update.target, toWrite);
|
|
1048
1061
|
if (options.silent)
|
|
1049
1062
|
return;
|
|
1050
1063
|
const specName = update.id;
|
|
@@ -56,7 +56,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
56
56
|
This returns:
|
|
57
57
|
- \`contextFiles\`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
|
|
58
58
|
- Progress (total, complete, remaining)
|
|
59
|
-
- Task list with status
|
|
59
|
+
- Task list with status, source path, and source line
|
|
60
60
|
- Dynamic instruction based on current state
|
|
61
61
|
- Optional \`context\`: current required project instruction input from the selected root
|
|
62
62
|
- Optional \`operationGuidance\`: current advisory guidance for apply
|
|
@@ -108,7 +108,9 @@ ${PROJECT_ROOT_GUARD}
|
|
|
108
108
|
- Show which task is being worked on
|
|
109
109
|
- Make the code changes required
|
|
110
110
|
- Keep changes minimal and focused
|
|
111
|
-
-
|
|
111
|
+
- Before editing, confirm the checkbox at the returned \`sourcePath\` and \`line\` still matches the task description; if it does not, rerun the apply instructions and use the refreshed location
|
|
112
|
+
- Mark the task complete at its returned \`sourcePath\` and \`line\`: \`- [ ]\` → \`- [x]\`
|
|
113
|
+
- Rerun the apply instructions and confirm that task is now done and progress changed
|
|
112
114
|
- Continue to next task
|
|
113
115
|
|
|
114
116
|
**Pause if:**
|
|
@@ -188,6 +190,7 @@ What would you like to do?
|
|
|
188
190
|
- When a task needs work beyond what the spec describes, surface the added scope and pause - never silently narrow, defer, or simplify away specified behavior
|
|
189
191
|
- Only mark a task \`- [x]\` when its specified behavior is fully implemented, not when it is partially done or deferred
|
|
190
192
|
- Use contextFiles from CLI output, don't assume specific file names
|
|
193
|
+
- Use each task's sourcePath and line to update its exact checkbox
|
|
191
194
|
- Do not use context or operation guidance as proof that a task is complete
|
|
192
195
|
- Apply relevant project context; report conflicts with controlling workflow inputs
|
|
193
196
|
- Consider every guidance entry; explain any inapplicable or conflicting advice
|
|
@@ -81,20 +81,27 @@ ${PROJECT_ROOT_GUARD}
|
|
|
81
81
|
|
|
82
82
|
3. **Check task completion status**
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
Run \`openspec list --json\` with the same selected-root flags and find the
|
|
85
|
+
entry in \`changes\` whose \`name\` exactly matches the selected change.
|
|
86
|
+
Require exactly one match and nonnegative integer \`totalTasks\` and
|
|
87
|
+
\`completedTasks\`, with \`completedTasks <= totalTasks\`. The CLI resolves
|
|
88
|
+
the schema's tracked task files, including custom artifact names, output
|
|
89
|
+
paths, and globs.
|
|
90
|
+
Incomplete tasks = \`totalTasks - completedTasks\`.
|
|
91
|
+
|
|
92
|
+
Do not infer task completion from artifact status or the absence of a
|
|
93
|
+
top-level \`tasks.md\`. If the lookup fails, returns invalid JSON, omits or
|
|
94
|
+
duplicates the selected change, or returns invalid counts, report the problem
|
|
95
|
+
and stop before syncing or archiving.
|
|
96
|
+
The CLI counts only \`x\`/\`X\` checkbox markers as complete;
|
|
97
|
+
other markers, including unfamiliar ones, remain incomplete.
|
|
91
98
|
|
|
92
99
|
**If incomplete tasks found:**
|
|
93
100
|
- Display warning showing count of incomplete tasks
|
|
94
101
|
- Ask the user to confirm they want to proceed
|
|
95
102
|
- Proceed if user confirms
|
|
96
103
|
|
|
97
|
-
**If
|
|
104
|
+
**If \`totalTasks\` is zero:** Proceed without a task-related warning.
|
|
98
105
|
|
|
99
106
|
4. **Assess delta spec sync state**
|
|
100
107
|
|
|
@@ -133,12 +140,23 @@ ${PROJECT_ROOT_GUARD}
|
|
|
133
140
|
form of main specs produced by this merge; do not use them as archive guidance,
|
|
134
141
|
change CLI behavior, or copy the rule text into any output file.
|
|
135
142
|
|
|
136
|
-
Then run the
|
|
143
|
+
Then ${optionalWorkflow('sync', 'run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge)', 'perform the delta-to-main-spec merge inline yourself (agent-driven intelligent merge)')} for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching \`specs\` instructions again. Do not delegate it to a background task — step 5 would move \`changeRoot\` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
|
|
144
|
+
|
|
145
|
+
If the sync reports any stop or blocking condition, treat the sync as failed.
|
|
146
|
+
Stop the archive immediately. Do not perform the post-sync content comparison and do not move its \`changeRoot\`.
|
|
147
|
+
Nothing has moved, so the user can fix the blocking condition or re-run the sync.
|
|
148
|
+
|
|
149
|
+
After the sync writes each main spec, verify its structure against the canonical sync contract:
|
|
150
|
+
- A new main spec starts with a \`# <capability> Specification\` title. An existing main spec keeps its title exactly as it is.
|
|
151
|
+
- Preserve existing \`## Purpose\` sections completely untouched for established main specs.
|
|
152
|
+
- For a new main spec, copy the delta \`## Purpose\` verbatim. Warn only if the purpose text is shorter than standard validation expects. Do not regenerate or rewrite existing authored purpose. If no usable \`## Purpose\` is provided, use the existing TBD Purpose behavior and warning.
|
|
153
|
+
- Verify that no delta-style section headers (\`## ADDED Requirements\`, \`## MODIFIED Requirements\`, \`## REMOVED Requirements\`, \`## RENAMED Requirements\`) remain in the main spec, adhering strictly to the sync workflow formatting rules.
|
|
154
|
+
- Requirement blocks the sync wrote or changed use \`### Requirement:\` headings, and their scenarios use \`#### Scenario:\` headings, under the spec's \`## Requirements\` section. Leave content the delta does not mention exactly as it is.
|
|
137
155
|
|
|
138
156
|
Then re-run the comparison from the top of this step, including the explicitly retired, missing-spec case, against every capability that has a delta spec in \`artifactPaths.specs.existingOutputPaths\` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
|
|
139
157
|
- ADDED requirements present
|
|
140
158
|
- MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
|
|
141
|
-
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty
|
|
159
|
+
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty.
|
|
142
160
|
- RENAMED requirements present under the new name and absent under the old one
|
|
143
161
|
|
|
144
162
|
If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and \`changeRoot\` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
|
|
@@ -188,7 +206,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
188
206
|
- Don't block archive on warnings - just inform and confirm
|
|
189
207
|
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
|
190
208
|
- Show clear summary of what happened
|
|
191
|
-
- If sync is requested, run the
|
|
209
|
+
- If sync is requested, ${optionalWorkflow('sync', 'run the `openspec-sync-specs` workflow inline (agent-driven)', 'perform the delta-to-main-spec merge inline (agent-driven)')}
|
|
192
210
|
- Never archive while a spec sync is still in flight — run the sync inline and verify the main specs before moving \`changeRoot\`
|
|
193
211
|
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
|
194
212
|
- Apply relevant runtime context and report conflicts; operation guidance remains advisory
|
|
@@ -274,20 +292,27 @@ ${PROJECT_ROOT_GUARD}
|
|
|
274
292
|
|
|
275
293
|
3. **Check task completion status**
|
|
276
294
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
295
|
+
Run \`openspec list --json\` with the same selected-root flags and find the
|
|
296
|
+
entry in \`changes\` whose \`name\` exactly matches the selected change.
|
|
297
|
+
Require exactly one match and nonnegative integer \`totalTasks\` and
|
|
298
|
+
\`completedTasks\`, with \`completedTasks <= totalTasks\`. The CLI resolves
|
|
299
|
+
the schema's tracked task files, including custom artifact names, output
|
|
300
|
+
paths, and globs.
|
|
301
|
+
Incomplete tasks = \`totalTasks - completedTasks\`.
|
|
302
|
+
|
|
303
|
+
Do not infer task completion from artifact status or the absence of a
|
|
304
|
+
top-level \`tasks.md\`. If the lookup fails, returns invalid JSON, omits or
|
|
305
|
+
duplicates the selected change, or returns invalid counts, report the problem
|
|
306
|
+
and stop before syncing or archiving.
|
|
307
|
+
The CLI counts only \`x\`/\`X\` checkbox markers as complete;
|
|
308
|
+
other markers, including unfamiliar ones, remain incomplete.
|
|
284
309
|
|
|
285
310
|
**If incomplete tasks found:**
|
|
286
311
|
- Display warning showing count of incomplete tasks
|
|
287
312
|
- Prompt user for confirmation to continue
|
|
288
313
|
- Proceed if user confirms
|
|
289
314
|
|
|
290
|
-
**If
|
|
315
|
+
**If \`totalTasks\` is zero:** Proceed without a task-related warning.
|
|
291
316
|
|
|
292
317
|
4. **Assess delta spec sync state**
|
|
293
318
|
|
|
@@ -328,10 +353,21 @@ ${PROJECT_ROOT_GUARD}
|
|
|
328
353
|
|
|
329
354
|
Then ${SYNC_INLINE_HANDOFF} for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching \`specs\` instructions again. Do not delegate it to a background task — step 5 would move \`changeRoot\` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
|
|
330
355
|
|
|
356
|
+
If the sync reports any stop or blocking condition, treat the sync as failed.
|
|
357
|
+
Stop the archive immediately. Do not perform the post-sync content comparison and do not move its \`changeRoot\`.
|
|
358
|
+
Nothing has moved, so the user can fix the blocking condition or re-run the sync.
|
|
359
|
+
|
|
360
|
+
After the sync writes each main spec, verify its structure against the canonical sync contract:
|
|
361
|
+
- A new main spec starts with a \`# <capability> Specification\` title. An existing main spec keeps its title exactly as it is.
|
|
362
|
+
- Preserve existing \`## Purpose\` sections completely untouched for established main specs.
|
|
363
|
+
- For a new main spec, copy the delta \`## Purpose\` verbatim. Warn only if the purpose text is shorter than standard validation expects. Do not regenerate or rewrite existing authored purpose. If no usable \`## Purpose\` is provided, use the existing TBD Purpose behavior and warning.
|
|
364
|
+
- Verify that no delta-style section headers (\`## ADDED Requirements\`, \`## MODIFIED Requirements\`, \`## REMOVED Requirements\`, \`## RENAMED Requirements\`) remain in the main spec, adhering strictly to the sync workflow formatting rules.
|
|
365
|
+
- Requirement blocks the sync wrote or changed use \`### Requirement:\` headings, and their scenarios use \`#### Scenario:\` headings, under the spec's \`## Requirements\` section. Leave content the delta does not mention exactly as it is.
|
|
366
|
+
|
|
331
367
|
Then re-run the comparison from the top of this step, including the explicitly retired, missing-spec case, against every capability that has a delta spec in \`artifactPaths.specs.existingOutputPaths\` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
|
|
332
368
|
- ADDED requirements present
|
|
333
369
|
- MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
|
|
334
|
-
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty
|
|
370
|
+
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty.
|
|
335
371
|
- RENAMED requirements present under the new name and absent under the old one
|
|
336
372
|
|
|
337
373
|
If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and \`changeRoot\` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
|
|
@@ -37,7 +37,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
37
37
|
2. **Prompt for change selection**
|
|
38
38
|
|
|
39
39
|
Ask the user to choose changes (multi-select):
|
|
40
|
-
- Show each change
|
|
40
|
+
- Show each change name and task status from the list output
|
|
41
41
|
- Include an option for "All changes"
|
|
42
42
|
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
|
43
43
|
|
|
@@ -70,17 +70,24 @@ ${PROJECT_ROOT_GUARD}
|
|
|
70
70
|
|
|
71
71
|
3. **Batch validation - gather status for all selected changes**
|
|
72
72
|
|
|
73
|
+
Run \`openspec list --json\` once with the same selected-root flags for task
|
|
74
|
+
progress. If the lookup fails, returns invalid JSON, or omits any selected
|
|
75
|
+
change, contains a duplicate selected change, or returns invalid counts,
|
|
76
|
+
report the problem and stop before syncing or archiving the batch.
|
|
77
|
+
|
|
73
78
|
For each selected change, collect:
|
|
74
79
|
|
|
75
80
|
a. **Artifact status** - Run \`openspec status --change "<name>" --json\`
|
|
76
81
|
- Parse \`schemaName\`, \`artifacts\`, \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`
|
|
77
82
|
- Note which artifacts are \`done\` vs other states
|
|
78
83
|
|
|
79
|
-
b. **Task completion** -
|
|
80
|
-
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
+
b. **Task completion** - Find the \`changes\` entry from the list response whose \`name\` exactly matches this change
|
|
85
|
+
- Require nonnegative integer \`totalTasks\` and \`completedTasks\`, with \`completedTasks <= totalTasks\`
|
|
86
|
+
- Incomplete tasks = \`totalTasks - completedTasks\`
|
|
87
|
+
- The CLI resolves the schema's tracked task files, including custom artifact names, output paths, and globs
|
|
88
|
+
- Do not infer task completion from artifact status, an artifact id of \`tasks\`, or the absence of a top-level \`tasks.md\`
|
|
89
|
+
- The CLI counts only \`x\`/\`X\` checkbox markers as complete; other markers remain incomplete
|
|
90
|
+
- If \`totalTasks\` is zero, note as "No tasks"
|
|
84
91
|
|
|
85
92
|
c. **Delta specs** - Check \`artifactPaths.specs.existingOutputPaths\` from status JSON
|
|
86
93
|
- List which capability specs exist
|
|
@@ -194,7 +201,9 @@ ${PROJECT_ROOT_GUARD}
|
|
|
194
201
|
Process changes in the determined order (respecting conflict resolution):
|
|
195
202
|
|
|
196
203
|
a. **Sync included delta specs**:
|
|
197
|
-
- Run the
|
|
204
|
+
- ${optionalWorkflow('sync', 'Run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge)', 'Perform the delta-to-main-spec merge inline yourself (agent-driven intelligent merge)')} only for changes with entries in \`includedDeltas\`, passing only the included delta paths and explicitly instructing it to ignore that change's \`excludedDeltas\`. Wait for it to finish.
|
|
205
|
+
- If the sync reports any stop or blocking condition, treat the sync as failed. Stop processing that change immediately. Before continuing to the next change, record this change's outcome as Failed in the batch results, including the sync blocking/error condition.
|
|
206
|
+
- Do not perform the post-sync content comparison and do not move its \`changeRoot\`; leave the change intact.
|
|
198
207
|
- For conflicts, apply in resolved order.
|
|
199
208
|
- Pass that change's fetched specs-rule snapshot into inline sync; inline
|
|
200
209
|
sync must reuse it without fetching instructions again
|
|
@@ -209,7 +218,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
209
218
|
- Verify that main specs are updated:
|
|
210
219
|
- ADDED requirements present
|
|
211
220
|
- MODIFIED requirements carrying scenario and description changes named in the delta, with their other scenarios intact
|
|
212
|
-
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty
|
|
221
|
+
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty.
|
|
213
222
|
- RENAMED requirements present under the new name and absent under the old one
|
|
214
223
|
- Do not verify delta specs in \`excludedDeltas\`; they are intentionally left unsynced.
|
|
215
224
|
- If sync failed or any capability does not match verification, report what differs and fail/skip moving that change's \`changeRoot\` — do not archive that change. \`changeRoot\` remains intact.
|
|
@@ -347,7 +356,7 @@ No active changes found. Create a new change to get started.
|
|
|
347
356
|
- Archive directory target uses the current date, computed once in step 3d and reused at the move: YYYY-MM-DD-<name>; a name that already starts with a \`YYYY-MM-DD-\` prefix is used as-is (never stack a second date)
|
|
348
357
|
- If archive target exists, fail that change but continue with others
|
|
349
358
|
- Check every archive target in step 3, before the first main-spec write; a change whose target exists is never synced or moved
|
|
350
|
-
- If sync is requested, run the
|
|
359
|
+
- If sync is requested, ${optionalWorkflow('sync', 'run the `openspec-sync-specs` workflow inline (agent-driven)', 'perform the delta-to-main-spec merge inline (agent-driven)')} for each change with included delta specs
|
|
351
360
|
- Carry the per-delta \`includedDeltas\` and \`excludedDeltas\` decisions into execution; sync and verify only included deltas
|
|
352
361
|
- Report every excluded delta as \`sync skipped\` without treating the archive itself as skipped
|
|
353
362
|
- Never archive a change while a spec sync is still in flight — run the sync inline and verify main specs at \`<planningHome.root>/openspec/specs/<capability-path>/spec.md\` before moving \`changeRoot\`
|
|
@@ -395,7 +404,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
395
404
|
2. **Prompt for change selection**
|
|
396
405
|
|
|
397
406
|
Ask the user to choose changes (multi-select):
|
|
398
|
-
- Show each change
|
|
407
|
+
- Show each change name and task status from the list output
|
|
399
408
|
- Include an option for "All changes"
|
|
400
409
|
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
|
401
410
|
|
|
@@ -428,17 +437,24 @@ ${PROJECT_ROOT_GUARD}
|
|
|
428
437
|
|
|
429
438
|
3. **Batch validation - gather status for all selected changes**
|
|
430
439
|
|
|
440
|
+
Run \`openspec list --json\` once with the same selected-root flags for task
|
|
441
|
+
progress. If the lookup fails, returns invalid JSON, or omits any selected
|
|
442
|
+
change, contains a duplicate selected change, or returns invalid counts,
|
|
443
|
+
report the problem and stop before syncing or archiving the batch.
|
|
444
|
+
|
|
431
445
|
For each selected change, collect:
|
|
432
446
|
|
|
433
447
|
a. **Artifact status** - Run \`openspec status --change "<name>" --json\`
|
|
434
448
|
- Parse \`schemaName\`, \`artifacts\`, \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`
|
|
435
449
|
- Note which artifacts are \`done\` vs other states
|
|
436
450
|
|
|
437
|
-
b. **Task completion** -
|
|
438
|
-
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
-
|
|
451
|
+
b. **Task completion** - Find the \`changes\` entry from the list response whose \`name\` exactly matches this change
|
|
452
|
+
- Require nonnegative integer \`totalTasks\` and \`completedTasks\`, with \`completedTasks <= totalTasks\`
|
|
453
|
+
- Incomplete tasks = \`totalTasks - completedTasks\`
|
|
454
|
+
- The CLI resolves the schema's tracked task files, including custom artifact names, output paths, and globs
|
|
455
|
+
- Do not infer task completion from artifact status, an artifact id of \`tasks\`, or the absence of a top-level \`tasks.md\`
|
|
456
|
+
- The CLI counts only \`x\`/\`X\` checkbox markers as complete; other markers remain incomplete
|
|
457
|
+
- If \`totalTasks\` is zero, note as "No tasks"
|
|
442
458
|
|
|
443
459
|
c. **Delta specs** - Check \`artifactPaths.specs.existingOutputPaths\` from status JSON
|
|
444
460
|
- List which capability specs exist
|
|
@@ -553,6 +569,8 @@ ${PROJECT_ROOT_GUARD}
|
|
|
553
569
|
|
|
554
570
|
a. **Sync included delta specs**:
|
|
555
571
|
- ${SYNC_INLINE_HANDOFF} only for changes with entries in \`includedDeltas\`, passing only the included delta paths and explicitly instructing it to ignore that change's \`excludedDeltas\`. Wait for it to finish.
|
|
572
|
+
- If the sync reports any stop or blocking condition, treat the sync as failed. Stop processing that change immediately. Before continuing to the next change, record this change's outcome as Failed in the batch results, including the sync blocking/error condition.
|
|
573
|
+
- Do not perform the post-sync content comparison and do not move its \`changeRoot\`; leave the change intact.
|
|
556
574
|
- For conflicts, apply in resolved order.
|
|
557
575
|
- Pass that change's fetched specs-rule snapshot into inline sync; inline
|
|
558
576
|
sync must reuse it without fetching instructions again
|
|
@@ -567,7 +585,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
567
585
|
- Verify that main specs are updated:
|
|
568
586
|
- ADDED requirements present
|
|
569
587
|
- MODIFIED requirements carrying scenario and description changes named in the delta, with their other scenarios intact
|
|
570
|
-
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty
|
|
588
|
+
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving \`## Requirements\` empty), its main spec deleted rather than left empty.
|
|
571
589
|
- RENAMED requirements present under the new name and absent under the old one
|
|
572
590
|
- Do not verify delta specs in \`excludedDeltas\`; they are intentionally left unsynced.
|
|
573
591
|
- If sync failed or any capability does not match verification, report what differs and fail/skip moving that change's \`changeRoot\` — do not archive that change. \`changeRoot\` remains intact.
|
|
@@ -30,7 +30,6 @@ ${PROJECT_ROOT_GUARD}
|
|
|
30
30
|
|
|
31
31
|
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
|
32
32
|
- Change name
|
|
33
|
-
- Schema (from \`schema\` field if present, otherwise "spec-driven")
|
|
34
33
|
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
|
35
34
|
- How recently it was modified (from \`lastModified\` field)
|
|
36
35
|
|
|
@@ -149,7 +148,6 @@ ${PROJECT_ROOT_GUARD}
|
|
|
149
148
|
|
|
150
149
|
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
|
151
150
|
- Change name
|
|
152
|
-
- Schema (from \`schema\` field if present, otherwise "spec-driven")
|
|
153
151
|
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
|
154
152
|
- How recently it was modified (from \`lastModified\` field)
|
|
155
153
|
|
|
@@ -128,7 +128,7 @@ openspec list --json
|
|
|
128
128
|
|
|
129
129
|
This tells you:
|
|
130
130
|
- If there are active changes
|
|
131
|
-
- Their names
|
|
131
|
+
- Their names and task status
|
|
132
132
|
- What the user might be working on
|
|
133
133
|
|
|
134
134
|
That is the *change* list - work in flight. It does not include the project's durable capabilities, so list those too:
|
|
@@ -459,7 +459,7 @@ openspec list --json
|
|
|
459
459
|
|
|
460
460
|
This tells you:
|
|
461
461
|
- If there are active changes
|
|
462
|
-
- Their names
|
|
462
|
+
- Their names and task status
|
|
463
463
|
- What the user might be working on
|
|
464
464
|
|
|
465
465
|
That is the *change* list - work in flight. It does not include the project's durable capabilities, so list those too:
|
|
@@ -88,7 +88,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
88
88
|
- Dependencies are enablers, not gates: if a required artifact is still \`blocked\` only because you skipped a conditional dependency, write it anyway
|
|
89
89
|
- Stop when every artifact in the required set is \`done\`, \`skipped\`, or was deliberately skipped
|
|
90
90
|
|
|
91
|
-
c. **If an artifact requires user input** (unclear context):
|
|
91
|
+
c. **If an artifact requires user input** (critically unclear context):
|
|
92
92
|
- Ask the user to clarify
|
|
93
93
|
- Then continue with creation
|
|
94
94
|
|
|
@@ -206,7 +206,7 @@ ${PROJECT_ROOT_GUARD}
|
|
|
206
206
|
- Dependencies are enablers, not gates: if a required artifact is still \`blocked\` only because you skipped a conditional dependency, write it anyway
|
|
207
207
|
- Stop when every artifact in the required set is \`done\`, \`skipped\`, or was deliberately skipped
|
|
208
208
|
|
|
209
|
-
c. **If an artifact requires user input** (unclear context):
|
|
209
|
+
c. **If an artifact requires user input** (critically unclear context):
|
|
210
210
|
- Ask the user to clarify
|
|
211
211
|
- Then continue with creation
|
|
212
212
|
|