File size: 2,754 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
import type { InterviewTemplate, InterviewItem, InterviewHandoff } from './types';

export interface ItemForm { question: string; criteria: string; required: boolean; }
export interface TemplateForm {
  title: string;
  description: string;
  artifactPath: string;
  items: ItemForm[];
  handoff: { label: string; prompt: string; mode: 'code' | 'chat' } | null;
}

/** Slug used for template ids and item ids. Mirrors SkillsService.slugify. */
export function slugify(name: string): string {
  return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
}

export function emptyForm(): TemplateForm {
  return {
    title: '',
    description: '',
    artifactPath: '/.interviews/untitled.md',
    items: [{ question: '', criteria: '', required: true }],
    handoff: null,
  };
}

export function templateToForm(t: InterviewTemplate): TemplateForm {
  return {
    title: t.title,
    description: t.description,
    artifactPath: t.artifacts[0]?.path ?? '/.interviews/untitled.md',
    items: t.items.map(i => ({
      question: i.elicit,
      criteria: i.completion.find(a => a.type === 'judge')?.criteria ?? '',
      required: i.required !== false,
    })),
    handoff: t.handoff ? { label: t.handoff.label, prompt: t.handoff.prompt, mode: t.handoff.mode } : null,
  };
}

export function formToTemplate(f: TemplateForm, id: string): InterviewTemplate {
  const items: InterviewItem[] = f.items.map(i => ({
    id: slugify(i.question) || 'item',
    elicit: i.question.trim(),
    required: i.required,
    completion: [{ type: 'judge' as const, criteria: i.criteria.trim(), description: i.question.trim() }],
  }));
  const handoff: InterviewHandoff | undefined = f.handoff && f.handoff.label.trim()
    ? { label: f.handoff.label.trim(), prompt: f.handoff.prompt.trim(), mode: f.handoff.mode }
    : undefined;
  return {
    id,
    title: f.title.trim(),
    description: f.description.trim(),
    artifacts: [{ path: f.artifactPath.trim() }],
    items,
    ...(handoff ? { handoff } : {}),
  };
}

/** Returns an error message, or null if valid. */
export function validateTemplateForm(f: TemplateForm): string | null {
  if (!f.title.trim()) return 'Give the template a title.';
  if (!f.artifactPath.startsWith('/.interviews/')) return 'The artifact path must be under /.interviews/.';
  if (!f.artifactPath.endsWith('.md')) return 'The artifact path must end in .md.';
  if (f.artifactPath.includes('..')) return 'The artifact path cannot contain "..".';
  if (f.items.length === 0) return 'Add at least one item.';
  for (const i of f.items) {
    if (!i.question.trim()) return 'Every item needs a question.';
    if (!i.criteria.trim()) return 'Every item needs a "done when" criteria.';
  }
  return null;
}