agent-fs

>-

desplega-ai/agent-fs1.2k installsMITSynced Sep 1

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: agent-fs
description: >-
license: MIT
---

# agent-fs CLI

agent-fs is an agent-first filesystem with full versioning, full-text search (FTS5), and semantic search. It provides a CLI that outputs JSON, making it ideal for agent workflows. Files are organized in drives within orgs.

## Storage Backends

agent-fs stores file bytes in a pluggable storage backend. The durable value — version history, comments, and search — lives in SQLite and works identically on every backend.

| Backend | Setup | Versioning tier | `signed-url` |
|---------|-------|-----------------|--------------|
| **S3 / MinIO** (default) | `agent-fs onboard -y` (local MinIO, needs Docker) or `--s3-*` flags for AWS/R2/etc. | Full — `revert` + historical `diff` via S3 object versioning | Real presigned URL (public, time-limited) |
| **Local filesystem** | `agent-fs onboard --filesystem` (no Docker, no S3) | Full — `revert` + historical `diff` via content-addressed blobs on disk | Falls back to an authenticated in-app link (requires sign-in; does not expire) |

Both backends are **full-tier**: every op — including `revert` and historical `diff` — works. Future backends may be **basic-tier** (no object versioning): on those, `revert` and historical `diff` are unavailable and fail cleanly with an `UNSUPPORTED_OPERATION` error (HTTP 422) rather than a raw storage error — current content, listing, comments, and search keep working. Check a backend's capabilities before relying on versioning if you're unsure which backend a drive uses.

## Quick Start

```bash
# 1. Set up (local MinIO — requires Docker)
agent-fs onboard -y

#    ...or with no Docker/S3 at all — store bytes on the local filesystem:
agent-fs onboard --filesystem            # uses ~/.agent-fs/storage
agent-fs onboard --filesystem --storage-root /data/agent-fs   # custom dir

# 2. Optionally start the daemon (CLI auto-detects and works without it)
agent-fs daemon start

# 3. Start using it
echo "hello world" | agent-fs write docs/readme.txt -m "initial version"
agent-fs cat docs/readme.txt
```

For custom S3 (AWS, R2, etc.), use flags: `agent-fs onboard --s3-endpoint <url> --s3-bucket <name> --s3-access-key <key> --s3-secret-key <key>`.

The local-filesystem backend (`--filesystem`, equivalently `--storage local`) needs no Docker and no S3 — bytes are stored under `--storage-root` (default `~/.agent-fs/storage`), with every version content-addressed so `revert` and historical `diff` work the same as on S3.

## just-bash Adapter

Use `@desplega.ai/agent-fs-just-bash` when a `just-bash` environment needs to
read and write through agent-fs as its `fs` implementation.

```ts
import { Bash } from "just-bash";
import { AgentFsFileSystem } from "@desplega.ai/agent-fs-just-bash";

const fs = new AgentFsFileSystem({
  baseUrl: process.env.AGENT_FS_API_URL,
  apiKey: process.env.AGENT_FS_API_KEY,
  orgId: "org_...",
  driveId: "drive_...",
});

const bash = new Bash({ fs, cwd: "/" });
```

The adapter uses `/raw` for byte-safe reads/writes and `/ops` for listing and
metadata. Empty directories are represented by a hidden `.agent-fs-dir` marker;
symlinks are unsupported and throw `EPERM`.

## Essential Patterns

1. **Use JSON for machine output** — pass `--json` when parsing CLI output (except `download` without `-o`, which writes raw bytes to stdout; `daemon status` and `auth register` print human-readable text).

2. **Auto-detection** — the CLI automatically detects whether the daemon is running. If it is, commands go via HTTP; otherwise, they use embedded mode directly. No user action needed.

3. **Stdin and file upload** — `write` accepts raw bytes from stdin or `--file`, and text from `--content`; `append` accepts text via stdin or `--content`:
   ```bash
   # Preferred for multi-line text
   echo "content here" | agent-fs write path/to/file.txt

   # Inline for short content
   agent-fs write path/to/file.txt --content "short text"

   # Binary-safe upload
   agent-fs write assets/screenshot.png --file ./screenshot.png
   ```

4. **Paths** — forward-slash separated, no leading slash required. Example: `docs/notes/meeting.md`

5. **Version messages** — optional but recommended for auditability:
   ```bash
   agent-fs write docs/spec.md --content "..." -m "added API section"
   ```

