A lightweight, mobile-first viewer and controller for AI coding agents.
Open any Claude Code session on your phone. Read the conversation. Send messages. Watch the terminal. Resume old sessions or spawn new ones — instantly.
The entire app is 6 files. Point Claude (or any AI agent) at this repo and tell it what you want:
- "Add a cost tracker that shows tokens and dollars per session"
- "Add push notifications when an agent needs my attention"
- "Show me a diff view when Claude edits a file"
- "Add a dark/light theme toggle"
- "Group consecutive tool calls into a collapsible block"
- "Add Codex support — here's how their session files work"
- "Add a search bar that filters across all sessions"
- "Show a green typing indicator when Claude is responding"
- "Add keyboard shortcuts — j/k to navigate sessions, Enter to open"
No abstractions to learn. No plugin API to read. One server file, one app shell, one message renderer. You describe it, the agent builds it.
Feather is designed to diverge. Fork it, make it yours, share what you build.
# Fork on GitHub, then:
git clone https://github.com/YOU/feather.git && cd feather
npm install && npm start
# hack awayPulling from other forks:
# Add someone's fork as a remote
git remote add phil https://github.com/phil/feather.git
git fetch phil
# See what they've been up to
git log phil/main --oneline
# Grab specific commits
git cherry-pick <sha>
# Or merge everything
git merge phil/mainContributing back:
Open a PR to inceptel/feather from your fork. Or don't — your fork is yours.
You're running Claude Code on a remote machine. You want to check on it from your phone, your iPad, another laptop. You want to send a follow-up message without SSH-ing in. You want to see the conversation rendered beautifully — like a texting app, not a terminal dump.
Feather reads Claude's raw JSONL session files, streams updates via SSE, and connects to tmux sessions via WebSocket terminals. No database. No build pipeline beyond Vite. Just node server.js.
npm install inceptel/feather
cd node_modules/feather && npm startOr from source:
git clone https://github.com/inceptel/feather.git && cd feather
npm install # installs deps + builds frontend automatically
npm start # → Feather on http://localhost:4870┌─────────────────────────────────────────┐
│ iPhone / Browser │
│ SolidJS SPA │
│ ├── MessageView (markdown, bubbles) │
│ ├── Terminal (xterm.js + WebSocket) │
│ └── Chat input (auto-grow textarea) │
└──────────┬──────────────────────────────┘
│ HTTP + SSE + WS
▼
┌──────────────────────────────────────────┐
│ Express server │
│ ├── JSONL parser (parseMessage) │
│ ├── Session discovery (2-phase scan) │
│ ├── SSE broadcaster (byte-offset IDs) │
│ ├── fs.watch (inotify, per-directory) │
│ ├── tmux manager (spawn/resume/send) │
│ └── WebSocket terminal (node-pty) │
└──────────┬──────────────────────────────┘
│ filesystem
▼
~/.claude/projects/<hash>/<session>.jsonl
tmux sessions: feather-<8chars>
| File | Purpose |
|---|---|
server.js |
Entire backend — API, SSE, WebSocket, JSONL parsing, tmux |
frontend/src/App.tsx |
UI shell — sidebar, header, tabs, input bar |
frontend/src/api.ts |
REST + SSE client, types |
frontend/src/components/MessageView.tsx |
Chat bubbles with markdown rendering |
frontend/src/components/Terminal.tsx |
xterm.js + WebSocket terminal |
frontend/src/index.tsx |
SolidJS mount point |
- No database. Read JSONL directly. The filesystem is the source of truth.
- No polling.
fs.watch→ SSE push. Client generates session UUID upfront (ClOrdId pattern). - tmux as the process manager. Every Claude session runs in a named tmux session. Terminal tab attaches to it. Chat input sends keystrokes via
send-keys/paste-buffer. - Two-phase session discovery. Stat-only scan + sort by mtime → read first 4KB of top N for titles. 7000+ sessions in 75ms.
- Byte-offset SSE IDs. Enables resumable streams and gap-free message delivery.
- Mobile-first.
--vhviewport fix, safe-area insets,-webkit-overflow-scrolling: touch, PWA meta tags.
cd ~/feather
npm run deploy # stamps version.json, builds frontend, restarts serverBoth backend (/api/health) and frontend (tab bar) show the same version timestamp.
sudo cp infra/feather.supervisor.conf /etc/supervisor/conf.d/feather.conf
supervisorctl reread && supervisorctl updatesudo cp infra/feather.service /etc/systemd/system/
sudo systemctl enable --now featherhandle /feather { redir /feather/ permanent }
handle /feather/api/* {
uri strip_prefix /feather
reverse_proxy localhost:4870 {
flush_interval -1 # required for SSE
}
}
handle /feather/* {
uri strip_prefix /feather
reverse_proxy localhost:4870
}
Backend: express, node-pty, ws Frontend: solid-js, @xterm/xterm, @xterm/addon-fit, marked, dompurify
Elastic License 2.0 — free to use, modify, and distribute. Cannot be offered as a hosted service.