https://github.com/danielgerlag/air-traffic
https://github.com/danielgerlag/air-traffic
Last synced: 8 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/danielgerlag/air-traffic
- Owner: danielgerlag
- License: mit
- Created: 2026-03-09T00:51:58.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-03-10T03:03:28.000Z (3 months ago)
- Last Synced: 2026-03-10T10:53:11.185Z (3 months ago)
- Language: TypeScript
- Size: 419 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Air Traffic

> Remote GitHub Copilot orchestration via Slack or Discord β from your phone.
## Overview
Air Traffic runs as a daemon on your development machine, watches Slack or Discord channels for commands and prompts, and bridges them to GitHub Copilot's agent SDK. You prompt Copilot from your phone (or any messaging client), and the agent executes tasks in your project directories β editing files, running shells, committing code β while streaming results back.
## Features
### π₯οΈ Multi-Machine Support
Run Air Traffic on every machine you work with β desktop, laptop, cloud VM, CI box. Each machine gets its own bot (created via `npx air-traffic init`), so events are routed directly with zero cross-daemon coordination. DM each bot to manage that machine's projects. You manage your entire fleet from your phone.
### π Multi-Platform Support
Air Traffic supports both **Slack** and **Discord** as messaging platforms. The core logic is platform-agnostic β all platform communication goes through the `MessagingAdapter` interface. See the platform-specific setup guides:
- [Slack Setup Guide](docs/slack-setup.md)
- [Discord Setup Guide](docs/discord-setup.md)
### π Session Sharing with Local Copilot CLI
Air Traffic can join any existing Copilot CLI session running on your machine. Run `copilot` locally to start a session, then join it remotely with `join ` β output streams to the Slack channel and you can continue prompting from your phone. Sessions are listed with `sessions`, showing ID prefix, task summary, branch, working directory, and age. Prefix-matching means you only need to type the first few characters of the session ID.
### πΈ Screenshot Capture & Auto-Upload
When Copilot takes a browser screenshot (via Playwright), the image is automatically uploaded to the project's Slack channel. The agent's tool output is also scanned for file paths matching image/PDF extensions (`.png`, `.jpg`, `.gif`, `.svg`, `.webp`, `.pdf`), and any matches are uploaded. You see exactly what Copilot sees without leaving Slack.
### π File Uploads via Slack
Drop a file into a project channel and it's saved directly to the project's working directory, making it available to Copilot immediately. Useful for sharing config files, design mockups, CSVs, or any reference material the agent might need. Files are downloaded via Slack's authenticated URL and saved to the project root.
### π Auto-Upload of Created Artifacts
When Copilot creates files with uploadable extensions (images, PDFs, SVGs), they're automatically posted to the channel. Plan files (`plan.md`, `PLAN.md`) are detected and uploaded when the task completes with a π marker, so you can review the plan before the agent continues.
### π€ Slack AI Assistant Status
Air Traffic integrates with Slack's `assistant.threads.setStatus` API to show live progress in the thread. The status line updates with the current intent (e.g., "is exploring codebase"), and `loading_messages` cycle through recent tool calls (`βοΈ grep β *.ts`, `β
view β config.ts`). Status is re-asserted after every message to stay visible.
### π¬ DM-Based Control
Just DM the bot directly β no dedicated control channel needed. All control commands (`create`, `list`, `config`, `sessions`, `join`, `status`, `models`, `menu`) work in DMs. You can also type naturally β "make a project called my-app" or "what's running?" β and the NL intent classifier routes to the right command. First-time DMs show an interactive welcome menu.
### π§ Natural Language Commands
Air Traffic includes a local NL intent classifier that maps natural phrases to commands β no LLM needed, zero latency. Examples:
- "make a project called api-server" β `create api-server`
- "what's running?" β `status`
- "show me my projects" β `list`
- "switch to gpt-5" β `model gpt-5`
### ποΈ Interactive Menu & Pickers
Type `menu` (or tap a menu button) to get an interactive Block Kit menu with buttons for all common actions. Commands with optional parameters show interactive dropdowns when arguments are omitted β `model` shows a picker of available models, `mode` displays all modes with descriptions, `config` walks through a guided wizard, `join` without an ID shows a session picker.
### π Granular Permission Controls
Each project has per-category permission policies: file edits, file creates, shell commands, git operations, and network access. Each category can be set to `auto` (approve silently) or `ask` (prompt in Slack with Allow / Always Allow / Deny buttons). "Always Allow" persists the decision to the project config. Common tools like `report_intent` and `ask_user` always bypass permission checks.
### π¦ Agent Modes
Three operating modes control how Copilot handles prompts:
- **Normal** β Default behavior, full interactive agent
- **Plan** β Prepends `[[PLAN]]` to prompts, making Copilot generate a detailed plan for review before implementing
- **Autopilot** β Runs without confirmation prompts, suitable for well-defined tasks
Switch modes with `mode` in a DM or `!mode` in a project channel.
### π§ Session Management
List all Copilot CLI sessions on the machine with `sessions` β see managed vs. unmanaged sessions, their working directories, branches, and ages. Join any session by ID prefix. When joining from a DM, the system auto-creates a project and Slack channel if needed, deriving the project name from the session's working directory.
### π Web Console
A built-in web dashboard (React + Tailwind) runs alongside the daemon on a configurable port. It provides:
- **Dashboard** β View all projects, create/delete projects, machine status
- **Project View** β Live terminal output with streamed deltas, send prompts, abort sessions
- **Session History** β Load conversation history for active sessions
- **File Browser** β Navigate project files with path-traversal protection
- **Git Info** β Branch, remote, status, last commit
- **Config** β Change model, agent, mode, and permission settings per project
- **Settings** β Machine info, available models, default permissions
### π Markdown β Slack mrkdwn Conversion
Copilot outputs standard Markdown, but Slack uses its own mrkdwn format. Air Traffic converts on the fly: `**bold**` β `*bold*`, `# Heading` β `*Heading*`, `[link](url)` β ``, `- list` β `β’ list`, and code blocks are preserved. The system prompt also instructs Copilot to prefer Slack-native formatting.
### ποΈ Project Isolation
Each project gets its own Slack channel (`#atc--`), working directory, Copilot session, model selection, agent type, permission policy, and operating mode. Projects can be cloned from a repo (`create my-app --from https://github.com/user/repo`) or created as empty directories.
### π Messaging Abstraction
The core logic is platform-agnostic. All platform communication goes through the `MessagingAdapter` interface β channels, messages, questions, permissions, file uploads, thread status. Slack is the first adapter; Discord, Teams, and others can be added by implementing the interface and swapping it in `src/index.ts`.
## Prerequisites
- **Node.js 18+**
- **GitHub Copilot CLI** installed and authenticated β verify with `copilot --version`
- **Active GitHub Copilot subscription** (Individual, Business, or Enterprise)
- **Slack workspace** or **Discord server** with admin access
## Platform Setup
Choose your messaging platform:
- **Slack** β [docs/slack-setup.md](docs/slack-setup.md)
- **Discord** β [docs/discord-setup.md](docs/discord-setup.md)
### Quick Start
```bash
npx air-traffic init
```
The setup wizard asks for your **platform** (Slack or Discord), machine name, and walks you through creating the bot and collecting tokens. It writes a `.env` file ready to go.
## Installation
### Quick Start with npx
Run Air Traffic directly without installing β just make sure your environment variables are set:
```bash
# Set required env vars (or use a .env file in the current directory)
export SLACK_BOT_TOKEN=xoxb-...
export SLACK_APP_TOKEN=xapp-...
export SLACK_SIGNING_SECRET=...
export ATC_MACHINE_NAME=my-machine
npx air-traffic
```
### Global Install
Install globally to get the `air-traffic` command:
```bash
npm install -g air-traffic
air-traffic
```
### From Source
```bash
git clone https://github.com/danielgerlag/air-traffic.git
cd air-traffic
npm install
cp .env.example .env
```
Edit `.env` with your Slack credentials and machine config:
```env
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-token
SLACK_SIGNING_SECRET=your-signing-secret
ATC_MACHINE_NAME=my-machine
```
Then run:
```bash
npm run dev
```
## Configuration
| Variable | Description | Default |
|---|---|---|
| `ATC_PLATFORM` | Messaging platform: `slack` or `discord` | `slack` |
| `ATC_MACHINE_NAME` | Unique name for this machine (e.g. `desktop`, `laptop`) | *required* |
| `ATC_PROJECTS_DIR` | Directory where project working copies are created | `~/projects` |
| `ATC_DATA_DIR` | Directory for project metadata and config storage | `~/.air-traffic/data` |
| `ATC_DEFAULT_MODEL` | Default Copilot model for new projects | `claude-sonnet-4.5` |
| `ATC_LOG_LEVEL` | Log verbosity: `error`, `warn`, `info`, `debug` | `info` |
| `ATC_WEB_PORT` | Port for the Air Traffic Console web UI | `8089` |
| `ATC_PERMISSION_TIMEOUT_MS` | Timeout for permission prompts (ms) | `300000` |
| `ATC_QUESTION_TIMEOUT_MS` | Timeout for agent questions (ms) | `300000` |
**Slack-specific** (when `ATC_PLATFORM=slack`):
| Variable | Description |
|---|---|
| `SLACK_BOT_TOKEN` | Bot User OAuth Token (`xoxb-...`) |
| `SLACK_APP_TOKEN` | App-Level Token with `connections:write` (`xapp-...`) |
| `SLACK_SIGNING_SECRET` | Slack app signing secret |
**Discord-specific** (when `ATC_PLATFORM=discord`):
| Variable | Description |
|---|---|
| `DISCORD_BOT_TOKEN` | Discord bot token |
| `DISCORD_GUILD_ID` | Discord server (guild) ID |
| `DISCORD_SPINNER_EMOJI` | Optional animated spinner emoji (e.g. ``) |
## Running
```bash
# Quick run (no install needed)
npx air-traffic
# Development (with hot-reload via tsx)
npm run dev
# Production
npm run build
npm start
# With PM2 (recommended for always-on daemon)
npm install -g pm2
pm2 start dist/index.js --name air-traffic
pm2 save
pm2 startup
```
## Air Traffic Console (Web UI)
The Console is a web dashboard that runs alongside the daemon on port 8089. It provides a browser-based interface for:
- **Dashboard** β View all projects, create/delete projects, see machine status
- **Project View** β Live terminal output, send prompts, abort sessions
- **Config** β Change model, agent, and permission settings per project
- **Settings** β View machine info, available models, and default permissions
### Development
Run the daemon and Vite dev server side by side:
```bash
# Terminal 1: Backend daemon
npm run dev
# Terminal 2: Frontend dev server (with hot-reload + proxy)
npm run console:dev
```
The Vite dev server runs on `http://localhost:5173` and proxies API/Socket.IO calls to `localhost:8089`.
### Production
Build the frontend, then run the daemon β it serves the compiled assets automatically:
```bash
npm run console:build # Compiles frontend to web/dist/
npm run build # Compile backend TypeScript
npm start # Serves everything on port 8089
```
Open `http://localhost:8089` to access the Console.
## Multi-Machine Setup
1. Run `npx air-traffic init` on each machine β give each a unique name (e.g. `desktop`, `laptop`, `cloud-dev`).
2. Each machine gets its own Slack app with a name like "ATC Desktop", "ATC Laptop", etc.
3. DM each bot to control that machine's projects.
4. No shared credentials, no routing conflicts β each app is independent.
## Command Reference
Commands are sent via DM to the bot or with `!` prefix in project channels. You can also type naturally β the NL intent classifier handles common phrases.
### DM Commands (control)
| Command | Description | Example |
|---|---|---|
| `create [--from ]` | Create a new project (optionally clone a repo) | `create my-app --from https://github.com/user/repo` |
| `delete [name]` | Delete a project (picker if omitted) | `delete my-app` |
| `list` | List all projects | `list` |
| `config [project] [field] [value]` | Update project config (guided wizard if omitted) | `config my-app model gpt-5` |
| `status` | Show machine status and active sessions | `status` |
| `models` | List available Copilot models | `models` |
| `sessions` | List all Copilot CLI sessions | `sessions` |
| `join [session-id]` | Join a session (picker if omitted) | `join a1b2` |
| `menu` | Show interactive menu with action buttons | `menu` |
| `help` | Show command reference | `help` |
### Project Channel Commands (`#atc--`)
In project channels, regular messages are sent as prompts to Copilot. Use `!` prefix for commands:
| Command | Description | Example |
|---|---|---|
| *(any text)* | Send as a prompt to the Copilot agent | `Add user authentication with JWT` |
| `!model [name]` | Change the model (picker if omitted) | `!model gpt-5` |
| `!agent [name]` | Set the agent type | `!agent code-review` |
| `!mode [mode]` | Set operating mode (picker if omitted) | `!mode autopilot` |
| `!status` | Show project status and session state | `!status` |
| `!abort` | Abort the current agent session | `!abort` |
| `!diff` | Show `git diff` of the project directory | `!diff` |
| `!history` | Show session message history | `!history` |
| `!sessions` | List all Copilot CLI sessions | `!sessions` |
| `!join [id]` | Join a session (picker if omitted) | `!join a1b2` |
| `!leave` | Detach from session without killing it | `!leave` |
| `!help` | Show project command reference | `!help` |
## Architecture
```
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Slack / Discord β
β (phone / desktop client) β
ββββββββββββββββ¬ββββββββββββββββββββ¬ββββββββββββ
β Socket Mode / β
β Gateway β
ββββββββββββββββΌββββββββββββββββββββΌββββββββββββ
β SlackAdapter / DiscordAdapter β
β (implements MessagingAdapter) β
ββββββββββββββββββββββββββββββββββββββββββββββββ€
β AirTrafficDaemon β
β βββββββββββββββ ββββββββββββββββββββββββββ β
β β ProjectMgr β β SessionOrchestrator β β
β β (CRUD, store)β β (CopilotClient, pool) β β
β βββββββββββββββ ββββββββββββββββββββββββββ β
β βββββββββββββββ ββββββββββββββββββββββββββ β
β βPermissionMgrβ β ModelRegistry β β
β βββββββββββββββ ββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββ€
β AgentSession (per project) β
β - Copilot SDK session with streaming β
β - Delta batching for Slack messages β
β - Permission & question flow via adapter β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
ββββββββββββββββΌβββββββββββββββββββββββββββββββββ
β GitHub Copilot SDK β
β (CopilotClient / CopilotSession) β
βββββββββββββββββββββββββββββββββββββββββββββββββ
```
The `MessagingAdapter` interface (`src/messaging/types.ts`) abstracts all platform-specific communication. To add a new platform, implement the interface and swap it in `src/index.ts`.
## Development
```bash
npm test # Run tests
npm run test:watch # Watch mode
npm run build # Compile TypeScript
```
### Project Structure
```
src/
βββ config.ts # Env var loading + Zod validation
βββ daemon.ts # AirTrafficDaemon β main command router
βββ index.ts # Entry point β wires config, adapter, daemon
βββ cli/
β βββ init.ts # Setup wizard (npx air-traffic init)
β βββ manifest-template.ts # Generates per-machine Slack app manifest
βββ copilot/
β βββ agent-session.ts # Per-project Copilot session with streaming
β βββ session-orchestrator.ts# CopilotClient lifecycle + session pool
β βββ permission-manager.ts # Tool β category mapping + policy check
β βββ model-registry.ts # Known model list
βββ messaging/
β βββ types.ts # Platform-agnostic interfaces
β βββ adapter.ts # BaseMessagingAdapter (shared event dispatch)
β βββ in-memory-adapter.ts # Test double
β βββ intent.ts # NL intent classifier (synonym/keyword map)
β βββ slack/
β βββ slack-adapter.ts # Slack Bolt integration
β βββ commands.ts # Message parsing (control + project channels)
β βββ formatters.ts # Block Kit formatting (help, menu, welcome)
β βββ presence.ts # Heartbeat manager
β βββ discord/
β βββ discord-adapter.ts # Discord.js integration
β βββ formatters.ts # Embed/component formatting
β βββ markdown.ts # Slack mrkdwn β Discord markdown
βββ projects/
β βββ types.ts # ProjectConfig, PermissionPolicy
β βββ project-manager.ts # CRUD + validation
β βββ project-store.ts # JSON file persistence
βββ utils/
β βββ errors.ts # Typed error hierarchy
β βββ logger.ts # Winston logger
βββ web/
βββ server.ts # Express + Socket.IO server
βββ api-routes.ts # REST API endpoints
βββ socket-handlers.ts # Socket.IO event handlers
βββ session-bridge.ts # AgentSession β Socket.IO bridge
web/ # React frontend (Vite + Tailwind)
βββ src/
β βββ App.tsx # Router + layout
β βββ pages/ # Dashboard, ProjectView, Settings
β βββ components/ # SessionTerminal, PromptInput, ConfigPanel
β βββ hooks/ # useProjects, useSession, useStatus
β βββ lib/ # API client, Socket.IO client, types
βββ dist/ # Built frontend (served by Express)
```
## License
MIT