Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

## 2026-04-27

- Renamed repo from `buildingopen/moto` to `floomhq/moto`
- Renamed repo from `buildingopen/moto` to `floomhq/fstack`
- Added 17th hook: `sandbox-heavy-tasks.sh` for memory-hungry command interception
- Added `ai-sidecar-health` script for provider verification
- Expanded skills catalog to 60+ commands
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# moto 🛵
# fstack 🛵

### A terminal IDE for AI agents, extracted from 500+ real sessions

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Claude Code](https://img.shields.io/badge/Claude_Code-compatible-blueviolet)](https://docs.anthropic.com/en/docs/claude-code)

`moto` is Floom's terminal IDE for AI agents: Claude as the control plane, Codex for backend/infra/debugging, opencode as an optional worker, subagent workflows, Docker-first runtime, CLAUDE.md / AGENTS.md-style context, safety hooks, 60+ skills, Gmail/WhatsApp integrations, memory, browser automation, and cost tracking.
`fstack` is Floom's terminal IDE for AI agents: Claude as the control plane, Codex for backend/infra/debugging, opencode as an optional worker, subagent workflows, Docker-first runtime, CLAUDE.md / AGENTS.md-style context, safety hooks, 60+ skills, Gmail/WhatsApp integrations, memory, browser automation, and cost tracking.

This repo was formerly `buildingopen/moto` and, before that, `buildingopen/claude-setup`. The old GitHub URLs redirect here automatically.
Formerly floomhq/moto and earlier buildingopen/moto. Old GitHub URLs redirect.

<p align="center">
<img src="assets/hook-demo.gif" alt="fstack hook demo" width="800">
Expand All @@ -27,7 +27,7 @@ This repo was formerly `buildingopen/moto` and, before that, `buildingopen/claud

## What makes this different?

Most AI coding setups are a prompt file plus a pile of tools. `moto` is the full operating environment: context, hooks, skills, memory, terminal sessions, remote server runtime, browser automation, Docker sandboxes, and launcher commands for Claude, Codex, and opencode.
Most AI coding setups are a prompt file plus a pile of tools. `fstack` is the full operating environment: context, hooks, skills, memory, terminal sessions, remote server runtime, browser automation, Docker sandboxes, and launcher commands for Claude, Codex, and opencode.

The philosophy is simple:

Expand All @@ -44,8 +44,8 @@ The philosophy is simple:
## Quick Start

```bash
git clone https://github.com/floomhq/moto.git
cd moto
git clone https://github.com/floomhq/fstack.git
cd fstack
./install.sh # Symlinks configs into ~/.claude/
```

Expand All @@ -69,11 +69,11 @@ $EDITOR .env

## Comparison

| | bare Claude Code | Claude Code + dotfiles | **moto** |
| | bare Claude Code | Claude Code + dotfiles | **fstack** |
|---|---|---|---|
| Context | manual per session | static files | CLAUDE.md + AGENTS.md + MEMORY.md |
| Safety | none | none | **17 hooks** blocking destructive commands, secret leaks, config tampering |
| Remote workflow | DIY SSH | DIY SSH | **`moto up`** + SSHFS + systemd + tmux |
| Remote workflow | DIY SSH | DIY SSH | **`fstack up`** + SSHFS + systemd + tmux |
| Skills | none | none | **60+ slash commands** (/cost, /qa, /ship, /debug, etc.) |
| Sidecars | none | none | **Groq / Gemini / NVIDIA** routing for cheap stateless work |
| Memory | none | none | **MEMORY.md** auto-updated across sessions |
Expand All @@ -86,10 +86,10 @@ $EDITOR .env
## Architecture

<p align="center">
<img src="assets/architecture.svg" alt="moto architecture" width="800">
<img src="assets/architecture.svg" alt="fstack architecture" width="800">
</p>

`moto` treats your Mac as the control plane and a remote Linux box as the runtime. Claude Code orchestrates; Codex/opencode handle heavy lifting; Docker sandboxes risky work; sidecars review cheaply; hooks enforce safety at every tool call.
`fstack` treats your Mac as the control plane and a remote Linux box as the runtime. Claude Code orchestrates; Codex/opencode handle heavy lifting; Docker sandboxes risky work; sidecars review cheaply; hooks enforce safety at every tool call.

Read the deep dive: [`docs/architecture.md`](docs/architecture.md)

Expand All @@ -100,7 +100,7 @@ Read the deep dive: [`docs/architecture.md`](docs/architecture.md)
| Directory | Contents | Learn more |
|-----------|----------|------------|
| [`claude/`](claude/) | CLAUDE.md, hooks, scripts, skills, memory | [`claude/skills/README.md`](claude/skills/README.md) · [`claude/hooks/README.md`](claude/hooks/README.md) · [`claude/memory/README.md`](claude/memory/README.md) |
| [`mac/`](mac/) | `moto` CLI, iTerm automation, shell aliases, launchd + SSH templates | [`mac/README.md`](mac/README.md) |
| [`mac/`](mac/) | `fstack` CLI, iTerm automation, shell aliases, launchd + SSH templates | [`mac/README.md`](mac/README.md) |
| [`server/`](server/) | Systemd services, Docker stack, safety utils, browser automation, tmux | [`docs/bootstrap.md`](docs/bootstrap.md) |
| [`whatsapp/`](whatsapp/) | OpenClaw gateway, verified send, SQLite contact lookup | [`whatsapp/README.md`](whatsapp/README.md) |
| [`gmail/`](gmail/) | IMAP checker, multi-account support | [`gmail/`](gmail/) |
Expand All @@ -114,7 +114,7 @@ Read the deep dive: [`docs/architecture.md`](docs/architecture.md)
No. `./install.sh` works locally. Add `./install.sh mac` + `./install.sh server-remote` only when you want the integrated remote workflow.

**Does this replace Claude Code?**
No. It's the operating environment around Claude Code (and Codex, and opencode). You still use Anthropic's CLI; moto just makes it reliable, safe, and reproducible.
No. It's the operating environment around Claude Code (and Codex, and opencode). You still use Anthropic's CLI; fstack just makes it reliable, safe, and reproducible.

**Is this safe?**
17 hooks run on every tool call blocking `rm -rf /`, secret leaks, wrong package managers, unverified WhatsApp sends, and more. See [`claude/hooks/README.md`](claude/hooks/README.md).
Expand Down
4 changes: 2 additions & 2 deletions assets/architecture.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion claude/scripts/ai-provider-key
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Store provider API keys for moto sidecars without writing them to repos."""
"""Store provider API keys for fstack sidecars without writing them to repos."""

from __future__ import annotations

Expand Down
4 changes: 2 additions & 2 deletions claude/scripts/ai-sidecar
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ def call_provider(provider: str, model: str, prompt: str, max_tokens: int, timeo
headers = {
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 moto-ai-sidecar/1.0",
"User-Agent": "Mozilla/5.0 fstack-ai-sidecar/1.0",
}
if provider == "openrouter":
headers.update({"HTTP-Referer": "https://localhost", "X-Title": "moto AI Sidecar"})
headers.update({"HTTP-Referer": "https://localhost", "X-Title": "fstack AI Sidecar"})

request = urllib.request.Request(
cfg["url"],
Expand Down
10 changes: 5 additions & 5 deletions claude/scripts/ai-sidecar-health
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Health check for moto low-cost AI sidecars."""
"""Health check for fstack low-cost AI sidecars."""

from __future__ import annotations

Expand Down Expand Up @@ -98,9 +98,9 @@ def check_openrouter() -> dict:
{
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 moto-ai-sidecar-health/1.0",
"User-Agent": "Mozilla/5.0 fstack-ai-sidecar-health/1.0",
"HTTP-Referer": "https://localhost",
"X-Title": "moto AI Sidecar Health",
"X-Title": "fstack AI Sidecar Health",
},
)
usage = data.get("usage", {})
Expand All @@ -123,7 +123,7 @@ def check_groq() -> dict:
{
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 moto-ai-sidecar-health/1.0",
"User-Agent": "Mozilla/5.0 fstack-ai-sidecar-health/1.0",
},
)
usage = data.get("usage", {})
Expand All @@ -147,7 +147,7 @@ def check_nvidia() -> dict:
{
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 moto-ai-sidecar-health/1.0",
"User-Agent": "Mozilla/5.0 fstack-ai-sidecar-health/1.0",
},
)
usage = data.get("usage", {})
Expand Down
4 changes: 2 additions & 2 deletions claude/skills/session-learn/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ Check if a topic file already exists before adding to MEMORY.md directly.
Topic files live at `~/.claude/projects/-root/memory/<topic>.md`.
If MEMORY.md exceeds 200 lines, add to a topic file and reference it.

### Sync to moto repo (if applicable)
### Sync to fstack repo (if applicable)

```bash
# Check if the skill directory is tracked by a setup repo
ls ~/moto/ 2>/dev/null || ls ~/claude-setup/ 2>/dev/null || ls ~/.claude-setup/ 2>/dev/null
ls ~/fstack/ 2>/dev/null || ls ~/claude-setup/ 2>/dev/null || ls ~/.claude-setup/ 2>/dev/null
# If it exists, copy new skill files there and commit
```

Expand Down
10 changes: 5 additions & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# moto — architecture
# fstack — architecture

## Physical layout

Expand All @@ -10,7 +10,7 @@
│ iTerm ─┐ │
│ │ AppleScript │
│ ▼ │
moto CLI │
fstack CLI │
│ │ │
│ ▼ │
│ ssh ax41 (ControlMaster)│
Expand Down Expand Up @@ -64,7 +64,7 @@ its children (your 20+ agent sessions) are touched.

### 3. One iTerm window, many tabs

The Mac's `moto up` (in zsh: `axo`) does this dance:
The Mac's `fstack up` (in zsh: `axo`) does this dance:

1. Detaches all existing tmux clients on the server
2. Lists sessions sorted by activity
Expand All @@ -75,7 +75,7 @@ The Mac's `moto up` (in zsh: `axo`) does this dance:
5. Waits 12s, then retries any sessions that didn't attach (up to 5 rounds,
then a final single-pass for stubborn ones)

This is idempotent: calling `moto up` at any time reconstructs the exact same
This is idempotent: calling `fstack up` at any time reconstructs the exact same
window layout on your Mac.

### 4. Logged-in browser for agents
Expand Down Expand Up @@ -120,7 +120,7 @@ network are up. It:
Tmux sessions survive automatically because the tmux server itself is
restarted by its own systemd unit and sessions were started by
`cs`/`cx`/`co`, which use `new-session -A` (attach-or-create). They're
empty until you re-run `moto up` — but tmux state persists, so prior
empty until you re-run `fstack up` — but tmux state persists, so prior
scrollback and the Claude CLI process come back cleanly *if* they were
still alive before the reboot (they won't be, because the box rebooted —
but the session *slots* are restored automatically when `./cs NAME` is re-run).
Expand Down
16 changes: 8 additions & 8 deletions docs/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ and Ubuntu 22.04+.
## 1. Clone on your Mac

```bash
git clone https://github.com/floomhq/moto.git ~/moto
cd ~/moto
git clone https://github.com/floomhq/fstack.git ~/fstack
cd ~/fstack
cp .env.example .env
$EDITOR .env
```
Expand All @@ -34,7 +34,7 @@ Fill in at minimum:
./install.sh mac
```

This installs the local `~/.claude` config, adds `~/.local/bin/moto`, sources
This installs the local `~/.claude` config, adds `~/.local/bin/fstack`, sources
shell functions into `~/.zshrc`, adds a `Host ax41` block to `~/.ssh/config`,
and loads the launchd agent for the reverse tunnel.

Expand Down Expand Up @@ -62,7 +62,7 @@ If the installer printed a new public key, add it to your Mac's

```bash
# On the Mac:
echo 'ssh-ed25519 AAAA... moto-server-hostname' >> ~/.ssh/authorized_keys
echo 'ssh-ed25519 AAAA... fstack-server-hostname' >> ~/.ssh/authorized_keys
```

## 4. Kick the reverse tunnel
Expand All @@ -79,11 +79,11 @@ ssh ax41 'ssh mac hostname'
# → prints your Mac's hostname
```

## 5. Run `moto doctor`
## 5. Run `fstack doctor`

```bash
# On the Mac:
moto doctor
fstack doctor
```

Every check turns green when the setup is healthy. If `reverse tunnel` is down, the SSH server on
Expand All @@ -93,11 +93,11 @@ the server's key.
## 6. Create your first session

```bash
moto new hello/world
fstack new hello/world
# → iTerm opens a new tab with Claude Code running on the server.
```

From now on, `moto up` will always restore your full set of sessions into one
From now on, `fstack up` will always restore your full set of sessions into one
iTerm window.

## 7. (Optional) Browser login
Expand Down
2 changes: 1 addition & 1 deletion docs/browser-login.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ ssh ax41 'bash /root/authenticated-browser/vnc-login.sh stop'

## 6. Use it from agents

From any Docker container on the moto network, Chrome is reachable at:
From any Docker container on the fstack network, Chrome is reachable at:

```
ws://172.17.0.1:9222/devtools/browser
Expand Down
4 changes: 2 additions & 2 deletions docs/claude-sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Two mounts, configured in `server/bin/check-mac-mounts`:
| `/mnt/mac` | `~/` (the Mac home dir) | Full Mac access from the server|

The full home mount is what lets `aximg` (copy image to server) and
`moto img` work — but it's also a footgun (you can `rm -rf ~/Desktop` from a
`fstack img` work — but it's also a footgun (you can `rm -rf ~/Desktop` from a
server terminal). Keep an eye on that.

## If you want the opposite direction
Expand All @@ -79,4 +79,4 @@ mkdir -p ~/mnt/ax41
sshfs ax41:/root ~/mnt/ax41
```

This isn't used by moto but can be handy.
This isn't used by fstack but can be handy.
8 changes: 4 additions & 4 deletions docs/demos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This repo is powerful but dense. Every major feature should have a 10–30 secon

`assets/demos/hero.gif` — the full 30-second pitch:
```
moto new myproj/feature
fstack new myproj/feature
# → iTerm spawns 4 tabs
# → SSH to remote
# → tmux session attaches
Expand All @@ -18,12 +18,12 @@ moto new myproj/feature
| Feature | Target file | What to record |
|---------|-------------|----------------|
| **WhatsApp setup** | `assets/demos/whatsapp-setup.gif` | `clawdbot-ctl login` → QR code → phone scan → `safe-wa-send "Name" "test"` → verify output → `--confirmed` send |
| **Install** | `assets/demos/install.gif` | `git clone … && cd moto && ./install.sh` → hooks installed → skills symlinked |
| **Install** | `assets/demos/install.gif` | `git clone … && cd fstack && ./install.sh` → hooks installed → skills symlinked |
| **Safety hook block** | `assets/demos/hook-block.gif` | Claude tries `rm -rf /` → hook intercepts → red error |
| **moto CLI** | `assets/demos/moto-cli.gif` | `moto ls` → `moto new proj` → `moto up` → tabs restore |
| **fstack CLI** | `assets/demos/fstack-cli.gif` | `fstack ls` → `fstack new proj` → `fstack up` → tabs restore |
| **Skills** | `assets/demos/skills.gif` | `/cost` → `/bouncer` → `/qa` in a Claude session |
| **Memory** | `assets/demos/memory.gif` | Claude updates `MEMORY.md` → next session recalls preference |
| **Server bootstrap** | `assets/demos/server.gif` | `./install.sh server-remote` → systemd units → `moto doctor` |
| **Server bootstrap** | `assets/demos/server.gif` | `./install.sh server-remote` → systemd units → `fstack doctor` |
| **Sidecar** | `assets/demos/sidecar.gif` | `ai-sidecar --provider groq --prompt "review this diff"` |

## Recording workflow
Expand Down
6 changes: 3 additions & 3 deletions docs/demos/record.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ set -euo pipefail

# Record a reproducible terminal demo using asciinema + agg
# Usage: ./docs/demos/record.sh <feature>
# feature = whatsapp | install | moto-cli | hook-block | skills | memory | server | sidecar
# feature = whatsapp | install | fstack-cli | hook-block | skills | memory | server | sidecar

FEATURE="${1:-}"
DEMO_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$DEMO_DIR/../.." && pwd)"
CAST_DIR="/tmp/moto-demos"
CAST_DIR="/tmp/fstack-demos"
mkdir -p "$CAST_DIR"

if [[ -z "$FEATURE" ]]; then
Expand All @@ -31,7 +31,7 @@ fi
command -v asciinema >/dev/null 2>&1 || { echo "Install asciinema first: brew install asciinema"; exit 1; }

echo "Recording $FEATURE → $CAST_FILE"
asciinema rec "$CAST_FILE" --command "bash $DEMO_SCRIPT" --title "moto: $FEATURE"
asciinema rec "$CAST_FILE" --command "bash $DEMO_SCRIPT" --title "fstack: $FEATURE"

if command -v agg >/dev/null 2>&1; then
mkdir -p "$(dirname "$GIF_FILE")"
Expand Down
8 changes: 4 additions & 4 deletions docs/demos/whatsapp.demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# This is run inside asciinema. Use echo + sleep to pace the narrative.

