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.
- package/README.md +237 -0
- package/dist/__tests__/tools.test.d.ts +1 -0
- package/dist/__tests__/tools.test.js +402 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/db.d.ts +4 -0
- package/dist/db.js +198 -0
- package/dist/db.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +237 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/documentTools.d.ts +5 -0
- package/dist/tools/documentTools.js +524 -0
- package/dist/tools/documentTools.js.map +1 -0
- package/dist/tools/documentationTools.d.ts +12 -0
- package/dist/tools/documentationTools.js +647 -0
- package/dist/tools/documentationTools.js.map +1 -0
- package/dist/tools/evalTools.d.ts +6 -0
- package/dist/tools/evalTools.js +335 -0
- package/dist/tools/evalTools.js.map +1 -0
- package/dist/tools/financialTools.d.ts +10 -0
- package/dist/tools/financialTools.js +403 -0
- package/dist/tools/financialTools.js.map +1 -0
- package/dist/tools/flywheelTools.d.ts +6 -0
- package/dist/tools/flywheelTools.js +366 -0
- package/dist/tools/flywheelTools.js.map +1 -0
- package/dist/tools/githubTools.d.ts +12 -0
- package/dist/tools/githubTools.js +432 -0
- package/dist/tools/githubTools.js.map +1 -0
- package/dist/tools/learningTools.d.ts +6 -0
- package/dist/tools/learningTools.js +199 -0
- package/dist/tools/learningTools.js.map +1 -0
- package/dist/tools/memoryTools.d.ts +5 -0
- package/dist/tools/memoryTools.js +137 -0
- package/dist/tools/memoryTools.js.map +1 -0
- package/dist/tools/metaTools.d.ts +7 -0
- package/dist/tools/metaTools.js +837 -0
- package/dist/tools/metaTools.js.map +1 -0
- package/dist/tools/planningTools.d.ts +5 -0
- package/dist/tools/planningTools.js +147 -0
- package/dist/tools/planningTools.js.map +1 -0
- package/dist/tools/qualityGateTools.d.ts +6 -0
- package/dist/tools/qualityGateTools.js +347 -0
- package/dist/tools/qualityGateTools.js.map +1 -0
- package/dist/tools/reconTools.d.ts +8 -0
- package/dist/tools/reconTools.js +729 -0
- package/dist/tools/reconTools.js.map +1 -0
- package/dist/tools/searchTools.d.ts +5 -0
- package/dist/tools/searchTools.js +145 -0
- package/dist/tools/searchTools.js.map +1 -0
- package/dist/tools/uiCaptureTools.d.ts +8 -0
- package/dist/tools/uiCaptureTools.js +339 -0
- package/dist/tools/uiCaptureTools.js.map +1 -0
- package/dist/tools/verificationTools.d.ts +6 -0
- package/dist/tools/verificationTools.js +472 -0
- package/dist/tools/verificationTools.js.map +1 -0
- package/dist/tools/visionTools.d.ts +12 -0
- package/dist/tools/visionTools.js +553 -0
- package/dist/tools/visionTools.js.map +1 -0
- package/dist/tools/webTools.d.ts +12 -0
- package/dist/tools/webTools.js +443 -0
- package/dist/tools/webTools.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- 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
|