Metadata-Version: 2.5
Name: nightsitter
Version: 0.2.0
Summary: Stop babysitting your AI agents — a night-shift watchdog for unattended agents (Claude Code, cron jobs, anything).
Project-URL: Homepage, https://github.com/minjong212121/nightsitter
Project-URL: Repository, https://github.com/minjong212121/nightsitter
Project-URL: Issues, https://github.com/minjong212121/nightsitter/issues
Author: minjong212121
License: MIT
License-File: LICENSE
Keywords: ai-agents,claude-code,dead-man-switch,monitoring,watchdog
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: psutil>=5.9
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: types-psutil; extra == 'dev'
Description-Content-Type: text/markdown

# nightsitter

**The watchdog for agents that stopped talking.**

[![CI](https://github.com/minjong212121/nightsitter/actions/workflows/ci.yml/badge.svg)](https://github.com/minjong212121/nightsitter/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/nightsitter)](https://pypi.org/project/nightsitter/)
[![Python](https://img.shields.io/pypi/pyversions/nightsitter)](https://pypi.org/project/nightsitter/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

![nightsitter demo — a dead agent caught, and a fleet snapshot](.github/demo.gif)

An agent that *needs* you will tell you. Claude Code's
[Remote Control](https://code.claude.com/docs/en/remote-control.md) pushes a
notification when a session asks for approval or input, and if you use it,
that case is already covered.

The failure that costs you the night is the opposite one: **the agent that
stops saying anything at all.** Wedged mid-task. Looping on the same three
tool calls. Killed by an OOM at 3am. The process is either healthy and doing
nothing, or gone — and nothing is coming to tell you, because the thing that
would tell you is the thing that broke.

nightsitter watches from the outside, so it still works when the agent can't
speak for itself.

```bash
pipx install nightsitter

nightsitter run -- python nightly_agent.py   # dead-man wrapper for ANY command
nightsitter watch                            # watch local Claude Code sessions
nightsitter status                           # one-shot fleet snapshot
nightsitter daemon install                   # keep watching after you close the terminal
```

## Any command, not just Claude Code

The wrapper is a first-class citizen, not an afterthought — cron'd agents,
LangChain scripts, scrapers, nightly jobs, anything with an exit code:

```bash
nightsitter run --label nightly-report --max-minutes 45 \
  --heartbeat-url https://hc-ping.com/<uuid> -- python nightly.py
```

- alerts on failure, and on the kill switch firing (SIGTERM → SIGKILL)
- `--max-minutes` bounds the runaway case that burns tokens until morning
- pings your healthchecks.io check on success, `/fail` on failure
- passes stdio through untouched, exits with the child's code

## What it detects in Claude Code sessions

`nightsitter watch` reads Claude Code's local transcripts — no hooks, no SDK,
no config inside the agent — and classifies every session by *progress*, not
process health:

| State | Meaning | Who else catches this |
|---|---|---|
| 🟡 **stalled** | process alive, mid-task, no activity — alive but stuck | nobody: a wedged agent has no reason to notify you |
| 🔴 **dead** | process gone mid-task | nobody: the notifier died with it |
| 🟠 waiting | turn closed — waiting on your approval or answer | Remote Control does this well; kept for setups without it |
| 🟢 active | transcript moving | — |
| ⚪ done | finished cleanly (silent by default) | — |

The two rows that matter are the ones nothing else covers. They are also the
hard ones: transcripts carry no PID, so deciding *which live process owns
which session* is a heuristic, and this tool shipped three wrong ones in two
days before the current approach (count live processes per working directory,
rank transcripts by last conversational event, top-N are alive).

## How this differs from Remote Control

| | Remote Control | nightsitter |
|---|---|---|
| Agent asks you something | ✅ push, and you can **answer from your phone** | ✅ alert only |
| Agent silently stops making progress | ❌ | ✅ |
| Agent process dies | ❌ (the reporter dies too) | ✅ |
| Non–Claude Code jobs (cron, scripts) | ❌ | ✅ |
| Runaway kill switch | ❌ | ✅ `--max-minutes` |

They compose well: Remote Control for *conversation*, nightsitter for
*silence*. If you only ever want to be pinged for approvals, use Remote
Control — it does that better than this ever will.

## Notifications

Zero-signup default: [ntfy.sh](https://ntfy.sh). Set a topic and install the
ntfy app:

```bash
export NIGHTSITTER_NTFY_TOPIC="your-secret-topic"
nightsitter test-notify
```

Telegram and desktop notifications are also supported — see
`~/.nightsitter/config.toml`:

```toml
[notify]
ntfy_topic = "your-secret-topic"
# telegram_token = "..."
# telegram_chat_id = "..."
# desktop = "on"
```

**If ntfy is slow or blocked on your network, use Telegram.** ntfy.sh is the
default because it needs no account, but on some networks (measured from a
Korean ISP) publishes take 20–30s and time out — sometimes *after* the server
accepted the message, which looks like a failure and costs you a duplicate
ping on the retry. `nightsitter test-notify` tells you which channels actually
work from where you are. Configure more than one: alerts fan out to every
channel, a channel that keeps failing is dropped once another has delivered,
and a channel that fails three times in a row is skipped for a few minutes —
so a flaky channel can neither spam you nor slow the watcher down.

## Run it as a service

The watcher only helps if it outlives your terminal:

```bash
nightsitter daemon install    # macOS launchd (KeepAlive) / Linux systemd --user
nightsitter daemon status
nightsitter daemon uninstall
```

Re-run `install` with different flags (`--stall-after 20`) to update in place.

## Status

v0.2 — local CLI, free forever (MIT).

Most of the work since v0.1 went into *not* alerting: repeats decay, a channel
that keeps failing is skipped rather than retried into duplicates, and alert
history survives a restart so a reboot does not re-announce everything. The
detection was the easy half.

## Cloud (founding users)

A hosted version is coming: alert history, multi-machine fleet view, and a
mobile app. Founding users lock in **$12/month forever** — fully refundable
any time before cloud ships, no questions asked.

**[→ Preorder nightsitter cloud](https://buy.polar.sh/polar_cl_lc0sAsgUJek5gx6ijNKvSBDaufLgE3vA2boze24WL5d)**
· or [join the free waitlist](https://forms.gle/Am9guGJnoHUncm9i9) — no payment, just a ping when it ships.

The local CLI stays free forever; preorders fund the hosted version.
