ghost-code 0.6.1

Ghostty + Claude Code Telegram integration — notifications, streaming chat, and terminal injection
# ghost-code

Ghostty + [Claude Code](https://docs.anthropic.com/en/docs/claude-code) Telegram bridge. Get notified on your phone when Claude finishes a task or needs input — reply to inject text back into the terminal, or chat with Claude directly via streaming responses.

## Features

- **Stop hook** — sends task completion summaries (silent, with expandable blockquote for long text)
- **Notification hook** — forwards Claude's attention requests
- **PreToolUse hook** — tool approval via inline Allow/Deny buttons (expandable details)
- **StatusLine hook** — real-time status bar showing model, cost, plan usage, context window, and tool stats
- **Streaming chat** — send messages to Claude from Telegram, responses stream live via `sendMessageDraft`
- **Session reply** — reply to any notification to inject text into the originating Ghostty tab, with target project confirmation to prevent misrouting
- **Multi-machine support** — all messages, commands, and sessions include hostname (e.g. `project@MacBook-Air`) to distinguish notifications across devices
- **Noise filtering** — suppresses noisy system notifications (quota recovery, session resumed, waiting for input)
- **Sleep prevention** — auto-runs `caffeinate` for 1 hour on every Telegram interaction to keep macOS awake
- **Single instance** — flock-based PID locking ensures only one daemon runs at a time
- **Graceful shutdown** — signal handling (SIGINT/SIGTERM) with PID file cleanup

Single binary, no runtime dependencies.

## Prerequisites

| Requirement | Version | Check |
|-------------|---------|-------|
| [Rust]https://rustup.rs | 1.70+ | `cargo --version` |
| [Claude Code]https://docs.anthropic.com/en/docs/claude-code | any | `claude --version` |
| [Ghostty]https://ghostty.org | any | only needed for session reply |

## Installation

### Step 1: Create a Telegram bot

1. Open Telegram and search for [@BotFather]https://t.me/BotFather
2. Send `/newbot` and follow the prompts to pick a name and username
3. BotFather replies with a **bot token** — save it (looks like `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`)
4. Open a chat with your new bot and send any message (this initializes the chat)
5. Get your **chat ID** by messaging [@userinfobot]https://t.me/userinfobot — it replies with your numeric ID

### Step 2: Install

**Recommended** — install from crates.io:

```bash
cargo install ghost-code
```

Then run the setup script to configure Claude Code hooks:

```bash
ghost-code setup
```

**Alternative** — install from source:

```bash
git clone https://github.com/sunoj/ghost-code.git
cd ghost-code
bash install.sh
```

The install script builds the binary, copies it to `~/.claude/hooks/ghost-code`, merges hook entries into `~/.claude/settings.json`, and creates `~/.claude/hooks/ghost-code.env` from the template.

### Step 3: Configure

Edit `~/.claude/hooks/ghost-code.env` with the token and chat ID from Step 1:

```bash
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
TELEGRAM_CHAT_ID=987654321
```

Values can optionally be quoted (`"value"` or `'value'`).

### Step 4: Verify

```bash
ghost-code test
```

You should receive two Telegram messages:
- A **silent** stop-hook summary
- A **notification** message

If nothing arrives, see [Troubleshooting](#troubleshooting) below.

## Bot daemon

The bot daemon handles callback queries, session replies, and streaming chat. It is auto-started by hook commands, or you can run it manually:

```bash
~/.claude/hooks/ghost-code bot
```

Send any text message to chat with Claude — responses stream live using `sendMessageDraft`. Reply to any hook notification to inject text into the originating Ghostty terminal tab.

### Bot commands

| Command        | Description                              |
|----------------|------------------------------------------|
| `/help`        | Show help (includes hostname)            |
| `/sessions`    | List active sessions on this machine     |
| `/dir [path]`  | Get/set working directory                |
| `/status`      | Show bot status with hostname and PID    |
| `/stop`        | Stop the bot                             |

### Configuration

All settings can be set in `ghost-code.env` or as environment variables (env vars take precedence):

| Variable            | Default                            | Description                                  |
|---------------------|------------------------------------|----------------------------------------------|
| `TELEGRAM_BOT_TOKEN`| (required)                         | Telegram bot token from BotFather            |
| `TELEGRAM_CHAT_ID`  | (required)                         | Your Telegram chat ID                        |
| `WORKING_DIR`       | `~/.claude/ghost-code/workspace`   | Working directory for `claude -p`            |
| `CLAUDE_TIMEOUT`    | `300`                              | Execution timeout in seconds                 |
| `APPROVAL_TOOLS`    | (empty)                            | Comma-separated tool names requiring approval|
| `APPROVAL_TIMEOUT`  | `120`                              | Seconds to wait for approval response        |
| `STATUSLINE`        | `true`                             | Enable statusline hook (see below)           |
| `DEBUG`             | `false`                            | Log raw hook JSON to debug file              |

### Tool approval

To require Telegram approval before Claude executes certain tools, set `APPROVAL_TOOLS`:

```
APPROVAL_TOOLS=Bash,Write,Edit
```

When Claude tries to use a listed tool, you'll receive a Telegram message with Allow/Deny buttons. The hook blocks until you respond or the timeout expires (defaults to deny).

## Statusline

The statusline hook adds a real-time status bar to Claude Code showing:

```
🤖 Opus 4.6 | 💰 $5 / $463 today | 📊 82% block · 38% weekly | 🧠 25% | 🔧 RTK 80% 3.6M | 🌐 WS 93% 3.3K | 📡 TG
```

| Component | Source | Description |
|-----------|--------|-------------|
| 🤖 Model | Claude Code session | Current model name |
| 💰 Costs | JSONL scan | Session cost / today's total |
| 📊 Plan | Anthropic API | Block (5h) and weekly usage with reset times |
| 🧠 Context | Claude Code session | Context window usage percentage |
| 🔧 RTK | `rtk gain` | Token savings from [RTK]https://github.com/sunoj/rtk (auto-detected) |
| 🌐 WS | `websummary stats` | Token savings from websummary (auto-detected) |
| 📡 TG | Bot daemon | Telegram bot status |

**Auto-detection**: RTK and websummary stats are shown only if the respective tools are installed. If `rtk` or `websummary` commands are not found, those sections are silently omitted.

### Enabling / disabling

The statusline hook is installed by default. To disable it:

1. Set `STATUSLINE=false` in `~/.claude/hooks/ghost-code.env`
2. Re-run `bash install.sh`

This removes the StatusLine hook from `~/.claude/settings.json`. Set back to `true` and re-run to re-enable.

## Telegram API features

This project uses modern Telegram Bot API features:

- **`link_preview_options`** — replaces deprecated `disable_web_page_preview`
- **`sendMessageDraft`** — streams partial responses as Claude generates them (Bot API 9.3+)
- **`disable_notification`** — stop hook sends silently (no ring)
- **Expandable blockquotes** (`<blockquote expandable>`) — long summaries and tool details are collapsible

## Hook data format

The binary reads JSON from stdin as provided by Claude Code hooks:

| Hook         | Key field                | Description                          |
|--------------|--------------------------|--------------------------------------|
| Stop         | `last_assistant_message` | Claude's final response text         |
| Notification | `message`, `title`       | What Claude needs from you           |
| PreToolUse   | `tool_name`, `tool_input`| Tool about to be executed            |
| StatusLine   | `model`, `cost`, `context_window` | Session data (piped to stdin) |

## Logging

The bot and hook handlers output structured log lines to stderr with timestamps and contextual tags:

```
12:34:56.789 [bot] started (PID 12345)
12:34:56.790 [bot] chat_id=123456789
12:34:56.790 [bot] working_dir=~/.claude/ghost-code/workspace
12:34:57.001 [poll] received 1 update(s)
12:34:57.002 [msg] from=Peter msg_id=42 len=15: hello claude
12:34:57.003 [claude] starting: dir=~/.claude/ghost-code/workspace timeout=300s prompt=hello claude
12:34:57.050 [claude] spawned pid=67890
12:34:58.100 [claude] streaming... 256chars, 1 drafts sent
12:35:02.100 [claude] done in 5.1s (512 chars, 6 drafts) status=Ok(ExitStatus(0))
[telegram] sent msg_id=Some(43) len=512 mode=Some("HTML")
```

Hook handlers log with `[hook:stop]`, `[hook:notification]`, and `[hook:pre-tool-use]` tags.

### Debug mode

Set `DEBUG=true` in the `.env` file (or as an env var). Raw hook JSON will be logged to `~/.claude/hooks/ghost-code.debug.log`. This is in addition to the standard stderr logging described above.

## Updating

```bash
cargo install ghost-code
```

If installed from source: `cd ghost-code && git pull && bash install.sh`

## Uninstalling

```bash
cargo uninstall ghost-code
rm -f ~/.claude/hooks/ghost-code ~/.claude/hooks/ghost-code.env ~/.claude/hooks/ghost-code.pid
```

Then remove the ghost-code hook entries from `~/.claude/settings.json` (under `hooks.Stop`, `hooks.Notification`, `hooks.PreToolUse`, `hooks.StatusLine`).

## Manual configuration

If you prefer not to use the install script, add this to `~/.claude/settings.json`:

```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/ghost-code stop"
          }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/ghost-code notification"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/ghost-code pre-tool-use"
          }
        ]
      }
    ],
    "StatusLine": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/ghost-code statusline"
          }
        ]
      }
    ]
  }
}
```

The StatusLine hook is optional — omit it if you don't want the status bar.

## License

MIT