hardness 1.0.0 → 1.1.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.
Files changed (52) hide show
  1. package/AGENTS.md +11 -0
  2. package/CHANGELOG.md +36 -0
  3. package/README.md +62 -15
  4. package/node_modules/@hardness/analyzers/package.json +1 -1
  5. package/node_modules/@hardness/core/dist/common/paths.js +2 -2
  6. package/node_modules/@hardness/core/dist/common/paths.js.map +1 -1
  7. package/node_modules/@hardness/core/package.json +1 -1
  8. package/node_modules/@hardness/prompts/package.json +1 -1
  9. package/package.json +1 -1
  10. package/packages/analyzers/package.json +1 -1
  11. package/packages/cli/dist/commands/discover.js +47 -6
  12. package/packages/cli/dist/commands/discover.js.map +1 -1
  13. package/packages/cli/dist/commands/plan.js +39 -6
  14. package/packages/cli/dist/commands/plan.js.map +1 -1
  15. package/packages/cli/dist/commands/spec.js +34 -6
  16. package/packages/cli/dist/commands/spec.js.map +1 -1
  17. package/packages/cli/dist/dispatcher.d.ts +2 -0
  18. package/packages/cli/dist/dispatcher.js +63 -62
  19. package/packages/cli/dist/dispatcher.js.map +1 -1
  20. package/packages/cli/dist/generators/prd-generator.d.ts +14 -0
  21. package/packages/cli/dist/generators/prd-generator.js +164 -0
  22. package/packages/cli/dist/generators/prd-generator.js.map +1 -0
  23. package/packages/cli/dist/generators/spec-generator.d.ts +35 -0
  24. package/packages/cli/dist/generators/spec-generator.js +245 -0
  25. package/packages/cli/dist/generators/spec-generator.js.map +1 -0
  26. package/packages/cli/dist/generators/sprint-generator.d.ts +51 -0
  27. package/packages/cli/dist/generators/sprint-generator.js +162 -0
  28. package/packages/cli/dist/generators/sprint-generator.js.map +1 -0
  29. package/packages/cli/dist/index.js +1 -1
  30. package/packages/cli/dist/interview/evaluator-prompt.d.ts +9 -0
  31. package/packages/cli/dist/interview/evaluator-prompt.js +192 -0
  32. package/packages/cli/dist/interview/evaluator-prompt.js.map +1 -0
  33. package/packages/cli/dist/interview/evaluator.d.ts +46 -0
  34. package/packages/cli/dist/interview/evaluator.js +142 -0
  35. package/packages/cli/dist/interview/evaluator.js.map +1 -0
  36. package/packages/cli/dist/interview/questions.d.ts +29 -0
  37. package/packages/cli/dist/interview/questions.js +642 -0
  38. package/packages/cli/dist/interview/questions.js.map +1 -0
  39. package/packages/cli/dist/interview/runner.d.ts +14 -0
  40. package/packages/cli/dist/interview/runner.js +327 -0
  41. package/packages/cli/dist/interview/runner.js.map +1 -0
  42. package/packages/cli/dist/interview/suggestions.d.ts +6 -0
  43. package/packages/cli/dist/interview/suggestions.js +230 -0
  44. package/packages/cli/dist/interview/suggestions.js.map +1 -0
  45. package/packages/cli/dist/interview/types.d.ts +46 -0
  46. package/packages/cli/dist/interview/types.js +50 -0
  47. package/packages/cli/dist/interview/types.js.map +1 -0
  48. package/packages/cli/package.json +1 -1
  49. package/packages/core/dist/common/paths.js +2 -2
  50. package/packages/core/dist/common/paths.js.map +1 -1
  51. package/packages/core/package.json +1 -1
  52. package/packages/prompts/package.json +1 -1
