
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
Connect your AI coding agents to Discord and Slack — get notified when tasks complete, see interactive prompts remotely, and send commands back.
aily
┌──────────────────────────────────────────────┐
│ │
│ Agent finishes task │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────┐ │
│ │ Notification │──▶│ post.sh │──┬──▶ Discord Thread
│ │ Hook │ │(dispatch)│ │ [agent] my-session
│ └──────────────┘ └──────────┘ │
│ └──▶ Slack Thread
│ Agent asks question [agent] my-session
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────┐ │
│ │ PreToolUse │──▶│ format + │──┬────────┘
│ │ Hook │ │ post │ └──▶ (both platforms)
│ └──────────────┘ └──────────┘
│ │
└──────────────────────────────────────────────┘
bridges (optional, bidirectional)
┌──────────────────────────────────────────────┐
│ │
│ Discord / Slack ──▶ SSH ──▶ tmux send-keys│
│ message in thread ──▶ agent input │
│ │
└──────────────────────────────────────────────┘
When an AI agent finishes a task, aily:
[agent] <tmux-session> on all configured platformsEach tmux session gets its own thread — notifications stay organized across projects and machines. Configure one platform or both.
🔔 Task Complete (claude)
🖥 Host: dev-server
📁 Project: my-app
⏰ Time: 2026-02-07 22:01:38
Response:
Fixed the bug in auth.ts by updating the token validation logic...
When Claude Code asks you a question (AskUserQuestion), aily forwards the choices immediately:
❓ Waiting for Input
📋 Approach
Which pattern should we use for the API client?
1️⃣ Singleton
Single shared instance, simpler but less testable
2️⃣ Factory
Create instances per-request, more flexible
3️⃣ Dependency injection
Register in container, best for testing
💬 Reply with option number (1, 2, 3) or type a custom answer
| Agent | Hook type | Extractor |
|---|---|---|
| Claude Code | Notification + PreToolUse | JSONL session parser |
| Codex CLI | notify | stdin message |
| Gemini CLI | AfterAgent | stdin JSON |
# Clone and install
git clone https://github.com/your-user/aily.git
cd aily
./install.sh
# Configure credentials
cp .env.example ~/.claude/hooks/.notify-env
chmod 600 ~/.claude/hooks/.notify-env
Edit ~/.claude/hooks/.notify-env with your platform credentials (one or both):
# Discord
DISCORD_BOT_TOKEN="your-bot-token"
DISCORD_CHANNEL_ID="your-channel-id"
# Slack
SLACK_BOT_TOKEN="xoxb-your-slack-bot-token"
SLACK_APP_TOKEN="xapp-your-slack-app-level-token"
SLACK_CHANNEL_ID="C0123456789"
Platforms are auto-detected from available tokens. Run aily status to verify.
aily)DISCORD_BOT_TOKEN)botSend Messages, Create Public Threads, Send Messages in Threads, Manage Threads, Read Message HistoryDISCORD_CHANNEL_ID:
Go to Slack API: Your Apps
Click Create New App → From scratch → name it (e.g., aily) → select your workspace
Enable Socket Mode (required for the bridge):
connections:write scope → name it (e.g., aily-socket)xapp-...) → this is your SLACK_APP_TOKENSet Bot Token Scopes:
chat:write — post messages and thread replieschannels:history — read channel messages (find threads)channels:read — list channelsreactions:write — add :lock: reaction on thread archiveSubscribe to Events (required for the bridge):
message.channels — messages in public channelsmessage.groups — messages in private channels (if needed)Install the App:
xoxb-...) → this is your SLACK_BOT_TOKENGet the Channel ID:
C) → this is your SLACK_CHANNEL_IDInvite the bot to the channel:
/invite @aily (or whatever you named your app)The install script automatically:
~/.claude/hooks/~/.codex/config.toml for Codex CLI~/.gemini/settings.json for Gemini CLIAdd to ~/.claude/settings.json:
{
"hooks": {
"Notification": [
{
"hooks": [{
"type": "command",
"command": "bash ~/.claude/hooks/notify-claude.sh",
"statusMessage": "Notifying..."
}]
}
],
"PreToolUse": [
{
"matcher": "AskUserQuestion",
"hooks": [{
"type": "command",
"command": "bash ~/.claude/hooks/ask-question-notify.sh",
"statusMessage": "Forwarding question to Discord..."
}]
}
]
}
}
Add to ~/.codex/config.toml:
notify = ["python3", "~/.claude/hooks/notify-codex.py"]
If you use oh-my-prompt, chain both hooks:
notify = "bash ~/.claude/hooks/notify-codex-wrapper.sh"
Add to ~/.gemini/settings.json:
{
"hooks": {
"AfterAgent": [
{
"hooks": [{
"type": "command",
"command": "~/.claude/hooks/notify-gemini.sh",
"name": "discord-notify",
"timeout": 10000
}]
}
]
}
}
Manage threads and tmux session sync from the command line:
aily start [name] # Create thread for current/named tmux session
aily stop [name] # Archive thread for current/named tmux session
aily auto [on|off] # Toggle auto thread sync (or show status)
aily sessions # List tmux sessions across hosts
aily status # Show which platforms are configured
aily help # Show help
When run inside tmux without a name argument, aily start and aily stop auto-detect the current session.
New threads receive a welcome message with available commands:
Welcome to [agent] my-session
Type a message here to forward it to the tmux session.
Commands:
!sessions — list all sessions
!kill my-session — kill this session + archive thread
When install.sh runs, it sets tmux global hooks:
Toggle auto-sync without editing config files:
aily auto off # disable auto thread creation/archival
aily auto on # re-enable
aily auto # show current status
For persistence across tmux restarts, add to ~/.tmux.conf:
set-hook -g session-created "run-shell '~/.claude/hooks/thread-sync.sh create #{session_name}'"
set-hook -g session-closed "run-shell '~/.claude/hooks/thread-sync.sh delete #{hook_session_name}'"
The agent bridges support ! commands in the workspace channel or any thread:
| Command | Action |
|---|---|
!new <name> [host] | Create tmux session + thread |
!kill <name> | Kill tmux session + archive thread |
!sessions | List all sessions with sync status |
> !sessions
my-project host-1 synced
dev-api host-1 synced
data-pipeline host-2 no thread
old-experiment --- orphan thread
aily/
├── hooks/
│ ├── post.sh # Multi-platform dispatcher
│ ├── discord-lib.sh # Discord API functions
│ ├── discord-post.sh # Discord message posting
│ ├── slack-lib.sh # Slack API functions
│ ├── slack-post.sh # Slack message posting
│ ├── thread-sync.sh # tmux session lifecycle (multi-platform)
│ ├── notify-claude.sh # Claude Code notification hook
│ ├── notify-codex.py # Codex CLI notification hook
│ ├── notify-gemini.sh # Gemini CLI notification hook
│ ├── ask-question-notify.sh # AskUserQuestion prompt forwarder
│ ├── format-question.py # Formats interactive prompts
│ └── extract-last-message.py # JSONL session response extractor
├── aily # CLI tool (start/stop/auto/sessions/status)
├── agent-bridge.py # Discord ↔ tmux bridge + ! commands
├── slack-bridge.py # Slack ↔ tmux bridge + ! commands
├── install.sh # One-command setup
└── docs/
└── architecture.md # Detailed technical docs
( ... ) & disown; exit 0) and return immediately to avoid agent hook timeouts[agent] <session-name>) on each platformpost.sh auto-detects enabled platforms from tokens and dispatches in parallel! commands manage both from messaging platformsAskUserQuestion prompt is active, task-complete notifications are suppressed to avoid stale messagesEach machine needs its own clone and .notify-env. The install script handles symlinking and agent config automatically.
# On a new machine
git clone https://github.com/your-user/aily.git ~/workspace-ext/aily
cd ~/workspace-ext/aily
./install.sh
cp .env.example ~/.claude/hooks/.notify-env
chmod 600 ~/.claude/hooks/.notify-env
# Edit .notify-env with your Discord credentials
cd ~/workspace-ext/aily
git pull
./install.sh # re-symlinks any new hooks
Bridges enable bidirectional communication — send messages from Discord/Slack back to your tmux sessions, plus ! commands for session management.
Message in [agent] thread
→ bridge detects it
→ SSH + tmux send-keys to the right session
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python agent-bridge.py
Requires a Slack app with Socket Mode enabled and an app-level token (xapp-...).
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python slack-bridge.py
See docs/architecture.md for implementation details.
| Symptom | Fix |
|---|---|
| No notification | Check ~/.claude/hooks/.notify-env exists with valid tokens |
| Thread not found | Verify channel ID matches your channel |
| tmux session not detected | Ensure you're inside tmux (echo $TMUX) |
| Codex hook not firing | Check ~/.codex/config.toml has the notify line |
| Gemini hook not firing | Check ~/.gemini/settings.json has AfterAgent hook |
| macOS tmux path | Script tries /opt/homebrew/bin/tmux first, falls back to tmux |
| Platform not detected | Run aily status to check configured platforms |
curltmuxMIT
FAQs
CLI for aily — AI agent session bridge for Discord and Slack
The npm package aily-cli receives a total of 11 weekly downloads. As such, aily-cli popularity was classified as not popular.
We found that aily-cli demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.