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,647 @@
1
+ /**
2
+ * Documentation & Research tools — Self-maintenance and market research.
3
+ * Enables agents to maintain AGENTS.md, research job markets, and set up local environments.
4
+ *
5
+ * - update_agents_md: Read/append/update sections in AGENTS.md
6
+ * - research_job_market: Aggregate job requirements for roles/skills
7
+ * - setup_local_env: Help agents configure their local environment
8
+ *
9
+ * Designed for fully local operation — helps agents bootstrap their own tooling.
10
+ */
11
+ import { readFileSync, writeFileSync, existsSync } from "fs";
12
+ import { join, dirname } from "path";
13
+ import { execSync } from "child_process";
14
+ // ─── Dynamic import helpers ───────────────────────────────────────────────────
15
+ async function canImport(pkg) {
16
+ try {
17
+ await import(pkg);
18
+ return true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ // ─── AGENTS.md template ───────────────────────────────────────────────────────
25
+ const AGENTS_MD_TEMPLATE = `# AGENTS.md
26
+
27
+ This file provides instructions for AI agents working on this project.
28
+
29
+ ## Project Overview
30
+
31
+ <!-- Describe your project's purpose, architecture, and key components -->
32
+
33
+ ## Development Setup
34
+
35
+ <!-- Document setup steps: dependencies, environment variables, build commands -->
36
+
37
+ ## Tech Stack
38
+
39
+ <!-- List frameworks, libraries, and tools used -->
40
+
41
+ ## Coding Conventions
42
+
43
+ <!-- Document code style, naming conventions, patterns to follow -->
44
+
45
+ ## Testing
46
+
47
+ <!-- Document how to run tests, what testing frameworks are used -->
48
+
49
+ ## Key Commands
50
+
51
+ \`\`\`bash
52
+ # Add your common commands here
53
+ npm install
54
+ npm run build
55
+ npm run test
56
+ \`\`\`
57
+
58
+ ## Edge Cases & Learnings
59
+
60
+ <!-- Document gotchas, edge cases, and lessons learned -->
61
+
62
+ ---
63
+
64
+ *This file is maintained by NodeBench MCP. Use \`update_agents_md\` to add learnings automatically.*
65
+ `;
66
+ function parseSections(content) {
67
+ const lines = content.split("\n");
68
+ const sections = [];
69
+ let currentSection = null;
70
+ for (let i = 0; i < lines.length; i++) {
71
+ const line = lines[i];
72
+ const match = line.match(/^(#{1,6})\s+(.+)$/);
73
+ if (match) {
74
+ // Close previous section
75
+ if (currentSection) {
76
+ currentSection.endLine = i - 1;
77
+ currentSection.content = lines
78
+ .slice(currentSection.startLine + 1, i)
79
+ .join("\n")
80
+ .trim();
81
+ sections.push(currentSection);
82
+ }
83
+ // Start new section
84
+ currentSection = {
85
+ name: match[2].trim(),
86
+ level: match[1].length,
87
+ startLine: i,
88
+ endLine: lines.length - 1,
89
+ content: "",
90
+ };
91
+ }
92
+ }
93
+ // Close last section
94
+ if (currentSection) {
95
+ currentSection.content = lines
96
+ .slice(currentSection.startLine + 1)
97
+ .join("\n")
98
+ .trim();
99
+ sections.push(currentSection);
100
+ }
101
+ return sections;
102
+ }
103
+ function findAgentsMdPath(startDir) {
104
+ let dir = startDir;
105
+ const maxLevels = 10;
106
+ for (let i = 0; i < maxLevels; i++) {
107
+ const candidate = join(dir, "AGENTS.md");
108
+ if (existsSync(candidate)) {
109
+ return candidate;
110
+ }
111
+ const parent = dirname(dir);
112
+ if (parent === dir)
113
+ break; // Reached root
114
+ dir = parent;
115
+ }
116
+ return null;
117
+ }
118
+ function detectEnvStatus() {
119
+ const envVars = {};
120
+ // API Keys
121
+ const apiKeys = [
122
+ { name: "GEMINI_API_KEY", suggestion: "Get from https://aistudio.google.com/apikey" },
123
+ { name: "GOOGLE_AI_API_KEY", suggestion: "Alias for GEMINI_API_KEY" },
124
+ { name: "OPENAI_API_KEY", suggestion: "Get from https://platform.openai.com/api-keys" },
125
+ { name: "ANTHROPIC_API_KEY", suggestion: "Get from https://console.anthropic.com/" },
126
+ { name: "OPENROUTER_API_KEY", suggestion: "Get from https://openrouter.ai/keys" },
127
+ { name: "PERPLEXITY_API_KEY", suggestion: "Get from https://perplexity.ai/settings/api" },
128
+ { name: "GITHUB_TOKEN", suggestion: "Get from https://github.com/settings/tokens" },
129
+ { name: "GH_TOKEN", suggestion: "Alias for GITHUB_TOKEN (used by gh CLI)" },
130
+ ];
131
+ for (const { name, suggestion } of apiKeys) {
132
+ const value = process.env[name];
133
+ envVars[name] = {
134
+ available: !!value,
135
+ value: value ? `${value.slice(0, 8)}...` : undefined,
136
+ suggestion: value ? undefined : suggestion,
137
+ };
138
+ }
139
+ return envVars;
140
+ }
141
+ async function detectSdkStatus() {
142
+ const sdks = [
143
+ "@google/genai",
144
+ "openai",
145
+ "@anthropic-ai/sdk",
146
+ "sharp",
147
+ "playwright",
148
+ "cheerio",
149
+ ];
150
+ const status = {};
151
+ for (const sdk of sdks) {
152
+ status[sdk] = await canImport(sdk);
153
+ }
154
+ return status;
155
+ }
156
+ function detectNodeVersion() {
157
+ return process.version;
158
+ }
159
+ function detectPackageManager() {
160
+ // Check for lockfiles
161
+ const cwd = process.cwd();
162
+ if (existsSync(join(cwd, "pnpm-lock.yaml")))
163
+ return "pnpm";
164
+ if (existsSync(join(cwd, "yarn.lock")))
165
+ return "yarn";
166
+ if (existsSync(join(cwd, "bun.lockb")))
167
+ return "bun";
168
+ if (existsSync(join(cwd, "package-lock.json")))
169
+ return "npm";
170
+ return "npm"; // Default
171
+ }
172
+ function detectGitStatus() {
173
+ try {
174
+ const isGitRepo = existsSync(join(process.cwd(), ".git"));
175
+ if (!isGitRepo)
176
+ return { isGitRepo: false };
177
+ const branch = execSync("git branch --show-current", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
178
+ const status = execSync("git status --porcelain", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
179
+ return {
180
+ isGitRepo: true,
181
+ branch: branch || "unknown",
182
+ hasUncommitted: status.length > 0,
183
+ };
184
+ }
185
+ catch {
186
+ return { isGitRepo: false };
187
+ }
188
+ }
189
+ // ─── Tools ────────────────────────────────────────────────────────────────────
190
+ export const documentationTools = [
191
+ {
192
+ name: "update_agents_md",
193
+ description: "Read, append, or update sections in the AGENTS.md file. This file contains instructions for AI agents working on the project. Use 'read' to see current contents and sections, 'append' to add new content at the end, or 'update_section' to replace a specific section's content. Creates AGENTS.md from template if it doesn't exist.",
194
+ inputSchema: {
195
+ type: "object",
196
+ properties: {
197
+ operation: {
198
+ type: "string",
199
+ enum: ["read", "append", "update_section"],
200
+ description: "Operation: 'read' (get current content), 'append' (add to end), 'update_section' (replace section content)",
201
+ },
202
+ section: {
203
+ type: "string",
204
+ description: "For update_section: the section heading to update (e.g., 'Edge Cases & Learnings', 'Tech Stack')",
205
+ },
206
+ content: {
207
+ type: "string",
208
+ description: "For append/update_section: the content to add or replace with",
209
+ },
210
+ projectRoot: {
211
+ type: "string",
212
+ description: "Root directory to search for AGENTS.md (default: current working directory). Searches upward.",
213
+ },
214
+ createIfMissing: {
215
+ type: "boolean",
216
+ description: "Create AGENTS.md from template if not found (default: true for append/update_section, false for read)",
217
+ },
218
+ },
219
+ required: ["operation"],
220
+ },
221
+ handler: async (args) => {
222
+ const operation = args.operation;
223
+ const section = args.section;
224
+ const content = args.content;
225
+ const projectRoot = args.projectRoot ?? process.cwd();
226
+ const createIfMissing = args.createIfMissing ?? operation !== "read";
227
+ // Find AGENTS.md
228
+ let agentsMdPath = findAgentsMdPath(projectRoot);
229
+ // Create if missing and allowed
230
+ if (!agentsMdPath && createIfMissing) {
231
+ agentsMdPath = join(projectRoot, "AGENTS.md");
232
+ writeFileSync(agentsMdPath, AGENTS_MD_TEMPLATE, "utf-8");
233
+ }
234
+ if (!agentsMdPath || !existsSync(agentsMdPath)) {
235
+ return {
236
+ error: true,
237
+ operation,
238
+ message: "AGENTS.md not found",
239
+ suggestion: "Set createIfMissing: true to create from template, or specify projectRoot.",
240
+ searchedFrom: projectRoot,
241
+ };
242
+ }
243
+ const currentContent = readFileSync(agentsMdPath, "utf-8");
244
+ const sections = parseSections(currentContent);
245
+ // READ operation
246
+ if (operation === "read") {
247
+ return {
248
+ operation: "read",
249
+ agentsMdPath,
250
+ sections: sections.map((s) => ({
251
+ name: s.name,
252
+ level: s.level,
253
+ lineRange: `${s.startLine + 1}-${s.endLine + 1}`,
254
+ contentPreview: s.content.slice(0, 200) + (s.content.length > 200 ? "..." : ""),
255
+ })),
256
+ fullContent: currentContent,
257
+ characterCount: currentContent.length,
258
+ sectionCount: sections.length,
259
+ };
260
+ }
261
+ // APPEND operation
262
+ if (operation === "append") {
263
+ if (!content) {
264
+ return {
265
+ error: true,
266
+ operation: "append",
267
+ message: "Content is required for append operation",
268
+ };
269
+ }
270
+ const newContent = currentContent.trimEnd() + "\n\n" + content.trim() + "\n";
271
+ writeFileSync(agentsMdPath, newContent, "utf-8");
272
+ return {
273
+ operation: "append",
274
+ success: true,
275
+ agentsMdPath,
276
+ appendedLength: content.length,
277
+ newTotalLength: newContent.length,
278
+ };
279
+ }
280
+ // UPDATE_SECTION operation
281
+ if (operation === "update_section") {
282
+ if (!section) {
283
+ return {
284
+ error: true,
285
+ operation: "update_section",
286
+ message: "Section name is required for update_section operation",
287
+ availableSections: sections.map((s) => s.name),
288
+ };
289
+ }
290
+ if (!content) {
291
+ return {
292
+ error: true,
293
+ operation: "update_section",
294
+ message: "Content is required for update_section operation",
295
+ };
296
+ }
297
+ // Find the section (case-insensitive)
298
+ const targetSection = sections.find((s) => s.name.toLowerCase() === section.toLowerCase());
299
+ if (!targetSection) {
300
+ // Section not found - append it as new section
301
+ const prefix = "#".repeat(2); // Default to H2
302
+ const newSectionContent = `\n\n${prefix} ${section}\n\n${content.trim()}\n`;
303
+ const newContent = currentContent.trimEnd() + newSectionContent;
304
+ writeFileSync(agentsMdPath, newContent, "utf-8");
305
+ return {
306
+ operation: "update_section",
307
+ success: true,
308
+ agentsMdPath,
309
+ sectionName: section,
310
+ action: "created_new_section",
311
+ newTotalLength: newContent.length,
312
+ };
313
+ }
314
+ // Replace section content
315
+ const lines = currentContent.split("\n");
316
+ const headerLine = lines[targetSection.startLine];
317
+ // Find end of section (next section of same or higher level, or EOF)
318
+ let endLine = targetSection.endLine;
319
+ for (let i = targetSection.startLine + 1; i < lines.length; i++) {
320
+ const match = lines[i].match(/^(#{1,6})\s+/);
321
+ if (match && match[1].length <= targetSection.level) {
322
+ endLine = i - 1;
323
+ break;
324
+ }
325
+ }
326
+ // Reconstruct content
327
+ const before = lines.slice(0, targetSection.startLine + 1);
328
+ const after = lines.slice(endLine + 1);
329
+ const newContent = [...before, "", content.trim(), "", ...after].join("\n");
330
+ writeFileSync(agentsMdPath, newContent, "utf-8");
331
+ return {
332
+ operation: "update_section",
333
+ success: true,
334
+ agentsMdPath,
335
+ sectionName: section,
336
+ action: "replaced_content",
337
+ previousLength: targetSection.content.length,
338
+ newLength: content.length,
339
+ newTotalLength: newContent.length,
340
+ };
341
+ }
342
+ return {
343
+ error: true,
344
+ message: `Unknown operation: ${operation}`,
345
+ validOperations: ["read", "append", "update_section"],
346
+ };
347
+ },
348
+ },
349
+ {
350
+ name: "research_job_market",
351
+ description: "Research job market requirements for a given role or skill set. Provides guidance on in-demand skills, common requirements, and career recommendations. Useful for project ideation, learning priorities, and understanding market needs. Note: Returns curated guidance based on knowledge — for real-time job listings, use web_search with job board queries.",
352
+ inputSchema: {
353
+ type: "object",
354
+ properties: {
355
+ role: {
356
+ type: "string",
357
+ description: "Target role (e.g., 'AI Engineer', 'Full-Stack Developer', 'DevOps Engineer', 'Product Manager')",
358
+ },
359
+ skills: {
360
+ type: "array",
361
+ items: { type: "string" },
362
+ description: "Skills to evaluate (e.g., ['TypeScript', 'React', 'Python', 'LLMs'])",
363
+ },
364
+ location: {
365
+ type: "string",
366
+ description: "Location filter (e.g., 'remote', 'San Francisco', 'Europe'). Default: 'remote'.",
367
+ },
368
+ focus: {
369
+ type: "string",
370
+ enum: ["requirements", "skills", "salary", "companies", "trends"],
371
+ description: "What to focus on: 'requirements' (job reqs), 'skills' (in-demand skills), 'salary' (compensation), 'companies' (who's hiring), 'trends' (emerging tech). Default: 'requirements'.",
372
+ },
373
+ },
374
+ required: ["role"],
375
+ },
376
+ handler: async (args) => {
377
+ const role = args.role;
378
+ const skills = args.skills ?? [];
379
+ const location = args.location ?? "remote";
380
+ const focus = args.focus ?? "requirements";
381
+ // Curated market data (2025-2026 knowledge)
382
+ const marketData = {
383
+ "AI Engineer": {
384
+ commonRequirements: [
385
+ { skill: "Python", frequency: 95 },
386
+ { skill: "Machine Learning", frequency: 90 },
387
+ { skill: "LLMs/Transformers", frequency: 85 },
388
+ { skill: "PyTorch or TensorFlow", frequency: 80 },
389
+ { skill: "RAG/Vector DBs", frequency: 75 },
390
+ { skill: "API Development", frequency: 70 },
391
+ { skill: "Cloud (AWS/GCP/Azure)", frequency: 65 },
392
+ { skill: "TypeScript/Node.js", frequency: 45 },
393
+ ],
394
+ emergingSkills: [
395
+ "Agent Frameworks (LangChain, CrewAI, AutoGen)",
396
+ "MCP (Model Context Protocol)",
397
+ "Multimodal AI",
398
+ "Fine-tuning & RLHF",
399
+ "AI Safety & Alignment",
400
+ "Prompt Engineering",
401
+ ],
402
+ salaryRange: { min: 150000, max: 350000, currency: "USD", market: "US Tech" },
403
+ topCompanies: [
404
+ "Anthropic", "OpenAI", "Google DeepMind", "Meta AI",
405
+ "Cohere", "Scale AI", "Hugging Face", "Databricks",
406
+ ],
407
+ },
408
+ "Full-Stack Developer": {
409
+ commonRequirements: [
410
+ { skill: "JavaScript/TypeScript", frequency: 95 },
411
+ { skill: "React or Vue or Svelte", frequency: 90 },
412
+ { skill: "Node.js", frequency: 85 },
413
+ { skill: "SQL & NoSQL", frequency: 80 },
414
+ { skill: "REST/GraphQL APIs", frequency: 75 },
415
+ { skill: "Git", frequency: 95 },
416
+ { skill: "Cloud Services", frequency: 65 },
417
+ { skill: "Testing", frequency: 60 },
418
+ ],
419
+ emergingSkills: [
420
+ "Next.js / Server Components",
421
+ "Edge Functions",
422
+ "AI/LLM Integration",
423
+ "Real-time (WebSockets, Convex)",
424
+ "Type-safe APIs (tRPC, GraphQL)",
425
+ "Tailwind CSS",
426
+ ],
427
+ salaryRange: { min: 100000, max: 220000, currency: "USD", market: "US Tech" },
428
+ topCompanies: [
429
+ "Vercel", "Stripe", "Shopify", "Notion",
430
+ "Linear", "Figma", "Airbnb", "Netflix",
431
+ ],
432
+ },
433
+ "DevOps Engineer": {
434
+ commonRequirements: [
435
+ { skill: "Linux", frequency: 95 },
436
+ { skill: "Docker/Containers", frequency: 90 },
437
+ { skill: "Kubernetes", frequency: 80 },
438
+ { skill: "CI/CD (GitHub Actions, Jenkins)", frequency: 85 },
439
+ { skill: "IaC (Terraform, Pulumi)", frequency: 75 },
440
+ { skill: "Cloud (AWS/GCP/Azure)", frequency: 90 },
441
+ { skill: "Scripting (Bash, Python)", frequency: 85 },
442
+ { skill: "Monitoring (Datadog, Prometheus)", frequency: 70 },
443
+ ],
444
+ emergingSkills: [
445
+ "Platform Engineering",
446
+ "GitOps (ArgoCD, Flux)",
447
+ "eBPF & Observability",
448
+ "AI Ops",
449
+ "Supply Chain Security",
450
+ "Green Computing",
451
+ ],
452
+ salaryRange: { min: 120000, max: 250000, currency: "USD", market: "US Tech" },
453
+ topCompanies: [
454
+ "HashiCorp", "Datadog", "GitLab", "Cloudflare",
455
+ "Confluent", "Elastic", "MongoDB", "Snowflake",
456
+ ],
457
+ },
458
+ "Product Manager": {
459
+ commonRequirements: [
460
+ { skill: "Product Strategy", frequency: 95 },
461
+ { skill: "User Research", frequency: 85 },
462
+ { skill: "Data Analysis", frequency: 80 },
463
+ { skill: "Roadmap Planning", frequency: 90 },
464
+ { skill: "Stakeholder Management", frequency: 85 },
465
+ { skill: "SQL (basic)", frequency: 60 },
466
+ { skill: "A/B Testing", frequency: 70 },
467
+ { skill: "Agile/Scrum", frequency: 85 },
468
+ ],
469
+ emergingSkills: [
470
+ "AI Product Management",
471
+ "PLG (Product-Led Growth)",
472
+ "AI/ML Literacy",
473
+ "Prompt Engineering (for PMs)",
474
+ "No-code Tools",
475
+ "Growth Experimentation",
476
+ ],
477
+ salaryRange: { min: 130000, max: 280000, currency: "USD", market: "US Tech" },
478
+ topCompanies: [
479
+ "Meta", "Google", "Stripe", "Airbnb",
480
+ "Notion", "Figma", "Spotify", "Slack",
481
+ ],
482
+ },
483
+ };
484
+ // Find closest match
485
+ const normalizedRole = role.toLowerCase();
486
+ let matchedRole = null;
487
+ for (const key of Object.keys(marketData)) {
488
+ if (normalizedRole.includes(key.toLowerCase().split(" ")[0])) {
489
+ matchedRole = key;
490
+ break;
491
+ }
492
+ }
493
+ if (!matchedRole) {
494
+ // Return generic guidance
495
+ return {
496
+ role,
497
+ skills,
498
+ location,
499
+ focus,
500
+ message: "Specific market data not available for this role. Use web_search for real-time job listings.",
501
+ suggestion: `Try: web_search({ query: '"${role}" jobs ${location} requirements 2026' })`,
502
+ genericRecommendation: "Focus on: problem-solving, communication, relevant domain expertise, and emerging technologies in your field.",
503
+ };
504
+ }
505
+ const data = marketData[matchedRole];
506
+ // Score provided skills
507
+ const skillScores = skills.map((skill) => {
508
+ const match = data.commonRequirements.find((r) => r.skill.toLowerCase().includes(skill.toLowerCase()));
509
+ return {
510
+ skill,
511
+ inDemand: !!match,
512
+ frequency: match?.frequency ?? 0,
513
+ };
514
+ });
515
+ const recommendation = skills.length > 0
516
+ ? `Your skills match: ${skillScores.filter((s) => s.inDemand).length}/${skills.length} common requirements. ` +
517
+ `Consider adding: ${data.emergingSkills.slice(0, 3).join(", ")}.`
518
+ : `Top skills for ${matchedRole}: ${data.commonRequirements.slice(0, 5).map((r) => r.skill).join(", ")}.`;
519
+ return {
520
+ role: matchedRole,
521
+ inputRole: role,
522
+ skills: skillScores,
523
+ location,
524
+ focus,
525
+ commonRequirements: data.commonRequirements,
526
+ emergingSkills: data.emergingSkills,
527
+ salaryRange: data.salaryRange,
528
+ topCompanies: data.topCompanies,
529
+ recommendation,
530
+ dataSource: "NodeBench curated market data (2025-2026)",
531
+ forRealTimeData: `Use: web_search({ query: '"${matchedRole}" jobs ${location} 2026' })`,
532
+ };
533
+ },
534
+ },
535
+ {
536
+ name: "setup_local_env",
537
+ description: "Discover and diagnose the local development environment. Checks for available API keys, installed SDKs, Node.js version, package manager, and git status. Returns suggestions for setting up missing components. Use this to help agents bootstrap their tooling or diagnose issues. Fully local — no external calls.",
538
+ inputSchema: {
539
+ type: "object",
540
+ properties: {
541
+ checkSdks: {
542
+ type: "boolean",
543
+ description: "Check if optional SDKs are installed (default: true)",
544
+ },
545
+ includeSetupCommands: {
546
+ type: "boolean",
547
+ description: "Include suggested setup commands (default: true)",
548
+ },
549
+ },
550
+ },
551
+ handler: async (args) => {
552
+ const checkSdks = args.checkSdks ?? true;
553
+ const includeSetupCommands = args.includeSetupCommands ?? true;
554
+ // Gather environment status
555
+ const envStatus = detectEnvStatus();
556
+ const gitStatus = detectGitStatus();
557
+ const nodeVersion = detectNodeVersion();
558
+ const packageManager = detectPackageManager();
559
+ let sdkStatus = {};
560
+ if (checkSdks) {
561
+ sdkStatus = await detectSdkStatus();
562
+ }
563
+ // Check for AGENTS.md
564
+ const agentsMdPath = findAgentsMdPath(process.cwd());
565
+ // Calculate readiness
566
+ const hasAnyApiKey = Object.values(envStatus).some((e) => e.available);
567
+ const hasVisionSdk = sdkStatus["@google/genai"] || sdkStatus["openai"] || sdkStatus["@anthropic-ai/sdk"];
568
+ const hasWebSdk = sdkStatus["cheerio"];
569
+ const hasCaptureSdk = sdkStatus["playwright"];
570
+ const capabilities = {
571
+ canAnalyzeScreenshots: hasVisionSdk && hasAnyApiKey,
572
+ canCaptureScreenshots: hasCaptureSdk,
573
+ canManipulateImages: sdkStatus["sharp"],
574
+ canSearchWeb: (envStatus.GEMINI_API_KEY?.available || envStatus.OPENAI_API_KEY?.available) &&
575
+ (sdkStatus["@google/genai"] || sdkStatus["openai"]),
576
+ canFetchUrls: true, // Native fetch available
577
+ canSearchGitHub: true, // Native fetch, GITHUB_TOKEN optional
578
+ canUpdateAgentsMd: true, // Always available (file system)
579
+ };
580
+ // Generate setup commands
581
+ const setupCommands = [];
582
+ if (includeSetupCommands) {
583
+ const pm = packageManager;
584
+ const installCmd = pm === "npm" ? "npm install" : pm === "yarn" ? "yarn add" : pm === "pnpm" ? "pnpm add" : "bun add";
585
+ if (!sdkStatus["@google/genai"]) {
586
+ setupCommands.push(`# Install Google AI SDK (for Gemini vision + search):`);
587
+ setupCommands.push(`${installCmd} @google/genai`);
588
+ }
589
+ if (!sdkStatus["openai"]) {
590
+ setupCommands.push(`# Install OpenAI SDK:`);
591
+ setupCommands.push(`${installCmd} openai`);
592
+ }
593
+ if (!sdkStatus["cheerio"]) {
594
+ setupCommands.push(`# Install Cheerio (for URL content extraction):`);
595
+ setupCommands.push(`${installCmd} cheerio`);
596
+ }
597
+ if (!sdkStatus["sharp"]) {
598
+ setupCommands.push(`# Install Sharp (for image manipulation):`);
599
+ setupCommands.push(`${installCmd} sharp`);
600
+ }
601
+ if (!sdkStatus["playwright"]) {
602
+ setupCommands.push(`# Install Playwright (for screenshot capture):`);
603
+ setupCommands.push(`${installCmd} playwright`);
604
+ setupCommands.push(`npx playwright install chromium`);
605
+ }
606
+ if (!envStatus.GEMINI_API_KEY?.available && !envStatus.GOOGLE_AI_API_KEY?.available) {
607
+ setupCommands.push(`# Set Gemini API key (recommended for vision + search):`);
608
+ setupCommands.push(`# Get key from: https://aistudio.google.com/apikey`);
609
+ setupCommands.push(`# Then add to .env: GEMINI_API_KEY=your_key_here`);
610
+ }
611
+ if (!agentsMdPath) {
612
+ setupCommands.push(`# Create AGENTS.md (project documentation for AI agents):`);
613
+ setupCommands.push(`# Call: update_agents_md({ operation: "read", createIfMissing: true })`);
614
+ }
615
+ }
616
+ const recommendation = !hasAnyApiKey
617
+ ? "No API keys detected. Set GEMINI_API_KEY for best experience (vision + web search + agentic capabilities)."
618
+ : !hasVisionSdk
619
+ ? "API keys present but SDKs not installed. Run the setup commands to enable all features."
620
+ : "Environment ready. All core capabilities available.";
621
+ return {
622
+ environment: {
623
+ nodeVersion,
624
+ packageManager,
625
+ cwd: process.cwd(),
626
+ gitStatus,
627
+ },
628
+ apiKeys: envStatus,
629
+ sdks: checkSdks ? sdkStatus : "skipped (checkSdks: false)",
630
+ capabilities,
631
+ agentsMd: {
632
+ found: !!agentsMdPath,
633
+ path: agentsMdPath ?? null,
634
+ },
635
+ setupCommands: includeSetupCommands ? setupCommands : "skipped (includeSetupCommands: false)",
636
+ recommendation,
637
+ nextSteps: [
638
+ !hasAnyApiKey ? "Set up API keys (GEMINI_API_KEY recommended)" : null,
639
+ !hasVisionSdk ? "Install AI SDKs (@google/genai recommended)" : null,
640
+ !agentsMdPath ? "Create AGENTS.md for project documentation" : null,
641
+ !sdkStatus["cheerio"] ? "Install cheerio for better URL content extraction" : null,
642
+ ].filter(Boolean),
643
+ };
644
+ },
645
+ },
646
+ ];
647
+ //# sourceMappingURL=documentationTools.js.map