@@ -0,0 +1,14 @@
1
+ import type { InterviewState } from '../interview/types.js';
2
+ export interface PrdGeneratorOptions {
3
+ /** Absolute path to write PRD.md. Defaults to cwd/PRD.md. */
4
+ outputPath?: string;
5
+ /** ISO date string to stamp in the header. Defaults to today. */
6
+ date?: string;
7
+ /** Override templates dir (for testing). */
8
+ templatesDir?: string;
9
+ }
10
+ /**
11
+ * Renders PRD.md from an InterviewState and writes it to disk.
12
+ * Returns the rendered markdown string.
13
+ */
14
+ export declare function generatePrd(state: InterviewState, options?: PrdGeneratorOptions): string;
@@ -0,0 +1,164 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ // ---------------------------------------------------------------------------
4
+ // Helper: resolve the templates/ directory relative to this package
5
+ // ---------------------------------------------------------------------------
6
+ function findTemplatesDir() {
7
+ // When running from dist/, go up to the package root then to templates/
8
+ // In source (tests): packages/cli/src/generators/ → up 3 → project root
9
+ // In dist: packages/cli/dist/generators/ → up 3 → project root
10
+ const candidates = [];
11
+ // Walk up from __filename looking for templates/PRD-TEMPLATE.md
12
+ let dir = path.dirname(new URL(import.meta.url).pathname);
13
+ // On Windows, pathname starts with /D:/...; normalize
14
+ if (process.platform === 'win32' && dir.startsWith('/')) {
15
+ dir = dir.slice(1);
16
+ }
17
+ for (let i = 0; i < 6; i++) {
18
+ const candidate = path.join(dir, 'templates', 'PRD-TEMPLATE.md');
19
+ if (fs.existsSync(candidate)) {
20
+ candidates.push(path.join(dir, 'templates'));
21
+ break;
22
+ }
23
+ const parent = path.dirname(dir);
24
+ if (parent === dir)
25
+ break;
26
+ dir = parent;
27
+ }
28
+ if (candidates.length > 0)
29
+ return candidates[0];
30
+ // Fallback: use HARDNESS_ROOT if set, then cwd
31
+ const envRoot = process.env.HARDNESS_ROOT ?? process.cwd();
32
+ return path.join(envRoot, 'templates');
33
+ }
34
+ // ---------------------------------------------------------------------------
35
+ // Technical level label
36
+ // ---------------------------------------------------------------------------
37
+ const TECH_LEVEL_LABELS = {
38
+ '1': 'Non-technical',
39
+ '2': 'Semi-technical',
40
+ '3': 'Technical',
41
+ 'Non-technical': 'Non-technical',
42
+ 'Semi-technical': 'Semi-technical',
43
+ 'Technical': 'Technical',
44
+ };
45
+ // ---------------------------------------------------------------------------
46
+ // Priority label
47
+ // ---------------------------------------------------------------------------
48
+ const PRIORITY_LABELS = {
49
+ '1': 'must',
50
+ '2': 'should',
51
+ '3': 'nice-to-have',
52
+ };
53
+ /**
54
+ * Renders PRD.md from an InterviewState and writes it to disk.
55
+ * Returns the rendered markdown string.
56
+ */
57
+ export function generatePrd(state, options = {}) {
58
+ const date = options.date ?? new Date().toISOString().slice(0, 10);
59
+ const outputPath = options.outputPath ?? path.join(process.cwd(), 'PRD.md');
60
+ // Read template (for structure validation; we render our own content)
61
+ const templatesDir = options.templatesDir ?? findTemplatesDir();
62
+ const templatePath = path.join(templatesDir, 'PRD-TEMPLATE.md');
63
+ if (!fs.existsSync(templatePath)) {
64
+ throw new Error(`PRD-TEMPLATE.md not found at: ${templatePath}`);
65
+ }
66
+ const projectName = state.answers['q1.1'] ?? 'Unnamed Project';
67
+ const oneLiner = state.answers['q1.2'] ?? '';
68
+ const problem = state.answers['q1.3'] ?? '';
69
+ const primaryPersona = state.answers['q2.1'] ?? 'user';
70
+ const secondaryPersonas = state.answers['q2.2'] ?? '';
71
+ const techLevelRaw = state.answers['q2.3'] ?? '2';
72
+ const techLevel = TECH_LEVEL_LABELS[techLevelRaw.split(/[\s—]/)[0].trim()] ?? techLevelRaw;
73
+ const features = state.answers['q3.1'];
74
+ const priorityRaw = state.answers['q3.2'] ?? '1';
75
+ const priorityKey = priorityRaw.split(/[\s—]/)[0].trim();
76
+ const priority = PRIORITY_LABELS[priorityKey] ?? 'must';
77
+ const acceptance = state.answers['q3.3'] ?? '';
78
+ const stack = state.detectedStack.label;
79
+ const database = state.answers['q4.3'] ?? 'N/A';
80
+ const auth = state.answers['q4.4'] ?? 'N/A';
81
+ const scale = state.answers['q4.5'] ?? 'N/A';
82
+ const deploy = state.answers['q4.6'] ?? 'N/A';
83
+ const performance = state.answers['q4.7'] ?? 'N/A';
84
+ const observability = state.answers['q4.8'] ?? 'N/A';
85
+ const outOfScope = state.answers['q5.1'];
86
+ const assumptions = state.answers['q5.2'] ?? '';
87
+ const risks = state.answers['q5.3'] ?? '';
88
+ // --- Sections ---
89
+ const problemText = Array.isArray(problem) ? problem.join('\n') : problem;
90
+ // Functional requirements
91
+ const featureList = Array.isArray(features) ? features : [features];
92
+ const frLines = featureList
93
+ .filter(Boolean)
94
+ .map((f, idx) => `- **FR-${String(idx + 1).padStart(3, '0')}** (${priority}): ${f}`)
95
+ .join('\n');
96
+ const acceptanceLine = acceptance
97
+ ? ` - Acceptance: ${acceptance}`
98
+ : '';
99
+ // Out of scope
100
+ const scopeList = Array.isArray(outOfScope) ? outOfScope : [outOfScope];
101
+ const scopeLines = scopeList
102
+ .filter(Boolean)
103
+ .map((s) => `- ${s}`)
104
+ .join('\n');
105
+ // Personas table
106
+ const personaRows = [
107
+ `| ${primaryPersona} | ${techLevel} | Core product needs |`,
108
+ ];
109
+ if (secondaryPersonas) {
110
+ secondaryPersonas.split(',').forEach((p) => {
111
+ const name = p.trim();
112
+ if (name)
113
+ personaRows.push(`| ${name} | Technical | Support / maintenance |`);
114
+ });
115
+ }
116
+ const md = `# PRD — ${projectName}
117
+
118
+ > Generated by \`hardness discover\` on ${date}.
119
+ > This document is the input for \`hardness spec\`.
120
+
121
+ ## 1. Overview
122
+
123
+ ${oneLiner}
124
+
125
+ ${problemText}
126
+
127
+ ## 2. Personas
128
+
129
+ | Persona | Description | Needs |
130
+ |---|---|---|
131
+ ${personaRows.join('\n')}
132
+
133
+ ## 3. Functional Requirements
134
+
135
+ ${frLines}
136
+ ${acceptanceLine ? acceptanceLine : ''}
137
+
138
+ ## 4. Non-Functional Requirements
139
+
140
+ - **NFR-001**: Stack — ${stack}
141
+ - **NFR-002**: Database — ${database}
142
+ - **NFR-003**: Authentication — ${auth}
143
+ - **NFR-004**: Scale — ${scale}
144
+ - **NFR-005**: Deployment — ${deploy}
145
+ - **NFR-006**: Performance — ${performance}
146
+ - **NFR-007**: Observability — ${observability}
147
+
148
+ ## 5. Out of Scope (v1.0)
149
+
150
+ ${scopeLines || '- (none specified)'}
151
+
152
+ ## 6. Product Acceptance Criteria
153
+
154
+ - [ ] ${acceptance || 'All core features work end-to-end without errors and are covered by tests.'}
155
+
156
+ ## 7. Assumptions and Risks
157
+
158
+ - **Assumptions**: ${assumptions || 'Standard runtime environment.'}
159
+ - **Risks**: ${risks || 'Scope may grow — strict sprint discipline required.'} — Mitigate with clear acceptance criteria per feature.
160
+ `;
161
+ fs.writeFileSync(outputPath, md, 'utf-8');
162
+ return md;
163
+ }
164
+ //# sourceMappingURL=prd-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prd-generator.js","sourceRoot":"","sources":["../../src/generators/prd-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export interface ParsedPrd {
2
+ projectName: string;
3
+ overview: string;
4
+ personas: string[];
5
+ functionalReqs: Array<{
6
+ id: string;
7
+ priority: string;
8
+ description: string;
9
+ }>;
10
+ nfrStack: string;
11
+ nfrDatabase: string;
12
+ nfrAuth: string;
13
+ nfrScale: string;
14
+ nfrDeploy: string;
15
+ nfrPerf: string;
16
+ nfrObs: string;
17
+ outOfScope: string[];
18
+ acceptanceCriteria: string[];
19
+ assumptions: string;
20
+ risks: string;
21
+ }
22
+ export declare function parsePrd(content: string): ParsedPrd;
23
+ export interface SpecGeneratorOptions {
24
+ /** Absolute path to write SPEC.md. Defaults to cwd/SPEC.md. */
25
+ outputPath?: string;
26
+ /** ISO date string. Defaults to today. */
27
+ date?: string;
28
+ /** Override templates dir (for testing). */
29
+ templatesDir?: string;
30
+ }
31
+ /**
32
+ * Reads PRD.md content, parses it, and renders SPEC.md.
33
+ * Returns the rendered markdown string.
34
+ */
35
+ export declare function generateSpec(prdContent: string, options?: SpecGeneratorOptions): string;
@@ -0,0 +1,245 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ // ---------------------------------------------------------------------------
4
+ // Template resolution (same pattern as prd-generator)
5
+ // ---------------------------------------------------------------------------
6
+ function findTemplatesDir() {
7
+ let dir = path.dirname(new URL(import.meta.url).pathname);
8
+ if (process.platform === 'win32' && dir.startsWith('/'))
9
+ dir = dir.slice(1);
10
+ for (let i = 0; i < 6; i++) {
11
+ const candidate = path.join(dir, 'templates', 'SPEC-TEMPLATE.md');
12
+ if (fs.existsSync(candidate))
13
+ return path.join(dir, 'templates');
14
+ const parent = path.dirname(dir);
15
+ if (parent === dir)
16
+ break;
17
+ dir = parent;
18
+ }
19
+ const envRoot = process.env.HARDNESS_ROOT ?? process.cwd();
20
+ return path.join(envRoot, 'templates');
21
+ }
22
+ export function parsePrd(content) {
23
+ const lines = content.split('\n');
24
+ // Project name from h1
25
+ const h1Line = lines.find((l) => l.startsWith('# PRD —'));
26
+ const projectName = h1Line ? h1Line.replace('# PRD —', '').trim() : 'Unknown Project';
27
+ // Helper: extract a section's content (between two ## headers)
28
+ function extractSection(header) {
29
+ const startIdx = lines.findIndex((l) => l.startsWith(`## ${header}`));
30
+ if (startIdx === -1)
31
+ return [];
32
+ const result = [];
33
+ for (let i = startIdx + 1; i < lines.length; i++) {
34
+ if (lines[i].startsWith('## '))
35
+ break;
36
+ result.push(lines[i]);
37
+ }
38
+ return result;
39
+ }
40
+ // Overview
41
+ const overviewLines = extractSection('1. Overview').filter((l) => l.trim());
42
+ const overview = overviewLines.join('\n').trim();
43
+ // Personas (extract from table rows, skip header and separator)
44
+ const personaLines = extractSection('2. Personas')
45
+ .filter((l) => l.startsWith('|') && !l.includes('---|'))
46
+ .slice(1); // skip column header
47
+ const personas = personaLines
48
+ .map((l) => l.split('|')[1]?.trim())
49
+ .filter(Boolean);
50
+ // Functional Requirements
51
+ const frLines = extractSection('3. Functional Requirements').filter((l) => l.startsWith('- **FR-'));
52
+ const functionalReqs = frLines.map((l) => {
53
+ const match = l.match(/\*\*(FR-\d+)\*\*\s*\(([^)]+)\):\s*(.*)/);
54
+ if (match)
55
+ return { id: match[1], priority: match[2], description: match[3].trim() };
56
+ return { id: 'FR-???', priority: 'must', description: l };
57
+ });
58
+ // NFRs
59
+ const nfrLines = extractSection('4. Non-Functional Requirements');
60
+ const getNfr = (key) => {
61
+ const line = nfrLines.find((l) => l.includes(key));
62
+ if (!line)
63
+ return 'N/A';
64
+ const idx = line.indexOf('—');
65
+ return idx !== -1 ? line.slice(idx + 1).trim() : line.replace(/.*:/, '').trim();
66
+ };
67
+ const nfrStack = getNfr('Stack');
68
+ const nfrDatabase = getNfr('Database');
69
+ const nfrAuth = getNfr('Authentication');
70
+ const nfrScale = getNfr('Scale');
71
+ const nfrDeploy = getNfr('Deployment');
72
+ const nfrPerf = getNfr('Performance');
73
+ const nfrObs = getNfr('Observability');
74
+ // Out of scope
75
+ const outOfScope = extractSection('5. Out of Scope')
76
+ .filter((l) => l.startsWith('- '))
77
+ .map((l) => l.slice(2).trim());
78
+ // Acceptance criteria
79
+ const acceptanceCriteria = extractSection('6. Product Acceptance Criteria')
80
+ .filter((l) => l.startsWith('- [ ]'))
81
+ .map((l) => l.replace('- [ ]', '').trim());
82
+ // Assumptions + risks
83
+ const assRiskLines = extractSection('7. Assumptions and Risks');
84
+ const assumptionsLine = assRiskLines.find((l) => l.startsWith('- **Assumptions**'));
85
+ const risksLine = assRiskLines.find((l) => l.startsWith('- **Risks**'));
86
+ const assumptions = assumptionsLine
87
+ ? assumptionsLine.replace('- **Assumptions**:', '').trim()
88
+ : '';
89
+ const risks = risksLine ? risksLine.replace('- **Risks**:', '').trim() : '';
90
+ return {
91
+ projectName,
92
+ overview,
93
+ personas,
94
+ functionalReqs,
95
+ nfrStack,
96
+ nfrDatabase,
97
+ nfrAuth,
98
+ nfrScale,
99
+ nfrDeploy,
100
+ nfrPerf,
101
+ nfrObs,
102
+ outOfScope,
103
+ acceptanceCriteria,
104
+ assumptions,
105
+ risks,
106
+ };
107
+ }
108
+ // ---------------------------------------------------------------------------
109
+ // FR → technical requirement mapping
110
+ // ---------------------------------------------------------------------------
111
+ function mapFrToTechReqs(fr) {
112
+ // Each FR becomes at least one tech requirement.
113
+ // Must-have FRs get an implementation + test requirement.
114
+ const reqs = [];
115
+ let idx = 1;
116
+ for (const f of fr) {
117
+ reqs.push(`${idx}. Implement **${f.id}** (${f.priority}): ${f.description}`);
118
+ idx++;
119
+ if (f.priority === 'must') {
120
+ reqs.push(`${idx}. Write tests for ${f.id}: cover happy path, edge cases, and error handling`);
121
+ idx++;
122
+ }
123
+ }
124
+ return reqs;
125
+ }
126
+ /**
127
+ * Reads PRD.md content, parses it, and renders SPEC.md.
128
+ * Returns the rendered markdown string.
129
+ */
130
+ export function generateSpec(prdContent, options = {}) {
131
+ const date = options.date ?? new Date().toISOString().slice(0, 10);
132
+ const outputPath = options.outputPath ?? path.join(process.cwd(), 'SPEC.md');
133
+ // Validate that the template exists
134
+ const templatesDir = options.templatesDir ?? findTemplatesDir();
135
+ const templatePath = path.join(templatesDir, 'SPEC-TEMPLATE.md');
136
+ if (!fs.existsSync(templatePath)) {
137
+ throw new Error(`SPEC-TEMPLATE.md not found at: ${templatePath}`);
138
+ }
139
+ const prd = parsePrd(prdContent);
140
+ const techReqs = mapFrToTechReqs(prd.functionalReqs);
141
+ // Detect stack profile from NFRs
142
+ const stackLine = prd.nfrStack.toLowerCase();
143
+ const isNode = stackLine.includes('node') || stackLine.includes('typescript');
144
+ const isPython = stackLine.includes('python');
145
+ const isGo = stackLine.includes('go');
146
+ const isRust = stackLine.includes('rust');
147
+ const testFramework = isNode
148
+ ? 'Vitest'
149
+ : isPython
150
+ ? 'pytest'
151
+ : isGo
152
+ ? 'go test'
153
+ : isRust
154
+ ? 'cargo test'
155
+ : 'Unit tests';
156
+ // Personas section
157
+ const personaRows = prd.personas.length
158
+ ? prd.personas.map((p) => `| ${p} | Core persona | Product requirements |`).join('\n')
159
+ : '| user | Primary user | Core functionality |';
160
+ // Module table: one row per must-have FR
161
+ const mustFrs = prd.functionalReqs.filter((f) => f.priority === 'must');
162
+ const moduleRows = mustFrs.length
163
+ ? mustFrs
164
+ .map((f) => `| \`${f.description.split(' ').slice(0, 3).join('-').toLowerCase()}\` | ${f.description} |`)
165
+ .join('\n')
166
+ : '| `core` | Core module |';
167
+ // Out of scope
168
+ const outLines = prd.outOfScope.length
169
+ ? prd.outOfScope.map((s) => `- ${s}`).join('\n')
170
+ : '- (none specified)';
171
+ // Technical requirements numbered list
172
+ const techReqsBlock = techReqs.join('\n');
173
+ const md = `# SPEC — ${prd.projectName}
174
+
175
+ > Generated by \`hardness spec\` on ${date} from PRD.md.
176
+ > This document is the input for \`hardness plan\`. The \`specLines\` in each feature reference lines from this file.
177
+
178
+ ## 1. Architectural Vision
179
+
180
+ ${prd.overview || 'See PRD.md for the product vision.'}
181
+
182
+ \`\`\`
183
+ ${prd.projectName}
184
+ +-- Core features: ${mustFrs.map((f) => f.description).join(', ')}
185
+ +-- Stack: ${prd.nfrStack}
186
+ \`\`\`
187
+
188
+ ## 2. Tech Stack
189
+
190
+ - **Language / Runtime**: ${prd.nfrStack}
191
+ - **Tests**: ${testFramework}
192
+ - **Database**: ${prd.nfrDatabase}
193
+ - **Authentication**: ${prd.nfrAuth}
194
+ - **Deployment**: ${prd.nfrDeploy}
195
+ - **Scale**: ${prd.nfrScale}
196
+ - **Performance**: ${prd.nfrPerf}
197
+ - **Observability**: ${prd.nfrObs}
198
+
199
+ ## 3. Modules / Packages
200
+
201
+ | Module | Responsibility |
202
+ |---|---|
203
+ ${moduleRows}
204
+
205
+ ## 4. Data Model
206
+
207
+ ${prd.personas.length ? `Personas: ${prd.personas.join(', ')}.` : 'See PRD.md for personas.'}
208
+
209
+ ${prd.functionalReqs.length
210
+ ? `Entities implied by functional requirements:\n${prd.functionalReqs
211
+ .filter((f) => f.priority === 'must')
212
+ .map((f) => `- **${f.id}**: ${f.description}`)
213
+ .join('\n')}`
214
+ : ''}
215
+
216
+ ## 5. Main Flows
217
+
218
+ ${prd.functionalReqs
219
+ .filter((f) => f.priority === 'must')
220
+ .map((f, i) => `### 5.${i + 1} ${f.description}
221
+ 1. Trigger the ${f.description.toLowerCase()} flow
222
+ 2. Validate inputs and apply business rules
223
+ 3. Persist changes and return result`)
224
+ .join('\n\n')}
225
+
226
+ ## 6. Integration Contracts
227
+
228
+ - **Out of scope (v1.0)**: ${prd.outOfScope.join('; ') || 'None specified'}
229
+ - **Assumptions**: ${prd.assumptions || 'Standard runtime environment'}
230
+ - **Risks**: ${prd.risks || 'See PRD.md'}
231
+
232
+ ## 7. Detailed Technical Requirements
233
+
234
+ (The lines below will be referenced by \`specLines\` in the sprints.)
235
+
236
+ ${techReqsBlock}
237
+
238
+ ### Out of Scope
239
+
240
+ ${outLines}
241
+ `;
242
+ fs.writeFileSync(outputPath, md, 'utf-8');
243
+ return md;
244
+ }
245
+ //# sourceMappingURL=spec-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-generator.js","sourceRoot":"","sources":["../../src/generators/spec-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ export interface TechRequirement {
2
+ /** 1-indexed position in section 7 */
3
+ index: number;
4
+ /** Full text of the requirement (without leading "N. ") */
5
+ text: string;
6
+ }
7
+ export declare function parseSpec(content: string): {
8
+ projectName: string;
9
+ techReqs: TechRequirement[];
10
+ stack: string;
11
+ };
12
+ export interface GeneratedFeature {
13
+ id: string;
14
+ title: string;
15
+ description: string;
16
+ specLines: string;
17
+ status: 'pending';
18
+ files: string[];
19
+ acceptanceCriteria: string[];
20
+ hints: string[];
21
+ verification: {
22
+ typecheck: boolean;
23
+ smoke: {
24
+ command: string;
25
+ executedBy: string;
26
+ timeoutSeconds: number;
27
+ };
28
+ };
29
+ }
30
+ export interface GeneratedSprint {
31
+ index: number;
32
+ name: string;
33
+ status: 'pending';
34
+ description: string;
35
+ crossCutting: string[];
36
+ features: GeneratedFeature[];
37
+ }
38
+ export interface SprintGeneratorOptions {
39
+ /** Directory where sprint JSON files will be written. Defaults to .hardness/sprints/ relative to root. */
40
+ sprintsDir?: string;
41
+ /** Project root to locate .hardness/. */
42
+ root?: string;
43
+ /** Dry run — return sprints without writing files. */
44
+ dryRun?: boolean;
45
+ }
46
+ export interface SprintGeneratorResult {
47
+ sprints: GeneratedSprint[];
48
+ indexJson: object;
49
+ sprintFiles: string[];
50
+ }
51
+ export declare function generateSprints(specContent: string, options?: SprintGeneratorOptions): SprintGeneratorResult;