@codewalla_india/openspec 1.3.1 → 1.3.2
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/LICENSE +22 -22
- package/README.md +272 -191
- package/bin/openspec.js +5 -5
- package/dist/cli/index.js +99 -62
- package/dist/commands/change.d.ts +0 -2
- package/dist/commands/change.js +107 -86
- package/dist/commands/config.js +14 -16
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/feedback.js +79 -30
- package/dist/commands/modify.d.ts +26 -0
- package/dist/commands/modify.js +147 -0
- package/dist/commands/schema.js +170 -91
- package/dist/commands/show.js +5 -2
- package/dist/commands/spec.js +49 -29
- package/dist/commands/store.js +15 -21
- package/dist/commands/validate.d.ts +8 -0
- package/dist/commands/validate.js +43 -20
- package/dist/commands/workflow/index.d.ts +2 -2
- package/dist/commands/workflow/index.js +1 -1
- package/dist/commands/workflow/instructions.d.ts +6 -26
- package/dist/commands/workflow/instructions.js +183 -376
- package/dist/commands/workflow/new-change.d.ts +0 -4
- package/dist/commands/workflow/new-change.js +10 -28
- package/dist/commands/workflow/shared.d.ts +34 -19
- package/dist/commands/workflow/shared.js +34 -4
- package/dist/commands/workflow/status.js +10 -32
- package/dist/commands/workflow/templates.js +15 -5
- package/dist/commands/workset.d.ts +2 -2
- package/dist/commands/workset.js +19 -22
- package/dist/comprehension-quiz/fingerprint.d.ts +39 -0
- package/dist/comprehension-quiz/fingerprint.js +84 -0
- package/dist/comprehension-quiz/index.d.ts +42 -0
- package/dist/comprehension-quiz/index.js +47 -0
- package/dist/comprehension-quiz/pass-record.d.ts +44 -0
- package/dist/comprehension-quiz/pass-record.js +98 -0
- package/dist/comprehension-quiz/providers/anthropic.d.ts +34 -0
- package/dist/comprehension-quiz/providers/anthropic.js +53 -0
- package/dist/comprehension-quiz/providers/factory.d.ts +44 -0
- package/dist/comprehension-quiz/providers/factory.js +174 -0
- package/dist/comprehension-quiz/providers/interface.d.ts +59 -0
- package/dist/comprehension-quiz/providers/interface.js +63 -0
- package/dist/comprehension-quiz/providers/local.d.ts +34 -0
- package/dist/comprehension-quiz/providers/local.js +53 -0
- package/dist/comprehension-quiz/providers/ollama.d.ts +34 -0
- package/dist/comprehension-quiz/providers/ollama.js +53 -0
- package/dist/comprehension-quiz/providers/openai.d.ts +34 -0
- package/dist/comprehension-quiz/providers/openai.js +53 -0
- package/dist/comprehension-quiz/question-generator.d.ts +69 -0
- package/dist/comprehension-quiz/question-generator.js +141 -0
- package/dist/comprehension-quiz/quiz-executor.d.ts +70 -0
- package/dist/comprehension-quiz/quiz-executor.js +165 -0
- package/dist/comprehension-quiz/types.d.ts +115 -0
- package/dist/comprehension-quiz/types.js +11 -0
- package/dist/core/archive.d.ts +14 -1
- package/dist/core/archive.js +1350 -190
- package/dist/core/artifact-graph/graph.d.ts +28 -9
- package/dist/core/artifact-graph/graph.js +94 -43
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/index.js +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +30 -3
- package/dist/core/artifact-graph/instruction-loader.js +70 -12
- package/dist/core/artifact-graph/outputs.d.ts +1 -0
- package/dist/core/artifact-graph/outputs.js +67 -5
- package/dist/core/artifact-graph/resolver.d.ts +14 -0
- package/dist/core/artifact-graph/resolver.js +69 -15
- package/dist/core/artifact-graph/types.js +21 -3
- package/dist/core/available-tools.d.ts +2 -2
- package/dist/core/available-tools.js +14 -3
- package/dist/core/change-metadata/schema.d.ts +2 -0
- package/dist/core/change-metadata/schema.js +13 -0
- package/dist/core/change-status-policy.d.ts +10 -1
- package/dist/core/change-status-policy.js +30 -2
- package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
- package/dist/core/command-generation/adapters/amazon-q.js +11 -5
- package/dist/core/command-generation/adapters/antigravity.js +6 -5
- package/dist/core/command-generation/adapters/auggie.js +7 -6
- package/dist/core/command-generation/adapters/bob.d.ts +5 -1
- package/dist/core/command-generation/adapters/bob.js +11 -10
- package/dist/core/command-generation/adapters/claude.d.ts +1 -1
- package/dist/core/command-generation/adapters/claude.js +12 -17
- package/dist/core/command-generation/adapters/cline.js +5 -5
- package/dist/core/command-generation/adapters/codebuddy.js +8 -7
- package/dist/core/command-generation/adapters/continue.js +8 -7
- package/dist/core/command-generation/adapters/costrict.js +7 -6
- package/dist/core/command-generation/adapters/crush.js +9 -9
- package/dist/core/command-generation/adapters/cursor.js +8 -8
- package/dist/core/command-generation/adapters/devin.d.ts +19 -0
- package/dist/core/command-generation/adapters/devin.js +36 -0
- package/dist/core/command-generation/adapters/factory.js +7 -6
- package/dist/core/command-generation/adapters/gemini.js +40 -5
- package/dist/core/command-generation/adapters/github-copilot.js +6 -5
- package/dist/core/command-generation/adapters/iflow.js +9 -8
- package/dist/core/command-generation/adapters/index.d.ts +4 -2
- package/dist/core/command-generation/adapters/index.js +4 -2
- package/dist/core/command-generation/adapters/junie.js +6 -5
- package/dist/core/command-generation/adapters/kilocode.js +1 -1
- package/dist/core/command-generation/adapters/kiro.js +6 -5
- package/dist/core/command-generation/adapters/lingma.js +9 -9
- package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
- package/dist/core/command-generation/adapters/oh-my-pi.js +42 -0
- package/dist/core/command-generation/adapters/opencode.js +6 -8
- package/dist/core/command-generation/adapters/pi.d.ts +2 -2
- package/dist/core/command-generation/adapters/pi.js +7 -10
- package/dist/core/command-generation/adapters/qoder.js +9 -9
- package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
- package/dist/core/command-generation/adapters/qwen.js +14 -9
- package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
- package/dist/core/command-generation/adapters/roocode.js +9 -9
- package/dist/core/command-generation/adapters/trae.d.ts +13 -0
- package/dist/core/command-generation/adapters/trae.js +28 -0
- package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
- package/dist/core/command-generation/adapters/zcode.js +33 -0
- package/dist/core/command-generation/generator.d.ts +8 -0
- package/dist/core/command-generation/generator.js +15 -1
- package/dist/core/command-generation/index.d.ts +1 -1
- package/dist/core/command-generation/index.js +1 -1
- package/dist/core/command-generation/invocation.d.ts +73 -0
- package/dist/core/command-generation/invocation.js +77 -0
- package/dist/core/command-generation/registry.js +8 -4
- package/dist/core/command-generation/types.d.ts +9 -2
- package/dist/core/command-generation/yaml.d.ts +20 -6
- package/dist/core/command-generation/yaml.js +31 -21
- package/dist/core/command-surface.d.ts +15 -0
- package/dist/core/command-surface.js +33 -0
- package/dist/core/completions/command-registry.js +16 -61
- package/dist/core/completions/generators/bash-generator.js +41 -41
- package/dist/core/completions/generators/fish-generator.js +7 -7
- package/dist/core/completions/generators/powershell-generator.js +44 -37
- package/dist/core/completions/generators/zsh-generator.js +33 -33
- package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
- package/dist/core/completions/installers/zsh-installer.js +20 -4
- package/dist/core/completions/templates/bash-templates.js +24 -24
- package/dist/core/completions/templates/fish-templates.js +38 -38
- package/dist/core/completions/templates/powershell-templates.js +28 -28
- package/dist/core/completions/templates/zsh-templates.js +39 -39
- package/dist/core/config-prompts.d.ts +1 -1
- package/dist/core/config-prompts.js +14 -1
- package/dist/core/config-schema.d.ts +10 -1
- package/dist/core/config-schema.js +67 -1
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.js +46 -5
- package/dist/core/file-state.js +56 -24
- package/dist/core/github-copilot/cloud-agent.d.ts +95 -0
- package/dist/core/github-copilot/cloud-agent.js +497 -0
- package/dist/core/global-config.d.ts +16 -0
- package/dist/core/init.d.ts +58 -0
- package/dist/core/init.js +474 -73
- package/dist/core/legacy-cleanup.d.ts +59 -0
- package/dist/core/legacy-cleanup.js +253 -24
- package/dist/core/list.js +23 -16
- package/dist/core/migration.d.ts +73 -1
- package/dist/core/migration.js +360 -11
- package/dist/core/modify/conflict-detector.d.ts +26 -0
- package/dist/core/modify/conflict-detector.js +62 -0
- package/dist/core/modify/dependency-propagator.d.ts +38 -0
- package/dist/core/modify/dependency-propagator.js +103 -0
- package/dist/core/modify/history-tracker.d.ts +34 -0
- package/dist/core/modify/history-tracker.js +114 -0
- package/dist/core/modify/index.d.ts +7 -0
- package/dist/core/modify/index.js +7 -0
- package/dist/core/onboarding-commands.d.ts +30 -0
- package/dist/core/onboarding-commands.js +38 -0
- package/dist/core/openspec-root.js +18 -15
- package/dist/core/parsers/change-parser.d.ts +17 -2
- package/dist/core/parsers/change-parser.js +35 -21
- package/dist/core/parsers/code-fence.d.ts +15 -0
- package/dist/core/parsers/code-fence.js +49 -0
- package/dist/core/parsers/markdown-parser.d.ts +0 -3
- package/dist/core/parsers/markdown-parser.js +7 -60
- package/dist/core/parsers/requirement-blocks.d.ts +29 -0
- package/dist/core/parsers/requirement-blocks.js +150 -31
- package/dist/core/parsers/requirement-text.d.ts +48 -0
- package/dist/core/parsers/requirement-text.js +98 -0
- package/dist/core/parsers/spec-structure.d.ts +1 -1
- package/dist/core/parsers/spec-structure.js +20 -30
- package/dist/core/profile-sync-drift.d.ts +0 -8
- package/dist/core/profile-sync-drift.js +49 -50
- package/dist/core/profiles.d.ts +2 -2
- package/dist/core/profiles.js +2 -2
- package/dist/core/project-config.d.ts +27 -18
- package/dist/core/project-config.js +101 -34
- package/dist/core/references.js +42 -4
- package/dist/core/relationship-health.d.ts +8 -0
- package/dist/core/relationship-health.js +13 -0
- package/dist/core/root-selection.d.ts +9 -6
- package/dist/core/root-selection.js +42 -5
- package/dist/core/schemas/base.schema.js +7 -2
- package/dist/core/shared/allowed-tools.d.ts +12 -0
- package/dist/core/shared/allowed-tools.js +12 -0
- package/dist/core/shared/index.d.ts +1 -0
- package/dist/core/shared/index.js +1 -0
- package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
- package/dist/core/shared/skill-content-equivalence.js +41 -0
- package/dist/core/shared/skill-generation.js +17 -13
- package/dist/core/shared/skill-paths.d.ts +13 -0
- package/dist/core/shared/skill-paths.js +23 -0
- package/dist/core/shared/tool-detection.d.ts +27 -7
- package/dist/core/shared/tool-detection.js +167 -37
- package/dist/core/shared-skill-target.d.ts +16 -0
- package/dist/core/shared-skill-target.js +156 -0
- package/dist/core/specs-apply.d.ts +80 -33
- package/dist/core/specs-apply.js +667 -145
- package/dist/core/store/git.d.ts +13 -0
- package/dist/core/store/git.js +24 -0
- package/dist/core/store/operations.js +20 -0
- package/dist/core/templates/skill-templates.d.ts +3 -2
- package/dist/core/templates/skill-templates.js +3 -2
- package/dist/core/templates/workflows/apply-change.d.ts +10 -0
- package/dist/core/templates/workflows/apply-change.js +186 -224
- package/dist/core/templates/workflows/archive-change.js +387 -260
- package/dist/core/templates/workflows/bulk-archive-change.js +655 -475
- package/dist/core/templates/workflows/continue-change.js +210 -219
- package/dist/core/templates/workflows/explore.js +479 -443
- package/dist/core/templates/workflows/feedback.js +97 -97
- package/dist/core/templates/workflows/ff-change.js +200 -191
- package/dist/core/templates/workflows/modify-change.d.ts +4 -1
- package/dist/core/templates/workflows/modify-change.js +237 -115
- package/dist/core/templates/workflows/new-change.js +127 -134
- package/dist/core/templates/workflows/onboard.js +548 -542
- package/dist/core/templates/workflows/propose.js +273 -217
- package/dist/core/templates/workflows/store-selection.d.ts +1 -1
- package/dist/core/templates/workflows/store-selection.js +1 -1
- package/dist/core/templates/workflows/sync-specs.js +500 -273
- package/dist/core/templates/workflows/update-change.d.ts +10 -0
- package/dist/core/templates/workflows/update-change.js +177 -0
- package/dist/core/templates/workflows/verify-change.js +324 -326
- package/dist/core/update.d.ts +40 -5
- package/dist/core/update.js +411 -96
- package/dist/core/validation/constants.d.ts +4 -1
- package/dist/core/validation/constants.js +4 -1
- package/dist/core/validation/plan-validator.d.ts +8 -0
- package/dist/core/validation/plan-validator.js +45 -0
- package/dist/core/validation/task-numbering.d.ts +16 -0
- package/dist/core/validation/task-numbering.js +57 -0
- package/dist/core/validation/validator.d.ts +27 -3
- package/dist/core/validation/validator.js +337 -48
- package/dist/core/version-check.d.ts +152 -0
- package/dist/core/version-check.js +731 -0
- package/dist/core/view.js +13 -18
- package/dist/prompts/searchable-multi-select.js +1 -1
- package/dist/telemetry/caller-detection.d.ts +27 -0
- package/dist/telemetry/caller-detection.js +85 -0
- package/dist/telemetry/caller-detection.test.d.ts +5 -0
- package/dist/telemetry/config.d.ts +48 -2
- package/dist/telemetry/config.js +113 -11
- package/dist/telemetry/config.test.d.ts +5 -0
- package/dist/telemetry/deduplication.d.ts +35 -0
- package/dist/telemetry/deduplication.js +82 -0
- package/dist/telemetry/deduplication.test.d.ts +5 -0
- package/dist/telemetry/identity.d.ts +62 -22
- package/dist/telemetry/identity.js +147 -102
- package/dist/telemetry/index.d.ts +126 -15
- package/dist/telemetry/index.js +569 -37
- package/dist/telemetry/sanitization.d.ts +40 -0
- package/dist/telemetry/sanitization.js +114 -0
- package/dist/telemetry/sanitization.test.d.ts +5 -0
- package/dist/telemetry/throttling.d.ts +20 -0
- package/dist/telemetry/throttling.js +70 -0
- package/dist/ui/welcome-screen.d.ts +9 -1
- package/dist/ui/welcome-screen.js +91 -38
- package/dist/utils/change-metadata.d.ts +43 -0
- package/dist/utils/change-metadata.js +123 -7
- package/dist/utils/change-utils.d.ts +10 -4
- package/dist/utils/change-utils.js +20 -13
- package/dist/utils/ci.d.ts +12 -0
- package/dist/utils/ci.js +16 -0
- package/dist/utils/command-references.d.ts +92 -6
- package/dist/utils/command-references.js +180 -7
- package/dist/utils/date.d.ts +8 -0
- package/dist/utils/date.js +13 -0
- package/dist/utils/file-system.d.ts +10 -0
- package/dist/utils/file-system.js +70 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/interactive.d.ts +23 -0
- package/dist/utils/interactive.js +32 -0
- package/dist/utils/item-discovery.d.ts +19 -0
- package/dist/utils/item-discovery.js +30 -48
- package/dist/utils/shell-detection.d.ts +1 -1
- package/dist/utils/shell-detection.js +64 -11
- package/dist/utils/spec-discovery.d.ts +40 -0
- package/dist/utils/spec-discovery.js +115 -0
- package/dist/utils/task-progress.d.ts +29 -1
- package/dist/utils/task-progress.js +109 -15
- package/package.json +96 -84
- package/schemas/spec-driven/schema.yaml +252 -184
- package/schemas/spec-driven/templates/design.md +19 -19
- package/schemas/spec-driven/templates/plan-template.md +91 -0
- package/schemas/spec-driven/templates/proposal.md +29 -24
- package/schemas/spec-driven/templates/spec.md +11 -8
- package/schemas/spec-driven/templates/tasks.md +9 -9
- package/scripts/postinstall.js +83 -83
- package/dist/core/command-generation/adapters/codex.d.ts +0 -16
- package/dist/core/command-generation/adapters/codex.js +0 -39
- package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
- package/dist/core/command-generation/adapters/windsurf.js +0 -38
- package/dist/core/comprehension/config.d.ts +0 -20
- package/dist/core/comprehension/config.js +0 -23
- package/dist/core/comprehension/fingerprint.d.ts +0 -14
- package/dist/core/comprehension/fingerprint.js +0 -33
- package/dist/core/comprehension/index.d.ts +0 -57
- package/dist/core/comprehension/index.js +0 -87
- package/dist/core/comprehension/pass-record.d.ts +0 -29
- package/dist/core/comprehension/pass-record.js +0 -64
- package/dist/core/comprehension/stats.d.ts +0 -35
- package/dist/core/comprehension/stats.js +0 -133
- package/dist/core/templates/workflows/comprehension-guidance.d.ts +0 -9
- package/dist/core/templates/workflows/comprehension-guidance.js +0 -73
- package/dist/core/templates/workflows/mcp-guidance.d.ts +0 -13
- package/dist/core/templates/workflows/mcp-guidance.js +0 -131
- package/dist/core/templates/workflows/user-prompt-guidance.d.ts +0 -16
- package/dist/core/templates/workflows/user-prompt-guidance.js +0 -48
- package/dist/telemetry/caller.d.ts +0 -5
- package/dist/telemetry/caller.js +0 -29
- package/dist/telemetry/client.d.ts +0 -27
- package/dist/telemetry/client.js +0 -127
- package/dist/telemetry/command-context.d.ts +0 -13
- package/dist/telemetry/command-context.js +0 -59
- package/dist/telemetry/comprehension.d.ts +0 -44
- package/dist/telemetry/comprehension.js +0 -105
- package/dist/telemetry/content.d.ts +0 -10
- package/dist/telemetry/content.js +0 -56
- package/dist/telemetry/git-stats.d.ts +0 -12
- package/dist/telemetry/git-stats.js +0 -69
- package/dist/telemetry/identify-cache.d.ts +0 -7
- package/dist/telemetry/identify-cache.js +0 -47
- package/dist/telemetry/input.d.ts +0 -17
- package/dist/telemetry/input.js +0 -68
- package/dist/telemetry/marker.d.ts +0 -37
- package/dist/telemetry/marker.js +0 -67
- package/dist/telemetry/workflow.d.ts +0 -85
- package/dist/telemetry/workflow.js +0 -318
- package/schemas/spec-driven/templates/plan.md +0 -19
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Tracks modification history in the change marker (.openspec.yaml).
|
|
5
|
+
*/
|
|
6
|
+
export class HistoryTracker {
|
|
7
|
+
changeRoot;
|
|
8
|
+
constructor(changeRoot) {
|
|
9
|
+
this.changeRoot = changeRoot;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Records a modification event in the change marker.
|
|
13
|
+
*/
|
|
14
|
+
async recordModification(changeName, modifiedArtifacts, intent) {
|
|
15
|
+
const markerPath = join(this.changeRoot, changeName, '.openspec.yaml');
|
|
16
|
+
try {
|
|
17
|
+
const content = await readFile(markerPath, 'utf-8');
|
|
18
|
+
const marker = this.parseMarker(content);
|
|
19
|
+
// Add new modification record
|
|
20
|
+
marker.modifyHistory.push({
|
|
21
|
+
timestamp: new Date().toISOString(),
|
|
22
|
+
modifiedArtifacts,
|
|
23
|
+
intent,
|
|
24
|
+
});
|
|
25
|
+
// Write back
|
|
26
|
+
await writeFile(markerPath, this.stringifyMarker(marker), 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
// If marker doesn't exist, create it
|
|
30
|
+
if (error.code === 'ENOENT') {
|
|
31
|
+
const marker = {
|
|
32
|
+
modifyHistory: [{
|
|
33
|
+
timestamp: new Date().toISOString(),
|
|
34
|
+
modifiedArtifacts,
|
|
35
|
+
intent,
|
|
36
|
+
}],
|
|
37
|
+
};
|
|
38
|
+
await writeFile(markerPath, this.stringifyMarker(marker), 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Gets the modification history for a change.
|
|
47
|
+
*/
|
|
48
|
+
async getHistory(changeName) {
|
|
49
|
+
const markerPath = join(this.changeRoot, changeName, '.openspec.yaml');
|
|
50
|
+
try {
|
|
51
|
+
const content = await readFile(markerPath, 'utf-8');
|
|
52
|
+
const marker = this.parseMarker(content);
|
|
53
|
+
return marker.modifyHistory || [];
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// If marker doesn't exist, return empty history
|
|
57
|
+
if (error.code === 'ENOENT') {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parses the change marker YAML content.
|
|
65
|
+
* In a full implementation, this would use a proper YAML parser.
|
|
66
|
+
*/
|
|
67
|
+
parseMarker(content) {
|
|
68
|
+
// Simplified parsing - in production, use a YAML library
|
|
69
|
+
const lines = content.split('\n');
|
|
70
|
+
const marker = { modifyHistory: [] };
|
|
71
|
+
let inHistory = false;
|
|
72
|
+
let currentRecord = {};
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
if (line.trim() === 'modifyHistory:') {
|
|
75
|
+
inHistory = true;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (inHistory) {
|
|
79
|
+
if (line.trim().startsWith('- timestamp:')) {
|
|
80
|
+
if (currentRecord.timestamp && currentRecord.modifiedArtifacts && currentRecord.intent) {
|
|
81
|
+
marker.modifyHistory.push(currentRecord);
|
|
82
|
+
}
|
|
83
|
+
currentRecord = { timestamp: line.split(':')[1].trim() };
|
|
84
|
+
}
|
|
85
|
+
else if (line.trim().startsWith('modifiedArtifacts:')) {
|
|
86
|
+
currentRecord.modifiedArtifacts = line.split(':')[1].trim().split(',').map(s => s.trim());
|
|
87
|
+
}
|
|
88
|
+
else if (line.trim().startsWith('intent:')) {
|
|
89
|
+
currentRecord.intent = line.split(':')[1].trim();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Add the last record
|
|
94
|
+
if (currentRecord.timestamp && currentRecord.modifiedArtifacts && currentRecord.intent) {
|
|
95
|
+
marker.modifyHistory.push(currentRecord);
|
|
96
|
+
}
|
|
97
|
+
return marker;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Stringifies the change marker to YAML format.
|
|
101
|
+
* In a full implementation, this would use a proper YAML stringifier.
|
|
102
|
+
*/
|
|
103
|
+
stringifyMarker(marker) {
|
|
104
|
+
// Simplified YAML generation - in production, use a YAML library
|
|
105
|
+
let yaml = 'modifyHistory:\n';
|
|
106
|
+
for (const record of marker.modifyHistory) {
|
|
107
|
+
yaml += ` - timestamp: ${record.timestamp}\n`;
|
|
108
|
+
yaml += ` modifiedArtifacts: [${record.modifiedArtifacts.join(', ')}]\n`;
|
|
109
|
+
yaml += ` intent: ${record.intent}\n`;
|
|
110
|
+
}
|
|
111
|
+
return yaml;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=history-tracker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { DependencyPropagator } from './dependency-propagator.js';
|
|
2
|
+
export type { DependencyPropagationResult } from './dependency-propagator.js';
|
|
3
|
+
export { ConflictDetector } from './conflict-detector.js';
|
|
4
|
+
export type { ConflictInfo } from './conflict-detector.js';
|
|
5
|
+
export { HistoryTracker } from './history-tracker.js';
|
|
6
|
+
export type { ModificationRecord, ChangeMarker } from './history-tracker.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Dependency Propagator
|
|
2
|
+
export { DependencyPropagator } from './dependency-propagator.js';
|
|
3
|
+
// Conflict Detector
|
|
4
|
+
export { ConflictDetector } from './conflict-detector.js';
|
|
5
|
+
// History Tracker
|
|
6
|
+
export { HistoryTracker } from './history-tracker.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding command hints.
|
|
3
|
+
*
|
|
4
|
+
* The commands shown to a user after setup must be limited to the workflows
|
|
5
|
+
* their profile actually installs, otherwise we advertise slash commands that
|
|
6
|
+
* were correctly never generated.
|
|
7
|
+
*
|
|
8
|
+
* This module decides WHICH hints to show. How each one is spelled for a given
|
|
9
|
+
* tool — command, skill, or a tool-specific skill prefix — is decided by
|
|
10
|
+
* src/utils/command-references.ts at the call site.
|
|
11
|
+
*/
|
|
12
|
+
import type { WorkflowId } from './profiles.js';
|
|
13
|
+
export type OnboardingCommand = {
|
|
14
|
+
workflow: WorkflowId;
|
|
15
|
+
command: string;
|
|
16
|
+
description: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Longest description the welcome screen can render. It shows these beside a
|
|
20
|
+
* 24-column art column and only animates at MIN_WIDTH (60) columns or wider; a
|
|
21
|
+
* longer line wraps, and the animation's cursor-up count assumes unwrapped
|
|
22
|
+
* lines. See src/ui/welcome-screen.ts.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DESCRIPTION_BUDGET = 17;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the onboarding hints for the installed workflows, in lifecycle order.
|
|
27
|
+
* Returns an empty array when none of the onboarding workflows are installed.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getOnboardingCommands(workflows: readonly string[]): OnboardingCommand[];
|
|
30
|
+
//# sourceMappingURL=onboarding-commands.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding command hints.
|
|
3
|
+
*
|
|
4
|
+
* The commands shown to a user after setup must be limited to the workflows
|
|
5
|
+
* their profile actually installs, otherwise we advertise slash commands that
|
|
6
|
+
* were correctly never generated.
|
|
7
|
+
*
|
|
8
|
+
* This module decides WHICH hints to show. How each one is spelled for a given
|
|
9
|
+
* tool — command, skill, or a tool-specific skill prefix — is decided by
|
|
10
|
+
* src/utils/command-references.ts at the call site.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Longest description the welcome screen can render. It shows these beside a
|
|
14
|
+
* 24-column art column and only animates at MIN_WIDTH (60) columns or wider; a
|
|
15
|
+
* longer line wraps, and the animation's cursor-up count assumes unwrapped
|
|
16
|
+
* lines. See src/ui/welcome-screen.ts.
|
|
17
|
+
*/
|
|
18
|
+
export const DESCRIPTION_BUDGET = 17;
|
|
19
|
+
/**
|
|
20
|
+
* Ordered onboarding hints. Each entry is shown only when its workflow is
|
|
21
|
+
* installed, so the list follows the change lifecycle: start, then build,
|
|
22
|
+
* then implement.
|
|
23
|
+
*/
|
|
24
|
+
const ONBOARDING_COMMANDS = [
|
|
25
|
+
{ workflow: 'propose', command: '/opsx:propose', description: 'Start a change' },
|
|
26
|
+
{ workflow: 'new', command: '/opsx:new', description: 'Scaffold a change' },
|
|
27
|
+
{ workflow: 'continue', command: '/opsx:continue', description: 'Next artifact' },
|
|
28
|
+
{ workflow: 'apply', command: '/opsx:apply', description: 'Implement tasks' },
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Returns the onboarding hints for the installed workflows, in lifecycle order.
|
|
32
|
+
* Returns an empty array when none of the onboarding workflows are installed.
|
|
33
|
+
*/
|
|
34
|
+
export function getOnboardingCommands(workflows) {
|
|
35
|
+
const installed = new Set(workflows);
|
|
36
|
+
return ONBOARDING_COMMANDS.filter((entry) => installed.has(entry.workflow));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=onboarding-commands.js.map
|
|
@@ -11,8 +11,8 @@ export const OPENSPEC_CHANGES_DIR = 'openspec/changes';
|
|
|
11
11
|
export const OPENSPEC_ARCHIVE_DIR = 'openspec/changes/archive';
|
|
12
12
|
export const DEFAULT_OPENSPEC_SCHEMA = 'spec-driven';
|
|
13
13
|
export const DIRECTORY_ANCHOR_FILE_NAME = '.gitkeep';
|
|
14
|
-
// Git cannot track empty directories, so
|
|
15
|
-
//
|
|
14
|
+
// Git cannot track empty directories, so setup anchors otherwise-empty
|
|
15
|
+
// conventional store directories for teammates who clone the repo later.
|
|
16
16
|
export const ANCHORED_OPENSPEC_DIRS = [OPENSPEC_SPECS_DIR, OPENSPEC_ARCHIVE_DIR];
|
|
17
17
|
async function pathKind(targetPath) {
|
|
18
18
|
try {
|
|
@@ -51,6 +51,14 @@ function unresolvedInspection() {
|
|
|
51
51
|
function missingDirectoryDiagnostic(code, message, target) {
|
|
52
52
|
return makeStoreDiagnostic('error', code, message, { target });
|
|
53
53
|
}
|
|
54
|
+
async function inspectOptionalPlanningDirectory(inspection, storeRoot, key, relativePath, notDirectoryCode, target) {
|
|
55
|
+
const kind = await pathKind(path.join(storeRoot, relativePath));
|
|
56
|
+
inspection[key] = { present: kind === 'directory' };
|
|
57
|
+
if (kind === 'directory' || kind === 'missing')
|
|
58
|
+
return kind;
|
|
59
|
+
inspection.diagnostics.push(missingDirectoryDiagnostic(notDirectoryCode, `${relativePath}/ exists but is not a directory.`, target));
|
|
60
|
+
return kind;
|
|
61
|
+
}
|
|
54
62
|
export async function inspectOpenSpecRoot(storeRoot) {
|
|
55
63
|
const rootKind = await pathKind(storeRoot);
|
|
56
64
|
const inspection = unresolvedInspection();
|
|
@@ -90,23 +98,18 @@ export async function inspectOpenSpecRoot(storeRoot) {
|
|
|
90
98
|
inspection.diagnostics.push(missingDirectoryDiagnostic('openspec_config_missing', 'Missing openspec/config.yaml or openspec/config.yml.', 'openspec.config'));
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
inspection
|
|
100
|
-
if (kind === 'directory')
|
|
101
|
-
continue;
|
|
102
|
-
inspection.diagnostics.push(missingDirectoryDiagnostic(kind === 'missing' ? code : code.replace('_missing', '_not_directory'), kind === 'missing' ? message : `${relativePath}/ exists but is not a directory.`, target));
|
|
101
|
+
await inspectOptionalPlanningDirectory(inspection, storeRoot, 'specs', OPENSPEC_SPECS_DIR, 'openspec_specs_not_directory', 'openspec.specs');
|
|
102
|
+
const changesKind = await inspectOptionalPlanningDirectory(inspection, storeRoot, 'changes', OPENSPEC_CHANGES_DIR, 'openspec_changes_not_directory', 'openspec.changes');
|
|
103
|
+
if (changesKind === 'directory') {
|
|
104
|
+
await inspectOptionalPlanningDirectory(inspection, storeRoot, 'archive', OPENSPEC_ARCHIVE_DIR, 'openspec_archive_not_directory', 'openspec.archive');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
inspection.archive = { present: false };
|
|
103
108
|
}
|
|
104
109
|
inspection.healthy =
|
|
105
110
|
inspection.present === true &&
|
|
106
111
|
inspection.config.present === true &&
|
|
107
|
-
inspection.
|
|
108
|
-
inspection.changes.present === true &&
|
|
109
|
-
inspection.archive.present === true;
|
|
112
|
+
inspection.diagnostics.length === 0;
|
|
110
113
|
return inspection;
|
|
111
114
|
}
|
|
112
115
|
async function ensureDirectory(storeRoot, relativePath, ledger) {
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
import { MarkdownParser } from './markdown-parser.js';
|
|
2
|
-
import { Change } from '../schemas/index.js';
|
|
1
|
+
import { MarkdownParser, Section } from './markdown-parser.js';
|
|
2
|
+
import { Change, Requirement } from '../schemas/index.js';
|
|
3
3
|
export declare class ChangeParser extends MarkdownParser {
|
|
4
4
|
private changeDir;
|
|
5
5
|
constructor(content: string, changeDir: string);
|
|
6
6
|
parseChangeWithDeltas(name: string): Promise<Change>;
|
|
7
7
|
private parseDeltaSpecs;
|
|
8
|
+
/**
|
|
9
|
+
* Read requirements from a delta section, ignoring headers that are not
|
|
10
|
+
* `### Requirement: <name>`.
|
|
11
|
+
*
|
|
12
|
+
* A delta section often carries divider headers such as
|
|
13
|
+
* `### Documentation Requirements`. The base parser treats every child header
|
|
14
|
+
* as a requirement, which invented a scenario-less requirement that does not
|
|
15
|
+
* exist (#498): archive warned about a missing scenario, and `show --json`
|
|
16
|
+
* reported an extra delta. The delta reader already skips these headers and
|
|
17
|
+
* notes them, so this keeps the two readers in agreement.
|
|
18
|
+
*
|
|
19
|
+
* Overriding here rather than in MarkdownParser keeps main spec parsing —
|
|
20
|
+
* `view`, `list`, `spec --json`, spec validation — untouched.
|
|
21
|
+
*/
|
|
22
|
+
protected parseRequirements(section: Section): Requirement[];
|
|
8
23
|
private parseSpecDeltas;
|
|
9
24
|
private parseRenames;
|
|
10
25
|
private parseSectionsFromContent;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { MarkdownParser } from './markdown-parser.js';
|
|
2
|
+
import { buildCodeFenceMask } from './requirement-text.js';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { promises as fs } from 'fs';
|
|
5
|
+
import { discoverSpecFiles } from '../../utils/spec-discovery.js';
|
|
4
6
|
export class ChangeParser extends MarkdownParser {
|
|
5
7
|
changeDir;
|
|
6
8
|
constructor(content, changeDir) {
|
|
@@ -37,30 +39,42 @@ export class ChangeParser extends MarkdownParser {
|
|
|
37
39
|
}
|
|
38
40
|
async parseDeltaSpecs(specsDir) {
|
|
39
41
|
const deltas = [];
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
catch (error) {
|
|
53
|
-
// Spec file might not exist, which is okay
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
42
|
+
// Discover delta specs recursively so nested layouts like
|
|
43
|
+
// specs/<area>/<capability>/spec.md are parsed too (#1353)
|
|
44
|
+
const specFiles = await discoverSpecFiles(specsDir);
|
|
45
|
+
for (const { id, specFile } of specFiles) {
|
|
46
|
+
try {
|
|
47
|
+
const content = await fs.readFile(specFile, 'utf-8');
|
|
48
|
+
const specDeltas = this.parseSpecDeltas(id, content);
|
|
49
|
+
deltas.push(...specDeltas);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// Spec file might not be readable, which is okay
|
|
53
|
+
continue;
|
|
56
54
|
}
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
// Specs directory might not exist, which is okay
|
|
60
|
-
return [];
|
|
61
55
|
}
|
|
62
56
|
return deltas;
|
|
63
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Read requirements from a delta section, ignoring headers that are not
|
|
60
|
+
* `### Requirement: <name>`.
|
|
61
|
+
*
|
|
62
|
+
* A delta section often carries divider headers such as
|
|
63
|
+
* `### Documentation Requirements`. The base parser treats every child header
|
|
64
|
+
* as a requirement, which invented a scenario-less requirement that does not
|
|
65
|
+
* exist (#498): archive warned about a missing scenario, and `show --json`
|
|
66
|
+
* reported an extra delta. The delta reader already skips these headers and
|
|
67
|
+
* notes them, so this keeps the two readers in agreement.
|
|
68
|
+
*
|
|
69
|
+
* Overriding here rather than in MarkdownParser keeps main spec parsing —
|
|
70
|
+
* `view`, `list`, `spec --json`, spec validation — untouched.
|
|
71
|
+
*/
|
|
72
|
+
parseRequirements(section) {
|
|
73
|
+
return super.parseRequirements({
|
|
74
|
+
...section,
|
|
75
|
+
children: section.children.filter((child) => /^Requirement:\s*\S/i.test(child.title.trim())),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
64
78
|
parseSpecDeltas(specName, content) {
|
|
65
79
|
const deltas = [];
|
|
66
80
|
const sections = this.parseSectionsFromContent(content);
|
|
@@ -148,7 +162,7 @@ export class ChangeParser extends MarkdownParser {
|
|
|
148
162
|
parseSectionsFromContent(content) {
|
|
149
163
|
const normalizedContent = ChangeParser.normalizeContent(content);
|
|
150
164
|
const lines = normalizedContent.split('\n');
|
|
151
|
-
const codeFenceLineMask =
|
|
165
|
+
const codeFenceLineMask = buildCodeFenceMask(lines);
|
|
152
166
|
const sections = [];
|
|
153
167
|
const stack = [];
|
|
154
168
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fenced-code-block detection for the Markdown parsers.
|
|
3
|
+
*
|
|
4
|
+
* Several parsers need to ignore Markdown structure (headers, requirement
|
|
5
|
+
* blocks, scenarios, delta sections) that appears inside fenced code blocks.
|
|
6
|
+
* Keeping this logic in one place avoids the drift that previously left
|
|
7
|
+
* `requirement-blocks.ts` treating fenced `### Requirement:` lines as real
|
|
8
|
+
* requirements during validation and archiving.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Builds a per-line mask where `true` marks a line that is part of a fenced
|
|
12
|
+
* code block (including the opening and closing fence lines themselves).
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildCodeFenceMask(lines: string[]): boolean[];
|
|
15
|
+
//# sourceMappingURL=code-fence.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fenced-code-block detection for the Markdown parsers.
|
|
3
|
+
*
|
|
4
|
+
* Several parsers need to ignore Markdown structure (headers, requirement
|
|
5
|
+
* blocks, scenarios, delta sections) that appears inside fenced code blocks.
|
|
6
|
+
* Keeping this logic in one place avoids the drift that previously left
|
|
7
|
+
* `requirement-blocks.ts` treating fenced `### Requirement:` lines as real
|
|
8
|
+
* requirements during validation and archiving.
|
|
9
|
+
*/
|
|
10
|
+
function getFenceMarker(line) {
|
|
11
|
+
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
|
|
12
|
+
if (!fenceMatch) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
marker: fenceMatch[1][0],
|
|
17
|
+
length: fenceMatch[1].length,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function isClosingFence(line, activeFence) {
|
|
21
|
+
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})\s*$/);
|
|
22
|
+
return Boolean(fenceMatch &&
|
|
23
|
+
fenceMatch[1][0] === activeFence.marker &&
|
|
24
|
+
fenceMatch[1].length >= activeFence.length);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Builds a per-line mask where `true` marks a line that is part of a fenced
|
|
28
|
+
* code block (including the opening and closing fence lines themselves).
|
|
29
|
+
*/
|
|
30
|
+
export function buildCodeFenceMask(lines) {
|
|
31
|
+
const mask = new Array(lines.length).fill(false);
|
|
32
|
+
let activeFence = null;
|
|
33
|
+
for (let i = 0; i < lines.length; i++) {
|
|
34
|
+
if (!activeFence) {
|
|
35
|
+
const fence = getFenceMarker(lines[i]);
|
|
36
|
+
if (fence) {
|
|
37
|
+
activeFence = fence;
|
|
38
|
+
mask[i] = true;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
mask[i] = true;
|
|
43
|
+
if (isClosingFence(lines[i], activeFence)) {
|
|
44
|
+
activeFence = null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return mask;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=code-fence.js.map
|
|
@@ -11,9 +11,6 @@ export declare class MarkdownParser {
|
|
|
11
11
|
private currentLine;
|
|
12
12
|
constructor(content: string);
|
|
13
13
|
protected static normalizeContent(content: string): string;
|
|
14
|
-
protected static buildCodeFenceMask(lines: string[]): boolean[];
|
|
15
|
-
private static getFenceMarker;
|
|
16
|
-
private static isClosingFence;
|
|
17
14
|
parseSpec(name: string): Spec;
|
|
18
15
|
parseChange(name: string): Change;
|
|
19
16
|
protected parseSections(): Section[];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildCodeFenceMask, extractRequirementText } from './requirement-text.js';
|
|
1
2
|
export class MarkdownParser {
|
|
2
3
|
lines;
|
|
3
4
|
codeFenceLineMask;
|
|
@@ -5,46 +6,12 @@ export class MarkdownParser {
|
|
|
5
6
|
constructor(content) {
|
|
6
7
|
const normalized = MarkdownParser.normalizeContent(content);
|
|
7
8
|
this.lines = normalized.split('\n');
|
|
8
|
-
this.codeFenceLineMask =
|
|
9
|
+
this.codeFenceLineMask = buildCodeFenceMask(this.lines);
|
|
9
10
|
this.currentLine = 0;
|
|
10
11
|
}
|
|
11
12
|
static normalizeContent(content) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
static buildCodeFenceMask(lines) {
|
|
15
|
-
const mask = new Array(lines.length).fill(false);
|
|
16
|
-
let activeFence = null;
|
|
17
|
-
for (let i = 0; i < lines.length; i++) {
|
|
18
|
-
const fence = MarkdownParser.getFenceMarker(lines[i]);
|
|
19
|
-
if (!activeFence) {
|
|
20
|
-
if (fence) {
|
|
21
|
-
activeFence = fence;
|
|
22
|
-
mask[i] = true;
|
|
23
|
-
}
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
mask[i] = true;
|
|
27
|
-
if (MarkdownParser.isClosingFence(lines[i], activeFence)) {
|
|
28
|
-
activeFence = null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return mask;
|
|
32
|
-
}
|
|
33
|
-
static getFenceMarker(line) {
|
|
34
|
-
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
|
|
35
|
-
if (!fenceMatch) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
marker: fenceMatch[1][0],
|
|
40
|
-
length: fenceMatch[1].length,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
static isClosingFence(line, activeFence) {
|
|
44
|
-
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})\s*$/);
|
|
45
|
-
return Boolean(fenceMatch &&
|
|
46
|
-
fenceMatch[1][0] === activeFence.marker &&
|
|
47
|
-
fenceMatch[1].length >= activeFence.length);
|
|
13
|
+
// Strip a UTF-8 BOM so a header on the first line still matches.
|
|
14
|
+
return content.replace(/^/, '').replace(/\r\n?/g, '\n');
|
|
48
15
|
}
|
|
49
16
|
parseSpec(name) {
|
|
50
17
|
const sections = this.parseSections();
|
|
@@ -149,29 +116,9 @@ export class MarkdownParser {
|
|
|
149
116
|
parseRequirements(section) {
|
|
150
117
|
const requirements = [];
|
|
151
118
|
for (const child of section.children) {
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (child.content.trim()) {
|
|
156
|
-
// Split content into lines and find content before any child headers
|
|
157
|
-
const lines = child.content.split('\n');
|
|
158
|
-
const contentBeforeChildren = [];
|
|
159
|
-
for (const line of lines) {
|
|
160
|
-
// Stop at child headers (scenarios start with ####)
|
|
161
|
-
if (line.trim().startsWith('#')) {
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
contentBeforeChildren.push(line);
|
|
165
|
-
}
|
|
166
|
-
// Find first non-empty line
|
|
167
|
-
const directContent = contentBeforeChildren.join('\n').trim();
|
|
168
|
-
if (directContent) {
|
|
169
|
-
const firstLine = directContent.split('\n').find(l => l.trim());
|
|
170
|
-
if (firstLine) {
|
|
171
|
-
text = firstLine.trim();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
119
|
+
// Read the requirement text via the shared reader (multi-line, fence- and
|
|
120
|
+
// metadata-aware, with the shared header-title fallback for empty bodies).
|
|
121
|
+
const text = extractRequirementText(child.title, child.content.split('\n'));
|
|
175
122
|
const scenarios = this.parseScenarios(child);
|
|
176
123
|
requirements.push({
|
|
177
124
|
text,
|
|
@@ -11,10 +11,28 @@ export interface RequirementsSectionParts {
|
|
|
11
11
|
after: string;
|
|
12
12
|
}
|
|
13
13
|
export declare function normalizeRequirementName(name: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Case- and whitespace-insensitive fold of a requirement name. Requirement
|
|
16
|
+
* matching itself is case-sensitive (normalizeRequirementName); this fold
|
|
17
|
+
* exists only for typo detection - near-miss REMOVED headers and the
|
|
18
|
+
* RENAMED+REMOVED cross-section conflict - where two spellings that differ
|
|
19
|
+
* only in case or interior whitespace mean a mistake, never two requirements.
|
|
20
|
+
*/
|
|
21
|
+
export declare function foldRequirementName(name: string): string;
|
|
14
22
|
/**
|
|
15
23
|
* Extracts the Requirements section from a spec file and parses requirement blocks.
|
|
16
24
|
*/
|
|
17
25
|
export declare function extractRequirementsSection(content: string): RequirementsSectionParts;
|
|
26
|
+
/**
|
|
27
|
+
* A level-3 header inside `## ADDED`/`## MODIFIED Requirements` that is not a
|
|
28
|
+
* canonical `### Requirement:` header, recorded at the moment the delta reader
|
|
29
|
+
* skips over it. Surfaced as an INFO note by `validate <change>` (#498).
|
|
30
|
+
*/
|
|
31
|
+
export interface SkippedHeader {
|
|
32
|
+
header: string;
|
|
33
|
+
section: string;
|
|
34
|
+
line: number;
|
|
35
|
+
}
|
|
18
36
|
export interface DeltaPlan {
|
|
19
37
|
added: RequirementBlock[];
|
|
20
38
|
modified: RequirementBlock[];
|
|
@@ -23,6 +41,7 @@ export interface DeltaPlan {
|
|
|
23
41
|
from: string;
|
|
24
42
|
to: string;
|
|
25
43
|
}>;
|
|
44
|
+
skippedHeaders: SkippedHeader[];
|
|
26
45
|
sectionPresence: {
|
|
27
46
|
added: boolean;
|
|
28
47
|
modified: boolean;
|
|
@@ -34,4 +53,14 @@ export interface DeltaPlan {
|
|
|
34
53
|
* Parse a delta-formatted spec change file content into a DeltaPlan with raw blocks.
|
|
35
54
|
*/
|
|
36
55
|
export declare function parseDeltaSpec(content: string): DeltaPlan;
|
|
56
|
+
/**
|
|
57
|
+
* Scenario names the current requirement block has and the incoming
|
|
58
|
+
* (MODIFIED) block does not. A MODIFIED requirement replaces the whole block,
|
|
59
|
+
* so every name reported here would be dropped from the main spec.
|
|
60
|
+
*
|
|
61
|
+
* Shared by archive (which refuses to apply the block) and validate (which
|
|
62
|
+
* reports the same loss at authoring time, #1477), so the two cannot disagree
|
|
63
|
+
* about what counts as a dropped scenario.
|
|
64
|
+
*/
|
|
65
|
+
export declare function findMissingCurrentScenarios(current: RequirementBlock, incoming: RequirementBlock): string[];
|
|
37
66
|
//# sourceMappingURL=requirement-blocks.d.ts.map
|