@aletheia-works/vivarium-mcp 0.0.1

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 (45) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +118 -0
  3. package/dist/bundled/recipes.json +217 -0
  4. package/dist/catalogue.d.ts +7 -0
  5. package/dist/catalogue.d.ts.map +1 -0
  6. package/dist/catalogue.js +63 -0
  7. package/dist/catalogue.js.map +1 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +18 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/server.d.ts +4 -0
  13. package/dist/server.d.ts.map +1 -0
  14. package/dist/server.js +114 -0
  15. package/dist/server.js.map +1 -0
  16. package/dist/tools/get_recipe.d.ts +28 -0
  17. package/dist/tools/get_recipe.d.ts.map +1 -0
  18. package/dist/tools/get_recipe.js +29 -0
  19. package/dist/tools/get_recipe.js.map +1 -0
  20. package/dist/tools/list_recipes.d.ts +34 -0
  21. package/dist/tools/list_recipes.d.ts.map +1 -0
  22. package/dist/tools/list_recipes.js +43 -0
  23. package/dist/tools/list_recipes.js.map +1 -0
  24. package/dist/tools/lookup_verdict.d.ts +39 -0
  25. package/dist/tools/lookup_verdict.d.ts.map +1 -0
  26. package/dist/tools/lookup_verdict.js +60 -0
  27. package/dist/tools/lookup_verdict.js.map +1 -0
  28. package/dist/tools/match_error.d.ts +62 -0
  29. package/dist/tools/match_error.d.ts.map +1 -0
  30. package/dist/tools/match_error.js +282 -0
  31. package/dist/tools/match_error.js.map +1 -0
  32. package/dist/tools/prepare_new_recipe.d.ts +92 -0
  33. package/dist/tools/prepare_new_recipe.d.ts.map +1 -0
  34. package/dist/tools/prepare_new_recipe.js +193 -0
  35. package/dist/tools/prepare_new_recipe.js.map +1 -0
  36. package/dist/tools/verify_branch_fix.d.ts +49 -0
  37. package/dist/tools/verify_branch_fix.d.ts.map +1 -0
  38. package/dist/tools/verify_branch_fix.js +207 -0
  39. package/dist/tools/verify_branch_fix.js.map +1 -0
  40. package/dist/types.d.ts +32 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +14 -0
  43. package/dist/types.js.map +1 -0
  44. package/package.json +56 -0
  45. package/src/bundled/recipes.json +217 -0
