# Warlock FS > Package: `@warlock.js/fs` > Filesystem primitives for Warlock.js — drop-in replacement for @warlock.js/fs. ## Skills - [hash-files](@warlock.js/fs/hash-files/SKILL.md): Compute hex digests — hashFile / hashFileAsync (streaming for large files), hashFileSmallAsync, hashString, hashBuffer. Defaults to SHA-256; supports sha1 / md5 / sha512. Triggers: `hashFile`, `hashFileAsync`, `hashFileSmallAsync`, `hashString`, `hashBuffer`, `HashAlgorithm`; "fingerprint a file for cache invalidation", "compute SHA-256 checksum", "compare two files for equality", "cache key from request input"; typical import `import { hashFileAsync, hashString } from "@warlock.js/fs"`. Skip: file IO — `@warlock.js/fs/read-and-write-files/SKILL.md`; competing libs `hasha`, `md5-file`, `crypto-hash`; native `node:crypto` `createHash`. - [manage-directories](@warlock.js/fs/manage-directories/SKILL.md): Manage directories + files — ensureDirectory (mkdir -p), list / listFiles / listDirectories, copyFile / copyDirectory, renameFile, unlink (ENOENT-safe), removeDirectory (recursive force). Triggers: `ensureDirectoryAsync`, `listAsync`, `listFilesAsync`, `listDirectoriesAsync`, `copyFileAsync`, `copyDirectoryAsync`, `renameFileAsync`, `unlinkAsync`, `removeDirectoryAsync`; "create directory recursively", "list files in folder", "delete directory recursively", "copy or rename folder", "walk a tree"; typical import `import { ensureDirectoryAsync, listFilesAsync, removeDirectoryAsync } from "@warlock.js/fs"`. Skip: file IO — `@warlock.js/fs/read-and-write-files/SKILL.md`; atomic writes — `@warlock.js/fs/write-atomically/SKILL.md`; competing libs `fs-extra`, `mkdirp`, `rimraf`, `recursive-readdir`, `glob`; native `node:fs/promises`. - [overview](@warlock.js/fs/overview/SKILL.md): Front-door orientation for `@warlock.js/fs` — filesystem primitives (read/write/JSON, dirs, copy/rename/delete, atomic writes, hashing, existence + stats). Two-suffix convention: `*Async` returns Promise, bare name is sync. Single canonical name per operation — no aliases. TRIGGER when: code imports anything from `@warlock.js/fs`; user asks "what does @warlock.js/fs do", "is fs the right package for X", "list all fs helpers", "fs sync vs async convention"; package.json adds `@warlock.js/fs`; user is choosing between fs vs `node:fs/promises`/`fs-extra`/`graceful-fs`. Skip: specific task already known — load the matching task skill directly (`@warlock.js/fs/read-and-write-files/SKILL.md`, `@warlock.js/fs/manage-directories/SKILL.md`, `@warlock.js/fs/write-atomically/SKILL.md`, `@warlock.js/fs/hash-files/SKILL.md`), or the ergonomic async facade (`fs.files.*` / `fs.dirs.*` / `fs.file()` / `fs.dir()`) — `@warlock.js/fs/use-the-fs-facade/SKILL.md`; the user is using plain `node:fs` and not touching fs imports. - [read-and-write-files](@warlock.js/fs/read-and-write-files/SKILL.md): Read and write files — getFile / getFileAsync / getJsonFile / putFile (auto-creates parent dirs), plus pathExists / fileExists / directoryExists / lastModified / stats. Triggers: `getFileAsync`, `getJsonFileAsync`, `putFileAsync`, `putJsonFileAsync`, `pathExists`, `fileExists`, `lastModifiedAsync`, `statsAsync`; "read a text file", "write a JSON file", "check if file exists"; typical import `import { getFileAsync, putJsonFileAsync, fileExists } from "@warlock.js/fs"`. Skip: atomic writes — `@warlock.js/fs/write-atomically/SKILL.md`; dirs + copy + delete — `@warlock.js/fs/manage-directories/SKILL.md`; hashing — `@warlock.js/fs/hash-files/SKILL.md`; competing libs `fs-extra`, `jsonfile`, `graceful-fs`; native `node:fs/promises`. - [use-the-fs-facade](@warlock.js/fs/use-the-fs-facade/SKILL.md): Use the ergonomic `fs` shorthand facade in `@warlock.js/fs` — `fs.files.*` (file ops), `fs.dirs.*` (directory ops), and lazy `fs.file(path)` / `fs.dir(path)` handles (`File` / `Directory`), plus `fs.exists(path)`. Async-only (delegates to the `*Async` primitives). Adds ops the flat primitives lack: `append`/`prepend`/`appendLine`/`appendJsonLine`, `size`/`isEmpty`/`count`, `ensure`(ensureFile)/`touch`/`empty`(emptyDir), EXDEV-safe `move`, `walk` + recursive `list*`, `readLines`, `edit`/`editJson`/`mergeJson`/`ensureJson`, `getJson({ schema })` (any Standard Schema — seal/zod/valibot), `checksumMatches`, directory `hash`, handle path helpers (`name`/`extension`/`basename`/`parent`/child `file`/`dir`). TRIGGER when: code uses `fs.files.`/`fs.dirs.`/`fs.file(`/`fs.dir(`; user wants a "read-modify-write"/patch a file or JSON, "merge into a config json", "list files recursively", "walk a directory", "get-or-create a json", "append a log line/NDJSON", "directory size/fingerprint", "File/Directory object"; user asks the ergonomic/OO way to do fs. Skip: sync/one-shot code — use the bare primitives (`getFile`/`putFile`/…) via `read-and-write-files` / `manage-directories`; path sandboxing/uploads — that is the storage layer, not fs. - [write-atomically](@warlock.js/fs/write-atomically/SKILL.md): Atomic file writes via atomicWriteAsync(path, content) — writes to a uniquely-named sibling temp + rename onto target so readers see old or complete new content, never half-written. Triggers: `atomicWriteAsync`, `atomicWriteJsonAsync`; "atomic file write", "write config file safely with concurrent readers", "manifest written by build step", "state file across runs", "avoid half-written files"; typical import `import { atomicWriteAsync, atomicWriteJsonAsync } from "@warlock.js/fs"`. Skip: plain writes — `@warlock.js/fs/read-and-write-files/SKILL.md`; read-modify-write locking — `@warlock.js/cache/use-cache-lock/SKILL.md`; competing libs `write-file-atomic`, `steno`, `fs-extra` `outputFile`.