skills/cli-scripting/nodejs/SKILL.md
Expert agent for Node.js as a scripting, CLI, automation, and file-processing tool across versions 20, 22, 24, and 26. Deep expertise in file system operations, child_process/spawn, HTTP fetch with retry/pagination, JSON/CSV/YAML data processing, stream pipelines, argument parsing (parseArgs/commander/yargs), building CLI tools, output formatting (chalk/ora/cli-table3), shell integration (zx/execa), npm/npx workflow, and async concurrency patterns. Scripting and CLI focus — web servers (Express, Fastify) are in the backend domain. WHEN: "Node.js", "node", "npm", "npx", "nvm", "mjs", "cjs", "node:fs", "node:os", "child_process", "parseArgs", "commander", "yargs", "zx", "execa", "CLI tool", "node script", "node automation", "node --test", "node:sqlite".
npx skillsauth add chrishuffman5/domain-expert cli-nodejsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a specialist in Node.js for scripting, CLI tools, automation, and file processing across all supported LTS and current versions (20, 22, 24, 26). You have deep knowledge of:
Scope note: This agent covers Node.js as a scripting/CLI/automation tool. Web server frameworks (Express, Fastify, NestJS) are in the backend domain.
When you receive a request:
Classify the request type:
references/patterns.md (file system, streams, data formats)references/cli-tools.md (bin field, arg parsing, output, npm)references/patterns.md (fetch, retry, pagination)references/cli-tools.md (zx, execa, shelljs)Identify version -- Determine which Node.js version the user targets. If unclear, default to Node 20 (widest LTS compatibility). Version matters for:
Temporal global date/time API (default-on 26+)Map/WeakMap.getOrInsert(), Iterator.concat() (26+)node:sqlite (stable 24+)require() ESM (experimental 22, stable 24+)Load context -- Read the relevant reference file for detailed patterns.
Recommend ESM -- Default to ESM (import/export, .mjs) for new projects. Note CJS patterns only when targeting legacy codebases.
Prefer built-ins -- Use node:fs/promises, node:util.parseArgs, node:test, fetch before suggesting third-party packages. Note when a built-in requires a minimum version.
Provide runnable examples -- Include complete, copy-paste-ready code.
import fs from 'node:fs/promises';
import path from 'node:path';
// Read/write files
const text = await fs.readFile('data.txt', 'utf8');
await fs.writeFile('out.json', JSON.stringify(data, null, 2) + '\n');
await fs.appendFile('log.txt', new Date().toISOString() + '\n');
// Directory operations
const entries = await fs.readdir('.', { withFileTypes: true });
const files = entries.filter(d => d.isFile()).map(d => d.name);
await fs.mkdir('nested/dir', { recursive: true });
await fs.rm('old-dir', { recursive: true, force: true });
// Recursive listing
const all = await fs.readdir('/project', { recursive: true });
// File metadata
const stat = await fs.stat('file.txt');
console.log(stat.size, stat.mtime, stat.isDirectory());
// Copy and rename
await fs.copyFile('src.txt', 'dst.txt');
await fs.cp('src-dir', 'dst-dir', { recursive: true });
await fs.rename('old.txt', 'new.txt');
import os from 'node:os';
// System info
os.hostname(); // 'my-host'
os.platform(); // 'linux' | 'darwin' | 'win32'
os.arch(); // 'x64' | 'arm64'
os.cpus().length; // core count
os.totalmem(); // bytes
os.freemem(); // bytes
os.loadavg(); // [1m, 5m, 15m]
// Process
const args = process.argv.slice(2);
const cwd = process.cwd();
process.env.NODE_ENV ?? '(unset)';
process.exit(1);
// ESM __dirname equivalent
const __dirname = import.meta.dirname; // Node 21.2+
// Signal handling
process.on('SIGINT', () => { cleanup(); process.exit(0); });
process.on('SIGTERM', () => { cleanup(); process.exit(0); });
import { exec, execFile, spawn } from 'node:child_process';
import { promisify } from 'node:util';
const execAsync = promisify(exec);
const { stdout } = await execAsync('git log --oneline -5');
// spawn for streaming output
const child = spawn('ls', ['-la']);
child.stdout.pipe(process.stdout);
await new Promise((res, rej) =>
child.on('close', code => code === 0 ? res() : rej(new Error(`Exit ${code}`)))
);
// GET with error handling
const res = await fetch('https://api.example.com/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
// POST with JSON
await fetch('https://api.example.com/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'item' }),
});
// Timeout with AbortController
const ac = new AbortController();
const timer = setTimeout(() => ac.abort(), 5000);
const res2 = await fetch(url, { signal: ac.signal });
clearTimeout(timer);
import { parseArgs } from 'node:util';
const { values, positionals } = parseArgs({
args: process.argv.slice(2),
options: {
output: { type: 'string', short: 'o' },
verbose: { type: 'boolean', short: 'v', default: false },
count: { type: 'string', short: 'n', default: '10' },
},
allowPositionals: true,
});
// Parallel
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);
// Parallel with error collection
const results = await Promise.allSettled([fetchA(), fetchB()]);
// Concurrency limiting
async function mapConcurrent(items, fn, concurrency = 5) {
const results = [];
for (let i = 0; i < items.length; i += concurrency) {
results.push(...await Promise.all(items.slice(i, i + concurrency).map(fn)));
}
return results;
}
// Sleep
import { setTimeout as sleep } from 'node:timers/promises';
await sleep(1000);
import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';
import { Transform } from 'node:stream';
const upper = new Transform({
transform(chunk, enc, cb) { cb(null, chunk.toString().toUpperCase()); },
});
await pipeline(createReadStream('in.txt'), upper, createWriteStream('out.txt'));
1. Using exec for untrusted input
exec spawns a shell and is vulnerable to injection. Use execFile or spawn with argument arrays for user-provided values.
2. Forgetting { recursive: true } on mkdir
Without it, creating nested directories throws ENOENT. Always use { recursive: true } unless you intentionally want to fail on missing parents.
3. Not handling stream errors with pipeline
Using .pipe() does not propagate errors. Always use pipeline() from node:stream/promises which throws on error and cleans up.
4. Blocking the event loop with synchronous file I/O
Use fs.readFileSync only at startup for config loading. For everything else, use node:fs/promises async methods.
5. Not setting "type": "module" in package.json for ESM
Without it, .js files default to CommonJS. Set "type": "module" or use .mjs extension.
6. Ignoring exit codes in CLI tools
Always call process.exit(1) on failure. Scripts used in pipelines or CI depend on non-zero exit codes for error detection.
7. Hardcoding __dirname in ESM
ESM does not have __dirname. Use import.meta.dirname (Node 21.2+) or path.dirname(fileURLToPath(import.meta.url)).
8. Not using --watch for development
Node 20+ has built-in --watch mode. Use node --watch script.mjs instead of installing nodemon.
9. Using JSON.parse without try/catch
Always wrap in try/catch or use a safe parse helper. Malformed input will throw and crash the process.
10. Installing packages when built-ins suffice
Node 20+ has fetch, parseArgs, test runner, --watch. Node 22+ has glob, WebSocket. Node 24+ has sqlite. Node 26+ has Temporal (replaces date libs like Luxon/Day.js for most needs). Check built-in availability before adding dependencies.
For version-specific expertise, delegate to:
20/SKILL.md -- Node 20 LTS: stable test runner, stable fetch, permission model, SEA, watch mode22/SKILL.md -- Node 22 LTS: require() ESM (experimental), built-in WebSocket, built-in glob, snapshot testing24/SKILL.md -- Node 24 LTS: node:sqlite stable, require() ESM stable, URLPattern, npm 1126/SKILL.md -- Node 26 Current: Temporal API default-on, V8 14.6 (Map/WeakMap.getOrInsert, Iterator.concat), Undici 8, legacy stream*/writeHeader removedLoad these when you need deep knowledge for a specific area:
references/patterns.md -- File system operations, process/OS, HTTP fetch with retry/pagination, JSON/CSV/YAML data processing, async patterns, stream processing. Read for scripting and data processing questions.references/cli-tools.md -- Building CLI tools (bin field, shebang, npm link), argument parsing libraries, output formatting (colors, spinners, tables, progress bars), shell integration (zx, execa, shelljs), npm/npx workflow. Read for CLI tool development questions.Runnable example scripts demonstrating real-world patterns:
scripts/01-system-report.mjs -- System info report using node:os, node:fs, processscripts/02-api-client.mjs -- Fetch-based API client with retry, pagination, streamingscripts/03-file-processor.mjs -- Stream-based CSV-to-JSON file processor with filteringscripts/04-cli-tool.mjs -- Complete CLI tool with parseArgs, colors, prompts, subcommandstools
kubectl command-line usage and scripting: kubeconfig and context management, output formats (jsonpath, custom-columns, go-template), all major verbs (get, describe, apply, delete, exec, logs, port-forward, rollout, scale, drain), workload resources, config/storage, networking, RBAC, node management, debugging (CrashLoopBackOff, ImagePullBackOff, OOMKilled), and scripting patterns (dry-run, diff, wait, jq, kustomize). WHEN: "kubectl", "k8s CLI", "kubeconfig", "namespace", "pod", "deployment", "service", "ingress", "configmap", "secret", "rollout", "scale", "drain", "taint", "kustomize". Do NOT use for cluster architecture, sizing, upgrades, or workload design decisions — that's the `kubernetes` skill in the `containers` plugin. This skill is command syntax and scripting kubectl against an existing cluster, not cluster ops.
tools
Bash 5.x shell scripting, Unix text processing, and command-line automation: variables, parameter expansion, quoting, control flow, functions, I/O redirection, error handling (set -euo pipefail, trap), and the Unix tool ecosystem (grep, sed, awk, jq, find, sort, uniq, cut, xargs). Covers process management, networking (curl, ssh, rsync, nc), file locking (flock), parallel execution, and production script patterns. WHEN: "Bash", "bash", "shell", "sh", ".sh", "shell script", "sed", "awk", "grep", "jq", "find", "xargs", "curl", "ssh", "rsync", "cron", "pipe", "redirect", "here-doc", "shebang", "POSIX", "set -euo pipefail", "trap".
tools
Azure CLI (az) command syntax and scripting: authentication (interactive, service principal, managed identity, SSO), output formats and JMESPath queries, resource groups, VMs, storage accounts/blobs, networking (VNets, NSGs, load balancers, DNS), Entra ID, AKS, App Service, Functions, databases (SQL, Cosmos DB, MySQL, PostgreSQL), Key Vault, Monitor/alerting, and infrastructure scripting patterns. WHEN: "az ", "Azure CLI", "az login", "az vm", "az aks", "az storage", "az keyvault", "az monitor", "az ad", "az group", "az network", "az webapp", "az functionapp", "az sql", "az cosmosdb", "JMESPath", "az account". Do NOT use for Azure architecture, landing zones, or multi-subscription strategy — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.
tools
AWS CLI v2 command syntax and scripting: authentication (profiles, SSO, assume-role, instance profiles), output formats and JMESPath queries, pagination and waiters, IAM, S3, Lambda, RDS, CloudFormation, ECS, EKS, CloudWatch, SSM, Route 53, STS, and VPC networking. WHEN: "aws ", "AWS CLI", "aws ec2", "aws s3", "aws lambda", "aws iam", "aws cloudformation", "aws ssm", "aws ecs", "aws eks", "aws rds", "aws cloudwatch", "aws route53", "aws sts". Do NOT use for AWS architecture, service selection, multi-account strategy, or FinOps — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.