@fission-ai/openspec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +119 -0
  2. package/bin/openspec.js +3 -0
  3. package/dist/cli/index.d.ts +2 -0
  4. package/dist/cli/index.js +240 -0
  5. package/dist/commands/change.d.ts +35 -0
  6. package/dist/commands/change.js +276 -0
  7. package/dist/commands/show.d.ts +14 -0
  8. package/dist/commands/show.js +131 -0
  9. package/dist/commands/spec.d.ts +15 -0
  10. package/dist/commands/spec.js +224 -0
  11. package/dist/commands/validate.d.ts +23 -0
  12. package/dist/commands/validate.js +275 -0
  13. package/dist/core/archive.d.ts +15 -0
  14. package/dist/core/archive.js +529 -0
  15. package/dist/core/config.d.ts +14 -0
  16. package/dist/core/config.js +12 -0
  17. package/dist/core/configurators/base.d.ts +7 -0
  18. package/dist/core/configurators/base.js +2 -0
  19. package/dist/core/configurators/claude.d.ts +8 -0
  20. package/dist/core/configurators/claude.js +15 -0
  21. package/dist/core/configurators/registry.d.ts +9 -0
  22. package/dist/core/configurators/registry.js +22 -0
  23. package/dist/core/converters/json-converter.d.ts +6 -0
  24. package/dist/core/converters/json-converter.js +48 -0
  25. package/dist/core/diff.d.ts +11 -0
  26. package/dist/core/diff.js +193 -0
  27. package/dist/core/index.d.ts +2 -0
  28. package/dist/core/index.js +2 -0
  29. package/dist/core/init.d.ts +10 -0
  30. package/dist/core/init.js +109 -0
  31. package/dist/core/list.d.ts +4 -0
  32. package/dist/core/list.js +89 -0
  33. package/dist/core/parsers/change-parser.d.ts +13 -0
  34. package/dist/core/parsers/change-parser.js +192 -0
  35. package/dist/core/parsers/markdown-parser.d.ts +21 -0
  36. package/dist/core/parsers/markdown-parser.js +183 -0
  37. package/dist/core/parsers/requirement-blocks.d.ts +31 -0
  38. package/dist/core/parsers/requirement-blocks.js +173 -0
  39. package/dist/core/schemas/base.schema.d.ts +13 -0
  40. package/dist/core/schemas/base.schema.js +13 -0
  41. package/dist/core/schemas/change.schema.d.ts +73 -0
  42. package/dist/core/schemas/change.schema.js +31 -0
  43. package/dist/core/schemas/index.d.ts +4 -0
  44. package/dist/core/schemas/index.js +4 -0
  45. package/dist/core/schemas/spec.schema.d.ts +18 -0
  46. package/dist/core/schemas/spec.schema.js +15 -0
  47. package/dist/core/templates/claude-template.d.ts +2 -0
  48. package/dist/core/templates/claude-template.js +96 -0
  49. package/dist/core/templates/index.d.ts +11 -0
  50. package/dist/core/templates/index.js +21 -0
  51. package/dist/core/templates/project-template.d.ts +8 -0
  52. package/dist/core/templates/project-template.js +32 -0
  53. package/dist/core/templates/readme-template.d.ts +2 -0
  54. package/dist/core/templates/readme-template.js +519 -0
  55. package/dist/core/update.d.ts +4 -0
  56. package/dist/core/update.js +47 -0
  57. package/dist/core/validation/constants.d.ts +34 -0
  58. package/dist/core/validation/constants.js +40 -0
  59. package/dist/core/validation/types.d.ts +18 -0
  60. package/dist/core/validation/types.js +2 -0
  61. package/dist/core/validation/validator.d.ts +32 -0
  62. package/dist/core/validation/validator.js +355 -0
  63. package/dist/index.d.ts +3 -0
  64. package/dist/index.js +3 -0
  65. package/dist/utils/file-system.d.ts +10 -0
  66. package/dist/utils/file-system.js +83 -0
  67. package/dist/utils/index.d.ts +2 -0
  68. package/dist/utils/index.js +2 -0
  69. package/dist/utils/interactive.d.ts +2 -0
  70. package/dist/utils/interactive.js +8 -0
  71. package/dist/utils/item-discovery.d.ts +3 -0
  72. package/dist/utils/item-discovery.js +49 -0
  73. package/dist/utils/match.d.ts +3 -0
  74. package/dist/utils/match.js +22 -0
  75. package/dist/utils/task-progress.d.ts +8 -0
  76. package/dist/utils/task-progress.js +36 -0
  77. package/package.json +68 -0
