nodebench-mcp 1.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 (65) hide show
  1. package/README.md +237 -0
  2. package/dist/__tests__/tools.test.d.ts +1 -0
  3. package/dist/__tests__/tools.test.js +402 -0
  4. package/dist/__tests__/tools.test.js.map +1 -0
  5. package/dist/db.d.ts +4 -0
  6. package/dist/db.js +198 -0
  7. package/dist/db.js.map +1 -0
  8. package/dist/index.d.ts +19 -0
  9. package/dist/index.js +237 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/tools/documentTools.d.ts +5 -0
  12. package/dist/tools/documentTools.js +524 -0
  13. package/dist/tools/documentTools.js.map +1 -0
  14. package/dist/tools/documentationTools.d.ts +12 -0
  15. package/dist/tools/documentationTools.js +647 -0
  16. package/dist/tools/documentationTools.js.map +1 -0
  17. package/dist/tools/evalTools.d.ts +6 -0
  18. package/dist/tools/evalTools.js +335 -0
  19. package/dist/tools/evalTools.js.map +1 -0
  20. package/dist/tools/financialTools.d.ts +10 -0
  21. package/dist/tools/financialTools.js +403 -0
  22. package/dist/tools/financialTools.js.map +1 -0
  23. package/dist/tools/flywheelTools.d.ts +6 -0
  24. package/dist/tools/flywheelTools.js +366 -0
  25. package/dist/tools/flywheelTools.js.map +1 -0
  26. package/dist/tools/githubTools.d.ts +12 -0
  27. package/dist/tools/githubTools.js +432 -0
  28. package/dist/tools/githubTools.js.map +1 -0
  29. package/dist/tools/learningTools.d.ts +6 -0
  30. package/dist/tools/learningTools.js +199 -0
  31. package/dist/tools/learningTools.js.map +1 -0
  32. package/dist/tools/memoryTools.d.ts +5 -0
  33. package/dist/tools/memoryTools.js +137 -0
  34. package/dist/tools/memoryTools.js.map +1 -0
  35. package/dist/tools/metaTools.d.ts +7 -0
  36. package/dist/tools/metaTools.js +837 -0
  37. package/dist/tools/metaTools.js.map +1 -0
  38. package/dist/tools/planningTools.d.ts +5 -0
  39. package/dist/tools/planningTools.js +147 -0
  40. package/dist/tools/planningTools.js.map +1 -0
  41. package/dist/tools/qualityGateTools.d.ts +6 -0
  42. package/dist/tools/qualityGateTools.js +347 -0
  43. package/dist/tools/qualityGateTools.js.map +1 -0
  44. package/dist/tools/reconTools.d.ts +8 -0
  45. package/dist/tools/reconTools.js +729 -0
  46. package/dist/tools/reconTools.js.map +1 -0
  47. package/dist/tools/searchTools.d.ts +5 -0
  48. package/dist/tools/searchTools.js +145 -0
  49. package/dist/tools/searchTools.js.map +1 -0
  50. package/dist/tools/uiCaptureTools.d.ts +8 -0
  51. package/dist/tools/uiCaptureTools.js +339 -0
  52. package/dist/tools/uiCaptureTools.js.map +1 -0
  53. package/dist/tools/verificationTools.d.ts +6 -0
  54. package/dist/tools/verificationTools.js +472 -0
  55. package/dist/tools/verificationTools.js.map +1 -0
  56. package/dist/tools/visionTools.d.ts +12 -0
  57. package/dist/tools/visionTools.js +553 -0
  58. package/dist/tools/visionTools.js.map +1 -0
  59. package/dist/tools/webTools.d.ts +12 -0
  60. package/dist/tools/webTools.js +443 -0
  61. package/dist/tools/webTools.js.map +1 -0
  62. package/dist/types.d.ts +16 -0
  63. package/dist/types.js +2 -0
  64. package/dist/types.js.map +1 -0
  65. package/package.json +66 -0
