জিগাই — Tool-agnostic terminal notification system for AI coding agents
Project description
JigAi (জিগাই)
জিগাই — Bangla for "asking" — Know when your AI agent is waiting for you.
A tool-agnostic terminal notification system that watches AI coding agents — Claude Code, Codex, Gemini CLI, Aider, or any custom tool — and notifies you via macOS notifications and optionally your phone over LAN the moment they go idle and need your input.
No hooks. No per-tool config. Just wrap your command and go.
pip install jigai
jigai watch claude
Table of Contents
- Prerequisites
- The Problem
- How JigAi Is Different
- How It Works
- Quick Start
- Supported Tools
- Notification Setup
- Known Issues and Caveats
- Configuration
- Commands
- Architecture
- Development
- Publishing to PyPI
- Roadmap
- Contributing
- License
Prerequisites
- macOS — notifications use macOS APIs. Linux and Windows support is planned.
- Python 3.10 or later
- terminal-notifier (strongly recommended) — required for banner popup notifications. Without it, notifications land silently in Notification Center.
brew install terminal-notifier
The Problem
You kick off Claude Code on a big refactor, switch to your browser to research something, and come back 20 minutes later to find it asked a clarifying question in the first 30 seconds. You just wasted 20 minutes.
Every AI coding tool has this problem. Existing solutions all have significant limitations:
- Hook-based tools (Claude Code Notifier, ntfy integrations) require per-tool configuration that breaks across versions and doesn't work with tools that don't have hooks.
- Cloud notification services (Pushover, ntfy.sh) require accounts, subscriptions, or sending your terminal output to a third-party server.
- Terminal replacements (cmux) require abandoning your existing terminal setup.
JigAi fixes this with a single transparent PTY proxy that watches any terminal output, detects idle patterns, and notifies you — locally, privately, instantly.
How JigAi Is Different
| Feature | ntfy / Pushover | Claude Code Notifier | cmux | JigAi |
|---|---|---|---|---|
| Works with any AI tool | ❌ manual wiring | ❌ Claude Code only | ✅ via hooks | ✅ auto-detect |
| No per-tool config needed | ❌ | ❌ | ❌ hooks required | ✅ |
| Auto-detects idle state | ❌ | ❌ hook-triggered | ❌ | ✅ |
| macOS notifications | ✅ | ✅ | ✅ | ✅ |
| LAN mobile push (no cloud) | ❌ cloud | ❌ | ❌ | ✅ |
| Works in any terminal | ✅ | ✅ | ❌ is the terminal | ✅ |
| Free and open source | ✅ ntfy | ❌ | ❌ | ✅ |
| Privacy-first (LAN only) | ❌ cloud | ✅ | ✅ | ✅ |
How It Works
JigAi wraps your AI tool in a PTY (pseudo-terminal) proxy. Your tool runs exactly as normal — same colors, same interactivity, same behavior. JigAi intercepts the output stream silently in the background:
You type: jigai watch claude
│
▼
┌──────────────────────┐
│ JigAi Watcher │ ← sits here transparently
│ (PTY proxy) │
└──────────┬───────────┘
│ all I/O passed through unchanged
▼
┌──────────────────────┐
│ Claude Code │ ← behaves as if launched directly
└──────────────────────┘
Idle detection uses three layers in order:
- Pattern match — recognizes the idle prompt for known tools instantly
- Timeout fallback — no output for N seconds = idle (works with any tool)
- Cooldown — suppresses repeated notifications during a single idle period
When idle is detected, JigAi fires:
- A macOS notification with the last meaningful output line
- A WebSocket push to the JigAi server (if running)
- Your phone receives the notification via the LAN server (mobile app coming in v0.2)
Quick Start
1. Install
pip install jigai
brew install terminal-notifier # recommended — enables banner popups
See Notification Setup for the one-time macOS configuration required.
2. Watch a tool
# Claude Code
jigai watch claude
# OpenAI Codex CLI
jigai watch codex
# Gemini CLI
jigai watch gemini
# Aider
jigai watch aider
# Any arbitrary command
jigai watch -- python my_agent.py
# Override tool name (custom idle prompts)
jigai watch --tool my_agent -- python agent.py
3. (Optional) Start the server for LAN mobile push
# Terminal 1 — keep the server running in the background
jigai server start
# Terminal 2 — watch your tool as normal
jigai watch claude
That's it. When your AI tool goes idle, you get notified.
Supported Tools
| Tool | Detection | Notes |
|---|---|---|
| Claude Code | Pattern + timeout | Built-in |
| OpenAI Codex CLI | Pattern + timeout | Built-in |
| Gemini CLI | Pattern + timeout | Built-in |
| Aider | Pattern + timeout | Built-in |
| OpenCode | Pattern + timeout | Built-in |
| Any custom tool | User-defined regex | Via ~/.jigai/patterns.yaml |
The timeout fallback (default: 30s of silence = idle) means JigAi works with any tool out of the box, even ones not listed above.
Notification Setup
JigAi tries two delivery mechanisms in order:
1. terminal-notifier — Recommended
Enables proper banner popup notifications.
brew install terminal-notifier
One-time macOS configuration:
- Open System Settings → Notifications → terminal-notifier
- Toggle Allow Notifications ON
- Check Desktop (required for the popup to appear on screen)
- Set Alert Style → Persistent (stays until dismissed) or Temporary (auto-dismisses after a few seconds)
- Toggle Play sound for notification ON
2. osascript — Fallback
No installation required. Notifications appear in Notification Center but may not show as popup banners on macOS Sequoia (15.x). See Known Issues.
Known Issues and Caveats
Read this before filing a bug — most common issues are documented here.
Focus Mode silences banner popups
Symptom: Notification appears in Notification Center, but the banner popup never shows — even with all settings correct.
Cause: macOS Focus (Do Not Disturb, Work, Personal, etc.) blocks notification banners from apps not on the allow list.
Fix:
- System Settings → Focus → select your active Focus profile
- Allowed Notifications → Apps → add terminal-notifier
One-time setup per Focus profile. This is the most common reason banners don't show.
terminal-notifier on macOS Sequoia (15.x)
Symptom: Notifications appear in Notification Center but never pop as banners, or no notifications appear at all.
Cause: terminal-notifier has a known compatibility issue on macOS 15, particularly on M-series chips. It predates the UserNotifications framework changes in Sequoia.
Workarounds (try in order):
-
Make sure Focus mode isn't blocking it (see above) — this is the cause 90% of the time.
-
Reset the notification registration cache:
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \ -kill -r -domain local -domain system -domain user
Then re-open System Settings → Notifications → terminal-notifier and re-enable.
-
If banners still don't appear, notifications will still land in Notification Center via the
osascriptfallback — you won't get the popup, but the sound will still play.
A native Swift notification backend is planned for v0.2 which will resolve this permanently.
Notifications fire while you're at the terminal
By default this is intentional — the sound cue is useful even when you're looking at the screen.
To suppress notifications when any terminal window is focused:
# ~/.jigai/config.yaml
notifications:
only_when_away: true
JigAi checks the frontmost macOS application. If it is Terminal, iTerm2, Warp, Ghostty, Alacritty, Kitty, Hyper, Tabby, or Rio, the notification is skipped.
Display mirroring or screen sharing
Symptom: Notifications don't appear while screen sharing or using a mirrored display.
Fix: System Settings → Notifications → enable "Allow notifications when mirroring or sharing the display"
Idle fires too early or too often
Cause: A pattern may match on output that looks like an idle prompt but isn't.
Fix: Raise the cooldown (minimum gap between notifications) or the timeout:
# ~/.jigai/config.yaml
detection:
timeout_seconds: 45 # wait longer before timeout-based idle
cooldown_seconds: 10 # minimum seconds between consecutive notifications
Debug which lines trigger detection:
jigai config test "the output line you want to test"
Pattern detection reliability for Claude Code
Claude Code renders a rich TUI with box-drawing characters and animated spinners. JigAi strips all ANSI codes and decorative Unicode before matching. If detection feels unreliable, fall back to a longer timeout:
jigai watch --timeout 60 claude
Mobile notifications (iOS / Android) — not yet available
The mobile app is planned for v0.2. jigai server start runs the WebSocket server that the React Native app will connect to — the server-side is already live.
iOS note: iOS aggressively suspends background WebSocket connections. The v0.2 mobile app will document foreground/background behavior in detail. For fully reliable background delivery on iOS, a push notification relay option will be offered as an opt-in.
Configuration
Initialize the config file with defaults:
jigai config init
~/.jigai/config.yaml — full reference
server:
port: 9384 # LAN server port
bind: "0.0.0.0" # Bind address (0.0.0.0 = all interfaces)
notifications:
macos: true # Enable macOS notifications
only_when_away: false # Skip notification if a terminal is focused
sound: "Ping" # macOS sound name: Ping, Basso, Funk, Glass, etc.
group_by_session: true # Group notifications per session in NC
show_last_output: true # Include last meaningful output line in body
output_lines: 3 # Lines of terminal context to capture
redact_patterns: # Patterns auto-redacted from notification body
- '(?i)(token|password|secret|key|api_key)=\S+'
detection:
timeout_seconds: 30 # Silence threshold for timeout-based idle
cooldown_seconds: 5 # Minimum gap between consecutive notifications
~/.jigai/patterns.yaml — custom tool patterns
custom_tools:
my_agent:
name: "My Custom Agent"
idle_patterns:
- 'READY>'
- 'awaiting instruction'
- '(?i)what would you like'
overrides:
timeout_seconds: 45
Commands
# Watching
jigai watch <cmd> # Wrap a command, notify on idle
jigai watch --tool <key> <cmd> # Override tool auto-detection
jigai watch --timeout 60 <cmd> # Override idle timeout
jigai watch --no-notify <cmd> # Disable macOS notifications for this session
jigai watch --no-server <cmd> # Don't push events to the server
# Server (LAN / mobile notifications)
jigai server start # Start on default port 9384
jigai server start --port 8080 # Custom port
jigai server status # Check if server is running
# Config
jigai config init # Create ~/.jigai/config.yaml with defaults
jigai config show # Print current configuration as JSON
jigai config test "<line>" # Test if a line matches any idle pattern
# Info
jigai patterns # Show all loaded patterns and timeout settings
jigai sessions # List active sessions (requires server running)
jigai --version # Print version
Architecture
┌──────────────────────────────────────────────────────────┐
│ jigai watch claude │
├──────────────────────────────────────────────────────────┤
│ │
│ stdin ──▶ PTY Proxy ──▶ Claude Code │
│ (pty_proxy) (child process) │
│ │ │
│ stdout ◀───────┤ (passed through unchanged) │
│ │ │
│ ▼ │
│ Idle Detector │
│ (detector.py) │
│ • strip ANSI / box-drawing chars │
│ • pattern match ──▶ match found ──┐ │
│ • timeout check ──▶ N seconds ────┤ │
│ ▼ │
│ ┌────────────────┐ │
│ │ macOS banner │ │
│ │ (terminal-nf.) │ │
│ │ Server push │ │
│ │ (HTTP → WS) │ │
│ └────────────────┘ │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────┐
│ jigai server start │
│ FastAPI + WebSocket │
│ mDNS/Bonjour broadcast │──▶ Mobile App (v0.2)
│ REST API for session tracking │ React Native, LAN-only
└──────────────────────────────────┘
Development
git clone https://github.com/nafistiham/jigai.git
cd jigai
pip install -e ".[dev]"
pytest # run tests
ruff check . # lint
mypy jigai/ # type check
Project Structure
jigai/ ← repo root
├── jigai/ ← Python package
│ ├── cli.py # Typer CLI entry point
│ ├── config.py # Config management (Pydantic + YAML)
│ ├── models.py # Session and IdleEvent data models
│ ├── notifier/
│ │ └── macos.py # macOS notifications (osascript + terminal-notifier)
│ ├── server/
│ │ ├── app.py # FastAPI REST + WebSocket server
│ │ ├── client.py # HTTP client (watcher → server push)
│ │ ├── discovery.py # mDNS/Bonjour service broadcasting
│ │ └── ws_manager.py # WebSocket connection manager
│ ├── patterns/
│ │ └── defaults.yaml # Built-in tool patterns (shipped with package)
│ └── watcher/
│ ├── detector.py # Idle detection engine
│ ├── patterns.py # Pattern registry and loader
│ ├── pty_proxy.py # Transparent PTY proxy
│ └── watcher.py # Session orchestrator
└── tests/
Publishing to PyPI
For maintainers.
First release
PyPI's Trusted Publishing requires the project to be registered before it can be used. The cleanest first-publish flow:
Option A — Pending Publisher (no manual upload needed):
- Register at pypi.org
- Go to pypi.org/manage/account/publishing
- Under "Add a new pending publisher", fill in:
- PyPI project name:
jigai - Owner:
nafistiham, Repository:jigai - Workflow filename:
publish.yml - Environment:
release
- PyPI project name:
- Create a GitHub Release with tag
v0.1.0— the workflow will create the PyPI project and publish in one step.
Option B — Manual first upload, then Trusted Publishing for subsequent releases:
pip install build twine
python -m build
twine upload dist/* # prompts for PyPI credentials
Then set up Trusted Publishing on your existing project for future releases.
Subsequent releases
- Bump
versioninpyproject.toml - Create a GitHub Release with tag
vX.Y.Z - The publish workflow runs automatically via Trusted Publishing — no tokens to manage.
Roadmap
- v0.1 — CLI · PTY proxy · idle detection · macOS notifications · LAN WebSocket server
- v0.2 — React Native mobile app (iOS + Android) · daemon mode
- v0.3 — Homebrew formula · Linux support (libnotify)
- v0.4 — Native Swift notification backend (fixes Sequoia) · web dashboard
- Future — Slack/Discord webhooks · Windows toast notifications
Contributing
Contributions are welcome. For non-trivial changes, please open an issue first so we can align on the approach before you invest time writing code.
Branch strategy
main ← stable, matches latest release
develop ← integration branch, target for PRs
feat/* ← feature branches, branched from develop
fix/* ← bug fix branches, branched from develop
Always open PRs against develop, not main.
Workflow
# Fork the repo, then:
git clone https://github.com/<your-username>/jigai.git
cd jigai
git checkout develop
git checkout -b feat/your-feature
pip install -e ".[dev]"
# Make changes, write tests
pytest
ruff check .
# Commit using conventional commits
git commit -m "feat(watcher): add support for foo tool"
git commit -m "fix(notifier): handle edge case on Sonoma"
git commit -m "docs: update custom patterns example"
git push -u origin feat/your-feature
# Open a PR → base: develop
Commit convention
feat(scope): new feature
fix(scope): bug fix
docs: documentation only
test: tests only
ci: CI/CD changes
chore: build, config, tooling
License
MIT — see LICENSE.
Built with frustration at missed Claude Code prompts.
github.com/nafistiham/jigai
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jigai-0.1.0.tar.gz.
File metadata
- Download URL: jigai-0.1.0.tar.gz
- Upload date:
- Size: 37.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d32322f6a3e71a2645ef5c5ee231ac441f518219ff969061cc34b0430c634043
|
|
| MD5 |
86ed46c55026fb644709c11279568e88
|
|
| BLAKE2b-256 |
a326e05bb53be5db12ddae0a9b54f09acb539a8fb0eadf464cc7d92bc5ff115b
|
Provenance
The following attestation bundles were made for jigai-0.1.0.tar.gz:
Publisher:
publish.yml on nafistiham/jigai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jigai-0.1.0.tar.gz -
Subject digest:
d32322f6a3e71a2645ef5c5ee231ac441f518219ff969061cc34b0430c634043 - Sigstore transparency entry: 1003352486
- Sigstore integration time:
-
Permalink:
nafistiham/jigai@20aadd120002ad1008355769eee28b1b23dc23e8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nafistiham
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20aadd120002ad1008355769eee28b1b23dc23e8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file jigai-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jigai-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db75f8efd5ac211e5a5004bfefc855bf06295190230998dbb5dc1721b01cce7b
|
|
| MD5 |
0f2425173d74222a44066faad5e4f195
|
|
| BLAKE2b-256 |
37369394ab972201c5fec574252d4d9afe4e7c253ef5848d057627aeffd47507
|
Provenance
The following attestation bundles were made for jigai-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on nafistiham/jigai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jigai-0.1.0-py3-none-any.whl -
Subject digest:
db75f8efd5ac211e5a5004bfefc855bf06295190230998dbb5dc1721b01cce7b - Sigstore transparency entry: 1003352487
- Sigstore integration time:
-
Permalink:
nafistiham/jigai@20aadd120002ad1008355769eee28b1b23dc23e8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/nafistiham
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20aadd120002ad1008355769eee28b1b23dc23e8 -
Trigger Event:
release
-
Statement type: