{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "atomic-write",
  "title": "atomic-write",
  "description": "Write files atomically: temp file, `fsync`, rename. Prevents corruption from crashes or concurrent CLI processes. Windows-aware (unlink before rename). Includes `atomicWriteJson` convenience.",
  "files": [
    {
      "path": "registry/foundation/atomic-write.ts",
      "content": "// cligentic block: atomic-write\n//\n// Write files atomically: write to a temp file, fsync, rename. Prevents\n// corruption from crashes, power loss, or concurrent CLI processes.\n//\n// Design rules:\n//   1. Write to a .tmp sibling file first.\n//   2. fsync before rename to ensure data hits disk.\n//   3. rename() is atomic on POSIX. On Windows, unlink target first.\n//   4. Enforce file mode (default 0o644, 0o600 for secrets).\n//   5. Create parent directories if they don't exist.\n//\n// Usage:\n//   import { atomicWrite, atomicWriteJson } from \"./foundation/atomic-write\";\n//\n//   atomicWrite(\"~/.myapp/config.toml\", tomlString);\n//   atomicWriteJson(\"~/.myapp/session.json\", session, { mode: 0o600 });\n\nimport {\n  closeSync,\n  existsSync,\n  fdatasyncSync,\n  mkdirSync,\n  openSync,\n  renameSync,\n  unlinkSync,\n  writeSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { platform } from \"node:os\";\n\nexport type WriteOptions = {\n  /** File permissions. Default 0o644. Use 0o600 for secrets. */\n  mode?: number;\n  /** Encoding for string content. Default \"utf8\". */\n  encoding?: BufferEncoding;\n};\n\n/**\n * Writes content to a file atomically. The file either contains the old\n * content or the new content, never a partial write.\n *\n * Steps:\n *   1. Create parent directories if missing\n *   2. Write to {path}.tmp\n *   3. fsync to flush to disk\n *   4. On Windows: unlink target if it exists (rename doesn't overwrite)\n *   5. rename .tmp to target (atomic on POSIX)\n */\nexport function atomicWrite(\n  filePath: string,\n  content: string | Buffer,\n  options: WriteOptions = {},\n): void {\n  const { mode = 0o644, encoding = \"utf8\" } = options;\n  const dir = dirname(filePath);\n  const tmpPath = join(dir, `.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`);\n\n  mkdirSync(dir, { recursive: true });\n\n  const data = typeof content === \"string\" ? Buffer.from(content, encoding) : content;\n  const fd = openSync(tmpPath, \"w\", mode);\n  try {\n    writeSync(fd, data);\n    fdatasyncSync(fd);\n  } finally {\n    closeSync(fd);\n  }\n\n  // Windows rename() fails if target exists. Unlink first.\n  if (platform() === \"win32\" && existsSync(filePath)) {\n    unlinkSync(filePath);\n  }\n\n  renameSync(tmpPath, filePath);\n}\n\n/**\n * Convenience: atomically write a JSON-serializable value with 2-space\n * indentation and a trailing newline. Common pattern for config/session files.\n */\nexport function atomicWriteJson(\n  filePath: string,\n  value: unknown,\n  options: WriteOptions = {},\n): void {\n  atomicWrite(filePath, `${JSON.stringify(value, null, 2)}\\n`, options);\n}\n",
      "type": "registry:file",
      "target": "src/cli/foundation/atomic-write.ts"
    }
  ],
  "type": "registry:file"
}