Node.js as a scripting, CLI, automation, and file-processing tool across versions 20, 22, 24, and 26: 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. WHEN: \"Node.js\", \"node\", \"npm\", \"npx\", \"nvm\", \"mjs\", \"cjs\", \"nod...
Pro scans all 11 files and shows the line behind each finding
Scanned 9/24/2026
npx -y skills add chrishuffman5/domain-expert --skill nodejs --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Nodejs?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/chrishuffman5-nodejs)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: nodejs
description: "Node.js as a scripting, CLI, automation, and file-processing tool across versions 20, 22, 24, and 26: 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. 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\". Do NOT use for Express/Fastify/NestJS web servers or REST API framework questions — that's the `backend` plugin. This skill covers Node.js as a scripting/CLI/automation tool, not web-service development."
license: MIT
---
# Node.js CLI & Scripting Expert
This skill covers Node.js for scripting, CLI tools, automation, and file processing across all supported LTS and current versions (20, 22, 24, 26). It has deep coverage of:
- File system operations (node:fs/promises, streams, glob, watch)
- Process management (child_process, spawn, exec, fork, signals)
- OS information gathering (node:os, process.env, process.argv)
- HTTP clients (built-in fetch, undici, AbortController, retry/pagination)
- Data formats (JSON, CSV, YAML, streaming JSON)
- Argument parsing (node:util.parseArgs, commander, yargs, meow)
- Async patterns (Promise.all/allSettled/any, concurrency limiting, generators)
- Building CLI tools (bin field, shebang, npm link, interactive prompts)
- Output formatting (chalk/picocolors, ora spinners, cli-table3, boxen)
- Shell integration (zx, execa, shelljs)
- npm/npx workflow (init, install, scripts, workspaces, audit, publish)
- Built-in test runner (node --test) and watch mode (node --watch)
## How to Approach Tasks
1. **Classify** the request type:
- **File processing** -- Load `references/patterns.md` (file system, streams, data formats)
- **CLI tool building** -- Load `references/cli-tools.md` (bin field, arg parsing, output, npm)
- **API/HTTP scripting** -- Load `references/patterns.md` (fetch, retry, pagination)
- **Shell scripting** -- Load `references/cli-tools.md` (zx, execa, shelljs)
- **Version features** -- Check the Version-specific guidance table below
2. **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+)
- Built-in WebSocket (22+)
- Built-in glob (22+)
- Permission model (experimental 20+)
3. **Load context** -- Read the relevant reference file for detailed patterns.
4. **Recommend ESM** -- Default to ESM (`import`/`export`, `.mjs`) for new projects. Note CJS patterns only when targeting legacy codebases.
5. **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.
6. **Provide runnable examples** -- Include complete, copy-paste-ready code.
## Core Expertise
### File System (node:fs/promises)
```js
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');
```
### Process and OS
```js
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); });
```
### Child Process
```js
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}`)))
);
```
### HTTP (fetch)
```js
// 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);
```
### Argument Parsing (built-in)
```js
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,
});
```
### Async Patterns
```js
// 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);
```
### Stream Processing
```js
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'));
```
## Common Pitfalls
**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.
## Version-specific guidance
| Version | Reference | What's version-specific |
|---|---|---|
| 20 LTS | `references/versions/20.md` | Stable test runner, stable fetch, permission model (experimental), SEA, watch mode |
| 22 LTS | `references/versions/22.md` | require() ESM (experimental), built-in WebSocket, built-in glob, snapshot testing |
| 24 LTS | `references/versions/24.md` | node:sqlite stable, require() ESM stable, URLPattern, npm 11 |
| 26 Current | `references/versions/26.md` | Temporal API default-on, V8 14.6 (Map/WeakMap.getOrInsert, Iterator.concat), Undici 8, legacy _stream_*/writeHeader removed |
## Reference Files
Load 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.
## Scripts
Runnable example scripts demonstrating real-world patterns:
- `scripts/01-system-report.mjs` -- System info report using node:os, node:fs, process
- `scripts/02-api-client.mjs` -- Fetch-based API client with retry, pagination, streaming
- `scripts/03-file-processor.mjs` -- Stream-based CSV-to-JSON file processor with filtering
- `scripts/04-cli-tool.mjs` -- Complete CLI tool with parseArgs, colors, prompts, subcommands
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!