@@ -0,0 +1,729 @@
1
+ /**
2
+ * Reconnaissance tools — structured research and context gathering.
3
+ * Phase 1 of verification requires checking latest SDKs, APIs, docs,
4
+ * AND understanding the existing codebase/project context.
5
+ * These tools structure that research as a trackable process.
6
+ */
7
+ import { getDb, genId } from "../db.js";
8
+ /** Pre-built source checklists for known ecosystems. No API keys needed. */
9
+ const FRAMEWORK_SOURCES = {
10
+ anthropic: [
11
+ {
12
+ source: "Anthropic Blog",
13
+ url: "https://www.anthropic.com/news",
14
+ checkFor: "New model releases, API changes, safety updates",
15
+ },
16
+ {
17
+ source: "Claude API Changelog",
18
+ url: "https://docs.anthropic.com/en/release-notes/api",
19
+ checkFor: "Breaking changes, new endpoints, deprecations",
20
+ },
21
+ {
22
+ source: "MCP Specification",
23
+ url: "https://github.com/modelcontextprotocol/specification",
24
+ checkFor: "Protocol version updates, new capabilities",
25
+ },
26
+ {
27
+ source: "MCP TypeScript SDK Releases",
28
+ url: "https://github.com/modelcontextprotocol/typescript-sdk/releases",
29
+ checkFor: "SDK API changes, Zod requirements, transport updates",
30
+ },
31
+ {
32
+ source: "Anthropic Cookbook",
33
+ url: "https://github.com/anthropics/anthropic-cookbook",
34
+ checkFor: "Best practices, tool use patterns, prompt engineering",
35
+ },
36
+ ],
37
+ langchain: [
38
+ {
39
+ source: "LangChain Blog",
40
+ url: "https://blog.langchain.dev/",
41
+ checkFor: "Architecture changes, LangGraph updates, new integrations",
42
+ },
43
+ {
44
+ source: "LangChain JS Releases",
45
+ url: "https://github.com/langchain-ai/langchainjs/releases",
46
+ checkFor: "Breaking changes, new tools, deprecated APIs",
47
+ },
48
+ {
49
+ source: "LangChain Python Releases",
50
+ url: "https://github.com/langchain-ai/langchain/releases",
51
+ checkFor: "Breaking changes, new tools, deprecated APIs",
52
+ },
53
+ {
54
+ source: "LangSmith Docs",
55
+ url: "https://docs.smith.langchain.com/",
56
+ checkFor: "Eval frameworks, tracing patterns, dataset management",
57
+ },
58
+ ],
59
+ openai: [
60
+ {
61
+ source: "OpenAI Blog",
62
+ url: "https://openai.com/blog",
63
+ checkFor: "New models, API features, pricing changes",
64
+ },
65
+ {
66
+ source: "OpenAI API Changelog",
67
+ url: "https://platform.openai.com/docs/changelog",
68
+ checkFor: "Endpoint changes, deprecations, new parameters",
69
+ },
70
+ {
71
+ source: "OpenAI Agents SDK",
72
+ url: "https://github.com/openai/openai-agents-sdk/releases",
73
+ checkFor: "Agent patterns, tool use, MCP integration",
74
+ },
75
+ {
76
+ source: "OpenAI Evals",
77
+ url: "https://github.com/openai/evals",
78
+ checkFor: "Evaluation datasets, benchmarks, scoring patterns",
79
+ },
80
+ ],
81
+ google: [
82
+ {
83
+ source: "Google AI Blog",
84
+ url: "https://blog.google/technology/ai/",
85
+ checkFor: "Gemini updates, new capabilities, research papers",
86
+ },
87
+ {
88
+ source: "Gemini API Changelog",
89
+ url: "https://ai.google.dev/gemini-api/docs/changelog",
90
+ checkFor: "API changes, new models, feature additions",
91
+ },
92
+ {
93
+ source: "Google AI Studio",
94
+ url: "https://aistudio.google.com/",
95
+ checkFor: "Tool use patterns, grounding, function calling updates",
96
+ },
97
+ ],
98
+ mcp: [
99
+ {
100
+ source: "MCP Specification",
101
+ url: "https://github.com/modelcontextprotocol/specification",
102
+ checkFor: "Protocol version, new capabilities, transport changes",
103
+ },
104
+ {
105
+ source: "MCP TypeScript SDK",
106
+ url: "https://github.com/modelcontextprotocol/typescript-sdk/releases",
107
+ checkFor: "SDK breaking changes, new server/client APIs",
108
+ },
109
+ {
110
+ source: "MCP Python SDK",
111
+ url: "https://github.com/modelcontextprotocol/python-sdk/releases",
112
+ checkFor: "SDK breaking changes, new patterns",
113
+ },
114
+ {
115
+ source: "MCP Servers Directory",
116
+ url: "https://github.com/modelcontextprotocol/servers",
117
+ checkFor: "Reference implementations, community patterns",
118
+ },
119
+ ],
120
+ };
121
+ export const reconTools = [
122
+ {
123
+ name: "run_recon",
124
+ description: "Start a reconnaissance research session. Use this at the start of Phase 1 (Context Gathering) to organize research into external sources (SDKs, APIs, blogs) AND internal context (codebase, project details, existing patterns). Returns a structured research plan with suggested sources and context-gathering questions.",
125
+ inputSchema: {
126
+ type: "object",
127
+ properties: {
128
+ target: {
129
+ type: "string",
130
+ description: "What you're researching (e.g., 'MCP SDK update', 'Anthropic Claude API', 'project auth system')",
131
+ },
132
+ description: {
133
+ type: "string",
134
+ description: "Why you're researching this (e.g., 'Planning MCP server upgrade', 'Understanding existing auth before refactor')",
135
+ },
136
+ projectContext: {
137
+ type: "object",
138
+ description: "Existing project context to inform the research. Include whatever is known.",
139
+ properties: {
140
+ techStack: {
141
+ type: "string",
142
+ description: "Languages, frameworks, runtimes (e.g., 'TypeScript, Node.js, Convex, React')",
143
+ },
144
+ currentVersions: {
145
+ type: "string",
146
+ description: "Relevant package versions (e.g., 'MCP SDK 1.25.3, better-sqlite3 11.x')",
147
+ },
148
+ architecture: {
149
+ type: "string",
150
+ description: "Brief architecture description (e.g., 'MCP server over stdio, SQLite local DB')",
151
+ },
152
+ knownIssues: {
153
+ type: "string",
154
+ description: "Known problems to investigate (e.g., 'Zod schema requirement in SDK >=1.17')",
155
+ },
156
+ },
157
+ },
158
+ },
159
+ required: ["target"],
160
+ },
161
+ handler: async (args) => {
162
+ const { target, description, projectContext } = args;
163
+ const db = getDb();
164
+ const sessionId = genId("recon");
165
+ const now = new Date().toISOString();
166
+ const fullDescription = projectContext
167
+ ? `${description ?? target}. Context: ${JSON.stringify(projectContext)}`
168
+ : description ?? null;
169
+ db.prepare("INSERT INTO recon_sessions (id, target, description, status, created_at) VALUES (?, ?, ?, 'active', ?)").run(sessionId, target, fullDescription, now);
170
+ // Generate research plan based on target keywords
171
+ const targetLower = target.toLowerCase();
172
+ const externalSources = [];
173
+ const internalChecks = [];
174
+ const contextQuestions = [];
175
+ // External source suggestions based on keywords
176
+ for (const [ecosystem, sources] of Object.entries(FRAMEWORK_SOURCES)) {
177
+ if (targetLower.includes(ecosystem) ||
178
+ (ecosystem === "anthropic" && targetLower.includes("claude")) ||
179
+ (ecosystem === "mcp" &&
180
+ targetLower.includes("model context protocol"))) {
181
+ for (const s of sources) {
182
+ externalSources.push(`[${s.source}] ${s.url} — ${s.checkFor}`);
183
+ }
184
+ }
185
+ }
186
+ // Always suggest generic external checks
187
+ externalSources.push("Search GitHub issues for recent bugs related to your target");
188
+ externalSources.push("Check npm/PyPI for latest package versions and changelogs");
189
+ // Internal context checks
190
+ internalChecks.push("Search codebase for existing usage of the target (grep for imports, function calls)");
191
+ internalChecks.push("Review AGENTS.md or project docs for documented patterns and conventions");
192
+ internalChecks.push("Check package.json / requirements.txt for current dependency versions");
193
+ internalChecks.push("Look for existing tests that cover the area being researched");
194
+ internalChecks.push("Search learnings DB for past issues related to this target");
195
+ // Context questions to ask if projectContext is missing
196
+ if (!projectContext) {
197
+ contextQuestions.push("What is the tech stack? (languages, frameworks, runtimes)");
198
+ contextQuestions.push("What are the current versions of relevant packages?");
199
+ contextQuestions.push("What is the high-level architecture? (monolith, microservices, MCP server, etc.)");
200
+ contextQuestions.push("Are there known issues or constraints related to this research?");
201
+ }
202
+ else {
203
+ if (!projectContext.techStack)
204
+ contextQuestions.push("What is the full tech stack?");
205
+ if (!projectContext.currentVersions)
206
+ contextQuestions.push("What versions of relevant packages are installed?");
207
+ }
208
+ return {
209
+ sessionId,
210
+ target,
211
+ status: "active",
212
+ researchPlan: {
213
+ externalSources: externalSources.length > 0
214
+ ? externalSources
215
+ : [
216
+ "No pre-built sources for this target. Use check_framework_updates for known ecosystems, or research manually.",
217
+ ],
218
+ internalChecks,
219
+ contextQuestions: contextQuestions.length > 0
220
+ ? contextQuestions
221
+ : ["Project context provided — no additional questions."],
222
+ projectContext: projectContext ?? null,
223
+ },
224
+ nextSteps: [
225
+ "1. Answer any contextQuestions (gather project context)",
226
+ "2. Use check_framework_updates for known ecosystems",
227
+ "3. Use search_learnings to check for past findings",
228
+ "4. Visit each external source",
229
+ "5. Call log_recon_finding for each discovery",
230
+ "6. Call get_recon_summary when research is complete",
231
+ ],
232
+ };
233
+ },
234
+ },
235
+ {
236
+ name: "log_recon_finding",
237
+ description: "Record a finding from reconnaissance research. Link it to a recon session and categorize it. Use for both external discoveries (SDK changes, blog posts) and internal findings (codebase patterns, existing implementations).",
238
+ inputSchema: {
239
+ type: "object",
240
+ properties: {
241
+ sessionId: {
242
+ type: "string",
243
+ description: "Recon session ID from run_recon",
244
+ },
245
+ sourceUrl: {
246
+ type: "string",
247
+ description: "Where you found this (URL to blog post, GitHub issue, docs page, or 'internal:filename' for codebase findings)",
248
+ },
249
+ category: {
250
+ type: "string",
251
+ enum: [
252
+ "breaking_change",
253
+ "new_feature",
254
+ "deprecation",
255
+ "best_practice",
256
+ "dataset",
257
+ "benchmark",
258
+ "codebase_pattern",
259
+ "existing_implementation",
260
+ ],
261
+ description: "Type of finding",
262
+ },
263
+ summary: {
264
+ type: "string",
265
+ description: "What you found (concise description)",
266
+ },
267
+ relevance: {
268
+ type: "string",
269
+ description: "How this affects current work",
270
+ },
271
+ actionItems: {
272
+ type: "string",
273
+ description: "What needs to be done based on this finding",
274
+ },
275
+ },
276
+ required: ["sessionId", "category", "summary"],
277
+ },
278
+ handler: async (args) => {
279
+ const { sessionId, sourceUrl, category, summary, relevance, actionItems } = args;
280
+ const db = getDb();
281
+ const session = db
282
+ .prepare("SELECT * FROM recon_sessions WHERE id = ?")
283
+ .get(sessionId);
284
+ if (!session)
285
+ throw new Error(`Recon session not found: ${sessionId}`);
286
+ const findingId = genId("finding");
287
+ const now = new Date().toISOString();
288
+ db.prepare("INSERT INTO recon_findings (id, session_id, source_url, category, summary, relevance, action_items, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(findingId, sessionId, sourceUrl ?? null, category, summary, relevance ?? null, actionItems ?? null, now);
289
+ // Count findings so far
290
+ const count = db
291
+ .prepare("SELECT COUNT(*) as count FROM recon_findings WHERE session_id = ?")
292
+ .get(sessionId).count;
293
+ return {
294
+ findingId,
295
+ sessionId,
296
+ category,
297
+ findingCount: count,
298
+ message: `Finding recorded (${count} total). Call get_recon_summary to see all findings for this session.`,
299
+ };
300
+ },
301
+ },
302
+ {
303
+ name: "get_recon_summary",
304
+ description: "Get aggregated summary of all findings from a recon session. Groups findings by category (breaking changes, new features, codebase patterns, etc.) with prioritized action items.",
305
+ inputSchema: {
306
+ type: "object",
307
+ properties: {
308
+ sessionId: {
309
+ type: "string",
310
+ description: "Recon session ID",
311
+ },
312
+ completeSession: {
313
+ type: "boolean",
314
+ description: "Mark session as completed (default false)",
315
+ },
316
+ },
317
+ required: ["sessionId"],
318
+ },
319
+ handler: async (args) => {
320
+ const { sessionId, completeSession } = args;
321
+ const db = getDb();
322
+ const session = db
323
+ .prepare("SELECT * FROM recon_sessions WHERE id = ?")
324
+ .get(sessionId);
325
+ if (!session)
326
+ throw new Error(`Recon session not found: ${sessionId}`);
327
+ const findings = db
328
+ .prepare("SELECT * FROM recon_findings WHERE session_id = ? ORDER BY created_at ASC")
329
+ .all(sessionId);
330
+ // Group by category
331
+ const byCategory = {};
332
+ for (const f of findings) {
333
+ if (!byCategory[f.category])
334
+ byCategory[f.category] = [];
335
+ byCategory[f.category].push({
336
+ findingId: f.id,
337
+ summary: f.summary,
338
+ sourceUrl: f.source_url,
339
+ relevance: f.relevance,
340
+ actionItems: f.action_items,
341
+ });
342
+ }
343
+ // Aggregate action items
344
+ const allActionItems = findings
345
+ .filter((f) => f.action_items)
346
+ .map((f) => ({
347
+ action: f.action_items,
348
+ category: f.category,
349
+ source: f.source_url,
350
+ }));
351
+ // Priority order for categories
352
+ const PRIORITY_ORDER = [
353
+ "breaking_change",
354
+ "deprecation",
355
+ "existing_implementation",
356
+ "codebase_pattern",
357
+ "new_feature",
358
+ "best_practice",
359
+ "dataset",
360
+ "benchmark",
361
+ ];
362
+ const prioritizedActions = allActionItems.sort((a, b) => {
363
+ const ai = PRIORITY_ORDER.indexOf(a.category);
364
+ const bi = PRIORITY_ORDER.indexOf(b.category);
365
+ return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
366
+ });
367
+ if (completeSession) {
368
+ const now = new Date().toISOString();
369
+ db.prepare("UPDATE recon_sessions SET status = 'completed', completed_at = ? WHERE id = ?").run(now, sessionId);
370
+ }
371
+ return {
372
+ sessionId,
373
+ target: session.target,
374
+ status: completeSession ? "completed" : session.status,
375
+ totalFindings: findings.length,
376
+ findingsByCategory: byCategory,
377
+ prioritizedActionItems: prioritizedActions,
378
+ recommendation: findings.length > 0
379
+ ? "Review by priority: breaking_change > deprecation > existing_implementation > codebase_pattern > new_feature > best_practice > dataset > benchmark"
380
+ : "No findings recorded yet. Continue research or close session.",
381
+ };
382
+ },
383
+ },
384
+ {
385
+ name: "check_framework_updates",
386
+ description: "Get a structured checklist of sources to check for framework/SDK updates. Pre-built source lists for: anthropic, langchain, openai, google, mcp. Each source includes what to check for. Use this to guide your reconnaissance research systematically.",
387
+ inputSchema: {
388
+ type: "object",
389
+ properties: {
390
+ ecosystem: {
391
+ type: "string",
392
+ enum: ["anthropic", "langchain", "openai", "google", "mcp"],
393
+ description: "Which ecosystem to get sources for",
394
+ },
395
+ },
396
+ required: ["ecosystem"],
397
+ },
398
+ handler: async (args) => {
399
+ const sources = FRAMEWORK_SOURCES[args.ecosystem];
400
+ if (!sources)
401
+ throw new Error(`Unknown ecosystem: ${args.ecosystem}. Valid: ${Object.keys(FRAMEWORK_SOURCES).join(", ")}`);
402
+ return {
403
+ ecosystem: args.ecosystem,
404
+ sourceCount: sources.length,
405
+ sources,
406
+ checklist: sources.map((s, i) => ({
407
+ step: i + 1,
408
+ action: `Visit ${s.source}`,
409
+ url: s.url,
410
+ lookFor: s.checkFor,
411
+ })),
412
+ usage: "Visit each source and record findings with log_recon_finding. Focus on: breaking_change (highest priority), deprecation, new_feature, best_practice.",
413
+ };
414
+ },
415
+ },
416
+ {
417
+ name: "search_all_knowledge",
418
+ description: "Search ALL accumulated knowledge in one call: learnings (edge cases, gotchas, patterns), recon findings (across ALL sessions), and resolved gaps from past verifications. Use this before starting any new work to see what the system already knows. This is the unified knowledge base that grows automatically as the tools are used.",
419
+ inputSchema: {
420
+ type: "object",
421
+ properties: {
422
+ query: {
423
+ type: "string",
424
+ description: "What you're looking for (e.g., 'MCP SDK breaking changes', 'auth patterns', 'SQLite gotchas')",
425
+ },
426
+ categories: {
427
+ type: "array",
428
+ items: { type: "string" },
429
+ description: "Filter by categories (optional). Applies to both learnings and recon findings.",
430
+ },
431
+ limit: {
432
+ type: "number",
433
+ description: "Max results per source (default 10)",
434
+ },
435
+ },
436
+ required: ["query"],
437
+ },
438
+ handler: async (args) => {
439
+ const { query, categories, limit: maxResults } = args;
440
+ const db = getDb();
441
+ const limit = maxResults ?? 10;
442
+ // 1. Search learnings via FTS5 (with LIKE fallback for syntax issues)
443
+ let learnings = [];
444
+ try {
445
+ learnings = db
446
+ .prepare(`SELECT l.key, l.content, l.category, l.tags, l.source_cycle, l.created_at
447
+ FROM learnings_fts
448
+ JOIN learnings l ON l.id = learnings_fts.rowid
449
+ WHERE learnings_fts MATCH ?
450
+ ORDER BY rank
451
+ LIMIT ?`)
452
+ .all(query, limit);
453
+ }
454
+ catch {
455
+ // FTS5 syntax error (e.g., special chars in query) — fall back to LIKE
456
+ learnings = db
457
+ .prepare(`SELECT key, content, category, tags, source_cycle, created_at
458
+ FROM learnings
459
+ WHERE content LIKE ? OR key LIKE ?
460
+ ORDER BY created_at DESC
461
+ LIMIT ?`)
462
+ .all(`%${query}%`, `%${query}%`, limit);
463
+ }
464
+ // Apply category filter if provided
465
+ if (categories && categories.length > 0) {
466
+ learnings = learnings.filter((l) => categories.includes(l.category));
467
+ }
468
+ // 2. Search recon findings across ALL sessions
469
+ let reconFindings;
470
+ if (categories && categories.length > 0) {
471
+ const placeholders = categories.map(() => "?").join(", ");
472
+ reconFindings = db
473
+ .prepare(`SELECT f.id, f.session_id, f.source_url, f.category, f.summary,
474
+ f.relevance, f.action_items, f.created_at,
475
+ s.target as session_target
476
+ FROM recon_findings f
477
+ JOIN recon_sessions s ON s.id = f.session_id
478
+ WHERE (f.summary LIKE ? OR f.relevance LIKE ? OR f.action_items LIKE ?)
479
+ AND f.category IN (${placeholders})
480
+ ORDER BY f.created_at DESC
481
+ LIMIT ?`)
482
+ .all(`%${query}%`, `%${query}%`, `%${query}%`, ...categories, limit);
483
+ }
484
+ else {
485
+ reconFindings = db
486
+ .prepare(`SELECT f.id, f.session_id, f.source_url, f.category, f.summary,
487
+ f.relevance, f.action_items, f.created_at,
488
+ s.target as session_target
489
+ FROM recon_findings f
490
+ JOIN recon_sessions s ON s.id = f.session_id
491
+ WHERE f.summary LIKE ? OR f.relevance LIKE ? OR f.action_items LIKE ?
492
+ ORDER BY f.created_at DESC
493
+ LIMIT ?`)
494
+ .all(`%${query}%`, `%${query}%`, `%${query}%`, limit);
495
+ }
496
+ // 3. Search ALL gaps from past verification cycles (resolved + open)
497
+ const matchedGaps = db
498
+ .prepare(`SELECT g.id, g.cycle_id, g.title, g.description, g.severity,
499
+ g.status, g.fix_strategy as resolution, g.resolved_at,
500
+ c.title as cycle_target
501
+ FROM gaps g
502
+ JOIN verification_cycles c ON c.id = g.cycle_id
503
+ WHERE (g.description LIKE ? OR g.fix_strategy LIKE ? OR g.title LIKE ?)
504
+ ORDER BY g.created_at DESC
505
+ LIMIT ?`)
506
+ .all(`%${query}%`, `%${query}%`, `%${query}%`, limit);
507
+ const totalResults = learnings.length + reconFindings.length + matchedGaps.length;
508
+ return {
509
+ query,
510
+ totalResults,
511
+ learnings: learnings.map((l) => ({
512
+ source: "learnings",
513
+ key: l.key,
514
+ content: l.content,
515
+ category: l.category,
516
+ tags: l.tags ? JSON.parse(l.tags) : [],
517
+ createdAt: l.created_at,
518
+ })),
519
+ reconFindings: reconFindings.map((f) => ({
520
+ source: "recon",
521
+ sessionTarget: f.session_target,
522
+ category: f.category,
523
+ summary: f.summary,
524
+ relevance: f.relevance,
525
+ actionItems: f.action_items,
526
+ sourceUrl: f.source_url,
527
+ createdAt: f.created_at,
528
+ })),
529
+ gaps: matchedGaps.map((g) => ({
530
+ source: "verification",
531
+ cycleTarget: g.cycle_target,
532
+ title: g.title,
533
+ description: g.description,
534
+ severity: g.severity,
535
+ status: g.status,
536
+ resolution: g.resolution,
537
+ resolvedAt: g.resolved_at,
538
+ })),
539
+ _contributeBack: {
540
+ instruction: "If you discover new information while working, record it so future agents benefit:",
541
+ actions: [
542
+ "record_learning — for edge cases, gotchas, patterns, conventions",
543
+ "log_recon_finding — for SDK changes, breaking changes, best practices",
544
+ ],
545
+ },
546
+ };
547
+ },
548
+ },
549
+ {
550
+ name: "bootstrap_project",
551
+ description: "Register or update your project's context (tech stack, architecture, conventions, build commands). This is stored persistently and used by all future agent sessions. Call this on first use to give the MCP full project awareness, or call again to update when your project evolves.",
552
+ inputSchema: {
553
+ type: "object",
554
+ properties: {
555
+ projectName: {
556
+ type: "string",
557
+ description: "Project name (e.g., 'my-saas-app', 'nodebench-mcp')",
558
+ },
559
+ techStack: {
560
+ type: "string",
561
+ description: "Languages, frameworks, runtimes (e.g., 'TypeScript, Node.js, React, Convex')",
562
+ },
563
+ architecture: {
564
+ type: "string",
565
+ description: "High-level architecture (e.g., 'Monorepo: MCP server (stdio) + web client (Next.js) + Convex backend')",
566
+ },
567
+ buildCommands: {
568
+ type: "string",
569
+ description: "Build commands (e.g., 'npm run build, tsc --noEmit')",
570
+ },
571
+ testCommands: {
572
+ type: "string",
573
+ description: "Test commands (e.g., 'npm test, npx jest, npx vitest')",
574
+ },
575
+ conventions: {
576
+ type: "string",
577
+ description: "Coding conventions (e.g., 'ESM modules, no default exports, strict TypeScript, raw JSON Schema for MCP tools')",
578
+ },
579
+ keyDependencies: {
580
+ type: "string",
581
+ description: "Key dependency versions (e.g., '@modelcontextprotocol/sdk 1.25.3, better-sqlite3 11.x')",
582
+ },
583
+ repoStructure: {
584
+ type: "string",
585
+ description: "Repository structure highlights (e.g., 'packages/mcp-local (MCP server), packages/web (frontend), convex/ (backend)')",
586
+ },
587
+ },
588
+ required: ["projectName"],
589
+ },
590
+ handler: async (args) => {
591
+ const db = getDb();
592
+ const now = new Date().toISOString();
593
+ const upsert = db.prepare("INSERT INTO project_context (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at");
594
+ const fields = {
595
+ project_name: args.projectName,
596
+ tech_stack: args.techStack,
597
+ architecture: args.architecture,
598
+ build_commands: args.buildCommands,
599
+ test_commands: args.testCommands,
600
+ conventions: args.conventions,
601
+ key_dependencies: args.keyDependencies,
602
+ repo_structure: args.repoStructure,
603
+ };
604
+ const stored = {};
605
+ for (const [key, value] of Object.entries(fields)) {
606
+ if (value) {
607
+ upsert.run(key, value, now);
608
+ stored[key] = value;
609
+ }
610
+ }
611
+ // Get knowledge base stats
612
+ const learningsCount = db.prepare("SELECT COUNT(*) as c FROM learnings").get().c;
613
+ const reconCount = db.prepare("SELECT COUNT(*) as c FROM recon_sessions").get().c;
614
+ const cycleCount = db.prepare("SELECT COUNT(*) as c FROM verification_cycles").get().c;
615
+ const gapCount = db
616
+ .prepare("SELECT COUNT(*) as c FROM gaps WHERE status = 'resolved'")
617
+ .get().c;
618
+ const isNew = learningsCount === 0 && reconCount === 0 && cycleCount === 0;
619
+ return {
620
+ projectName: args.projectName,
621
+ storedFields: Object.keys(stored),
622
+ context: stored,
623
+ knowledgeBase: {
624
+ learnings: learningsCount,
625
+ reconSessions: reconCount,
626
+ verificationCycles: cycleCount,
627
+ resolvedGaps: gapCount,
628
+ },
629
+ ...(isNew
630
+ ? {
631
+ _onboarding: {
632
+ message: "Project registered! This is a fresh install. Here's how to get started:",
633
+ nextSteps: [
634
+ 'Call getMethodology("overview") to see all available development methodologies',
635
+ 'Call search_all_knowledge("your current task") before starting any work',
636
+ 'Call run_recon with your project context to research latest SDK/framework updates',
637
+ "As you work, tool responses will guide you to record learnings and findings",
638
+ "The knowledge base grows automatically — future sessions benefit from past work",
639
+ ],
640
+ },
641
+ }
642
+ : {
643
+ _returning: {
644
+ message: `Welcome back! Knowledge base has ${learningsCount} learnings, ${reconCount} recon sessions, ${cycleCount} verification cycles, and ${gapCount} resolved gaps.`,
645
+ nextSteps: [
646
+ "Call search_all_knowledge with your current task to see what's already known",
647
+ "Call get_project_context to see full project context",
648
+ "Continue where you left off — all past work is searchable",
649
+ ],
650
+ },
651
+ }),
652
+ };
653
+ },
654
+ },
655
+ {
656
+ name: "get_project_context",
657
+ description: "Retrieve the stored project context (tech stack, architecture, conventions, etc.) and knowledge base stats. Call this at the start of any session to refresh your project awareness. If no project context exists, returns onboarding instructions.",
658
+ inputSchema: {
659
+ type: "object",
660
+ properties: {
661
+ key: {
662
+ type: "string",
663
+ description: "Get a specific field (optional). Keys: project_name, tech_stack, architecture, build_commands, test_commands, conventions, key_dependencies, repo_structure",
664
+ },
665
+ },
666
+ },
667
+ handler: async (args) => {
668
+ const db = getDb();
669
+ // Get project context
670
+ let rows;
671
+ if (args.key) {
672
+ rows = db
673
+ .prepare("SELECT * FROM project_context WHERE key = ?")
674
+ .all(args.key);
675
+ }
676
+ else {
677
+ rows = db
678
+ .prepare("SELECT * FROM project_context ORDER BY key")
679
+ .all();
680
+ }
681
+ const context = {};
682
+ for (const row of rows) {
683
+ context[row.key] = row.value;
684
+ }
685
+ // Knowledge base stats
686
+ const learningsCount = db.prepare("SELECT COUNT(*) as c FROM learnings").get().c;
687
+ const reconCount = db.prepare("SELECT COUNT(*) as c FROM recon_sessions").get().c;
688
+ const cycleCount = db.prepare("SELECT COUNT(*) as c FROM verification_cycles").get().c;
689
+ const gapCount = db
690
+ .prepare("SELECT COUNT(*) as c FROM gaps WHERE status = 'resolved'")
691
+ .get().c;
692
+ const evalCount = db.prepare("SELECT COUNT(*) as c FROM eval_runs").get().c;
693
+ if (rows.length === 0) {
694
+ return {
695
+ context: {},
696
+ knowledgeBase: {
697
+ learnings: learningsCount,
698
+ reconSessions: reconCount,
699
+ verificationCycles: cycleCount,
700
+ resolvedGaps: gapCount,
701
+ evalRuns: evalCount,
702
+ },
703
+ _onboarding: {
704
+ message: "No project context stored yet. Call bootstrap_project to register your project.",
705
+ example: 'bootstrap_project({ projectName: "my-app", techStack: "TypeScript, React, Node.js", architecture: "Next.js frontend + Express API" })',
706
+ },
707
+ };
708
+ }
709
+ return {
710
+ context,
711
+ knowledgeBase: {
712
+ learnings: learningsCount,
713
+ reconSessions: reconCount,
714
+ verificationCycles: cycleCount,
715
+ resolvedGaps: gapCount,
716
+ evalRuns: evalCount,
717
+ },
718
+ _contributeBack: {
719
+ instruction: "Keep project context current. Call bootstrap_project to update if your stack or architecture changes.",
720
+ actions: [
721
+ "bootstrap_project — update tech stack, dependencies, or conventions",
722
+ "search_all_knowledge — check existing knowledge before starting work",
723
+ ],
724
+ },
725
+ };
726
+ },
727
+ },
728
+ ];
729
+ //# sourceMappingURL=reconTools.js.map