install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/node-path-fs-patterns" ~/.claude/skills/intense-visions-harness-engineering-node-path-fs-patterns-f44f43 && rm -rf "$T"
manifest:
agents/skills/codex/node-path-fs-patterns/SKILL.mdsource content
Node.js Path and FS Patterns
Perform file system operations correctly using fs.promises, path utilities, and file watching
When to Use
- Reading, writing, or manipulating files and directories
- Building file paths safely across operating systems
- Watching files for changes (hot reload, build tools)
- Handling file operations asynchronously without blocking the event loop
Instructions
- Always use
for async operations:fs/promises
import { readFile, writeFile, mkdir, readdir, stat, rm } from 'node:fs/promises'; import { join } from 'node:path'; const content = await readFile(join(dir, 'data.json'), 'utf-8'); const data = JSON.parse(content);
- Build paths with
andpath.join
:path.resolve
import { join, resolve, basename, dirname, extname } from 'node:path'; join('src', 'utils', 'helper.ts'); // 'src/utils/helper.ts' resolve('src', 'utils'); // '/absolute/path/to/src/utils' basename('/path/to/file.ts'); // 'file.ts' basename('/path/to/file.ts', '.ts'); // 'file' dirname('/path/to/file.ts'); // '/path/to' extname('file.test.ts'); // '.ts'
Never concatenate paths with string templates — use
join() for OS-safe separators.
- Create directories recursively:
await mkdir(join(outputDir, 'images', 'thumbnails'), { recursive: true });
- Write files safely (atomic write pattern):
import { writeFile, rename } from 'node:fs/promises'; async function writeFileAtomic(filePath: string, content: string) { const tempPath = `${filePath}.tmp`; await writeFile(tempPath, content, 'utf-8'); await rename(tempPath, filePath); // Atomic on most file systems }
- Check if a file exists:
import { access, constants } from 'node:fs/promises'; async function fileExists(path: string): Promise<boolean> { try { await access(path, constants.F_OK); return true; } catch { return false; } }
- List directory contents recursively:
import { readdir } from 'node:fs/promises'; const files = await readdir(srcDir, { recursive: true, withFileTypes: true }); const tsFiles = files .filter((f) => f.isFile() && f.name.endsWith('.ts')) .map((f) => join(f.parentPath, f.name));
- Watch files for changes:
import { watch } from 'node:fs/promises'; const watcher = watch(srcDir, { recursive: true }); for await (const event of watcher) { console.log(`${event.eventType}: ${event.filename}`); }
- Stream large files instead of loading into memory:
import { createReadStream, createWriteStream } from 'node:fs'; import { pipeline } from 'node:stream/promises'; await pipeline( createReadStream('large-input.csv'), transformStream, createWriteStream('output.csv') );
- Clean up with
:rm
await rm(tempDir, { recursive: true, force: true });
Details
node:fs/promises provides Promise-based file system operations. Always prefer it over the callback-based fs and the synchronous fs.*Sync variants.
vs readFile
: Use createReadStream
readFile for files under ~50MB that you need in memory. Use createReadStream for larger files or when you can process data incrementally.
: Available on recursive: true
mkdir (create nested directories), readdir (list all descendants), rm (delete directory trees), and watch (monitor subdirectories). Node.js 20+ supports readdir with recursive and withFileTypes together.
File watching:
fs.watch is OS-dependent and can emit duplicate events. For production file watching, consider chokidar which normalizes behavior across platforms.
Trade-offs:
is non-blocking — but adds async overhead for small operationsfs/promises
handles OS differences — but you must remember to use it consistentlypath.join- Atomic writes prevent corruption — but require temporary file cleanup on failure
onrecursive: true
is convenient — but can be slow on large directory treesreaddir
Source
https://nodejs.org/api/fs.html
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.