clear
echo -e "\033[1;36m=== moto WhatsApp Setup ===\033[0m"
echo -e "\033[1;36m=== fstack WhatsApp Setup ===\033[0m"
sleep 1

echo ""
Expand All @@ -23,15 +23,15 @@ sleep 2
echo -e "\033[32m[+] QR code scanned. Session linked.\033[0m"

echo ""
echo "$ ./safe-wa-send \"Alice\" \"Hey from moto 👋\""
echo "$ ./safe-wa-send \"Alice\" \"Hey from fstack 👋\""
sleep 0.5
echo -e "\033[36m Resolved: Alice (+1 555-0199)\033[0m"
echo -e "\033[36m JID: 15550199@s.whatsapp.net\033[0m"
echo -e "\033[36m Message: Hey from moto 👋\033[0m"
echo -e "\033[36m Message: Hey from fstack 👋\033[0m"
echo -e "\033[31m [DRY RUN] Add --confirmed to send.\033[0m"

echo ""
echo "$ ./safe-wa-send \"Alice\" \"Hey from moto 👋\" --confirmed"
echo "$ ./safe-wa-send \"Alice\" \"Hey from fstack 👋\" --confirmed"
sleep 0.5
echo -e "\033[32m[+] Message sent to Alice (+1 555-0199)\033[0m"

Expand Down
2 changes: 1 addition & 1 deletion docs/proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Why: many services (Google sign-in, LinkedIn, Zomato, Instagram) flag or block
data-center IPs. Routing agent traffic through a residential IP makes you look
like a normal user.

## How moto does it
## How fstack does it

`server/docker/compose.yaml` includes an optional `proxy` service (tinyproxy,
built locally from `server/docker/proxy/`) under the `proxy` profile. It:
Expand Down
Loading
Loading