Bidirectional sync between your local command line agent and Boxel workspaces.
Edit Boxel cards locally with your IDE or AI agent, sync changes instantly, and collaborate seamlessly between web UI and local development.
Note: Boxel CLI is developed and tested with Claude Code. For the best experience, install Claude Code first and let it guide you through setup.
Boxel CLI uses two related but distinct terms:
- Realm — Server-side concept. A realm is the data store on the server that holds files, cards, and indexes. Use "realm" when talking about server operations, repair, and configuration. Commands:
boxel realms,boxel doctor repair-realm. - Workspace — User-interface concept. A workspace is how users interact with realms through the Boxel web UI. Matrix manages the user's workspace list. Commands:
boxel workspace-list,boxel create.
| Context | Term | Example |
|---|---|---|
| Server data store | Realm | .realm.json, boxel realms add |
| UI workspace picker | Workspace | boxel workspace-list, boxel create |
| Local config for multi-realm dev | Realm | .boxel-workspaces.json |
| Matrix account data | Workspace | app.boxel.realms workspace list |
Boxel CLI supports:
- macOS
- Linux
- Windows (PowerShell / Command Prompt)
Requirements:
- Node.js 18+
- Git
For boxel share PR creation, install GitHub CLI (gh):
- Windows:
winget install GitHub.cli - macOS:
brew install gh - Linux: see cli.github.com
git clone https://github.com/cardstack/boxel-cli.git
cd boxel-cli
npm install && npm run buildNow you can use npx boxel (or boxel after npm link):
npx boxel profile add # Set up your account
npx boxel workspace-list # List your workspaces
npx boxel pull <workspace-url> # Pull a workspace to ~/boxel-workspaces/If you have Claude Code installed, just start it in the repo:
claudeClaude will detect the Boxel CLI project and guide you through setup automatically.
To use boxel directly without npx:
npm link
boxel profile add
boxel workspace-list
boxel sync .- Bidirectional Sync - Push local changes, pull remote updates, resolve conflicts
- Track Mode - Auto-checkpoint local edits as you type in your IDE
- Watch Mode - Auto-pull server changes with configurable polling
- Checkpoint History - Git-based undo/restore with automatic snapshots
- Multi-Realm Support - Manage multiple workspaces (code + data separation)
- GitHub Integration - Share/gather for team collaboration via PRs
- Claude Code Integration - AI-assisted development with Boxel skills
- Edit Locking - Prevent overwrites while editing locally
- Profile Management - Switch between production and staging environments
Boxel CLI uses git in two fundamentally different ways:
- Checkpoint System (
.boxel-history/) - Local-only git for undo/restore. Single-user, no remotes, continuous backup. Combined with server sync, can also act as a checkpoint / undo system for hosted Boxel usage. - GitHub Collaboration (
share/gather) - Traditional branch/merge/PR workflow for team collaboration.
This separation is intentional: Boxel Server is the source of truth for your realm, not git.
flowchart TB
subgraph cloud["☁️ Cloud Services"]
boxel["🟣 Boxel Server<br/>(Source of Truth)"]
github["🐙 GitHub Repo<br/>(Team Collaboration)"]
end
subgraph local["💻 Local Machine"]
workspace["📁 Working Folder<br/>/my-workspace"]
history["📍 .boxel-history/<br/>(Local Git)"]
end
%% Sync flow
boxel <-->|"sync<br/>push/pull"| workspace
boxel -->|"watch<br/>(pull server changes)"| workspace
%% Track flow (local file watching)
workspace -->|"track<br/>(monitor local edits)"| history
history -->|"restore<br/>history -r"| workspace
%% GitHub flow
workspace -->|"share<br/>(export)"| github
github -->|"gather<br/>(import)"| workspace
%% Styling
style boxel fill:#7c3aed,color:#fff
style github fill:#24292e,color:#fff
style workspace fill:#3b82f6,color:#fff
style history fill:#6b7280,color:#fff
| Aspect | .boxel-history/ (Checkpoints) |
GitHub (Collaboration) |
|---|---|---|
| Purpose | Undo/restore safety net | Team code review & merge |
| Scope | Single user | Multiple collaborators |
| Remote | None (local only) | GitHub origin |
| Workflow | Automatic on sync/watch | Manual share/gather |
| Branches | Single linear history | Feature branches + PRs |
| Source of truth | Boxel Server | Boxel Server (via gather→sync) |
┌─────────────────────────────────────────────────────────────────┐
│ INDIVIDUAL WORKFLOW (You + Boxel) │
│ │
│ Boxel Web UI ◄───────► Boxel Server ◄────────► Local IDE │
│ edit sync/push/pull edit │
│ │ │ │
│ watch ◄──┘ │ │
│ (pull server changes) │ │
│ │ │
│ track │ │
│ (local edits)│ │
│ ▼ │
│ .boxel-history/ │
│ (checkpoint/restore) │
└─────────────────────────────────────────────────────────────────┘
│
share ↓ ↑ gather
│
┌─────────────────────────────────────────────────────────────────┐
│ TEAM WORKFLOW (Collaboration via GitHub) │
│ │
│ Developer A ──► branch ──► PR ──► review ──► merge │
│ Developer B ──► gather ◄────────────────────┘ │
│ │ │
│ ▼ │
│ sync --prefer-local │
│ │ │
│ ▼ │
│ Boxel Server │
└─────────────────────────────────────────────────────────────────┘
Key insight: Git is NOT the source of truth. Boxel Server is. Git serves two separate roles:
- Locally: Time machine for your work (checkpoints)
- GitHub: Collaboration layer for team publishing
boxel profile add # Interactive setup (recommended)
boxel profile list # Show profiles (★ = active)
boxel profile switch username # Switch profile
# Non-interactive (CI/automation only - avoid in shell history)
BOXEL_PASSWORD="pass" boxel profile add -u @user:boxel.ai -n "Prod"Security Note: Avoid passing passwords directly via
-pflag as they may be exposed in shell history and process listings. Use the interactive wizard or environment variables for credentials.
Profiles stored in ~/.boxel-cli/profiles.json
# Production (app.boxel.ai)
MATRIX_URL=https://matrix.boxel.ai
MATRIX_USERNAME=your-username
MATRIX_PASSWORD=your-password
# Staging (realms-staging.stack.cards)
MATRIX_URL=https://matrix-staging.stack.cards
MATRIX_USERNAME=your-username
MATRIX_PASSWORD=your-passwordPull, Push, and Sync relationship:
| Command | Direction | Purpose | Deletes Local | Deletes Remote |
|---|---|---|---|---|
pull |
Remote → Local | Fresh download | with --delete |
never |
push |
Local → Remote | Deploy changes | never | with --delete |
sync |
Both ways | Stay in sync | with --prefer-remote |
with --prefer-local |
boxel sync . # Bidirectional sync (interactive)
boxel sync . --prefer-local # Keep local on conflicts, sync deletions to server
boxel sync . --prefer-remote # Keep remote on conflicts
boxel sync . --prefer-newest # Keep newest by timestamp
boxel sync . --delete # Sync deletions both ways
boxel sync . --dry-run # Preview only
boxel push ./local <url> # One-way push (local → remote)
boxel push ./local <url> --delete # Push and remove orphaned remote files
boxel push ./local <url> --batch # Atomic batch upload (10 files per batch)
boxel push ./local <url> --batch --batch-size 25 # Custom batch size
boxel pull <url> ./local # One-way pull (remote → local)Note:
boxel pullwrites.boxel-sync.jsonautomatically after a fresh download, so you can runboxel sync .immediately against a freshly-pulled workspace with no extra setup.
--batchmode (push only):.gtsdefinitions upload individually in dependency order, then.jsoninstances batch through the server's/_atomicendpoint in groups of N. Meaningfully faster on big pushes (50+ files) and reduces UI flashing during server re-indexing. Binary files (images, fonts) and plain-text files (.md,.csv,.yaml) are routed to per-file POST regardless of mode, because/_atomiconly accepts card and source resource types.
Failed download cleanup: When sync encounters files that return 500 errors (broken on server), it will prompt you to delete them:
⚠️ 3 file(s) failed to download (server error):
- Staff/broken-card.json
- Student/corrupted.json
These files may be broken on the server. Delete them from remote? [y/N]
Safety tip: Before any destructive operation (deleting files, restoring checkpoints), create a checkpoint with a descriptive message:
boxel history . -m "Before cleanup: removing broken server files"
# Track LOCAL file changes (checkpoint as you edit in IDE)
boxel track . # Track local edits, auto-checkpoint
boxel track . --push # Track AND push to server (real-time sync)
boxel track . -d 5 -i 30 # 5s debounce, 30s min between checkpoints
boxel track . -q # Quiet mode
boxel track . -v # Verbose mode (debug output)
# Watch REMOTE server changes (pull external updates)
boxel watch . # Watch single workspace (30s default)
boxel watch . ./other-realm # Watch multiple realms
boxel watch # Watch all configured realms
boxel watch . -i 5 -d 3 # 5s interval, 3s debounce
boxel watch . -q # Quiet mode
# Stop all watchers and trackers
boxel stop # Stops all running watch (⇅) and track (⇆) processes
boxel status . # Show sync status
boxel status --all # All workspaces
boxel status . --pull # Auto-pull changesTrack vs Watch:
| Command | Symbol | Direction | Purpose |
|---|---|---|---|
track |
⇆ | Local → Checkpoints | Backup your IDE edits as you type |
track --push |
⇆→ | Local → Server | Real-time sync with batch upload |
watch |
⇅ | Server → Local | Pull external changes from Boxel web UI |
boxel history . # View checkpoint history
boxel history . -r # Interactive restore
boxel history . -r 3 # Quick restore to #3
boxel history . -r abc123 # Restore by hash
boxel history . -m "Message" # Create checkpoint with message
boxel milestone . 1 -n "v1.0" # Mark checkpoint as milestone
boxel milestone . --list # Show milestonesboxel edit . file.gts # Lock file before editing
boxel edit . --list # Show locked files
boxel edit . --done file.gts # Release lock
boxel edit . --clear # Clear all locks
boxel touch . # Force re-index all files
boxel touch . Card/instance.json # Touch specific file
boxel check ./file.json # Inspect file sync state
boxel check ./file.json --sync # Auto-sync if neededboxel workspace-list # List your workspaces (alias: boxel list)
boxel workspace-list --all-accessible # Include all accessible realms (even hidden)
boxel workspace-list --hidden # Only realms not in your UI workspace list
boxel create my-app "My App" # Create new workspace
boxel remove https://realms-staging.stack.cards/user/my-app/ # Soft remove from your account listRarely-needed maintenance commands live under boxel doctor:
boxel doctor repair-realm <url> # Repair one realm's .realm.json + index.json
boxel doctor repair-realms # Batch repair all your realms
boxel doctor consolidate-workspaces # Fix workspace dirs (defaults to ~/boxel-workspaces/)
boxel doctor force-reindex . # Force server to re-index files (workaround)Realm repair fixes .realm.json metadata (name, icon, background) and optionally
repairs index.json/cards-grid.json. Use --fix-index to enable index repair.
Batch mode (repair-realms) defaults to config-only repair (no index overwrite).
# Preview one realm repair
boxel doctor repair-realm https://realms-staging.stack.cards/ctse/odd-sheep/ --dry-run
# Repair with index fix
boxel doctor repair-realm https://realms-staging.stack.cards/ctse/odd-sheep/ --fix-index
# Batch repair all owned realms (config only, no index overwrite)
boxel doctor repair-realms
# Batch repair with explicit options
boxel doctor repair-realms --owner ctse --include-personal --match-endpointboxel realms init # Create .boxel-workspaces.json
boxel realms list # Show configuration (default)
boxel realms add ./code --purpose "Definitions" --patterns "*.gts" --default
boxel realms add ./data --purpose "Content" --card-types "Post,Product"
boxel realms llm # Output file placement guidance
boxel realms remove ./code # Remove realmboxel share . -t /path/to/repo -b branch-name # Export to GitHub
boxel share . -t /repo -b branch --no-pr # No auto-PR
boxel gather . -s /path/to/repo # Import from GitHub
boxel gather . -s /repo --branch feature # From specific branchboxel skills --refresh # Fetch skills from Boxel
boxel skills --list # List available
boxel skills --enable "Name" # Enable skill
boxel skills --export . # Export to .claude/commands/boxel track . # Start tracking local edits
# In another terminal or IDE, edit files...
# Checkpoints created automatically as you save
# IMPORTANT: Track creates LOCAL checkpoints only!
# When ready to push changes to Boxel server:
boxel sync . --prefer-local # Push changes to serverRemember: track does NOT sync to server - it only creates local checkpoints for safety. Always run sync --prefer-local when you want your changes live.
boxel track . --push # Track AND push to server automatically
# Edit files in IDE - changes sync to Boxel server in real-timeWith --push: Uses batch upload via /_atomic endpoint for efficient multi-file uploads. Definitions (.gts) are sorted before instances (.json) to ensure proper indexing on the server.
boxel edit . my-card.gts # Lock file (if watch is running)
# ... edit locally ...
boxel sync . --prefer-local # Push changes
boxel touch . Card/instance.json # Force re-index
boxel edit . --done my-card.gts # Release lock# STOP watch first (Ctrl+C)
boxel history . # Find checkpoint
boxel history . -r 3 # Restore to #3
boxel sync . --prefer-local # CRITICAL: sync deletions to serverboxel watch . -i 30 -d 10 # 30s poll, 10s debounce
# Checkpoints created automatically
boxel history . # Review what changed# Share to GitHub
boxel share . -t /path/to/repo -b feature/my-work --no-pr
# Push via GitHub Desktop
# Teammate gathers
boxel gather . -s /path/to/repo --branch feature/my-work
boxel sync . --prefer-local # Push to Boxel serverAlways create a checkpoint with a descriptive message before:
- Deleting files from server (
--prefer-local,push --delete) - Restoring to an earlier checkpoint
- Bulk cleanup operations
boxel history . -m "Before cleanup: removing sample data"
# Now safe to proceed with destructive operation
boxel sync . --prefer-localThis ensures you can always recover if something goes wrong.
boxel history . -r 3 # Deletes files locally
boxel sync . --prefer-local # Syncs deletions to serverWithout this, deleted files won't be removed from server.
Watch will re-pull deleted files if running during restore.
boxel edit . file.gts # Lock before editing
# ... edit ...
boxel sync . --prefer-local
boxel edit . --done file.gts # Release after sync# After updating card-def.gts remotely
boxel touch . CardDef/instance.json # Force re-indexWhen editing .gts files, write clean source code:
export class MyCard extends CardDef {
static fitted = class Fitted extends Component<typeof MyCard> {
<template>
<div>...</div>
<style scoped>...</style>
</template>
};
}NEVER write compiled JSON blocks or base64-encoded imports.
The CLI stores synced workspaces under ~/boxel-workspaces/ by default, organized by realm-server-hostname/username/realm:
~/boxel-workspaces/ # Default root (all platforms)
├── app.boxel.ai/ # Production realm server
│ └── acme-corp/
│ ├── personal/
│ ├── project-atlas/
│ └── inventory-tracker/
├── realms-staging.stack.cards/ # Staging realm server
│ └── acme-corp/
│ └── sandbox/
└── realms.stack.cards/ # Production realm server (stack.cards)
└── acme-corp/
└── production-app/
Default root: ~/boxel-workspaces/ on macOS, Linux, and Windows. Override by passing an explicit local path to pull or sync.
Benefits:
- Full realm server URL as folder name eliminates staging/production ambiguity
- Each environment is clearly identifiable by its path
- No collision between realms on different servers
First-time sync:
# The CLI automatically places workspaces under ~/boxel-workspaces/
boxel pull https://app.boxel.ai/username/realm/
boxel pull https://realms-staging.stack.cards/username/realm/
# Or specify an explicit local path to override the default:
boxel pull https://app.boxel.ai/username/realm/ ./my-custom-dirThe .boxel-workspaces.json config and local directory structure are profile-agnostic by design. Switching profiles changes which remote server you sync with, but all workspaces accumulate on your local machine without conflict. The directory paths encode which realm server they belong to, so there's no need for per-profile configuration.
workspace/
├── .boxel-sync.json # Sync manifest (auto-generated)
├── .boxel-history/ # Checkpoint history (git-based)
├── .boxel-edit.json # Edit locks (auto-generated)
├── .boxel-workspaces.json # Multi-realm config (optional)
├── .realm.json # Workspace config
├── index.json # Workspace index
├── blog-post.gts # Card definition (kebab-case)
└── BlogPost/ # Instance directory (PascalCase)
└── my-post.json # Instance file
| Type | Convention | Example |
|---|---|---|
| Definitions | kebab-case.gts |
blog-post.gts |
| Instance dirs | PascalCase/ |
BlogPost/ |
| Instance files | kebab-case.json |
my-first-post.json |
The adoptsFrom.module path is relative to the JSON file:
// In BlogPost/my-post.json:
{
"data": {
"meta": {
"adoptsFrom": {
"module": "../blog-post", // Go UP to parent
"name": "BlogPost"
}
}
}
}| JSON Location | Definition | Module Path |
|---|---|---|
root/Card.json |
root/card.gts |
"./card" |
root/Card/instance.json |
root/card.gts |
"../card" |
| Field Type | In .gts use |
In .json use |
|---|---|---|
Extends CardDef |
linksTo / linksToMany |
relationships |
Extends FieldDef |
contains / containsMany |
attributes |
Commands accept:
.- Current directory (needs.boxel-sync.json)./path- Local path@user/workspace- By name (e.g.,@aallen90/personal)https://...- Full URL
Classification:
[MAJOR]- New/deleted files, .gts changes, >3 files[minor]- Small updates to existing .json files
Source indicators:
⇆ LOCAL(green) - Local edits (fromtrackcommand)⇅ SERVER(cyan) - External change from web UI (fromwatchcommand)● MANUAL(magenta) - Restored
Milestones: Mark important checkpoints with boxel milestone
| Scenario | Flag | Result |
|---|---|---|
| Keep local | --prefer-local |
Overwrite remote, sync deletions |
| Keep remote | --prefer-remote |
Overwrite local |
| Keep newest | --prefer-newest |
Compare timestamps |
| Sync deletions | --delete |
Delete on both sides |
| Interactive | (default) | Prompt for each conflict |
URLs like https://app.boxel.ai/user/realm/Type/card-id are Card IDs, not fetchable URLs.
| URL Part | Meaning |
|---|---|
app.boxel.ai |
Production server |
user |
User/organization |
realm |
Workspace name |
Type/card-id |
Card type and instance |
NEVER use WebFetch on Boxel URLs (realms are private). Look for local synced copy:
# Parse: Type/card-id → ./Type/card-id.json
cat ./Type/card-id.json| Issue | Solution |
|---|---|
| "Authentication failed" | Check boxel profile, verify web login works |
| "No workspace found" | Run boxel workspace-list, use full URL for first sync |
| Files reverting after restore | Stop watch first, use --prefer-local after |
| Watch not detecting changes | Check interval, verify workspace URL |
| Definition changes not reflected | boxel touch . Instance/file.json |
| "500 Internal Server Error" on files | Broken on server - sync will prompt to delete, or use push --delete |
npm install # Install dependencies
npm run dev -- <command> # Run CLI in development mode
npm run build # Compile TypeScript
npm test # Run tests
npm run lint # Check code styleNote: Use
npm run dev -- <command>during development (no rebuild needed). After build, usenpx boxelorboxel(afternpm link).
This repo includes Claude Code support in .claude/:
CLAUDE.md- Project instructions and command referencecommands/- Slash commands (/watch,/restore,/sync, etc.)
When you open this repo in Claude Code, it will guide you through setup and provide AI-assisted development.
See CHANGELOG.md for per-version changes.
PRs welcome! Please ensure:
- Code passes linting (
npm run lint) - New features have documentation
- Breaking changes are noted in PR description
- Add a bullet to
CHANGELOG.mdunder the in-progress version (or start a new## Unreleasedsection if one doesn't exist)
MIT - See LICENSE
- Boxel - Main website
- Discord - Community support
- GitHub Issues - Bug reports & features