Package Exports
- handsoff
- handsoff/dist/cli/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (handsoff) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Handsoff
Hands off your desk, hand off control to your phone.
Leave your desk, not your code.
Handsoff is a local gateway that bridges AI programming assistants (like Claude Code) with messaging platforms (like Telegram, Feishu). It captures agent events in real-time and forwards them to your phone, letting you monitor and control your AI assistant from anywhere.
The Problem
You're running Claude Code on your workstation. It asks for permission to execute a tool. You're away from your desk—maybe grabbing coffee, in a meeting, or simply stepping out. The session sits there waiting. Time ticks away.
The Solution
Handsoff keeps you connected to your coding sessions wherever you are:
- Monitor — Get notified when Claude starts or finishes tasks
- Control — Approve or deny tool execution requests right from your phone
- Stay informed — Receive execution summaries with token usage and results
No more tethering yourself to your desk. Your code doesn't stop when you step away.
Install (recommended)
Install globally via npm or pnpm:
npm install -g handsoffOr run without installing:
npx handsoff initQuick start (TL;DR)
# 1. Configure (interactive wizard)
handsoff init
# 2. Start the gateway daemon
handsoff gateway start
# 3. Start using Codex from your bound chat channel
# Example: send "/codex explain the latest failing test"Done. Codex requests, permission prompts, and task completions will now arrive on your connected channels.
How it works
Claude Code + Telegram integration
The core workflow bridges Claude Code with Telegram:
- Session Monitoring — Handsoff listens for Claude Code session start/end events and notifies you on Telegram
- Permission Control — When Claude requests to execute a tool, you receive a permission prompt on Telegram with Allow/Deny buttons
- Real-time Decisions — Click the button in Telegram to grant or deny the tool execution
- Task Completion — Receive final notifications when tasks complete, including execution duration, token usage, and results
How it works
Data flow
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Claude Code │ │ Telegram / │ │ Web / Mobile │
│ (agent) │ │ Feishu │ │ (-dashboard) │
└────────┬────────┘ └────────▲────────┘ └────────▲────────┘
│ │ │
│ hooks │ push │ ws / http
│ │ │
v │ │
┌─────────────────┐ │ │
│ ClaudeHook │ │ │
│ Adapter │ │ │
└────────┬────────┘ │ │
│ │ │
│ event │ │
│ │ │
v │ │
┌─────────────────┐ ┌────────┴───────────────────────┴────────┐
│ Handsoff │ │ Gateway │
│ Gateway │ │ ┌─────────┐ ┌─────────────────────┐ │
│ │ │ │EventBus │ │ InteractionService │ │
│ ┌───────────┐ │ │ │Session │ │ NotificationHandler │ │
│ │ HTTP/WS │ │◄────┼──│Manager │ │ EventPublisher │ │
│ │ Server │ │ │ └────┬────┘ └──────────┬──────────┘ │
│ └───────────┘ │ │ │ │ │
└─────────────────┘ └───────┼───────────────────┼─────────────┘
│ │
│ subscribe │ publish
│ │
v v
┌─────────────────┐ ┌─────────────────┐
│ ChannelManager │ │ Dashboard / │
│ (Telegram/ │ │ Logger │
│ Feishu) │ └─────────────────┘
└─────────────────┘- Agent hooks — Claude Code fires lifecycle hooks (
SessionStart,PermissionRequest,Stop, etc.) - Gateway ingests —
ClaudeHookAdapternormalizes events and feeds them into the Gateway - Gateway routes —
EventBusroutes events to the right handler:- Session events →
NotificationHandler→ push to channels - Permission requests →
InteractionService→ wait for user response - Commands from channels →
Command Handler→ control the agent
- Session events →
- Channels notify —
ChannelManagerforwards messages to Telegram / Feishu with interactive buttons - Web dashboard — HTTP + WebSocket server exposes live sessions and event history
Request/response cycle
A typical permission-request flow looks like this:
Claude needs to run Bash
│
▼
Gateway receives PermissionRequest
│
▼
NotificationHandler sends card to Telegram/Feishu
│
▼
User clicks Allow / Deny on phone
│
▼
ChannelManager posts interaction response
│
▼
InteractionService resolves the pending request
│
▼
Claude continues with the resultSupported platforms
- CLI agent support:
claude,codex - Chat channel support:
Telegram,Feishu,App
Main commands
handsoff init— initialize or update~/.handsoff/config.tomlhandsoff gateway start— start or restart the gateway daemonhandsoff gateway stop— stop the gateway daemonhandsoff gateway restart— restart the gateway daemonhandsoff gateway status— show gateway health and active sessionshandsoff attach <agent-type>— attach a supported agent to the gatewayhandsoff status— view CLI statushandsoff logs— inspect logshandsoff stop— stop the daemon
Configuration
Most configuration is handled by handsoff init interactive wizard, which guides you through:
- Setting up your Claude Code hook server port
- Enabling Codex agent integration
- Configuring Telegram bot credentials
- Testing channel connectivity
For manual setup or advanced options, edit ~/.handsoff/config.toml. Key sections:
General settings:
default_agent— default agent to use (currentlyclaude)hook_server_port— local port for receiving agent hooks (default9876)log_level— logging verbosity (debug,info,warn,error)
Telegram channel:
bot_token— your Telegram bot token from BotFatherallowed_users— list of Telegram user IDs allowed to interact with the bot (e.g.,[123456789, 987654321])notify_types— which event types to forward (permission_request,question_request,finished,session_start,error)
App channel:
enabled— whether to start the App channel WebSocket server with the gatewaychannel_id— unique identifier for this app client (e.g.,app:desktop-001)auth_token— token used to authenticate app clientsheartbeat_interval_ms— optional heartbeat interval in milliseconds (default:30000)notify_types— which event types to forward (*,permission_request,question_request,finished,session_start,error)
Connect to: ws://localhost:<port>/ws/channels/app
Claude agent:
adapter— integration method (alwayshooks)binary— Claude Code binary namepermission_mode— how to handle tool permissions
Codex agent:
enabled— whether to start the Codex adapter with the gatewaycommand— command used to launchcodex app-serverwork_dir— default working directory for chat-triggered Codex sessionsmodel— default Codex model for new sessionsapproval_policy— default approval policy passed to Codex
Example ~/.handsoff/config.toml:
[general]
default_agent = "claude"
log_level = "info"
hook_server_port = 9876
[channel.telegram]
enabled = true
bot_token = "YOUR_BOT_TOKEN"
allowed_users = ["123456789"]
notify_types = ["permission_request", "question_request", "finished", "session_start", "error"]
[channels.app]
enabled = false
channel_id = "app:desktop-001"
auth_token = "change-me"
notify_types = ["*"]
heartbeat_interval_ms = 30000
[agent.claude]
adapter = "hooks"
binary = "claude"
work_dir = ""
model = ""
permission_mode = ""
[agent.codex]
enabled = true
command = ["codex", "app-server", "--listen", "stdio://"]
work_dir = ""
model = ""
approval_policy = ""Using Codex from Telegram / Feishu
After the gateway is running and the channel is bound to codex, send one of these commands in chat:
/codex <prompt>— run a Codex task in the current chat/codex status— inspect the active Codex session for the current chat/codex stop— stop the active Codex session/codex help— show the Codex command help
From source (development)
Requires Node.js >= 20.
# Clone the repo
git clone https://github.com/tapclaw/handsoff.git
cd handsoff
# Install dependencies
pnpm install
# Development mode
pnpm run dev -- init
# Run tests
pnpm test
# Build for production
pnpm run buildFuture plan
- Add more agent adapters
- Add more channel adapters
Notes
- Use
config.example.tomlas the starting point. - Runtime files are stored in
~/.handsoff.