6. **Optimistic concurrency** — use `--expected-version` on `write` to prevent conflicts:
   ```bash
   agent-fs write config.json --content '{}' --expected-version 3
   # Fails if file is not at version 3
   ```

## Command Quick Reference

### File Operations

| Command | Usage | Description |
|---------|-------|-------------|
| `write` | `agent-fs write <path> [--content <text>] [--file <local-path>] [-m <msg>] [--expected-version <n>]` | Write text or binary bytes |
| `cat` | `agent-fs cat <path> [--offset <n>] [--limit <n>] [--raw]` | Read text file content |
| `edit` | `agent-fs edit <path> --old <text> --new <text> [-m <msg>]` | Find-and-replace in file |
| `append` | `agent-fs append <path> [--content <text>] [-m <msg>]` | Append to file (stdin or --content) |
| `tail` | `agent-fs tail <path> [--lines <n>]` | Last N lines (default: 20) |
| `ls` | `agent-fs ls [path]` | List directory contents (defaults to /) |
| `stat` | `agent-fs stat <path>` | Show file metadata (size, version, timestamps) |
| `tree` | `agent-fs tree [path] [--depth <n>]` | Recursive directory listing |
| `glob` | `agent-fs glob <pattern> [path]` | Find files by pattern (`*.md`, `**/*.md`) |
| `rm` | `agent-fs rm <path>` | Delete a file |
| `mv` | `agent-fs mv <from> <to> [-m <msg>]` | Move or rename a file |
| `cp` | `agent-fs cp <from> <to>` | Copy a file |
| `signed-url` | `agent-fs signed-url <path> [--expires-in <seconds>]` | Generate a download URL. On S3/MinIO: a presigned URL (default 24h, max 7 days, `kind: "presigned"`). On local-FS: an authenticated in-app link (`kind: "app"`, requires sign-in, non-expiring). |
| `download` | `agent-fs download <path> [-o <local-path>]` | Download raw bytes |

`cat` is a paginated viewer, not a raw file reader: without `--limit`, it defaults to the first 200 lines at a TTY, but returns the **whole file** when stdout is piped or redirected (a pipe/redirect almost always means "give me everything"). Any time `cat` returns fewer lines than requested, a `truncated: showing N of M lines (use --limit)` note goes to **stderr** — never stdout, so it never corrupts piped/redirected output. The default (non-`--raw`, TTY) view also prefixes each line with a line number for readability; that prefix is **not** part of the stored bytes. For a complete, byte-exact read — required before parsing as CSV/JSON, or any time line numbers or a partial read would corrupt the data — use `agent-fs cat <path> --raw` or, better, `agent-fs download <path> -o <file>`.

### Versioning

| Command | Usage | Description |
|---------|-------|-------------|
| `log` | `agent-fs log <path> [--limit <n>]` | Show version history |
| `diff` | `agent-fs diff <path> --v1 <n> --v2 <n>` | Diff between versions |
| `revert` | `agent-fs revert <path> --version <n>` | Revert to a previous version |

`log` works on every backend (version metadata is in SQLite). `revert` and historical `diff` (comparing two stored versions) need a **full-tier** backend — S3/MinIO and local-filesystem both qualify. On a basic-tier backend without object versioning, `revert` and historical `diff` fail cleanly with `UNSUPPORTED_OPERATION` (HTTP 422); `diff` then degrades to the stored summary instead of full content.

### Search & Discovery

| Command | Usage | Description |
|---------|-------|-------------|
| `grep` | `agent-fs grep <pattern> <path>` | Regex search in file content |
| `fts` | `agent-fs fts <pattern> [path]` | Full-text search (FTS5) across all files |
| `search` | `agent-fs search <query> [--limit <n>]` | Hybrid search (semantic + keyword, best for general queries) |
| `vec-search` | `agent-fs vec-search <query> [--limit <n>]` | Vector-only semantic search using embeddings |
| `recent` | `agent-fs recent [path] [--since <duration>] [--limit <n>]` | Recent activity (e.g., `--since 24h`) |
| `reindex` | `agent-fs reindex [path]` | Re-index files with failed/missing embeddings |

**When to use which:**
- `grep` — you know the exact pattern and path (regex)
- `fts` — keyword search across all files (fast, FTS5-based)
- `search` — general-purpose search combining keywords and meaning (recommended default)
- `vec-search` — pure semantic search when you want conceptual matches only

### SQL Queries (DuckDB)

