nodebench-mcp 1.4.1 → 2.1.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 (41) hide show
  1. package/NODEBENCH_AGENTS.md +154 -2
  2. package/README.md +214 -215
  3. package/dist/__tests__/comparativeBench.test.d.ts +1 -0
  4. package/dist/__tests__/comparativeBench.test.js +722 -0
  5. package/dist/__tests__/comparativeBench.test.js.map +1 -0
  6. package/dist/__tests__/evalHarness.test.js +24 -2
  7. package/dist/__tests__/evalHarness.test.js.map +1 -1
  8. package/dist/__tests__/gaiaCapabilityEval.test.d.ts +14 -0
  9. package/dist/__tests__/gaiaCapabilityEval.test.js +420 -0
  10. package/dist/__tests__/gaiaCapabilityEval.test.js.map +1 -0
  11. package/dist/__tests__/gaiaCapabilityFilesEval.test.d.ts +15 -0
  12. package/dist/__tests__/gaiaCapabilityFilesEval.test.js +303 -0
  13. package/dist/__tests__/gaiaCapabilityFilesEval.test.js.map +1 -0
  14. package/dist/__tests__/openDatasetParallelEvalGaia.test.d.ts +7 -0
  15. package/dist/__tests__/openDatasetParallelEvalGaia.test.js +279 -0
  16. package/dist/__tests__/openDatasetParallelEvalGaia.test.js.map +1 -0
  17. package/dist/__tests__/openDatasetPerfComparison.test.d.ts +10 -0
  18. package/dist/__tests__/openDatasetPerfComparison.test.js +318 -0
  19. package/dist/__tests__/openDatasetPerfComparison.test.js.map +1 -0
  20. package/dist/__tests__/tools.test.js +155 -7
  21. package/dist/__tests__/tools.test.js.map +1 -1
  22. package/dist/__tests__/toolsetGatingEval.test.d.ts +1 -0
  23. package/dist/__tests__/toolsetGatingEval.test.js +1031 -0
  24. package/dist/__tests__/toolsetGatingEval.test.js.map +1 -0
  25. package/dist/db.js +56 -0
  26. package/dist/db.js.map +1 -1
  27. package/dist/index.js +462 -28
  28. package/dist/index.js.map +1 -1
  29. package/dist/tools/localFileTools.d.ts +15 -0
  30. package/dist/tools/localFileTools.js +386 -0
  31. package/dist/tools/localFileTools.js.map +1 -0
  32. package/dist/tools/metaTools.js +170 -3
  33. package/dist/tools/metaTools.js.map +1 -1
  34. package/dist/tools/parallelAgentTools.d.ts +18 -0
  35. package/dist/tools/parallelAgentTools.js +1272 -0
  36. package/dist/tools/parallelAgentTools.js.map +1 -0
  37. package/dist/tools/selfEvalTools.js +240 -10
  38. package/dist/tools/selfEvalTools.js.map +1 -1
  39. package/dist/tools/webTools.js +171 -37
  40. package/dist/tools/webTools.js.map +1 -1
  41. package/package.json +26 -8
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Local file parsing tools (deterministic).
3
+ *
4
+ * These tools intentionally avoid network access and operate only on local files.
5
+ * Primary use cases:
6
+ * - GAIA file-backed tasks (PDF / XLSX / CSV attachments)
7
+ * - Internal agent workflows that need structured parsing without "LLM OCR"
8
+ *
9
+ * Optional deps (installed by default when available):
10
+ * - xlsx: XLSX parsing
11
+ * - papaparse: CSV parsing
12
+ * - pdf-parse: PDF text extraction (page-aware)
13
+ */
14
+ import { readFile } from "node:fs/promises";
15
+ import { existsSync } from "node:fs";
16
+ import os from "node:os";
17
+ import path from "node:path";
18
+ function expandTilde(p) {
19
+ if (!p)
20
+ return p;
21
+ if (p === "~")
22
+ return os.homedir();
23
+ if (p.startsWith("~/") || p.startsWith("~\\"))
24
+ return path.join(os.homedir(), p.slice(2));
25
+ return p;
26
+ }
27
+ function resolveLocalPath(inputPath) {
28
+ const expanded = expandTilde(String(inputPath ?? "").trim());
29
+ if (!expanded)
30
+ throw new Error("path is required");
31
+ return path.isAbsolute(expanded) ? expanded : path.resolve(process.cwd(), expanded);
32
+ }
33
+ function clampInt(value, fallback, min, max) {
34
+ const n = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
35
+ if (!Number.isFinite(n))
36
+ return fallback;
37
+ return Math.max(min, Math.min(max, n));
38
+ }
39
+ function truncateCell(value, maxChars) {
40
+ if (value === null || value === undefined)
41
+ return null;
42
+ if (typeof value === "number")
43
+ return value;
44
+ if (typeof value === "boolean")
45
+ return value;
46
+ if (value instanceof Date)
47
+ return value.toISOString();
48
+ const s = String(value);
49
+ if (s.length <= maxChars)
50
+ return s;
51
+ return s.slice(0, maxChars) + "...";
52
+ }
53
+ async function getXlsx() {
54
+ try {
55
+ const mod = await import("xlsx");
56
+ return mod.default ?? mod;
57
+ }
58
+ catch (err) {
59
+ throw new Error("Missing optional dependency: xlsx. Install it (or run npm install in packages/mcp-local) to use XLSX parsing.");
60
+ }
61
+ }
62
+ async function getPapaParse() {
63
+ try {
64
+ const mod = await import("papaparse");
65
+ return mod.default ?? mod;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ async function getPdfParseModule() {
72
+ try {
73
+ return await import("pdf-parse");
74
+ }
75
+ catch {
76
+ throw new Error("Missing optional dependency: pdf-parse. Install it (or run npm install in packages/mcp-local) to use PDF parsing.");
77
+ }
78
+ }
79
+ export const localFileTools = [
80
+ {
81
+ name: "read_csv_file",
82
+ description: "Read a local CSV file and return a bounded table preview (headers + rows). Deterministic, no network.",
83
+ inputSchema: {
84
+ type: "object",
85
+ properties: {
86
+ path: {
87
+ type: "string",
88
+ description: "Path to a local .csv file (absolute or relative to current working directory).",
89
+ },
90
+ hasHeader: {
91
+ type: "boolean",
92
+ description: "If true, treats the first row as headers.",
93
+ default: true,
94
+ },
95
+ delimiter: {
96
+ type: "string",
97
+ description: "Optional delimiter override, e.g. ',' or '\\t'. If omitted, parser default is used.",
98
+ },
99
+ encoding: {
100
+ type: "string",
101
+ description: "File encoding (default: utf8).",
102
+ default: "utf8",
103
+ },
104
+ maxRows: {
105
+ type: "number",
106
+ description: "Maximum number of data rows to return (excluding header).",
107
+ default: 200,
108
+ },
109
+ maxCols: {
110
+ type: "number",
111
+ description: "Maximum number of columns to return.",
112
+ default: 50,
113
+ },
114
+ maxCellChars: {
115
+ type: "number",
116
+ description: "Maximum characters to return per cell (long cells are truncated).",
117
+ default: 2000,
118
+ },
119
+ },
120
+ required: ["path"],
121
+ },
122
+ handler: async (args) => {
123
+ const filePath = resolveLocalPath(args?.path);
124
+ if (!existsSync(filePath))
125
+ throw new Error(`File not found: ${filePath}`);
126
+ const encoding = String(args?.encoding ?? "utf8");
127
+ const text = await readFile(filePath, { encoding });
128
+ const hasHeader = args?.hasHeader !== false;
129
+ const maxRows = clampInt(args?.maxRows, 200, 1, 5000);
130
+ const maxCols = clampInt(args?.maxCols, 50, 1, 500);
131
+ const maxCellChars = clampInt(args?.maxCellChars, 2000, 20, 20000);
132
+ const delimiter = typeof args?.delimiter === "string" ? args.delimiter : undefined;
133
+ const papa = await getPapaParse();
134
+ let rows = [];
135
+ let parseErrors = [];
136
+ if (papa?.parse) {
137
+ const result = papa.parse(text, {
138
+ ...(delimiter ? { delimiter } : {}),
139
+ skipEmptyLines: true,
140
+ dynamicTyping: false,
141
+ });
142
+ rows = Array.isArray(result?.data) ? result.data : [];
143
+ parseErrors = Array.isArray(result?.errors) ? result.errors : [];
144
+ }
145
+ else {
146
+ // Minimal fallback parser: split by newlines and commas (no quote handling).
147
+ const lines = String(text).split(/\r?\n/).filter((l) => l.trim().length > 0);
148
+ rows = lines.map((l) => l.split(delimiter ?? ","));
149
+ }
150
+ // Normalize to array-of-arrays and bound.
151
+ const normalized = rows
152
+ .filter((r) => Array.isArray(r))
153
+ .map((r) => r.slice(0, maxCols).map((c) => truncateCell(c, maxCellChars)));
154
+ const headerRow = hasHeader ? normalized[0] : undefined;
155
+ const dataRows = hasHeader ? normalized.slice(1) : normalized;
156
+ const limitedRows = dataRows.slice(0, maxRows);
157
+ const colCount = Math.max(headerRow ? headerRow.length : 0, ...limitedRows.map((r) => r.length));
158
+ const truncated = dataRows.length > limitedRows.length ||
159
+ normalized.some((r) => r.length > maxCols) ||
160
+ colCount > maxCols;
161
+ const headers = headerRow
162
+ ? headerRow.map((h) => String(h ?? "").trim())
163
+ : Array.from({ length: colCount }, (_, i) => `col_${i + 1}`);
164
+ return {
165
+ path: filePath,
166
+ encoding,
167
+ hasHeader,
168
+ delimiter: delimiter ?? null,
169
+ parseErrors: parseErrors.length > 0 ? parseErrors.slice(0, 5) : [],
170
+ rowCount: dataRows.length,
171
+ returnedRows: limitedRows.length,
172
+ colCount,
173
+ truncated,
174
+ headers: headers.slice(0, maxCols),
175
+ rows: limitedRows.map((r) => r.slice(0, maxCols)),
176
+ };
177
+ },
178
+ },
179
+ {
180
+ name: "read_xlsx_file",
181
+ description: "Read a local XLSX workbook and return a bounded sheet preview (headers + rows). Deterministic, no network.",
182
+ inputSchema: {
183
+ type: "object",
184
+ properties: {
185
+ path: {
186
+ type: "string",
187
+ description: "Path to a local .xlsx file (absolute or relative to current working directory).",
188
+ },
189
+ sheetName: {
190
+ type: "string",
191
+ description: "Sheet to read. If omitted, the first sheet is used.",
192
+ },
193
+ headerRow: {
194
+ type: "number",
195
+ description: "1-based header row index. Use 0 for no header row.",
196
+ default: 1,
197
+ },
198
+ rangeA1: {
199
+ type: "string",
200
+ description: "Optional A1 range (e.g. A1:D50) to limit parsing to a specific region.",
201
+ },
202
+ maxRows: {
203
+ type: "number",
204
+ description: "Maximum number of data rows to return (excluding header).",
205
+ default: 200,
206
+ },
207
+ maxCols: {
208
+ type: "number",
209
+ description: "Maximum number of columns to return.",
210
+ default: 50,
211
+ },
212
+ maxCellChars: {
213
+ type: "number",
214
+ description: "Maximum characters to return per cell (long cells are truncated).",
215
+ default: 2000,
216
+ },
217
+ },
218
+ required: ["path"],
219
+ },
220
+ handler: async (args) => {
221
+ const filePath = resolveLocalPath(args?.path);
222
+ if (!existsSync(filePath))
223
+ throw new Error(`File not found: ${filePath}`);
224
+ const XLSX = await getXlsx();
225
+ const wb = XLSX.readFile(filePath, {
226
+ cellDates: true,
227
+ dense: true,
228
+ });
229
+ const sheets = Array.isArray(wb?.SheetNames) ? wb.SheetNames : [];
230
+ if (sheets.length === 0)
231
+ throw new Error(`No sheets found in workbook: ${filePath}`);
232
+ const requestedSheet = typeof args?.sheetName === "string" ? args.sheetName.trim() : "";
233
+ const sheetName = requestedSheet || sheets[0];
234
+ const sheet = wb.Sheets?.[sheetName];
235
+ if (!sheet) {
236
+ throw new Error(`Sheet not found: "${sheetName}". Available sheets: ${sheets.join(", ")}`);
237
+ }
238
+ const headerRow = clampInt(args?.headerRow, 1, 0, 1000);
239
+ const maxRows = clampInt(args?.maxRows, 200, 1, 5000);
240
+ const maxCols = clampInt(args?.maxCols, 50, 1, 500);
241
+ const maxCellChars = clampInt(args?.maxCellChars, 2000, 20, 20000);
242
+ const rangeA1 = typeof args?.rangeA1 === "string" ? args.rangeA1.trim() : "";
243
+ const table = XLSX.utils.sheet_to_json(sheet, {
244
+ header: 1,
245
+ blankrows: false,
246
+ defval: "",
247
+ ...(rangeA1 ? { range: rangeA1 } : {}),
248
+ });
249
+ const normalized = table
250
+ .filter((r) => Array.isArray(r))
251
+ .map((r) => r.slice(0, maxCols).map((c) => truncateCell(c, maxCellChars)));
252
+ const headerIdx = headerRow > 0 ? headerRow - 1 : -1;
253
+ const header = headerIdx >= 0 ? normalized[headerIdx] : undefined;
254
+ const dataRows = headerIdx >= 0 ? normalized.slice(headerIdx + 1) : normalized;
255
+ const limitedRows = dataRows.slice(0, maxRows);
256
+ const colCount = Math.max(header ? header.length : 0, ...limitedRows.map((r) => r.length));
257
+ const truncated = dataRows.length > limitedRows.length ||
258
+ normalized.some((r) => r.length > maxCols) ||
259
+ colCount > maxCols;
260
+ const headers = header
261
+ ? header.map((h) => String(h ?? "").trim())
262
+ : Array.from({ length: colCount }, (_, i) => `col_${i + 1}`);
263
+ return {
264
+ path: filePath,
265
+ sheets,
266
+ sheetName,
267
+ headerRow,
268
+ rangeA1: rangeA1 || null,
269
+ rowCount: dataRows.length,
270
+ returnedRows: limitedRows.length,
271
+ colCount,
272
+ truncated,
273
+ headers: headers.slice(0, maxCols),
274
+ rows: limitedRows.map((r) => r.slice(0, maxCols)),
275
+ };
276
+ },
277
+ },
278
+ {
279
+ name: "read_pdf_text",
280
+ description: "Extract text from a local PDF file for selected pages. Returns bounded text with page markers. Deterministic, no network.",
281
+ inputSchema: {
282
+ type: "object",
283
+ properties: {
284
+ path: {
285
+ type: "string",
286
+ description: "Path to a local .pdf file (absolute or relative to current working directory).",
287
+ },
288
+ pageStart: {
289
+ type: "number",
290
+ description: "1-based start page (inclusive). Defaults to 1.",
291
+ default: 1,
292
+ },
293
+ pageEnd: {
294
+ type: "number",
295
+ description: "1-based end page (inclusive). Defaults to 3.",
296
+ default: 3,
297
+ },
298
+ pageNumbers: {
299
+ type: "array",
300
+ description: "Optional explicit list of 1-based pages to extract (overrides pageStart/pageEnd).",
301
+ items: { type: "number" },
302
+ },
303
+ maxChars: {
304
+ type: "number",
305
+ description: "Maximum characters to return across all extracted pages (text is truncated).",
306
+ default: 12000,
307
+ },
308
+ },
309
+ required: ["path"],
310
+ },
311
+ handler: async (args) => {
312
+ const filePath = resolveLocalPath(args?.path);
313
+ if (!existsSync(filePath))
314
+ throw new Error(`File not found: ${filePath}`);
315
+ const maxChars = clampInt(args?.maxChars, 12000, 1000, 200000);
316
+ const pageNumbersRaw = Array.isArray(args?.pageNumbers) ? args.pageNumbers : null;
317
+ const explicitPages = pageNumbersRaw
318
+ ? pageNumbersRaw
319
+ .map((n) => clampInt(n, 0, 0, 100000))
320
+ .filter((n) => n > 0)
321
+ : null;
322
+ const pageStart = clampInt(args?.pageStart, 1, 1, 100000);
323
+ const pageEnd = clampInt(args?.pageEnd, 3, 1, 100000);
324
+ const mod = await getPdfParseModule();
325
+ const PDFParse = mod?.PDFParse;
326
+ if (typeof PDFParse !== "function") {
327
+ throw new Error("pdf-parse module missing PDFParse export (unsupported version)");
328
+ }
329
+ const buffer = await readFile(filePath);
330
+ const parser = new PDFParse({ data: buffer });
331
+ let numPages = 0;
332
+ let text = "";
333
+ let extractedPages = [];
334
+ try {
335
+ const parseParams = {
336
+ // Prefer consistent structure; we add our own page markers below.
337
+ lineEnforce: true,
338
+ pageJoiner: "",
339
+ parseHyperlinks: false,
340
+ };
341
+ if (explicitPages && explicitPages.length > 0) {
342
+ parseParams.partial = explicitPages;
343
+ }
344
+ else {
345
+ const start = Math.min(pageStart, pageEnd);
346
+ const end = Math.max(pageStart, pageEnd);
347
+ parseParams.first = start;
348
+ parseParams.last = end;
349
+ }
350
+ const result = await parser.getText(parseParams);
351
+ numPages = Number(result?.total ?? 0);
352
+ const pages = Array.isArray(result?.pages) ? result.pages : [];
353
+ extractedPages = pages
354
+ .map((p) => Number(p?.num ?? 0))
355
+ .filter((n) => Number.isFinite(n) && n > 0);
356
+ text = pages
357
+ .map((p) => `\n\n[PAGE ${Number(p?.num ?? 0)}]\n${String(p?.text ?? "").trim()}\n`)
358
+ .join("")
359
+ .trim();
360
+ }
361
+ finally {
362
+ try {
363
+ await parser.destroy();
364
+ }
365
+ catch {
366
+ // ignore
367
+ }
368
+ }
369
+ let truncated = false;
370
+ if (text.length > maxChars) {
371
+ text = text.slice(0, maxChars);
372
+ truncated = true;
373
+ }
374
+ const pagesIncluded = extractedPages;
375
+ return {
376
+ path: filePath,
377
+ numPages,
378
+ pagesIncluded,
379
+ maxChars,
380
+ truncated,
381
+ text,
382
+ };
383
+ },
384
+ },
385
+ ];
386
+ //# sourceMappingURL=localFileTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localFileTools.js","sourceRoot":"","sources":["../../src/tools/localFileTools.ts"],"names":[],"mappings":"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"}
@@ -723,9 +723,120 @@ const METHODOLOGY_CONTENT = {
723
723
  },
724
724
  composesWith: "Use alongside agent_bootstrap for complete autonomous infrastructure setup. Feeds into triple_verify for validation.",
725
725
  },
726
+ parallel_agent_teams: {
727
+ title: "Parallel Agent Teams — Multi-Agent Coordination",
728
+ description: 'Based on Anthropic\'s "Building a C Compiler with Parallel Claudes" (Feb 2026). Patterns for running multiple AI agents in parallel on a shared codebase: task locking to prevent duplicate work, role specialization, context window budget management, oracle-based testing, and progress tracking for fresh agent sessions. Reference: https://www.anthropic.com/engineering/building-c-compiler',
729
+ steps: [
730
+ {
731
+ step: 1,
732
+ name: "Orient — Check Parallel Status",
733
+ description: "Every new agent session starts by checking what other agents are doing. Avoid duplicate work. Review blocked tasks that need fresh eyes.",
734
+ tools: ["get_parallel_status", "list_agent_tasks"],
735
+ action: "Call get_parallel_status to see active tasks, roles, failed oracle tests, and context budgets. Then call list_agent_tasks to see what's claimed.",
736
+ },
737
+ {
738
+ step: 2,
739
+ name: "Assign Role — Specialize",
740
+ description: "Assign yourself a role. Specialization enables parallelism: implementer, dedup_reviewer, performance_optimizer, documentation_maintainer, code_quality_critic, test_writer, security_auditor.",
741
+ tools: ["assign_agent_role", "get_agent_role"],
742
+ action: 'Call assign_agent_role({ role: "implementer", focusArea: "auth module" }). Each parallel agent should have a different role or focus area.',
743
+ },
744
+ {
745
+ step: 3,
746
+ name: "Claim Task — Lock Before Working",
747
+ description: "Claim a task lock before starting work. This prevents two agents from solving the same problem. If another agent already claimed the task, pick a different one.",
748
+ tools: ["claim_agent_task"],
749
+ action: 'Call claim_agent_task({ taskKey: "fix_auth_middleware", description: "Fix JWT validation" }). If conflict, pick another task.',
750
+ },
751
+ {
752
+ step: 4,
753
+ name: "Work — Monitor Context Budget",
754
+ description: "Work on the task. Track context window usage to prevent pollution. Pre-compute summaries instead of dumping raw output. Log errors with ERROR prefix on same line for easy grep.",
755
+ tools: ["log_context_budget"],
756
+ action: "After reading large files or receiving large test output, call log_context_budget to track usage. Heed warnings about approaching limits.",
757
+ },
758
+ {
759
+ step: 5,
760
+ name: "Validate — Oracle Comparison",
761
+ description: "Compare your output against a known-good reference (oracle). The oracle pattern enables parallel debugging: each failing comparison can be assigned to a different agent.",
762
+ tools: ["run_oracle_comparison"],
763
+ action: 'Call run_oracle_comparison({ testLabel: "api_output", actualOutput: "...", expectedOutput: "...", oracleSource: "production_v2" }). Fix differences before committing.',
764
+ },
765
+ {
766
+ step: 6,
767
+ name: "Release — Handoff to Next Agent",
768
+ description: "Release the task lock with a progress note. If blocked, mark as blocked so another agent can pick it up with context about what was tried.",
769
+ tools: ["release_agent_task"],
770
+ action: 'Call release_agent_task({ taskKey: "fix_auth_middleware", status: "completed", progressNote: "Fixed JWT validation, added tests" }).',
771
+ },
772
+ ],
773
+ keyPatterns: {
774
+ task_locking: "File-based locks in Anthropic's implementation; SQLite-based locks in NodeBench. Prevents duplicate work when agents run in parallel.",
775
+ role_specialization: "Different agents for different concerns: one implements features, one deduplicates code, one optimizes performance, one maintains docs, one reviews code quality.",
776
+ context_pollution_prevention: "Test harnesses should NOT print thousands of useless bytes. Pre-compute summaries, use --fast sampling for large suites, log details to files.",
777
+ time_blindness_mitigation: "Agents can't tell time. Print incremental progress infrequently. Include --fast option for 1-10% random sample of tests.",
778
+ oracle_testing: "Use a known-good reference as an oracle. Compare outputs to identify which specific components are broken. Enables parallel debugging.",
779
+ progress_files: "Maintain running docs of status, failed approaches, and remaining tasks. Fresh agent sessions read these to orient themselves.",
780
+ bootstrap_external_repos: "When connected to another project that lacks parallel agent capabilities, use bootstrap_parallel_agents to auto-detect gaps and scaffold infrastructure. The tool scans 7 categories (task coordination, roles, oracle, context budget, progress files, AGENTS.md parallel section, git worktrees) and generates ready-to-use files. Follow the AI Flywheel closed loop: detect → scaffold → verify → fix → document.",
781
+ },
782
+ claudeCodeNativePath: {
783
+ title: "Claude Code Native Parallel Subagents",
784
+ description: "If you are using Claude Code (Anthropic's CLI), you already have parallel subagent support via the Task tool. NodeBench MCP adds coordination, tracking, and impact measurement on top of Claude Code's native parallelism.",
785
+ howItWorks: [
786
+ "1. The main Claude Code session is the COORDINATOR — it breaks work into independent tasks",
787
+ "2. Each Task tool call spawns a SUBAGENT — a separate Claude instance with its own context",
788
+ "3. Each subagent has access to the same NodeBench MCP tools (shared SQLite database)",
789
+ "4. Subagents use claim_agent_task/release_agent_task to coordinate without conflicts",
790
+ "5. The coordinator uses get_parallel_status to monitor progress across all subagents",
791
+ ],
792
+ exampleWorkflow: [
793
+ "COORDINATOR: Break work into 3 independent tasks",
794
+ "COORDINATOR: For each task, spawn a Task tool subagent with prompt:",
795
+ ' "You have access to NodeBench MCP. First call claim_agent_task({ taskKey: \'fix_auth\' }), then do the work, then call release_agent_task with a progress note."',
796
+ "COORDINATOR: Call get_parallel_status to see all subagent progress",
797
+ "COORDINATOR: When all subagents complete, aggregate results and run quality gate",
798
+ ],
799
+ impact: "Prevents duplicate work across subagents, captures per-task learnings, enables oracle-based validation, tracks context budget per subagent",
800
+ },
801
+ whenToUseParallelTools: {
802
+ description: "Parallel agent tools are NOT always needed. Use them ONLY when the situation calls for coordination.",
803
+ useWhen: [
804
+ "You are running 2+ agent sessions (Claude Code subagents, worktrees, or separate terminals)",
805
+ "You need to prevent two agents from working on the same thing",
806
+ "You want oracle-based testing to split failures into independent work items",
807
+ "You are bootstrapping parallel agent infrastructure for an external project",
808
+ ],
809
+ doNotUseWhen: [
810
+ "You are a single agent working sequentially — standard verification/eval tools are sufficient",
811
+ "The task is simple enough for one agent to handle end-to-end",
812
+ "You are not in a multi-agent or multi-session context",
813
+ ],
814
+ },
815
+ impactPerStep: {
816
+ orient: "IMPACT: Prevents wasted time re-doing work another agent already started or completed",
817
+ assign_role: "IMPACT: Specialization enables parallelism — agents don't step on each other's toes",
818
+ claim_task: "IMPACT: Zero duplicate work — each task is owned by exactly one agent",
819
+ monitor_budget: "IMPACT: Prevents context window exhaustion that forces expensive session restarts",
820
+ oracle_validate: "IMPACT: Catches regressions by comparing against known-good reference — each failure is an independent debuggable work item",
821
+ release_handoff: "IMPACT: Progress notes ensure the next agent (or next session) doesn't restart from scratch",
822
+ },
823
+ bootstrapForExternalRepos: {
824
+ description: "When nodebench-mcp detects that a target project repo does not have parallel agent infrastructure, it can automatically bootstrap it using the AI Flywheel closed loop.",
825
+ steps: [
826
+ "1. DETECT: Call bootstrap_parallel_agents({ projectRoot: '/path/to/their/repo', dryRun: true }) — scans 7 categories and returns gap report",
827
+ "2. SCAFFOLD: Call bootstrap_parallel_agents({ projectRoot: '...', dryRun: false, techStack: 'TypeScript/React' }) — creates .parallel-agents/ dir, progress.md, roles.json, lock dirs, oracle dirs",
828
+ "3. AGENTS.MD: Call generate_parallel_agents_md({ techStack: '...', projectName: '...', maxAgents: 4 }) — generates portable AGENTS.md section, paste into their repo",
829
+ "4. VERIFY: Run the 6-step flywheel plan returned by bootstrap_parallel_agents — static analysis, happy path, conflict test, oracle test, gap re-scan, document",
830
+ "5. FIX: If any flywheel step fails, fix and re-verify from step 1",
831
+ "6. DOCUMENT: Call record_learning with patterns discovered during bootstrap",
832
+ ],
833
+ },
834
+ authoritativeSource: "https://www.anthropic.com/engineering/building-c-compiler",
835
+ composesWith: "Works with verification cycles (each agent runs its own), eval runs (aggregate across agents), and learnings (shared knowledge base). Task claims compose with the mandatory flywheel. bootstrap_parallel_agents works on ANY project directory — not just nodebench.",
836
+ },
726
837
  self_reinforced_learning: {
727
838
  title: "Self-Reinforced Learning Loop",
728
- description: "Continuous self-improvement cycle for agents using the MCP. As users develop with the tools, the system accumulates trajectory data, identifies patterns, and surfaces recommendations. The loop: Use tools → Log calls → Analyze trajectories → Find gaps → Recommend improvements → Apply → Re-analyze. Over time, the system gets smarter about the project's specific development patterns.",
839
+ description: "Continuous self-improvement cycle for agents using the MCP. Tool calls are now auto-instrumented every call is automatically logged with timing and status. The loop: Use tools → Auto-log → Analyze trajectories → Find gaps → Clean stale data → Synthesize recon → Recommend improvements → Apply → Re-analyze. Over time, the system gets smarter about the project's specific development patterns.",
729
840
  steps: [
730
841
  {
731
842
  step: 1,
@@ -757,6 +868,13 @@ const METHODOLOGY_CONTENT = {
757
868
  },
758
869
  {
759
870
  step: 5,
871
+ name: "Clean & Synthesize",
872
+ description: "Clean up stale runs and synthesize recon findings into learnings. Orphaned eval runs and stale gaps skew health scores. Recon findings are ephemeral — convert them into searchable learnings.",
873
+ tools: ["cleanup_stale_runs", "synthesize_recon_to_learnings"],
874
+ action: "Call cleanup_stale_runs(dryRun=true) to preview, then dryRun=false to clean. Call synthesize_recon_to_learnings(dryRun=true) to preview conversions, then dryRun=false.",
875
+ },
876
+ {
877
+ step: 6,
760
878
  name: "Apply & Re-Analyze",
761
879
  description: "Implement the top recommendations, then re-run the analysis to verify improvement. This closes the loop and makes the system progressively smarter.",
762
880
  tools: ["record_learning", "get_self_eval_report"],
@@ -789,7 +907,8 @@ const METHODOLOGY_CONTENT = {
789
907
  agents_md_maintenance: "AGENTS.md Maintenance — keep documentation synchronized with implementations",
790
908
  agent_bootstrap: "Agent Bootstrap — self-discover infrastructure, triple verification with authoritative sources, self-implement missing components",
791
909
  autonomous_maintenance: "Autonomous Maintenance — risk-tiered execution, re-update before create, self-maintenance cycles, OpenClaw scaffolding, Ralph Wiggum loops",
792
- self_reinforced_learning: "Self-Reinforced Learningtrajectory analysis, self-evaluation reports, improvement recommendations, closed-loop optimization",
910
+ parallel_agent_teams: "Parallel Agent Teams task locking, role specialization, context budget management, oracle testing. Based on Anthropic's 'Building a C Compiler with Parallel Claudes' (Feb 2026)",
911
+ self_reinforced_learning: "Self-Reinforced Learning — auto-instrumented trajectory analysis, self-evaluation reports, improvement recommendations, stale run cleanup, recon synthesis, closed-loop optimization",
793
912
  },
794
913
  },
795
914
  {
@@ -851,6 +970,7 @@ export function createMetaTools(allTools) {
851
970
  "documentation",
852
971
  "bootstrap",
853
972
  "self_eval",
973
+ "parallel_agents",
854
974
  "meta",
855
975
  ],
856
976
  description: "Filter by tool category (optional)",
@@ -945,6 +1065,20 @@ export function createMetaTools(allTools) {
945
1065
  "get_trajectory_analysis",
946
1066
  "get_self_eval_report",
947
1067
  "get_improvement_recommendations",
1068
+ "cleanup_stale_runs",
1069
+ "synthesize_recon_to_learnings",
1070
+ ],
1071
+ parallel_agents: [
1072
+ "claim_agent_task",
1073
+ "release_agent_task",
1074
+ "list_agent_tasks",
1075
+ "assign_agent_role",
1076
+ "get_agent_role",
1077
+ "log_context_budget",
1078
+ "run_oracle_comparison",
1079
+ "get_parallel_status",
1080
+ "bootstrap_parallel_agents",
1081
+ "generate_parallel_agents_md",
948
1082
  ],
949
1083
  meta: ["findTools", "getMethodology"],
950
1084
  };
@@ -957,6 +1091,36 @@ export function createMetaTools(allTools) {
957
1091
  const text = `${t.name} ${t.description}`.toLowerCase();
958
1092
  return query.split(/\s+/).some((word) => text.includes(word));
959
1093
  });
1094
+ // Contextual recommendations: surface parallel tools only when relevant
1095
+ const parallelKeywords = ["parallel", "agent", "team", "multi-agent", "subagent", "concurrent", "worktree", "oracle", "lock", "coordinate", "role"];
1096
+ const queryHintsParallel = parallelKeywords.some((kw) => query.includes(kw));
1097
+ const parallelToolNames = new Set(categoryMap.parallel_agents);
1098
+ // Add contextual hint about parallel tools
1099
+ const contextHints = [];
1100
+ if (!queryHintsParallel && !category) {
1101
+ // Don't surface parallel tools unless the query is about parallel/agent work
1102
+ const filtered = matches.filter((t) => !parallelToolNames.has(t.name));
1103
+ if (filtered.length < matches.length) {
1104
+ contextHints.push("Parallel agent tools are available but not shown (query didn't indicate multi-agent work). " +
1105
+ "Use findTools({ category: 'parallel_agents' }) or include 'parallel'/'agent'/'team' in your query to discover them.");
1106
+ }
1107
+ return {
1108
+ query,
1109
+ count: filtered.length,
1110
+ tools: filtered.map((t) => ({
1111
+ name: t.name,
1112
+ description: t.description,
1113
+ })),
1114
+ contextHints,
1115
+ tip: "Use category filter or specific keywords to narrow results. Call getMethodology('overview') for all available methodologies.",
1116
+ };
1117
+ }
1118
+ // When parallel tools ARE relevant, add Claude Code guidance
1119
+ if (queryHintsParallel || category === "parallel_agents") {
1120
+ contextHints.push("Claude Code users: Use the Task tool to spawn parallel subagents, each with access to NodeBench MCP. " +
1121
+ "Each subagent calls claim_agent_task to lock work, assign_agent_role for specialization, and release_agent_task when done. " +
1122
+ "Call getMethodology('parallel_agent_teams') for the full workflow.");
1123
+ }
960
1124
  return {
961
1125
  query,
962
1126
  count: matches.length,
@@ -964,12 +1128,14 @@ export function createMetaTools(allTools) {
964
1128
  name: t.name,
965
1129
  description: t.description,
966
1130
  })),
1131
+ contextHints,
1132
+ tip: "Use category filter or specific keywords to narrow results. Call getMethodology('overview') for all available methodologies.",
967
1133
  };
968
1134
  },
