npm package: @fscli/fscli
Description: Run node fs commands from the terminal with familiar method names and arguments.
Latest version: 1.0.0

[![npm version](https://img.shields.io/npm/v/%40fscli%2Ffscli)](https://www.npmjs.com/package/@fscli/fscli)
[![CI](https://github.com/fscli/fscli/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/fscli/fscli/actions/workflows/ci.yaml)
[![codecov](https://codecov.io/gh/fscli/fscli/branch/master/graph/badge.svg)](https://codecov.io/gh/fscli/fscli)
[![license](https://img.shields.io/npm/l/%40fscli%2Ffscli)](LICENSE)

# fscli

`fscli` brings Node `fs` to the terminal with the same method names JavaScript
developers already use in code.

It is intentionally thin: `fscli readFile`, `fscli mkdir`, `fscli rmdir`,
`fscli rm`, `fscli cp`, and the rest all call the matching file system method from
the JavaScript runtime you are already using.

## Compatibility

- Runtimes: Node >= 18
- Module format: ESM CLI package
- Required APIs: Node's or compatible built-in file system APIs
- Implementation note: the current command surface follows the Promise-based
  Node file system exports in the Node version that runs `fscli`

## Goals

- Keep shell usage close to the Node `fs` method surface JavaScript developers
  already know
- Support global installation for daily terminal use
- Work cleanly through `npx` and `bunx` when installed locally
- Accept JSON values for option objects, arrays, booleans, and
  numbers
- Stay small enough to feel like a native terminal tool, not a framework

## Installation

Global install is the recommended setup:

```sh
npm install -g @fscli/fscli
```

Local project install also works:

```sh
npm install --save-dev @fscli/fscli
```

Then run it from the project root with:

```sh
npx fscli readFile ./package.json '{"encoding":"utf8"}'
bunx fscli readFile ./package.json '{"encoding":"utf8"}'
```

Basic entry points:

```sh
fscli
fscli --help
fscli --version
```

## Usage

The command name is the Node file system method name:

```sh
fscli <command> [...positionals]
fscli <command> --<param> <value> ...
fscli --help
fscli --version
```

Run `fscli` or `fscli --help` to print the live command list from the runtime
that is executing it.

Examples:

```sh
fscli rmdir -P ./path/here
fscli readFile ./package.json '{"encoding":"utf8"}'
fscli writeFile ./tmp/hello.txt "hello from fscli" '{"encoding":"utf8"}'
fscli mkdir --path ./tmp/nested --options '{"recursive":true}'
fscli rm --path ./tmp/nested --options '{"recursive":true,"force":true}'
fscli cp --src ./src --dest ./backup --options '{"recursive":true}'
fscli readdir --path . --options '{"withFileTypes":true}'
```

### Argument decoding

CLI values are decoded before they are passed into Node:

- `true` and `false` become booleans
- Numeric strings become numbers
- JSON arrays, JSON objects, and JSON strings are parsed with `JSON.parse`
- Everything else is passed as a plain string

That keeps option objects close to normal JavaScript:

```sh
fscli mkdir ./tmp/cache '{"recursive":true}'
fscli writeFile ./data.json '{"ok":true}' '{"encoding":"utf8"}'
```

If you are on PowerShell, escape inner double quotes inside JSON:

```powershell
fscli mkdir --path .\tmp\nested --options "{\"recursive\":true}"
```

### Output behavior

- Strings are written directly to stdout
- `Uint8Array` results are written as raw bytes
- Everything else is printed with Node's default `console.log` formatting

## Command Reference

This reference reflects the current package on Node `v22.14.0`. `fscli`
positions itself as Node `fs` for your terminal, while the current command
surface follows the Promise-based file system exports available in the runtime
that executes it. Different Node or Bun versions and different runtimes can
change what you see here. For the live list on your machine, run `fscli` or
`fscli --help`.

| Command      | Parameters                | Flags                                                    |
| ------------ | ------------------------- | -------------------------------------------------------- |
| `access`     | `path`, `mode`            | `-P` = `--path`<br>`-M` = `--mode`                       |
| `appendFile` | `path`, `data`, `options` | `-P` = `--path`<br>`-D` = `--data`<br>`-O` = `--options` |
| `chmod`      | `path`, `mode`            | `-P` = `--path`<br>`-M` = `--mode`                       |
| `chown`      | `path`, `uid`, `gid`      | `-P` = `--path`<br>`-U` = `--uid`<br>`-G` = `--gid`      |
| `copyFile`   | `src`, `dest`, `mode`     | `-S` = `--src`<br>`-D` = `--dest`<br>`-M` = `--mode`     |
| `cp`         | `src`, `dest`, `options`  | `-S` = `--src`<br>`-D` = `--dest`<br>`-O` = `--options`  |
| `glob`       | `pattern`, `options`      | `-P` = `--pattern`<br>`-O` = `--options`                 |
| `lchmod`     | `path`, `mode`            | `-P` = `--path`<br>`-M` = `--mode`                       |
| `lchown`     | `path`, `uid`, `gid`      | `-P` = `--path`<br>`-U` = `--uid`<br>`-G` = `--gid`      |
| `link`       | `existingPath`, `newPath` | `-E` = `--existingPath`<br>`-N` = `--newPath`            |
| `lstat`      | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `lutimes`    | `path`, `atime`, `mtime`  | `-P` = `--path`<br>`-A` = `--atime`<br>`-M` = `--mtime`  |
| `mkdir`      | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `mkdtemp`    | `prefix`, `options`       | `-P` = `--prefix`<br>`-O` = `--options`                  |
| `open`       | `path`, `flags`, `mode`   | `-P` = `--path`<br>`-F` = `--flags`<br>`-M` = `--mode`   |
| `opendir`    | `args`                    | `-A` = `--args`                                          |
| `readFile`   | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `readdir`    | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `readlink`   | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `realpath`   | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `rename`     | `oldPath`, `newPath`      | `-O` = `--oldPath`<br>`-N` = `--newPath`                 |
| `rm`         | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `rmdir`      | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `stat`       | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `statfs`     | `path`, `options`         | `-P` = `--path`<br>`-O` = `--options`                    |
| `symlink`    | `target`, `path`, `type_` | `-T` = `--target`<br>`-P` = `--path`<br>`-Y` = `--type_` |
| `truncate`   | `path`, `len`             | `-P` = `--path`<br>`-L` = `--len`                        |
| `unlink`     | `path`                    | `-P` = `--path`                                          |
| `utimes`     | `path`, `atime`, `mtime`  | `-P` = `--path`<br>`-A` = `--atime`<br>`-M` = `--mtime`  |
| `watch`      | `filename`, `options`     | `-F` = `--filename`<br>`-O` = `--options`                |
| `writeFile`  | `path`, `data`, `options` | `-P` = `--path`<br>`-D` = `--data`<br>`-O` = `--options` |

For the most stable usage across Node versions, prefer positional arguments when
you already know the method signature.

## Tests

Run the package test suite with:

```sh
npm test
```

The current suite covers package metadata, publish-facing docs, and real CLI
execution against temporary files and directories.

## Links

- GitHub: <https://github.com/fscli/fscli>
- npm: <https://www.npmjs.com/package/@fscli/fscli>
- Homepage: <https://fscli.github.io/fscli>

## License

Apache-2.0