@@ -0,0 +1,282 @@
1
+ // Phase 6 X.2 → Phase 7 A5 — match_error tool, v2.
2
+ //
3
+ // Server-side mirror of the docs-site error → recipe matcher
4
+ // (Phase 6 S.2, ADR-0025). The scoring weights are bit-identical
5
+ // with the docs-site `ErrorRecipeMatcher.tsx`:
6
+ // symptom segment match → +5
7
+ // tag segment match → +3
8
+ // project / slug match → +2
9
+ // Recipes with score 0 are dropped. Ties broken by (layer asc, slug asc).
10
+ //
11
+ // Phase 7 A5 (ADR-0028) extends v1 with three accuracy improvements,
12
+ // none of which change the weights above:
13
+ // 1. Adjacent-pair token expansion — "data type" also tries `datatype`,
14
+ // so multi-word user input can hit single-word catalogue terms.
15
+ // 2. Synonym groups — variants of the same concept (e.g. `dtype` ⇄
16
+ // `datatype`) all expand to the whole group.
17
+ // 3. Bounded fuzzy match — tokens of length ≥ 6 within Levenshtein
18
+ // distance 1 score the same as exact, so "missmatch" still hits
19
+ // `mismatch`.
20
+ // 4. Multi-language stopwords — German, Spanish, French, Chinese,
21
+ // Korean noise tokens drop alongside the existing English+Japanese.
22
+ //
23
+ // **CRITICAL: keep in sync with `docs/components/ErrorRecipeMatcher.tsx`.**
24
+ // ADR-0025 §Neutral made the docs surface the canonical implementation;
25
+ // X.2 lifted it server-side bit-identical, and A5 keeps both copies
26
+ // updated atomically. Diverging the scoring between MCP and the docs
27
+ // matcher would surface as agent-vs-UI disagreement on identical input.
28
+ //
29
+ // The matcher remains mechanical — no LLM, no semantic embeddings.
30
+ // AGENTS.md §3.3's mechanical-over-judgement rule carries through.
31
+ import { getCatalogue } from '../catalogue.js';
32
+ const MAX_INPUT_BYTES = 16 * 1024;
33
+ const DEFAULT_LIMIT = 10;
34
+ const MAX_LIMIT = 50;
35
+ // Multi-language stopword set (Phase 7 A5). The goal isn't full
36
+ // lexical coverage of any single language — it's dropping the
37
+ // frequent noise tokens that would never sit in a recipe overlay.
38
+ // Anything that sneaks through scores 0 against the catalogue
39
+ // anyway; the worst case is wasted comparison cycles, not wrong
40
+ // matches.
41
+ const STOPWORDS = new Set([
42
+ // English
43
+ 'the', 'and', 'for', 'with', 'from', 'that', 'this', 'have',
44
+ 'has', 'are', 'was', 'were', 'will', 'not', 'but', 'all',
45
+ 'error', 'errors', 'exception', 'failed', 'failure', 'trace',
46
+ 'traceback', 'stack', 'line', 'file', 'most', 'recent', 'call',
47
+ // Japanese
48
+ 'です', 'ます', 'した', 'する', 'これ', 'それ', 'その', 'この',
49
+ 'エラー', '例外', '失敗', 'スタック',
50
+ // German
51
+ 'der', 'die', 'das', 'und', 'mit', 'von', 'für', 'fehler',
52
+ 'ausnahme', 'aufgetreten',
53
+ // Spanish
54
+ 'que', 'por', 'una', 'los', 'las', 'del', 'con',
55
+ 'excepción', 'fallo',
56
+ // French
57
+ 'pour', 'avec', 'sur', 'dans', 'erreur',
58
+ 'échec',
59
+ // Chinese (Simplified + Traditional, common particles + error words)
60
+ '错误', '异常', '失败', '堆栈', '錯誤', '異常', '失敗', '堆疊',
61
+ // Korean (common particles + error words)
62
+ '오류', '예외', '실패', '스택',
63
+ ]);
64
+ // Synonym groups (Phase 7 A5). Within a group, any token in the
65
+ // user's input expands the input set to include all members.
66
+ // Variants are stored hyphen-stripped (lowercase, alphanumeric)
67
+ // to match the kebab-segmented catalogue tokens.
68
+ //
69
+ // Conservative on purpose — false-positive pressure rises with
70
+ // table size. Add a group only when there's an unambiguous mapping
71
+ // between a user-input shape and a catalogue overlay token.
72
+ const SYNONYM_GROUPS = [
73
+ ['dtype', 'datatype'],
74
+ ['nullptr', 'nullpointer', 'nilptr', 'nullpointerexception'],
75
+ ['segfault', 'sigsegv', 'segmentation'],
76
+ ['flock', 'filelock'],
77
+ ['deadlock', 'deadblock'],
78
+ ['datarace', 'racecondition'],
79
+ ['exitcode', 'exitstatus', 'returncode'],
80
+ ['oom', 'outofmemory'],
81
+ ['encoding', 'utf'],
82
+ ['mismatch', 'mismatched'],
83
+ ];
84
+ const SYNONYM_MAP = (() => {
85
+ const m = new Map();
86
+ for (const group of SYNONYM_GROUPS) {
87
+ for (const member of group) {
88
+ m.set(member, group.filter((g) => g !== member));
89
+ }
90
+ }
91
+ return m;
92
+ })();
93
+ // Phase 7 A5: bounded fuzzy match. Only fires for tokens of length
94
+ // ≥ FUZZY_MIN_LEN; only Levenshtein distance ≤ 1 is accepted (single
95
+ // insert / delete / substitute). Same weight as exact, but the
96
+ // MatchedToken records `via: 'fuzzy'` for client-side rendering.
97
+ const FUZZY_MIN_LEN = 6;
98
+ function withinDistance1(a, b) {
99
+ if (a === b)
100
+ return true;
101
+ const lenA = a.length;
102
+ const lenB = b.length;
103
+ if (Math.abs(lenA - lenB) > 1)
104
+ return false;
105
+ if (lenA === lenB) {
106
+ let diffs = 0;
107
+ for (let i = 0; i < lenA; i++) {
108
+ if (a[i] !== b[i]) {
109
+ diffs++;
110
+ if (diffs > 1)
111
+ return false;
112
+ }
113
+ }
114
+ return diffs === 1;
115
+ }
116
+ const shorter = lenA < lenB ? a : b;
117
+ const longer = lenA < lenB ? b : a;
118
+ let i = 0;
119
+ let j = 0;
120
+ let diffs = 0;
121
+ while (i < shorter.length && j < longer.length) {
122
+ if (shorter[i] !== longer[j]) {
123
+ diffs++;
124
+ if (diffs > 1)
125
+ return false;
126
+ j++;
127
+ }
128
+ else {
129
+ i++;
130
+ j++;
131
+ }
132
+ }
133
+ return true;
134
+ }
135
+ function tokenise(input) {
136
+ let trimmed = input;
137
+ if (trimmed.length > MAX_INPUT_BYTES) {
138
+ trimmed = trimmed.slice(trimmed.length - MAX_INPUT_BYTES);
139
+ }
140
+ const lower = trimmed.toLowerCase();
141
+ const raw = lower.split(/[^a-z0-9_]+/);
142
+ // Stage 1: filter raw tokens (length ≥ 3, not stopword, dedup).
143
+ const ordered = [];
144
+ const seenRaw = new Set();
145
+ for (const t of raw) {
146
+ if (t.length < 3)
147
+ continue;
148
+ if (STOPWORDS.has(t))
149
+ continue;
150
+ if (seenRaw.has(t))
151
+ continue;
152
+ seenRaw.add(t);
153
+ ordered.push(t);
154
+ }
155
+ // Stage 2: adjacent-pair expansion. Lets multi-word user input
156
+ // (e.g. "data type") match single-word catalogue terms (`dtype`)
157
+ // via the synonym table.
158
+ const direct = new Set(ordered);
159
+ for (let i = 0; i < ordered.length - 1; i++) {
160
+ direct.add(ordered[i] + ordered[i + 1]);
161
+ }
162
+ // Stage 3: synonym expansion. For each direct token, if it has a
163
+ // synonym group, add the other members and remember which input
164
+ // token brought them in.
165
+ const synonyms = new Map();
166
+ for (const t of direct) {
167
+ const partners = SYNONYM_MAP.get(t);
168
+ if (!partners)
169
+ continue;
170
+ for (const p of partners) {
171
+ if (direct.has(p) || synonyms.has(p))
172
+ continue;
173
+ synonyms.set(p, t);
174
+ }
175
+ }
176
+ // Stage 4: fuzzy candidate set — direct tokens (not synonym
177
+ // additions) of sufficient length.
178
+ const fuzzyCandidates = [];
179
+ for (const t of direct) {
180
+ if (t.length >= FUZZY_MIN_LEN)
181
+ fuzzyCandidates.push(t);
182
+ }
183
+ return { direct, synonyms, fuzzyCandidates };
184
+ }
185
+ function kebabSegments(value) {
186
+ return value
187
+ .toLowerCase()
188
+ .split(/[^a-z0-9]+/)
189
+ .filter((t) => t.length >= 3 && !STOPWORDS.has(t));
190
+ }
191
+ function matchSegment(seg, tokens) {
192
+ if (tokens.direct.has(seg))
193
+ return { matched: true };
194
+ const synonymInput = tokens.synonyms.get(seg);
195
+ if (synonymInput !== undefined) {
196
+ return { matched: true, via: 'synonym', input: synonymInput };
197
+ }
198
+ if (seg.length >= FUZZY_MIN_LEN) {
199
+ for (const candidate of tokens.fuzzyCandidates) {
200
+ if (withinDistance1(seg, candidate)) {
201
+ return { matched: true, via: 'fuzzy', input: candidate };
202
+ }
203
+ }
204
+ }
205
+ return { matched: false };
206
+ }
207
+ function scoreRecipe(recipe, tokens) {
208
+ const matched = [];
209
+ let score = 0;
210
+ const apply = (source, weight, seg) => {
211
+ const m = matchSegment(seg, tokens);
212
+ if (!m.matched)
213
+ return;
214
+ score += weight;
215
+ const entry = { source, token: seg };
216
+ if (m.via)
217
+ entry.via = m.via;
218
+ if (m.input !== undefined)
219
+ entry.input = m.input;
220
+ matched.push(entry);
221
+ };
222
+ if (recipe.symptom) {
223
+ for (const seg of kebabSegments(recipe.symptom))
224
+ apply('symptom', 5, seg);
225
+ }
226
+ for (const tag of recipe.tags ?? []) {
227
+ for (const seg of kebabSegments(tag))
228
+ apply('tags', 3, seg);
229
+ }
230
+ for (const seg of kebabSegments(recipe.project))
231
+ apply('project', 2, seg);
232
+ for (const seg of kebabSegments(recipe.slug))
233
+ apply('slug', 2, seg);
234
+ return { recipe, score, matched };
235
+ }
236
+ export async function matchError(args) {
237
+ const text = (args.text ?? '').trim();
238
+ if (!text) {
239
+ return { ok: false, error: 'missing required argument: text' };
240
+ }
241
+ const limit = Math.min(Math.max(1, args.limit ?? DEFAULT_LIMIT), MAX_LIMIT);
242
+ const tokens = tokenise(text);
243
+ const { recipes } = await getCatalogue();
244
+ const scored = recipes
245
+ .map((r) => scoreRecipe(r, tokens))
246
+ .filter((s) => s.score > 0);
247
+ scored.sort((a, b) => {
248
+ if (b.score !== a.score)
249
+ return b.score - a.score;
250
+ if (a.recipe.layer !== b.recipe.layer)
251
+ return a.recipe.layer - b.recipe.layer;
252
+ return a.recipe.slug.localeCompare(b.recipe.slug);
253
+ });
254
+ return {
255
+ ok: true,
256
+ query_token_count: tokens.direct.size,
257
+ total_recipes: recipes.length,
258
+ matches: scored.slice(0, limit),
259
+ };
260
+ }
261
+ export const MATCH_ERROR_TOOL = {
262
+ name: 'match_error',
263
+ description: "Find Vivarium recipes that match a pasted error message or stack trace by mechanical token overlap (no LLM). The text is tokenised, lowercase-compared against each recipe's symptom (weight 5 per segment), tags (3), project (2), and slug (2). v2 (Phase 7 A5) adds adjacent-pair tokens (so 'data type' can hit `dtype`), a small synonym table (e.g. dtype⇄datatype, segfault⇄sigsegv), and bounded Levenshtein-1 fuzzy matching for tokens of length ≥ 6. Stopword set covers English, Japanese, German, Spanish, French, Chinese, and Korean noise tokens. Recipes with score > 0 are returned in descending score order; ties broken by (layer asc, slug asc). Each matched token reports its `via` (`synonym` or `fuzzy` when non-exact) and the original `input` that triggered it, so agents can render the provenance. Pair with `get_recipe` to drill into a specific result, or `list_recipes` for unfiltered browsing.",
264
+ inputSchema: {
265
+ type: 'object',
266
+ properties: {
267
+ text: {
268
+ type: 'string',
269
+ description: 'Error message, stack trace, or any free-text fragment to match against the catalogue. Tokenised on non-alphanumeric runs after lowercasing; tokens shorter than 3 chars and a multi-language stopword list are dropped. Input longer than 16 KB is truncated from the front.',
270
+ },
271
+ limit: {
272
+ type: 'integer',
273
+ minimum: 1,
274
+ maximum: 50,
275
+ default: 10,
276
+ description: 'Maximum number of matches to return. Defaults to 10; capped at 50.',
277
+ },
278
+ },
279
+ required: ['text'],
280
+ },
281
+ };
282
+ //# sourceMappingURL=match_error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match_error.js","sourceRoot":"","sources":["../../src/tools/match_error.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ import type { Layer } from '../types.js';
2
+ export interface PrepareNewRecipeArgs {
3
+ project?: string;
4
+ issue?: number;
5
+ title?: string;
6
+ base_image?: string;
7
+ repo_owner?: string;
8
+ layer?: Layer;
9
+ }
10
+ interface RecipeFacetsRow {
11
+ key: string;
12
+ value: {
13
+ language: string;
14
+ symptom: string;
15
+ severity: string;
16
+ tags: string[];
17
+ };
18
+ }
19
+ interface ProjectsRow {
20
+ key: string;
21
+ value: {
22
+ display_name: string;
23
+ tagline: string;
24
+ description: string;
25
+ homepage: string;
26
+ github: string;
27
+ };
28
+ note: string;
29
+ }
30
+ interface PrepareNewRecipeOk {
31
+ ok: true;
32
+ slug: string;
33
+ layer: Layer;
34
+ upstream_issue_url: string;
35
+ scaffold_command: string;
36
+ verify_command: string;
37
+ recipe_facets_row: RecipeFacetsRow;
38
+ projects_row: ProjectsRow;
39
+ commit_subject: string;
40
+ next_steps: string[];
41
+ references: {
42
+ rules: string;
43
+ layer_readme: string;
44
+ selection_policy: string;
45
+ };
46
+ }
47
+ interface PrepareNewRecipeError {
48
+ ok: false;
49
+ error: string;
50
+ }
51
+ export type PrepareNewRecipeResult = PrepareNewRecipeOk | PrepareNewRecipeError;
52
+ export declare function prepareNewRecipe(args: PrepareNewRecipeArgs): Promise<PrepareNewRecipeResult>;
53
+ export declare const PREPARE_NEW_RECIPE_TOOL: {
54
+ readonly name: "prepare_new_recipe";
55
+ readonly description: "Prepare everything an AI agent needs to author a new Vivarium recipe for a given upstream project + issue. SCAFFOLDING HELPER, not an execution engine — returns the exact `mise run recipes:new` and `mise run recipes:verify` commands the agent should run, plus placeholder rows for docs/data/recipe-facets.json and (if the project is new) docs/data/projects.json, plus a commit-subject template and a sequenced next-steps checklist. Validates the slug at call time against the same regex `docs/scripts/generate-recipes-index.ts` uses, so unparseable slugs are rejected before any work begins. Use this immediately after picking an issue from `gh search`; pair with `match_error` / `list_recipes` if you need to confirm no existing recipe already covers the bug.";
56
+ readonly inputSchema: {
57
+ readonly type: "object";
58
+ readonly properties: {
59
+ readonly project: {
60
+ readonly type: "string";
61
+ readonly pattern: "^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)*$";
62
+ readonly description: "Upstream project name in kebab-case (e.g. 'node', 'cpython', 'typescript'). Must not contain trailing digits — those would confuse the slug parser.";
63
+ };
64
+ readonly issue: {
65
+ readonly type: "integer";
66
+ readonly minimum: 1;
67
+ readonly description: "Upstream issue number (positive integer, e.g. 63041 for nodejs/node#63041).";
68
+ };
69
+ readonly title: {
70
+ readonly type: "string";
71
+ readonly description: "One-line bug title (e.g. \"Intl.DateTimeFormat drops month with calendar:'iso8601'\"). Used in the scaffold command and as the recipe README's H1.";
72
+ };
73
+ readonly base_image: {
74
+ readonly type: "string";
75
+ readonly description: "Optional Docker base image for Layer 2 recipes (e.g. 'node:26-slim'). Forwarded to `mise run recipes:new -- ... --base <image>`. Layer 1 / 3 ignore this.";
76
+ };
77
+ readonly repo_owner: {
78
+ readonly type: "string";
79
+ readonly description: "Optional upstream owner/repo override (e.g. 'microsoft/TypeScript'). Defaults to a heuristic per project name: 'node' → 'nodejs/node', 'cpython' → 'python/cpython', etc. Only forward to the scaffold command when it differs from the heuristic.";
80
+ };
81
+ readonly layer: {
82
+ readonly type: "integer";
83
+ readonly enum: readonly [1, 2, 3];
84
+ readonly default: 2;
85
+ readonly description: "Vivarium layer: 1=WASM (in-browser), 2=Docker (default), 3=record-replay (rr). The commit scope, scaffold command, and next-steps tailor to the layer.";
86
+ };
87
+ };
88
+ readonly required: readonly ["project", "issue", "title"];
89
+ };
90
+ };
91
+ export {};
92
+ //# sourceMappingURL=prepare_new_recipe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare_new_recipe.d.ts","sourceRoot":"","sources":["../../src/tools/prepare_new_recipe.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,eAAe;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;CACH;AAED,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,kBAAkB;IAC1B,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,eAAe,CAAC;IACnC,YAAY,EAAE,WAAW,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,UAAU,qBAAqB;IAC7B,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,sBAAsB,GAC9B,kBAAkB,GAClB,qBAAqB,CAAC;AAuC1B,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,sBAAsB,CAAC,CAoHjC;AAED,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4C1B,CAAC"}
@@ -0,0 +1,193 @@
1
+ // prepare_new_recipe — given an upstream project + issue + title,
2
+ // returns everything an AI agent (or human) needs to author a new
3
+ // Vivarium recipe in a single step:
4
+ //
5
+ // - validated slug (rejects values that the recipes-index parser
6
+ // in docs/scripts/generate-recipes-index.ts would not resolve)
7
+ // - the exact `mise run recipes:new -- ...` and
8
+ // `mise run recipes:verify -- ...` commands the agent should run
9
+ // - placeholder rows for docs/data/recipe-facets.json (always) and
10
+ // docs/data/projects.json (only when the project is new), with
11
+ // TODO markers for the human to fill in
12
+ // - a commit-subject template matching the `feat(layer<N>):` /
13
+ // `feat(wasm):` historical scope conventions
14
+ // - a sequenced next-steps checklist
15
+ //
16
+ // SCAFFOLDING HELPER, NOT an execution engine — same pattern as
17
+ // verify_branch_fix. The MCP server returns the commands and
18
+ // metadata; the agent's shell tool actually invokes mise.
19
+ // Same regex as parseSlug() in docs/scripts/generate-recipes-index.ts.
20
+ // Single-sourced as a literal here because the MCP package is published
21
+ // independently and cannot import from docs/.
22
+ const SLUG_REGEX = /^([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*?)-(\d+)$/;
23
+ // Default upstream owner/repo for common projects, mirroring the lookup
24
+ // in docs/scripts/new-recipe.ts. Override via repo_owner.
25
+ const DEFAULT_REPO = {
26
+ node: 'nodejs/node',
27
+ cpython: 'python/cpython',
28
+ typescript: 'microsoft/TypeScript',
29
+ rust: 'rust-lang/rust',
30
+ pandas: 'pandas-dev/pandas',
31
+ numpy: 'numpy/numpy',
32
+ php: 'php/php-src',
33
+ ruby: 'ruby/ruby',
34
+ regex: 'rust-lang/regex',
35
+ };
36
+ // Layer scope conventions: feat(wasm) for Layer 1, feat(layer2) for
37
+ // Layer 2, feat(layer3) for Layer 3 — established by PRs #92/#94/#98/
38
+ // #194 (Layer 2), #180/#189/#192 (Layer 1 = wasm), #106 (Layer 3).
39
+ function commitScopeFor(layer) {
40
+ switch (layer) {
41
+ case 1:
42
+ return 'wasm';
43
+ case 2:
44
+ return 'layer2';
45
+ case 3:
46
+ return 'layer3';
47
+ }
48
+ }
49
+ function defaultRepoFor(project) {
50
+ return DEFAULT_REPO[project] ?? `${project}/${project}`;
51
+ }
52
+ export async function prepareNewRecipe(args) {
53
+ const project = (args.project ?? '').trim();
54
+ const issueNum = args.issue;
55
+ const title = (args.title ?? '').trim();
56
+ const layer = args.layer ?? 2;
57
+ if (!project)
58
+ return { ok: false, error: 'project is required' };
59
+ if (issueNum === undefined || issueNum === null)
60
+ return { ok: false, error: 'issue is required' };
61
+ if (!Number.isInteger(issueNum) || issueNum <= 0)
62
+ return { ok: false, error: `issue must be a positive integer (got ${issueNum})` };
63
+ if (!title)
64
+ return { ok: false, error: 'title is required' };
65
+ if (![1, 2, 3].includes(layer))
66
+ return { ok: false, error: `layer must be 1, 2, or 3 (got ${layer})` };
67
+ const slug = `${project}-${issueNum}`;
68
+ if (!SLUG_REGEX.test(slug)) {
69
+ return {
70
+ ok: false,
71
+ error: `generated slug "${slug}" is not parseable by ` +
72
+ `docs/scripts/generate-recipes-index.ts. project must be ` +
73
+ `kebab-case ([a-z][a-z0-9]*(-[a-z][a-z0-9]*)*) and contain no ` +
74
+ `trailing digits.`,
75
+ };
76
+ }
77
+ const ownerRepo = (args.repo_owner ?? defaultRepoFor(project)).trim();
78
+ const upstreamIssueUrl = `https://github.com/${ownerRepo}/issues/${issueNum}`;
79
+ // Layer 2 has the canonical scaffolder today (mise run recipes:new).
80
+ // Layer 1 / 3 scaffolders do not yet exist — for those, the agent
81
+ // copies from an existing recipe; we still return scaffolding intent.
82
+ const baseFlag = args.base_image
83
+ ? ` --base "${args.base_image}"`
84
+ : '';
85
+ const repoFlag = args.repo_owner && args.repo_owner !== defaultRepoFor(project)
86
+ ? ` --repo ${args.repo_owner}`
87
+ : '';
88
+ const scaffoldCommand = layer === 2
89
+ ? `mise run recipes:new -- ${project} ${issueNum} "${title}"${baseFlag}${repoFlag}`
90
+ : `# No scaffolder for Layer ${layer} yet — copy from an existing src/layer${layer}_*/ recipe and adapt.`;
91
+ const verifyCommand = layer === 2
92
+ ? `mise run recipes:verify -- ${slug}`
93
+ : `# No verifier for Layer ${layer} yet — see src/layer${layer}_*/README.md for per-layer validation.`;
94
+ const recipeFacetsRow = {
95
+ key: slug,
96
+ value: {
97
+ language: 'TODO-fill-in',
98
+ symptom: 'TODO-fill-in',
99
+ severity: 'TODO-fill-in',
100
+ tags: [],
101
+ },
102
+ };
103
+ const projectsRow = {
104
+ key: project,
105
+ value: {
106
+ display_name: project,
107
+ tagline: 'TODO-fill-in',
108
+ description: 'TODO-fill-in',
109
+ homepage: '',
110
+ github: `https://github.com/${ownerRepo}`,
111
+ },
112
+ note: `Only add this row to docs/data/projects.json if "${project}" is a new project (i.e. no existing recipe under src/layer*_*/${project}-*/).`,
113
+ };
114
+ const commitSubject = `feat(${commitScopeFor(layer)}): ${slug} reproduction (...)`;
115
+ const nextSteps = layer === 2
116
+ ? [
117
+ `Run: ${scaffoldCommand}`,
118
+ `Edit src/layer2_docker/${slug}/Dockerfile — set the real base image.`,
119
+ `Edit src/layer2_docker/${slug}/repro.sh — replace the TODO stub with the real probe.`,
120
+ `Edit src/layer2_docker/${slug}/README.md — fill in bug description, verdict contract, references.`,
121
+ `Edit src/layer2_docker/${slug}/index.html — fill in the lede.`,
122
+ `Add the recipe_facets_row to docs/data/recipe-facets.json with real values.`,
123
+ `If "${project}" is a new project, add the projects_row to docs/data/projects.json.`,
124
+ `Run: ${verifyCommand} (this regenerates indices, runs lint, runs the docker build/run, and the rspress build).`,
125
+ `Commit with subject: ${commitSubject}`,
126
+ `Submit the PR.`,
127
+ ]
128
+ : [
129
+ `Copy from an existing recipe under src/layer${layer}_*/ — the canonical authoring reference.`,
130
+ `Add the recipe_facets_row to docs/data/recipe-facets.json with real values.`,
131
+ `If "${project}" is a new project, add the projects_row to docs/data/projects.json.`,
132
+ `Run: cd docs && mise exec -- bun run generate-index && mise exec -- bun run generate-project-pages`,
133
+ `Validate locally per the layer's README + the .claude/rules/recipe-authoring.md checklist.`,
134
+ `Commit with subject: ${commitSubject}`,
135
+ `Submit the PR.`,
136
+ ];
137
+ return {
138
+ ok: true,
139
+ slug,
140
+ layer,
141
+ upstream_issue_url: upstreamIssueUrl,
142
+ scaffold_command: scaffoldCommand,
143
+ verify_command: verifyCommand,
144
+ recipe_facets_row: recipeFacetsRow,
145
+ projects_row: projectsRow,
146
+ commit_subject: commitSubject,
147
+ next_steps: nextSteps,
148
+ references: {
149
+ rules: 'https://github.com/aletheia-works/vivarium/blob/main/.claude/rules/recipe-authoring.md',
150
+ layer_readme: `https://github.com/aletheia-works/vivarium/blob/main/src/layer${layer}_${layer === 1 ? 'wasm' : layer === 2 ? 'docker' : 'thirdway'}/README.md`,
151
+ selection_policy: 'https://github.com/aletheia-works/vivarium (issue selection policy is in _context/strategy/issue_selection_policy.md, gitignored)',
152
+ },
153
+ };
154
+ }
155
+ export const PREPARE_NEW_RECIPE_TOOL = {
156
+ name: 'prepare_new_recipe',
157
+ description: "Prepare everything an AI agent needs to author a new Vivarium recipe for a given upstream project + issue. SCAFFOLDING HELPER, not an execution engine — returns the exact `mise run recipes:new` and `mise run recipes:verify` commands the agent should run, plus placeholder rows for docs/data/recipe-facets.json and (if the project is new) docs/data/projects.json, plus a commit-subject template and a sequenced next-steps checklist. Validates the slug at call time against the same regex `docs/scripts/generate-recipes-index.ts` uses, so unparseable slugs are rejected before any work begins. Use this immediately after picking an issue from `gh search`; pair with `match_error` / `list_recipes` if you need to confirm no existing recipe already covers the bug.",
158
+ inputSchema: {
159
+ type: 'object',
160
+ properties: {
161
+ project: {
162
+ type: 'string',
163
+ pattern: '^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)*$',
164
+ description: "Upstream project name in kebab-case (e.g. 'node', 'cpython', 'typescript'). Must not contain trailing digits — those would confuse the slug parser.",
165
+ },
166
+ issue: {
167
+ type: 'integer',
168
+ minimum: 1,
169
+ description: 'Upstream issue number (positive integer, e.g. 63041 for nodejs/node#63041).',
170
+ },
171
+ title: {
172
+ type: 'string',
173
+ description: "One-line bug title (e.g. \"Intl.DateTimeFormat drops month with calendar:'iso8601'\"). Used in the scaffold command and as the recipe README's H1.",
174
+ },
175
+ base_image: {
176
+ type: 'string',
177
+ description: "Optional Docker base image for Layer 2 recipes (e.g. 'node:26-slim'). Forwarded to `mise run recipes:new -- ... --base <image>`. Layer 1 / 3 ignore this.",
178
+ },
179
+ repo_owner: {
180
+ type: 'string',
181
+ description: "Optional upstream owner/repo override (e.g. 'microsoft/TypeScript'). Defaults to a heuristic per project name: 'node' → 'nodejs/node', 'cpython' → 'python/cpython', etc. Only forward to the scaffold command when it differs from the heuristic.",
182
+ },
183
+ layer: {
184
+ type: 'integer',
185
+ enum: [1, 2, 3],
186
+ default: 2,
187
+ description: 'Vivarium layer: 1=WASM (in-browser), 2=Docker (default), 3=record-replay (rr). The commit scope, scaffold command, and next-steps tailor to the layer.',
188
+ },
189
+ },
190
+ required: ['project', 'issue', 'title'],
191
+ },
192
+ };
193
+ //# sourceMappingURL=prepare_new_recipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare_new_recipe.js","sourceRoot":"","sources":["../../src/tools/prepare_new_recipe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { Layer } from '../types.js';
2
+ export interface VerifyBranchFixArgs {
3
+ slug: string;
4
+ fix_url?: string;
5
+ fix_source?: string;
6
+ }
7
+ interface VerifyBranchFixOk {
8
+ ok: true;
9
+ slug: string;
10
+ layer: Layer;
11
+ path: 'A' | 'B';
12
+ page_url: string;
13
+ compare_url: string;
14
+ gh_command?: string;
15
+ instructions: string;
16
+ notes: string[];
17
+ }
18
+ interface VerifyBranchFixError {
19
+ ok: false;
20
+ error: string;
21
+ }
22
+ export type VerifyBranchFixResult = VerifyBranchFixOk | VerifyBranchFixError;
23
+ export declare function verifyBranchFix(args: VerifyBranchFixArgs): Promise<VerifyBranchFixResult>;
24
+ export declare const VERIFY_BRANCH_FIX_TOOL: {
25
+ readonly name: "verify_branch_fix";
26
+ readonly description: "Generate a deep-link URL and instructions for verifying a candidate branch-fix against a Vivarium recipe. SCAFFOLDING HELPER, not an execution engine — actual reproduction runs in the visitor's browser (Layer 1) or in GitHub Actions (Layer 2/3). Layer dispatch is automatic from the recipe's catalogue layer. Layer 1 returns Path A: a recipe-page URL with the fix pre-loaded via `?fix_url=` or `?fix=<base64url>`. Layer 2/3 returns Path B: a `/repro/compare` URL plus the `gh workflow run branch-fix-verdict.yml` command the contributor runs to capture a verdict against their pushed Docker image. Use after `match_error` or `list_recipes` has narrowed to a specific slug. Pair with the D-5 walkthrough page (https://aletheia-works.github.io/vivarium/guide/compare-branch-fix) for the full AI-slop verification loop.";
27
+ readonly inputSchema: {
28
+ readonly type: "object";
29
+ readonly properties: {
30
+ readonly slug: {
31
+ readonly type: "string";
32
+ readonly pattern: "^[a-z0-9]+(-[a-z0-9]+)*$";
33
+ readonly description: "Kebab-case recipe slug (e.g. 'php-12167', 'bash-local-shadows-exit'). Same convention as Manifest v1's `slug`.";
34
+ };
35
+ readonly fix_url: {
36
+ readonly type: "string";
37
+ readonly description: "Public URL of the candidate fix source (raw.githubusercontent.com / gist.githubusercontent.com). Layer 1 only — embedded in the returned compare_url as `?fix_url=`. Mutually exclusive with fix_source.";
38
+ };
39
+ readonly fix_source: {
40
+ readonly type: "string";
41
+ readonly maxLength: number;
42
+ readonly description: "Inline candidate fix source (max 4096 bytes). Layer 1 only — embedded in the returned compare_url as `?fix=<base64url>`. Mutually exclusive with fix_url. Long fixes should use fix_url instead.";
43
+ };
44
+ };
45
+ readonly required: readonly ["slug"];
46
+ };
47
+ };
48
+ export {};
49
+ //# sourceMappingURL=verify_branch_fix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify_branch_fix.d.ts","sourceRoot":"","sources":["../../src/tools/verify_branch_fix.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,iBAAiB;IACzB,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,GAAG,GAAG,GAAG,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,UAAU,oBAAoB;IAC5B,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AA6H7E,wBAAsB,eAAe,CACnC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAyFhC;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;CA2BzB,CAAC"}