@codewalla_india/openspec 1.0.3 → 1.0.5
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 +7 -2
- package/dist/commands/workflow/instructions.js +46 -5
- package/dist/commands/workflow/shared.d.ts +2 -0
- package/dist/core/completions/command-registry.js +19 -0
- package/dist/core/comprehension/fingerprint.d.ts +10 -1
- package/dist/core/comprehension/fingerprint.js +22 -14
- package/dist/core/comprehension/index.d.ts +10 -2
- package/dist/core/comprehension/index.js +17 -8
- package/dist/core/comprehension/stats.d.ts +18 -1
- package/dist/core/comprehension/stats.js +94 -2
- package/dist/core/templates/workflows/apply-change.js +5 -4
- package/dist/core/templates/workflows/archive-change.js +5 -4
- package/dist/core/templates/workflows/bulk-archive-change.js +5 -4
- package/dist/core/templates/workflows/comprehension-guidance.d.ts +2 -2
- package/dist/core/templates/workflows/comprehension-guidance.js +18 -13
- package/dist/core/templates/workflows/continue-change.js +5 -4
- package/dist/core/templates/workflows/ff-change.js +5 -4
- package/dist/core/templates/workflows/new-change.js +3 -2
- package/dist/core/templates/workflows/onboard.js +2 -2
- package/dist/core/templates/workflows/propose.js +8 -5
- package/dist/core/templates/workflows/sync-specs.js +3 -2
- package/dist/core/templates/workflows/user-prompt-guidance.d.ts +14 -0
- package/dist/core/templates/workflows/user-prompt-guidance.js +39 -0
- package/dist/core/templates/workflows/verify-change.js +3 -2
- package/package.json +1 -1
- package/schemas/spec-driven/schema.yaml +27 -4
- package/schemas/spec-driven/templates/plan.md +19 -0
package/README.md
CHANGED
|
@@ -64,7 +64,9 @@ AI: Created openspec/changes/add-dark-mode/
|
|
|
64
64
|
Ready for implementation!
|
|
65
65
|
|
|
66
66
|
You: /opsx:apply
|
|
67
|
-
AI:
|
|
67
|
+
AI: Comprehension check — 6 questions on your proposal, design, specs, and tasks...
|
|
68
|
+
✓ 83% — ready to implement.
|
|
69
|
+
Implementing tasks...
|
|
68
70
|
✓ 1.1 Add theme context provider
|
|
69
71
|
✓ 1.2 Create toggle component
|
|
70
72
|
✓ 2.1 Add CSS variables
|
|
@@ -106,8 +108,9 @@ Now talk to your AI:
|
|
|
106
108
|
|
|
107
109
|
- **Not sure what to build yet?** Start with `/opsx:explore`, a no-stakes thinking partner that reads your code, weighs options, and shapes a plan before anything is written. ([Explore guide](docs/explore.md))
|
|
108
110
|
- **Already know what you want?** Go straight to `/opsx:propose <what-you-want-to-build>`.
|
|
111
|
+
- **Ready to implement?** Run `/opsx:apply` — a short comprehension quiz checks you understand the proposal, design, specs, and tasks before any code is written.
|
|
109
112
|
|
|
110
|
-
|
|
113
|
+
The default `core` profile includes `/opsx:explore`, `/opsx:propose`, `/opsx:apply`, `/opsx:sync`, and `/opsx:archive`. If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
|
|
111
114
|
|
|
112
115
|
> [!NOTE]
|
|
113
116
|
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 25+ tools and growing.
|
|
@@ -182,6 +185,8 @@ openspec update
|
|
|
182
185
|
|
|
183
186
|
**Context hygiene**: OpenSpec benefits from a clean context window. Clear your context before starting implementation and maintain good context hygiene throughout your session.
|
|
184
187
|
|
|
188
|
+
**Comprehension check**: `/opsx:apply` runs a short quiz (enabled by default) on proposal, design, specs, and pending tasks before implementation. Questions test holistic understanding of the change, not task numbers or checklist trivia. Disable with `comprehension.enabled: false` in `openspec/config.yaml`. See [Workflows](docs/workflows.md#comprehension-quiz-before-apply).
|
|
189
|
+
|
|
185
190
|
## Contributing
|
|
186
191
|
|
|
187
192
|
**Small fixes** — Bug fixes, typo corrections, and minor improvements can be submitted directly as PRs.
|
|
@@ -15,6 +15,22 @@ import { readRegistrySnapshot } from '../../core/store/registry.js';
|
|
|
15
15
|
import { readProjectConfig } from '../../core/project-config.js';
|
|
16
16
|
import { checkComprehensionGate, ComprehensionPassError, recordComprehensionPass, } from '../../core/comprehension/index.js';
|
|
17
17
|
import { validateChangeExists, validateSchemaExists, } from './shared.js';
|
|
18
|
+
function buildArtifactPresence(contextFiles, pendingTaskCount) {
|
|
19
|
+
return {
|
|
20
|
+
hasPlan: (contextFiles.plan?.length ?? 0) > 0,
|
|
21
|
+
hasProposal: (contextFiles.proposal?.length ?? 0) > 0,
|
|
22
|
+
hasDesign: (contextFiles.design?.length ?? 0) > 0,
|
|
23
|
+
hasSpecs: (contextFiles.specs?.length ?? 0) > 0,
|
|
24
|
+
hasTasks: pendingTaskCount > 0 || (contextFiles.tasks?.length ?? 0) > 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function resolvePlanPath(contextFiles, changeDir) {
|
|
28
|
+
if (contextFiles.plan?.[0]) {
|
|
29
|
+
return contextFiles.plan[0];
|
|
30
|
+
}
|
|
31
|
+
const fallback = path.join(changeDir, 'plan.md');
|
|
32
|
+
return fs.existsSync(fallback) ? fallback : null;
|
|
33
|
+
}
|
|
18
34
|
// -----------------------------------------------------------------------------
|
|
19
35
|
// Artifact Instructions Command
|
|
20
36
|
// -----------------------------------------------------------------------------
|
|
@@ -298,13 +314,15 @@ export async function generateApplyInstructions(projectRoot, changeName, schemaN
|
|
|
298
314
|
if (state === 'ready') {
|
|
299
315
|
const specPaths = contextFiles.specs ?? [];
|
|
300
316
|
const tasksPath = tracksFile && tracksFileExists ? path.join(changeDir, tracksFile) : null;
|
|
317
|
+
const planPath = resolvePlanPath(contextFiles, changeDir);
|
|
301
318
|
const pendingTaskCount = tasks.filter((task) => !task.done).length;
|
|
302
|
-
const
|
|
319
|
+
const artifactPresence = buildArtifactPresence(contextFiles, pendingTaskCount);
|
|
320
|
+
const gate = checkComprehensionGate(changeDir, specPaths, options.projectConfig ?? readProjectConfig(projectRoot), { tasksPath, planPath, pendingTaskCount, artifactPresence });
|
|
303
321
|
if (gate.active && !gate.passed && gate.info) {
|
|
304
322
|
state = 'blocked';
|
|
305
323
|
missingComprehension = true;
|
|
306
324
|
comprehension = gate.info;
|
|
307
|
-
instruction = `Complete the comprehension quiz in /opsx:apply before implementation (score ≥ ${gate.info.thresholdPercent}% on proposal, design, specs, and tasks).`;
|
|
325
|
+
instruction = `Complete the comprehension quiz in /opsx:apply before implementation (score ≥ ${gate.info.thresholdPercent}% on proposal, design, specs, plan, and tasks; plan receives the majority of questions per questionAllocation).`;
|
|
308
326
|
}
|
|
309
327
|
else if (gate.active && gate.info) {
|
|
310
328
|
comprehension = gate.info;
|
|
@@ -359,15 +377,32 @@ export async function applyInstructionsCommand(options) {
|
|
|
359
377
|
}
|
|
360
378
|
const tracksFile = schema.apply?.tracks ?? null;
|
|
361
379
|
const tasksPath = tracksFile ? path.join(changeDir, tracksFile) : null;
|
|
380
|
+
const planPath = fs.existsSync(path.join(changeDir, 'plan.md'))
|
|
381
|
+
? path.join(changeDir, 'plan.md')
|
|
382
|
+
: null;
|
|
383
|
+
const pendingTaskCount = tasksPath && fs.existsSync(tasksPath)
|
|
384
|
+
? parseTasksFile(fs.readFileSync(tasksPath, 'utf-8')).filter((t) => !t.done).length
|
|
385
|
+
: 0;
|
|
386
|
+
const contextFilesForPresence = {};
|
|
387
|
+
for (const artifact of schema.artifacts) {
|
|
388
|
+
const outputs = resolveArtifactOutputs(changeDir, artifact.generates);
|
|
389
|
+
if (outputs.length > 0) {
|
|
390
|
+
contextFilesForPresence[artifact.id] = outputs;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const artifactPresence = buildArtifactPresence(contextFilesForPresence, pendingTaskCount);
|
|
362
394
|
try {
|
|
363
395
|
const record = recordComprehensionPass({
|
|
364
396
|
changeDir,
|
|
365
397
|
specPaths,
|
|
366
398
|
tasksPath,
|
|
399
|
+
planPath,
|
|
367
400
|
projectConfig,
|
|
368
401
|
scorePercent: options.score,
|
|
369
402
|
attempt: options.attempt ?? 1,
|
|
370
403
|
questionCount: options.questionCount ?? 0,
|
|
404
|
+
pendingTaskCount,
|
|
405
|
+
artifactPresence,
|
|
371
406
|
});
|
|
372
407
|
spinner?.stop();
|
|
373
408
|
if (options.json) {
|
|
@@ -453,11 +488,17 @@ export function printApplyInstructionsText(instructions) {
|
|
|
453
488
|
if (state === 'blocked' && missingComprehension && comprehension) {
|
|
454
489
|
console.log('### ⚠️ Comprehension Required');
|
|
455
490
|
console.log();
|
|
456
|
-
console.log(`Pass the
|
|
457
|
-
console.log(`Questions: ${comprehension.questionCount}`);
|
|
491
|
+
console.log(`Pass the comprehension quiz (score ≥ ${comprehension.thresholdPercent}%) before implementation.`);
|
|
492
|
+
console.log(`Questions: ${comprehension.questionCount} (${comprehension.optionsPerQuestion} options each)`);
|
|
493
|
+
const allocationParts = Object.entries(comprehension.questionAllocation)
|
|
494
|
+
.filter(([, count]) => (count ?? 0) > 0)
|
|
495
|
+
.map(([category, count]) => `${category}×${count}`);
|
|
496
|
+
if (allocationParts.length > 0) {
|
|
497
|
+
console.log(`Allocation: ${allocationParts.join(', ')}`);
|
|
498
|
+
}
|
|
458
499
|
console.log(`Specs: ${comprehension.requirementCount} requirements, ${comprehension.scenarioCount} scenarios; Tasks: ${comprehension.pendingTaskCount} pending`);
|
|
459
500
|
if (comprehension.bestScorePercent !== undefined) {
|
|
460
|
-
console.log(`Previous score: ${comprehension.bestScorePercent}% (
|
|
501
|
+
console.log(`Previous score: ${comprehension.bestScorePercent}% (artifacts changed — retake required)`);
|
|
461
502
|
}
|
|
462
503
|
console.log('Complete the quiz via /opsx:apply.');
|
|
463
504
|
console.log();
|
|
@@ -23,6 +23,8 @@ export interface ApplyComprehensionInfo {
|
|
|
23
23
|
thresholdPercent: number;
|
|
24
24
|
bestScorePercent?: number;
|
|
25
25
|
questionCount: number;
|
|
26
|
+
questionAllocation: Record<string, number>;
|
|
27
|
+
optionsPerQuestion: number;
|
|
26
28
|
requirementCount: number;
|
|
27
29
|
scenarioCount: number;
|
|
28
30
|
pendingTaskCount: number;
|
|
@@ -191,6 +191,25 @@ export const COMMAND_REGISTRY = [
|
|
|
191
191
|
description: 'Schema override',
|
|
192
192
|
takesValue: true,
|
|
193
193
|
},
|
|
194
|
+
{
|
|
195
|
+
name: 'record-comprehension-pass',
|
|
196
|
+
description: 'Record a successful comprehension quiz pass (use with instructions apply)',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'score',
|
|
200
|
+
description: 'Quiz score 0-100 (required with --record-comprehension-pass)',
|
|
201
|
+
takesValue: true,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'attempt',
|
|
205
|
+
description: 'Quiz attempt number',
|
|
206
|
+
takesValue: true,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'question-count',
|
|
210
|
+
description: 'Number of quiz questions taken',
|
|
211
|
+
takesValue: true,
|
|
212
|
+
},
|
|
194
213
|
COMMON_FLAGS.json,
|
|
195
214
|
COMMON_FLAGS.store,
|
|
196
215
|
],
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
export interface ApplyArtifactFingerprintInput {
|
|
2
|
+
specPaths: string[];
|
|
3
|
+
tasksPath?: string | null;
|
|
4
|
+
planPath?: string | null;
|
|
5
|
+
}
|
|
1
6
|
/**
|
|
2
|
-
* SHA-256 fingerprint of delta
|
|
7
|
+
* SHA-256 fingerprint of delta specs and optional plan/tasks file contents.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fingerprintApplyArtifacts(input: ApplyArtifactFingerprintInput): string;
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Use fingerprintApplyArtifacts
|
|
3
12
|
*/
|
|
4
13
|
export declare function fingerprintSpecFiles(specPaths: string[], tasksPath?: string | null): string;
|
|
5
14
|
//# sourceMappingURL=fingerprint.d.ts.map
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
function hashFileContent(hash, label, filePath) {
|
|
4
|
+
hash.update(`${label}:`);
|
|
5
|
+
hash.update(filePath);
|
|
6
|
+
hash.update('\0');
|
|
7
|
+
hash.update(readFileSync(filePath, 'utf-8'));
|
|
8
|
+
hash.update('\0');
|
|
9
|
+
}
|
|
3
10
|
/**
|
|
4
|
-
* SHA-256 fingerprint of delta
|
|
11
|
+
* SHA-256 fingerprint of delta specs and optional plan/tasks file contents.
|
|
5
12
|
*/
|
|
6
|
-
export function
|
|
7
|
-
const sorted = [...specPaths].sort();
|
|
13
|
+
export function fingerprintApplyArtifacts(input) {
|
|
14
|
+
const sorted = [...input.specPaths].sort();
|
|
8
15
|
const hash = createHash('sha256');
|
|
9
16
|
for (const specPath of sorted) {
|
|
10
|
-
hash
|
|
11
|
-
hash.update(specPath);
|
|
12
|
-
hash.update('\0');
|
|
13
|
-
hash.update(readFileSync(specPath, 'utf-8'));
|
|
14
|
-
hash.update('\0');
|
|
17
|
+
hashFileContent(hash, 'spec', specPath);
|
|
15
18
|
}
|
|
16
|
-
if (
|
|
17
|
-
hash
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
hash
|
|
21
|
-
hash.update('\0');
|
|
19
|
+
if (input.planPath && existsSync(input.planPath)) {
|
|
20
|
+
hashFileContent(hash, 'plan', input.planPath);
|
|
21
|
+
}
|
|
22
|
+
if (input.tasksPath && existsSync(input.tasksPath)) {
|
|
23
|
+
hashFileContent(hash, 'tasks', input.tasksPath);
|
|
22
24
|
}
|
|
23
25
|
return hash.digest('hex');
|
|
24
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Use fingerprintApplyArtifacts
|
|
29
|
+
*/
|
|
30
|
+
export function fingerprintSpecFiles(specPaths, tasksPath) {
|
|
31
|
+
return fingerprintApplyArtifacts({ specPaths, tasksPath });
|
|
32
|
+
}
|
|
25
33
|
//# sourceMappingURL=fingerprint.js.map
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { ProjectConfig } from '../project-config.js';
|
|
2
2
|
import { type ComprehensionPassRecord } from './pass-record.js';
|
|
3
|
+
import { OPTIONS_PER_QUESTION, type ArtifactPresence, type QuestionAllocation } from './stats.js';
|
|
3
4
|
export { DEFAULT_COMPREHENSION_CONFIG, resolveComprehensionConfig, type ComprehensionConfig, } from './config.js';
|
|
4
|
-
export { fingerprintSpecFiles } from './fingerprint.js';
|
|
5
|
+
export { fingerprintApplyArtifacts, fingerprintSpecFiles } from './fingerprint.js';
|
|
5
6
|
export { COMPREHENSION_PASS_FILENAME, COMPREHENSION_SESSION_FILENAME, buildPassRecord, deleteSessionRecord, isPassValid, readPassRecord, writePassRecord, type ComprehensionPassRecord, } from './pass-record.js';
|
|
6
|
-
export { computeQuestionCount, computeSpecStats, countSpecStats, type SpecStats } from './stats.js';
|
|
7
|
+
export { computeQuestionAllocation, computeQuestionCount, computeSpecStats, countSpecStats, OPTIONS_PER_QUESTION, type ArtifactPresence, type QuestionAllocation, type QuestionCategory, type SpecStats, } from './stats.js';
|
|
7
8
|
export interface ComprehensionGateInfo {
|
|
8
9
|
required: boolean;
|
|
9
10
|
passed: boolean;
|
|
10
11
|
thresholdPercent: number;
|
|
11
12
|
bestScorePercent?: number;
|
|
12
13
|
questionCount: number;
|
|
14
|
+
questionAllocation: QuestionAllocation;
|
|
15
|
+
optionsPerQuestion: number;
|
|
13
16
|
requirementCount: number;
|
|
14
17
|
scenarioCount: number;
|
|
15
18
|
pendingTaskCount: number;
|
|
@@ -17,7 +20,9 @@ export interface ComprehensionGateInfo {
|
|
|
17
20
|
}
|
|
18
21
|
export interface ComprehensionGateOptions {
|
|
19
22
|
tasksPath?: string | null;
|
|
23
|
+
planPath?: string | null;
|
|
20
24
|
pendingTaskCount?: number;
|
|
25
|
+
artifactPresence?: ArtifactPresence;
|
|
21
26
|
}
|
|
22
27
|
export interface ComprehensionGateResult {
|
|
23
28
|
active: boolean;
|
|
@@ -40,10 +45,13 @@ export declare function recordComprehensionPass(input: {
|
|
|
40
45
|
changeDir: string;
|
|
41
46
|
specPaths: string[];
|
|
42
47
|
tasksPath?: string | null;
|
|
48
|
+
planPath?: string | null;
|
|
43
49
|
projectConfig: ProjectConfig | null | undefined;
|
|
44
50
|
scorePercent: number;
|
|
45
51
|
attempt: number;
|
|
46
52
|
questionCount: number;
|
|
47
53
|
pendingTaskCount?: number;
|
|
54
|
+
artifactPresence?: ArtifactPresence;
|
|
48
55
|
}): ComprehensionPassRecord;
|
|
56
|
+
export { OPTIONS_PER_QUESTION as comprehensionOptionsPerQuestion };
|
|
49
57
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { resolveComprehensionConfig } from './config.js';
|
|
2
|
-
import {
|
|
2
|
+
import { fingerprintApplyArtifacts } from './fingerprint.js';
|
|
3
3
|
import { buildPassRecord, deleteSessionRecord, isPassValid, readPassRecord, writePassRecord, } from './pass-record.js';
|
|
4
|
-
import { computeSpecStats } from './stats.js';
|
|
4
|
+
import { computeSpecStats, OPTIONS_PER_QUESTION, } from './stats.js';
|
|
5
5
|
export { DEFAULT_COMPREHENSION_CONFIG, resolveComprehensionConfig, } from './config.js';
|
|
6
|
-
export { fingerprintSpecFiles } from './fingerprint.js';
|
|
6
|
+
export { fingerprintApplyArtifacts, fingerprintSpecFiles } from './fingerprint.js';
|
|
7
7
|
export { COMPREHENSION_PASS_FILENAME, COMPREHENSION_SESSION_FILENAME, buildPassRecord, deleteSessionRecord, isPassValid, readPassRecord, writePassRecord, } from './pass-record.js';
|
|
8
|
-
export { computeQuestionCount, computeSpecStats, countSpecStats } from './stats.js';
|
|
8
|
+
export { computeQuestionAllocation, computeQuestionCount, computeSpecStats, countSpecStats, OPTIONS_PER_QUESTION, } from './stats.js';
|
|
9
9
|
/**
|
|
10
10
|
* Evaluate whether apply is blocked by the comprehension gate.
|
|
11
11
|
*/
|
|
@@ -13,17 +13,19 @@ export function checkComprehensionGate(changeDir, specPaths, projectConfig, gate
|
|
|
13
13
|
const config = resolveComprehensionConfig(projectConfig);
|
|
14
14
|
const pendingTaskCount = gateOptions.pendingTaskCount ?? 0;
|
|
15
15
|
const tasksPath = gateOptions.tasksPath ?? null;
|
|
16
|
+
const planPath = gateOptions.planPath ?? null;
|
|
17
|
+
const artifactPresence = gateOptions.artifactPresence ?? {};
|
|
16
18
|
if (!config.enabled) {
|
|
17
19
|
return { active: false, passed: true };
|
|
18
20
|
}
|
|
19
21
|
if (specPaths.length === 0) {
|
|
20
22
|
return { active: false, passed: true };
|
|
21
23
|
}
|
|
22
|
-
const stats = computeSpecStats(specPaths, config, pendingTaskCount);
|
|
24
|
+
const stats = computeSpecStats(specPaths, config, pendingTaskCount, artifactPresence);
|
|
23
25
|
if (stats.requirementCount === 0) {
|
|
24
26
|
return { active: false, passed: true };
|
|
25
27
|
}
|
|
26
|
-
const fingerprint =
|
|
28
|
+
const fingerprint = fingerprintApplyArtifacts({ specPaths, tasksPath, planPath });
|
|
27
29
|
const record = readPassRecord(changeDir);
|
|
28
30
|
const passed = isPassValid(record, fingerprint);
|
|
29
31
|
const info = {
|
|
@@ -31,6 +33,8 @@ export function checkComprehensionGate(changeDir, specPaths, projectConfig, gate
|
|
|
31
33
|
passed,
|
|
32
34
|
thresholdPercent: config.thresholdPercent,
|
|
33
35
|
questionCount: stats.questionCount,
|
|
36
|
+
questionAllocation: stats.questionAllocation,
|
|
37
|
+
optionsPerQuestion: stats.optionsPerQuestion,
|
|
34
38
|
requirementCount: stats.requirementCount,
|
|
35
39
|
scenarioCount: stats.scenarioCount,
|
|
36
40
|
pendingTaskCount: stats.pendingTaskCount,
|
|
@@ -62,8 +66,12 @@ export function recordComprehensionPass(input) {
|
|
|
62
66
|
}
|
|
63
67
|
const stats = input.questionCount > 0
|
|
64
68
|
? { questionCount: input.questionCount }
|
|
65
|
-
: computeSpecStats(input.specPaths, config, input.pendingTaskCount ?? 0);
|
|
66
|
-
const fingerprint =
|
|
69
|
+
: computeSpecStats(input.specPaths, config, input.pendingTaskCount ?? 0, input.artifactPresence ?? {});
|
|
70
|
+
const fingerprint = fingerprintApplyArtifacts({
|
|
71
|
+
specPaths: input.specPaths,
|
|
72
|
+
tasksPath: input.tasksPath,
|
|
73
|
+
planPath: input.planPath,
|
|
74
|
+
});
|
|
67
75
|
const record = buildPassRecord({
|
|
68
76
|
scorePercent: input.scorePercent,
|
|
69
77
|
thresholdPercent: config.thresholdPercent,
|
|
@@ -75,4 +83,5 @@ export function recordComprehensionPass(input) {
|
|
|
75
83
|
deleteSessionRecord(input.changeDir);
|
|
76
84
|
return record;
|
|
77
85
|
}
|
|
86
|
+
export { OPTIONS_PER_QUESTION as comprehensionOptionsPerQuestion };
|
|
78
87
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import type { ComprehensionConfig } from './config.js';
|
|
2
|
+
export declare const OPTIONS_PER_QUESTION = 3;
|
|
3
|
+
export type QuestionCategory = 'plan' | 'specs' | 'design' | 'proposal' | 'tasks';
|
|
4
|
+
export type QuestionAllocation = Partial<Record<QuestionCategory, number>>;
|
|
5
|
+
export interface ArtifactPresence {
|
|
6
|
+
hasPlan?: boolean;
|
|
7
|
+
hasProposal?: boolean;
|
|
8
|
+
hasDesign?: boolean;
|
|
9
|
+
hasSpecs?: boolean;
|
|
10
|
+
hasTasks?: boolean;
|
|
11
|
+
}
|
|
2
12
|
export interface SpecStats {
|
|
3
13
|
requirementCount: number;
|
|
4
14
|
scenarioCount: number;
|
|
5
15
|
pendingTaskCount: number;
|
|
6
16
|
questionCount: number;
|
|
17
|
+
questionAllocation: QuestionAllocation;
|
|
18
|
+
optionsPerQuestion: number;
|
|
7
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Distribute quiz questions across artifact categories.
|
|
22
|
+
* When plan exists, plan receives ceil(total/2) — strictly more than any other category.
|
|
23
|
+
*/
|
|
24
|
+
export declare function computeQuestionAllocation(total: number, presence: ArtifactPresence): QuestionAllocation;
|
|
8
25
|
/**
|
|
9
26
|
* Count requirements and scenarios across delta spec files.
|
|
10
27
|
*/
|
|
@@ -14,5 +31,5 @@ export declare function countSpecStats(specPaths: string[]): Pick<SpecStats, 're
|
|
|
14
31
|
* clamp(min, max, round(req * 0.6 + scenarios * 0.15 + pendingTasks * 0.15)).
|
|
15
32
|
*/
|
|
16
33
|
export declare function computeQuestionCount(requirementCount: number, scenarioCount: number, pendingTaskCount: number, config: Pick<ComprehensionConfig, 'minQuestions' | 'maxQuestions'>): number;
|
|
17
|
-
export declare function computeSpecStats(specPaths: string[], config: Pick<ComprehensionConfig, 'minQuestions' | 'maxQuestions'>, pendingTaskCount?: number): SpecStats;
|
|
34
|
+
export declare function computeSpecStats(specPaths: string[], config: Pick<ComprehensionConfig, 'minQuestions' | 'maxQuestions'>, pendingTaskCount?: number, presence?: ArtifactPresence): SpecStats;
|
|
18
35
|
//# sourceMappingURL=stats.d.ts.map
|
|
@@ -1,6 +1,89 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { parseDeltaSpec } from '../parsers/requirement-blocks.js';
|
|
3
3
|
const SCENARIO_HEADER_REGEX = /^####\s*Scenario:/gim;
|
|
4
|
+
export const OPTIONS_PER_QUESTION = 3;
|
|
5
|
+
const NON_PLAN_PRIORITY = ['specs', 'design', 'proposal', 'tasks'];
|
|
6
|
+
function isCategoryPresent(category, presence) {
|
|
7
|
+
switch (category) {
|
|
8
|
+
case 'plan':
|
|
9
|
+
return presence.hasPlan === true;
|
|
10
|
+
case 'specs':
|
|
11
|
+
return presence.hasSpecs === true;
|
|
12
|
+
case 'design':
|
|
13
|
+
return presence.hasDesign === true;
|
|
14
|
+
case 'proposal':
|
|
15
|
+
return presence.hasProposal === true;
|
|
16
|
+
case 'tasks':
|
|
17
|
+
return presence.hasTasks === true;
|
|
18
|
+
default:
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function sumAllocation(allocation) {
|
|
23
|
+
return Object.values(allocation).reduce((sum, n) => sum + (n ?? 0), 0);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Distribute quiz questions across artifact categories.
|
|
27
|
+
* When plan exists, plan receives ceil(total/2) — strictly more than any other category.
|
|
28
|
+
*/
|
|
29
|
+
export function computeQuestionAllocation(total, presence) {
|
|
30
|
+
if (total <= 0) {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
if (presence.hasPlan) {
|
|
34
|
+
const allocation = {};
|
|
35
|
+
const planQuota = Math.ceil(total / 2);
|
|
36
|
+
allocation.plan = planQuota;
|
|
37
|
+
let remainder = total - planQuota;
|
|
38
|
+
for (const category of NON_PLAN_PRIORITY) {
|
|
39
|
+
if (remainder <= 0) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
if (!isCategoryPresent(category, presence)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
allocation[category] = (allocation[category] ?? 0) + 1;
|
|
46
|
+
remainder--;
|
|
47
|
+
}
|
|
48
|
+
const presentOthers = NON_PLAN_PRIORITY.filter((category) => isCategoryPresent(category, presence));
|
|
49
|
+
let index = 0;
|
|
50
|
+
while (remainder > 0 && presentOthers.length > 0) {
|
|
51
|
+
const category = presentOthers[index % presentOthers.length];
|
|
52
|
+
allocation[category] = (allocation[category] ?? 0) + 1;
|
|
53
|
+
remainder--;
|
|
54
|
+
index++;
|
|
55
|
+
}
|
|
56
|
+
return allocation;
|
|
57
|
+
}
|
|
58
|
+
return computeQuestionAllocationEven(total, presence);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Even split when plan is absent: at least one per present category, extras specs-first.
|
|
62
|
+
*/
|
|
63
|
+
function computeQuestionAllocationEven(total, presence) {
|
|
64
|
+
const present = NON_PLAN_PRIORITY.filter((category) => isCategoryPresent(category, presence));
|
|
65
|
+
if (present.length === 0) {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
const allocation = {};
|
|
69
|
+
let assigned = 0;
|
|
70
|
+
for (const category of present) {
|
|
71
|
+
if (assigned >= total) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
allocation[category] = 1;
|
|
75
|
+
assigned++;
|
|
76
|
+
}
|
|
77
|
+
let remainder = total - assigned;
|
|
78
|
+
let index = 0;
|
|
79
|
+
while (remainder > 0) {
|
|
80
|
+
const category = present[index % present.length];
|
|
81
|
+
allocation[category] = (allocation[category] ?? 0) + 1;
|
|
82
|
+
remainder--;
|
|
83
|
+
index++;
|
|
84
|
+
}
|
|
85
|
+
return allocation;
|
|
86
|
+
}
|
|
4
87
|
function countScenariosInBlock(raw) {
|
|
5
88
|
const matches = raw.match(SCENARIO_HEADER_REGEX);
|
|
6
89
|
return matches?.length ?? 0;
|
|
@@ -33,9 +116,18 @@ export function computeQuestionCount(requirementCount, scenarioCount, pendingTas
|
|
|
33
116
|
const raw = Math.round(requirementCount * 0.6 + scenarioCount * 0.15 + pendingTaskCount * 0.15);
|
|
34
117
|
return clamp(config.minQuestions, config.maxQuestions, raw);
|
|
35
118
|
}
|
|
36
|
-
export function computeSpecStats(specPaths, config, pendingTaskCount = 0) {
|
|
119
|
+
export function computeSpecStats(specPaths, config, pendingTaskCount = 0, presence = {}) {
|
|
37
120
|
const { requirementCount, scenarioCount } = countSpecStats(specPaths);
|
|
38
121
|
const questionCount = computeQuestionCount(requirementCount, scenarioCount, pendingTaskCount, config);
|
|
39
|
-
|
|
122
|
+
const questionAllocation = computeQuestionAllocation(questionCount, presence);
|
|
123
|
+
const allocationTotal = sumAllocation(questionAllocation);
|
|
124
|
+
return {
|
|
125
|
+
requirementCount,
|
|
126
|
+
scenarioCount,
|
|
127
|
+
pendingTaskCount,
|
|
128
|
+
questionCount: allocationTotal > 0 ? allocationTotal : questionCount,
|
|
129
|
+
questionAllocation,
|
|
130
|
+
optionsPerQuestion: OPTIONS_PER_QUESTION,
|
|
131
|
+
};
|
|
40
132
|
}
|
|
41
133
|
//# sourceMappingURL=stats.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ATLASSIAN_ENRICHMENT_GUIDANCE, CONTEXT7_LOOKUP_GUIDANCE, PLAYWRIGHT_APPLY_GUARDRAIL, } from './mcp-guidance.js';
|
|
2
2
|
import { COMPREHENSION_APPLY_GUARDRAIL, COMPREHENSION_QUIZ_GUIDANCE, } from './comprehension-guidance.js';
|
|
3
3
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
4
|
+
import { PROMPT_SELECT_CHANGE } from './user-prompt-guidance.js';
|
|
4
5
|
export function getApplyChangeSkillTemplate() {
|
|
5
6
|
return {
|
|
6
7
|
name: 'openspec-apply-change',
|
|
@@ -18,7 +19,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
18
19
|
If a name is provided, use it. Otherwise:
|
|
19
20
|
- Infer from conversation context if the user mentioned a change
|
|
20
21
|
- Auto-select if only one active change exists
|
|
21
|
-
- If ambiguous,
|
|
22
|
+
- If ambiguous, ${PROMPT_SELECT_CHANGE}
|
|
22
23
|
|
|
23
24
|
Always announce: "Using change: <name>" and how to override (e.g., \`/opsx:apply <other>\`).
|
|
24
25
|
|
|
@@ -57,7 +58,7 @@ ${COMPREHENSION_QUIZ_GUIDANCE}
|
|
|
57
58
|
|
|
58
59
|
After comprehension is passed (or not required), read every file path listed under \`contextFiles\` from the apply instructions output.
|
|
59
60
|
The files depend on the schema being used:
|
|
60
|
-
- **spec-driven**: proposal, specs, design, tasks
|
|
61
|
+
- **spec-driven**: proposal, specs, design, plan, tasks
|
|
61
62
|
- Other schemas: follow the contextFiles from CLI output
|
|
62
63
|
|
|
63
64
|
6. **Show current progress**
|
|
@@ -187,7 +188,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
187
188
|
If a name is provided, use it. Otherwise:
|
|
188
189
|
- Infer from conversation context if the user mentioned a change
|
|
189
190
|
- Auto-select if only one active change exists
|
|
190
|
-
- If ambiguous,
|
|
191
|
+
- If ambiguous, ${PROMPT_SELECT_CHANGE}
|
|
191
192
|
|
|
192
193
|
Always announce: "Using change: <name>" and how to override (e.g., \`/opsx:apply <other>\`).
|
|
193
194
|
|
|
@@ -226,7 +227,7 @@ ${COMPREHENSION_QUIZ_GUIDANCE}
|
|
|
226
227
|
|
|
227
228
|
After comprehension is passed (or not required), read every file path listed under \`contextFiles\` from the apply instructions output.
|
|
228
229
|
The files depend on the schema being used:
|
|
229
|
-
- **spec-driven**: proposal, specs, design, tasks
|
|
230
|
+
- **spec-driven**: proposal, specs, design, plan, tasks
|
|
230
231
|
- Other schemas: follow the contextFiles from CLI output
|
|
231
232
|
|
|
232
233
|
6. **Show current progress**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
2
|
+
import { PROMPT_CONFIRM, PROMPT_SELECT_CHANGE } from './user-prompt-guidance.js';
|
|
2
3
|
export function getArchiveChangeSkillTemplate() {
|
|
3
4
|
return {
|
|
4
5
|
name: 'openspec-archive-change',
|
|
@@ -13,7 +14,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
13
14
|
|
|
14
15
|
1. **If no change name provided, prompt for selection**
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
${PROMPT_SELECT_CHANGE}
|
|
17
18
|
|
|
18
19
|
Show only active changes (not already archived).
|
|
19
20
|
Include the schema used for each change if available.
|
|
@@ -31,7 +32,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
31
32
|
|
|
32
33
|
**If any artifacts are not \`done\`:**
|
|
33
34
|
- Display warning listing incomplete artifacts
|
|
34
|
-
-
|
|
35
|
+
- ${PROMPT_CONFIRM}
|
|
35
36
|
- Proceed if user confirms
|
|
36
37
|
|
|
37
38
|
3. **Check task completion status**
|
|
@@ -42,7 +43,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
42
43
|
|
|
43
44
|
**If incomplete tasks found:**
|
|
44
45
|
- Display warning showing count of incomplete tasks
|
|
45
|
-
-
|
|
46
|
+
- ${PROMPT_CONFIRM}
|
|
46
47
|
- Proceed if user confirms
|
|
47
48
|
|
|
48
49
|
**If no tasks file exists:** Proceed without task-related warning.
|
|
@@ -130,7 +131,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
130
131
|
|
|
131
132
|
1. **If no change name provided, prompt for selection**
|
|
132
133
|
|
|
133
|
-
|
|
134
|
+
${PROMPT_SELECT_CHANGE}
|
|
134
135
|
|
|
135
136
|
Show only active changes (not already archived).
|
|
136
137
|
Include the schema used for each change if available.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
2
|
+
import { PROMPT_CONFIRM, PROMPT_MULTI_SELECT_CHANGES } from './user-prompt-guidance.js';
|
|
2
3
|
export function getBulkArchiveChangeSkillTemplate() {
|
|
3
4
|
return {
|
|
4
5
|
name: 'openspec-bulk-archive-change',
|
|
@@ -21,7 +22,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
21
22
|
|
|
22
23
|
2. **Prompt for change selection**
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
${PROMPT_MULTI_SELECT_CHANGES}
|
|
25
26
|
- Show each change with its schema
|
|
26
27
|
- Include an option for "All changes"
|
|
27
28
|
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
|
@@ -102,7 +103,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
102
103
|
|
|
103
104
|
7. **Confirm batch operation**
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
${PROMPT_CONFIRM}
|
|
106
107
|
|
|
107
108
|
- "Archive N changes?" with options based on status
|
|
108
109
|
- Options might include:
|
|
@@ -269,7 +270,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
269
270
|
|
|
270
271
|
2. **Prompt for change selection**
|
|
271
272
|
|
|
272
|
-
|
|
273
|
+
${PROMPT_MULTI_SELECT_CHANGES}
|
|
273
274
|
- Show each change with its schema
|
|
274
275
|
- Include an option for "All changes"
|
|
275
276
|
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
|
@@ -350,7 +351,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
350
351
|
|
|
351
352
|
7. **Confirm batch operation**
|
|
352
353
|
|
|
353
|
-
|
|
354
|
+
${PROMPT_CONFIRM}
|
|
354
355
|
|
|
355
356
|
- "Archive N changes?" with options based on status
|
|
356
357
|
- Options might include:
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
* Interpolated into apply skill and slash command templates so agents run
|
|
5
5
|
* a spec comprehension gate before implementation.
|
|
6
6
|
*/
|
|
7
|
-
export declare const COMPREHENSION_QUIZ_GUIDANCE = "4. **Comprehension quiz (required before implementation)**\n\n After `openspec instructions apply --change \"<name>\" --json`, check comprehension status:\n\n - If `missingComprehension` is true OR `comprehension.required && !comprehension.passed`:\n - Do NOT edit application source code or mark task checkboxes yet\n - Read `contextFiles.proposal`, `contextFiles.design`, `contextFiles.specs`, and `contextFiles.tasks` (or the `tasks` array in apply JSON)\n - Use `comprehension.questionCount` from the JSON
|
|
8
|
-
export declare const COMPREHENSION_APPLY_GUARDRAIL = "- NEVER implement code or mark tasks while `missingComprehension` is true\n- NEVER skip the comprehension quiz when the apply JSON requires it\n- If the user asks to skip the quiz, refuse and explain they must pass or set comprehension.enabled: false in openspec/config.yaml";
|
|
7
|
+
export declare const COMPREHENSION_QUIZ_GUIDANCE = "4. **Comprehension quiz (required before implementation)**\n\n After `openspec instructions apply --change \"<name>\" --json`, check comprehension status:\n\n - If `missingComprehension` is true OR `comprehension.required && !comprehension.passed`:\n - Do NOT edit application source code or mark task checkboxes yet\n - Read `contextFiles.proposal`, `contextFiles.design`, `contextFiles.specs`, `contextFiles.plan`, and `contextFiles.tasks` (or the `tasks` array in apply JSON)\n - Use `comprehension.questionCount` and `comprehension.questionAllocation` from the JSON\n\n **Generate questions**\n - Create exactly `comprehension.questionCount` multiple-choice questions\n - **Follow `comprehension.questionAllocation`** \u2014 generate the exact count per category (e.g. plan\u00D74, specs\u00D71); do not invent your own split\n - Each question maps to one artifact category:\n - **Proposal**: motivation, scope, or impact from `proposal.md`\n - **Design**: decisions, trade-offs, or approach from `design.md`\n - **Specs**: a `### Requirement:` or `#### Scenario:` from delta specs\n - **Plan**: code map, file targets, test plan, sequencing, or alignment with design from `plan.md`\n - **Tasks**: conceptual understanding of the implementation approach from pending (unchecked) tasks\n - Do NOT use completed tasks as question sources\n - Each question: **3 options** (`comprehension.optionsPerQuestion`, default 3) \u2014 1 correct from source substance, 2 plausible distractors from other proposal/design/spec/plan/task substance in the change\n\n **Plan question quality**\n - Test code map, file targets, test plan, sequencing, or alignment with design\n - **Forbidden**: section numbers, verbatim headings, trivia answerable without reading plan substance\n\n **Task question quality**\n - Test scope, approach, dependencies, sequencing rationale, or alignment with proposal/design/plan\n - **Forbidden**: task numbers, checklist order, \"which task says X verbatim\", or answers identifiable only by task index or checkbox position\n - Good: \"What is the primary file where quiz rules are centralized?\" (answer from task substance)\n - Bad: \"Which task number updates `comprehension-guidance.ts`?\" or \"What is the exact text of task 2.1?\"\n\n **Present and grade**\n - Present each question in chat with labeled options (A/B/C/D or 1\u20134)\n - Ask ONE question at a time; after each, STOP and wait for the user's answer before the next question\n - NEVER select answers yourself, infer what the user would pick, or call `--record-comprehension-pass` until the user has answered every question\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.\n - Grade: `score_percent = round(correct / question_count * 100)`\n - Pass when `score_percent >= comprehension.thresholdPercent` (default 80)\n\n **On failure (score below threshold)**\n - Announce score and that a new quiz is required\n - Update `.comprehension-session.yaml` in the change dir with `used_sources` from this attempt\n - Generate a NEW question set using different proposal/design/spec/plan/task sources (avoid `used_sources`)\n - Retry until pass\n\n **On pass**\n ```bash\n openspec instructions apply --change \"<name>\" --record-comprehension-pass --score <score> --attempt <n> --question-count <count> --json\n ```\n - Re-run `openspec instructions apply --change \"<name>\" --json`\n - Confirm `state` is `\"ready\"` and `comprehension.passed` is true before continuing\n\n **Output template**\n ```\n ## Applying: <change-name> \u2014 comprehension check\n\n plan\u00D7N, specs\u00D7N, design\u00D7N, proposal\u00D7N, tasks\u00D7N \u2192 <questionCount> questions (3 options each)\n\n Question 1/N: ...\n ...\n \u2713 Comprehension passed (<score>%, attempt <n>)\n ```\n\n Then continue to step 5 (show progress) and implementation.";
|
|
8
|
+
export declare const COMPREHENSION_APPLY_GUARDRAIL = "- NEVER implement code or mark tasks while `missingComprehension` is true\n- NEVER skip the comprehension quiz when the apply JSON requires it\n- NEVER answer comprehension quiz questions yourself \u2014 the human developer must answer every question\n- NEVER call `--record-comprehension-pass` until the user has answered every question\n- If the user asks to skip the quiz, refuse and explain they must pass or set comprehension.enabled: false in openspec/config.yaml";
|
|
9
9
|
//# sourceMappingURL=comprehension-guidance.d.ts.map
|
|
@@ -4,41 +4,44 @@
|
|
|
4
4
|
* Interpolated into apply skill and slash command templates so agents run
|
|
5
5
|
* a spec comprehension gate before implementation.
|
|
6
6
|
*/
|
|
7
|
+
import { COMPREHENSION_PRESENT_AND_GRADE } from './user-prompt-guidance.js';
|
|
7
8
|
export const COMPREHENSION_QUIZ_GUIDANCE = `4. **Comprehension quiz (required before implementation)**
|
|
8
9
|
|
|
9
10
|
After \`openspec instructions apply --change "<name>" --json\`, check comprehension status:
|
|
10
11
|
|
|
11
12
|
- If \`missingComprehension\` is true OR \`comprehension.required && !comprehension.passed\`:
|
|
12
13
|
- Do NOT edit application source code or mark task checkboxes yet
|
|
13
|
-
- Read \`contextFiles.proposal\`, \`contextFiles.design\`, \`contextFiles.specs\`, and \`contextFiles.tasks\` (or the \`tasks\` array in apply JSON)
|
|
14
|
-
- Use \`comprehension.questionCount\` from the JSON
|
|
14
|
+
- Read \`contextFiles.proposal\`, \`contextFiles.design\`, \`contextFiles.specs\`, \`contextFiles.plan\`, and \`contextFiles.tasks\` (or the \`tasks\` array in apply JSON)
|
|
15
|
+
- Use \`comprehension.questionCount\` and \`comprehension.questionAllocation\` from the JSON
|
|
15
16
|
|
|
16
17
|
**Generate questions**
|
|
17
18
|
- Create exactly \`comprehension.questionCount\` multiple-choice questions
|
|
18
|
-
-
|
|
19
|
+
- **Follow \`comprehension.questionAllocation\`** — generate the exact count per category (e.g. plan×4, specs×1); do not invent your own split
|
|
20
|
+
- Each question maps to one artifact category:
|
|
19
21
|
- **Proposal**: motivation, scope, or impact from \`proposal.md\`
|
|
20
22
|
- **Design**: decisions, trade-offs, or approach from \`design.md\`
|
|
21
23
|
- **Specs**: a \`### Requirement:\` or \`#### Scenario:\` from delta specs
|
|
22
|
-
- **
|
|
23
|
-
|
|
24
|
+
- **Plan**: code map, file targets, test plan, sequencing, or alignment with design from \`plan.md\`
|
|
25
|
+
- **Tasks**: conceptual understanding of the implementation approach from pending (unchecked) tasks
|
|
24
26
|
- Do NOT use completed tasks as question sources
|
|
25
|
-
- Each question:
|
|
27
|
+
- Each question: **3 options** (\`comprehension.optionsPerQuestion\`, default 3) — 1 correct from source substance, 2 plausible distractors from other proposal/design/spec/plan/task substance in the change
|
|
28
|
+
|
|
29
|
+
**Plan question quality**
|
|
30
|
+
- Test code map, file targets, test plan, sequencing, or alignment with design
|
|
31
|
+
- **Forbidden**: section numbers, verbatim headings, trivia answerable without reading plan substance
|
|
26
32
|
|
|
27
33
|
**Task question quality**
|
|
28
|
-
- Test scope, approach, dependencies, sequencing rationale, or alignment with proposal/design
|
|
34
|
+
- Test scope, approach, dependencies, sequencing rationale, or alignment with proposal/design/plan
|
|
29
35
|
- **Forbidden**: task numbers, checklist order, "which task says X verbatim", or answers identifiable only by task index or checkbox position
|
|
30
36
|
- Good: "What is the primary file where quiz rules are centralized?" (answer from task substance)
|
|
31
37
|
- Bad: "Which task number updates \`comprehension-guidance.ts\`?" or "What is the exact text of task 2.1?"
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
- Use the **AskUserQuestion tool** for each question (one at a time)
|
|
35
|
-
- Grade: \`score_percent = round(correct / question_count * 100)\`
|
|
36
|
-
- Pass when \`score_percent >= comprehension.thresholdPercent\` (default 80)
|
|
39
|
+
${COMPREHENSION_PRESENT_AND_GRADE}
|
|
37
40
|
|
|
38
41
|
**On failure (score below threshold)**
|
|
39
42
|
- Announce score and that a new quiz is required
|
|
40
43
|
- Update \`.comprehension-session.yaml\` in the change dir with \`used_sources\` from this attempt
|
|
41
|
-
- Generate a NEW question set using different proposal/design/
|
|
44
|
+
- Generate a NEW question set using different proposal/design/spec/plan/task sources (avoid \`used_sources\`)
|
|
42
45
|
- Retry until pass
|
|
43
46
|
|
|
44
47
|
**On pass**
|
|
@@ -52,7 +55,7 @@ export const COMPREHENSION_QUIZ_GUIDANCE = `4. **Comprehension quiz (required be
|
|
|
52
55
|
\`\`\`
|
|
53
56
|
## Applying: <change-name> — comprehension check
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
plan×N, specs×N, design×N, proposal×N, tasks×N → <questionCount> questions (3 options each)
|
|
56
59
|
|
|
57
60
|
Question 1/N: ...
|
|
58
61
|
...
|
|
@@ -62,5 +65,7 @@ export const COMPREHENSION_QUIZ_GUIDANCE = `4. **Comprehension quiz (required be
|
|
|
62
65
|
Then continue to step 5 (show progress) and implementation.`;
|
|
63
66
|
export const COMPREHENSION_APPLY_GUARDRAIL = `- NEVER implement code or mark tasks while \`missingComprehension\` is true
|
|
64
67
|
- NEVER skip the comprehension quiz when the apply JSON requires it
|
|
68
|
+
- NEVER answer comprehension quiz questions yourself — the human developer must answer every question
|
|
69
|
+
- NEVER call \`--record-comprehension-pass\` until the user has answered every question
|
|
65
70
|
- If the user asks to skip the quiz, refuse and explain they must pass or set comprehension.enabled: false in openspec/config.yaml`;
|
|
66
71
|
//# sourceMappingURL=comprehension-guidance.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
2
|
+
import { PROMPT_SELECT_CHANGE_RECENT } from './user-prompt-guidance.js';
|
|
2
3
|
export function getContinueChangeSkillTemplate() {
|
|
3
4
|
return {
|
|
4
5
|
name: 'openspec-continue-change',
|
|
@@ -13,7 +14,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
13
14
|
|
|
14
15
|
1. **If no change name provided, prompt for selection**
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
${PROMPT_SELECT_CHANGE_RECENT}
|
|
17
18
|
|
|
18
19
|
Present the top 3-4 most recently modified changes as options, showing:
|
|
19
20
|
- Change name
|
|
@@ -94,7 +95,7 @@ The artifact types and their purpose depend on the schema. Use the \`instruction
|
|
|
94
95
|
|
|
95
96
|
Common artifact patterns:
|
|
96
97
|
|
|
97
|
-
**spec-driven schema** (proposal → specs → design → tasks):
|
|
98
|
+
**spec-driven schema** (proposal → specs → design → plan → tasks):
|
|
98
99
|
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
|
|
99
100
|
- The Capabilities section is critical - each capability listed will need a spec file.
|
|
100
101
|
- **specs/<capability>/spec.md**: Create one spec per capability listed in the proposal's Capabilities section (use the capability name, not the change name).
|
|
@@ -134,7 +135,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
134
135
|
|
|
135
136
|
1. **If no change name provided, prompt for selection**
|
|
136
137
|
|
|
137
|
-
|
|
138
|
+
${PROMPT_SELECT_CHANGE_RECENT}
|
|
138
139
|
|
|
139
140
|
Present the top 3-4 most recently modified changes as options, showing:
|
|
140
141
|
- Change name
|
|
@@ -215,7 +216,7 @@ The artifact types and their purpose depend on the schema. Use the \`instruction
|
|
|
215
216
|
|
|
216
217
|
Common artifact patterns:
|
|
217
218
|
|
|
218
|
-
**spec-driven schema** (proposal → specs → design → tasks):
|
|
219
|
+
**spec-driven schema** (proposal → specs → design → plan → tasks):
|
|
219
220
|
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
|
|
220
221
|
- The Capabilities section is critical - each capability listed will need a spec file.
|
|
221
222
|
- **specs/<capability>/spec.md**: Create one spec per capability listed in the proposal's Capabilities section (use the capability name, not the change name).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
2
|
+
import { PROMPT_CLARIFY, PROMPT_OPEN_ENDED } from './user-prompt-guidance.js';
|
|
2
3
|
export function getFfChangeSkillTemplate() {
|
|
3
4
|
return {
|
|
4
5
|
name: 'openspec-ff-change',
|
|
@@ -13,7 +14,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
13
14
|
|
|
14
15
|
1. **If no clear input provided, ask what they want to build**
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
${PROMPT_OPEN_ENDED} Ask:
|
|
17
18
|
> "What change do you want to work on? Describe what you want to build or fix."
|
|
18
19
|
|
|
19
20
|
From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`).
|
|
@@ -64,7 +65,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
64
65
|
- Stop when all \`applyRequires\` artifacts are done
|
|
65
66
|
|
|
66
67
|
c. **If an artifact requires user input** (unclear context):
|
|
67
|
-
-
|
|
68
|
+
- ${PROMPT_CLARIFY}
|
|
68
69
|
- Then continue with creation
|
|
69
70
|
|
|
70
71
|
5. **Show final status**
|
|
@@ -117,7 +118,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
117
118
|
|
|
118
119
|
1. **If no input provided, ask what they want to build**
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
${PROMPT_OPEN_ENDED} Ask:
|
|
121
122
|
> "What change do you want to work on? Describe what you want to build or fix."
|
|
122
123
|
|
|
123
124
|
From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`).
|
|
@@ -168,7 +169,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
168
169
|
- Stop when all \`applyRequires\` artifacts are done
|
|
169
170
|
|
|
170
171
|
c. **If an artifact requires user input** (unclear context):
|
|
171
|
-
-
|
|
172
|
+
- ${PROMPT_CLARIFY}
|
|
172
173
|
- Then continue with creation
|
|
173
174
|
|
|
174
175
|
5. **Show final status**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
2
|
+
import { PROMPT_OPEN_ENDED } from './user-prompt-guidance.js';
|
|
2
3
|
export function getNewChangeSkillTemplate() {
|
|
3
4
|
return {
|
|
4
5
|
name: 'openspec-new-change',
|
|
@@ -13,7 +14,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
13
14
|
|
|
14
15
|
1. **If no clear input provided, ask what they want to build**
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
${PROMPT_OPEN_ENDED} Ask:
|
|
17
18
|
> "What change do you want to work on? Describe what you want to build or fix."
|
|
18
19
|
|
|
19
20
|
From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`).
|
|
@@ -89,7 +90,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
89
90
|
|
|
90
91
|
1. **If no input provided, ask what they want to build**
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
${PROMPT_OPEN_ENDED} Ask:
|
|
93
94
|
> "What change do you want to work on? Describe what you want to build or fix."
|
|
94
95
|
|
|
95
96
|
From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`).
|
|
@@ -47,7 +47,7 @@ I'll walk you through a complete change cycle—from idea to implementation—us
|
|
|
47
47
|
1. Pick a small, real task in your codebase
|
|
48
48
|
2. Explore the problem briefly
|
|
49
49
|
3. Create a change (the container for our work)
|
|
50
|
-
4. Build the artifacts: proposal → specs → design → tasks
|
|
50
|
+
4. Build the artifacts: proposal → specs → design → plan → tasks
|
|
51
51
|
5. Implement the tasks
|
|
52
52
|
6. Archive the completed change
|
|
53
53
|
|
|
@@ -170,7 +170,7 @@ Now let's create a change to hold our work.
|
|
|
170
170
|
\`\`\`
|
|
171
171
|
## Creating a Change
|
|
172
172
|
|
|
173
|
-
A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives at the \`changeRoot\` reported by \`openspec status --change "<name>" --json\` and holds your artifacts—proposal, specs, design, tasks.
|
|
173
|
+
A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives at the \`changeRoot\` reported by \`openspec status --change "<name>" --json\` and holds your artifacts—proposal, specs, design, plan, tasks.
|
|
174
174
|
|
|
175
175
|
Let me create one for our task.
|
|
176
176
|
\`\`\`
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { ATLASSIAN_PROPOSE_GUIDANCE } from './mcp-guidance.js';
|
|
2
2
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
3
|
+
import { PROMPT_CLARIFY, PROMPT_OPEN_ENDED } from './user-prompt-guidance.js';
|
|
3
4
|
export function getOpsxProposeSkillTemplate() {
|
|
4
5
|
return {
|
|
5
6
|
name: 'openspec-propose',
|
|
6
|
-
description: 'Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.',
|
|
7
|
+
description: 'Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, plan, and tasks ready for implementation.',
|
|
7
8
|
instructions: `Propose a new change - create the change and generate all artifacts in one step.
|
|
8
9
|
|
|
9
10
|
I'll create a change with artifacts:
|
|
10
11
|
- proposal.md (what & why)
|
|
11
12
|
- design.md (how)
|
|
13
|
+
- plan.md (file-level implementation plan)
|
|
12
14
|
- tasks.md (implementation steps)
|
|
13
15
|
|
|
14
16
|
When ready to implement, run /opsx:apply
|
|
@@ -25,7 +27,7 @@ ${ATLASSIAN_PROPOSE_GUIDANCE}
|
|
|
25
27
|
|
|
26
28
|
1. **If no clear input provided, ask what they want to build**
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
${PROMPT_OPEN_ENDED} Ask:
|
|
29
31
|
> "What change do you want to work on? Describe what you want to build or fix."
|
|
30
32
|
|
|
31
33
|
From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`).
|
|
@@ -76,7 +78,7 @@ ${ATLASSIAN_PROPOSE_GUIDANCE}
|
|
|
76
78
|
- Stop when all \`applyRequires\` artifacts are done
|
|
77
79
|
|
|
78
80
|
c. **If an artifact requires user input** (unclear context):
|
|
79
|
-
-
|
|
81
|
+
- ${PROMPT_CLARIFY}
|
|
80
82
|
- Then continue with creation
|
|
81
83
|
|
|
82
84
|
5. **Show final status**
|
|
@@ -124,6 +126,7 @@ export function getOpsxProposeCommandTemplate() {
|
|
|
124
126
|
I'll create a change with artifacts:
|
|
125
127
|
- proposal.md (what & why)
|
|
126
128
|
- design.md (how)
|
|
129
|
+
- plan.md (file-level implementation plan)
|
|
127
130
|
- tasks.md (implementation steps)
|
|
128
131
|
|
|
129
132
|
When ready to implement, run /opsx:apply
|
|
@@ -140,7 +143,7 @@ ${ATLASSIAN_PROPOSE_GUIDANCE}
|
|
|
140
143
|
|
|
141
144
|
1. **If no input provided, ask what they want to build**
|
|
142
145
|
|
|
143
|
-
|
|
146
|
+
${PROMPT_OPEN_ENDED} Ask:
|
|
144
147
|
> "What change do you want to work on? Describe what you want to build or fix."
|
|
145
148
|
|
|
146
149
|
From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`).
|
|
@@ -191,7 +194,7 @@ ${ATLASSIAN_PROPOSE_GUIDANCE}
|
|
|
191
194
|
- Stop when all \`applyRequires\` artifacts are done
|
|
192
195
|
|
|
193
196
|
c. **If an artifact requires user input** (unclear context):
|
|
194
|
-
-
|
|
197
|
+
- ${PROMPT_CLARIFY}
|
|
195
198
|
- Then continue with creation
|
|
196
199
|
|
|
197
200
|
5. **Show final status**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
2
|
+
import { PROMPT_SELECT_CHANGE } from './user-prompt-guidance.js';
|
|
2
3
|
export function getSyncSpecsSkillTemplate() {
|
|
3
4
|
return {
|
|
4
5
|
name: 'openspec-sync-specs',
|
|
@@ -15,7 +16,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
15
16
|
|
|
16
17
|
1. **If no change name provided, prompt for selection**
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
${PROMPT_SELECT_CHANGE}
|
|
19
20
|
|
|
20
21
|
Show changes that have delta specs (under \`specs/\` directory).
|
|
21
22
|
|
|
@@ -162,7 +163,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
162
163
|
|
|
163
164
|
1. **If no change name provided, prompt for selection**
|
|
164
165
|
|
|
165
|
-
|
|
166
|
+
${PROMPT_SELECT_CHANGE}
|
|
166
167
|
|
|
167
168
|
Show changes that have delta specs (under \`specs/\` directory).
|
|
168
169
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-agnostic user interaction guidance for agent workflows.
|
|
3
|
+
*
|
|
4
|
+
* Cursor's AskUserQuestion is an optional enhancement, not a requirement.
|
|
5
|
+
* All editors (Windsurf, Claude Code, etc.) use plain chat with stop-and-wait.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PROMPT_SELECT_CHANGE = "Prompt the user to select a change:\n - Run `openspec list --json` to get available changes\n - Present the options clearly in chat (numbered or labeled)\n - Ask ONE selection question; STOP and wait for the user's reply before continuing. NEVER answer, infer, or choose on the user's behalf.\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.";
|
|
8
|
+
export declare const PROMPT_SELECT_CHANGE_RECENT = "Run `openspec list --json` to get available changes sorted by most recently modified. Then prompt the user to select which change to work on in chat. Ask ONE selection question; STOP and wait for the user's reply before continuing. NEVER answer, infer, or choose on the user's behalf.\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.";
|
|
9
|
+
export declare const PROMPT_MULTI_SELECT_CHANGES = "Prompt the user to select one or more changes in chat:\n - Show each change with its schema\n - Allow multiple selections (e.g., \"select all that apply\" or comma-separated names)\n - STOP and wait for the user's reply before continuing. NEVER answer, infer, or choose on the user's behalf.\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.";
|
|
10
|
+
export declare const PROMPT_CONFIRM = "Ask the user to confirm before proceeding in chat:\n - State what they are confirming and the consequences\n - Present clear yes/no options\n - STOP and wait for the user's reply before continuing. NEVER answer, infer, or choose on the user's behalf.\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.";
|
|
11
|
+
export declare const PROMPT_OPEN_ENDED = "Ask the user an open-ended question in chat (no preset multiple-choice options):\n - STOP and wait for the user's reply before continuing. NEVER answer, infer, or choose on the user's behalf.\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.";
|
|
12
|
+
export declare const PROMPT_CLARIFY = "Ask the user a clarifying question in chat:\n - STOP and wait for the user's reply before continuing. NEVER answer, infer, or choose on the user's behalf.\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.";
|
|
13
|
+
export declare const COMPREHENSION_PRESENT_AND_GRADE = "**Present and grade**\n - Present each question in chat with labeled options (A/B/C/D or 1\u20134)\n - Ask ONE question at a time; after each, STOP and wait for the user's answer before the next question\n - NEVER select answers yourself, infer what the user would pick, or call `--record-comprehension-pass` until the user has answered every question\n - On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.\n - Grade: `score_percent = round(correct / question_count * 100)`\n - Pass when `score_percent >= comprehension.thresholdPercent` (default 80)";
|
|
14
|
+
//# sourceMappingURL=user-prompt-guidance.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-agnostic user interaction guidance for agent workflows.
|
|
3
|
+
*
|
|
4
|
+
* Cursor's AskUserQuestion is an optional enhancement, not a requirement.
|
|
5
|
+
* All editors (Windsurf, Claude Code, etc.) use plain chat with stop-and-wait.
|
|
6
|
+
*/
|
|
7
|
+
const WAIT_FOR_USER = "STOP and wait for the user's reply before continuing. NEVER answer, infer, or choose on the user's behalf.";
|
|
8
|
+
const CURSOR_HINT = 'On Cursor, you may use the **AskUserQuestion tool** instead of plain chat for this step.';
|
|
9
|
+
export const PROMPT_SELECT_CHANGE = `Prompt the user to select a change:
|
|
10
|
+
- Run \`openspec list --json\` to get available changes
|
|
11
|
+
- Present the options clearly in chat (numbered or labeled)
|
|
12
|
+
- Ask ONE selection question; ${WAIT_FOR_USER}
|
|
13
|
+
- ${CURSOR_HINT}`;
|
|
14
|
+
export const PROMPT_SELECT_CHANGE_RECENT = `Run \`openspec list --json\` to get available changes sorted by most recently modified. Then prompt the user to select which change to work on in chat. Ask ONE selection question; ${WAIT_FOR_USER}
|
|
15
|
+
- ${CURSOR_HINT}`;
|
|
16
|
+
export const PROMPT_MULTI_SELECT_CHANGES = `Prompt the user to select one or more changes in chat:
|
|
17
|
+
- Show each change with its schema
|
|
18
|
+
- Allow multiple selections (e.g., "select all that apply" or comma-separated names)
|
|
19
|
+
- ${WAIT_FOR_USER}
|
|
20
|
+
- ${CURSOR_HINT}`;
|
|
21
|
+
export const PROMPT_CONFIRM = `Ask the user to confirm before proceeding in chat:
|
|
22
|
+
- State what they are confirming and the consequences
|
|
23
|
+
- Present clear yes/no options
|
|
24
|
+
- ${WAIT_FOR_USER}
|
|
25
|
+
- ${CURSOR_HINT}`;
|
|
26
|
+
export const PROMPT_OPEN_ENDED = `Ask the user an open-ended question in chat (no preset multiple-choice options):
|
|
27
|
+
- ${WAIT_FOR_USER}
|
|
28
|
+
- ${CURSOR_HINT}`;
|
|
29
|
+
export const PROMPT_CLARIFY = `Ask the user a clarifying question in chat:
|
|
30
|
+
- ${WAIT_FOR_USER}
|
|
31
|
+
- ${CURSOR_HINT}`;
|
|
32
|
+
export const COMPREHENSION_PRESENT_AND_GRADE = `**Present and grade**
|
|
33
|
+
- Present each question in chat with labeled options (A/B/C/D or 1–4)
|
|
34
|
+
- Ask ONE question at a time; after each, STOP and wait for the user's answer before the next question
|
|
35
|
+
- NEVER select answers yourself, infer what the user would pick, or call \`--record-comprehension-pass\` until the user has answered every question
|
|
36
|
+
- ${CURSOR_HINT}
|
|
37
|
+
- Grade: \`score_percent = round(correct / question_count * 100)\`
|
|
38
|
+
- Pass when \`score_percent >= comprehension.thresholdPercent\` (default 80)`;
|
|
39
|
+
//# sourceMappingURL=user-prompt-guidance.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PLAYWRIGHT_VERIFY_GUIDANCE } from './mcp-guidance.js';
|
|
2
2
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
3
|
+
import { PROMPT_SELECT_CHANGE } from './user-prompt-guidance.js';
|
|
3
4
|
export function getVerifyChangeSkillTemplate() {
|
|
4
5
|
return {
|
|
5
6
|
name: 'openspec-verify-change',
|
|
@@ -14,7 +15,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
14
15
|
|
|
15
16
|
1. **If no change name provided, prompt for selection**
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
${PROMPT_SELECT_CHANGE}
|
|
18
19
|
|
|
19
20
|
Show changes that have implementation tasks (tasks artifact exists).
|
|
20
21
|
Include the schema used for each change if available.
|
|
@@ -188,7 +189,7 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
188
189
|
|
|
189
190
|
1. **If no change name provided, prompt for selection**
|
|
190
191
|
|
|
191
|
-
|
|
192
|
+
${PROMPT_SELECT_CHANGE}
|
|
192
193
|
|
|
193
194
|
Show changes that have implementation tasks (tasks artifact exists).
|
|
194
195
|
Include the schema used for each change if available.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
name: spec-driven
|
|
2
2
|
version: 1
|
|
3
|
-
description: Default OpenSpec workflow - proposal → specs → design → tasks
|
|
3
|
+
description: Default OpenSpec workflow - proposal → specs → design → plan → tasks
|
|
4
4
|
artifacts:
|
|
5
5
|
- id: proposal
|
|
6
6
|
generates: proposal.md
|
|
@@ -24,7 +24,7 @@ artifacts:
|
|
|
24
24
|
Keep it concise (1-2 pages). Focus on the "why" not the "how" -
|
|
25
25
|
implementation details belong in design.md.
|
|
26
26
|
|
|
27
|
-
This is the foundation - specs, design, and tasks all build on this.
|
|
27
|
+
This is the foundation - specs, design, plan, and tasks all build on this.
|
|
28
28
|
requires: []
|
|
29
29
|
|
|
30
30
|
- id: specs
|
|
@@ -110,6 +110,26 @@ artifacts:
|
|
|
110
110
|
requires:
|
|
111
111
|
- proposal
|
|
112
112
|
|
|
113
|
+
- id: plan
|
|
114
|
+
generates: plan.md
|
|
115
|
+
description: File-level implementation plan with code map
|
|
116
|
+
template: plan.md
|
|
117
|
+
instruction: |
|
|
118
|
+
Create a concrete implementation plan from design and specs.
|
|
119
|
+
|
|
120
|
+
Sections:
|
|
121
|
+
- **Code map**: Files to create, modify, or delete
|
|
122
|
+
- **Implementation order**: Sequenced steps at file/module level
|
|
123
|
+
- **Test plan**: How to verify the change
|
|
124
|
+
- **Risks**: Known risks with mitigations ([Risk] → Mitigation)
|
|
125
|
+
- **Done definition**: Objective criteria for completion
|
|
126
|
+
|
|
127
|
+
Do NOT restate behavioral requirements — link to specs.
|
|
128
|
+
Do NOT duplicate design decisions — reference design.md.
|
|
129
|
+
requires:
|
|
130
|
+
- specs
|
|
131
|
+
- design
|
|
132
|
+
|
|
113
133
|
- id: tasks
|
|
114
134
|
generates: tasks.md
|
|
115
135
|
description: Implementation checklist with trackable tasks
|
|
@@ -139,14 +159,17 @@ artifacts:
|
|
|
139
159
|
- [ ] 2.2 Add CSV formatting utilities
|
|
140
160
|
```
|
|
141
161
|
|
|
142
|
-
|
|
162
|
+
Derive tasks from plan.md code map. Reference specs for what to build,
|
|
163
|
+
design for architectural decisions, and plan for file-level targets.
|
|
164
|
+
Include file/module references where helpful.
|
|
143
165
|
Each task should be verifiable - you know when it's done.
|
|
144
166
|
requires:
|
|
145
167
|
- specs
|
|
146
168
|
- design
|
|
169
|
+
- plan
|
|
147
170
|
|
|
148
171
|
apply:
|
|
149
|
-
requires: [tasks]
|
|
172
|
+
requires: [plan, tasks]
|
|
150
173
|
tracks: tasks.md
|
|
151
174
|
instruction: |
|
|
152
175
|
Read context files, work through pending tasks, mark complete as you go.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Code Map
|
|
2
|
+
|
|
3
|
+
<!-- files to create/modify/delete -->
|
|
4
|
+
|
|
5
|
+
## Implementation Order
|
|
6
|
+
|
|
7
|
+
<!-- sequenced steps at file/module level -->
|
|
8
|
+
|
|
9
|
+
## Test Plan
|
|
10
|
+
|
|
11
|
+
<!-- how to verify -->
|
|
12
|
+
|
|
13
|
+
## Risks
|
|
14
|
+
|
|
15
|
+
<!-- [Risk] → Mitigation -->
|
|
16
|
+
|
|
17
|
+
## Done Definition
|
|
18
|
+
|
|
19
|
+
<!-- objective completion criteria -->
|