969
1135
  },
970
1136
  {
971
1137
  name: "getMethodology",
972
- description: 'Get step-by-step guidance for a development methodology. Topics: verification, eval, flywheel, mandatory_flywheel, reconnaissance, quality_gates, ui_ux_qa, agentic_vision, closed_loop, learnings, project_ideation, tech_stack_2026, telemetry_setup, agents_md_maintenance, agent_bootstrap, autonomous_maintenance, self_reinforced_learning, overview. Call with topic "overview" to see all available methodologies.',
1138
+ description: 'Get step-by-step guidance for a development methodology. Topics: verification, eval, flywheel, mandatory_flywheel, reconnaissance, quality_gates, ui_ux_qa, agentic_vision, closed_loop, learnings, project_ideation, tech_stack_2026, telemetry_setup, agents_md_maintenance, agent_bootstrap, autonomous_maintenance, parallel_agent_teams, self_reinforced_learning, overview. Call with topic "overview" to see all available methodologies.',
973
1139
  inputSchema: {
974
1140
  type: "object",
975
1141
  properties: {
@@ -992,6 +1158,7 @@ export function createMetaTools(allTools) {
992
1158
  "agents_md_maintenance",
993
1159
  "agent_bootstrap",
994
1160
  "autonomous_maintenance",
1161
+ "parallel_agent_teams",
995
1162
  "self_reinforced_learning",
996
1163
  "overview",
997
1164
  ],