| Command | Usage | Description |
|---------|-------|-------------|
| `sql` | `agent-fs sql <query> [-t name=path[:format]]... [--max-rows <n>]` | Run DuckDB SQL over stored documents |

Supported formats: csv, tsv, parquet, xlsx, json, ndjson/jsonl (each also `.gz` except parquet/xlsx), sqlite (`.db`/`.sqlite`/`.sqlite3`), and `.duckdb`. Reference file-format documents directly by quoted drive path inside the query, or bind any document to a table name with `-t`. SQLite/DuckDB databases require a `-t` binding and expose their tables as `<name>.<table>`. Append `:format` to a binding to query documents with non-standard extensions (e.g. `-t logs=/raw/data.txt:csv`). Queries are sandboxed — no host filesystem or network access. Results cap at `--max-rows` (default 1000, max 10000); `truncated: true` in JSON output signals more rows exist.

### Comments

| Command | Usage | Description |
|---------|-------|-------------|
| `comment add` | `agent-fs comment add <path> --body <text> [--line-start <n>] [--line-end <n>]` | Add a comment to a file |
| `comment reply` | `agent-fs comment reply <comment-id> --body <text>` | Reply to a comment |
| `comment list` | `agent-fs comment list [path]` | List comments (with inline replies) |
| `comment get` | `agent-fs comment get <id>` | Get a comment with its replies |
| `comment update` | `agent-fs comment update <id> --body <text>` | Update a comment (author only) |
| `comment delete` | `agent-fs comment delete <id>` | Soft-delete a comment (author only) |
| `comment resolve` | `agent-fs comment resolve <id>` | Resolve a comment |
| `comment notifications` | `agent-fs comment notifications [--unread] [--limit <n>]` | List comment notifications for the current user in the active drive |
| `comment read` | `agent-fs comment read [ids...] [--all]` | Mark selected notification event IDs, or all active-drive notifications, as read |

### Setup & Auth

| Command | Usage | Description |
|---------|-------|-------------|
| `onboard` | `agent-fs onboard [--local] [--filesystem] [--storage <minio\|local>] [--storage-root <dir>] [-y] [--embeddings <provider>]` | Set up agent-fs (storage backend + database + user). `--filesystem` (or `--storage local`) uses an on-disk backend — no Docker/S3; `--storage-root <dir>` sets its directory. |
| `init` | `agent-fs init [--local] [-y]` | Alias for `onboard` |
| `auth register` | `agent-fs auth register <email>` | Register a new user |
| `auth whoami` | `agent-fs auth whoami` | Show current user info |

### Member Management

| Command | Usage | Description |
|---------|-------|-------------|
| `member list` | `agent-fs member list` | List org members (use `--drive <id>` for drive members) |
| `member invite` | `agent-fs member invite <email> --role <role>` | Invite user to org (viewer/editor/admin) |
| `member update-role` | `agent-fs member update-role <email> --role <role>` | Update org role (use `--drive <id>` for drive role) |
| `member remove` | `agent-fs member remove <email>` | Remove from org (use `--drive <id>` for drive only) |

The `--drive` flag is a global option — place it before the subcommand: `agent-fs --drive <id> member list`.

Member commands are admin-gated: org-scoped commands require org `admin`; drive-scoped commands (`--drive <id>`) require drive `admin` or admin of the owning org, and the drive must belong to the current org. Non-admins get a permission error; org/drive IDs outside your memberships return "not found".

### Drive Management

| Command | Usage | Description |
|---------|-------|-------------|
| `drive list` | `agent-fs drive list` | List drives in current org |
| `drive create` | `agent-fs drive create <name>` | Create a new drive (requires org admin) |
| `drive current` | `agent-fs drive current` | Show current drive context |
| `drive invite` | `agent-fs drive invite <email> --role <role>` | Invite user (viewer/editor/admin) |

Drive membership is explicit: `drive list` shows only drives you're a member of. Creating a drive automatically grants you admin membership on it; other users must be invited per drive (or via org invite, which grants access to the default drive).

### Config & Daemon

| Command | Usage | Description |
|---------|-------|-------------|
| `config get` | `agent-fs config get <key>` | Get config value (dot notation: `s3.bucket`) |
| `config set` | `agent-fs config set <key> <value>` | Set config value |
| `config list` | `agent-fs config list` | Show all configuration |
| `config validate` | `agent-fs config validate` | Check S3, database, auth, embeddings health |
| `daemon start` | `agent-fs daemon start` | Start the background daemon |
| `daemon stop` | `agent-fs daemon stop` | Stop the daemon |
| `daemon status` | `agent-fs daemon status` | Check if daemon is running |

