@specsafe/core 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/adapters/base.d.ts +44 -0
- package/dist/agents/adapters/base.d.ts.map +1 -0
- package/dist/agents/adapters/base.js +164 -0
- package/dist/agents/adapters/base.js.map +1 -0
- package/dist/agents/adapters/claude-code.d.ts +14 -0
- package/dist/agents/adapters/claude-code.d.ts.map +1 -0
- package/dist/agents/adapters/claude-code.js +120 -0
- package/dist/agents/adapters/claude-code.js.map +1 -0
- package/dist/agents/adapters/copilot.d.ts +13 -0
- package/dist/agents/adapters/copilot.d.ts.map +1 -0
- package/dist/agents/adapters/copilot.js +115 -0
- package/dist/agents/adapters/copilot.js.map +1 -0
- package/dist/agents/adapters/cursor.d.ts +13 -0
- package/dist/agents/adapters/cursor.d.ts.map +1 -0
- package/dist/agents/adapters/cursor.js +105 -0
- package/dist/agents/adapters/cursor.js.map +1 -0
- package/dist/agents/adapters/gemini-cli.d.ts +13 -0
- package/dist/agents/adapters/gemini-cli.d.ts.map +1 -0
- package/dist/agents/adapters/gemini-cli.js +79 -0
- package/dist/agents/adapters/gemini-cli.js.map +1 -0
- package/dist/agents/adapters/index.d.ts +16 -0
- package/dist/agents/adapters/index.d.ts.map +1 -0
- package/dist/agents/adapters/index.js +47 -0
- package/dist/agents/adapters/index.js.map +1 -0
- package/dist/agents/adapters/opencode.d.ts +13 -0
- package/dist/agents/adapters/opencode.d.ts.map +1 -0
- package/dist/agents/adapters/opencode.js +67 -0
- package/dist/agents/adapters/opencode.js.map +1 -0
- package/dist/agents/index.d.ts +8 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +9 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/registry.d.ts +70 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +194 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/types.d.ts +71 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +6 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/delta/merger.d.ts +36 -0
- package/dist/delta/merger.d.ts.map +1 -0
- package/dist/delta/merger.js +264 -0
- package/dist/delta/merger.js.map +1 -0
- package/dist/delta/parser.d.ts +27 -0
- package/dist/delta/parser.d.ts.map +1 -0
- package/dist/delta/parser.js +196 -0
- package/dist/delta/parser.js.map +1 -0
- package/dist/delta/types.d.ts +39 -0
- package/dist/delta/types.d.ts.map +1 -0
- package/dist/delta/types.js +6 -0
- package/dist/delta/types.js.map +1 -0
- package/dist/ears/index.d.ts +11 -0
- package/dist/ears/index.d.ts.map +1 -0
- package/dist/ears/index.js +11 -0
- package/dist/ears/index.js.map +1 -0
- package/dist/ears/parser.d.ts +22 -0
- package/dist/ears/parser.d.ts.map +1 -0
- package/dist/ears/parser.js +273 -0
- package/dist/ears/parser.js.map +1 -0
- package/dist/ears/template.d.ts +20 -0
- package/dist/ears/template.d.ts.map +1 -0
- package/dist/ears/template.js +364 -0
- package/dist/ears/template.js.map +1 -0
- package/dist/ears/types.d.ts +58 -0
- package/dist/ears/types.d.ts.map +1 -0
- package/dist/ears/types.js +6 -0
- package/dist/ears/types.js.map +1 -0
- package/dist/ears/validator.d.ts +37 -0
- package/dist/ears/validator.d.ts.map +1 -0
- package/dist/ears/validator.js +234 -0
- package/dist/ears/validator.js.map +1 -0
- package/dist/elicitation/engine.d.ts +75 -0
- package/dist/elicitation/engine.d.ts.map +1 -0
- package/dist/elicitation/engine.js +174 -0
- package/dist/elicitation/engine.js.map +1 -0
- package/dist/elicitation/flows.d.ts +18 -0
- package/dist/elicitation/flows.d.ts.map +1 -0
- package/dist/elicitation/flows.js +331 -0
- package/dist/elicitation/flows.js.map +1 -0
- package/dist/elicitation/generator.d.ts +20 -0
- package/dist/elicitation/generator.d.ts.map +1 -0
- package/dist/elicitation/generator.js +260 -0
- package/dist/elicitation/generator.js.map +1 -0
- package/dist/elicitation/index.d.ts +27 -0
- package/dist/elicitation/index.d.ts.map +1 -0
- package/dist/elicitation/index.js +29 -0
- package/dist/elicitation/index.js.map +1 -0
- package/dist/elicitation/types.d.ts +69 -0
- package/dist/elicitation/types.d.ts.map +1 -0
- package/dist/elicitation/types.js +6 -0
- package/dist/elicitation/types.js.map +1 -0
- package/dist/extensions/builtins/complexity.d.ts +7 -0
- package/dist/extensions/builtins/complexity.d.ts.map +1 -0
- package/dist/extensions/builtins/complexity.js +97 -0
- package/dist/extensions/builtins/complexity.js.map +1 -0
- package/dist/extensions/builtins/owasp.d.ts +7 -0
- package/dist/extensions/builtins/owasp.d.ts.map +1 -0
- package/dist/extensions/builtins/owasp.js +76 -0
- package/dist/extensions/builtins/owasp.js.map +1 -0
- package/dist/extensions/index.d.ts +54 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +72 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/loader.d.ts +28 -0
- package/dist/extensions/loader.d.ts.map +1 -0
- package/dist/extensions/loader.js +62 -0
- package/dist/extensions/loader.js.map +1 -0
- package/dist/extensions/registry.d.ts +74 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/extensions/registry.js +159 -0
- package/dist/extensions/registry.js.map +1 -0
- package/dist/extensions/types.d.ts +70 -0
- package/dist/extensions/types.d.ts.map +1 -0
- package/dist/extensions/types.js +2 -0
- package/dist/extensions/types.js.map +1 -0
- package/dist/governance/builtins.d.ts +7 -0
- package/dist/governance/builtins.d.ts.map +1 -0
- package/dist/governance/builtins.js +105 -0
- package/dist/governance/builtins.js.map +1 -0
- package/dist/governance/constitution.d.ts +23 -0
- package/dist/governance/constitution.d.ts.map +1 -0
- package/dist/governance/constitution.js +245 -0
- package/dist/governance/constitution.js.map +1 -0
- package/dist/governance/index.d.ts +3 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/governance/template.d.ts +12 -0
- package/dist/governance/template.d.ts.map +1 -0
- package/dist/governance/template.js +84 -0
- package/dist/governance/template.js.map +1 -0
- package/dist/governance/types.d.ts +64 -0
- package/dist/governance/types.d.ts.map +1 -0
- package/dist/governance/types.js +2 -0
- package/dist/governance/types.js.map +1 -0
- package/dist/index.d.ts +23 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -18
- package/dist/index.js.map +1 -1
- package/dist/templates/checklist.d.ts +7 -0
- package/dist/templates/checklist.d.ts.map +1 -0
- package/dist/templates/checklist.js +131 -0
- package/dist/templates/checklist.js.map +1 -0
- package/dist/templates/delta-template.d.ts +18 -0
- package/dist/templates/delta-template.d.ts.map +1 -0
- package/dist/templates/delta-template.js +191 -0
- package/dist/templates/delta-template.js.map +1 -0
- package/dist/templates/engine.d.ts +20 -0
- package/dist/templates/engine.d.ts.map +1 -0
- package/dist/templates/engine.js +187 -0
- package/dist/templates/engine.js.map +1 -0
- package/dist/templates/types.d.ts +67 -0
- package/dist/templates/types.d.ts.map +1 -0
- package/dist/templates/types.js +5 -0
- package/dist/templates/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EARS Validator
|
|
3
|
+
* Validates requirements against EARS patterns and suggests improvements
|
|
4
|
+
*/
|
|
5
|
+
import { parseEARSRequirement } from './parser.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validate all requirements in a spec for EARS compliance
|
|
8
|
+
*/
|
|
9
|
+
export function validateRequirements(spec) {
|
|
10
|
+
const validations = [];
|
|
11
|
+
for (const requirement of spec.requirements) {
|
|
12
|
+
const validation = validateRequirement(requirement.text);
|
|
13
|
+
validations.push(validation);
|
|
14
|
+
}
|
|
15
|
+
// Calculate summary
|
|
16
|
+
const compliantCount = validations.filter(v => v.isCompliant).length;
|
|
17
|
+
const score = spec.requirements.length > 0
|
|
18
|
+
? Math.round((compliantCount / spec.requirements.length) * 100)
|
|
19
|
+
: 0;
|
|
20
|
+
// Group by EARS type
|
|
21
|
+
const typeCounts = new Map();
|
|
22
|
+
for (const validation of validations) {
|
|
23
|
+
if (validation.earsRequirement) {
|
|
24
|
+
const type = validation.earsRequirement.type;
|
|
25
|
+
typeCounts.set(type, (typeCounts.get(type) || 0) + 1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const summary = Array.from(typeCounts.entries()).map(([type, count]) => ({
|
|
29
|
+
type,
|
|
30
|
+
count
|
|
31
|
+
}));
|
|
32
|
+
// Generate recommendation
|
|
33
|
+
let recommendation = '';
|
|
34
|
+
if (score >= 90) {
|
|
35
|
+
recommendation = '✅ Excellent EARS compliance! Requirements are well-structured and testable.';
|
|
36
|
+
}
|
|
37
|
+
else if (score >= 70) {
|
|
38
|
+
recommendation = '⚠️ Good EARS compliance, but some requirements could be improved for better testability.';
|
|
39
|
+
}
|
|
40
|
+
else if (score >= 50) {
|
|
41
|
+
recommendation = '⚠️ Moderate EARS compliance. Consider rewriting requirements using EARS patterns.';
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
recommendation = '❌ Low EARS compliance. Requirements should be rewritten using EARS patterns for testability.';
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
score,
|
|
48
|
+
totalRequirements: spec.requirements.length,
|
|
49
|
+
compliantCount,
|
|
50
|
+
requirements: validations,
|
|
51
|
+
summary,
|
|
52
|
+
recommendation
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validate a single requirement text
|
|
57
|
+
*
|
|
58
|
+
* Note: A requirement is considered compliant (isCompliant = true) ONLY if it both:
|
|
59
|
+
* 1. Matches an EARS pattern with sufficient confidence, AND
|
|
60
|
+
* 2. Has zero quality issues (no ambiguous words, vague terms, etc.)
|
|
61
|
+
*
|
|
62
|
+
* @param text - The requirement text to validate
|
|
63
|
+
* @returns Validation result with compliance status, issues, and suggestions
|
|
64
|
+
*/
|
|
65
|
+
export function validateRequirement(text) {
|
|
66
|
+
const earsRequirement = parseEARSRequirement(text);
|
|
67
|
+
const issues = [];
|
|
68
|
+
let isCompliant = false;
|
|
69
|
+
let suggestion;
|
|
70
|
+
// Check if it matches an EARS pattern
|
|
71
|
+
if (earsRequirement.type === 'unknown') {
|
|
72
|
+
issues.push('Does not follow any EARS pattern');
|
|
73
|
+
suggestion = generateEARSSuggestion(text);
|
|
74
|
+
}
|
|
75
|
+
else if (earsRequirement.confidence < 0.7) {
|
|
76
|
+
issues.push('Weak EARS pattern match');
|
|
77
|
+
suggestion = generateEARSSuggestion(text);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
isCompliant = true;
|
|
81
|
+
}
|
|
82
|
+
// Additional quality checks
|
|
83
|
+
if (!text.match(/\b(shall|must|will)\b/i)) {
|
|
84
|
+
issues.push('Missing modal verb (shall/must/will)');
|
|
85
|
+
}
|
|
86
|
+
if (text.split(' ').length < 5) {
|
|
87
|
+
issues.push('Requirement is too short - may lack necessary detail');
|
|
88
|
+
}
|
|
89
|
+
if (text.split(' ').length > 40) {
|
|
90
|
+
issues.push('Requirement is too long - consider splitting into multiple requirements');
|
|
91
|
+
}
|
|
92
|
+
// Check for ambiguous words
|
|
93
|
+
const ambiguousWords = ['should', 'may', 'might', 'could', 'possibly', 'probably', 'usually', 'maybe', 'perhaps'];
|
|
94
|
+
for (const word of ambiguousWords) {
|
|
95
|
+
if (new RegExp(`\\b${word}\\b`, 'i').test(text)) {
|
|
96
|
+
issues.push(`Contains ambiguous word: "${word}"`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Check for vague terms (precompiled with word boundaries to avoid false positives)
|
|
100
|
+
const vagueTerms = ['appropriate', 'adequate', 'reasonable', 'efficient', 'user-friendly', 'as needed'];
|
|
101
|
+
const vagueTermPatterns = vagueTerms.map(term => new RegExp(`\\b${term}\\b`, 'i'));
|
|
102
|
+
for (let i = 0; i < vagueTerms.length; i++) {
|
|
103
|
+
if (vagueTermPatterns[i].test(text)) {
|
|
104
|
+
issues.push(`Contains vague term: "${vagueTerms[i]}" - specify measurable criteria`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
text,
|
|
109
|
+
isCompliant: isCompliant && issues.length === 0,
|
|
110
|
+
earsRequirement: earsRequirement.type !== 'unknown' ? earsRequirement : undefined,
|
|
111
|
+
issues,
|
|
112
|
+
suggestion
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Generate an EARS-formatted suggestion for a non-compliant requirement
|
|
117
|
+
*/
|
|
118
|
+
function generateEARSSuggestion(text) {
|
|
119
|
+
// Try to detect implicit patterns and suggest EARS format
|
|
120
|
+
// If it mentions a trigger condition, suggest event-driven
|
|
121
|
+
if (/\b(after|once|following|receives|detects|triggers)\b/i.test(text)) {
|
|
122
|
+
return `Consider event-driven EARS: "When [event occurs], the system shall [action]"\nExample: "When user submits the form, the system shall validate all fields"`;
|
|
123
|
+
}
|
|
124
|
+
// If it mentions a state, suggest state-driven
|
|
125
|
+
if (/\b(during|active|running|enabled|in\s+\w+\s+mode)\b/i.test(text)) {
|
|
126
|
+
return `Consider state-driven EARS: "While [state exists], the system shall [action]"\nExample: "While user is logged in, the system shall display the dashboard"`;
|
|
127
|
+
}
|
|
128
|
+
// If it mentions a condition, suggest optional
|
|
129
|
+
if (/\b(if|when|in case|for|with)\b/i.test(text) && !/then/i.test(text)) {
|
|
130
|
+
return `Consider optional EARS: "Where [condition], the system shall [action]"\nExample: "Where user has admin privileges, the system shall allow access to settings"`;
|
|
131
|
+
}
|
|
132
|
+
// If it mentions error handling, suggest unwanted
|
|
133
|
+
if (/\b(error|fail|invalid|incorrect|wrong|exception)\b/i.test(text)) {
|
|
134
|
+
return `Consider unwanted behavior EARS: "If [unwanted condition], then the system shall [action]"\nExample: "If user enters invalid credentials, then the system shall display an error message"`;
|
|
135
|
+
}
|
|
136
|
+
// Default to ubiquitous
|
|
137
|
+
return `Consider ubiquitous EARS: "The system shall [action]"\nExample: "The system shall encrypt all sensitive data at rest"`;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get EARS compliance score for a spec (0-100)
|
|
141
|
+
*/
|
|
142
|
+
export function getEARSScore(spec) {
|
|
143
|
+
const result = validateRequirements(spec);
|
|
144
|
+
return result.score;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check if a spec meets minimum EARS compliance threshold
|
|
148
|
+
* @param spec - The spec to check
|
|
149
|
+
* @param threshold - Minimum score (0-100)
|
|
150
|
+
* @param precomputedScore - Optional pre-computed score to avoid re-validation
|
|
151
|
+
*/
|
|
152
|
+
export function meetsEARSThreshold(spec, threshold = 80, precomputedScore) {
|
|
153
|
+
const score = precomputedScore !== undefined ? precomputedScore : getEARSScore(spec);
|
|
154
|
+
return score >= threshold;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Generate a detailed EARS compliance report as markdown
|
|
158
|
+
*/
|
|
159
|
+
export function generateEARSReport(spec) {
|
|
160
|
+
const result = validateRequirements(spec);
|
|
161
|
+
let report = `# EARS Compliance Report\n\n`;
|
|
162
|
+
report += `**Spec ID:** ${spec.id}\n`;
|
|
163
|
+
report += `**Spec Name:** ${spec.name}\n`;
|
|
164
|
+
report += `**Date:** ${new Date().toISOString().split('T')[0]}\n\n`;
|
|
165
|
+
report += `## Overall Score: ${result.score}/100\n\n`;
|
|
166
|
+
report += `${result.recommendation}\n\n`;
|
|
167
|
+
report += `### Summary\n`;
|
|
168
|
+
report += `- **Total Requirements:** ${result.totalRequirements}\n`;
|
|
169
|
+
report += `- **Compliant:** ${result.compliantCount}\n`;
|
|
170
|
+
report += `- **Non-Compliant:** ${result.totalRequirements - result.compliantCount}\n\n`;
|
|
171
|
+
if (result.summary.length > 0) {
|
|
172
|
+
report += `### Requirements by EARS Type\n\n`;
|
|
173
|
+
for (const { type, count } of result.summary) {
|
|
174
|
+
const emoji = getEARSTypeEmoji(type);
|
|
175
|
+
report += `- ${emoji} **${type}:** ${count}\n`;
|
|
176
|
+
}
|
|
177
|
+
report += `\n`;
|
|
178
|
+
}
|
|
179
|
+
report += `## Detailed Analysis\n\n`;
|
|
180
|
+
for (let i = 0; i < result.requirements.length; i++) {
|
|
181
|
+
const validation = result.requirements[i];
|
|
182
|
+
const status = validation.isCompliant ? '✅' : '❌';
|
|
183
|
+
report += `### ${status} Requirement ${i + 1}\n\n`;
|
|
184
|
+
report += `**Text:** "${validation.text}"\n\n`;
|
|
185
|
+
if (validation.earsRequirement && validation.earsRequirement.type !== 'unknown') {
|
|
186
|
+
const ears = validation.earsRequirement;
|
|
187
|
+
report += `**EARS Type:** ${ears.type}\n`;
|
|
188
|
+
report += `**Confidence:** ${Math.round(ears.confidence * 100)}%\n\n`;
|
|
189
|
+
if (ears.event)
|
|
190
|
+
report += `- **Event:** ${ears.event}\n`;
|
|
191
|
+
if (ears.state)
|
|
192
|
+
report += `- **State:** ${ears.state}\n`;
|
|
193
|
+
if (ears.condition)
|
|
194
|
+
report += `- **Condition:** ${ears.condition}\n`;
|
|
195
|
+
if (ears.unwantedCondition)
|
|
196
|
+
report += `- **Unwanted Condition:** ${ears.unwantedCondition}\n`;
|
|
197
|
+
if (ears.conditions) {
|
|
198
|
+
report += `- **Conditions:**\n`;
|
|
199
|
+
for (const cond of ears.conditions) {
|
|
200
|
+
report += ` - ${cond.type}: ${cond.value}\n`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
report += `- **Action:** ${ears.action}\n\n`;
|
|
204
|
+
}
|
|
205
|
+
if (validation.issues.length > 0) {
|
|
206
|
+
report += `**Issues:**\n`;
|
|
207
|
+
for (const issue of validation.issues) {
|
|
208
|
+
report += `- ⚠️ ${issue}\n`;
|
|
209
|
+
}
|
|
210
|
+
report += `\n`;
|
|
211
|
+
}
|
|
212
|
+
if (validation.suggestion) {
|
|
213
|
+
report += `**Suggestion:**\n${validation.suggestion}\n\n`;
|
|
214
|
+
}
|
|
215
|
+
report += `---\n\n`;
|
|
216
|
+
}
|
|
217
|
+
return report;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get emoji for EARS type
|
|
221
|
+
*/
|
|
222
|
+
function getEARSTypeEmoji(type) {
|
|
223
|
+
const emojis = {
|
|
224
|
+
ubiquitous: '🌐',
|
|
225
|
+
event: '⚡',
|
|
226
|
+
state: '🔄',
|
|
227
|
+
optional: '🔀',
|
|
228
|
+
unwanted: '🚫',
|
|
229
|
+
complex: '🔗',
|
|
230
|
+
unknown: '❓'
|
|
231
|
+
};
|
|
232
|
+
return emojis[type] || '❓';
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/ears/validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elicitation Engine
|
|
3
|
+
* Manages interactive specification elicitation workflows
|
|
4
|
+
*/
|
|
5
|
+
import type { ElicitationFlow, ElicitationStep, ElicitationResult } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Engine for running elicitation flows
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const engine = new ElicitationEngine(quickFlow);
|
|
12
|
+
* const firstStep = engine.start();
|
|
13
|
+
*
|
|
14
|
+
* while (!engine.isComplete()) {
|
|
15
|
+
* const current = engine.getCurrentStep();
|
|
16
|
+
* const userAnswer = await getUserInput(current);
|
|
17
|
+
* const nextStep = engine.answer(current.id, userAnswer);
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* const result = engine.getResult();
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class ElicitationEngine {
|
|
24
|
+
private flow;
|
|
25
|
+
private answers;
|
|
26
|
+
private skipped;
|
|
27
|
+
private currentIndex;
|
|
28
|
+
private startedAt;
|
|
29
|
+
private completedAt;
|
|
30
|
+
/**
|
|
31
|
+
* Create a new elicitation engine
|
|
32
|
+
* @param flow The elicitation flow to execute
|
|
33
|
+
*/
|
|
34
|
+
constructor(flow: ElicitationFlow);
|
|
35
|
+
/**
|
|
36
|
+
* Start the elicitation flow
|
|
37
|
+
* @returns The first step to present to the user, or null if no valid steps
|
|
38
|
+
*/
|
|
39
|
+
start(): ElicitationStep | null;
|
|
40
|
+
/**
|
|
41
|
+
* Record an answer and move to the next step
|
|
42
|
+
* @param stepId The step ID being answered
|
|
43
|
+
* @param value The user's answer
|
|
44
|
+
* @returns The next step, or null if flow is complete
|
|
45
|
+
*/
|
|
46
|
+
answer(stepId: string, value: any): ElicitationStep | null;
|
|
47
|
+
/**
|
|
48
|
+
* Skip a step (only allowed if not required)
|
|
49
|
+
* @param stepId The step ID to skip
|
|
50
|
+
* @returns The next step, or null if flow is complete
|
|
51
|
+
*/
|
|
52
|
+
skip(stepId: string): ElicitationStep | null;
|
|
53
|
+
/**
|
|
54
|
+
* Get the current step
|
|
55
|
+
* @returns The current step, or null if not started or completed
|
|
56
|
+
*/
|
|
57
|
+
getCurrentStep(): ElicitationStep | null;
|
|
58
|
+
/**
|
|
59
|
+
* Check if the flow is complete
|
|
60
|
+
* @returns True if all steps have been processed
|
|
61
|
+
*/
|
|
62
|
+
isComplete(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Get the elicitation result
|
|
65
|
+
* @returns The complete result with answers and metadata
|
|
66
|
+
*/
|
|
67
|
+
getResult(): ElicitationResult;
|
|
68
|
+
/**
|
|
69
|
+
* Find the next valid step, skipping conditional steps that don't match
|
|
70
|
+
* @param startIndex Index to start searching from
|
|
71
|
+
* @returns The next valid step, or null if flow is complete
|
|
72
|
+
*/
|
|
73
|
+
private getNextValidStep;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/elicitation/engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAqB;IAExC;;;OAGG;gBACS,IAAI,EAAE,eAAe;IAIjC;;;OAGG;IACH,KAAK,IAAI,eAAe,GAAG,IAAI;IAM/B;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,eAAe,GAAG,IAAI;IA0C1D;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IA8B5C;;;OAGG;IACH,cAAc,IAAI,eAAe,GAAG,IAAI;IAOxC;;;OAGG;IACH,UAAU,IAAI,OAAO;IAIrB;;;OAGG;IACH,SAAS,IAAI,iBAAiB;IAgB9B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAuBzB"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elicitation Engine
|
|
3
|
+
* Manages interactive specification elicitation workflows
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Engine for running elicitation flows
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const engine = new ElicitationEngine(quickFlow);
|
|
11
|
+
* const firstStep = engine.start();
|
|
12
|
+
*
|
|
13
|
+
* while (!engine.isComplete()) {
|
|
14
|
+
* const current = engine.getCurrentStep();
|
|
15
|
+
* const userAnswer = await getUserInput(current);
|
|
16
|
+
* const nextStep = engine.answer(current.id, userAnswer);
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* const result = engine.getResult();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class ElicitationEngine {
|
|
23
|
+
flow;
|
|
24
|
+
answers = {};
|
|
25
|
+
skipped = [];
|
|
26
|
+
currentIndex = -1;
|
|
27
|
+
startedAt = null;
|
|
28
|
+
completedAt = null;
|
|
29
|
+
/**
|
|
30
|
+
* Create a new elicitation engine
|
|
31
|
+
* @param flow The elicitation flow to execute
|
|
32
|
+
*/
|
|
33
|
+
constructor(flow) {
|
|
34
|
+
this.flow = flow;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Start the elicitation flow
|
|
38
|
+
* @returns The first step to present to the user, or null if no valid steps
|
|
39
|
+
*/
|
|
40
|
+
start() {
|
|
41
|
+
this.startedAt = new Date();
|
|
42
|
+
this.currentIndex = 0;
|
|
43
|
+
return this.getNextValidStep(0);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Record an answer and move to the next step
|
|
47
|
+
* @param stepId The step ID being answered
|
|
48
|
+
* @param value The user's answer
|
|
49
|
+
* @returns The next step, or null if flow is complete
|
|
50
|
+
*/
|
|
51
|
+
answer(stepId, value) {
|
|
52
|
+
const step = this.flow.steps.find(s => s.id === stepId);
|
|
53
|
+
if (!step) {
|
|
54
|
+
throw new Error(`Step not found: ${stepId}`);
|
|
55
|
+
}
|
|
56
|
+
// Validate required fields
|
|
57
|
+
if (step.required && (value === null || value === undefined || value === '')) {
|
|
58
|
+
throw new Error(`Step '${stepId}' is required`);
|
|
59
|
+
}
|
|
60
|
+
// Run custom validation if present
|
|
61
|
+
if (step.validate) {
|
|
62
|
+
const validationResult = step.validate(value);
|
|
63
|
+
if (validationResult !== true) {
|
|
64
|
+
const errorMessage = typeof validationResult === 'string'
|
|
65
|
+
? validationResult
|
|
66
|
+
: `Validation failed for step '${stepId}'`;
|
|
67
|
+
throw new Error(errorMessage);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Store the answer
|
|
71
|
+
this.answers[stepId] = value;
|
|
72
|
+
// Move to next step
|
|
73
|
+
const currentStepIndex = this.flow.steps.findIndex(s => s.id === stepId);
|
|
74
|
+
if (currentStepIndex === -1) {
|
|
75
|
+
throw new Error(`Step not found in flow: ${stepId}`);
|
|
76
|
+
}
|
|
77
|
+
this.currentIndex = currentStepIndex + 1;
|
|
78
|
+
// Check if we've completed all steps
|
|
79
|
+
if (this.currentIndex >= this.flow.steps.length) {
|
|
80
|
+
this.completedAt = new Date();
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return this.getNextValidStep(this.currentIndex);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Skip a step (only allowed if not required)
|
|
87
|
+
* @param stepId The step ID to skip
|
|
88
|
+
* @returns The next step, or null if flow is complete
|
|
89
|
+
*/
|
|
90
|
+
skip(stepId) {
|
|
91
|
+
const step = this.flow.steps.find(s => s.id === stepId);
|
|
92
|
+
if (!step) {
|
|
93
|
+
throw new Error(`Step not found: ${stepId}`);
|
|
94
|
+
}
|
|
95
|
+
if (step.required) {
|
|
96
|
+
throw new Error(`Cannot skip required step: ${stepId}`);
|
|
97
|
+
}
|
|
98
|
+
// Mark as skipped
|
|
99
|
+
this.skipped.push(stepId);
|
|
100
|
+
// Move to next step
|
|
101
|
+
const currentStepIndex = this.flow.steps.findIndex(s => s.id === stepId);
|
|
102
|
+
if (currentStepIndex === -1) {
|
|
103
|
+
throw new Error(`Step not found in flow: ${stepId}`);
|
|
104
|
+
}
|
|
105
|
+
this.currentIndex = currentStepIndex + 1;
|
|
106
|
+
// Check if we've completed all steps
|
|
107
|
+
if (this.currentIndex >= this.flow.steps.length) {
|
|
108
|
+
this.completedAt = new Date();
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return this.getNextValidStep(this.currentIndex);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the current step
|
|
115
|
+
* @returns The current step, or null if not started or completed
|
|
116
|
+
*/
|
|
117
|
+
getCurrentStep() {
|
|
118
|
+
if (this.currentIndex < 0 || this.currentIndex >= this.flow.steps.length) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return this.flow.steps[this.currentIndex];
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if the flow is complete
|
|
125
|
+
* @returns True if all steps have been processed
|
|
126
|
+
*/
|
|
127
|
+
isComplete() {
|
|
128
|
+
return this.completedAt !== null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the elicitation result
|
|
132
|
+
* @returns The complete result with answers and metadata
|
|
133
|
+
*/
|
|
134
|
+
getResult() {
|
|
135
|
+
if (!this.startedAt) {
|
|
136
|
+
throw new Error('Elicitation has not been started');
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
flowId: this.flow.id,
|
|
140
|
+
answers: { ...this.answers },
|
|
141
|
+
metadata: {
|
|
142
|
+
startedAt: this.startedAt,
|
|
143
|
+
completedAt: this.completedAt || new Date(),
|
|
144
|
+
skipped: [...this.skipped],
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Find the next valid step, skipping conditional steps that don't match
|
|
150
|
+
* @param startIndex Index to start searching from
|
|
151
|
+
* @returns The next valid step, or null if flow is complete
|
|
152
|
+
*/
|
|
153
|
+
getNextValidStep(startIndex) {
|
|
154
|
+
for (let i = startIndex; i < this.flow.steps.length; i++) {
|
|
155
|
+
const step = this.flow.steps[i];
|
|
156
|
+
// Check if step has a condition
|
|
157
|
+
if (step.condition) {
|
|
158
|
+
const shouldShow = step.condition(this.answers);
|
|
159
|
+
if (!shouldShow) {
|
|
160
|
+
// Skip this step
|
|
161
|
+
this.skipped.push(step.id);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Found a valid step
|
|
166
|
+
this.currentIndex = i;
|
|
167
|
+
return step;
|
|
168
|
+
}
|
|
169
|
+
// No more valid steps - mark as complete and return null
|
|
170
|
+
this.completedAt = new Date();
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/elicitation/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Elicitation Flows
|
|
3
|
+
* Provides pre-configured flows for common spec creation scenarios
|
|
4
|
+
*/
|
|
5
|
+
import type { ElicitationFlow } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Quick flow: 5 essential questions for rapid spec creation
|
|
8
|
+
*/
|
|
9
|
+
export declare const quickFlow: ElicitationFlow;
|
|
10
|
+
/**
|
|
11
|
+
* Full flow: Comprehensive spec elicitation (15+ steps)
|
|
12
|
+
*/
|
|
13
|
+
export declare const fullFlow: ElicitationFlow;
|
|
14
|
+
/**
|
|
15
|
+
* EARS flow: EARS-focused requirement elicitation
|
|
16
|
+
*/
|
|
17
|
+
export declare const earsFlow: ElicitationFlow;
|
|
18
|
+
//# sourceMappingURL=flows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../src/elicitation/flows.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,eA6DvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,eA6HtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,eAgItB,CAAC"}
|