@@ -0,0 +1,21 @@
1
+ import { Spec, Change, Requirement, Scenario, Delta } from '../schemas/index.js';
2
+ export interface Section {
3
+ level: number;
4
+ title: string;
5
+ content: string;
6
+ children: Section[];
7
+ }
8
+ export declare class MarkdownParser {
9
+ private lines;
10
+ private currentLine;
11
+ constructor(content: string);
12
+ parseSpec(name: string): Spec;
13
+ parseChange(name: string): Change;
14
+ protected parseSections(): Section[];
15
+ protected getContentUntilNextHeader(startLine: number, currentLevel: number): string;
16
+ protected findSection(sections: Section[], title: string): Section | undefined;
17
+ protected parseRequirements(section: Section): Requirement[];
18
+ protected parseScenarios(requirementSection: Section): Scenario[];
19
+ protected parseDeltas(content: string): Delta[];
20
+ }
21
+ //# sourceMappingURL=markdown-parser.d.ts.map
@@ -0,0 +1,183 @@
1
+ export class MarkdownParser {
2
+ lines;
3
+ currentLine;
4
+ constructor(content) {
5
+ this.lines = content.split('\n');
6
+ this.currentLine = 0;
7
+ }
8
+ parseSpec(name) {
9
+ const sections = this.parseSections();
10
+ const purpose = this.findSection(sections, 'Purpose')?.content || '';
11
+ const requirementsSection = this.findSection(sections, 'Requirements');
12
+ if (!purpose) {
13
+ throw new Error('Spec must have a Purpose section');
14
+ }
15
+ if (!requirementsSection) {
16
+ throw new Error('Spec must have a Requirements section');
17
+ }
18
+ const requirements = this.parseRequirements(requirementsSection);
19
+ return {
20
+ name,
21
+ overview: purpose.trim(),
22
+ requirements,
23
+ metadata: {
24
+ version: '1.0.0',
25
+ format: 'openspec',
26
+ },
27
+ };
28
+ }
29
+ parseChange(name) {
30
+ const sections = this.parseSections();
31
+ const why = this.findSection(sections, 'Why')?.content || '';
32
+ const whatChanges = this.findSection(sections, 'What Changes')?.content || '';
33
+ if (!why) {
34
+ throw new Error('Change must have a Why section');
35
+ }
36
+ if (!whatChanges) {
37
+ throw new Error('Change must have a What Changes section');
38
+ }
39
+ const deltas = this.parseDeltas(whatChanges);
40
+ return {
41
+ name,
42
+ why: why.trim(),
43
+ whatChanges: whatChanges.trim(),
44
+ deltas,
45
+ metadata: {
46
+ version: '1.0.0',
47
+ format: 'openspec-change',
48
+ },
49
+ };
50
+ }
51
+ parseSections() {
52
+ const sections = [];
53
+ const stack = [];
54
+ for (let i = 0; i < this.lines.length; i++) {
55
+ const line = this.lines[i];
56
+ const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
57
+ if (headerMatch) {
58
+ const level = headerMatch[1].length;
59
+ const title = headerMatch[2].trim();
60
+ const content = this.getContentUntilNextHeader(i + 1, level);
61
+ const section = {
62
+ level,
63
+ title,
64
+ content,
65
+ children: [],
66
+ };
67
+ while (stack.length > 0 && stack[stack.length - 1].level >= level) {
68
+ stack.pop();
69
+ }
70
+ if (stack.length === 0) {
71
+ sections.push(section);
72
+ }
73
+ else {
74
+ stack[stack.length - 1].children.push(section);
75
+ }
76
+ stack.push(section);
77
+ }
78
+ }
79
+ return sections;
80
+ }
81
+ getContentUntilNextHeader(startLine, currentLevel) {
82
+ const contentLines = [];
83
+ for (let i = startLine; i < this.lines.length; i++) {
84
+ const line = this.lines[i];
85
+ const headerMatch = line.match(/^(#{1,6})\s+/);
86
+ if (headerMatch && headerMatch[1].length <= currentLevel) {
87
+ break;
88
+ }
89
+ contentLines.push(line);
90
+ }
91
+ return contentLines.join('\n').trim();
92
+ }
93
+ findSection(sections, title) {
94
+ for (const section of sections) {
95
+ if (section.title.toLowerCase() === title.toLowerCase()) {
96
+ return section;
97
+ }
98
+ const child = this.findSection(section.children, title);
99
+ if (child) {
100
+ return child;
101
+ }
102
+ }
103
+ return undefined;
104
+ }
105
+ parseRequirements(section) {
106
+ const requirements = [];
107
+ for (const child of section.children) {
108
+ // Extract requirement text from first non-empty content line, fall back to heading
109
+ let text = child.title;
110
+ // Get content before any child sections (scenarios)
111
+ if (child.content.trim()) {
112
+ // Split content into lines and find content before any child headers
113
+ const lines = child.content.split('\n');
114
+ const contentBeforeChildren = [];
115
+ for (const line of lines) {
116
+ // Stop at child headers (scenarios start with ####)
117
+ if (line.trim().startsWith('#')) {
118
+ break;
119
+ }
120
+ contentBeforeChildren.push(line);
121
+ }
122
+ // Find first non-empty line
123
+ const directContent = contentBeforeChildren.join('\n').trim();
124
+ if (directContent) {
125
+ const firstLine = directContent.split('\n').find(l => l.trim());
126
+ if (firstLine) {
127
+ text = firstLine.trim();
128
+ }
129
+ }
130
+ }
131
+ const scenarios = this.parseScenarios(child);
132
+ requirements.push({
133
+ text,
134
+ scenarios,
135
+ });
136
+ }
137
+ return requirements;
138
+ }
139
+ parseScenarios(requirementSection) {
140
+ const scenarios = [];
141
+ for (const scenarioSection of requirementSection.children) {
142
+ // Store the raw text content of the scenario section
143
+ if (scenarioSection.content.trim()) {
144
+ scenarios.push({
145
+ rawText: scenarioSection.content
146
+ });
147
+ }
148
+ }
149
+ return scenarios;
150
+ }
151
+ parseDeltas(content) {
152
+ const deltas = [];
153
+ const lines = content.split('\n');
154
+ for (const line of lines) {
155
+ // Match both formats: **spec:** and **spec**:
156
+ const deltaMatch = line.match(/^\s*-\s*\*\*([^*:]+)(?::\*\*|\*\*:)\s*(.+)$/);
157
+ if (deltaMatch) {
158
+ const specName = deltaMatch[1].trim();
159
+ const description = deltaMatch[2].trim();
160
+ let operation = 'MODIFIED';
161
+ const lowerDesc = description.toLowerCase();
162
+ // Use word boundaries to avoid false matches (e.g., "address" matching "add")
163
+ // Check RENAMED first since it's more specific than patterns containing "new"
164
+ if (/\brename(s|d|ing)?\b/.test(lowerDesc) || /\brenamed\s+(to|from)\b/.test(lowerDesc)) {
165
+ operation = 'RENAMED';
166
+ }
167
+ else if (/\badd(s|ed|ing)?\b/.test(lowerDesc) || /\bcreate(s|d|ing)?\b/.test(lowerDesc) || /\bnew\b/.test(lowerDesc)) {
168
+ operation = 'ADDED';
169
+ }
170
+ else if (/\bremove(s|d|ing)?\b/.test(lowerDesc) || /\bdelete(s|d|ing)?\b/.test(lowerDesc)) {
171
+ operation = 'REMOVED';
172
+ }
173
+ deltas.push({
174
+ spec: specName,
175
+ operation,
176
+ description,
177
+ });
178
+ }
179
+ }
180
+ return deltas;
181
+ }
182
+ }
183
+ //# sourceMappingURL=markdown-parser.js.map
@@ -0,0 +1,31 @@
1
+ export interface RequirementBlock {
2
+ headerLine: string;
3
+ name: string;
4
+ raw: string;
5
+ }
6
+ export interface RequirementsSectionParts {
7
+ before: string;
8
+ headerLine: string;
9
+ preamble: string;
10
+ bodyBlocks: RequirementBlock[];
11
+ after: string;
12
+ }
13
+ export declare function normalizeRequirementName(name: string): string;
14
+ /**
15
+ * Extracts the Requirements section from a spec file and parses requirement blocks.
16
+ */
17
+ export declare function extractRequirementsSection(content: string): RequirementsSectionParts;
18
+ export interface DeltaPlan {
19
+ added: RequirementBlock[];
20
+ modified: RequirementBlock[];
21
+ removed: string[];
22
+ renamed: Array<{
23
+ from: string;
24
+ to: string;
25
+ }>;
26
+ }
27
+ /**
28
+ * Parse a delta-formatted spec change file content into a DeltaPlan with raw blocks.
29
+ */
30
+ export declare function parseDeltaSpec(content: string): DeltaPlan;
31
+ //# sourceMappingURL=requirement-blocks.d.ts.map
@@ -0,0 +1,173 @@
1
+ export function normalizeRequirementName(name) {
2
+ return name.trim();
3
+ }
4
+ const REQUIREMENT_HEADER_REGEX = /^###\s*Requirement:\s*(.+)\s*$/;
5
+ /**
6
+ * Extracts the Requirements section from a spec file and parses requirement blocks.
7
+ */
8
+ export function extractRequirementsSection(content) {
9
+ const lines = content.split('\n');
10
+ const reqHeaderIndex = lines.findIndex(l => /^##\s+Requirements\s*$/i.test(l));
11
+ if (reqHeaderIndex === -1) {
12
+ // No requirements section; create an empty one at the end
13
+ const before = content.trimEnd();
14
+ const headerLine = '## Requirements';
15
+ return {
16
+ before: before ? before + '\n\n' : '',
17
+ headerLine,
18
+ preamble: '',
19
+ bodyBlocks: [],
20
+ after: '\n',
21
+ };
22
+ }
23
+ // Find end of this section: next line that starts with '## ' at same or higher level
24
+ let endIndex = lines.length;
25
+ for (let i = reqHeaderIndex + 1; i < lines.length; i++) {
26
+ if (/^##\s+/.test(lines[i])) {
27
+ endIndex = i;
28
+ break;
29
+ }
30
+ }
31
+ const before = lines.slice(0, reqHeaderIndex).join('\n');
32
+ const headerLine = lines[reqHeaderIndex];
33
+ const sectionBodyLines = lines.slice(reqHeaderIndex + 1, endIndex);
34
+ // Parse requirement blocks within section body
35
+ const blocks = [];
36
+ let cursor = 0;
37
+ let preambleLines = [];
38
+ // Collect preamble lines until first requirement header
39
+ while (cursor < sectionBodyLines.length && !/^###\s+Requirement:/.test(sectionBodyLines[cursor])) {
40
+ preambleLines.push(sectionBodyLines[cursor]);
41
+ cursor++;
42
+ }
43
+ while (cursor < sectionBodyLines.length) {
44
+ const headerStart = cursor;
45
+ const headerLineCandidate = sectionBodyLines[cursor];
46
+ const headerMatch = headerLineCandidate.match(REQUIREMENT_HEADER_REGEX);
47
+ if (!headerMatch) {
48
+ // Not a requirement header; skip line defensively
49
+ cursor++;
50
+ continue;
51
+ }
52
+ const name = normalizeRequirementName(headerMatch[1]);
53
+ cursor++;
54
+ // Gather lines until next requirement header or end of section
55
+ const bodyLines = [headerLineCandidate];
56
+ while (cursor < sectionBodyLines.length && !/^###\s+Requirement:/.test(sectionBodyLines[cursor]) && !/^##\s+/.test(sectionBodyLines[cursor])) {
57
+ bodyLines.push(sectionBodyLines[cursor]);
58
+ cursor++;
59
+ }
60
+ const raw = bodyLines.join('\n').trimEnd();
61
+ blocks.push({ headerLine: headerLineCandidate, name, raw });
62
+ }
63
+ const after = lines.slice(endIndex).join('\n');
64
+ const preamble = preambleLines.join('\n').trimEnd();
65
+ return {
66
+ before: before.trimEnd() ? before + '\n' : before,
67
+ headerLine,
68
+ preamble,
69
+ bodyBlocks: blocks,
70
+ after: after.startsWith('\n') ? after : '\n' + after,
71
+ };
72
+ }
73
+ /**
74
+ * Parse a delta-formatted spec change file content into a DeltaPlan with raw blocks.
75
+ */
76
+ export function parseDeltaSpec(content) {
77
+ const sections = splitTopLevelSections(content);
78
+ const added = parseRequirementBlocksFromSection(sections['ADDED Requirements'] || '');
79
+ const modified = parseRequirementBlocksFromSection(sections['MODIFIED Requirements'] || '');
80
+ const removedNames = parseRemovedNames(sections['REMOVED Requirements'] || '');
81
+ const renamedPairs = parseRenamedPairs(sections['RENAMED Requirements'] || '');
82
+ return { added, modified, removed: removedNames, renamed: renamedPairs };
83
+ }
84
+ function splitTopLevelSections(content) {
85
+ const lines = content.split('\n');
86
+ const result = {};
87
+ const indices = [];
88
+ for (let i = 0; i < lines.length; i++) {
89
+ const m = lines[i].match(/^(##)\s+(.+)$/);
90
+ if (m) {
91
+ const level = m[1].length; // only care for '##'
92
+ indices.push({ title: m[2].trim(), index: i, level });
93
+ }
94
+ }
95
+ for (let i = 0; i < indices.length; i++) {
96
+ const current = indices[i];
97
+ const next = indices[i + 1];
98
+ const body = lines.slice(current.index + 1, next ? next.index : lines.length).join('\n');
99
+ result[current.title] = body;
100
+ }
101
+ return result;
102
+ }
103
+ function parseRequirementBlocksFromSection(sectionBody) {
104
+ if (!sectionBody)
105
+ return [];
106
+ const lines = sectionBody.split('\n');
107
+ const blocks = [];
108
+ let i = 0;
109
+ while (i < lines.length) {
110
+ // Seek next requirement header
111
+ while (i < lines.length && !/^###\s+Requirement:/.test(lines[i]))
112
+ i++;
113
+ if (i >= lines.length)
114
+ break;
115
+ const headerLine = lines[i];
116
+ const m = headerLine.match(REQUIREMENT_HEADER_REGEX);
117
+ if (!m) {
118
+ i++;
119
+ continue;
120
+ }
121
+ const name = normalizeRequirementName(m[1]);
122
+ const buf = [headerLine];
123
+ i++;
124
+ while (i < lines.length && !/^###\s+Requirement:/.test(lines[i]) && !/^##\s+/.test(lines[i])) {
125
+ buf.push(lines[i]);
126
+ i++;
127
+ }
128
+ blocks.push({ headerLine, name, raw: buf.join('\n').trimEnd() });
129
+ }
130
+ return blocks;
131
+ }
132
+ function parseRemovedNames(sectionBody) {
133
+ if (!sectionBody)
134
+ return [];
135
+ const names = [];
136
+ const lines = sectionBody.split('\n');
137
+ for (const line of lines) {
138
+ const m = line.match(REQUIREMENT_HEADER_REGEX);
139
+ if (m) {
140
+ names.push(normalizeRequirementName(m[1]));
141
+ continue;
142
+ }
143
+ // Also support bullet list of headers
144
+ const bullet = line.match(/^\s*-\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
145
+ if (bullet) {
146
+ names.push(normalizeRequirementName(bullet[1]));
147
+ }
148
+ }
149
+ return names;
150
+ }
151
+ function parseRenamedPairs(sectionBody) {
152
+ if (!sectionBody)
153
+ return [];
154
+ const pairs = [];
155
+ const lines = sectionBody.split('\n');
156
+ let current = {};
157
+ for (const line of lines) {
158
+ const fromMatch = line.match(/^\s*-?\s*FROM:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
159
+ const toMatch = line.match(/^\s*-?\s*TO:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
160
+ if (fromMatch) {
161
+ current.from = normalizeRequirementName(fromMatch[1]);
162
+ }
163
+ else if (toMatch) {
164
+ current.to = normalizeRequirementName(toMatch[1]);
165
+ if (current.from && current.to) {
166
+ pairs.push({ from: current.from, to: current.to });
167
+ current = {};
168
+ }
169
+ }
170
+ }
171
+ return pairs;
172
+ }
173
+ //# sourceMappingURL=requirement-blocks.js.map
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ export declare const ScenarioSchema: z.ZodObject<{
3
+ rawText: z.ZodString;
4
+ }, z.core.$strip>;
5
+ export declare const RequirementSchema: z.ZodObject<{
6
+ text: z.ZodString;
7
+ scenarios: z.ZodArray<z.ZodObject<{
8
+ rawText: z.ZodString;
9
+ }, z.core.$strip>>;
10
+ }, z.core.$strip>;
11
+ export type Scenario = z.infer<typeof ScenarioSchema>;
12
+ export type Requirement = z.infer<typeof RequirementSchema>;
13
+ //# sourceMappingURL=base.schema.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ import { VALIDATION_MESSAGES } from '../validation/constants.js';
3
+ export const ScenarioSchema = z.object({
4
+ rawText: z.string().min(1, VALIDATION_MESSAGES.SCENARIO_EMPTY),
5
+ });
6
+ export const RequirementSchema = z.object({
7
+ text: z.string()
8
+ .min(1, VALIDATION_MESSAGES.REQUIREMENT_EMPTY)
9
+ .refine((text) => text.includes('SHALL') || text.includes('MUST'), VALIDATION_MESSAGES.REQUIREMENT_NO_SHALL),
10
+ scenarios: z.array(ScenarioSchema)
11
+ .min(1, VALIDATION_MESSAGES.REQUIREMENT_NO_SCENARIOS),
12
+ });
13
+ //# sourceMappingURL=base.schema.js.map
@@ -0,0 +1,73 @@
1
+ import { z } from 'zod';
2
+ export declare const DeltaOperationType: z.ZodEnum<{
3
+ ADDED: "ADDED";
4
+ MODIFIED: "MODIFIED";
5
+ REMOVED: "REMOVED";
6
+ RENAMED: "RENAMED";
7
+ }>;
8
+ export declare const DeltaSchema: z.ZodObject<{
9
+ spec: z.ZodString;
10
+ operation: z.ZodEnum<{
11
+ ADDED: "ADDED";
12
+ MODIFIED: "MODIFIED";
13
+ REMOVED: "REMOVED";
14
+ RENAMED: "RENAMED";
15
+ }>;
16
+ description: z.ZodString;
17
+ requirement: z.ZodOptional<z.ZodObject<{
18
+ text: z.ZodString;
19
+ scenarios: z.ZodArray<z.ZodObject<{
20
+ rawText: z.ZodString;
21
+ }, z.core.$strip>>;
22
+ }, z.core.$strip>>;
23
+ requirements: z.ZodOptional<z.ZodArray<z.ZodObject<{
24
+ text: z.ZodString;
25
+ scenarios: z.ZodArray<z.ZodObject<{
26
+ rawText: z.ZodString;
27
+ }, z.core.$strip>>;
28
+ }, z.core.$strip>>>;
29
+ rename: z.ZodOptional<z.ZodObject<{
30
+ from: z.ZodString;
31
+ to: z.ZodString;
32
+ }, z.core.$strip>>;
33
+ }, z.core.$strip>;
34
+ export declare const ChangeSchema: z.ZodObject<{
35
+ name: z.ZodString;
36
+ why: z.ZodString;
37
+ whatChanges: z.ZodString;
38
+ deltas: z.ZodArray<z.ZodObject<{
39
+ spec: z.ZodString;
40
+ operation: z.ZodEnum<{
41
+ ADDED: "ADDED";
42
+ MODIFIED: "MODIFIED";
43
+ REMOVED: "REMOVED";
44
+ RENAMED: "RENAMED";
45
+ }>;
46
+ description: z.ZodString;
47
+ requirement: z.ZodOptional<z.ZodObject<{
48
+ text: z.ZodString;
49
+ scenarios: z.ZodArray<z.ZodObject<{
50
+ rawText: z.ZodString;
51
+ }, z.core.$strip>>;
52
+ }, z.core.$strip>>;
53
+ requirements: z.ZodOptional<z.ZodArray<z.ZodObject<{
54
+ text: z.ZodString;
55
+ scenarios: z.ZodArray<z.ZodObject<{
56
+ rawText: z.ZodString;
57
+ }, z.core.$strip>>;
58
+ }, z.core.$strip>>>;
59
+ rename: z.ZodOptional<z.ZodObject<{
60
+ from: z.ZodString;
61
+ to: z.ZodString;
62
+ }, z.core.$strip>>;
63
+ }, z.core.$strip>>;
64
+ metadata: z.ZodOptional<z.ZodObject<{
65
+ version: z.ZodDefault<z.ZodString>;
66
+ format: z.ZodLiteral<"openspec-change">;
67
+ sourcePath: z.ZodOptional<z.ZodString>;
68
+ }, z.core.$strip>>;
69
+ }, z.core.$strip>;
70
+ export type DeltaOperation = z.infer<typeof DeltaOperationType>;
71
+ export type Delta = z.infer<typeof DeltaSchema>;
72
+ export type Change = z.infer<typeof ChangeSchema>;
73
+ //# sourceMappingURL=change.schema.d.ts.map
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import { RequirementSchema } from './base.schema.js';
3
+ import { MIN_WHY_SECTION_LENGTH, MAX_WHY_SECTION_LENGTH, MAX_DELTAS_PER_CHANGE, VALIDATION_MESSAGES } from '../validation/constants.js';
4
+ export const DeltaOperationType = z.enum(['ADDED', 'MODIFIED', 'REMOVED', 'RENAMED']);
5
+ export const DeltaSchema = z.object({
6
+ spec: z.string().min(1, VALIDATION_MESSAGES.DELTA_SPEC_EMPTY),
7
+ operation: DeltaOperationType,
8
+ description: z.string().min(1, VALIDATION_MESSAGES.DELTA_DESCRIPTION_EMPTY),
9
+ requirement: RequirementSchema.optional(),
10
+ requirements: z.array(RequirementSchema).optional(),
11
+ rename: z.object({
12
+ from: z.string(),
13
+ to: z.string(),
14
+ }).optional(),
15
+ });
16
+ export const ChangeSchema = z.object({
17
+ name: z.string().min(1, VALIDATION_MESSAGES.CHANGE_NAME_EMPTY),
18
+ why: z.string()
19
+ .min(MIN_WHY_SECTION_LENGTH, VALIDATION_MESSAGES.CHANGE_WHY_TOO_SHORT)
20
+ .max(MAX_WHY_SECTION_LENGTH, VALIDATION_MESSAGES.CHANGE_WHY_TOO_LONG),
21
+ whatChanges: z.string().min(1, VALIDATION_MESSAGES.CHANGE_WHAT_EMPTY),
22
+ deltas: z.array(DeltaSchema)
23
+ .min(1, VALIDATION_MESSAGES.CHANGE_NO_DELTAS)
24
+ .max(MAX_DELTAS_PER_CHANGE, VALIDATION_MESSAGES.CHANGE_TOO_MANY_DELTAS),
25
+ metadata: z.object({
26
+ version: z.string().default('1.0.0'),
27
+ format: z.literal('openspec-change'),
28
+ sourcePath: z.string().optional(),
29
+ }).optional(),
30
+ });
31
+ //# sourceMappingURL=change.schema.js.map
@@ -0,0 +1,4 @@
1
+ export { ScenarioSchema, RequirementSchema, type Scenario, type Requirement, } from './base.schema.js';
2
+ export { SpecSchema, type Spec, } from './spec.schema.js';
3
+ export { DeltaOperationType, DeltaSchema, ChangeSchema, type DeltaOperation, type Delta, type Change, } from './change.schema.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ export { ScenarioSchema, RequirementSchema, } from './base.schema.js';
2
+ export { SpecSchema, } from './spec.schema.js';
3
+ export { DeltaOperationType, DeltaSchema, ChangeSchema, } from './change.schema.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ export declare const SpecSchema: z.ZodObject<{
3
+ name: z.ZodString;
4
+ overview: z.ZodString;
5
+ requirements: z.ZodArray<z.ZodObject<{
6
+ text: z.ZodString;
7
+ scenarios: z.ZodArray<z.ZodObject<{
8
+ rawText: z.ZodString;
9
+ }, z.core.$strip>>;
10
+ }, z.core.$strip>>;
11
+ metadata: z.ZodOptional<z.ZodObject<{
12
+ version: z.ZodDefault<z.ZodString>;
13
+ format: z.ZodLiteral<"openspec">;
14
+ sourcePath: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strip>>;
16
+ }, z.core.$strip>;
17
+ export type Spec = z.infer<typeof SpecSchema>;
18
+ //# sourceMappingURL=spec.schema.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+ import { RequirementSchema } from './base.schema.js';
3
+ import { VALIDATION_MESSAGES } from '../validation/constants.js';
4
+ export const SpecSchema = z.object({
5
+ name: z.string().min(1, VALIDATION_MESSAGES.SPEC_NAME_EMPTY),
6
+ overview: z.string().min(1, VALIDATION_MESSAGES.SPEC_PURPOSE_EMPTY),
7
+ requirements: z.array(RequirementSchema)
8
+ .min(1, VALIDATION_MESSAGES.SPEC_NO_REQUIREMENTS),
9
+ metadata: z.object({
10
+ version: z.string().default('1.0.0'),
11
+ format: z.literal('openspec'),
12
+ sourcePath: z.string().optional(),
13
+ }).optional(),
14
+ });
15
+ //# sourceMappingURL=spec.schema.js.map
@@ -0,0 +1,2 @@
1
+ export declare const claudeTemplate = "# OpenSpec Project\n\nThis project uses OpenSpec for spec-driven development. Specifications are the source of truth.\n\nSee @openspec/README.md for detailed conventions and guidelines.\n\n## Three-Stage Workflow\n\n### Stage 1: Creating Changes\nCreate proposal for: features, breaking changes, architecture changes\nSkip proposal for: bug fixes, typos, non-breaking updates\n\n### Stage 2: Implementing Changes\n1. Read proposal.md to understand the change\n2. Read design.md if it exists for technical context\n3. Read tasks.md for implementation checklist\n4. Complete tasks one by one\n5. Mark each task complete immediately: `- [x]`\n\n### Stage 3: Archiving\nAfter deployment, use `openspec archive [change]` (add `--skip-specs` for tooling-only changes)\n\n## Before Any Task\n\n**Always:**\n- Check existing specs: `openspec list --specs`\n- Check active changes: `openspec list`\n- Read relevant specs before creating new ones\n- Prefer modifying existing specs over creating duplicates\n\n## CLI Quick Reference\n\n```bash\n# Essential\nopenspec list # Active changes\nopenspec list --specs # Existing specifications\nopenspec show [item] # View details\nopenspec diff [change] # Show spec differences\nopenspec validate --strict # Validate thoroughly\nopenspec archive [change] # Archive after deployment\n\n# Interactive\nopenspec show # Prompts for selection\nopenspec validate # Bulk validation\n\n# Debugging\nopenspec show [change] --json --deltas-only\n```\n\n## Creating Changes\n\n1. **Directory:** `changes/[descriptive-name]/`\n2. **Files:**\n - `proposal.md` - Why, what, impact\n - `tasks.md` - Implementation checklist\n - `specs/[capability]/spec.md` - Delta changes (ADDED/MODIFIED/REMOVED)\n\n## Critical: Scenario Format\n\n**CORRECT:**\n```markdown\n#### Scenario: User login\n- **WHEN** valid credentials\n- **THEN** return token\n```\n\n**WRONG:** Using bullets (- **Scenario**), bold (**Scenario:**), or ### headers\n\nEvery requirement MUST have scenarios using `#### Scenario:` format.\n\n## Complexity Management\n\n**Default to minimal:**\n- <100 lines of new code\n- Single-file implementations\n- No frameworks without justification\n- Boring, proven patterns\n\n**Only add complexity with:**\n- Performance data showing need\n- Concrete scale requirements (>1000 users)\n- Multiple proven use cases\n\n## Troubleshooting\n\n**\"Change must have at least one delta\"**\n- Check `changes/[name]/specs/` exists\n- Verify operation prefixes (## ADDED Requirements)\n\n**\"Requirement must have at least one scenario\"**\n- Use `#### Scenario:` format (4 hashtags)\n- Don't use bullets or bold\n\n**Debug:** `openspec show [change] --json --deltas-only`\n";
2
+ //# sourceMappingURL=claude-template.d.ts.map