### FUSE Mount (Linux only)

Expose all org drives as a Linux FUSE filesystem so agents can use plain shell verbs (`cat`, `grep`, `mv`, `rm`) against agent-fs content. Requires `/dev/fuse` and `SYS_ADMIN` cap; not available on macOS or in gVisor-based sandboxes.

Two topologies are supported:
- **Local mode** (default): helper talks to a local daemon over a Unix socket. Daemon must be running and have an S3 backend configured.
- **Remote mode** (`--remote`): helper talks directly to a remote agent-fs HTTP API. No local daemon required — ideal for sandboxes (sprite, E2B, Hetzner VMs, GitHub Actions runners) that can reach a hosted agent-fs but can't run the full daemon stack.

FUSE writes require the `editor` role or better on the drive — on drives where you're a `viewer`, the mount is read-only for file writes (writes fail with `EACCES`; check `<mount>/.agent-fs/errors.ndjson` for the `PERMISSION_DENIED` record).

| Command | Usage | Description |
|---------|-------|-------------|
| `mount` | `agent-fs mount <path> [--allow-other] [--foreground]` | Mount drives at `<path>` via local daemon (e.g. `/mnt/agent-fs/<drive>/`). |
| `mount --remote` | `agent-fs mount <path> --remote [--api-url <url>] [--api-key <key>]` | Mount against a remote agent-fs HTTP API. Reads `apiUrl`/`apiKey` from `~/.agent-fs/config.json` or `AGENT_FS_API_URL`/`AGENT_FS_API_KEY` env if flags omitted. Prefer env over `--api-key` (the latter exposes the key in `ps`). |
| `umount` | `agent-fs umount <path>` | Unmount the FUSE mountpoint. |
| `mount status` | `agent-fs mount status` | Show whether a mount is active and where. |

## Common Workflows

### Store and retrieve a document

```bash
# Write a document (multi-line via stdin)
cat <<'EOF' | agent-fs write reports/q1-summary.md -m "Q1 summary draft"
# Q1 Summary
Revenue grew 15% quarter over quarter.
EOF

# Read it back
agent-fs cat reports/q1-summary.md

# Check metadata
agent-fs stat reports/q1-summary.md
```

### Search across files

```bash
# Regex search within a specific path
agent-fs grep "revenue|growth" reports/

# Full-text search across all files (FTS5 — fast keyword matching)
agent-fs fts "quarterly revenue"

# Hybrid search (combines keyword + semantic matching — recommended default)
agent-fs search "financial performance metrics" --limit 5

# Vector-only semantic search (conceptual matches only)
agent-fs vec-search "financial performance metrics" --limit 5
```

### Query data files with SQL

```bash
# Query a CSV directly by path
agent-fs sql "SELECT category, sum(amount) AS total FROM '/finance/2026.csv' GROUP BY category" --json

# Join documents of different formats
agent-fs sql "SELECT s.name, t.tag FROM sales s JOIN tags t ON s.id = t.id" \
  -t sales=/data/sales.csv -t tags=/data/tags.parquet

# Query a stored SQLite database (tables exposed as app.<table>)
agent-fs sql "SELECT count(*) FROM app.users" -t app=/backups/app.db

# Pipe a query via stdin
echo "SELECT count(*) FROM '/data/events.ndjson'" | agent-fs sql
```

### Review and revert changes

```bash
# View version history
agent-fs log docs/spec.md --limit 10

# Compare two versions
agent-fs diff docs/spec.md --v1 2 --v2 5

# Revert to version 2
agent-fs revert docs/spec.md --version 2
```

### Comments and collaboration

```bash
# Add a comment to a file
agent-fs comment add docs/spec.md --body "Needs more detail on auth"

# Reply to a comment
agent-fs comment reply <comment-id> --body "Added in v3"

# List comments
agent-fs comment list docs/spec.md

# Check unread notifications (the returned IDs are notification event IDs)
agent-fs comment notifications --unread --limit 20

# Mark selected notifications as read
agent-fs comment read <notification-id> [<notification-id>...]

# Or acknowledge every notification in the active drive
agent-fs comment read --all

# Resolve a comment
agent-fs comment resolve <comment-id>
```

