zouroboros-workflow 2.0.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.
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Stage 1: Mechanical verification
3
+ */
4
+ import { existsSync, readFileSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { execSync } from 'child_process';
7
+ /**
8
+ * Run mechanical checks on an artifact
9
+ */
10
+ export function runMechanicalChecks(artifactPath) {
11
+ const checks = [];
12
+ const hasPackageJson = existsSync(join(artifactPath, 'package.json'));
13
+ const hasTsConfig = existsSync(join(artifactPath, 'tsconfig.json'));
14
+ const hasPyFiles = hasPythonFiles(artifactPath);
15
+ // TypeScript checks
16
+ if (hasTsConfig) {
17
+ checks.push(checkTypeScript(artifactPath));
18
+ }
19
+ // Lint check
20
+ if (hasPackageJson) {
21
+ checks.push(checkLint(artifactPath));
22
+ checks.push(checkTests(artifactPath));
23
+ }
24
+ // Python checks
25
+ if (hasPyFiles) {
26
+ checks.push(checkPythonSyntax(artifactPath));
27
+ }
28
+ return checks.filter(Boolean);
29
+ }
30
+ function checkTypeScript(artifactPath) {
31
+ try {
32
+ execSync(`cd "${artifactPath}" && npx tsc --noEmit 2>&1`, {
33
+ encoding: 'utf-8',
34
+ timeout: 30000,
35
+ });
36
+ return { name: 'TypeScript compile', passed: true, detail: 'No type errors' };
37
+ }
38
+ catch (e) {
39
+ const output = e.stdout || e.message || 'Unknown error';
40
+ const errorLines = output.split('\n').filter((l) => l.includes('error TS')).slice(0, 5);
41
+ return {
42
+ name: 'TypeScript compile',
43
+ passed: false,
44
+ detail: errorLines.join('; ') || 'Compilation failed',
45
+ };
46
+ }
47
+ }
48
+ function checkLint(artifactPath) {
49
+ try {
50
+ const pkg = JSON.parse(readFileSync(join(artifactPath, 'package.json'), 'utf-8'));
51
+ if (!pkg.scripts?.lint) {
52
+ return { name: 'Lint', passed: true, detail: 'No lint script configured' };
53
+ }
54
+ execSync(`cd "${artifactPath}" && npm run lint 2>&1`, {
55
+ encoding: 'utf-8',
56
+ timeout: 30000,
57
+ });
58
+ return { name: 'Lint', passed: true, detail: 'No lint errors' };
59
+ }
60
+ catch (e) {
61
+ const output = (e.stdout || '').split('\n').slice(-5).join('; ');
62
+ return { name: 'Lint', passed: false, detail: output || 'Lint failed' };
63
+ }
64
+ }
65
+ function checkTests(artifactPath) {
66
+ try {
67
+ const pkg = JSON.parse(readFileSync(join(artifactPath, 'package.json'), 'utf-8'));
68
+ if (!pkg.scripts?.test) {
69
+ return { name: 'Tests', passed: true, detail: 'No test script configured' };
70
+ }
71
+ const result = execSync(`cd "${artifactPath}" && npm test 2>&1`, {
72
+ encoding: 'utf-8',
73
+ timeout: 60000,
74
+ });
75
+ const passMatch = result.match(/(\d+)\s*(?:tests?\s*)?pass/i);
76
+ return {
77
+ name: 'Tests',
78
+ passed: true,
79
+ detail: passMatch ? `${passMatch[1]} passing` : 'All tests passed',
80
+ };
81
+ }
82
+ catch (e) {
83
+ const output = (e.stdout || '').split('\n').slice(-5).join('; ');
84
+ return { name: 'Tests', passed: false, detail: output || 'Tests failed' };
85
+ }
86
+ }
87
+ function checkPythonSyntax(artifactPath) {
88
+ try {
89
+ execSync(`cd "${artifactPath}" && python3 -m py_compile $(find . -name "*.py" -maxdepth 3 | head -10 | tr '\\n' ' ') 2>&1`, { encoding: 'utf-8', timeout: 15000 });
90
+ return { name: 'Python syntax', passed: true, detail: 'No syntax errors' };
91
+ }
92
+ catch (e) {
93
+ return { name: 'Python syntax', passed: false, detail: 'Syntax errors found' };
94
+ }
95
+ }
96
+ function hasPythonFiles(artifactPath) {
97
+ try {
98
+ const result = execSync(`find "${artifactPath}" -name "*.py" -maxdepth 3 | head -1`, {
99
+ encoding: 'utf-8',
100
+ });
101
+ return result.trim().length > 0;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Stage 2: Semantic evaluation
3
+ */
4
+ import type { SeedSpec, SemanticResult } from './types.js';
5
+ /**
6
+ * Parse a seed specification from YAML content
7
+ */
8
+ export declare function parseSeed(seedPath: string): SeedSpec;
9
+ /**
10
+ * Run semantic evaluation against acceptance criteria
11
+ */
12
+ export declare function runSemanticEvaluation(seed: SeedSpec, artifactPath: string): SemanticResult;
13
+ export declare function calculateDrift(seed: SeedSpec, artifactPath: string): number;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Stage 2: Semantic evaluation
3
+ */
4
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
5
+ import { join } from 'path';
6
+ /**
7
+ * Parse a seed specification from YAML content
8
+ */
9
+ export function parseSeed(seedPath) {
10
+ const content = readFileSync(seedPath, 'utf-8');
11
+ const spec = {};
12
+ const goalMatch = content.match(/^goal:\s*"?(.+?)"?\s*$/m);
13
+ if (goalMatch)
14
+ spec.goal = goalMatch[1];
15
+ spec.constraints = [];
16
+ spec.acceptanceCriteria = [];
17
+ const lines = content.split('\n');
18
+ let inConstraints = false;
19
+ let inAC = false;
20
+ for (const line of lines) {
21
+ if (line.match(/^constraints:/)) {
22
+ inConstraints = true;
23
+ inAC = false;
24
+ continue;
25
+ }
26
+ if (line.match(/^acceptance_criteria:/)) {
27
+ inAC = true;
28
+ inConstraints = false;
29
+ continue;
30
+ }
31
+ if (line.match(/^[a-z_]+:/) && !line.startsWith(' ')) {
32
+ inConstraints = false;
33
+ inAC = false;
34
+ continue;
35
+ }
36
+ const itemMatch = line.match(/^\s+-\s+"?(.+?)"?\s*$/);
37
+ if (itemMatch) {
38
+ if (inConstraints)
39
+ spec.constraints.push(itemMatch[1]);
40
+ if (inAC)
41
+ spec.acceptanceCriteria.push(itemMatch[1]);
42
+ }
43
+ }
44
+ return spec;
45
+ }
46
+ /**
47
+ * Run semantic evaluation against acceptance criteria
48
+ */
49
+ export function runSemanticEvaluation(seed, artifactPath) {
50
+ const criteria = [];
51
+ for (const ac of seed.acceptanceCriteria || []) {
52
+ const evidence = searchForEvidence(ac, artifactPath);
53
+ criteria.push({
54
+ name: ac,
55
+ met: evidence.found,
56
+ evidence: evidence.details,
57
+ });
58
+ }
59
+ const metCount = criteria.filter((c) => c.met).length;
60
+ const totalCount = criteria.length || 1;
61
+ const acCompliance = metCount / totalCount;
62
+ // Calculate goal alignment (simplified)
63
+ const goalAlignment = calculateGoalAlignment(seed, artifactPath);
64
+ // Calculate drift score
65
+ const driftScore = calculateDrift(seed, artifactPath);
66
+ // Overall score
67
+ const overallScore = 0.5 * goalAlignment + 0.3 * acCompliance + 0.2 * (1 - driftScore);
68
+ const recommendations = [];
69
+ if (acCompliance < 0.8) {
70
+ const unmet = criteria.filter(c => !c.met).map(c => c.name);
71
+ recommendations.push(`Unmet acceptance criteria: ${unmet.join(', ')}`);
72
+ }
73
+ if (goalAlignment < 0.7) {
74
+ recommendations.push('Goal alignment is low — verify implementation matches the stated objective');
75
+ }
76
+ if (driftScore > 0.3) {
77
+ recommendations.push('Significant drift detected — review scope changes against original spec');
78
+ }
79
+ return {
80
+ acCompliance,
81
+ goalAlignment,
82
+ driftScore,
83
+ overallScore,
84
+ criteria,
85
+ recommendations,
86
+ passed: overallScore >= 0.8,
87
+ };
88
+ }
89
+ function searchForEvidence(criterion, artifactPath) {
90
+ const keywords = criterion.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
91
+ let foundCount = 0;
92
+ let searchedFiles = 0;
93
+ function searchDir(dir, depth = 0) {
94
+ if (depth > 3)
95
+ return;
96
+ try {
97
+ const entries = readdirSync(dir);
98
+ for (const entry of entries) {
99
+ if (entry.startsWith('.') || entry === 'node_modules')
100
+ continue;
101
+ const fullPath = join(dir, entry);
102
+ const stat = statSync(fullPath);
103
+ if (stat.isDirectory()) {
104
+ searchDir(fullPath, depth + 1);
105
+ }
106
+ else if (stat.isFile() && isCodeFile(entry)) {
107
+ searchedFiles++;
108
+ try {
109
+ const content = readFileSync(fullPath, 'utf-8').toLowerCase();
110
+ const matches = keywords.filter((kw) => content.includes(kw)).length;
111
+ if (matches >= Math.ceil(keywords.length / 2)) {
112
+ foundCount++;
113
+ }
114
+ }
115
+ catch {
116
+ // Skip unreadable files
117
+ }
118
+ }
119
+ }
120
+ }
121
+ catch {
122
+ // Skip inaccessible directories
123
+ }
124
+ }
125
+ if (existsSync(artifactPath)) {
126
+ const stat = statSync(artifactPath);
127
+ if (stat.isDirectory()) {
128
+ searchDir(artifactPath);
129
+ }
130
+ else if (stat.isFile()) {
131
+ searchedFiles = 1;
132
+ try {
133
+ const content = readFileSync(artifactPath, 'utf-8').toLowerCase();
134
+ const matches = keywords.filter((kw) => content.includes(kw)).length;
135
+ if (matches >= Math.ceil(keywords.length / 2)) {
136
+ foundCount++;
137
+ }
138
+ }
139
+ catch {
140
+ // Skip
141
+ }
142
+ }
143
+ }
144
+ return {
145
+ found: foundCount > 0,
146
+ details: searchedFiles > 0
147
+ ? `Found evidence in ${foundCount} of ${searchedFiles} searched files`
148
+ : 'No files searched',
149
+ };
150
+ }
151
+ function isCodeFile(filename) {
152
+ const codeExts = ['.ts', '.js', '.tsx', '.jsx', '.py', '.go', '.rs', '.java', '.cpp', '.c', '.h', '.md'];
153
+ return codeExts.some((ext) => filename.endsWith(ext));
154
+ }
155
+ function calculateGoalAlignment(seed, artifactPath) {
156
+ // Simplified goal alignment check
157
+ if (!seed.goal)
158
+ return 0.5;
159
+ const goalKeywords = seed.goal.toLowerCase().split(/\s+/).filter((w) => w.length > 4);
160
+ const evidence = searchForEvidence(seed.goal, artifactPath);
161
+ return evidence.found ? 0.9 : 0.5;
162
+ }
163
+ export function calculateDrift(seed, artifactPath) {
164
+ // Simplified drift calculation
165
+ // In a full implementation, this would compare ontology fields, constraints, etc.
166
+ return 0.1; // Assume low drift for now
167
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Types for three-stage evaluation
3
+ */
4
+ export interface MechanicalCheck {
5
+ name: string;
6
+ passed: boolean;
7
+ detail: string;
8
+ }
9
+ export interface SemanticCriterion {
10
+ name: string;
11
+ met: boolean;
12
+ evidence: string;
13
+ }
14
+ export interface SemanticResult {
15
+ acCompliance: number;
16
+ goalAlignment: number;
17
+ driftScore: number;
18
+ overallScore: number;
19
+ criteria: SemanticCriterion[];
20
+ recommendations: string[];
21
+ passed: boolean;
22
+ }
23
+ export interface ConsensusVote {
24
+ perspective: 'proposer' | 'devils_advocate' | 'synthesizer';
25
+ verdict: 'approve' | 'reject';
26
+ reasoning: string;
27
+ }
28
+ export interface ConsensusResult {
29
+ votes: ConsensusVote[];
30
+ finalVerdict: 'approve' | 'reject';
31
+ confidence: number;
32
+ }
33
+ export interface EvaluationReport {
34
+ id: string;
35
+ seedId: string;
36
+ artifactPath: string;
37
+ timestamp: string;
38
+ stage1: {
39
+ passed: boolean;
40
+ checks: MechanicalCheck[];
41
+ };
42
+ stage2: SemanticResult | null;
43
+ stage3: ConsensusResult | null;
44
+ decision: 'approved' | 'needs_work' | 'rejected';
45
+ recommendations: string[];
46
+ }
47
+ export interface SeedSpec {
48
+ id?: string;
49
+ goal?: string;
50
+ constraints?: string[];
51
+ acceptanceCriteria?: string[];
52
+ evaluationPrinciples?: Array<{
53
+ name: string;
54
+ description: string;
55
+ weight: number;
56
+ }>;
57
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Types for three-stage evaluation
3
+ */
4
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Zouroboros Workflow
3
+ *
4
+ * Spec-first development tools: interview, evaluation, unstuck, and autoloop.
5
+ *
6
+ * @module zouroboros-workflow
7
+ */
8
+ export { scoreAmbiguity, } from './interview/ambiguity.js';
9
+ export { generateSeed, formatSeedYAML, } from './interview/seed.js';
10
+ export type { InterviewConfig, AmbiguityScore, SeedSpecification } from './interview/types.js';
11
+ export { runMechanicalChecks } from './evaluate/mechanical.js';
12
+ export { parseSeed, runSemanticEvaluation, calculateDrift } from './evaluate/semantic.js';
13
+ export type { MechanicalCheck, SeedSpec, SemanticResult, EvaluationReport } from './evaluate/types.js';
14
+ export { autoSelectPersona, getStrategy, getAllPersonas, STRATEGIES } from './unstuck/strategies.js';
15
+ export type { UnstuckPersona, UnstuckStrategy, UnstuckSession, AutoSelectResult } from './unstuck/types.js';
16
+ export { parseProgram, validateProgram } from './autoloop/parser.js';
17
+ export { initState, shouldContinue, isBetter, runExperiment, saveResults, getStagnationLevel, getStagnationModifier } from './autoloop/loop.js';
18
+ export type { ProgramConfig, MetricConfig, ConstraintConfig, StagnationConfig, ExperimentRecord, LoopState, LoopStatus } from './autoloop/types.js';
19
+ export declare const VERSION = "2.0.0";
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Zouroboros Workflow
3
+ *
4
+ * Spec-first development tools: interview, evaluation, unstuck, and autoloop.
5
+ *
6
+ * @module zouroboros-workflow
7
+ */
8
+ // Spec-First Interview
9
+ export { scoreAmbiguity, } from './interview/ambiguity.js';
10
+ export { generateSeed, formatSeedYAML, } from './interview/seed.js';
11
+ // Three-Stage Evaluation
12
+ export { runMechanicalChecks } from './evaluate/mechanical.js';
13
+ export { parseSeed, runSemanticEvaluation, calculateDrift } from './evaluate/semantic.js';
14
+ // Unstuck Lateral
15
+ export { autoSelectPersona, getStrategy, getAllPersonas, STRATEGIES } from './unstuck/strategies.js';
16
+ // Autoloop
17
+ export { parseProgram, validateProgram } from './autoloop/parser.js';
18
+ export { initState, shouldContinue, isBetter, runExperiment, saveResults, getStagnationLevel, getStagnationModifier } from './autoloop/loop.js';
19
+ // Version
20
+ export const VERSION = '2.0.0';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Ambiguity scoring for spec-first interview
3
+ */
4
+ import type { AmbiguityScore } from './types.js';
5
+ /**
6
+ * Score the ambiguity of a request
7
+ * Returns goal clarity, constraint clarity, success criteria clarity, and overall ambiguity
8
+ */
9
+ export declare function scoreAmbiguity(request: string): AmbiguityScore;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Ambiguity scoring for spec-first interview
3
+ */
4
+ /**
5
+ * Score the ambiguity of a request
6
+ * Returns goal clarity, constraint clarity, success criteria clarity, and overall ambiguity
7
+ */
8
+ export function scoreAmbiguity(request) {
9
+ let goal = 0;
10
+ let constraints = 0;
11
+ let success = 0;
12
+ const words = request.toLowerCase().split(/\s+/);
13
+ const len = words.length;
14
+ // Goal clarity signals
15
+ const goalSignals = ['build', 'create', 'implement', 'add', 'fix', 'migrate', 'refactor', 'deploy', 'integrate'];
16
+ const hasVerb = goalSignals.some((s) => words.includes(s));
17
+ const hasObject = len > 2;
18
+ const hasSpecificity = len > 6;
19
+ goal = (hasVerb ? 0.3 : 0) + (hasObject ? 0.3 : 0.1) + (hasSpecificity ? 0.4 : 0.1);
20
+ // Constraint signals
21
+ const constraintWords = ['must', 'should', 'only', 'without', 'using', 'in', 'with', 'no', 'cannot', 'limit'];
22
+ const constraintCount = constraintWords.filter((w) => words.includes(w)).length;
23
+ constraints = Math.min(1.0, constraintCount * 0.25);
24
+ // Success criteria signals
25
+ const successWords = ['when', 'so that', 'passes', 'returns', 'displays', 'sends', 'saves', 'validates'];
26
+ const successCount = successWords.filter((w) => request.toLowerCase().includes(w)).length;
27
+ success = Math.min(1.0, successCount * 0.3);
28
+ // Vagueness penalties
29
+ const vagueWords = ['better', 'faster', 'improve', 'optimize', 'fix', 'update', 'change', 'nice', 'good'];
30
+ const vagueCount = vagueWords.filter((w) => words.includes(w)).length;
31
+ const vaguePenalty = vagueCount * 0.15;
32
+ goal = Math.max(0, Math.min(1, goal - vaguePenalty * 0.5));
33
+ constraints = Math.max(0, Math.min(1, constraints));
34
+ success = Math.max(0, Math.min(1, success));
35
+ const ambiguity = +(1 - (goal * 0.4 + constraints * 0.3 + success * 0.3)).toFixed(2);
36
+ let assessment;
37
+ if (ambiguity <= 0.2) {
38
+ assessment = 'READY — Ambiguity is low enough to proceed to seed generation.';
39
+ }
40
+ else if (ambiguity <= 0.5) {
41
+ assessment = 'NEEDS CLARIFICATION — Run a Socratic interview to fill gaps.';
42
+ }
43
+ else {
44
+ assessment = 'HIGH AMBIGUITY — Significant interview required before any implementation.';
45
+ }
46
+ return {
47
+ goal: +goal.toFixed(2),
48
+ constraints: +constraints.toFixed(2),
49
+ success: +success.toFixed(2),
50
+ ambiguity,
51
+ assessment,
52
+ };
53
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Seed specification generator
3
+ */
4
+ import type { SeedSpecification } from './types.js';
5
+ /**
6
+ * Generate a seed specification from interview notes
7
+ */
8
+ export declare function generateSeed(topic: string, notesPath?: string, interviewNotes?: string): SeedSpecification;
9
+ /**
10
+ * Format seed as YAML string
11
+ */
12
+ export declare function formatSeedYAML(seed: SeedSpecification): string;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Seed specification generator
3
+ */
4
+ import { randomUUID } from 'crypto';
5
+ import { existsSync, readFileSync } from 'fs';
6
+ /**
7
+ * Generate a seed specification from interview notes
8
+ */
9
+ export function generateSeed(topic, notesPath, interviewNotes) {
10
+ const id = `seed-${randomUUID().slice(0, 8)}`;
11
+ const now = new Date().toISOString();
12
+ let notes = interviewNotes || '';
13
+ if (notesPath && existsSync(notesPath)) {
14
+ notes = readFileSync(notesPath, 'utf-8');
15
+ }
16
+ // Extract information from notes if available
17
+ const constraints = extractConstraints(notes);
18
+ const acceptanceCriteria = extractAcceptanceCriteria(notes);
19
+ return {
20
+ id,
21
+ created: now,
22
+ status: 'draft',
23
+ goal: topic || 'TODO: Define the primary objective',
24
+ constraints: constraints.length > 0 ? constraints : ['TODO: Add hard constraints from interview'],
25
+ acceptanceCriteria: acceptanceCriteria.length > 0
26
+ ? acceptanceCriteria
27
+ : ['TODO: Add measurable success criteria'],
28
+ ontology: {
29
+ name: 'TODO',
30
+ description: 'TODO: Describe the domain model',
31
+ fields: [
32
+ { name: 'id', type: 'string', description: 'Unique identifier' },
33
+ ],
34
+ },
35
+ evaluationPrinciples: [
36
+ { name: 'correctness', description: 'Does it do what was asked?', weight: 0.4 },
37
+ { name: 'completeness', description: 'Are all acceptance criteria met?', weight: 0.3 },
38
+ { name: 'quality', description: 'Is the implementation sound?', weight: 0.3 },
39
+ ],
40
+ exitConditions: [
41
+ { name: 'all_ac_met', description: 'All acceptance criteria satisfied', criteria: 'AC compliance = 100%' },
42
+ ],
43
+ };
44
+ }
45
+ /**
46
+ * Format seed as YAML string
47
+ */
48
+ export function formatSeedYAML(seed) {
49
+ const fieldsYAML = seed.ontology.fields
50
+ .map(f => ` - name: ${f.name}\n type: ${f.type}\n description: "${f.description}"`)
51
+ .join('\n');
52
+ const principlesYAML = seed.evaluationPrinciples
53
+ .map(p => ` - name: ${p.name}\n description: "${p.description}"\n weight: ${p.weight}`)
54
+ .join('\n');
55
+ const exitYAML = seed.exitConditions
56
+ .map(e => ` - name: ${e.name}\n description: "${e.description}"\n criteria: "${e.criteria}"`)
57
+ .join('\n');
58
+ return `# Seed Specification
59
+ # Generated: ${seed.created}
60
+ # ID: ${seed.id}
61
+
62
+ id: "${seed.id}"
63
+ created: "${seed.created}"
64
+ status: ${seed.status}
65
+
66
+ goal: "${seed.goal}"
67
+
68
+ constraints:
69
+ ${seed.constraints.map(c => ` - "${c}"`).join('\n')}
70
+
71
+ acceptance_criteria:
72
+ ${seed.acceptanceCriteria.map(ac => ` - "${ac}"`).join('\n')}
73
+
74
+ ontology:
75
+ name: "${seed.ontology.name}"
76
+ description: "${seed.ontology.description}"
77
+ fields:
78
+ ${fieldsYAML}
79
+
80
+ evaluation_principles:
81
+ ${principlesYAML}
82
+
83
+ exit_conditions:
84
+ ${exitYAML}
85
+ `;
86
+ }
87
+ function extractConstraints(notes) {
88
+ const constraints = [];
89
+ const lines = notes.split('\n');
90
+ let inConstraints = false;
91
+ for (const line of lines) {
92
+ if (line.match(/constraint|must|should|limit|restriction/i)) {
93
+ inConstraints = true;
94
+ }
95
+ if (inConstraints && line.trim().startsWith('-')) {
96
+ constraints.push(line.replace(/^-\s*/, '').trim());
97
+ }
98
+ if (line.match(/acceptance|criteria|success/i)) {
99
+ inConstraints = false;
100
+ }
101
+ }
102
+ return constraints;
103
+ }
104
+ function extractAcceptanceCriteria(notes) {
105
+ const criteria = [];
106
+ const lines = notes.split('\n');
107
+ let inCriteria = false;
108
+ for (const line of lines) {
109
+ if (line.match(/acceptance|criteria|success|should.*when/i)) {
110
+ inCriteria = true;
111
+ }
112
+ if (inCriteria && line.trim().startsWith('-')) {
113
+ criteria.push(line.replace(/^-\s*/, '').trim());
114
+ }
115
+ if (line.match(/evaluation|exit|complete/i)) {
116
+ inCriteria = false;
117
+ }
118
+ }
119
+ return criteria;
120
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Types for spec-first interview
3
+ */
4
+ export interface InterviewConfig {
5
+ topic: string;
6
+ outputDir: string;
7
+ maxQuestions?: number;
8
+ }
9
+ export interface AmbiguityScore {
10
+ goal: number;
11
+ constraints: number;
12
+ success: number;
13
+ ambiguity: number;
14
+ assessment: string;
15
+ }
16
+ export interface SeedSpecification {
17
+ id: string;
18
+ created: string;
19
+ status: 'draft' | 'approved' | 'rejected';
20
+ goal: string;
21
+ constraints: string[];
22
+ acceptanceCriteria: string[];
23
+ ontology: {
24
+ name: string;
25
+ description: string;
26
+ fields: Array<{
27
+ name: string;
28
+ type: string;
29
+ description: string;
30
+ }>;
31
+ };
32
+ evaluationPrinciples: Array<{
33
+ name: string;
34
+ description: string;
35
+ weight: number;
36
+ }>;
37
+ exitConditions: Array<{
38
+ name: string;
39
+ description: string;
40
+ criteria: string;
41
+ }>;
42
+ }
43
+ export interface InterviewSession {
44
+ id: string;
45
+ topic: string;
46
+ questions: string[];
47
+ answers: string[];
48
+ timestamp: number;
49
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Types for spec-first interview
3
+ */
4
+ export {};
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Unstuck persona strategies and auto-selection
3
+ */
4
+ import type { UnstuckPersona, UnstuckStrategy, AutoSelectResult } from './types.js';
5
+ /**
6
+ * All available unstuck strategies
7
+ */
8
+ export declare const STRATEGIES: Record<UnstuckPersona, UnstuckStrategy>;
9
+ /**
10
+ * Auto-select the best unstuck persona based on problem description
11
+ */
12
+ export declare function autoSelectPersona(problem: string): AutoSelectResult;
13
+ /**
14
+ * Get strategy for a specific persona
15
+ */
16
+ export declare function getStrategy(persona: UnstuckPersona): UnstuckStrategy;
17
+ /**
18
+ * Get all available personas
19
+ */
20
+ export declare function getAllPersonas(): UnstuckPersona[];