desplega-ai/agent-fs

agent-fs

- Use when the user wants to store, retrieve, search, or manage files in agent-fs — an agent-first filesystem backed by S3.

View source
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

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.

BackendSetupVersioning tiersigned-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 versioningReal presigned URL (public, time-limited)
Local filesystemagent-fs onboard --filesystem (no Docker, no S3)Full — revert + historical diff via content-addressed blobs on diskFalls 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).
  1. 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.
  1. Default org/drive resolution — with no --org/--drive flag, the CLI resolves in order: the flag itself, then local config (org switch <id> / drive switch <id>, sticky per machine), then the AGENT_FS_DEFAULT_ORG_ID / AGENT_FS_DEFAULT_DRIVE_ID env vars (a deployment-level hint, e.g. the shared org an agent-swarm worker container is provisioned to write to), then the account's own default org/drive from GET /me (the auto-created personal org, unless changed). Check agent-fs org current / agent-fs drive current (source field) if a write lands somewhere unexpected — a write with no flags always lands on the personal org/drive unless one of the earlier tiers is set.
  1. Stdin and file uploadwrite 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
  1. Paths — forward-slash separated, no leading slash required. Example: docs/notes/meeting.md
  1. Version messages — optional but recommended for auditability:
bash
   agent-fs write docs/spec.md --content "..." -m "added API section"
  1. 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

CommandUsageDescription
writeagent-fs write <path> [--content <text>] [--file <local-path>] [-m <msg>] [--expected-version <n>]Write text or binary bytes
catagent-fs cat <path> [--offset <n>] [--limit <n>] [--raw]Read text file content
editagent-fs edit <path> --old <text> --new <text> [-m <msg>]Find-and-replace in file
appendagent-fs append <path> [--content <text>] [-m <msg>]Append to file (stdin or --content)
tailagent-fs tail <path> [--lines <n>]Last N lines (default: 20)
lsagent-fs ls [path]List directory contents (defaults to /)
statagent-fs stat <path>Show file metadata (size, version, timestamps)
treeagent-fs tree [path] [--depth <n>]Recursive directory listing
globagent-fs glob <pattern> [path]Find files by pattern (*.md, **/*.md)
rmagent-fs rm <path>Delete a file
mvagent-fs mv <from> <to> [-m <msg>]Move or rename a file
cpagent-fs cp <from> <to>Copy a file
signed-urlagent-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).
downloadagent-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

CommandUsageDescription
logagent-fs log <path> [--limit <n>]Show version history
diffagent-fs diff <path> --v1 <n> --v2 <n>Diff between versions
revertagent-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

CommandUsageDescription
grepagent-fs grep <pattern> <path>Regex search in file content
ftsagent-fs fts <pattern> [path]Full-text search (FTS5) across all files
searchagent-fs search <query> [--limit <n>]Hybrid search (semantic + keyword, best for general queries)
vec-searchagent-fs vec-search <query> [--limit <n>]Vector-only semantic search using embeddings
recentagent-fs recent [path] [--since <duration>] [--limit <n>]Recent activity (e.g., --since 24h)
reindexagent-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)

CommandUsageDescription
sqlagent-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

CommandUsageDescription
comment addagent-fs comment add <path> --body <text> [--line-start <n>] [--line-end <n>]Add a comment to a file
comment replyagent-fs comment reply <comment-id> --body <text>Reply to a comment
comment listagent-fs comment list [path]List comments (with inline replies)
comment getagent-fs comment get <id>Get a comment with its replies
comment updateagent-fs comment update <id> --body <text>Update a comment (author only)
comment deleteagent-fs comment delete <id>Soft-delete a comment (author only)
comment resolveagent-fs comment resolve <id>Resolve a comment
comment notificationsagent-fs comment notifications [--unread] [--limit <n>]List comment notifications for the current user in the active drive
comment readagent-fs comment read [ids...] [--all]Mark selected notification event IDs, or all active-drive notifications, as read

Setup & Auth

CommandUsageDescription
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.
initagent-fs init [--local] [-y]Alias for onboard
auth registeragent-fs auth register <email>Register a new user
auth whoamiagent-fs auth whoamiShow current user info

Member Management

CommandUsageDescription
member listagent-fs member listList org members (use --drive <id> for drive members)
member inviteagent-fs member invite <email> --role <role>Invite user to org (viewer/editor/admin)
member update-roleagent-fs member update-role <email> --role <role>Update org role (use --drive <id> for drive role)
member removeagent-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

CommandUsageDescription
drive listagent-fs drive listList drives in current org
drive createagent-fs drive create <name>Create a new drive (requires org admin)
drive currentagent-fs drive currentShow current drive context
drive inviteagent-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

CommandUsageDescription
config getagent-fs config get <key>Get config value (dot notation: s3.bucket)
config setagent-fs config set <key> <value>Set config value
config listagent-fs config listShow all configuration
config validateagent-fs config validateCheck S3, database, auth, embeddings health
daemon startagent-fs daemon startStart the background daemon
daemon stopagent-fs daemon stopStop the daemon
daemon statusagent-fs daemon statusCheck 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).

CommandUsageDescription
mountagent-fs mount <path> [--allow-other] [--foreground]Mount drives at <path> via local daemon (e.g. /mnt/agent-fs/<drive>/).
mount --remoteagent-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).
umountagent-fs umount <path>Unmount the FUSE mountpoint.
mount statusagent-fs mount statusShow 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).