@brainforge/core 3.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +393 -0
  2. package/dist/index.js +3556 -0
  3. package/package.json +54 -0
package/dist/index.js ADDED
@@ -0,0 +1,3556 @@
1
+ // src/state/manager.ts
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ var STATE_DIR = ".brainforge";
5
+ var STATE_FILE = "core.json";
6
+ var StateManager = class {
7
+ constructor(workingDir) {
8
+ this.workingDir = workingDir;
9
+ this.statePath = path.join(workingDir, STATE_DIR, STATE_FILE);
10
+ }
11
+ workingDir;
12
+ statePath;
13
+ state = null;
14
+ async load() {
15
+ const raw = await fs.readFile(this.statePath, "utf-8");
16
+ this.state = JSON.parse(raw);
17
+ return this.state;
18
+ }
19
+ async save() {
20
+ if (!this.state) throw new Error("No state loaded \u2014 call init() or load() first.");
21
+ this.state.metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
22
+ await fs.mkdir(path.dirname(this.statePath), { recursive: true });
23
+ await fs.writeFile(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
24
+ await this.writeDerivedViews();
25
+ }
26
+ async init(project) {
27
+ this.state = {
28
+ version: "3.0.0",
29
+ project,
30
+ phases: [],
31
+ context: {},
32
+ metadata: {
33
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
34
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
35
+ workingDirectory: this.workingDir
36
+ }
37
+ };
38
+ await this.save();
39
+ return this.state;
40
+ }
41
+ get() {
42
+ if (!this.state) throw new Error("State not loaded \u2014 call load() first.");
43
+ return this.state;
44
+ }
45
+ set(updater) {
46
+ if (!this.state) throw new Error("State not loaded.");
47
+ updater(this.state);
48
+ }
49
+ async configureAI(config) {
50
+ this.get().aiConfig = config;
51
+ await this.save();
52
+ }
53
+ async addPhase(phase) {
54
+ const newPhase = { ...phase, tasks: [] };
55
+ this.get().phases.push(newPhase);
56
+ await this.save();
57
+ return newPhase;
58
+ }
59
+ async addTask(task) {
60
+ const phase = this.get().phases.find((p) => p.id === task.phaseId);
61
+ if (!phase) throw new Error(`Phase "${task.phaseId}" not found.`);
62
+ phase.tasks.push(task);
63
+ await this.save();
64
+ }
65
+ async updateTaskStatus(taskId, status) {
66
+ for (const phase of this.get().phases) {
67
+ const task = phase.tasks.find((t) => t.id === taskId);
68
+ if (task) {
69
+ task.status = status;
70
+ task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
71
+ await this.save();
72
+ return;
73
+ }
74
+ }
75
+ throw new Error(`Task "${taskId}" not found.`);
76
+ }
77
+ async writeDerivedViews() {
78
+ await Promise.all([this.writeProjectMd(), this.writeStateMd()]);
79
+ }
80
+ async writeProjectMd() {
81
+ const s = this.state;
82
+ const phaseLines = s.phases.length ? s.phases.map((p) => `- [${p.status === "completed" ? "x" : " "}] **${p.name}** \u2014 ${p.description}`) : ["- No phases yet. Run `brainforge plan` to create one."];
83
+ const content = [
84
+ `# ${s.project.name}`,
85
+ "",
86
+ `> ${s.project.description}`,
87
+ "",
88
+ `**Type:** ${s.project.type} | **Language:** ${s.project.language}${s.project.framework ? ` | **Framework:** ${s.project.framework}` : ""}${s.project.studentLevel ? ` | **Level:** ${s.project.studentLevel}` : ""}`,
89
+ "",
90
+ "## Phases",
91
+ "",
92
+ ...phaseLines,
93
+ "",
94
+ `---`,
95
+ `*Generated by BrainForge AI v3 \u2014 ${s.metadata.updatedAt}*`
96
+ ].join("\n");
97
+ await fs.writeFile(path.join(this.workingDir, "PROJECT.md"), content, "utf-8");
98
+ }
99
+ async writeStateMd() {
100
+ const s = this.state;
101
+ const active = s.phases.find((p) => p.id === s.activePhaseId);
102
+ const totalTasks = s.phases.reduce((n, p) => n + p.tasks.length, 0);
103
+ const doneTasks = s.phases.reduce((n, p) => n + p.tasks.filter((t) => t.status === "done").length, 0);
104
+ const phaseBlocks = s.phases.map((p) => {
105
+ const done = p.tasks.filter((t) => t.status === "done").length;
106
+ return `### ${p.name} \`${p.status}\`
107
+ ${p.description}
108
+ Tasks: ${done}/${p.tasks.length}`;
109
+ });
110
+ const content = [
111
+ "# Project State",
112
+ "",
113
+ `**Last Updated:** ${s.metadata.updatedAt}`,
114
+ `**Active Phase:** ${active ? active.name : "None"}`,
115
+ `**Progress:** ${doneTasks}/${totalTasks} tasks complete`,
116
+ "",
117
+ "## Phases",
118
+ "",
119
+ phaseBlocks.length ? phaseBlocks.join("\n\n") : "No phases yet."
120
+ ].join("\n");
121
+ await fs.writeFile(path.join(this.workingDir, "STATE.md"), content, "utf-8");
122
+ }
123
+ };
124
+
125
+ // src/state/state-machine.ts
126
+ function reduce(state, action) {
127
+ switch (action.type) {
128
+ case "SET_ACTIVE_PHASE":
129
+ return { ...state, activePhaseId: action.phaseId };
130
+ case "COMPLETE_PHASE": {
131
+ const phases = state.phases.map(
132
+ (p) => p.id === action.phaseId ? { ...p, status: "completed", completedAt: (/* @__PURE__ */ new Date()).toISOString() } : p
133
+ );
134
+ return { ...state, phases };
135
+ }
136
+ case "UPDATE_TASK": {
137
+ const phases = state.phases.map((p) => ({
138
+ ...p,
139
+ tasks: p.tasks.map(
140
+ (t) => t.id === action.taskId ? { ...t, ...action.updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } : t
141
+ )
142
+ }));
143
+ return { ...state, phases };
144
+ }
145
+ case "SET_CONTEXT":
146
+ return { ...state, context: { ...state.context, [action.key]: action.value } };
147
+ default:
148
+ return state;
149
+ }
150
+ }
151
+
152
+ // src/watcher/file-watcher.ts
153
+ import chokidar from "chokidar";
154
+ import { EventEmitter } from "events";
155
+ var FileWatcher = class extends EventEmitter {
156
+ watcher = null;
157
+ watch(rootDir, patterns = ["**/*"]) {
158
+ this.watcher = chokidar.watch(patterns, {
159
+ cwd: rootDir,
160
+ ignored: /(node_modules|\.git|\.brainforge)/,
161
+ persistent: true,
162
+ ignoreInitial: true
163
+ });
164
+ const emit = (type) => (filePath) => {
165
+ this.emit("change", {
166
+ type,
167
+ path: filePath,
168
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
169
+ });
170
+ };
171
+ this.watcher.on("add", emit("add")).on("change", emit("change")).on("unlink", emit("unlink"));
172
+ }
173
+ async stop() {
174
+ await this.watcher?.close();
175
+ this.watcher = null;
176
+ }
177
+ };
178
+
179
+ // src/prompt-engine/engine.ts
180
+ import Handlebars from "handlebars";
181
+ var BUILTIN_TEMPLATES = {
182
+ discuss: `You are BrainForge AI, helping a {{project.type}} developer with "{{project.name}}".
183
+
184
+ Project: {{project.description}}
185
+ Language: {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}
186
+ {{#if project.studentLevel}}Student Level: {{project.studentLevel}}{{/if}}
187
+
188
+ The developer wants to discuss: {{topic}}
189
+
190
+ Provide structured, actionable guidance appropriate for their level. Be concise but thorough.`,
191
+ plan: `You are BrainForge AI. Analyze the following task for project "{{project.name}}":
192
+
193
+ Task: {{task.title}}
194
+ Description: {{task.description}}
195
+
196
+ Generate a structured implementation plan with:
197
+ 1. Files to modify/create
198
+ 2. Key functions to implement
199
+ 3. Potential risks
200
+ 4. Estimated complexity (1-10)
201
+
202
+ Keep the response focused and actionable.`,
203
+ review: `Review this code for a {{project.type}} developer{{#if project.studentLevel}} at {{project.studentLevel}} level{{/if}}.
204
+
205
+ File: {{filePath}}
206
+ \`\`\`{{project.language}}
207
+ {{code}}
208
+ \`\`\`
209
+
210
+ Evaluate:
211
+ - Correctness and logic
212
+ - Best practices for their experience level
213
+ {{#if isStudent}}- Academic integrity flags (patterns too advanced for declared level?)
214
+ - Can they explain every line?{{/if}}
215
+ - Top 3 improvement suggestions`
216
+ };
217
+ var PromptEngine = class {
218
+ compiled = /* @__PURE__ */ new Map();
219
+ constructor() {
220
+ for (const [name, source] of Object.entries(BUILTIN_TEMPLATES)) {
221
+ this.compiled.set(name, Handlebars.compile(source));
222
+ }
223
+ }
224
+ render(templateName, context) {
225
+ const template = this.compiled.get(templateName);
226
+ if (!template) throw new Error(`Template "${templateName}" not found.`);
227
+ return template(context);
228
+ }
229
+ registerTemplate(name, source) {
230
+ this.compiled.set(name, Handlebars.compile(source));
231
+ }
232
+ listTemplates() {
233
+ return Array.from(this.compiled.keys());
234
+ }
235
+ };
236
+
237
+ // src/mapper/mapper.ts
238
+ import fs2 from "fs/promises";
239
+ import path2 from "path";
240
+
241
+ // src/mapper/extractors.ts
242
+ function extractFromFile(filePath, content, language) {
243
+ const normalizedPath = filePath.replace(/\\/g, "/");
244
+ switch (language) {
245
+ case "typescript":
246
+ case "javascript":
247
+ return extractJS(normalizedPath, content, language);
248
+ case "python":
249
+ return extractPython(normalizedPath, content);
250
+ case "java":
251
+ return extractJava(normalizedPath, content);
252
+ case "go":
253
+ return extractGo(normalizedPath, content);
254
+ case "php":
255
+ return extractPHP(normalizedPath, content);
256
+ default:
257
+ return emptyNode(normalizedPath, language);
258
+ }
259
+ }
260
+ function extractJS(filePath, content, language) {
261
+ const lines = content.split("\n");
262
+ const functions = [];
263
+ const classes = [];
264
+ const imports = [];
265
+ const exports = [];
266
+ let currentClass = null;
267
+ let braceDepth = 0;
268
+ let classStartDepth = -1;
269
+ for (let i = 0; i < lines.length; i++) {
270
+ const line = lines[i];
271
+ const lineNum = i + 1;
272
+ braceDepth += (line.match(/\{/g) || []).length;
273
+ braceDepth -= (line.match(/\}/g) || []).length;
274
+ braceDepth = Math.max(0, braceDepth);
275
+ if (currentClass && braceDepth <= classStartDepth) {
276
+ classes.push(currentClass);
277
+ currentClass = null;
278
+ classStartDepth = -1;
279
+ }
280
+ let m = line.match(/^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/);
281
+ if (m) {
282
+ currentClass = { name: m[1], line: lineNum, isExported: /\bexport\b/.test(line), methods: [] };
283
+ classStartDepth = braceDepth - 1;
284
+ continue;
285
+ }
286
+ if (currentClass) {
287
+ const method = line.match(/^\s{2,}(?:(?:public|private|protected|static|async|override|get|set)\s+)*(\w+)\s*\([^)]*\)\s*(?::\s*[\w<>[\]|]+)?\s*\{/);
288
+ if (method && !["constructor", "if", "for", "while", "switch", "catch"].includes(method[1])) {
289
+ currentClass.methods.push(method[1]);
290
+ }
291
+ continue;
292
+ }
293
+ m = line.match(/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*[(<]/);
294
+ if (m) {
295
+ functions.push({ name: m[1], line: lineNum, isExported: /\bexport\b/.test(line), isAsync: /\basync\b/.test(line) });
296
+ continue;
297
+ }
298
+ m = line.match(/^(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\(/);
299
+ if (m) {
300
+ functions.push({ name: m[1], line: lineNum, isExported: /\bexport\b/.test(line), isAsync: /\basync\b/.test(line) });
301
+ continue;
302
+ }
303
+ m = line.match(/^import\s+.+?\s+from\s+['"]([^'"]+)['"]/);
304
+ if (m) {
305
+ imports.push(m[1]);
306
+ continue;
307
+ }
308
+ m = line.match(/^export\s+(?:default\s+)?(?:function|class|const|let|var|type|interface|enum)\s+(\w+)/);
309
+ if (m) {
310
+ exports.push(m[1]);
311
+ continue;
312
+ }
313
+ m = line.match(/^export\s+\{\s*([^}]+)\s*\}/);
314
+ if (m) {
315
+ exports.push(...m[1].split(",").map((e) => e.trim().split(/\s+/)[0]).filter(Boolean));
316
+ }
317
+ }
318
+ if (currentClass) classes.push(currentClass);
319
+ return { path: filePath, language, functions, classes, imports, exports, lastScanned: (/* @__PURE__ */ new Date()).toISOString() };
320
+ }
321
+ function extractPython(filePath, content) {
322
+ const lines = content.split("\n");
323
+ const functions = [];
324
+ const classes = [];
325
+ const imports = [];
326
+ const exports = [];
327
+ let currentClass = null;
328
+ for (let i = 0; i < lines.length; i++) {
329
+ const line = lines[i];
330
+ const lineNum = i + 1;
331
+ let m = line.match(/^class\s+(\w+)/);
332
+ if (m) {
333
+ if (currentClass) classes.push(currentClass);
334
+ currentClass = { name: m[1], line: lineNum, isExported: true, methods: [] };
335
+ continue;
336
+ }
337
+ if (currentClass && line.match(/^ (?:async\s+)?def\s+/)) {
338
+ m = line.match(/^ (?:async\s+)?def\s+(\w+)\s*\(/);
339
+ if (m) {
340
+ currentClass.methods.push(m[1]);
341
+ continue;
342
+ }
343
+ }
344
+ m = line.match(/^(?:async\s+)?def\s+(\w+)\s*\(/);
345
+ if (m) {
346
+ if (currentClass) {
347
+ classes.push(currentClass);
348
+ currentClass = null;
349
+ }
350
+ functions.push({ name: m[1], line: lineNum, isExported: true, isAsync: /^async/.test(line.trim()) });
351
+ continue;
352
+ }
353
+ m = line.match(/^import\s+([\w.]+)/);
354
+ if (m) {
355
+ imports.push(m[1]);
356
+ continue;
357
+ }
358
+ m = line.match(/^from\s+([\w.]+)\s+import/);
359
+ if (m) imports.push(m[1]);
360
+ }
361
+ if (currentClass) classes.push(currentClass);
362
+ return { path: filePath, language: "python", functions, classes, imports, exports, lastScanned: (/* @__PURE__ */ new Date()).toISOString() };
363
+ }
364
+ function extractJava(filePath, content) {
365
+ const lines = content.split("\n");
366
+ const functions = [];
367
+ const classes = [];
368
+ const imports = [];
369
+ for (let i = 0; i < lines.length; i++) {
370
+ const line = lines[i];
371
+ const lineNum = i + 1;
372
+ let m = line.match(/^import\s+([\w.]+);/);
373
+ if (m) {
374
+ imports.push(m[1]);
375
+ continue;
376
+ }
377
+ m = line.match(/(?:public|private|protected)?\s*(?:abstract\s+)?(?:class|interface|enum)\s+(\w+)/);
378
+ if (m) {
379
+ classes.push({ name: m[1], line: lineNum, isExported: /public/.test(line), methods: [] });
380
+ continue;
381
+ }
382
+ m = line.match(/(?:public|private|protected)\s+(?:static\s+)?(?:[\w<>[\]]+\s+)?(\w+)\s*\([^)]*\)\s*(?:throws\s+\w+\s*)?\{/);
383
+ if (m && !["if", "for", "while", "switch", "try", "catch"].includes(m[1])) {
384
+ functions.push({ name: m[1], line: lineNum, isExported: /public/.test(line), isAsync: false });
385
+ }
386
+ }
387
+ return { path: filePath, language: "java", functions, classes, imports, exports: [], lastScanned: (/* @__PURE__ */ new Date()).toISOString() };
388
+ }
389
+ function extractGo(filePath, content) {
390
+ const lines = content.split("\n");
391
+ const functions = [];
392
+ const classes = [];
393
+ const imports = [];
394
+ for (let i = 0; i < lines.length; i++) {
395
+ const line = lines[i];
396
+ const lineNum = i + 1;
397
+ let m = line.match(/^import\s+"([^"]+)"/);
398
+ if (m) {
399
+ imports.push(m[1]);
400
+ continue;
401
+ }
402
+ m = line.match(/^type\s+(\w+)\s+struct/);
403
+ if (m) {
404
+ classes.push({ name: m[1], line: lineNum, isExported: /^[A-Z]/.test(m[1]), methods: [] });
405
+ continue;
406
+ }
407
+ m = line.match(/^func\s+(?:\(\w+\s+\*?\w+\)\s+)?(\w+)\s*\(/);
408
+ if (m) {
409
+ functions.push({ name: m[1], line: lineNum, isExported: /^[A-Z]/.test(m[1]), isAsync: false });
410
+ }
411
+ }
412
+ return { path: filePath, language: "go", functions, classes, imports, exports: [], lastScanned: (/* @__PURE__ */ new Date()).toISOString() };
413
+ }
414
+ function extractPHP(filePath, content) {
415
+ const lines = content.split("\n");
416
+ const functions = [];
417
+ const classes = [];
418
+ const imports = [];
419
+ for (let i = 0; i < lines.length; i++) {
420
+ const line = lines[i];
421
+ const lineNum = i + 1;
422
+ let m = line.match(/^(?:use|require|include)\s+['"]?([^'";\s]+)/);
423
+ if (m) {
424
+ imports.push(m[1]);
425
+ continue;
426
+ }
427
+ m = line.match(/^(?:abstract\s+)?class\s+(\w+)/);
428
+ if (m) {
429
+ classes.push({ name: m[1], line: lineNum, isExported: true, methods: [] });
430
+ continue;
431
+ }
432
+ m = line.match(/^(?:public\s+|private\s+|protected\s+)?(?:static\s+)?function\s+(\w+)\s*\(/);
433
+ if (m) {
434
+ functions.push({ name: m[1], line: lineNum, isExported: /public/.test(line), isAsync: false });
435
+ }
436
+ }
437
+ return { path: filePath, language: "php", functions, classes, imports, exports: [], lastScanned: (/* @__PURE__ */ new Date()).toISOString() };
438
+ }
439
+ function emptyNode(filePath, language) {
440
+ return { path: filePath, language, functions: [], classes: [], imports: [], exports: [], lastScanned: (/* @__PURE__ */ new Date()).toISOString() };
441
+ }
442
+
443
+ // src/mapper/mapper.ts
444
+ var MAP_FILE = path2.join(".brainforge", "codebase-map.json");
445
+ var LANGUAGE_EXTENSIONS = {
446
+ typescript: [".ts", ".tsx"],
447
+ javascript: [".js", ".jsx", ".mjs", ".cjs"],
448
+ python: [".py"],
449
+ java: [".java"],
450
+ go: [".go"],
451
+ php: [".php"]
452
+ };
453
+ var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".brainforge", "dist", "build", "coverage", "__pycache__", ".venv", "vendor"]);
454
+ var CodebaseMapper = class {
455
+ constructor(workingDir) {
456
+ this.workingDir = workingDir;
457
+ }
458
+ workingDir;
459
+ async scan(language) {
460
+ const extensions = LANGUAGE_EXTENSIONS[language];
461
+ const sourceFiles = await this.findFiles(extensions);
462
+ const fileNodes = [];
463
+ for (const filePath of sourceFiles) {
464
+ try {
465
+ const content = await fs2.readFile(path2.join(this.workingDir, filePath), "utf-8");
466
+ fileNodes.push(extractFromFile(filePath, content, language));
467
+ } catch {
468
+ }
469
+ }
470
+ const map = {
471
+ version: "3.0.0",
472
+ scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
473
+ rootDir: this.workingDir,
474
+ files: fileNodes,
475
+ stats: {
476
+ totalFiles: fileNodes.length,
477
+ totalFunctions: fileNodes.reduce((n, f) => n + f.functions.length, 0),
478
+ totalClasses: fileNodes.reduce((n, f) => n + f.classes.length, 0)
479
+ }
480
+ };
481
+ await this.saveMap(map);
482
+ return map;
483
+ }
484
+ async load() {
485
+ const raw = await fs2.readFile(path2.join(this.workingDir, MAP_FILE), "utf-8");
486
+ return JSON.parse(raw);
487
+ }
488
+ findRelevantFiles(map, query) {
489
+ const keywords = tokenize(query);
490
+ if (keywords.length === 0) return map.files.slice(0, 5);
491
+ const scored = map.files.map((file) => {
492
+ let score = 0;
493
+ const pathLower = file.path.toLowerCase();
494
+ for (const kw of keywords) {
495
+ if (pathLower.includes(kw)) score += 3;
496
+ if (file.functions.some((f) => f.name.toLowerCase().includes(kw))) score += 2;
497
+ if (file.classes.some((c) => c.name.toLowerCase().includes(kw))) score += 2;
498
+ if (file.classes.some((c) => c.methods.some((m) => m.toLowerCase().includes(kw)))) score += 1;
499
+ }
500
+ return { file, score };
501
+ });
502
+ return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, 8).map((s) => s.file);
503
+ }
504
+ async findFiles(extensions) {
505
+ const results = [];
506
+ await this.walk(this.workingDir, "", extensions, results);
507
+ return results;
508
+ }
509
+ async walk(root, rel, extensions, results) {
510
+ const dir = rel ? path2.join(root, rel) : root;
511
+ let entries;
512
+ try {
513
+ entries = await fs2.readdir(dir, { withFileTypes: true });
514
+ } catch {
515
+ return;
516
+ }
517
+ for (const entry of entries) {
518
+ if (IGNORED_DIRS.has(entry.name)) continue;
519
+ const relPath = rel ? `${rel}/${entry.name}` : entry.name;
520
+ if (entry.isDirectory()) {
521
+ await this.walk(root, relPath, extensions, results);
522
+ } else if (extensions.some((ext) => entry.name.endsWith(ext))) {
523
+ results.push(relPath);
524
+ }
525
+ }
526
+ }
527
+ async saveMap(map) {
528
+ const dest = path2.join(this.workingDir, MAP_FILE);
529
+ await fs2.mkdir(path2.dirname(dest), { recursive: true });
530
+ await fs2.writeFile(dest, JSON.stringify(map, null, 2), "utf-8");
531
+ }
532
+ };
533
+ function tokenize(text) {
534
+ const STOP_WORDS = /* @__PURE__ */ new Set([
535
+ "a",
536
+ "an",
537
+ "the",
538
+ "and",
539
+ "or",
540
+ "but",
541
+ "in",
542
+ "on",
543
+ "at",
544
+ "to",
545
+ "for",
546
+ "of",
547
+ "with",
548
+ "by",
549
+ "from",
550
+ "as",
551
+ "is",
552
+ "was",
553
+ "are",
554
+ "were",
555
+ "be",
556
+ "been",
557
+ "being",
558
+ "have",
559
+ "has",
560
+ "had",
561
+ "do",
562
+ "does",
563
+ "did",
564
+ "will",
565
+ "would",
566
+ "could",
567
+ "should",
568
+ "may",
569
+ "might",
570
+ "i",
571
+ "my",
572
+ "we",
573
+ "our",
574
+ "it",
575
+ "its",
576
+ "this",
577
+ "that",
578
+ "which",
579
+ "how",
580
+ "what",
581
+ "when",
582
+ "where",
583
+ "add",
584
+ "create",
585
+ "new",
586
+ "implement",
587
+ "build",
588
+ "make",
589
+ "write",
590
+ "update"
591
+ ]);
592
+ return text.toLowerCase().split(/\W+/).filter((w) => w.length > 2 && !STOP_WORDS.has(w));
593
+ }
594
+
595
+ // src/planner/planner.ts
596
+ import { randomUUID } from "crypto";
597
+ var RISK_KEYWORDS = {
598
+ auth: "Authentication logic requires careful security review \u2014 never hardcode secrets.",
599
+ password: "Password handling must use proper hashing (bcrypt/argon2), never plain text.",
600
+ token: "Token validation needs expiry checks and secure storage.",
601
+ database: "Database changes may require migrations and backward compatibility.",
602
+ migration: "Migrations are irreversible \u2014 test on a copy before running in production.",
603
+ delete: "Deletion operations should be soft-deleted or confirmed before executing.",
604
+ remove: "Removal of functionality may break dependent code \u2014 check all call sites.",
605
+ permission: "Permission checks must be enforced on the server, not just the client.",
606
+ upload: "File uploads need type validation, size limits, and path sanitization.",
607
+ payment: "Payment flows require idempotency keys and thorough error handling.",
608
+ email: "Email delivery is asynchronous \u2014 queue it to avoid blocking the request.",
609
+ cache: "Caching logic must handle invalidation to avoid stale data.",
610
+ concurrent: "Concurrent access requires locks or atomic operations to prevent race conditions.",
611
+ async: "Unhandled promise rejections will silently fail \u2014 always await or catch."
612
+ };
613
+ var TaskPlanner = class {
614
+ plan(title, description, map, relevantFiles, project) {
615
+ const id = randomUUID();
616
+ const steps = this.buildSteps(title, description, relevantFiles);
617
+ const impactedFiles = relevantFiles.map((f) => f.path);
618
+ const impactedFunctions = relevantFiles.flatMap((f) => [
619
+ ...f.functions.map((fn) => `${f.path}:${fn.name}`),
620
+ ...f.classes.flatMap((c) => c.methods.map((m) => `${f.path}:${c.name}.${m}`))
621
+ ]);
622
+ const risks = this.detectRisks(title + " " + description);
623
+ const complexity = this.estimateComplexity(steps.length, impactedFiles.length, risks.length);
624
+ const agentPrompt = this.buildAgentPrompt(title, description, steps, project);
625
+ return {
626
+ id,
627
+ title,
628
+ description,
629
+ impactedFiles,
630
+ impactedFunctions,
631
+ steps,
632
+ estimatedComplexity: complexity,
633
+ risks,
634
+ agentPrompt,
635
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
636
+ };
637
+ }
638
+ buildSteps(title, description, files) {
639
+ const steps = [];
640
+ const combined = (title + " " + description).toLowerCase();
641
+ if (files.length === 0) {
642
+ steps.push({
643
+ id: randomUUID(),
644
+ order: 1,
645
+ action: "create",
646
+ file: "src/[module]/index.ts",
647
+ description: `Create the main module file for: ${title}`,
648
+ targetFunctions: []
649
+ });
650
+ return steps;
651
+ }
652
+ for (let i = 0; i < files.length; i++) {
653
+ const file = files[i];
654
+ const action = this.inferAction(combined, file);
655
+ const targetFns = [
656
+ ...file.functions.map((f) => f.name),
657
+ ...file.classes.flatMap((c) => c.methods)
658
+ ].slice(0, 5);
659
+ steps.push({
660
+ id: randomUUID(),
661
+ order: i + 1,
662
+ action,
663
+ file: file.path,
664
+ description: `${action === "modify" ? "Update" : "Review"} ${file.path} for: ${title}`,
665
+ targetFunctions: targetFns
666
+ });
667
+ }
668
+ return steps;
669
+ }
670
+ inferAction(combined, file) {
671
+ const hasCode = file.functions.length > 0 || file.classes.length > 0;
672
+ if (!hasCode) return "review";
673
+ if (/\b(add|create|new|implement|introduce)\b/.test(combined)) return "modify";
674
+ if (/\b(delete|remove|drop)\b/.test(combined)) return "modify";
675
+ return "modify";
676
+ }
677
+ detectRisks(text) {
678
+ const lower = text.toLowerCase();
679
+ return Object.entries(RISK_KEYWORDS).filter(([keyword]) => lower.includes(keyword)).map(([, message]) => message);
680
+ }
681
+ estimateComplexity(steps, files, risks) {
682
+ const base = 1 + steps * 0.8 + files * 0.5 + risks * 0.7;
683
+ return Math.min(10, Math.max(1, Math.round(base)));
684
+ }
685
+ buildAgentPrompt(title, description, steps, project) {
686
+ const stepList = steps.map((s) => `${s.order}. [${s.action.toUpperCase()}] ${s.file}
687
+ ${s.description}`).join("\n");
688
+ return `You are implementing a task for BrainForge AI.
689
+
690
+ Project: ${project.name} (${project.language}${project.framework ? "/" + project.framework : ""})
691
+ Developer Type: ${project.type}${project.studentLevel ? " / " + project.studentLevel : ""}
692
+
693
+ ## Task
694
+ ${title}
695
+
696
+ ${description}
697
+
698
+ ## Implementation Steps
699
+ ${stepList}
700
+
701
+ ## Instructions
702
+ - Follow the steps in order.
703
+ - After each file change, verify correctness before moving to the next step.
704
+ - Use existing patterns and conventions already in the codebase.
705
+ - Do not introduce dependencies not already in use unless necessary.
706
+ ${project.type === "student" ? "- Write code you can explain line-by-line \u2014 you will be asked to defend it." : ""}`;
707
+ }
708
+ };
709
+
710
+ // src/professor/scanner.ts
711
+ import fs3 from "fs/promises";
712
+ import path3 from "path";
713
+
714
+ // src/professor/patterns.ts
715
+ var PATTERNS = [
716
+ // ── JavaScript / TypeScript ─────────────────────────────────────────────────
717
+ {
718
+ id: "js-currying",
719
+ name: "Curried function",
720
+ explanation: "Currying (returning functions from functions) is an advanced functional programming pattern.",
721
+ regex: /=>\s*(?:\([^)]*\)|[a-zA-Z_$]\w*)\s*=>/,
722
+ languages: ["javascript", "typescript"],
723
+ flagForLevels: ["beginner"],
724
+ severity: "warn"
725
+ },
726
+ {
727
+ id: "js-generator",
728
+ name: "Generator function",
729
+ explanation: "`function*` and `yield` are advanced control-flow mechanisms.",
730
+ regex: /function\s*\*|yield\s+/,
731
+ languages: ["javascript", "typescript"],
732
+ flagForLevels: ["beginner"],
733
+ severity: "warn"
734
+ },
735
+ {
736
+ id: "js-proxy",
737
+ name: "Proxy / Reflect",
738
+ explanation: "Proxy and Reflect are advanced metaprogramming APIs.",
739
+ regex: /\bnew\s+Proxy\s*\(|\bReflect\./,
740
+ languages: ["javascript", "typescript"],
741
+ flagForLevels: ["beginner", "intermediate"],
742
+ severity: "error"
743
+ },
744
+ {
745
+ id: "js-symbol",
746
+ name: "Symbol usage",
747
+ explanation: "Symbols are a low-level primitive mainly used in library/framework internals.",
748
+ regex: /\bSymbol\s*\(/,
749
+ languages: ["javascript", "typescript"],
750
+ flagForLevels: ["beginner"],
751
+ severity: "warn"
752
+ },
753
+ {
754
+ id: "js-define-property",
755
+ name: "Object.defineProperty",
756
+ explanation: "Manual property descriptor manipulation is metaprogramming \u2014 rarely needed in application code.",
757
+ regex: /Object\.defineProperty\s*\(/,
758
+ languages: ["javascript", "typescript"],
759
+ flagForLevels: ["beginner", "intermediate"],
760
+ severity: "warn"
761
+ },
762
+ {
763
+ id: "js-decorator",
764
+ name: "Decorator",
765
+ explanation: "Decorators are a TypeScript/Stage-3 feature for class metaprogramming.",
766
+ regex: /^\s*@\w+/m,
767
+ languages: ["javascript", "typescript"],
768
+ flagForLevels: ["beginner"],
769
+ severity: "warn"
770
+ },
771
+ {
772
+ id: "ts-conditional-type",
773
+ name: "Conditional type / infer",
774
+ explanation: "`infer` in conditional types is an advanced TypeScript type-system feature.",
775
+ regex: /\binfer\s+\w+/,
776
+ languages: ["typescript"],
777
+ flagForLevels: ["beginner", "intermediate"],
778
+ severity: "warn"
779
+ },
780
+ {
781
+ id: "js-weakmap",
782
+ name: "WeakMap / WeakSet",
783
+ explanation: "Weak collections are a memory-management tool rarely needed in student projects.",
784
+ regex: /\bnew\s+Weak(?:Map|Set|Ref)\s*\(/,
785
+ languages: ["javascript", "typescript"],
786
+ flagForLevels: ["beginner"],
787
+ severity: "warn"
788
+ },
789
+ // ── Python ───────────────────────────────────────────────────────────────────
790
+ {
791
+ id: "py-metaclass",
792
+ name: "Metaclass",
793
+ explanation: "Metaclasses override Python class creation machinery \u2014 an expert-level feature.",
794
+ regex: /\bmetaclass\s*=/,
795
+ languages: ["python"],
796
+ flagForLevels: ["beginner", "intermediate"],
797
+ severity: "error"
798
+ },
799
+ {
800
+ id: "py-descriptor",
801
+ name: "Descriptor protocol",
802
+ explanation: "`__get__` / `__set__` / `__delete__` implement the descriptor protocol used in framework internals.",
803
+ regex: /def\s+__(?:get|set|delete)__\s*\(/,
804
+ languages: ["python"],
805
+ flagForLevels: ["beginner", "intermediate"],
806
+ severity: "warn"
807
+ },
808
+ {
809
+ id: "py-context-manager",
810
+ name: "Custom context manager",
811
+ explanation: "Implementing `__enter__` / `__exit__` is an intermediate-to-advanced pattern.",
812
+ regex: /def\s+__(?:enter|exit)__\s*\(/,
813
+ languages: ["python"],
814
+ flagForLevels: ["beginner"],
815
+ severity: "warn"
816
+ },
817
+ {
818
+ id: "py-abc",
819
+ name: "Abstract Base Class",
820
+ explanation: "`abc.ABC` and `@abstractmethod` are used to define interfaces \u2014 uncommon in intro projects.",
821
+ regex: /from\s+abc\s+import|ABC\s*\)|\@abstractmethod/,
822
+ languages: ["python"],
823
+ flagForLevels: ["beginner"],
824
+ severity: "warn"
825
+ },
826
+ {
827
+ id: "py-generator-send",
828
+ name: "Generator.send()",
829
+ explanation: "`.send()` on generators is an advanced coroutine-like pattern.",
830
+ regex: /\.\s*send\s*\(/,
831
+ languages: ["python"],
832
+ flagForLevels: ["beginner"],
833
+ severity: "warn"
834
+ },
835
+ // ── Java ──────────────────────────────────────────────────────────────────────
836
+ {
837
+ id: "java-reflection",
838
+ name: "Reflection API",
839
+ explanation: "Java Reflection bypasses compile-time checks and is a framework-level tool, not student code.",
840
+ regex: /\.getDeclaredMethod|\.getDeclaredField|Class\.forName\s*\(/,
841
+ languages: ["java"],
842
+ flagForLevels: ["beginner", "intermediate"],
843
+ severity: "error"
844
+ },
845
+ {
846
+ id: "java-generic-wildcard",
847
+ name: "Unbounded wildcard",
848
+ explanation: "`<?>` wildcards and generic bounds are advanced generics topics.",
849
+ regex: /<\s*\?\s*(?:extends|super)/,
850
+ languages: ["java"],
851
+ flagForLevels: ["beginner"],
852
+ severity: "warn"
853
+ }
854
+ ];
855
+
856
+ // src/professor/scanner.ts
857
+ var LANGUAGE_EXTENSIONS2 = {
858
+ typescript: [".ts", ".tsx"],
859
+ javascript: [".js", ".jsx", ".mjs"],
860
+ python: [".py"],
861
+ java: [".java"],
862
+ go: [".go"],
863
+ php: [".php"]
864
+ };
865
+ var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
866
+ "node_modules",
867
+ ".git",
868
+ ".brainforge",
869
+ "dist",
870
+ "build",
871
+ "coverage",
872
+ "__pycache__",
873
+ ".venv",
874
+ "vendor"
875
+ ]);
876
+ var ProfessorScanner = class {
877
+ constructor(workingDir) {
878
+ this.workingDir = workingDir;
879
+ }
880
+ workingDir;
881
+ async scan(language, studentLevel) {
882
+ const applicable = PATTERNS.filter(
883
+ (p) => p.languages.includes(language) && p.flagForLevels.includes(studentLevel)
884
+ );
885
+ const extensions = LANGUAGE_EXTENSIONS2[language];
886
+ const files = await this.findFiles(extensions);
887
+ const matches = [];
888
+ for (const relPath of files) {
889
+ const content = await fs3.readFile(path3.join(this.workingDir, relPath), "utf-8").catch(() => null);
890
+ if (!content) continue;
891
+ const lines = content.split("\n");
892
+ for (const pattern of applicable) {
893
+ for (let i = 0; i < lines.length; i++) {
894
+ const line = lines[i];
895
+ if (pattern.regex.test(line)) {
896
+ matches.push({
897
+ patternId: pattern.id,
898
+ name: pattern.name,
899
+ explanation: pattern.explanation,
900
+ severity: pattern.severity,
901
+ file: relPath.replace(/\\/g, "/"),
902
+ line: i + 1,
903
+ snippet: line.trim().slice(0, 120)
904
+ });
905
+ }
906
+ }
907
+ pattern.regex.lastIndex = 0;
908
+ }
909
+ }
910
+ const errors = matches.filter((m) => m.severity === "error").length;
911
+ const warnings = matches.filter((m) => m.severity === "warn").length;
912
+ const report = {
913
+ scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
914
+ language,
915
+ studentLevel,
916
+ totalFilesScanned: files.length,
917
+ matches,
918
+ summary: { errors, warnings, clean: matches.length === 0 }
919
+ };
920
+ await this.writeReport(report);
921
+ return report;
922
+ }
923
+ async findFiles(extensions) {
924
+ const results = [];
925
+ await this.walk("", extensions, results);
926
+ return results;
927
+ }
928
+ async walk(rel, extensions, results) {
929
+ const dir = rel ? path3.join(this.workingDir, rel) : this.workingDir;
930
+ let entries;
931
+ try {
932
+ entries = await fs3.readdir(dir, { withFileTypes: true });
933
+ } catch {
934
+ return;
935
+ }
936
+ for (const entry of entries) {
937
+ if (IGNORED_DIRS2.has(entry.name)) continue;
938
+ const relPath = rel ? `${rel}/${entry.name}` : entry.name;
939
+ if (entry.isDirectory()) {
940
+ await this.walk(relPath, extensions, results);
941
+ } else if (extensions.some((ext) => entry.name.endsWith(ext))) {
942
+ results.push(relPath);
943
+ }
944
+ }
945
+ }
946
+ async writeReport(report) {
947
+ const lines = [
948
+ "# Professor Report",
949
+ "",
950
+ `**Scanned:** ${report.scannedAt}`,
951
+ `**Language:** ${report.language} | **Level:** ${report.studentLevel}`,
952
+ `**Files scanned:** ${report.totalFilesScanned}`,
953
+ `**Result:** ${report.summary.clean ? "CLEAN" : `${report.summary.errors} error(s), ${report.summary.warnings} warning(s)`}`,
954
+ ""
955
+ ];
956
+ if (report.summary.clean) {
957
+ lines.push("No advanced patterns detected. Your code matches your declared level.");
958
+ } else {
959
+ lines.push("## Findings", "");
960
+ for (const m of report.matches) {
961
+ const icon = m.severity === "error" ? "[ERROR]" : "[WARN]";
962
+ lines.push(
963
+ `### ${icon} ${m.name}`,
964
+ `**File:** \`${m.file}\` (line ${m.line})`,
965
+ `**Pattern:** \`${m.snippet}\``,
966
+ `**Why this matters:** ${m.explanation}`,
967
+ ""
968
+ );
969
+ }
970
+ lines.push(
971
+ "---",
972
+ "",
973
+ "> These findings do not mean your code is wrong. They mean you may be asked",
974
+ "> to explain these patterns in detail during a code defense.",
975
+ ""
976
+ );
977
+ }
978
+ const dest = path3.join(this.workingDir, "PROFESSOR_REPORT.md");
979
+ await fs3.writeFile(dest, lines.join("\n"), "utf-8");
980
+ }
981
+ };
982
+
983
+ // src/server/server.ts
984
+ import http from "http";
985
+ import fs4 from "fs/promises";
986
+ import path4 from "path";
987
+ import { fileURLToPath } from "url";
988
+ import { WebSocketServer, WebSocket } from "ws";
989
+ var MIME = {
990
+ ".html": "text/html; charset=utf-8",
991
+ ".js": "application/javascript",
992
+ ".css": "text/css",
993
+ ".json": "application/json",
994
+ ".svg": "image/svg+xml",
995
+ ".ico": "image/x-icon",
996
+ ".woff2": "font/woff2"
997
+ };
998
+ var BrainForgeServer = class {
999
+ constructor(workingDir, port = 3742, dashboardDir) {
1000
+ this.workingDir = workingDir;
1001
+ this.port = port;
1002
+ const here = path4.dirname(fileURLToPath(import.meta.url));
1003
+ this.dashboardDir = dashboardDir ?? path4.resolve(here, "../../../dashboard/dist");
1004
+ }
1005
+ workingDir;
1006
+ port;
1007
+ httpServer;
1008
+ wss;
1009
+ clients = /* @__PURE__ */ new Set();
1010
+ dashboardDir;
1011
+ boundPort;
1012
+ async start() {
1013
+ this.httpServer = http.createServer((req, res) => {
1014
+ void this.handle(req, res);
1015
+ });
1016
+ this.wss = new WebSocketServer({ server: this.httpServer });
1017
+ this.wss.on("connection", (ws) => {
1018
+ this.clients.add(ws);
1019
+ ws.on("close", () => this.clients.delete(ws));
1020
+ ws.send(JSON.stringify({ type: "connected", payload: { version: "3.0.0" } }));
1021
+ });
1022
+ await new Promise(
1023
+ (resolve, reject) => this.httpServer.listen(this.port, "127.0.0.1", () => {
1024
+ this.boundPort = this.httpServer.address().port;
1025
+ resolve();
1026
+ }).on("error", reject)
1027
+ );
1028
+ }
1029
+ stop() {
1030
+ this.clients.forEach((ws) => ws.close());
1031
+ this.wss?.close();
1032
+ this.httpServer?.close();
1033
+ }
1034
+ broadcast(type, payload) {
1035
+ const msg = JSON.stringify({ type, payload, ts: (/* @__PURE__ */ new Date()).toISOString() });
1036
+ for (const ws of this.clients) {
1037
+ if (ws.readyState === WebSocket.OPEN) ws.send(msg);
1038
+ }
1039
+ }
1040
+ get url() {
1041
+ return `http://127.0.0.1:${this.boundPort}`;
1042
+ }
1043
+ async handle(req, res) {
1044
+ const { pathname } = new URL(req.url ?? "/", `http://localhost`);
1045
+ res.setHeader("Access-Control-Allow-Origin", "*");
1046
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
1047
+ try {
1048
+ if (pathname === "/api/health") return this.json(res, { ok: true, version: "3.0.0" });
1049
+ if (pathname === "/api/state") return await this.fileJson(res, ".brainforge/core.json");
1050
+ if (pathname === "/api/map") return await this.fileJson(res, ".brainforge/codebase-map.json");
1051
+ if (pathname === "/api/scan") return await this.fileText(res, "PROFESSOR_REPORT.md");
1052
+ await this.static(res, pathname);
1053
+ } catch (err) {
1054
+ res.writeHead(500, { "Content-Type": "application/json" });
1055
+ res.end(JSON.stringify({ error: String(err) }));
1056
+ }
1057
+ }
1058
+ json(res, data) {
1059
+ res.writeHead(200, { "Content-Type": "application/json" });
1060
+ res.end(JSON.stringify(data));
1061
+ }
1062
+ async fileJson(res, rel) {
1063
+ try {
1064
+ const raw = await fs4.readFile(path4.join(this.workingDir, rel), "utf-8");
1065
+ res.writeHead(200, { "Content-Type": "application/json" });
1066
+ res.end(raw);
1067
+ } catch {
1068
+ res.writeHead(404, { "Content-Type": "application/json" });
1069
+ res.end(JSON.stringify({ error: "not found" }));
1070
+ }
1071
+ }
1072
+ async fileText(res, rel) {
1073
+ try {
1074
+ const raw = await fs4.readFile(path4.join(this.workingDir, rel), "utf-8");
1075
+ res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
1076
+ res.end(raw);
1077
+ } catch {
1078
+ res.writeHead(404, { "Content-Type": "text/plain" });
1079
+ res.end("");
1080
+ }
1081
+ }
1082
+ async static(res, pathname) {
1083
+ let target = path4.join(this.dashboardDir, pathname === "/" ? "index.html" : pathname);
1084
+ let found = await exists(target);
1085
+ if (!found) {
1086
+ target = path4.join(this.dashboardDir, "index.html");
1087
+ found = await exists(target);
1088
+ }
1089
+ if (!found) {
1090
+ res.writeHead(404, { "Content-Type": "text/plain" });
1091
+ res.end("Dashboard not built. Run: npm run build --workspace=packages/dashboard");
1092
+ return;
1093
+ }
1094
+ const ext = path4.extname(target);
1095
+ res.writeHead(200, { "Content-Type": MIME[ext] ?? "application/octet-stream" });
1096
+ res.end(await fs4.readFile(target));
1097
+ }
1098
+ };
1099
+ async function exists(p) {
1100
+ return fs4.access(p).then(() => true).catch(() => false);
1101
+ }
1102
+
1103
+ // src/defense/defense.ts
1104
+ import fs5 from "fs/promises";
1105
+ import path5 from "path";
1106
+
1107
+ // src/defense/question-bank.ts
1108
+ var STATIC_QUESTIONS = [
1109
+ // ── TypeScript / JavaScript ─────────────────────────────────────────────
1110
+ {
1111
+ id: "js-promise-explain",
1112
+ text: "What is a Promise, and why did you use async/await instead of .then()/.catch()?",
1113
+ modelAnswer: "A Promise represents a value that will be available in the future. async/await is syntactic sugar over Promises that makes asynchronous code read like synchronous code, improving readability and making error handling with try/catch more natural.",
1114
+ difficulty: "easy",
1115
+ languages: ["typescript", "javascript"],
1116
+ levels: ["beginner", "intermediate"]
1117
+ },
1118
+ {
1119
+ id: "js-export-explain",
1120
+ text: "What does `export` do? What is the difference between `export default` and named exports?",
1121
+ modelAnswer: "`export` makes a value available to other modules. Named exports (`export function foo`) let you export multiple things per file, imported with `{ foo }`. `export default` exports one main value per file, imported without braces.",
1122
+ difficulty: "easy",
1123
+ languages: ["typescript", "javascript"],
1124
+ levels: ["beginner"]
1125
+ },
1126
+ {
1127
+ id: "ts-interface-type",
1128
+ text: "When would you use an `interface` versus a `type` alias in TypeScript?",
1129
+ modelAnswer: "Both define shapes for objects. `interface` is preferred for object shapes that may be extended or implemented by classes. `type` is more flexible \u2014 it can represent unions, intersections, and primitives. In practice, either works for plain object shapes.",
1130
+ difficulty: "medium",
1131
+ languages: ["typescript"],
1132
+ levels: ["beginner", "intermediate"]
1133
+ },
1134
+ {
1135
+ id: "ts-any-vs-unknown",
1136
+ text: "Why is `unknown` safer than `any` in TypeScript?",
1137
+ modelAnswer: "`unknown` forces you to narrow the type before using it (via typeof, instanceof, or type guards), preventing accidental unsafe operations. `any` disables all type checking. Prefer `unknown` whenever the type is genuinely unknown at compile time.",
1138
+ difficulty: "medium",
1139
+ languages: ["typescript"],
1140
+ levels: ["intermediate", "advanced"]
1141
+ },
1142
+ {
1143
+ id: "js-closure",
1144
+ text: "What is a closure, and can you find an example in your code?",
1145
+ modelAnswer: "A closure is a function that captures variables from its surrounding scope even after that scope has returned. Example: a callback defined inside a function that references the outer function's variables. Closures power module patterns, event handlers, and factory functions.",
1146
+ difficulty: "medium",
1147
+ languages: ["javascript", "typescript"],
1148
+ levels: ["beginner", "intermediate"]
1149
+ },
1150
+ {
1151
+ id: "js-this-binding",
1152
+ text: "Explain how `this` binding works in regular functions vs. arrow functions.",
1153
+ modelAnswer: "In regular functions, `this` is determined at call time \u2014 it's the object before the dot, or `undefined` in strict mode. Arrow functions capture `this` lexically from their enclosing scope and never rebind it. This is why class methods often use arrow functions to avoid losing `this` context in callbacks.",
1154
+ difficulty: "medium",
1155
+ languages: ["javascript", "typescript"],
1156
+ levels: ["intermediate"]
1157
+ },
1158
+ {
1159
+ id: "js-event-loop",
1160
+ text: "What is the JavaScript event loop and why can't you block it?",
1161
+ modelAnswer: "The event loop is a single-threaded mechanism that processes one task at a time from a task queue. Blocking it (e.g., with a long synchronous loop) prevents all other code \u2014 including UI updates or incoming requests \u2014 from running. Async operations (I/O, timers) are handled off-thread and their callbacks are queued when done.",
1162
+ difficulty: "hard",
1163
+ languages: ["javascript", "typescript"],
1164
+ levels: ["intermediate", "advanced"]
1165
+ },
1166
+ {
1167
+ id: "js-prototype",
1168
+ text: "How does prototypal inheritance work in JavaScript? How does `class` syntax relate to it?",
1169
+ modelAnswer: "Every object has a `[[Prototype]]` (accessible via `__proto__`). When a property is not found on an object, JS looks up the prototype chain. `class` is syntactic sugar over prototypal inheritance \u2014 `class Foo extends Bar` sets up the prototype chain automatically.",
1170
+ difficulty: "hard",
1171
+ languages: ["javascript", "typescript"],
1172
+ levels: ["intermediate", "advanced"]
1173
+ },
1174
+ // ── Python ───────────────────────────────────────────────────────────────
1175
+ {
1176
+ id: "py-decorator-explain",
1177
+ text: "What is a Python decorator and what does it do to the function it wraps?",
1178
+ modelAnswer: "A decorator is a function that takes a function as input and returns a modified version. `@my_decorator` before a function is syntactic sugar for `my_func = my_decorator(my_func)`. Decorators are commonly used for logging, authentication checks, caching, and input validation.",
1179
+ difficulty: "medium",
1180
+ languages: ["python"],
1181
+ levels: ["intermediate", "advanced"]
1182
+ },
1183
+ {
1184
+ id: "py-list-comp",
1185
+ text: "What does this list comprehension do: `[x**2 for x in range(10) if x % 2 == 0]`?",
1186
+ modelAnswer: "It creates a list of squares of even numbers from 0 to 9: [0, 4, 16, 36, 64]. List comprehensions combine a for loop and optional filter into a single readable expression, and are generally faster than equivalent for loops.",
1187
+ difficulty: "easy",
1188
+ languages: ["python"],
1189
+ levels: ["beginner", "intermediate"]
1190
+ },
1191
+ {
1192
+ id: "py-dunder",
1193
+ text: "What are dunder (double-underscore) methods? Give two examples and explain when Python calls them.",
1194
+ modelAnswer: "Dunder methods (like `__init__`, `__str__`, `__len__`) define how objects respond to built-in operations. `__init__` is called when an instance is created. `__str__` is called when you use `str()` or `print()` on an object. They let you make your classes behave like built-in Python types.",
1195
+ difficulty: "medium",
1196
+ languages: ["python"],
1197
+ levels: ["beginner", "intermediate"]
1198
+ },
1199
+ {
1200
+ id: "py-generator-basics",
1201
+ text: "What is a generator function (`yield`)? When would you choose it over returning a list?",
1202
+ modelAnswer: "A generator function yields values one at a time rather than building a full list in memory. Each call to `next()` resumes execution until the next `yield`. Use generators when the full collection is large or infinite \u2014 they're memory-efficient because they produce values lazily.",
1203
+ difficulty: "medium",
1204
+ languages: ["python"],
1205
+ levels: ["intermediate", "advanced"]
1206
+ },
1207
+ // ── Java ─────────────────────────────────────────────────────────────────
1208
+ {
1209
+ id: "java-interface-abstract",
1210
+ text: "What is the difference between a Java `interface` and an `abstract class`?",
1211
+ modelAnswer: "An interface defines a contract of method signatures (all abstract by default, can have default/static methods since Java 8). An abstract class can have both abstract and concrete methods plus state (fields). A class can implement multiple interfaces but extend only one class. Use interfaces for capabilities (Comparable, Serializable), abstract classes for shared implementation.",
1212
+ difficulty: "medium",
1213
+ languages: ["java"],
1214
+ levels: ["beginner", "intermediate"]
1215
+ },
1216
+ {
1217
+ id: "java-generics",
1218
+ text: "What do generics (`<T>`) provide in Java?",
1219
+ modelAnswer: "Generics provide compile-time type safety for collections and methods. Instead of `List` (which accepts `Object` and requires casting), `List<String>` only allows Strings. The type parameter `<T>` is erased at runtime (type erasure), but the compiler enforces correctness.",
1220
+ difficulty: "medium",
1221
+ languages: ["java"],
1222
+ levels: ["intermediate"]
1223
+ }
1224
+ ];
1225
+ var PATTERN_QUESTIONS = {
1226
+ "js-currying": {
1227
+ text: "Explain the currying pattern used in your code. What problem does it solve here?",
1228
+ modelAnswer: "Currying transforms a function that takes multiple arguments into a chain of functions each taking one argument. It enables partial application (pre-filling some arguments) and makes code more composable. However, it reduces readability for beginners and should only be used when the functional pattern genuinely clarifies intent.",
1229
+ difficulty: "hard"
1230
+ },
1231
+ "js-generator": {
1232
+ text: "You used a generator function (`function*`). Walk me through what happens step-by-step when code calls `.next()` on it.",
1233
+ modelAnswer: "Calling the generator returns an iterator without executing any code. Each `.next()` call runs until the next `yield` expression, pauses, and returns `{ value: yieldedValue, done: false }`. After the function returns, `done` becomes `true`. This lazy execution is why generators suit streaming or infinite sequences.",
1234
+ difficulty: "hard"
1235
+ },
1236
+ "js-proxy": {
1237
+ text: "Explain what `new Proxy(target, handler)` does. Why is it typically not found in student code?",
1238
+ modelAnswer: "A Proxy wraps an object and intercepts fundamental operations (get, set, has, etc.) via handler traps. It's used in frameworks for reactivity, validation, and ORM magic. In student code it's rare because it's a metaprogramming tool \u2014 before using it you must fully understand the operations it intercepts and its performance implications.",
1239
+ difficulty: "hard"
1240
+ },
1241
+ "py-metaclass": {
1242
+ text: "You used a metaclass. Explain what it does and why this was the right choice over a simpler alternative.",
1243
+ modelAnswer: 'A metaclass is "the class of a class" \u2014 it controls how classes are created. Common uses: registering subclasses, validating class definitions, adding methods automatically (like ORMs do). Alternatives like `__init_subclass__` or decorators usually achieve the same goal with less complexity. If this is in student code, the professor will ask you to justify why a metaclass was necessary.',
1244
+ difficulty: "hard"
1245
+ },
1246
+ "java-reflection": {
1247
+ text: "You used Java Reflection. Explain what it does, what the risks are, and why a direct approach wouldn't work here.",
1248
+ modelAnswer: "Reflection lets you inspect and call methods/fields at runtime by name, bypassing compile-time type checks. Risks: it's slow, breaks type safety, can bypass access modifiers, and makes code hard to follow. It's appropriate in frameworks (dependency injection, serialization) where the type isn't known at compile time. In most student use cases, a direct method call or interface would be simpler and safer.",
1249
+ difficulty: "hard"
1250
+ }
1251
+ };
1252
+ function buildQuestionSet(project, map, report, maxQuestions = 8) {
1253
+ const level = project.studentLevel ?? "intermediate";
1254
+ const lang = project.language;
1255
+ const questions = [];
1256
+ if (report) {
1257
+ for (const match of report.matches) {
1258
+ const pq = PATTERN_QUESTIONS[match.patternId];
1259
+ if (pq && questions.length < 4) {
1260
+ questions.push(pq);
1261
+ }
1262
+ }
1263
+ }
1264
+ const relevant = STATIC_QUESTIONS.filter(
1265
+ (q) => q.languages.includes(lang) && q.levels.includes(level)
1266
+ );
1267
+ const shuffled = relevant.sort(() => Math.random() - 0.5);
1268
+ for (const q of shuffled) {
1269
+ if (questions.length >= maxQuestions) break;
1270
+ questions.push({ text: q.text, modelAnswer: q.modelAnswer, difficulty: q.difficulty });
1271
+ }
1272
+ const allFunctions = map.files.flatMap((f) => f.functions.map((fn) => ({ file: f.path, fn })));
1273
+ const publicFns = allFunctions.filter((x) => x.fn.isExported).slice(0, 3);
1274
+ for (const { file, fn } of publicFns) {
1275
+ if (questions.length >= maxQuestions) break;
1276
+ questions.push({
1277
+ text: `Explain what \`${fn.name}()\` in \`${file}\` does and why it's exported.`,
1278
+ modelAnswer: `[Code-specific \u2014 answer based on your implementation of ${fn.name}]`,
1279
+ difficulty: "easy"
1280
+ });
1281
+ }
1282
+ return questions.slice(0, maxQuestions);
1283
+ }
1284
+
1285
+ // src/defense/defense.ts
1286
+ var MockDefense = class {
1287
+ constructor(workingDir) {
1288
+ this.workingDir = workingDir;
1289
+ }
1290
+ workingDir;
1291
+ selectQuestions(project, map, report, count = 8) {
1292
+ return buildQuestionSet(project, map, report, count);
1293
+ }
1294
+ async saveSession(session) {
1295
+ const lines = [
1296
+ "# Defense Prep Report",
1297
+ "",
1298
+ `**Project:** ${session.projectName}`,
1299
+ `**Language:** ${session.language} | **Level:** ${session.studentLevel}`,
1300
+ `**Completed:** ${session.completedAt}`,
1301
+ `**Average Confidence:** ${session.averageConfidence.toFixed(1)}/5`,
1302
+ "",
1303
+ "---",
1304
+ ""
1305
+ ];
1306
+ for (let i = 0; i < session.questions.length; i++) {
1307
+ const q = session.questions[i];
1308
+ const stars = "\u2605".repeat(q.confidence) + "\u2606".repeat(5 - q.confidence);
1309
+ lines.push(
1310
+ `## Q${i + 1} [${stars}]`,
1311
+ "",
1312
+ `**Question:** ${q.question}`,
1313
+ "",
1314
+ "**Your answer:**",
1315
+ q.answer || "*(no answer provided)*",
1316
+ "",
1317
+ "**Model answer:**",
1318
+ q.modelAnswer,
1319
+ "",
1320
+ "---",
1321
+ ""
1322
+ );
1323
+ }
1324
+ const lowConfidence = session.questions.filter((q) => q.confidence <= 2);
1325
+ if (lowConfidence.length > 0) {
1326
+ lines.push(
1327
+ "## Areas to Review",
1328
+ "",
1329
+ ...lowConfidence.map((q, i) => `${i + 1}. ${q.question}`),
1330
+ ""
1331
+ );
1332
+ }
1333
+ await fs5.writeFile(
1334
+ path5.join(this.workingDir, "DEFENSE_PREP.md"),
1335
+ lines.join("\n"),
1336
+ "utf-8"
1337
+ );
1338
+ }
1339
+ };
1340
+
1341
+ // src/skills/registry.ts
1342
+ import fs6 from "fs/promises";
1343
+ import path6 from "path";
1344
+ import Handlebars2 from "handlebars";
1345
+
1346
+ // src/skills/built-in.ts
1347
+ var BUILT_IN_SKILLS = [
1348
+ // ── Backend ──────────────────────────────────────────────────────────────────
1349
+ {
1350
+ id: "backend/crud-service",
1351
+ name: "CRUD Service",
1352
+ category: "backend",
1353
+ description: "Generate a complete CRUD service for an entity",
1354
+ template: `Generate a complete CRUD service for "{{entity}}" in {{project.language}}{{#if project.framework}} using {{project.framework}}{{/if}}.
1355
+
1356
+ Entity fields: {{fields}}
1357
+
1358
+ Include:
1359
+ - createOne: validate inputs, insert, return created entity
1360
+ - findById: return entity or throw NotFoundError
1361
+ - findAll: list with pagination (page, limit) and optional filter by {{filterField}}
1362
+ - updateOne: partial update, validate changed fields, return updated entity
1363
+ - deleteOne: {{#if softDelete}}soft delete (set deletedAt timestamp){{else}}hard delete{{/if}}
1364
+
1365
+ Requirements:
1366
+ - Full TypeScript types for the entity, CreateDTO, UpdateDTO, and responses
1367
+ - Input validation at the service boundary
1368
+ - Consistent error types (NotFoundError, ValidationError)
1369
+ - No direct database calls \u2014 use a repository/data-access abstraction
1370
+ {{#if (eq project.type "student")}}- Add JSDoc comments explaining each method{{/if}}`,
1371
+ contextSchema: [
1372
+ { key: "entity", label: "Entity name", type: "text", placeholder: "User" },
1373
+ { key: "fields", label: "Entity fields", type: "text", placeholder: "id: uuid, email: string, name: string, createdAt: date" },
1374
+ { key: "filterField", label: "Main filter field", type: "text", placeholder: "status", default: "status" },
1375
+ { key: "softDelete", label: "Use soft delete?", type: "boolean", default: true }
1376
+ ],
1377
+ tags: ["crud", "service", "api", "backend"],
1378
+ version: "1.0.0"
1379
+ },
1380
+ {
1381
+ id: "backend/api-endpoint",
1382
+ name: "REST API Endpoint",
1383
+ category: "backend",
1384
+ description: "Design a single REST API endpoint with full validation and error handling",
1385
+ template: `Design a {{method}} {{route}} endpoint in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1386
+
1387
+ Purpose: {{purpose}}
1388
+
1389
+ Implement:
1390
+ 1. Route handler with input validation (body/params/query)
1391
+ 2. Business logic delegation to a service layer
1392
+ 3. Success response shape: {{successShape}}
1393
+ 4. Error handling: 400 (validation), 401 (auth), 404 (not found), 500 (server)
1394
+ 5. Request/response TypeScript types
1395
+ 6. Middleware chain: auth \u2192 validate \u2192 handle \u2192 respond
1396
+
1397
+ Include a brief usage example (curl or fetch).`,
1398
+ contextSchema: [
1399
+ { key: "method", label: "HTTP method", type: "select", options: ["GET", "POST", "PUT", "PATCH", "DELETE"], default: "POST" },
1400
+ { key: "route", label: "Route path", type: "text", placeholder: "/api/users/:id" },
1401
+ { key: "purpose", label: "What does it do?", type: "text", placeholder: "Update a user's profile" },
1402
+ { key: "successShape", label: "Success response shape", type: "text", placeholder: "{ user: User, updatedAt: string }" }
1403
+ ],
1404
+ tags: ["api", "rest", "endpoint", "backend"],
1405
+ version: "1.0.0"
1406
+ },
1407
+ {
1408
+ id: "backend/auth-middleware",
1409
+ name: "Auth Middleware",
1410
+ category: "backend",
1411
+ description: "Implement authentication/authorization middleware",
1412
+ template: `Implement {{authType}} authentication middleware in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1413
+
1414
+ Requirements:
1415
+ - Extract token from Authorization header (Bearer scheme)
1416
+ - Validate the token (signature, expiry, claims)
1417
+ - Attach decoded user to request context
1418
+ - Return 401 with clear message on invalid/missing token
1419
+ - Return 403 if user lacks required role: {{requiredRole}}
1420
+ - Never leak token internals in error responses
1421
+
1422
+ Also implement:
1423
+ - A login endpoint that issues the token
1424
+ - A token refresh endpoint{{#if rememberMe}} with remember-me (long-lived refresh tokens){{/if}}
1425
+ - Logout (token invalidation/blacklist)
1426
+
1427
+ Security requirements:
1428
+ - Store secrets in environment variables, never hardcode
1429
+ - Use a well-tested crypto library, not custom crypto
1430
+ - Set short expiry on access tokens (15 min recommended)`,
1431
+ contextSchema: [
1432
+ { key: "authType", label: "Auth type", type: "select", options: ["JWT", "Session", "API Key", "OAuth2"], default: "JWT" },
1433
+ { key: "requiredRole", label: "Required role (optional)", type: "text", placeholder: "admin", default: "any authenticated user" },
1434
+ { key: "rememberMe", label: "Include remember-me?", type: "boolean", default: false }
1435
+ ],
1436
+ tags: ["auth", "jwt", "security", "middleware"],
1437
+ version: "1.0.0"
1438
+ },
1439
+ {
1440
+ id: "backend/error-handler",
1441
+ name: "Error Handler",
1442
+ category: "backend",
1443
+ description: "Centralized error handling with typed error classes",
1444
+ template: `Implement centralized error handling in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1445
+
1446
+ Create a typed error hierarchy:
1447
+ - AppError (base): message, statusCode, isOperational
1448
+ - ValidationError (400): field errors map
1449
+ - AuthenticationError (401)
1450
+ - AuthorizationError (403)
1451
+ - NotFoundError (404): resource name
1452
+ - ConflictError (409): duplicate resource
1453
+ - RateLimitError (429)
1454
+ - InternalError (500): hides internal details from clients
1455
+
1456
+ Global error middleware/handler that:
1457
+ - Catches all thrown errors
1458
+ - Logs operational errors at WARN, unexpected errors at ERROR
1459
+ - Returns JSON: { error: { code, message, {{#if includeDetails}}details{{/if}} } }
1460
+ - Never exposes stack traces or internal messages to clients
1461
+ - Handles async errors (unhandledRejection, uncaughtException)`,
1462
+ contextSchema: [
1463
+ { key: "includeDetails", label: "Include error details in dev mode?", type: "boolean", default: true }
1464
+ ],
1465
+ tags: ["error-handling", "middleware", "backend"],
1466
+ version: "1.0.0"
1467
+ },
1468
+ {
1469
+ id: "backend/validation-schema",
1470
+ name: "Validation Schema",
1471
+ category: "backend",
1472
+ description: "Create input validation schemas with a validation library",
1473
+ template: `Create validation schemas for {{entity}} in {{project.language}} using {{library}}.
1474
+
1475
+ Fields to validate:
1476
+ {{fields}}
1477
+
1478
+ For each field define:
1479
+ - Type (string, number, boolean, array, object, date)
1480
+ - Required vs optional
1481
+ - Constraints (min/max length, regex pattern, enum values, range)
1482
+ - Custom error messages in plain English
1483
+
1484
+ Also provide:
1485
+ - CreateSchema (all required fields)
1486
+ - UpdateSchema (all fields optional for PATCH)
1487
+ - QuerySchema (pagination + filters)
1488
+ - A validation utility function that returns typed errors`,
1489
+ contextSchema: [
1490
+ { key: "entity", label: "Entity name", type: "text", placeholder: "User" },
1491
+ { key: "fields", label: "Fields to validate", type: "text", placeholder: "email: string email, age: number 18-120, role: admin|user|viewer" },
1492
+ { key: "library", label: "Validation library", type: "select", options: ["Zod", "Joi", "Yup", "class-validator", "Pydantic", "built-in"], default: "Zod" }
1493
+ ],
1494
+ tags: ["validation", "schema", "backend"],
1495
+ version: "1.0.0"
1496
+ },
1497
+ {
1498
+ id: "backend/rate-limiter",
1499
+ name: "Rate Limiter",
1500
+ category: "backend",
1501
+ description: "Implement request rate limiting middleware",
1502
+ template: `Implement rate limiting middleware for a {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}} API.
1503
+
1504
+ Strategy: {{strategy}}
1505
+ Limits: {{limits}}
1506
+
1507
+ Requirements:
1508
+ - Track requests per {{window}} window per {{keyBy}}
1509
+ - Return 429 Too Many Requests with Retry-After header when exceeded
1510
+ - Include rate limit headers on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
1511
+ - Different limits for: authenticated users vs anonymous, admin vs regular endpoints
1512
+ - Storage backend: {{storage}} (must survive process restart in production)
1513
+ - Graceful degradation: if the rate limit store is unavailable, fail open (don't block requests) and log a warning
1514
+
1515
+ Provide the middleware, a config object, and an example of applying different limits to different route groups.`,
1516
+ contextSchema: [
1517
+ { key: "strategy", label: "Rate limit strategy", type: "select", options: ["Fixed window", "Sliding window", "Token bucket", "Leaky bucket"], default: "Sliding window" },
1518
+ { key: "limits", label: "Limits (e.g. 100 req/min per user)", type: "text", placeholder: "100 req/min per user, 1000 req/hour per IP" },
1519
+ { key: "window", label: "Time window", type: "select", options: ["1 minute", "5 minutes", "1 hour", "1 day"], default: "1 minute" },
1520
+ { key: "keyBy", label: "Key by", type: "select", options: ["IP address", "user ID", "API key", "IP + user ID"], default: "user ID" },
1521
+ { key: "storage", label: "Storage backend", type: "select", options: ["Redis", "in-memory (dev only)", "database"], default: "Redis" }
1522
+ ],
1523
+ tags: ["rate-limiting", "middleware", "backend", "security"],
1524
+ version: "1.0.0"
1525
+ },
1526
+ {
1527
+ id: "backend/caching",
1528
+ name: "Caching Layer",
1529
+ category: "backend",
1530
+ description: "Add a caching strategy to a service or endpoint",
1531
+ template: `Design a caching strategy for {{target}} in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1532
+
1533
+ What to cache: {{whatToCache}}
1534
+ Cache backend: {{cacheBackend}}
1535
+ TTL: {{ttl}}
1536
+
1537
+ Implement:
1538
+ 1. Cache-aside pattern: check cache \u2192 on miss, fetch from source \u2192 store in cache \u2192 return
1539
+ 2. Cache key design: deterministic, namespaced, versioned (e.g. \`{{project.name}}:v1:users:{id}\`)
1540
+ 3. Cache invalidation: {{invalidationStrategy}}
1541
+ 4. Stampede protection: single-flight / mutex for concurrent misses on the same key
1542
+ 5. Cache warming: pre-populate on startup for {{criticalKeys}}
1543
+
1544
+ Edge cases to handle:
1545
+ - Null/empty result caching (negative cache) to prevent DB hammering
1546
+ - Cache unavailability: fall through to source without crashing
1547
+ - Stale-while-revalidate for non-critical data
1548
+
1549
+ Provide helper functions for get-or-set and invalidate patterns.`,
1550
+ contextSchema: [
1551
+ { key: "target", label: "What to cache", type: "text", placeholder: "User profile endpoint GET /users/:id" },
1552
+ { key: "whatToCache", label: "Data being cached", type: "text", placeholder: "User objects after DB lookup" },
1553
+ { key: "cacheBackend", label: "Cache backend", type: "select", options: ["Redis", "Memcached", "in-memory (Map)", "CDN edge cache"], default: "Redis" },
1554
+ { key: "ttl", label: "Time-to-live", type: "text", default: "5 minutes" },
1555
+ { key: "invalidationStrategy", label: "Invalidation strategy", type: "select", options: ["TTL expiry only", "Event-based (on write)", "Manual + TTL", "Cache tags"], default: "Event-based (on write)" },
1556
+ { key: "criticalKeys", label: "Keys to warm on startup", type: "text", placeholder: "top 100 users, app config" }
1557
+ ],
1558
+ tags: ["caching", "redis", "performance", "backend"],
1559
+ version: "1.0.0"
1560
+ },
1561
+ {
1562
+ id: "backend/file-upload",
1563
+ name: "File Upload Handler",
1564
+ category: "backend",
1565
+ description: "Implement secure multipart file upload with storage and validation",
1566
+ template: `Implement a file upload handler for {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1567
+
1568
+ Upload type: {{uploadType}}
1569
+ Storage: {{storage}}
1570
+ Allowed types: {{allowedTypes}}
1571
+ Max size: {{maxSize}}
1572
+
1573
+ Requirements:
1574
+ - Validate file type by MIME type AND magic bytes (not just extension)
1575
+ - Enforce max file size: {{maxSize}} \u2014 reject before fully reading the stream
1576
+ - Sanitize filenames: strip path traversal characters, generate a UUID-based storage name
1577
+ - Virus scanning hook (placeholder if no scanner available)
1578
+ - Progress: emit upload progress events if using streaming
1579
+ - Store: {{storage}} with a public URL returned to the client
1580
+ - Multiple files: support up to {{maxFiles}} files per request
1581
+
1582
+ Security checklist:
1583
+ - Never serve uploaded files from the same origin as the app (use a separate domain or CDN)
1584
+ - Set Content-Disposition: attachment for downloads
1585
+ - Strip EXIF metadata from images
1586
+
1587
+ Return: { url, filename, size, mimeType, uploadedAt }`,
1588
+ contextSchema: [
1589
+ { key: "uploadType", label: "Upload type", type: "select", options: ["images only", "documents (PDF/DOC)", "any file", "videos"], default: "images only" },
1590
+ { key: "storage", label: "Storage destination", type: "select", options: ["AWS S3", "Cloudflare R2", "local disk (dev)", "Supabase Storage"], default: "AWS S3" },
1591
+ { key: "allowedTypes", label: "Allowed MIME types", type: "text", default: "image/jpeg, image/png, image/webp" },
1592
+ { key: "maxSize", label: "Max file size", type: "text", default: "5 MB" },
1593
+ { key: "maxFiles", label: "Max files per request", type: "text", default: "5" }
1594
+ ],
1595
+ tags: ["file-upload", "storage", "backend", "security"],
1596
+ version: "1.0.0"
1597
+ },
1598
+ {
1599
+ id: "backend/event-bus",
1600
+ name: "Event Bus",
1601
+ category: "backend",
1602
+ description: "Implement an in-process or distributed event bus",
1603
+ template: `Implement an event bus for {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1604
+
1605
+ Events to support: {{events}}
1606
+ Bus type: {{busType}}
1607
+
1608
+ Design:
1609
+ 1. Typed event definitions \u2014 each event has a name and a strict payload type
1610
+ 2. Publisher: emit(eventName, payload) \u2014 fire and forget, never blocks the caller
1611
+ 3. Subscriber: on(eventName, handler) \u2014 registers a handler, returns an unsubscribe function
1612
+ 4. Error isolation: a handler throwing must not prevent other handlers from running
1613
+ 5. Async handlers: all handlers run asynchronously, errors are caught and logged
1614
+
1615
+ {{#if (eq busType "distributed")}}
1616
+ For distributed (message queue):
1617
+ - Use {{broker}} as the broker
1618
+ - Persistent messages: events survive process restarts
1619
+ - At-least-once delivery: handlers must be idempotent
1620
+ - Dead letter queue for failed messages after 3 retries
1621
+ {{/if}}
1622
+
1623
+ Provide: EventBus class/module, event type definitions, and an example of wiring publisher and subscriber in the app bootstrap.`,
1624
+ contextSchema: [
1625
+ { key: "events", label: "Events (e.g. user.created, order.paid)", type: "text", placeholder: "user.registered, payment.succeeded, order.shipped" },
1626
+ { key: "busType", label: "Bus type", type: "select", options: ["in-process", "distributed"], default: "in-process" },
1627
+ { key: "broker", label: "Message broker (if distributed)", type: "select", options: ["Redis Pub/Sub", "RabbitMQ", "Kafka", "BullMQ"], default: "BullMQ" }
1628
+ ],
1629
+ tags: ["event-bus", "events", "architecture", "backend"],
1630
+ version: "1.0.0"
1631
+ },
1632
+ {
1633
+ id: "backend/websocket",
1634
+ name: "WebSocket Server",
1635
+ category: "backend",
1636
+ description: "Implement a real-time WebSocket server with room/channel support",
1637
+ template: `Implement a WebSocket server in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1638
+
1639
+ Use case: {{useCase}}
1640
+ Events: {{events}}
1641
+
1642
+ Requirements:
1643
+ - Connection lifecycle: connect \u2192 authenticate \u2192 join rooms \u2192 message \u2192 disconnect
1644
+ - Authentication: verify JWT on the initial handshake (reject unauthenticated connections)
1645
+ - Rooms/channels: clients can join/leave named rooms; broadcast to room members only
1646
+ - Message schema: { type, payload, roomId?, timestamp }
1647
+ - Heartbeat: ping/pong every 30s to detect dead connections; clean up on timeout
1648
+ - Reconnection: emit a "missed events" catch-up on reconnect using a cursor/sequence number
1649
+ - Backpressure: don't queue unlimited messages for slow clients \u2014 drop or pause after {{bufferSize}} queued messages
1650
+
1651
+ Client events to handle: {{events}}
1652
+ Server-to-client events to emit: {{serverEvents}}
1653
+
1654
+ Provide the server setup, a typed message protocol, and a minimal client-side connection handler.`,
1655
+ contextSchema: [
1656
+ { key: "useCase", label: "Use case", type: "text", placeholder: "Real-time collaborative document editing" },
1657
+ { key: "events", label: "Client events", type: "text", placeholder: "join-room, leave-room, send-message, typing" },
1658
+ { key: "serverEvents", label: "Server events", type: "text", placeholder: "message, user-joined, user-left, error" },
1659
+ { key: "bufferSize", label: "Max buffer size per client", type: "text", default: "100 messages" }
1660
+ ],
1661
+ tags: ["websocket", "realtime", "backend"],
1662
+ version: "1.0.0"
1663
+ },
1664
+ // ── Frontend ─────────────────────────────────────────────────────────────────
1665
+ {
1666
+ id: "frontend/react-component",
1667
+ name: "React Component",
1668
+ category: "frontend",
1669
+ description: "Scaffold a typed, accessible React component",
1670
+ template: `Create a React component called {{componentName}} in TypeScript.
1671
+
1672
+ Purpose: {{purpose}}
1673
+
1674
+ Props interface:
1675
+ {{props}}
1676
+
1677
+ Requirements:
1678
+ - Full TypeScript prop types with JSDoc on each prop
1679
+ - Accessibility: semantic HTML, ARIA attributes where needed, keyboard navigation
1680
+ - Loading and error states if the component fetches data
1681
+ - Memoization (React.memo, useMemo, useCallback) where it genuinely helps
1682
+ - Export: named export + default export
1683
+ {{#if includeStory}}- Storybook story with default, loading, and error variants{{/if}}
1684
+ {{#if includeTest}}- Jest + Testing Library unit tests for key interactions{{/if}}
1685
+
1686
+ Use the project's existing patterns \u2014 check for a component library or design system before creating primitives from scratch.`,
1687
+ contextSchema: [
1688
+ { key: "componentName", label: "Component name", type: "text", placeholder: "UserProfileCard" },
1689
+ { key: "purpose", label: "What does it do?", type: "text", placeholder: "Display a user's avatar, name, and role" },
1690
+ { key: "props", label: "Props (one per line)", type: "text", placeholder: 'userId: string\nonClick?: () => void\nsize?: "sm" | "md" | "lg"' },
1691
+ { key: "includeStory", label: "Include Storybook story?", type: "boolean", default: false },
1692
+ { key: "includeTest", label: "Include unit tests?", type: "boolean", default: true }
1693
+ ],
1694
+ tags: ["react", "component", "frontend", "typescript"],
1695
+ version: "1.0.0"
1696
+ },
1697
+ {
1698
+ id: "frontend/form-handler",
1699
+ name: "Form Handler",
1700
+ category: "frontend",
1701
+ description: "Implement a form with validation, submission, and error display",
1702
+ template: `Implement a {{formName}} form in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1703
+
1704
+ Fields:
1705
+ {{fields}}
1706
+
1707
+ Implement:
1708
+ - Controlled inputs with real-time validation
1709
+ - Field-level error messages shown after blur
1710
+ - Form-level error on submission failure (API error)
1711
+ - Submit button disabled while submitting
1712
+ - Success feedback after submission
1713
+ - Reset form on success
1714
+
1715
+ Validation rules: {{validationRules}}
1716
+ On submit: call {{submitAction}}
1717
+
1718
+ Use {{formLibrary}} for form state management if it's already in the project; otherwise use React useState.`,
1719
+ contextSchema: [
1720
+ { key: "formName", label: "Form name", type: "text", placeholder: "LoginForm" },
1721
+ { key: "fields", label: "Form fields", type: "text", placeholder: "email: email, password: password (min 8 chars)" },
1722
+ { key: "validationRules", label: "Validation rules", type: "text", placeholder: "email required valid format, password min 8 chars" },
1723
+ { key: "submitAction", label: "Submit action", type: "text", placeholder: "authService.login(data)" },
1724
+ { key: "formLibrary", label: "Form library", type: "select", options: ["React Hook Form", "Formik", "plain React state"], default: "React Hook Form" }
1725
+ ],
1726
+ tags: ["form", "validation", "frontend", "react"],
1727
+ version: "1.0.0"
1728
+ },
1729
+ {
1730
+ id: "frontend/api-client",
1731
+ name: "API Client",
1732
+ category: "frontend",
1733
+ description: "Build a typed HTTP client service for a REST API",
1734
+ template: `Create a typed API client service in {{project.language}} for the {{apiName}} API.
1735
+
1736
+ Base URL: {{baseUrl}}
1737
+
1738
+ Endpoints to support:
1739
+ {{endpoints}}
1740
+
1741
+ Requirements:
1742
+ - Single axios/fetch instance with base config
1743
+ - Automatic auth token injection from storage (localStorage or cookie)
1744
+ - Request/response interceptors: add auth header, handle 401 (auto-refresh or redirect to login)
1745
+ - Response typing: each endpoint returns a typed Promise
1746
+ - Error normalization: network errors and API errors both return an ApiError type
1747
+ - Timeout: {{timeout}}ms default
1748
+ - Retry: {{retries}} retries on network failure (not on 4xx)
1749
+
1750
+ Export individual functions, not a class.`,
1751
+ contextSchema: [
1752
+ { key: "apiName", label: "API name", type: "text", placeholder: "User API" },
1753
+ { key: "baseUrl", label: "Base URL", type: "text", placeholder: "/api/v1" },
1754
+ { key: "endpoints", label: "Endpoints (one per line)", type: "text", placeholder: "GET /users, POST /users, GET /users/:id, PUT /users/:id" },
1755
+ { key: "timeout", label: "Timeout (ms)", type: "text", default: "10000" },
1756
+ { key: "retries", label: "Retry count", type: "text", default: "3" }
1757
+ ],
1758
+ tags: ["api", "http", "client", "frontend"],
1759
+ version: "1.0.0"
1760
+ },
1761
+ {
1762
+ id: "frontend/responsive-layout",
1763
+ name: "Responsive Layout",
1764
+ category: "frontend",
1765
+ description: "Create a responsive CSS layout structure",
1766
+ template: `Design a responsive {{layoutName}} layout in {{project.language}}{{#if project.framework}} using {{project.framework}}{{/if}}.
1767
+
1768
+ Layout description: {{description}}
1769
+
1770
+ Breakpoints:
1771
+ - Mobile: < 640px \u2014 {{mobile}}
1772
+ - Tablet: 640px\u20131024px \u2014 {{tablet}}
1773
+ - Desktop: > 1024px \u2014 {{desktop}}
1774
+
1775
+ Requirements:
1776
+ - CSS Grid or Flexbox (explain the choice)
1777
+ - No magic numbers \u2014 use design tokens/CSS variables for spacing and color
1778
+ - Accessible: correct heading hierarchy, landmark regions (header, main, nav, footer)
1779
+ - Dark mode support via prefers-color-scheme
1780
+ - No horizontal scroll at any viewport width
1781
+ - Print-friendly (hide navigation, show content)`,
1782
+ contextSchema: [
1783
+ { key: "layoutName", label: "Layout name", type: "text", placeholder: "Dashboard" },
1784
+ { key: "description", label: "Layout description", type: "text", placeholder: "Sidebar + main content + top nav" },
1785
+ { key: "mobile", label: "Mobile layout", type: "text", placeholder: "single column, hamburger menu" },
1786
+ { key: "tablet", label: "Tablet layout", type: "text", placeholder: "collapsible sidebar, content" },
1787
+ { key: "desktop", label: "Desktop layout", type: "text", placeholder: "fixed sidebar, main + right panel" }
1788
+ ],
1789
+ tags: ["layout", "css", "responsive", "frontend"],
1790
+ version: "1.0.0"
1791
+ },
1792
+ {
1793
+ id: "frontend/state-management",
1794
+ name: "State Management",
1795
+ category: "frontend",
1796
+ description: "Set up a client-side state management store",
1797
+ template: `Set up {{library}} state management in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1798
+
1799
+ State to manage: {{stateDescription}}
1800
+ Slices/stores: {{slices}}
1801
+
1802
+ Requirements:
1803
+ - Separate concerns: server state (API data) vs client/UI state (modals, selected items)
1804
+ - Async actions: loading / success / error states for each API slice
1805
+ - Selectors: memoized derived state \u2014 don't recompute on unrelated changes
1806
+ - Persistence: {{persistence}} using localStorage/sessionStorage
1807
+ - DevTools integration: time-travel debugging in development
1808
+ - TypeScript: full type coverage \u2014 no \`any\` in store or selectors
1809
+
1810
+ For each slice provide:
1811
+ 1. State shape with TypeScript interface
1812
+ 2. Actions / reducers / thunks
1813
+ 3. Selectors
1814
+ 4. Example component consuming the slice
1815
+
1816
+ Also explain: when should this state live in the global store vs local component state?`,
1817
+ contextSchema: [
1818
+ { key: "library", label: "State library", type: "select", options: ["Zustand", "Redux Toolkit", "Jotai", "Recoil", "MobX"], default: "Zustand" },
1819
+ { key: "stateDescription", label: "What state to manage", type: "text", placeholder: "Current user auth, shopping cart, UI theme" },
1820
+ { key: "slices", label: "Slices / stores", type: "text", placeholder: "auth, cart, ui" },
1821
+ { key: "persistence", label: "Persist to storage?", type: "select", options: ["none", "auth only", "full store"], default: "auth only" }
1822
+ ],
1823
+ tags: ["state", "zustand", "redux", "frontend"],
1824
+ version: "1.0.0"
1825
+ },
1826
+ {
1827
+ id: "frontend/data-table",
1828
+ name: "Data Table",
1829
+ category: "frontend",
1830
+ description: "Build a data table with sorting, filtering, and pagination",
1831
+ template: `Build a {{tableName}} data table in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1832
+
1833
+ Data shape: {{dataShape}}
1834
+ Columns: {{columns}}
1835
+
1836
+ Features to implement:
1837
+ - Column sorting (click header to toggle asc/desc, multi-column sort optional)
1838
+ - Column filtering: text search + {{filterType}} per column
1839
+ - Pagination: {{paginationType}} with configurable page size
1840
+ - Row selection: {{selection}}
1841
+ - Column visibility toggle (show/hide columns)
1842
+ - Loading skeleton (not just a spinner \u2014 show column widths)
1843
+ - Empty state with helpful message
1844
+
1845
+ Performance:
1846
+ - Virtualize rows if dataset > 1000 items
1847
+ - Debounce filter inputs (300ms)
1848
+ - Memoize sort/filter computations
1849
+
1850
+ Accessibility:
1851
+ - Role="grid" with aria-sort on sortable headers
1852
+ - Keyboard navigation: arrow keys to move between cells
1853
+ - Announce sort/filter changes to screen readers`,
1854
+ contextSchema: [
1855
+ { key: "tableName", label: "Table name", type: "text", placeholder: "UsersTable" },
1856
+ { key: "dataShape", label: "Row data shape", type: "text", placeholder: "id, name, email, role, createdAt, status" },
1857
+ { key: "columns", label: "Visible columns", type: "text", placeholder: "Name, Email, Role, Status, Created At, Actions" },
1858
+ { key: "filterType", label: "Filter type", type: "select", options: ["text search only", "dropdown select", "date range", "text + dropdown"], default: "text + dropdown" },
1859
+ { key: "paginationType", label: "Pagination type", type: "select", options: ["page numbers", "cursor-based", "infinite scroll", "load more button"], default: "page numbers" },
1860
+ { key: "selection", label: "Row selection", type: "select", options: ["none", "single row", "multi-select with checkbox"], default: "multi-select with checkbox" }
1861
+ ],
1862
+ tags: ["table", "data-grid", "frontend"],
1863
+ version: "1.0.0"
1864
+ },
1865
+ {
1866
+ id: "frontend/auth-flow",
1867
+ name: "Auth UI Flow",
1868
+ category: "frontend",
1869
+ description: "Implement login, register, and protected route UI flow",
1870
+ template: `Implement the authentication UI flow in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1871
+
1872
+ Backend auth API: {{authApi}}
1873
+ Token storage: {{tokenStorage}}
1874
+
1875
+ Screens to build:
1876
+ 1. **Login form** \u2014 email + password, remember me, forgot password link
1877
+ 2. **Register form** \u2014 name, email, password + confirm, terms checkbox
1878
+ 3. **Forgot password** \u2014 email input, confirmation message
1879
+ 4. **Protected route wrapper** \u2014 redirect to /login if not authenticated, preserve intended URL
1880
+
1881
+ Auth state management:
1882
+ - Store: user profile + access token + refresh token + expiry
1883
+ - On app load: restore session from {{tokenStorage}}, validate token expiry
1884
+ - Auto-refresh: silently refresh access token {{refreshWindow}} before expiry
1885
+ - Logout: clear all tokens, invalidate session on server, redirect to /login
1886
+
1887
+ UX requirements:
1888
+ - Show loading state during auth operations
1889
+ - Surface backend error messages (wrong password, email taken)
1890
+ - Redirect to originally requested page after login
1891
+ - Logout confirmation only if user has unsaved work`,
1892
+ contextSchema: [
1893
+ { key: "authApi", label: "Auth API base", type: "text", placeholder: "/api/auth (POST /login, POST /register, POST /refresh)" },
1894
+ { key: "tokenStorage", label: "Token storage", type: "select", options: ["localStorage", "httpOnly cookie", "in-memory + refresh cookie"], default: "httpOnly cookie" },
1895
+ { key: "refreshWindow", label: "Refresh how early before expiry", type: "text", default: "2 minutes" }
1896
+ ],
1897
+ tags: ["auth", "login", "frontend", "react"],
1898
+ version: "1.0.0"
1899
+ },
1900
+ {
1901
+ id: "frontend/toast-notifications",
1902
+ name: "Toast Notifications",
1903
+ category: "frontend",
1904
+ description: "Implement a toast/snackbar notification system",
1905
+ template: `Implement a toast notification system in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1906
+
1907
+ Requirements:
1908
+ - Types: success, error, warning, info (with distinct colors and icons)
1909
+ - Auto-dismiss after {{autoDismiss}}s (configurable per toast)
1910
+ - Manual dismiss button on each toast
1911
+ - Stack multiple toasts (max {{maxToasts}} visible at once, queue the rest)
1912
+ - Position: {{position}}
1913
+ - Animations: slide-in on appear, fade-out on dismiss
1914
+ - Accessibility: role="alert" for errors, role="status" for info; auto-focus management
1915
+
1916
+ API design (imperative, usable anywhere in the app):
1917
+ \`\`\`
1918
+ toast.success("Profile saved")
1919
+ toast.error("Failed to save", { duration: 0 }) // persistent
1920
+ toast.promise(saveProfile(), { loading: "Saving...", success: "Saved!", error: "Failed" })
1921
+ \`\`\`
1922
+
1923
+ Provide: ToastProvider, useToast hook, and the individual Toast component.`,
1924
+ contextSchema: [
1925
+ { key: "position", label: "Toast position", type: "select", options: ["top-right", "top-center", "bottom-right", "bottom-center"], default: "top-right" },
1926
+ { key: "autoDismiss", label: "Auto-dismiss delay (seconds)", type: "text", default: "5" },
1927
+ { key: "maxToasts", label: "Max visible toasts", type: "text", default: "3" }
1928
+ ],
1929
+ tags: ["toast", "notifications", "ui", "frontend"],
1930
+ version: "1.0.0"
1931
+ },
1932
+ {
1933
+ id: "frontend/infinite-scroll",
1934
+ name: "Infinite Scroll",
1935
+ category: "frontend",
1936
+ description: "Implement infinite scroll with intersection observer",
1937
+ template: `Implement infinite scroll for {{listName}} in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
1938
+
1939
+ Data source: {{dataSource}}
1940
+ Item component: {{itemComponent}}
1941
+
1942
+ Implementation:
1943
+ - Use IntersectionObserver (not scroll events) to detect when the sentinel element enters the viewport
1944
+ - Trigger next page load when sentinel is within {{threshold}}px of the viewport
1945
+ - Show a loading skeleton for new items (not a full-page spinner)
1946
+ - Handle: initial load, loading next page, no more pages, network error with retry
1947
+ - Preserve scroll position on browser back/forward navigation
1948
+ - Cleanup: disconnect observer on unmount
1949
+
1950
+ Cursor-based pagination:
1951
+ - Use cursor (not page number) to avoid duplicate/missing items if data changes
1952
+ - API call: GET {{dataSource}}?cursor={{cursor}}&limit={{pageSize}}
1953
+ - Response shape: { items: T[], nextCursor: string | null }
1954
+
1955
+ Provide: useInfiniteScroll hook + ItemList component + sentinel element pattern.`,
1956
+ contextSchema: [
1957
+ { key: "listName", label: "List name", type: "text", placeholder: "PostFeed" },
1958
+ { key: "dataSource", label: "API endpoint", type: "text", placeholder: "/api/posts" },
1959
+ { key: "itemComponent", label: "Item component", type: "text", placeholder: "PostCard" },
1960
+ { key: "threshold", label: "Load trigger threshold (px from bottom)", type: "text", default: "200" },
1961
+ { key: "pageSize", label: "Items per page", type: "text", default: "20" }
1962
+ ],
1963
+ tags: ["infinite-scroll", "pagination", "performance", "frontend"],
1964
+ version: "1.0.0"
1965
+ },
1966
+ // ── Database ─────────────────────────────────────────────────────────────────
1967
+ {
1968
+ id: "database/schema-design",
1969
+ name: "Database Schema",
1970
+ category: "database",
1971
+ description: "Design a normalized relational database schema",
1972
+ template: `Design a database schema for {{domain}} in {{dbType}}.
1973
+
1974
+ Entities to model: {{entities}}
1975
+
1976
+ Requirements:
1977
+ - Normalize to 3NF unless there's a documented performance reason to denormalize
1978
+ - Primary keys: {{pkStrategy}}
1979
+ - All tables must have: created_at, updated_at timestamps
1980
+ - Foreign key constraints with ON DELETE behavior specified
1981
+ - Indexes: primary key (auto), foreign keys, and {{indexColumns}}
1982
+ - Soft delete pattern where data must be auditable
1983
+
1984
+ Provide:
1985
+ 1. Entity-relationship description
1986
+ 2. CREATE TABLE statements
1987
+ 3. Explanation of relationships and cardinality
1988
+ 4. Query for the most common access pattern: {{commonQuery}}`,
1989
+ contextSchema: [
1990
+ { key: "domain", label: "Domain / system name", type: "text", placeholder: "e-commerce order management" },
1991
+ { key: "entities", label: "Entities (comma-separated)", type: "text", placeholder: "User, Order, Product, OrderItem" },
1992
+ { key: "dbType", label: "Database", type: "select", options: ["PostgreSQL", "MySQL", "SQLite", "MongoDB"], default: "PostgreSQL" },
1993
+ { key: "pkStrategy", label: "Primary key strategy", type: "select", options: ["UUID", "auto-increment integer", "ULID"], default: "UUID" },
1994
+ { key: "indexColumns", label: "Columns to index", type: "text", placeholder: "email (unique), status, created_at" },
1995
+ { key: "commonQuery", label: "Most common query", type: "text", placeholder: "Get all orders for a user with product details" }
1996
+ ],
1997
+ tags: ["database", "schema", "sql"],
1998
+ version: "1.0.0"
1999
+ },
2000
+ {
2001
+ id: "database/migration-script",
2002
+ name: "Migration Script",
2003
+ category: "database",
2004
+ description: "Write a safe, reversible database migration",
2005
+ template: `Write a database migration for {{description}} in {{migrationTool}}.
2006
+
2007
+ Change: {{change}}
2008
+
2009
+ Safety requirements:
2010
+ - The migration must be fully reversible (down() must exactly undo up())
2011
+ - No data loss without explicit confirmation comment
2012
+ - For large tables (>1M rows), use batched operations to avoid lock timeouts
2013
+ - If adding a NOT NULL column to an existing table, provide a backfill default
2014
+ - If renaming a column, do it in 3 steps: add new \u2192 backfill \u2192 remove old (separate migrations)
2015
+ - Test the rollback: does down() leave the schema identical to before up()?
2016
+
2017
+ Also provide:
2018
+ - SQL preview of both up() and down()
2019
+ - Risk assessment: what breaks if this migration fails mid-way?`,
2020
+ contextSchema: [
2021
+ { key: "description", label: "Migration description", type: "text", placeholder: "Add email verification to users table" },
2022
+ { key: "change", label: "Specific change", type: "text", placeholder: "Add verified_at TIMESTAMP NULL and verification_token VARCHAR(64)" },
2023
+ { key: "migrationTool", label: "Migration tool", type: "select", options: ["Knex", "Flyway", "Liquibase", "Alembic", "Prisma", "TypeORM", "raw SQL"], default: "Knex" }
2024
+ ],
2025
+ tags: ["database", "migration", "sql"],
2026
+ version: "1.0.0"
2027
+ },
2028
+ {
2029
+ id: "database/orm-model",
2030
+ name: "ORM Model",
2031
+ category: "database",
2032
+ description: "Define ORM models with relations, validations, and hooks",
2033
+ template: `Define ORM models for {{entities}} in {{project.language}} using {{orm}}.
2034
+
2035
+ For each entity provide:
2036
+ 1. Model/schema definition with all fields, types, and constraints
2037
+ 2. Relations: one-to-one, one-to-many, many-to-many with junction tables
2038
+ 3. Indexes: primary key, unique constraints, compound indexes for common queries
2039
+ 4. Hooks/middleware: {{hooks}}
2040
+ 5. Virtual fields / computed properties
2041
+ 6. Serialization: toJSON() that excludes sensitive fields (passwords, tokens)
2042
+
2043
+ Entity descriptions:
2044
+ {{entityDescriptions}}
2045
+
2046
+ Also provide:
2047
+ - Repository pattern wrapper with typed CRUD methods
2048
+ - Example of an eager-loaded query with nested relations
2049
+ - Explain your choice of cascade settings (ON DELETE behavior for each FK)`,
2050
+ contextSchema: [
2051
+ { key: "entities", label: "Entities to model", type: "text", placeholder: "User, Post, Comment, Tag" },
2052
+ { key: "orm", label: "ORM / ODM", type: "select", options: ["Prisma", "TypeORM", "Drizzle", "Sequelize", "Mongoose", "SQLAlchemy"], default: "Prisma" },
2053
+ { key: "hooks", label: "Lifecycle hooks needed", type: "text", placeholder: "hash password before save, set updatedAt, log deletes" },
2054
+ { key: "entityDescriptions", label: "Entity descriptions", type: "text", placeholder: "User: has email, password hash, role. Post: belongs to User, has title, body, tags. Comment: belongs to Post and User." }
2055
+ ],
2056
+ tags: ["orm", "database", "prisma", "typeorm"],
2057
+ version: "1.0.0"
2058
+ },
2059
+ {
2060
+ id: "database/query-optimization",
2061
+ name: "Query Optimization",
2062
+ category: "database",
2063
+ description: "Diagnose and optimize a slow database query",
2064
+ template: `Diagnose and optimize the following slow {{dbType}} query.
2065
+
2066
+ Query:
2067
+ {{query}}
2068
+
2069
+ Context:
2070
+ - Table sizes: {{tableSizes}}
2071
+ - Current indexes: {{currentIndexes}}
2072
+ - Query execution time: {{currentTime}}
2073
+ - Called: {{callFrequency}}
2074
+
2075
+ Analysis steps:
2076
+ 1. **EXPLAIN / EXPLAIN ANALYZE** \u2014 interpret the query plan, identify sequential scans and high row estimates
2077
+ 2. **Index recommendations** \u2014 which columns to index, composite index column order (selectivity first), partial index if applicable
2078
+ 3. **Query rewrite** \u2014 can the same result be achieved with a more efficient query?
2079
+ 4. **N+1 detection** \u2014 is this query inside a loop? Replace with a JOIN or subquery
2080
+ 5. **Covering index** \u2014 can we avoid heap fetches by including all needed columns in the index?
2081
+ 6. **Pagination** \u2014 if this is a list query, use cursor pagination not OFFSET for large tables
2082
+
2083
+ Provide:
2084
+ - Optimized query
2085
+ - Index DDL statements
2086
+ - Expected improvement estimate
2087
+ - Trade-offs (write overhead for new indexes)`,
2088
+ contextSchema: [
2089
+ { key: "query", label: "Slow query (SQL or ORM)", type: "text", placeholder: "SELECT * FROM orders JOIN users ON ..." },
2090
+ { key: "dbType", label: "Database", type: "select", options: ["PostgreSQL", "MySQL", "SQLite"], default: "PostgreSQL" },
2091
+ { key: "tableSizes", label: "Table sizes", type: "text", placeholder: "orders: 2M rows, users: 50k rows" },
2092
+ { key: "currentIndexes", label: "Current indexes", type: "text", placeholder: "PRIMARY KEY on id, INDEX on user_id" },
2093
+ { key: "currentTime", label: "Current execution time", type: "text", placeholder: "3.2 seconds" },
2094
+ { key: "callFrequency", label: "How often is it called?", type: "text", placeholder: "500 times/minute" }
2095
+ ],
2096
+ tags: ["database", "performance", "sql", "optimization"],
2097
+ version: "1.0.0"
2098
+ },
2099
+ {
2100
+ id: "database/seed-data",
2101
+ name: "Seed Data",
2102
+ category: "database",
2103
+ description: "Write database seed scripts for development and testing",
2104
+ template: `Write database seed scripts for {{project.language}} using {{orm}}.
2105
+
2106
+ Entities to seed: {{entities}}
2107
+
2108
+ Requirements:
2109
+ - Idempotent: running the seed twice must not create duplicates (upsert or check-before-insert)
2110
+ - Ordered: respect foreign key constraints (seed parent records before children)
2111
+ - Realistic data: use a faker library for names, emails, dates \u2014 no "Test User 1"
2112
+ - Separate seeds: one file per entity + a master seed that runs them in order
2113
+ - Environment-aware: dev seeds create {{devCount}} records; test seeds create minimal fixture data
2114
+ - Relationships: ensure seeded records form valid, queryable relationships
2115
+
2116
+ For each entity provide:
2117
+ 1. Factory function: \`createFakeUser(overrides?)\` for use in tests
2118
+ 2. Bulk seed function that inserts N records efficiently (batch insert, not N queries)
2119
+ 3. Teardown function to reset to a clean state in tests
2120
+
2121
+ Include a seed for the default admin user with documented credentials.`,
2122
+ contextSchema: [
2123
+ { key: "entities", label: "Entities to seed", type: "text", placeholder: "User, Product, Category, Order" },
2124
+ { key: "orm", label: "ORM / query builder", type: "select", options: ["Prisma", "Knex", "TypeORM", "Sequelize", "raw SQL"], default: "Prisma" },
2125
+ { key: "devCount", label: "Records per entity in dev", type: "text", default: "50" }
2126
+ ],
2127
+ tags: ["database", "seed", "testing", "fixtures"],
2128
+ version: "1.0.0"
2129
+ },
2130
+ // ── Testing ───────────────────────────────────────────────────────────────────
2131
+ {
2132
+ id: "testing/unit-test",
2133
+ name: "Unit Test Suite",
2134
+ category: "testing",
2135
+ description: "Write a complete unit test suite for a function or class",
2136
+ template: `Write a complete unit test suite for {{target}} in {{project.language}} using {{testFramework}}.
2137
+
2138
+ Code to test:
2139
+ {{codeDescription}}
2140
+
2141
+ Test cases to cover:
2142
+ 1. Happy path: {{happyPath}}
2143
+ 2. Edge cases: {{edgeCases}}
2144
+ 3. Error cases: {{errorCases}}
2145
+ 4. Boundary values
2146
+
2147
+ Requirements:
2148
+ - Mock all external dependencies (database, HTTP, file system)
2149
+ - Each test should have one assertion focus (Arrange-Act-Assert)
2150
+ - Descriptive test names: "should [action] when [condition]"
2151
+ - Test data factories for complex inputs (don't inline large objects)
2152
+ - Coverage target: all branches + all exported functions
2153
+ {{#if (eq project.type "student")}}- Comments explaining what each describe block is testing and why{{/if}}`,
2154
+ contextSchema: [
2155
+ { key: "target", label: "Function/class to test", type: "text", placeholder: "UserService.createUser()" },
2156
+ { key: "codeDescription", label: "Describe what it does", type: "text", placeholder: "Validates input, hashes password, saves to DB, sends welcome email" },
2157
+ { key: "testFramework", label: "Test framework", type: "select", options: ["Jest", "Vitest", "Mocha", "pytest", "JUnit"], default: "Jest" },
2158
+ { key: "happyPath", label: "Happy path", type: "text", placeholder: "valid input creates and returns user" },
2159
+ { key: "edgeCases", label: "Edge cases", type: "text", placeholder: "duplicate email, empty name, very long input" },
2160
+ { key: "errorCases", label: "Error cases", type: "text", placeholder: "DB failure, email service down" }
2161
+ ],
2162
+ tags: ["testing", "unit-test", "tdd"],
2163
+ version: "1.0.0"
2164
+ },
2165
+ {
2166
+ id: "testing/tdd-plan",
2167
+ name: "TDD Plan",
2168
+ category: "testing",
2169
+ description: "Design a test-first implementation plan (London School TDD)",
2170
+ template: `Design a TDD implementation plan for {{feature}} in {{project.language}}.
2171
+
2172
+ Feature: {{description}}
2173
+
2174
+ Follow London School TDD (outside-in, mock collaborators):
2175
+
2176
+ 1. Start with an acceptance test that describes the feature from the outside
2177
+ 2. Identify the collaborators (dependencies) and mock them
2178
+ 3. Write the first failing unit test
2179
+ 4. Implement the minimum code to make it pass
2180
+ 5. Refactor
2181
+ 6. Repeat inward until the acceptance test passes
2182
+
2183
+ Produce:
2184
+ - Acceptance test skeleton
2185
+ - Unit test skeletons for each layer (controller \u2192 service \u2192 repository)
2186
+ - Mock definitions for each boundary
2187
+ - Implementation order (outermost layer first)
2188
+ - Definition of Done: what must be true for this feature to be "complete"?`,
2189
+ contextSchema: [
2190
+ { key: "feature", label: "Feature name", type: "text", placeholder: "User login with JWT" },
2191
+ { key: "description", label: "Feature description", type: "text", placeholder: "User provides email + password, receives JWT access token and refresh token" }
2192
+ ],
2193
+ tags: ["testing", "tdd", "london-school"],
2194
+ version: "1.0.0"
2195
+ },
2196
+ {
2197
+ id: "testing/integration-test",
2198
+ name: "Integration Test",
2199
+ category: "testing",
2200
+ description: "Write integration tests against a real database or service",
2201
+ template: `Write integration tests for {{target}} in {{project.language}} using {{testFramework}}.
2202
+
2203
+ Test scope: {{scope}}
2204
+
2205
+ Setup requirements:
2206
+ - Spin up a test database: {{dbSetup}}
2207
+ - Run migrations before the test suite; roll back (or truncate) after each test
2208
+ - No mocking of the database \u2014 test the full stack from service to DB
2209
+ - Use a dedicated test schema/database to never affect development data
2210
+
2211
+ Test cases:
2212
+ {{testCases}}
2213
+
2214
+ Test structure:
2215
+ - beforeAll: start test DB, run migrations
2216
+ - beforeEach: seed minimal required fixtures
2217
+ - afterEach: truncate tables (faster than re-migrating)
2218
+ - afterAll: close DB connection, stop test server
2219
+
2220
+ Isolation: each test must be independent \u2014 never rely on state from a previous test
2221
+
2222
+ Provide: test helpers for creating fixtures, a test DB configuration, and the test file.`,
2223
+ contextSchema: [
2224
+ { key: "target", label: "Feature to test", type: "text", placeholder: "User registration and login flow" },
2225
+ { key: "scope", label: "Test scope", type: "text", placeholder: "POST /api/users and POST /api/auth/login" },
2226
+ { key: "testFramework", label: "Test framework", type: "select", options: ["Jest", "Vitest", "pytest", "JUnit", "Go testing"], default: "Jest" },
2227
+ { key: "dbSetup", label: "DB setup method", type: "select", options: ["Docker container", "SQLite in-memory", "test schema on dev DB", "Testcontainers"], default: "Docker container" },
2228
+ { key: "testCases", label: "Test cases to cover", type: "text", placeholder: "successful registration, duplicate email, invalid password, login with wrong password" }
2229
+ ],
2230
+ tags: ["integration-test", "testing", "database"],
2231
+ version: "1.0.0"
2232
+ },
2233
+ {
2234
+ id: "testing/e2e-test",
2235
+ name: "E2E Test Plan",
2236
+ category: "testing",
2237
+ description: "Create end-to-end tests for a user-facing flow",
2238
+ template: `Write end-to-end tests for the {{flow}} flow using {{e2eFramework}}.
2239
+
2240
+ User flow to test:
2241
+ {{flowDescription}}
2242
+
2243
+ Test scenarios:
2244
+ 1. **Happy path**: full successful flow with real user inputs
2245
+ 2. **Validation errors**: missing fields, invalid formats \u2014 UI shows error messages
2246
+ 3. **Server errors**: simulate network failure or 500 \u2014 UI shows error state
2247
+ 4. **Edge cases**: {{edgeCases}}
2248
+
2249
+ Requirements:
2250
+ - Use Page Object Model (POM) \u2014 separate UI interaction logic from test assertions
2251
+ - Never use \`sleep\` \u2014 use proper await conditions (element visible, network request complete)
2252
+ - Test IDs: use \`data-testid\` attributes on interactive elements, not CSS selectors or text
2253
+ - Viewport: test on {{viewports}}
2254
+ - Record a video / screenshot on failure for CI debugging
2255
+
2256
+ For each scenario provide:
2257
+ - Page Object methods
2258
+ - Test steps in plain English then code
2259
+ - What to assert (not just "page loaded" but specific data visible)`,
2260
+ contextSchema: [
2261
+ { key: "flow", label: "User flow name", type: "text", placeholder: "Checkout" },
2262
+ { key: "flowDescription", label: "Flow description", type: "text", placeholder: "User adds item to cart \u2192 enters shipping \u2192 pays \u2192 sees confirmation" },
2263
+ { key: "e2eFramework", label: "E2E framework", type: "select", options: ["Playwright", "Cypress", "Selenium", "Puppeteer"], default: "Playwright" },
2264
+ { key: "edgeCases", label: "Edge cases", type: "text", placeholder: "empty cart, expired payment card, address validation failure" },
2265
+ { key: "viewports", label: "Viewports to test", type: "text", default: "desktop (1280x720) and mobile (390x844)" }
2266
+ ],
2267
+ tags: ["e2e", "testing", "playwright", "cypress"],
2268
+ version: "1.0.0"
2269
+ },
2270
+ {
2271
+ id: "testing/mock-strategy",
2272
+ name: "Mock Strategy",
2273
+ category: "testing",
2274
+ description: "Design a consistent mocking strategy for a module or service",
2275
+ template: `Design a mocking strategy for {{target}} in {{project.language}} using {{testFramework}}.
2276
+
2277
+ Dependencies to mock: {{dependencies}}
2278
+
2279
+ Strategy:
2280
+ 1. **What to mock**: only cross-boundary dependencies (external APIs, databases, file system, time)
2281
+ 2. **What NOT to mock**: internal modules, pure functions \u2014 test them for real
2282
+ 3. **Mock fidelity**: mocks must have the same interface as the real thing \u2014 no drifting
2283
+
2284
+ For each dependency provide:
2285
+ - Mock factory: a reusable function that returns a pre-configured mock
2286
+ - Default behavior: what the mock returns when not configured otherwise
2287
+ - Per-test override: how to configure specific behavior for edge case tests
2288
+ - Spy assertions: how to assert that the mock was called with the right arguments
2289
+
2290
+ Patterns:
2291
+ - For HTTP: {{httpMockStrategy}}
2292
+ - For database: use an in-memory SQLite or a repository mock
2293
+ - For time: mock Date.now() / clock for deterministic tests
2294
+ - For env vars: use a test environment config, never modify process.env directly
2295
+
2296
+ Provide the mock factory implementations and an example test using each mock.`,
2297
+ contextSchema: [
2298
+ { key: "target", label: "Module to write tests for", type: "text", placeholder: "PaymentService" },
2299
+ { key: "dependencies", label: "Dependencies to mock", type: "text", placeholder: "Stripe SDK, UserRepository, EmailService, logger" },
2300
+ { key: "testFramework", label: "Test framework", type: "select", options: ["Jest", "Vitest", "Sinon", "pytest-mock"], default: "Jest" },
2301
+ { key: "httpMockStrategy", label: "HTTP mock strategy", type: "select", options: ["MSW (Mock Service Worker)", "nock", "jest.fn()", "fetch-mock"], default: "MSW (Mock Service Worker)" }
2302
+ ],
2303
+ tags: ["mocking", "testing", "jest", "strategy"],
2304
+ version: "1.0.0"
2305
+ },
2306
+ {
2307
+ id: "testing/api-test",
2308
+ name: "API Test Suite",
2309
+ category: "testing",
2310
+ description: "Write tests for a REST API endpoint",
2311
+ template: `Write a test suite for the {{endpoint}} API endpoint in {{project.language}} using {{testFramework}}.
2312
+
2313
+ Endpoint: {{method}} {{route}}
2314
+ Expected behavior: {{behavior}}
2315
+
2316
+ Test cases to cover:
2317
+ - **200/201**: valid request returns expected response shape
2318
+ - **400**: missing required fields, invalid types, constraint violations
2319
+ - **401**: missing/invalid/expired auth token
2320
+ - **403**: valid auth but insufficient permissions
2321
+ - **404**: resource does not exist
2322
+ - **409**: conflict (e.g., duplicate creation)
2323
+ - **500**: upstream failure (mock the DB/service to throw)
2324
+
2325
+ For each test:
2326
+ 1. Setup: create required DB fixtures
2327
+ 2. Request: set headers, body, path params
2328
+ 3. Assert: status code + response body shape + side effects (DB state, events emitted)
2329
+ 4. Teardown: no side effects left for next test
2330
+
2331
+ Provide: test server setup, auth helper (create + login test user), and request helpers.`,
2332
+ contextSchema: [
2333
+ { key: "endpoint", label: "Endpoint to test", type: "text", placeholder: "Create Order" },
2334
+ { key: "method", label: "HTTP method", type: "select", options: ["GET", "POST", "PUT", "PATCH", "DELETE"], default: "POST" },
2335
+ { key: "route", label: "Route", type: "text", placeholder: "/api/orders" },
2336
+ { key: "behavior", label: "What it should do", type: "text", placeholder: "Creates an order, deducts inventory, sends confirmation email" },
2337
+ { key: "testFramework", label: "Test framework", type: "select", options: ["Jest + Supertest", "Vitest + Supertest", "pytest + requests", "Rest Assured"], default: "Jest + Supertest" }
2338
+ ],
2339
+ tags: ["api-testing", "testing", "rest", "supertest"],
2340
+ version: "1.0.0"
2341
+ },
2342
+ // ── DevOps ────────────────────────────────────────────────────────────────────
2343
+ {
2344
+ id: "devops/dockerfile",
2345
+ name: "Dockerfile",
2346
+ category: "devops",
2347
+ description: "Write an optimized multi-stage Dockerfile",
2348
+ template: `Write an optimized multi-stage Dockerfile for a {{project.language}} application{{#if project.framework}} using {{project.framework}}{{/if}}.
2349
+
2350
+ App type: {{appType}}
2351
+ Port: {{port}}
2352
+
2353
+ Requirements:
2354
+ - Multi-stage: builder stage + minimal runtime stage
2355
+ - Use the smallest viable base image (alpine or distroless)
2356
+ - Run as a non-root user
2357
+ - Layer cache optimization: copy package files + install before copying source
2358
+ - .dockerignore to exclude node_modules, .env, .git, test files
2359
+ - Health check endpoint: {{healthCheck}}
2360
+ - Build args for environment (dev/staging/prod)
2361
+ - Environment variables documented with ENV defaults
2362
+
2363
+ Also provide the .dockerignore and a docker-compose.yml for local development with a database service.`,
2364
+ contextSchema: [
2365
+ { key: "appType", label: "App type", type: "select", options: ["web server", "worker/background job", "CLI tool"], default: "web server" },
2366
+ { key: "port", label: "Exposed port", type: "text", default: "3000" },
2367
+ { key: "healthCheck", label: "Health check path", type: "text", placeholder: "/api/health", default: "/health" }
2368
+ ],
2369
+ tags: ["docker", "devops", "deployment"],
2370
+ version: "1.0.0"
2371
+ },
2372
+ {
2373
+ id: "devops/github-action",
2374
+ name: "GitHub Actions CI",
2375
+ category: "devops",
2376
+ description: "Create a CI/CD pipeline with GitHub Actions",
2377
+ template: `Create a GitHub Actions CI/CD workflow for a {{project.language}} project.
2378
+
2379
+ Pipeline stages: {{stages}}
2380
+
2381
+ Requirements:
2382
+ - Trigger: push to main + all pull requests
2383
+ - Cache: dependency cache (npm/pip/maven) keyed to lock file hash
2384
+ - Matrix: test on Node {{nodeVersions}} (if applicable)
2385
+ - Fail fast: if linting fails, don't run tests
2386
+ - PR checks: lint, type-check, test, coverage threshold ({{coverageThreshold}}%)
2387
+ - Deploy job: runs only on push to main, requires all checks to pass
2388
+ - Secrets: never echo secrets; use GitHub Secrets for API keys
2389
+ - Notifications: comment on PR with test results summary
2390
+
2391
+ Deploy target: {{deployTarget}}`,
2392
+ contextSchema: [
2393
+ { key: "stages", label: "Pipeline stages", type: "text", default: "lint, type-check, test, build, deploy" },
2394
+ { key: "nodeVersions", label: "Node versions to test", type: "text", default: "20, 22" },
2395
+ { key: "coverageThreshold", label: "Coverage threshold (%)", type: "text", default: "80" },
2396
+ { key: "deployTarget", label: "Deploy target", type: "select", options: ["Vercel", "Railway", "AWS ECS", "Fly.io", "none"], default: "none" }
2397
+ ],
2398
+ tags: ["ci-cd", "github-actions", "devops"],
2399
+ version: "1.0.0"
2400
+ },
2401
+ {
2402
+ id: "devops/env-config",
2403
+ name: "Environment Config",
2404
+ category: "devops",
2405
+ description: "Design environment variable management across dev/staging/prod",
2406
+ template: `Design environment configuration management for a {{project.language}} project.
2407
+
2408
+ Environments: {{environments}}
2409
+ Config values needed: {{configValues}}
2410
+
2411
+ Requirements:
2412
+ 1. **Schema validation on startup**: use a schema (Zod/Joi/env-schema) to validate all required env vars at boot \u2014 fail fast with a clear message listing missing vars
2413
+ 2. **Type safety**: typed config object derived from validated env vars \u2014 no raw process.env in business logic
2414
+ 3. **Secrets vs config**: distinguish between secrets (API keys, DB passwords) and non-secret config (port, log level)
2415
+ 4. **Local development**: .env.example with all required vars documented but no real values; .env is git-ignored
2416
+ 5. **Environment hierarchy**: .env < .env.local < .env.{NODE_ENV} (explain override order)
2417
+ 6. **CI/CD**: document which vars go in GitHub Secrets vs non-secret CI variables
2418
+
2419
+ File structure to create:
2420
+ - \`config/env.ts\` \u2014 schema + validation + typed export
2421
+ - \`.env.example\` \u2014 all vars with placeholder values and comments
2422
+ - CI environment setup instructions
2423
+
2424
+ Never log environment variables, even in debug mode.`,
2425
+ contextSchema: [
2426
+ { key: "environments", label: "Environments", type: "text", default: "development, test, staging, production" },
2427
+ { key: "configValues", label: "Config values needed", type: "text", placeholder: "DATABASE_URL, JWT_SECRET, SMTP_HOST, REDIS_URL, PORT, LOG_LEVEL" }
2428
+ ],
2429
+ tags: ["config", "environment", "devops", "security"],
2430
+ version: "1.0.0"
2431
+ },
2432
+ {
2433
+ id: "devops/monitoring",
2434
+ name: "Monitoring Setup",
2435
+ category: "devops",
2436
+ description: "Add application monitoring, logging, and alerting",
2437
+ template: `Design a monitoring and observability setup for a {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}} application.
2438
+
2439
+ Stack: {{monitoringStack}}
2440
+
2441
+ The three pillars of observability:
2442
+
2443
+ **1. Logging**
2444
+ - Structured JSON logs with: timestamp, level, requestId, userId, duration, error
2445
+ - Log levels: ERROR (alerts), WARN (investigate), INFO (audit trail), DEBUG (dev only)
2446
+ - Never log: passwords, tokens, PII, full request/response bodies
2447
+ - Correlation ID: propagate through all async operations and service calls
2448
+
2449
+ **2. Metrics**
2450
+ - Key metrics to track: {{metrics}}
2451
+ - USE method for each service: Utilization, Saturation, Errors
2452
+ - Expose a /metrics endpoint in Prometheus format
2453
+
2454
+ **3. Traces**
2455
+ - Distributed tracing across: {{services}}
2456
+ - Trace: HTTP request \u2192 service \u2192 DB query \u2192 external API call
2457
+ - Sampling: 100% in dev, {{traceSampling}}% in production
2458
+
2459
+ Alerting rules:
2460
+ - P99 latency > 2s \u2192 page oncall
2461
+ - Error rate > 1% \u2192 alert
2462
+ - Disk > 85% \u2192 warning
2463
+
2464
+ Provide: logger setup, middleware instrumentation, and health check endpoint.`,
2465
+ contextSchema: [
2466
+ { key: "monitoringStack", label: "Monitoring stack", type: "select", options: ["Datadog", "Grafana + Prometheus", "New Relic", "AWS CloudWatch", "OpenTelemetry (vendor-neutral)"], default: "OpenTelemetry (vendor-neutral)" },
2467
+ { key: "metrics", label: "Key metrics", type: "text", default: "request rate, error rate, p50/p95/p99 latency, DB pool usage" },
2468
+ { key: "services", label: "Services to trace", type: "text", placeholder: "API server, PostgreSQL, Redis, Stripe API" },
2469
+ { key: "traceSampling", label: "Trace sampling % in production", type: "text", default: "10" }
2470
+ ],
2471
+ tags: ["monitoring", "logging", "observability", "devops"],
2472
+ version: "1.0.0"
2473
+ },
2474
+ {
2475
+ id: "devops/deploy-checklist",
2476
+ name: "Deploy Checklist",
2477
+ category: "devops",
2478
+ description: "Generate a pre-deployment and rollback checklist",
2479
+ template: `Generate a deployment checklist for {{project.name}} deploying to {{environment}}.
2480
+
2481
+ Change being deployed: {{changeDescription}}
2482
+ Deploy method: {{deployMethod}}
2483
+
2484
+ **Pre-deploy checklist**
2485
+ - [ ] All tests pass (unit, integration, E2E)
2486
+ - [ ] Migration plan reviewed (is it backward-compatible with current production code?)
2487
+ - [ ] Feature flags in place for risky changes
2488
+ - [ ] Runbook updated for new operational behavior
2489
+ - [ ] On-call engineer notified of deploy window
2490
+ - [ ] Rollback procedure documented and tested
2491
+ - [ ] Dependencies updated and audited for CVEs
2492
+ - [ ] Performance tested under production-like load if traffic-path change
2493
+
2494
+ **During deploy**
2495
+ - [ ] Deploy to staging first; smoke test critical paths
2496
+ - [ ] Monitor error rate and p99 latency during rollout
2497
+ - [ ] Watch for increased DB CPU / slow queries from migrations
2498
+
2499
+ **Post-deploy verification**
2500
+ - [ ] Health check endpoints return 200
2501
+ - [ ] Run smoke test against production: {{smokeTests}}
2502
+ - [ ] Verify new feature works as expected in production
2503
+
2504
+ **Rollback procedure for {{deployMethod}}**:
2505
+ {{rollbackSteps}}
2506
+
2507
+ **Estimated deploy risk**: {{riskLevel}} \u2014 based on scope of change`,
2508
+ contextSchema: [
2509
+ { key: "environment", label: "Target environment", type: "select", options: ["staging", "production", "canary"], default: "production" },
2510
+ { key: "changeDescription", label: "What is being deployed", type: "text", placeholder: "New checkout flow with Stripe integration" },
2511
+ { key: "deployMethod", label: "Deploy method", type: "select", options: ["blue/green", "rolling update", "canary", "in-place"], default: "rolling update" },
2512
+ { key: "smokeTests", label: "Post-deploy smoke tests", type: "text", placeholder: "login, create order, view dashboard" },
2513
+ { key: "rollbackSteps", label: "Rollback steps", type: "text", placeholder: "git revert + redeploy, then run down migrations" },
2514
+ { key: "riskLevel", label: "Risk level", type: "select", options: ["low", "medium", "high", "critical"], default: "medium" }
2515
+ ],
2516
+ tags: ["deployment", "devops", "checklist", "release"],
2517
+ version: "1.0.0"
2518
+ },
2519
+ // ── Academic ──────────────────────────────────────────────────────────────────
2520
+ {
2521
+ id: "academic/complexity-analysis",
2522
+ name: "Complexity Analysis",
2523
+ category: "academic",
2524
+ description: "Analyze time and space complexity of an algorithm",
2525
+ template: `Analyze the time and space complexity of the following algorithm in {{project.language}}.
2526
+
2527
+ Algorithm: {{algorithm}}
2528
+ Code:
2529
+ {{code}}
2530
+
2531
+ Provide:
2532
+ 1. **Time complexity** \u2014 worst case, best case, average case with Big-O notation
2533
+ 2. **Space complexity** \u2014 auxiliary space used (excluding input)
2534
+ 3. **Step-by-step derivation** \u2014 show how you counted operations, don't just state the answer
2535
+ 4. **Dominant terms** \u2014 which loops/recursion drives the complexity?
2536
+ 5. **Comparison** \u2014 is there a known better algorithm for this problem? What is its complexity?
2537
+ 6. **When it matters** \u2014 at what input size (n) does this become a problem in practice?
2538
+
2539
+ Use concrete examples: for n=10, n=1000, n=1,000,000 \u2014 approximately how many operations?`,
2540
+ contextSchema: [
2541
+ { key: "algorithm", label: "Algorithm name", type: "text", placeholder: "Bubble sort" },
2542
+ { key: "code", label: "Paste your code here", type: "text", placeholder: "def bubble_sort(arr): ..." }
2543
+ ],
2544
+ tags: ["complexity", "big-o", "academic", "algorithms"],
2545
+ version: "1.0.0"
2546
+ },
2547
+ {
2548
+ id: "academic/algorithm-explain",
2549
+ name: "Algorithm Explanation",
2550
+ category: "academic",
2551
+ description: "Get a step-by-step explanation of an algorithm",
2552
+ template: `Explain the {{algorithm}} algorithm to a {{project.studentLevel}} computer science student.
2553
+
2554
+ Structure the explanation as:
2555
+ 1. **Problem it solves** \u2014 one sentence, concrete
2556
+ 2. **Intuition** \u2014 explain the core idea without code (analogy or visual)
2557
+ 3. **Step-by-step walkthrough** \u2014 trace through this example: {{example}}
2558
+ 4. **Pseudocode** \u2014 language-agnostic, focusing on logic not syntax
2559
+ 5. **Implementation in {{project.language}}** \u2014 clean, commented code
2560
+ 6. **Complexity** \u2014 time and space, with brief justification
2561
+ 7. **When to use it** \u2014 and when NOT to use it (what's the alternative?)
2562
+ 8. **Defense question** \u2014 "What would a professor ask you about this?"
2563
+
2564
+ Use concrete numbers, not vague descriptions. Show the state of data structures at each step.`,
2565
+ contextSchema: [
2566
+ { key: "algorithm", label: "Algorithm name", type: "text", placeholder: "Binary search" },
2567
+ { key: "example", label: "Concrete example input", type: "text", placeholder: "array [1, 3, 5, 7, 9], searching for 7" }
2568
+ ],
2569
+ tags: ["algorithms", "explanation", "academic"],
2570
+ version: "1.0.0"
2571
+ },
2572
+ {
2573
+ id: "academic/design-pattern",
2574
+ name: "Design Pattern",
2575
+ category: "academic",
2576
+ description: "Explain and implement a design pattern",
2577
+ template: `Explain the {{pattern}} design pattern to a {{project.studentLevel}} developer and implement it in {{project.language}}.
2578
+
2579
+ Include:
2580
+ 1. **Category** \u2014 Creational, Structural, or Behavioral
2581
+ 2. **Problem it solves** \u2014 what goes wrong without it?
2582
+ 3. **Structure** \u2014 participants, their roles, and relationships
2583
+ 4. **Implementation** \u2014 complete {{project.language}} code for {{useCase}}
2584
+ 5. **Before vs. After** \u2014 show the messy code it replaces
2585
+ 6. **Trade-offs** \u2014 when to use it vs. when it's overkill
2586
+ 7. **Real-world examples** \u2014 name 2 places this is used in popular frameworks/libraries
2587
+ 8. **Common mistakes** \u2014 what do beginners misunderstand?
2588
+ {{#if (eq project.type "student")}}9. **Defense question** \u2014 "What would your professor ask?"{{/if}}`,
2589
+ contextSchema: [
2590
+ { key: "pattern", label: "Pattern name", type: "text", placeholder: "Observer" },
2591
+ { key: "useCase", label: "Your specific use case", type: "text", placeholder: "event system for a game where multiple UI components react to game state changes" }
2592
+ ],
2593
+ tags: ["design-patterns", "oop", "academic"],
2594
+ version: "1.0.0"
2595
+ },
2596
+ {
2597
+ id: "academic/pseudocode-to-code",
2598
+ name: "Pseudocode to Code",
2599
+ category: "academic",
2600
+ description: "Translate pseudocode or a description into working code",
2601
+ template: `Translate the following pseudocode/description into working {{project.language}} code.
2602
+
2603
+ Pseudocode:
2604
+ {{pseudocode}}
2605
+
2606
+ Requirements:
2607
+ - Implement exactly what the pseudocode describes \u2014 no extra features
2608
+ - Variable and function names should be descriptive, not p1, p2, x, y
2609
+ - Handle edge cases mentioned in the pseudocode
2610
+ - Add type annotations (for typed languages)
2611
+
2612
+ After the implementation, provide:
2613
+ 1. **What it does** \u2014 one sentence
2614
+ 2. **Example run** \u2014 trace through with: {{example}}
2615
+ 3. **Edge cases handled** \u2014 what happens with empty input, negative numbers, etc.
2616
+ 4. **What a student should be able to explain** \u2014 3 specific questions a professor might ask`,
2617
+ contextSchema: [
2618
+ { key: "pseudocode", label: "Your pseudocode or description", type: "text", placeholder: "function fibonacci(n):\n if n <= 1 return n\n return fibonacci(n-1) + fibonacci(n-2)" },
2619
+ { key: "example", label: "Example input to trace", type: "text", placeholder: "fibonacci(5)" }
2620
+ ],
2621
+ tags: ["pseudocode", "implementation", "academic"],
2622
+ version: "1.0.0"
2623
+ },
2624
+ {
2625
+ id: "academic/data-structure",
2626
+ name: "Data Structure Guide",
2627
+ category: "academic",
2628
+ description: "Deep explanation and implementation of a data structure",
2629
+ template: `Explain the {{dataStructure}} data structure to a {{project.studentLevel}} student and implement it in {{project.language}}.
2630
+
2631
+ Structure the guide:
2632
+ 1. **What problem does it solve?** \u2014 why does this structure exist? What's awkward without it?
2633
+ 2. **Mental model** \u2014 the simplest analogy (physical world or everyday experience)
2634
+ 3. **Anatomy** \u2014 the key components: nodes, pointers, invariants that must always hold
2635
+ 4. **Core operations** with complexity:
2636
+ - Insert: how + O(?)
2637
+ - Delete: how + O(?)
2638
+ - Search/Access: how + O(?)
2639
+ - Any structure-specific operations (e.g., push/pop, enqueue/dequeue, heapify)
2640
+ 5. **Full implementation in {{project.language}}** \u2014 including all core operations
2641
+ 6. **Step-by-step trace** \u2014 walk through this example: {{example}}
2642
+ 7. **When to use it** \u2014 and the alternatives (what data structure competes with it and when?)
2643
+ 8. **Common bugs** \u2014 what mistakes do beginners make when implementing it?
2644
+ {{#if (eq project.type "student")}}9. **Defense questions** \u2014 3 questions a professor would ask{{/if}}`,
2645
+ contextSchema: [
2646
+ { key: "dataStructure", label: "Data structure", type: "text", placeholder: "Binary Search Tree" },
2647
+ { key: "example", label: "Example to trace through", type: "text", placeholder: "Insert 5, 3, 7, 1, 4 then delete 3" }
2648
+ ],
2649
+ tags: ["data-structures", "academic", "algorithms"],
2650
+ version: "1.0.0"
2651
+ },
2652
+ {
2653
+ id: "academic/system-design",
2654
+ name: "System Design",
2655
+ category: "academic",
2656
+ description: "Walk through a simplified system design problem",
2657
+ template: `Walk through a system design for {{system}} appropriate for a {{project.studentLevel}} student.
2658
+
2659
+ Scale requirements: {{scale}}
2660
+ Key features: {{features}}
2661
+
2662
+ Structure (adapted for student level):
2663
+
2664
+ **1. Requirements clarification**
2665
+ - Functional requirements: what the system must do
2666
+ - Non-functional requirements: scale, availability, consistency needs
2667
+
2668
+ **2. Capacity estimation**
2669
+ - Traffic: {{scale}} \u2014 requests/second estimate
2670
+ - Storage: data size estimate for 1 year
2671
+ - Bandwidth: read vs write ratio
2672
+
2673
+ **3. High-level design**
2674
+ - Components and their responsibilities
2675
+ - Data flow diagram (describe in text/ASCII)
2676
+ - Database choice and why (SQL vs NoSQL for this use case)
2677
+
2678
+ **4. Deep dive on the hardest part**
2679
+ - The component with the most design decisions: {{hardestPart}}
2680
+ - Trade-offs made and why
2681
+
2682
+ **5. Bottlenecks and scaling**
2683
+ - Where will this break first as scale increases?
2684
+ - One solution for each bottleneck
2685
+
2686
+ **6. What you'd do differently with more time**
2687
+
2688
+ Keep explanations concrete \u2014 use numbers, not vague terms like "a lot of traffic".`,
2689
+ contextSchema: [
2690
+ { key: "system", label: "System to design", type: "text", placeholder: "URL shortener like bit.ly" },
2691
+ { key: "scale", label: "Scale", type: "text", placeholder: "1M URLs created/day, 100M redirects/day" },
2692
+ { key: "features", label: "Key features", type: "text", placeholder: "shorten URL, redirect, track click analytics, expiry" },
2693
+ { key: "hardestPart", label: "Hardest design part", type: "text", placeholder: "generating unique short codes at high write volume" }
2694
+ ],
2695
+ tags: ["system-design", "architecture", "academic", "interview"],
2696
+ version: "1.0.0"
2697
+ },
2698
+ {
2699
+ id: "academic/recursion",
2700
+ name: "Recursion Explained",
2701
+ category: "academic",
2702
+ description: "Visual explanation of recursive thinking and implementation",
2703
+ template: `Explain recursive thinking and solve {{problem}} recursively in {{project.language}}.
2704
+
2705
+ Level: {{project.studentLevel}}
2706
+
2707
+ Structure:
2708
+ 1. **The core idea** \u2014 base case + recursive case in plain language (before any code)
2709
+ 2. **The call stack** \u2014 draw (in ASCII) the call stack for this input: {{example}}
2710
+ - Show each frame: function name, parameters, return value
2711
+ - Show when frames are popped (where the "magic" happens)
2712
+ 3. **Implementation** \u2014 clean recursive solution with comments on WHY each line exists
2713
+ 4. **Trace table** \u2014 step-by-step execution for {{example}}
2714
+ 5. **Common mistakes**:
2715
+ - Forgetting the base case (infinite recursion)
2716
+ - Wrong return value
2717
+ - Mutating shared state across calls
2718
+ 6. **Memoization** \u2014 if this has overlapping subproblems, show the memoized version
2719
+ 7. **Iterative equivalent** \u2014 rewrite using a stack/loop; explain the trade-off
2720
+ 8. **Complexity** \u2014 time and space, including call stack space
2721
+
2722
+ The "aha moment": recursive functions trust that the recursive call works \u2014 just define what the function does for one case.`,
2723
+ contextSchema: [
2724
+ { key: "problem", label: "Problem to solve recursively", type: "text", placeholder: "merge sort" },
2725
+ { key: "example", label: "Example input to trace", type: "text", placeholder: "[5, 2, 8, 1, 9]" }
2726
+ ],
2727
+ tags: ["recursion", "algorithms", "academic"],
2728
+ version: "1.0.0"
2729
+ },
2730
+ {
2731
+ id: "academic/sorting",
2732
+ name: "Sorting Algorithms",
2733
+ category: "academic",
2734
+ description: "Compare sorting algorithms and implement the right one for the use case",
2735
+ template: `Compare sorting algorithms and implement the best one for {{useCase}} in {{project.language}}.
2736
+
2737
+ Constraint: {{constraint}}
2738
+
2739
+ Comparison table for the algorithms most relevant to this use case:
2740
+
2741
+ | Algorithm | Best | Average | Worst | Space | Stable? | When to use |
2742
+ |-----------|------|---------|-------|-------|---------|-------------|
2743
+ (fill in for: bubble, insertion, selection, merge, quick, heap, counting, radix \u2014 only include the ones relevant to {{useCase}})
2744
+
2745
+ **For {{useCase}}, the best choice is {{algorithm}} because:**
2746
+ - [Justify based on constraints and data properties]
2747
+
2748
+ **Full implementation in {{project.language}}:**
2749
+ - The algorithm with comments
2750
+ - Step-by-step trace on: {{example}}
2751
+
2752
+ **When the built-in sort beats everything:**
2753
+ - Language built-in sorts (Array.sort, sorted(), Collections.sort) are highly optimized
2754
+ - Only roll your own when you need: stable sort with custom comparator, partial sort, or external sort
2755
+
2756
+ **Debugging tip:** write a test that verifies: sorted output matches Array.from(input).sort()`,
2757
+ contextSchema: [
2758
+ { key: "useCase", label: "Use case / constraint", type: "text", placeholder: "sorting 10k user records by last name" },
2759
+ { key: "constraint", label: "Key constraint", type: "text", placeholder: "must be stable (preserve original order of ties), memory limited" },
2760
+ { key: "example", label: "Example input", type: "text", placeholder: "[64, 25, 12, 22, 11]" }
2761
+ ],
2762
+ tags: ["sorting", "algorithms", "academic", "complexity"],
2763
+ version: "1.0.0"
2764
+ },
2765
+ // ── Review ────────────────────────────────────────────────────────────────────
2766
+ {
2767
+ id: "review/code-review",
2768
+ name: "Code Review",
2769
+ category: "review",
2770
+ description: "Full code review for correctness, style, and best practices",
2771
+ template: `Perform a thorough code review of the following {{project.language}} code.
2772
+
2773
+ Context: {{context}}
2774
+
2775
+ Code:
2776
+ {{code}}
2777
+
2778
+ Review dimensions:
2779
+ 1. **Correctness** \u2014 does it do what it claims? Are there bugs?
2780
+ 2. **Edge cases** \u2014 what inputs would break it?
2781
+ 3. **Security** \u2014 injection, auth bypass, data exposure, secret leaks
2782
+ 4. **Performance** \u2014 O(n\xB2) loops, N+1 queries, unnecessary allocations
2783
+ 5. **Readability** \u2014 naming, structure, comments (are they accurate?)
2784
+ 6. **Testability** \u2014 is it easy to unit test? Hard dependencies?
2785
+ 7. **Maintainability** \u2014 what breaks when requirements change?
2786
+ {{#if isStudent}}8. **Academic integrity** \u2014 does the complexity match a {{project.studentLevel}} student? Could they explain every line?{{/if}}
2787
+
2788
+ Format: severity [CRITICAL|MAJOR|MINOR|NIT] + file:line + explanation + suggested fix`,
2789
+ contextSchema: [
2790
+ { key: "code", label: "Code to review", type: "text", placeholder: "Paste your code here..." },
2791
+ { key: "context", label: "Context / what it does", type: "text", placeholder: "Authentication service for a REST API" },
2792
+ { key: "isStudent", label: "Academic integrity check?", type: "boolean", default: false }
2793
+ ],
2794
+ tags: ["code-review", "review", "quality"],
2795
+ version: "1.0.0"
2796
+ },
2797
+ {
2798
+ id: "review/security-audit",
2799
+ name: "Security Audit",
2800
+ category: "review",
2801
+ description: "Security audit for OWASP Top 10 vulnerabilities",
2802
+ template: `Perform a security audit of the following {{project.language}} code{{#if project.framework}} using {{project.framework}}{{/if}}.
2803
+
2804
+ Code / feature: {{description}}
2805
+
2806
+ Check for OWASP Top 10 and beyond:
2807
+ 1. **Injection** \u2014 SQL, NoSQL, command, LDAP injection
2808
+ 2. **Broken Authentication** \u2014 weak sessions, token storage, password policies
2809
+ 3. **Sensitive Data Exposure** \u2014 PII in logs, unencrypted data at rest/transit
2810
+ 4. **Broken Access Control** \u2014 missing authorization checks, IDOR
2811
+ 5. **Security Misconfiguration** \u2014 default creds, verbose errors, open CORS
2812
+ 6. **XSS** \u2014 reflected, stored, DOM-based
2813
+ 7. **CSRF** \u2014 state-changing requests without CSRF tokens
2814
+ 8. **Insecure Dependencies** \u2014 known CVEs in dependencies
2815
+ 9. **Insufficient Logging** \u2014 failed logins, privilege changes not logged
2816
+
2817
+ For each finding: [CRITICAL|HIGH|MEDIUM|LOW] + attack vector + proof-of-concept + remediation`,
2818
+ contextSchema: [
2819
+ { key: "description", label: "Feature / code to audit", type: "text", placeholder: "User authentication and session management" }
2820
+ ],
2821
+ tags: ["security", "audit", "owasp"],
2822
+ version: "1.0.0"
2823
+ },
2824
+ {
2825
+ id: "review/performance-review",
2826
+ name: "Performance Review",
2827
+ category: "review",
2828
+ description: "Profile and identify performance bottlenecks in code",
2829
+ template: `Review the following {{project.language}} code for performance issues.
2830
+
2831
+ Context: {{context}}
2832
+ Code:
2833
+ {{code}}
2834
+
2835
+ Analysis dimensions:
2836
+ 1. **Algorithmic complexity** \u2014 is there a better algorithm? O(n\xB2) hidden in innocent-looking code?
2837
+ 2. **Database** \u2014 N+1 queries, missing indexes, fetching more columns/rows than needed
2838
+ 3. **Memory** \u2014 large allocations, memory leaks (unclosed streams, retained event listeners), unintended object retention
2839
+ 4. **CPU** \u2014 blocking the event loop (Node.js), synchronous I/O, excessive JSON serialization
2840
+ 5. **Network** \u2014 unnecessary round trips, large payloads, missing compression, no caching headers
2841
+ 6. **Render performance** (if frontend) \u2014 unnecessary re-renders, missing memoization, layout thrashing
2842
+
2843
+ For each finding:
2844
+ - [CRITICAL|HIGH|MEDIUM|LOW] severity
2845
+ - What is slow and why
2846
+ - Profiling technique to confirm: how to measure it
2847
+ - Optimized code or approach
2848
+ - Expected improvement magnitude
2849
+
2850
+ Profiling tip: measure first, optimize second. Profile with realistic data sizes, not toy examples.`,
2851
+ contextSchema: [
2852
+ { key: "code", label: "Code to review", type: "text", placeholder: "Paste the code or describe the function..." },
2853
+ { key: "context", label: "Context (what the code does, current perf numbers)", type: "text", placeholder: "Endpoint that lists orders, takes 3s at 10k orders" }
2854
+ ],
2855
+ tags: ["performance", "profiling", "review", "optimization"],
2856
+ version: "1.0.0"
2857
+ },
2858
+ {
2859
+ id: "review/accessibility-audit",
2860
+ name: "Accessibility Audit",
2861
+ category: "review",
2862
+ description: "Audit UI code for WCAG 2.1 accessibility compliance",
2863
+ template: `Perform an accessibility audit of the following {{project.language}} UI code.
2864
+
2865
+ Component/page: {{componentDescription}}
2866
+ Code:
2867
+ {{code}}
2868
+
2869
+ Check against WCAG 2.1 Level AA:
2870
+
2871
+ **Perceivable**
2872
+ - [ ] Images have descriptive alt text (or alt="" if decorative)
2873
+ - [ ] Color contrast ratio \u2265 4.5:1 for normal text, 3:1 for large text
2874
+ - [ ] Content not conveyed by color alone
2875
+ - [ ] Videos have captions
2876
+
2877
+ **Operable**
2878
+ - [ ] All functionality reachable by keyboard (Tab, Enter, Space, Arrow keys)
2879
+ - [ ] No keyboard traps (user can always Tab out)
2880
+ - [ ] Visible focus indicator on interactive elements
2881
+ - [ ] No content that flashes > 3 times/second
2882
+
2883
+ **Understandable**
2884
+ - [ ] Form inputs have visible labels (not just placeholder)
2885
+ - [ ] Error messages identify which field and how to fix it
2886
+ - [ ] Language attribute set on <html>
2887
+
2888
+ **Robust**
2889
+ - [ ] Semantic HTML (button not div for clicks, nav/main/header landmarks)
2890
+ - [ ] ARIA used correctly (role, aria-label, aria-describedby, aria-live)
2891
+ - [ ] Works with screen reader (NVDA/VoiceOver mental walkthrough)
2892
+
2893
+ For each issue: [CRITICAL|MAJOR|MINOR] + specific element + WCAG criterion + fix.`,
2894
+ contextSchema: [
2895
+ { key: "code", label: "UI code to audit", type: "text", placeholder: "Paste the component or HTML here..." },
2896
+ { key: "componentDescription", label: "What the component does", type: "text", placeholder: "Login form with email and password fields" }
2897
+ ],
2898
+ tags: ["accessibility", "wcag", "a11y", "review"],
2899
+ version: "1.0.0"
2900
+ },
2901
+ {
2902
+ id: "review/api-design",
2903
+ name: "API Design Review",
2904
+ category: "review",
2905
+ description: "Review REST API design for consistency and best practices",
2906
+ template: `Review the following API design for {{project.name}}.
2907
+
2908
+ API endpoints:
2909
+ {{endpoints}}
2910
+
2911
+ Review dimensions:
2912
+
2913
+ **Naming & Structure**
2914
+ - [ ] Resources are nouns, not verbs (/users not /getUsers)
2915
+ - [ ] Consistent pluralization (/users/{id}/orders not mixed)
2916
+ - [ ] Nested resources max 2 levels deep
2917
+ - [ ] Query params for filtering/sorting, path params for identity
2918
+
2919
+ **HTTP Semantics**
2920
+ - [ ] Correct HTTP methods (GET=read, POST=create, PUT=replace, PATCH=partial, DELETE=remove)
2921
+ - [ ] Idempotent operations use PUT/DELETE correctly
2922
+ - [ ] Correct status codes (201 for create, 204 for no-body delete, 409 for conflict)
2923
+
2924
+ **Request/Response**
2925
+ - [ ] Consistent response envelope (data, meta, errors)
2926
+ - [ ] Pagination: cursor-based preferred; always include total count and nextCursor
2927
+ - [ ] Error shape: { error: { code, message, details? } }
2928
+ - [ ] Versioning strategy (URI v1 vs Accept header)
2929
+
2930
+ **Security**
2931
+ - [ ] No sensitive data in URLs (tokens, passwords, PII)
2932
+ - [ ] Rate limiting headers documented
2933
+ - [ ] Auth scheme documented (Bearer JWT)
2934
+
2935
+ For each issue: severity + endpoint + what's wrong + suggested fix.`,
2936
+ contextSchema: [
2937
+ { key: "endpoints", label: "API endpoints to review", type: "text", placeholder: "GET /getUsers, POST /createOrder/:id, DELETE /user?id=123" }
2938
+ ],
2939
+ tags: ["api", "rest", "design", "review"],
2940
+ version: "1.0.0"
2941
+ },
2942
+ // ── General ───────────────────────────────────────────────────────────────────
2943
+ {
2944
+ id: "general/refactor",
2945
+ name: "Refactor Plan",
2946
+ category: "general",
2947
+ description: "Plan a safe, incremental refactoring",
2948
+ template: `Plan a safe refactoring of {{target}} in {{project.language}}.
2949
+
2950
+ Current problem: {{problem}}
2951
+ Goal: {{goal}}
2952
+
2953
+ Produce a step-by-step refactoring plan where each step:
2954
+ - Is independently deployable (the codebase works after each step)
2955
+ - Has a clear rollback path
2956
+ - Can be reviewed in a small PR
2957
+
2958
+ Steps should cover:
2959
+ 1. Add tests to cover current behavior (before changing anything)
2960
+ 2. Incremental structural changes
2961
+ 3. Rename/move phase
2962
+ 4. Cleanup phase (remove dead code)
2963
+
2964
+ Also flag: which step has the highest risk of regression, and how to detect it?`,
2965
+ contextSchema: [
2966
+ { key: "target", label: "What to refactor", type: "text", placeholder: "UserController (God class with 800 lines)" },
2967
+ { key: "problem", label: "Current problem", type: "text", placeholder: "All user logic is in one file, hard to test, one class does too much" },
2968
+ { key: "goal", label: "Goal after refactoring", type: "text", placeholder: "Split into UserService, UserRepository, UserValidator with clear responsibilities" }
2969
+ ],
2970
+ tags: ["refactoring", "code-quality", "general"],
2971
+ version: "1.0.0"
2972
+ },
2973
+ {
2974
+ id: "general/debug-session",
2975
+ name: "Debug Session",
2976
+ category: "general",
2977
+ description: "Structured debugging guide for a bug or unexpected behavior",
2978
+ template: `Guide me through debugging the following issue in {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}.
2979
+
2980
+ Bug description: {{bugDescription}}
2981
+
2982
+ Symptom: {{symptom}}
2983
+ Expected: {{expected}}
2984
+ Actual: {{actual}}
2985
+
2986
+ Apply the scientific debugging method:
2987
+ 1. **Hypothesis** \u2014 what are the 3 most likely root causes?
2988
+ 2. **Observation points** \u2014 where to add logging/breakpoints to confirm/deny each hypothesis
2989
+ 3. **Bisection** \u2014 how to narrow the problem to the smallest failing case
2990
+ 4. **Common traps** \u2014 async timing issues, mutation bugs, off-by-one, encoding issues related to this symptom
2991
+ 5. **Fix** \u2014 once root cause is identified, what's the minimal fix?
2992
+ 6. **Prevention** \u2014 what test would have caught this before it shipped?`,
2993
+ contextSchema: [
2994
+ { key: "bugDescription", label: "Bug description", type: "text", placeholder: "Login fails silently for users with special characters in their email" },
2995
+ { key: "symptom", label: "Symptom", type: "text", placeholder: "POST /login returns 200 but no token is returned" },
2996
+ { key: "expected", label: "Expected behavior", type: "text", placeholder: 'Returns { token: "..." } and redirects to dashboard' },
2997
+ { key: "actual", label: "Actual behavior", type: "text", placeholder: "Returns { } and shows a blank page" }
2998
+ ],
2999
+ tags: ["debugging", "bug", "general"],
3000
+ version: "1.0.0"
3001
+ },
3002
+ {
3003
+ id: "general/documentation",
3004
+ name: "Documentation",
3005
+ category: "general",
3006
+ description: "Generate developer documentation for code or a system",
3007
+ template: `Write developer documentation for {{target}} in {{project.language}}.
3008
+
3009
+ Type of documentation needed: {{docType}}
3010
+
3011
+ Code/system description: {{description}}
3012
+
3013
+ Documentation should include:
3014
+ - **Overview** \u2014 what it is, what problem it solves, who uses it
3015
+ - **Quick start** \u2014 minimum code to get it working in 3 minutes
3016
+ - **API reference** \u2014 every public function/method: signature, parameters, return value, throws
3017
+ - **Examples** \u2014 at least 3 realistic usage examples
3018
+ - **Configuration** \u2014 all options, their types, defaults, and effects
3019
+ - **Common errors** \u2014 top 5 errors users hit and how to fix them
3020
+ - **Architecture note** \u2014 key design decisions and why
3021
+
3022
+ Tone: peer developer, not corporate. Assume they can read code but don't know YOUR code.`,
3023
+ contextSchema: [
3024
+ { key: "target", label: "What to document", type: "text", placeholder: "BrainForgeClient npm package" },
3025
+ { key: "description", label: "What it does", type: "text", placeholder: "A client library for the BrainForge AI API" },
3026
+ { key: "docType", label: "Documentation type", type: "select", options: ["README", "API reference", "tutorial", "architecture doc"], default: "README" }
3027
+ ],
3028
+ tags: ["documentation", "readme", "general"],
3029
+ version: "1.0.0"
3030
+ },
3031
+ {
3032
+ id: "general/architecture-decision",
3033
+ name: "Architecture Decision Record",
3034
+ category: "general",
3035
+ description: "Document a technical decision as a structured ADR",
3036
+ template: `Write an Architecture Decision Record (ADR) for the following decision in {{project.name}}.
3037
+
3038
+ Decision: {{decision}}
3039
+ Date: today
3040
+
3041
+ # ADR: {{decision}}
3042
+
3043
+ ## Status
3044
+ Proposed
3045
+
3046
+ ## Context
3047
+ {{context}}
3048
+
3049
+ What is the problem or opportunity that requires a decision? What forces are at play (technical constraints, team skills, budget, deadlines)?
3050
+
3051
+ ## Decision
3052
+ We will {{chosenOption}}.
3053
+
3054
+ ## Options Considered
3055
+
3056
+ **Option A: {{optionA}}**
3057
+ - Pros: [analyze]
3058
+ - Cons: [analyze]
3059
+ - Risk: [what could go wrong]
3060
+
3061
+ **Option B: {{optionB}}**
3062
+ - Pros: [analyze]
3063
+ - Cons: [analyze]
3064
+ - Risk: [what could go wrong]
3065
+
3066
+ {{#if optionC}}**Option C: {{optionC}}**
3067
+ - Pros: [analyze]
3068
+ - Cons: [analyze]{{/if}}
3069
+
3070
+ ## Rationale
3071
+ Why {{chosenOption}} over the alternatives. Be specific \u2014 reference the context forces.
3072
+
3073
+ ## Consequences
3074
+ **Positive:** what gets better
3075
+ **Negative:** what gets harder or costs more
3076
+ **Neutral:** things that change but are neither better nor worse
3077
+
3078
+ ## Review Trigger
3079
+ This decision should be revisited if: {{reviewTrigger}}`,
3080
+ contextSchema: [
3081
+ { key: "decision", label: "Decision to document", type: "text", placeholder: "Use PostgreSQL instead of MongoDB for primary data store" },
3082
+ { key: "context", label: "Context / problem", type: "text", placeholder: "We need to choose a database before starting the data layer" },
3083
+ { key: "chosenOption", label: "Chosen option", type: "text", placeholder: "PostgreSQL" },
3084
+ { key: "optionA", label: "Option A", type: "text", placeholder: "PostgreSQL" },
3085
+ { key: "optionB", label: "Option B", type: "text", placeholder: "MongoDB" },
3086
+ { key: "optionC", label: "Option C (optional)", type: "text", placeholder: "DynamoDB" },
3087
+ { key: "reviewTrigger", label: "Review trigger", type: "text", placeholder: "query performance degrades below 100ms P99 at 10M records" }
3088
+ ],
3089
+ tags: ["adr", "architecture", "decision", "documentation"],
3090
+ version: "1.0.0"
3091
+ },
3092
+ {
3093
+ id: "general/tech-debt",
3094
+ name: "Tech Debt Inventory",
3095
+ category: "general",
3096
+ description: "Identify, prioritize, and plan a tech debt paydown",
3097
+ template: `Create a tech debt inventory and paydown plan for {{project.name}} in {{project.language}}.
3098
+
3099
+ Area of concern: {{area}}
3100
+
3101
+ **Step 1 \u2014 Identify debt items**
3102
+ Analyze the described codebase area for:
3103
+ - Design debt: poor abstractions, wrong patterns, violated SOLID/DRY
3104
+ - Code debt: duplicated logic, dead code, magic numbers, missing types
3105
+ - Test debt: missing tests, brittle tests, slow test suite
3106
+ - Documentation debt: outdated docs, missing ADRs, no onboarding guide
3107
+ - Dependency debt: outdated packages, deprecated APIs, security CVEs
3108
+ - Infrastructure debt: manual processes that should be automated
3109
+
3110
+ **Step 2 \u2014 Prioritize by impact \xD7 effort**
3111
+ For each item:
3112
+ | Debt Item | Type | Impact (1-5) | Effort (days) | Risk if ignored | Priority |
3113
+ |-----------|------|--------------|---------------|-----------------|----------|
3114
+
3115
+ **Step 3 \u2014 Paydown plan for {{sprint}} sprints**
3116
+ - Items to tackle this sprint (highest impact/effort ratio)
3117
+ - Items to defer with a trigger condition for when to revisit
3118
+ - Items to accept and document as intentional constraints
3119
+
3120
+ **Step 4 \u2014 Prevention**
3121
+ - What process change prevents this class of debt from re-accumulating?
3122
+ - Suggest a definition of done that includes tech debt hygiene`,
3123
+ contextSchema: [
3124
+ { key: "area", label: "Codebase area with debt", type: "text", placeholder: "Authentication module \u2014 was written in a hackathon, never cleaned up" },
3125
+ { key: "sprint", label: "Planning horizon (sprints)", type: "text", default: "3" }
3126
+ ],
3127
+ tags: ["tech-debt", "refactoring", "planning", "general"],
3128
+ version: "1.0.0"
3129
+ },
3130
+ {
3131
+ id: "general/onboarding",
3132
+ name: "Developer Onboarding",
3133
+ category: "general",
3134
+ description: "Create a new developer onboarding guide for a codebase",
3135
+ template: `Write a developer onboarding guide for {{project.name}}.
3136
+
3137
+ Role being onboarded: {{role}}
3138
+ Stack: {{project.language}}{{#if project.framework}} / {{project.framework}}{{/if}}
3139
+
3140
+ The guide should get a developer from "just joined" to "merged first PR" in {{targetDays}} days.
3141
+
3142
+ **Day 1 \u2014 Environment setup**
3143
+ 1. Prerequisites (what must be installed)
3144
+ 2. Clone + install: exact commands, not "install dependencies"
3145
+ 3. Environment variables: copy .env.example \u2192 .env, document which vars need real values for local dev
3146
+ 4. Run the app: exact command + expected output + URL
3147
+ 5. Run the tests: expect all green
3148
+
3149
+ **First week \u2014 Mental model**
3150
+ - Architecture overview: the 3 most important concepts to understand first
3151
+ - Key directories and what lives where
3152
+ - How data flows: from HTTP request to database and back
3153
+ - The 3 files to read first
3154
+
3155
+ **First PR checklist**
3156
+ - Good first issues: what type of task is appropriate for the first contribution?
3157
+ - PR process: branch naming, commit format, review expectations
3158
+ - Definition of done for this project
3159
+
3160
+ **Common pitfalls**
3161
+ - Top 3 things that trip up new developers on this codebase
3162
+ - Local gotchas (Windows/Mac differences, port conflicts, etc.)
3163
+
3164
+ **Who to ask for what** \u2014 map topics to team members/channels`,
3165
+ contextSchema: [
3166
+ { key: "role", label: "Developer role being onboarded", type: "text", placeholder: "junior fullstack developer" },
3167
+ { key: "targetDays", label: "Days to first PR", type: "text", default: "3" }
3168
+ ],
3169
+ tags: ["onboarding", "documentation", "team", "general"],
3170
+ version: "1.0.0"
3171
+ },
3172
+ {
3173
+ id: "general/api-design",
3174
+ name: "API Design",
3175
+ category: "general",
3176
+ description: "Design a RESTful API from scratch with consistent conventions",
3177
+ template: `Design a REST API for {{resource}} in {{project.name}}.
3178
+
3179
+ Purpose: {{purpose}}
3180
+ Consumers: {{consumers}}
3181
+
3182
+ **Resource model**
3183
+ - Primary resource: {{resource}}
3184
+ - Relationships: {{relationships}}
3185
+ - Actions that don't map to CRUD: {{customActions}}
3186
+
3187
+ **Endpoint design**
3188
+
3189
+ For each endpoint provide: method + path + description + request body + response shape + possible error codes
3190
+
3191
+ Standard CRUD:
3192
+ - List (with filtering, sorting, pagination)
3193
+ - Get by ID
3194
+ - Create
3195
+ - Update (full replacement)
3196
+ - Partial update
3197
+ - Delete
3198
+
3199
+ Custom actions: {{customActions}}
3200
+
3201
+ **Conventions to follow**
3202
+ - Response envelope: { data, meta: { total, page, limit } } for lists; { data } for single
3203
+ - Error envelope: { error: { code, message, details? } }
3204
+ - Timestamps: ISO 8601 (2024-01-15T10:30:00Z)
3205
+ - IDs: UUID v4
3206
+ - Soft delete: return 404 for deleted resources (don't expose deleted=true)
3207
+
3208
+ **Versioning strategy**: URI prefix /v1/
3209
+
3210
+ Produce a full OpenAPI-style endpoint list with example request/response JSON.`,
3211
+ contextSchema: [
3212
+ { key: "resource", label: "Primary resource", type: "text", placeholder: "Blog Post" },
3213
+ { key: "purpose", label: "API purpose", type: "text", placeholder: "Content management for a blog platform" },
3214
+ { key: "consumers", label: "API consumers", type: "text", placeholder: "React SPA, iOS app, third-party webhook integrations" },
3215
+ { key: "relationships", label: "Relationships", type: "text", placeholder: "Post belongs to User, has many Comments and Tags" },
3216
+ { key: "customActions", label: "Non-CRUD actions", type: "text", placeholder: "publish, unpublish, duplicate" }
3217
+ ],
3218
+ tags: ["api", "rest", "design", "openapi"],
3219
+ version: "1.0.0"
3220
+ },
3221
+ {
3222
+ id: "general/incident-postmortem",
3223
+ name: "Incident Post-mortem",
3224
+ category: "general",
3225
+ description: "Write a blameless post-mortem for a production incident",
3226
+ template: `Write a blameless post-mortem for a production incident affecting {{project.name}}.
3227
+
3228
+ Incident summary: {{summary}}
3229
+ Duration: {{duration}}
3230
+ Impact: {{impact}}
3231
+
3232
+ # Post-mortem: {{summary}}
3233
+
3234
+ ## Timeline (UTC)
3235
+ | Time | Event |
3236
+ |------|-------|
3237
+ (fill in detection \u2192 diagnosis \u2192 mitigation \u2192 resolution)
3238
+
3239
+ ## Impact
3240
+ - Users affected: {{impact}}
3241
+ - Duration: {{duration}}
3242
+ - Services affected: {{services}}
3243
+
3244
+ ## Root Cause
3245
+ (The single technical root cause \u2014 not "human error")
3246
+
3247
+ ## Contributing Factors
3248
+ (Conditions that made the incident worse or harder to detect)
3249
+
3250
+ ## What Went Well
3251
+ - Fast detection
3252
+ - Clear communication
3253
+ - Effective rollback
3254
+
3255
+ ## What Went Poorly
3256
+ - (Honest assessment without blame)
3257
+
3258
+ ## Action Items
3259
+ | Action | Owner | Due Date | Priority |
3260
+ |--------|-------|----------|----------|
3261
+ | Add alert for [metric] | | | HIGH |
3262
+ | Add test for [scenario] | | | HIGH |
3263
+ | Update runbook for [procedure] | | | MEDIUM |
3264
+
3265
+ ## Lessons Learned
3266
+ Three concrete changes that will prevent this class of incident.
3267
+
3268
+ ## Blameless Culture Note
3269
+ Systems fail. This post-mortem focuses on fixing systems, not assigning blame. The people involved made reasonable decisions given what they knew at the time.`,
3270
+ contextSchema: [
3271
+ { key: "summary", label: "Incident summary", type: "text", placeholder: "Database connection pool exhaustion caused 15-minute outage" },
3272
+ { key: "duration", label: "Incident duration", type: "text", placeholder: "47 minutes" },
3273
+ { key: "impact", label: "User impact", type: "text", placeholder: "2,300 users unable to login; ~$8k revenue impact" },
3274
+ { key: "services", label: "Services affected", type: "text", placeholder: "API server, dashboard, mobile app" }
3275
+ ],
3276
+ tags: ["incident", "postmortem", "sre", "general"],
3277
+ version: "1.0.0"
3278
+ }
3279
+ ];
3280
+
3281
+ // src/skills/registry.ts
3282
+ var SkillRegistry = class {
3283
+ skills = /* @__PURE__ */ new Map();
3284
+ compiled = /* @__PURE__ */ new Map();
3285
+ constructor() {
3286
+ Handlebars2.registerHelper("eq", (a, b) => a === b);
3287
+ Handlebars2.registerHelper("ne", (a, b) => a !== b);
3288
+ for (const skill of BUILT_IN_SKILLS) {
3289
+ this.register(skill);
3290
+ }
3291
+ }
3292
+ register(skill) {
3293
+ this.skills.set(skill.id, skill);
3294
+ this.compiled.set(skill.id, Handlebars2.compile(skill.template));
3295
+ }
3296
+ async loadUserSkills(skillsDir) {
3297
+ try {
3298
+ const entries = await fs6.readdir(skillsDir, { withFileTypes: true });
3299
+ for (const entry of entries) {
3300
+ if (!entry.name.endsWith(".json")) continue;
3301
+ try {
3302
+ const raw = await fs6.readFile(path6.join(skillsDir, entry.name), "utf-8");
3303
+ const skill = JSON.parse(raw);
3304
+ this.register(skill);
3305
+ } catch {
3306
+ }
3307
+ }
3308
+ } catch {
3309
+ }
3310
+ }
3311
+ get(id) {
3312
+ return this.skills.get(id);
3313
+ }
3314
+ list(filter) {
3315
+ let skills = Array.from(this.skills.values());
3316
+ if (filter?.category) skills = skills.filter((s) => s.category === filter.category);
3317
+ if (filter?.tag) skills = skills.filter((s) => s.tags.includes(filter.tag));
3318
+ return skills.sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));
3319
+ }
3320
+ listByCategory() {
3321
+ const map = /* @__PURE__ */ new Map();
3322
+ for (const skill of this.list()) {
3323
+ const group = map.get(skill.category) ?? [];
3324
+ group.push(skill);
3325
+ map.set(skill.category, group);
3326
+ }
3327
+ return map;
3328
+ }
3329
+ render(id, context) {
3330
+ const template = this.compiled.get(id);
3331
+ if (!template) throw new Error(`Skill "${id}" not found.`);
3332
+ return template(context);
3333
+ }
3334
+ count() {
3335
+ const builtin = BUILT_IN_SKILLS.length;
3336
+ return { builtin, custom: this.skills.size - builtin };
3337
+ }
3338
+ search(query) {
3339
+ const q = query.toLowerCase();
3340
+ return this.list().filter(
3341
+ (s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.tags.some((t) => t.includes(q)) || s.id.toLowerCase().includes(q)
3342
+ );
3343
+ }
3344
+ };
3345
+
3346
+ // src/adapters/types.ts
3347
+ var DEFAULT_MODELS = {
3348
+ claude: "claude-sonnet-4-6",
3349
+ openai: "gpt-4o",
3350
+ gemini: "gemini-1.5-pro",
3351
+ deepseek: "deepseek-chat"
3352
+ };
3353
+
3354
+ // src/adapters/claude.ts
3355
+ var ClaudeAdapter = class {
3356
+ provider = "claude";
3357
+ model;
3358
+ apiKeyEnv;
3359
+ constructor(config) {
3360
+ this.model = config.model ?? DEFAULT_MODELS.claude;
3361
+ this.apiKeyEnv = config.apiKeyEnv;
3362
+ }
3363
+ isConfigured() {
3364
+ return Boolean(process.env[this.apiKeyEnv]);
3365
+ }
3366
+ async complete(prompt, options = {}) {
3367
+ const apiKey = process.env[this.apiKeyEnv];
3368
+ if (!apiKey) {
3369
+ throw new Error(
3370
+ `Anthropic API key not found. Set the ${this.apiKeyEnv} environment variable.
3371
+ Get your key at https://console.anthropic.com/`
3372
+ );
3373
+ }
3374
+ let Anthropic;
3375
+ try {
3376
+ ({ default: Anthropic } = await import("@anthropic-ai/sdk"));
3377
+ } catch {
3378
+ throw new Error(
3379
+ "Anthropic SDK not installed. Run: npm install @anthropic-ai/sdk"
3380
+ );
3381
+ }
3382
+ const client = new Anthropic({ apiKey });
3383
+ const response = await client.messages.create({
3384
+ model: this.model,
3385
+ max_tokens: options.maxTokens ?? 4096,
3386
+ ...options.system ? { system: options.system } : {},
3387
+ messages: [{ role: "user", content: prompt }]
3388
+ });
3389
+ const block = response.content[0];
3390
+ return block?.type === "text" ? block.text : "";
3391
+ }
3392
+ };
3393
+
3394
+ // src/adapters/openai.ts
3395
+ var OpenAIAdapter = class {
3396
+ provider = "openai";
3397
+ model;
3398
+ apiKeyEnv;
3399
+ constructor(config) {
3400
+ this.model = config.model ?? DEFAULT_MODELS.openai;
3401
+ this.apiKeyEnv = config.apiKeyEnv;
3402
+ }
3403
+ isConfigured() {
3404
+ return Boolean(process.env[this.apiKeyEnv]);
3405
+ }
3406
+ async complete(prompt, options = {}) {
3407
+ const apiKey = process.env[this.apiKeyEnv];
3408
+ if (!apiKey) {
3409
+ throw new Error(
3410
+ `OpenAI API key not found. Set the ${this.apiKeyEnv} environment variable.
3411
+ Get your key at https://platform.openai.com/api-keys`
3412
+ );
3413
+ }
3414
+ let OpenAI;
3415
+ try {
3416
+ ({ default: OpenAI } = await import("openai"));
3417
+ } catch {
3418
+ throw new Error("OpenAI SDK not installed. Run: npm install openai");
3419
+ }
3420
+ const client = new OpenAI({ apiKey });
3421
+ const response = await client.chat.completions.create({
3422
+ model: this.model,
3423
+ max_tokens: options.maxTokens ?? 4096,
3424
+ temperature: options.temperature ?? 0.7,
3425
+ messages: [
3426
+ ...options.system ? [{ role: "system", content: options.system }] : [],
3427
+ { role: "user", content: prompt }
3428
+ ]
3429
+ });
3430
+ return response.choices[0]?.message.content ?? "";
3431
+ }
3432
+ };
3433
+
3434
+ // src/adapters/gemini.ts
3435
+ var GeminiAdapter = class {
3436
+ provider = "gemini";
3437
+ model;
3438
+ apiKeyEnv;
3439
+ constructor(config) {
3440
+ this.model = config.model ?? DEFAULT_MODELS.gemini;
3441
+ this.apiKeyEnv = config.apiKeyEnv;
3442
+ }
3443
+ isConfigured() {
3444
+ return Boolean(process.env[this.apiKeyEnv]);
3445
+ }
3446
+ async complete(prompt, options = {}) {
3447
+ const apiKey = process.env[this.apiKeyEnv];
3448
+ if (!apiKey) {
3449
+ throw new Error(
3450
+ `Gemini API key not found. Set the ${this.apiKeyEnv} environment variable.
3451
+ Get your key at https://aistudio.google.com/app/apikey`
3452
+ );
3453
+ }
3454
+ let GoogleGenerativeAI;
3455
+ try {
3456
+ ({ GoogleGenerativeAI } = await import("@google/generative-ai"));
3457
+ } catch {
3458
+ throw new Error(
3459
+ "Google AI SDK not installed. Run: npm install @google/generative-ai"
3460
+ );
3461
+ }
3462
+ const genAI = new GoogleGenerativeAI(apiKey);
3463
+ const genModel = genAI.getGenerativeModel({ model: this.model });
3464
+ const fullPrompt = options.system ? `${options.system}
3465
+
3466
+ ${prompt}` : prompt;
3467
+ const result = await genModel.generateContent(fullPrompt);
3468
+ return result.response.text();
3469
+ }
3470
+ };
3471
+
3472
+ // src/adapters/deepseek.ts
3473
+ var DeepSeekAdapter = class {
3474
+ provider = "deepseek";
3475
+ model;
3476
+ apiKeyEnv;
3477
+ constructor(config) {
3478
+ this.model = config.model ?? DEFAULT_MODELS.deepseek;
3479
+ this.apiKeyEnv = config.apiKeyEnv;
3480
+ }
3481
+ isConfigured() {
3482
+ return Boolean(process.env[this.apiKeyEnv]);
3483
+ }
3484
+ async complete(prompt, options = {}) {
3485
+ const apiKey = process.env[this.apiKeyEnv];
3486
+ if (!apiKey) {
3487
+ throw new Error(
3488
+ `DeepSeek API key not found. Set the ${this.apiKeyEnv} environment variable.
3489
+ Get your key at https://platform.deepseek.com/api_keys`
3490
+ );
3491
+ }
3492
+ let OpenAI;
3493
+ try {
3494
+ ({ default: OpenAI } = await import("openai"));
3495
+ } catch {
3496
+ throw new Error(
3497
+ "OpenAI SDK not installed (required for DeepSeek). Run: npm install openai"
3498
+ );
3499
+ }
3500
+ const client = new OpenAI({
3501
+ apiKey,
3502
+ baseURL: "https://api.deepseek.com"
3503
+ });
3504
+ const response = await client.chat.completions.create({
3505
+ model: this.model,
3506
+ max_tokens: options.maxTokens ?? 4096,
3507
+ temperature: options.temperature ?? 0.7,
3508
+ messages: [
3509
+ ...options.system ? [{ role: "system", content: options.system }] : [],
3510
+ { role: "user", content: prompt }
3511
+ ]
3512
+ });
3513
+ return response.choices[0]?.message.content ?? "";
3514
+ }
3515
+ };
3516
+
3517
+ // src/adapters/manager.ts
3518
+ var AdapterManager = class _AdapterManager {
3519
+ static create(config) {
3520
+ switch (config.provider) {
3521
+ case "claude":
3522
+ return new ClaudeAdapter(config);
3523
+ case "openai":
3524
+ return new OpenAIAdapter(config);
3525
+ case "gemini":
3526
+ return new GeminiAdapter(config);
3527
+ case "deepseek":
3528
+ return new DeepSeekAdapter(config);
3529
+ default:
3530
+ throw new Error(`Unknown AI provider: ${String(config.provider)}`);
3531
+ }
3532
+ }
3533
+ static fromState(state) {
3534
+ if (!state.aiConfig) return null;
3535
+ return _AdapterManager.create(state.aiConfig);
3536
+ }
3537
+ };
3538
+ export {
3539
+ AdapterManager,
3540
+ BUILT_IN_SKILLS,
3541
+ BrainForgeServer,
3542
+ ClaudeAdapter,
3543
+ CodebaseMapper,
3544
+ DeepSeekAdapter,
3545
+ FileWatcher,
3546
+ GeminiAdapter,
3547
+ MockDefense,
3548
+ OpenAIAdapter,
3549
+ ProfessorScanner,
3550
+ PromptEngine,
3551
+ SkillRegistry,
3552
+ StateManager,
3553
+ TaskPlanner,
3554
+ buildQuestionSet,
3555
+ reduce
3556
+ };