### Set up a new drive and invite users

```bash
# Create a shared drive
agent-fs drive create "team-docs"

# Invite a teammate
agent-fs drive invite alice@company.com --role editor

# Check current drive context
agent-fs drive current
```

### Manage members

```bash
# List org members
agent-fs member list

# List drive members
agent-fs --drive <driveId> member list

# Invite a user
agent-fs member invite alice@company.com --role editor

# Change role
agent-fs member update-role alice@company.com --role admin

# Remove from org (cascades to all drives)
agent-fs member remove alice@company.com

# Remove from a specific drive only (keeps org membership)
agent-fs --drive <driveId> member remove alice@company.com
```

### Check recent activity

```bash
# What changed in the last hour?
agent-fs recent --since 1h

# Recent changes under a specific path
agent-fs recent docs/ --since 24h --limit 20
```

### Generate a shareable download link

```bash
# Default expiry (24 hours)
agent-fs signed-url docs/report.pdf

# Custom expiry (1 hour)
agent-fs signed-url docs/report.pdf --expires-in 3600

# JSON output (useful for agents)
agent-fs signed-url docs/report.pdf --json
# → { "url": "https://...", "path": "/docs/report.pdf", "expiresIn": 86400, "expiresAt": "2026-03-20T..." }
```

On an S3/MinIO backend (`kind: "presigned"`) the URL requires no authentication — anyone with the link can download the file until it expires. Access is RBAC-checked only at generation time (viewer-or-better on the drive); after that the URL is a bearer secret. Don't log it or paste it anywhere you wouldn't paste a credential, and prefer the shortest workable `--expires-in`. Signed URLs serve the correct `Content-Type` header based on file extension (e.g., `application/pdf` for `.pdf`, `image/png` for `.png`), so browsers render them natively.

On a backend without presigned URLs (the local-filesystem backend), `signed-url` does **not** fail — it falls back to an authenticated in-app link (`kind: "app"`, `expiresIn: 0`) of the form `<appUrl>/file/~/<org>/<drive>/<path>`. Unlike a presigned URL this link is **not** a public bearer secret: the daemon's `/raw` route and the web viewer require sign-in, so the recipient must be an authenticated member of the drive. Set `AGENT_FS_APP_URL` (or `appUrl` in config) so the link points at your deployment.

**MIME types on upload:** `write`, `edit`, `append`, and `revert` automatically detect and set the correct `Content-Type` on S3 objects based on file extension. The content type is also stored in the database and visible in `stat` output via the `contentType` field. Raw stdin and `--file` uploads preserve bytes exactly; text search/indexing is applied only when the payload is valid, indexable UTF-8 text.

### App URL in responses

When `AGENT_FS_APP_URL` is set (e.g., `https://live.agent-fs.dev`), file-related ops automatically include an `appUrl` field pointing to the file in the live web app:

```bash
AGENT_FS_APP_URL=https://live.agent-fs.dev agent-fs stat docs/report.pdf --json
# → { ..., "appUrl": "https://live.agent-fs.dev/file/~/org-id/drive-id/docs/report.pdf" }
```

This applies to any op that returns a `path` or `to` field (write, stat, edit, append, rm, cp, mv, signed-url, etc.).

### Validate your setup

```bash
agent-fs config validate
```

### Mount a remote drive from a sandbox

Use `--remote` when the agent is running in a Linux sandbox (sprite, E2B, Hetzner VM, GitHub Actions runner, etc.) that can reach a hosted agent-fs HTTP API but cannot run the full daemon + S3 stack locally.

```bash
# Linux prereqs (run once per sandbox)
sudo apt-get install -y fuse3
sudo chmod 666 /dev/fuse
sudo ln -sf /proc/mounts /etc/mtab
echo user_allow_other | sudo tee -a /etc/fuse.conf

# Auth — either env vars or ~/.agent-fs/config.json
export AGENT_FS_API_URL=https://agent-fs.example.com
export AGENT_FS_API_KEY=<key>

# Mount — no local daemon needed
mkdir -p ~/mnt
agent-fs mount ~/mnt --remote

# Use plain shell verbs against remote content
ls ~/mnt
cat ~/mnt/current/docs/spec.md
echo "edit from sandbox $(date)" > ~/mnt/current/notes.txt

# Unmount
fusermount3 -u ~/mnt
```

See `docs/mounting/` for per-environment guides (sprite, E2B, Hetzner).

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY