File size: 5,599 Bytes
391c43e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { describe, it, expect } from 'vitest';
import { listInterviewTemplates, getInterviewTemplate, filterInterviewTemplates, isBuiltInInterviewTemplateId } from '../templates';
import { renderInterviewAgenda, withInterviewAgenda } from '../agenda';

describe('interview template registry', () => {
  it('exposes the built-in templates by id', () => {
    expect(getInterviewTemplate('understand-company')?.title).toBe('Understand a company');
    expect(getInterviewTemplate('plan-feature')?.title).toBe('Plan a feature');
    expect(getInterviewTemplate('nope')).toBeUndefined();
    expect(listInterviewTemplates().length).toBeGreaterThanOrEqual(2);
  });

  it('exposes the website and publish templates with their artifacts and handoffs', () => {
    const site = getInterviewTemplate('plan-website');
    expect(site?.title).toBe('Plan a website');
    expect(site?.artifacts[0].path).toBe('/.interviews/site-plan.md');
    expect(site?.handoff?.mode).toBe('code');

    const pub = getInterviewTemplate('prepare-publish');
    expect(pub?.title).toBe('Get ready to publish');
    expect(pub?.artifacts[0].path).toBe('/.interviews/publish-checklist.md');
    expect(pub?.handoff?.mode).toBe('code');
  });

  it('every built-in item is well-formed (id, elicit, completion)', () => {
    for (const t of listInterviewTemplates()) {
      expect(t.artifacts.length).toBeGreaterThan(0);
      expect(t.items.length).toBeGreaterThan(0);
      for (const item of t.items) {
        expect(item.id).toBeTruthy();
        expect(item.elicit).toBeTruthy();
        expect(item.completion.length).toBeGreaterThan(0);
      }
    }
  });
});

describe('renderInterviewAgenda', () => {
  it('renders the title, artifact target, and each item elicit', () => {
    const t = getInterviewTemplate('understand-company')!;
    const agenda = renderInterviewAgenda(t);
    expect(agenda).toContain('Understand a company');
    expect(agenda).toContain('/.interviews/company-profile.md');
    for (const item of t.items) {
      expect(agenda).toContain(item.elicit);
    }
  });

  it('marks optional items', () => {
    const agenda = renderInterviewAgenda(getInterviewTemplate('understand-company')!);
    expect(agenda).toMatch(/optional/i);
  });

  it('does not leak raw completion assertions to the agent', () => {
    const t = getInterviewTemplate('plan-feature')!;
    const agenda = renderInterviewAgenda(t);
    expect(agenda).not.toContain('judge');
    for (const item of t.items) {
      for (const assertion of item.completion) {
        if (assertion.type === 'judge') {
          expect(agenda).not.toContain(assertion.criteria);
        }
      }
    }
  });
});

describe('filterInterviewTemplates', () => {
  const all = listInterviewTemplates();

  it('returns all templates for an empty or whitespace query', () => {
    expect(filterInterviewTemplates(all, '')).toHaveLength(all.length);
    expect(filterInterviewTemplates(all, '   ')).toHaveLength(all.length);
  });

  it('matches on title, case-insensitively', () => {
    const ids = filterInterviewTemplates(all, 'COMPANY').map(t => t.id);
    expect(ids).toContain('understand-company');
    expect(ids).not.toContain('plan-feature');
  });

  it('matches on description', () => {
    const ids = filterInterviewTemplates(all, 'spec').map(t => t.id);
    expect(ids).toContain('plan-feature');
    expect(ids).not.toContain('understand-company');
  });

  it('returns empty when nothing matches', () => {
    expect(filterInterviewTemplates(all, 'zzzzz')).toHaveLength(0);
  });
});

describe('isBuiltInInterviewTemplateId', () => {
  it('is true for built-ins and false otherwise', () => {
    expect(isBuiltInInterviewTemplateId('understand-company')).toBe(true);
    expect(isBuiltInInterviewTemplateId('my-custom-one')).toBe(false);
  });
});

describe('withInterviewAgenda', () => {
  it('appends the rendered agenda when the template id resolves', () => {
    const base = 'SYSTEM PROMPT';
    const result = withInterviewAgenda(base, 'understand-company');
    expect(result.startsWith(base)).toBe(true);
    expect(result).toContain('Understand a company');
    expect(result).toContain('/.interviews/company-profile.md');
    expect(result.length).toBeGreaterThan(base.length);
  });

  it('returns the prompt unchanged when no template id is given', () => {
    expect(withInterviewAgenda('SYSTEM PROMPT', undefined)).toBe('SYSTEM PROMPT');
  });

  it('returns the prompt unchanged when the template id is unknown', () => {
    expect(withInterviewAgenda('SYSTEM PROMPT', 'no-such-template')).toBe('SYSTEM PROMPT');
  });

  it('appends the agenda for a custom template passed in directly (not in the built-in registry)', () => {
    const base = 'SYSTEM PROMPT';
    const custom = {
      id: 'image-generation',
      title: 'Image generation',
      description: 'Creates an image generation prompt',
      artifacts: [{ path: '/.interviews/image-generation.md' }],
      items: [{ id: 'type', elicit: 'What type of image', completion: [{ type: 'judge' as const, criteria: 'The type of image is clear', description: 'type' }] }],
    };
    // The id does not resolve in the built-in registry, so only the passed object can supply the agenda.
    expect(withInterviewAgenda(base, 'image-generation')).toBe(base);
    const result = withInterviewAgenda(base, 'image-generation', custom);
    expect(result.startsWith(base)).toBe(true);
    expect(result).toContain('Image generation');
    expect(result).toContain('/.interviews/image-generation.md');
    expect(result).toContain('What type of image');
  });
});