@vibe-interviewing/core 0.1.0 → 0.2.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,389 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const AIRulesSchema: z.ZodObject<{
4
+ /** Role description for the AI assistant */
5
+ role: z.ZodString;
6
+ /** Behavioral rules (e.g., "don't reveal the answer") */
7
+ rules: z.ZodArray<z.ZodString, "many">;
8
+ /** Knowledge about the bug/solution (hidden from candidate) */
9
+ knowledge: z.ZodString;
10
+ }, "strip", z.ZodTypeAny, {
11
+ role: string;
12
+ rules: string[];
13
+ knowledge: string;
14
+ }, {
15
+ role: string;
16
+ rules: string[];
17
+ knowledge: string;
18
+ }>;
19
+ declare const EvaluationSchema: z.ZodObject<{
20
+ /** Evaluation criteria for the interviewer */
21
+ criteria: z.ZodArray<z.ZodString, "many">;
22
+ /** Description of the expected fix */
23
+ expected_fix: z.ZodOptional<z.ZodString>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ criteria: string[];
26
+ expected_fix?: string | undefined;
27
+ }, {
28
+ criteria: string[];
29
+ expected_fix?: string | undefined;
30
+ }>;
31
+ /** Scenario type — determines validation rules and system prompt context */
32
+ declare const ScenarioTypeSchema: z.ZodDefault<z.ZodEnum<["debug", "feature", "refactor"]>>;
33
+ /** Full scenario configuration schema */
34
+ declare const ScenarioConfigSchema: z.ZodObject<{
35
+ /** Scenario display name */
36
+ name: z.ZodString;
37
+ /** One-line description (candidate-visible — describe symptoms/task, never the root cause or solution) */
38
+ description: z.ZodString;
39
+ /** Scenario type: debug (find a bug), feature (build something), refactor (improve code) */
40
+ type: z.ZodDefault<z.ZodEnum<["debug", "feature", "refactor"]>>;
41
+ /** Difficulty level */
42
+ difficulty: z.ZodEnum<["easy", "medium", "hard"]>;
43
+ /** Estimated time (e.g., "30-45m") */
44
+ estimated_time: z.ZodString;
45
+ /** Searchable tags */
46
+ tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
47
+ /** GitHub repo URL or owner/repo shorthand */
48
+ repo: z.ZodString;
49
+ /** Commit SHA to pin the clone to (ensures reproducibility) */
50
+ commit: z.ZodString;
51
+ /** Shell commands to run after cloning (e.g., ["npm install"]) */
52
+ setup: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
53
+ /** Find-and-replace patches to inject the bug after cloning */
54
+ patch: z.ZodDefault<z.ZodArray<z.ZodObject<{
55
+ /** Path to the file relative to repo root */
56
+ file: z.ZodString;
57
+ /** The original text to find */
58
+ find: z.ZodString;
59
+ /** The replacement text (with the bug) */
60
+ replace: z.ZodString;
61
+ }, "strip", z.ZodTypeAny, {
62
+ find: string;
63
+ file: string;
64
+ replace: string;
65
+ }, {
66
+ find: string;
67
+ file: string;
68
+ replace: string;
69
+ }>, "many">>;
70
+ /** Files or directories to delete after cloning (globs relative to repo root) */
71
+ delete_files: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
72
+ /** Briefing shown to the candidate (written like a team lead message) */
73
+ briefing: z.ZodString;
74
+ /** AI behavioral rules (injected via system prompt, hidden from candidate) */
75
+ ai_rules: z.ZodObject<{
76
+ /** Role description for the AI assistant */
77
+ role: z.ZodString;
78
+ /** Behavioral rules (e.g., "don't reveal the answer") */
79
+ rules: z.ZodArray<z.ZodString, "many">;
80
+ /** Knowledge about the bug/solution (hidden from candidate) */
81
+ knowledge: z.ZodString;
82
+ }, "strip", z.ZodTypeAny, {
83
+ role: string;
84
+ rules: string[];
85
+ knowledge: string;
86
+ }, {
87
+ role: string;
88
+ rules: string[];
89
+ knowledge: string;
90
+ }>;
91
+ /** Interviewer reference — what the fix/implementation looks like */
92
+ solution: z.ZodOptional<z.ZodString>;
93
+ /** Acceptance criteria for feature scenarios (concrete, testable requirements) */
94
+ acceptance_criteria: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
95
+ /** Evaluation rubric */
96
+ evaluation: z.ZodOptional<z.ZodObject<{
97
+ /** Evaluation criteria for the interviewer */
98
+ criteria: z.ZodArray<z.ZodString, "many">;
99
+ /** Description of the expected fix */
100
+ expected_fix: z.ZodOptional<z.ZodString>;
101
+ }, "strip", z.ZodTypeAny, {
102
+ criteria: string[];
103
+ expected_fix?: string | undefined;
104
+ }, {
105
+ criteria: string[];
106
+ expected_fix?: string | undefined;
107
+ }>>;
108
+ /** License of the original project */
109
+ license: z.ZodOptional<z.ZodString>;
110
+ }, "strip", z.ZodTypeAny, {
111
+ type: "debug" | "feature" | "refactor";
112
+ name: string;
113
+ description: string;
114
+ difficulty: "easy" | "medium" | "hard";
115
+ estimated_time: string;
116
+ tags: string[];
117
+ repo: string;
118
+ commit: string;
119
+ setup: string[];
120
+ patch: {
121
+ find: string;
122
+ file: string;
123
+ replace: string;
124
+ }[];
125
+ delete_files: string[];
126
+ briefing: string;
127
+ ai_rules: {
128
+ role: string;
129
+ rules: string[];
130
+ knowledge: string;
131
+ };
132
+ solution?: string | undefined;
133
+ acceptance_criteria?: string[] | undefined;
134
+ evaluation?: {
135
+ criteria: string[];
136
+ expected_fix?: string | undefined;
137
+ } | undefined;
138
+ license?: string | undefined;
139
+ }, {
140
+ name: string;
141
+ description: string;
142
+ difficulty: "easy" | "medium" | "hard";
143
+ estimated_time: string;
144
+ repo: string;
145
+ commit: string;
146
+ briefing: string;
147
+ ai_rules: {
148
+ role: string;
149
+ rules: string[];
150
+ knowledge: string;
151
+ };
152
+ type?: "debug" | "feature" | "refactor" | undefined;
153
+ tags?: string[] | undefined;
154
+ setup?: string[] | undefined;
155
+ patch?: {
156
+ find: string;
157
+ file: string;
158
+ replace: string;
159
+ }[] | undefined;
160
+ delete_files?: string[] | undefined;
161
+ solution?: string | undefined;
162
+ acceptance_criteria?: string[] | undefined;
163
+ evaluation?: {
164
+ criteria: string[];
165
+ expected_fix?: string | undefined;
166
+ } | undefined;
167
+ license?: string | undefined;
168
+ }>;
169
+ type ScenarioConfig = z.infer<typeof ScenarioConfigSchema>;
170
+ type ScenarioType = z.infer<typeof ScenarioTypeSchema>;
171
+ type AIRules = z.infer<typeof AIRulesSchema>;
172
+ type Evaluation = z.infer<typeof EvaluationSchema>;
173
+ /** Metadata about a discovered scenario */
174
+ interface ScenarioInfo {
175
+ /** Scenario name */
176
+ name: string;
177
+ /** Parsed config */
178
+ config: ScenarioConfig;
179
+ /** Whether this is a built-in scenario */
180
+ builtIn: boolean;
181
+ }
182
+
183
+ /** Event types that can be captured during a session */
184
+ type SessionEventType = 'stdout' | 'stderr' | 'command' | 'note';
185
+ /** A single timestamped event captured during a session */
186
+ interface SessionEvent {
187
+ /** Milliseconds since recording started */
188
+ timestamp: number;
189
+ /** The kind of event */
190
+ type: SessionEventType;
191
+ /** The captured data */
192
+ data: string;
193
+ }
194
+ /** Serialized recording format */
195
+ interface RecordingData {
196
+ /** Session ID this recording belongs to */
197
+ sessionId: string;
198
+ /** ISO string of when recording started */
199
+ startedAt: string;
200
+ /** All captured events */
201
+ events: SessionEvent[];
202
+ }
203
+ /**
204
+ * Records timestamped events during an interview session.
205
+ *
206
+ * Captures stdout, stderr, commands, and notes with millisecond timestamps
207
+ * relative to when the recorder was created.
208
+ */
209
+ declare class SessionRecorder {
210
+ private readonly events;
211
+ private readonly startTime;
212
+ private readonly startedAt;
213
+ constructor();
214
+ /** Record a timestamped event */
215
+ record(type: SessionEventType, data: string): void;
216
+ /** Get all recorded events */
217
+ getEvents(): ReadonlyArray<SessionEvent>;
218
+ /** Serialize the recording to a JSON-compatible object */
219
+ toJSON(sessionId: string): RecordingData;
220
+ /** Create a SessionRecorder pre-populated with events from serialized data */
221
+ static fromJSON(data: RecordingData): SessionRecorder;
222
+ /** Save the recording to disk */
223
+ save(sessionId: string): Promise<void>;
224
+ /** Load a recording from disk */
225
+ static load(sessionId: string): Promise<SessionRecorder>;
226
+ /** List all available recording session IDs */
227
+ static list(): Promise<string[]>;
228
+ }
229
+
230
+ /** Configuration for launching an AI coding tool */
231
+ interface LaunchConfig {
232
+ /** Scenario name for display */
233
+ scenarioName: string;
234
+ /** Path to system prompt file (hidden from candidate) */
235
+ systemPromptPath: string;
236
+ /** Model to use */
237
+ model?: string;
238
+ /** Permission mode for the AI tool */
239
+ permissionMode?: 'default' | 'plan' | 'acceptEdits' | 'bypassPermissions';
240
+ /** Tools to disallow (e.g., WebSearch for fairness) */
241
+ disallowedTools?: string[];
242
+ /** Whether to record stdout/stderr during the session */
243
+ recording?: boolean;
244
+ }
245
+ /** A running AI tool process */
246
+ interface LaunchedProcess {
247
+ /** Wait for the process to exit */
248
+ wait(): Promise<{
249
+ exitCode: number;
250
+ }>;
251
+ /** Kill the process */
252
+ kill(): Promise<void>;
253
+ /** Session recorder, present when recording is enabled */
254
+ recorder?: SessionRecorder;
255
+ }
256
+ /** Interface for AI coding tool launchers */
257
+ interface AIToolLauncher {
258
+ /** Internal name identifier */
259
+ readonly name: string;
260
+ /** Human-readable display name */
261
+ readonly displayName: string;
262
+ /** Check if this tool is installed and accessible */
263
+ isInstalled(): Promise<boolean>;
264
+ /** Get the installed version string */
265
+ getVersion(): Promise<string | null>;
266
+ /** Launch the tool pointed at a working directory */
267
+ launch(workdir: string, config: LaunchConfig): Promise<LaunchedProcess>;
268
+ }
269
+
270
+ /** Status of an interview session */
271
+ type SessionStatus = 'cloning' | 'setting-up' | 'running' | 'complete';
272
+ /** Interview session — used both in-memory and for persistence */
273
+ interface Session {
274
+ /** Unique session identifier */
275
+ id: string;
276
+ /** Name of the scenario being run */
277
+ scenarioName: string;
278
+ /** Local working directory for the candidate */
279
+ workdir: string;
280
+ /** Path to the system prompt file (outside workspace) */
281
+ systemPromptPath: string;
282
+ /** Current session status */
283
+ status: SessionStatus;
284
+ /** ISO timestamp of session creation */
285
+ createdAt: string;
286
+ /** ISO timestamp of when the AI tool was launched */
287
+ startedAt?: string;
288
+ /** ISO timestamp of session completion */
289
+ completedAt?: string;
290
+ /** Name of the AI tool used */
291
+ aiTool?: string;
292
+ }
293
+ /**
294
+ * Serializable session data for persistence.
295
+ * Identical to Session — kept as an alias for API clarity at persistence boundaries.
296
+ */
297
+ type StoredSession = Session;
298
+ /** Convert a Session to a StoredSession for persistence */
299
+ declare function toStoredSession(session: Session): StoredSession;
300
+
301
+ /** Callback for reporting session progress */
302
+ type ProgressCallback = (stage: string) => void;
303
+ /** Manages the lifecycle of an interview session */
304
+ declare class SessionManager {
305
+ private launcher;
306
+ constructor(launcher: AIToolLauncher);
307
+ /**
308
+ * Create a new interview session.
309
+ *
310
+ * Flow:
311
+ * 1. Clone the repo at a pinned commit
312
+ * 2. Apply bug patches (find/replace in source files)
313
+ * 3. Delete files excluded by the scenario (e.g., tests that reveal the bug)
314
+ * 4. Wipe git history so the candidate can't diff to find the bug
315
+ * 5. Remove scenario.yaml from workspace (interviewer-only)
316
+ * 6. Write BRIEFING.md and system prompt
317
+ * 7. Run setup commands (npm install, etc.)
318
+ */
319
+ createSession(config: ScenarioConfig, workdir?: string, onProgress?: ProgressCallback, options?: {
320
+ skipSetup?: boolean;
321
+ }): Promise<{
322
+ session: Session;
323
+ config: ScenarioConfig;
324
+ }>;
325
+ /** Launch the AI coding tool for an active session */
326
+ launchAITool(session: Session, _config: ScenarioConfig, launchConfig?: Partial<LaunchConfig>): Promise<{
327
+ exitCode: number;
328
+ }>;
329
+ /** Destroy a session by removing its stored data */
330
+ destroySession(session: Session): Promise<void>;
331
+ /** Get elapsed time since the AI tool was launched, formatted as a human-readable string */
332
+ getElapsedTime(session: Session): string | null;
333
+ }
334
+
335
+ /** Base error class for all vibe-interviewing errors */
336
+ declare class VibeError extends Error {
337
+ readonly code: string;
338
+ readonly hint?: string | undefined;
339
+ constructor(message: string, code: string, hint?: string | undefined);
340
+ }
341
+ declare class ScenarioNotFoundError extends VibeError {
342
+ constructor(name: string);
343
+ }
344
+ declare class ScenarioValidationError extends VibeError {
345
+ readonly issues: string[];
346
+ constructor(message: string, issues: string[]);
347
+ }
348
+ declare class AIToolNotFoundError extends VibeError {
349
+ static readonly installHints: Record<string, string>;
350
+ constructor(tool: string);
351
+ }
352
+ declare class SessionNotFoundError extends VibeError {
353
+ constructor(id: string);
354
+ }
355
+ declare class GitCloneError extends VibeError {
356
+ constructor(repo: string, reason?: string);
357
+ }
358
+ declare class SetupError extends VibeError {
359
+ constructor(command: string, reason?: string);
360
+ }
361
+
362
+ /**
363
+ * Encode a host:port pair into a human-typeable session code.
364
+ *
365
+ * Format: VIBE-XXXXXXXXXX (10 base36 chars encoding 6 bytes: 4 IP octets + 2 port bytes)
366
+ */
367
+ declare function encodeSessionCode(host: string, port: number): string;
368
+ /**
369
+ * Decode a session code back to host:port.
370
+ *
371
+ * Accepts formats: VIBE-XXXXXXXXXX, vibe-xxxxxxxxxx, or just XXXXXXXXXX
372
+ */
373
+ declare function decodeSessionCode(code: string): {
374
+ host: string;
375
+ port: number;
376
+ };
377
+ /**
378
+ * Check if a session code is a cloud code (6 chars) vs LAN code (10 chars).
379
+ *
380
+ * Cloud codes are 6 alphanumeric characters: VIBE-A3X9K2
381
+ * LAN codes are 10 alphanumeric characters: VIBE-3R8KW1F0NX
382
+ */
383
+ declare function isCloudSessionCode(code: string): boolean;
384
+ /** Error for invalid session codes */
385
+ declare class InvalidSessionCodeError extends VibeError {
386
+ constructor(message: string);
387
+ }
388
+
389
+ export { type AIToolLauncher as A, type Evaluation as E, GitCloneError as G, InvalidSessionCodeError as I, type LaunchConfig as L, type ProgressCallback as P, type RecordingData as R, type ScenarioConfig as S, VibeError as V, type ScenarioInfo as a, type LaunchedProcess as b, type StoredSession as c, type AIRules as d, AIToolNotFoundError as e, ScenarioConfigSchema as f, ScenarioNotFoundError as g, type ScenarioType as h, ScenarioValidationError as i, type Session as j, type SessionEvent as k, type SessionEventType as l, SessionManager as m, SessionNotFoundError as n, SessionRecorder as o, SetupError as p, decodeSessionCode as q, encodeSessionCode as r, isCloudSessionCode as s, toStoredSession as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-interviewing/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Core library for vibe-interviewing — scenario engine, session management, AI tool launchers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,13 +9,17 @@
9
9
  ".": {
10
10
  "types": "./dist/index.d.ts",
11
11
  "default": "./dist/index.js"
12
+ },
13
+ "./network": {
14
+ "types": "./dist/network/index.d.ts",
15
+ "default": "./dist/network/index.js"
12
16
  }
13
17
  },
14
18
  "dependencies": {
15
19
  "simple-git": "^3.27.0",
16
20
  "yaml": "^2.7.0",
17
21
  "zod": "^3.24.0",
18
- "@vibe-interviewing/scenarios": "0.1.0"
22
+ "@vibe-interviewing/scenarios": "0.2.0"
19
23
  },
20
24
  "devDependencies": {
21
25
  "@types/node": "^20.17.0",