profileShare

rasmusjy / voicetask

Read-only snapshot

No repository description.

main default branch 105 files Expires Sep 13, 2026, 9:06 AM
llmMock.ts 2,667 bytes
1 import { CATEGORY_IDS, type Coverage, type InterviewTurn } from '../../shared/types'
2 import type { GenerateFileRequest, InterviewContext, InterviewLlm } from './types'
3
4 export function createLlmMock(): InterviewLlm {
5 return {
6 async nextTurn(context: InterviewContext): Promise<InterviewTurn> {
7 return mockNextTurn(context)
8 },
9 async generateFile(request: GenerateFileRequest): Promise<string> {
10 return mockFileContent(request.file)
11 },
12 }
13 }
14
15 function mockNextTurn(context: InterviewContext): InterviewTurn {
16 const lastUserSegment = [...context.segments].reverse().find((s) => s.speaker === 'user')
17 if (lastUserSegment && lastUserSegment.text.trim().toLowerCase() === 'done') {
18 return {
19 coverage: context.coverage,
20 nextQuestion: 'Understood, wrapping up the interview.',
21 contradiction: null,
22 done: true,
23 summaryUpdate: null,
24 }
25 }
26
27 const clearedCount = CATEGORY_IDS.filter((category) => context.coverage[category] === 'clear').length
28 const categoryToClear = CATEGORY_IDS[clearedCount]
29
30 if (!categoryToClear) {
31 return {
32 coverage: context.coverage,
33 nextQuestion: 'All categories covered, ready to generate the spec pack.',
34 contradiction: null,
35 done: true,
36 summaryUpdate: null,
37 }
38 }
39
40 const coverage: Coverage = { ...context.coverage, [categoryToClear]: 'clear' }
41 const nextCategory = CATEGORY_IDS[clearedCount + 1]
42 const done = nextCategory === undefined
43
44 return {
45 coverage,
46 nextQuestion: done
47 ? 'All categories covered, ready to generate the spec pack.'
48 : `[mock] Tell me about ${nextCategory}.`,
49 contradiction: null,
50 done,
51 summaryUpdate: null,
52 }
53 }
54
55 function mockFileContent(file: GenerateFileRequest['file']): string {
56 switch (file) {
57 case 'SPEC.md':
58 return [
59 '# SPEC: Mock',
60 '',
61 '## Functional requirements',
62 '',
63 '- FR-001: The mock system does the first thing. [S1]',
64 '- FR-002: The mock system does the second thing. [S999]',
65 '',
66 ].join('\n')
67 case 'PLAN.md':
68 return '# PLAN: Mock\n\nMinimal mock plan.\n'
69 case 'TASKS.md':
70 return [
71 '# TASKS: Mock',
72 '',
73 '- [ ] T1 Mock task',
74 ' - Verify: `npm test` exits 0.',
75 '',
76 ].join('\n')
77 case 'VERIFICATION.md':
78 return '# VERIFICATION: Mock\n\nRun `npm test`.\n'
79 case 'HANDOFF.md':
80 return [
81 '# HANDOFF: Mock',
82 '',
83 'Launch a sandboxed session with:',
84 '',
85 '```',
86 'claude "Work through spec/TASKS.md in order."',
87 '```',
88 '',
89 ].join('\n')
90 }
91 }
92