@josharsh/demon-cli
v0.1.1
Published
AI-native daemon runtime — run persistent demons (the CLI host for @josharsh/demon)
Downloads
23
Maintainers
Readme
demon
Run AI that watches and acts on its own judgment — instead of waiting to be called.
demon.josharsh.com · GitHub · by Harsh Joshi · MIT
An agent is a function: you invoke it, it runs, it returns, it's gone. A demon is a process: it stays running, watches a domain you give it, and acts only when something actually warrants it. Same LLM underneath — a different shape around it.
You reach for an agent when you know what to ask. You reach for a demon for the things you'd never think to ask about in time: the PR that's been sitting four days, the error rate creeping up after a deploy, the customer who went quiet before renewal. Noticing is the job. An agent can't notice — it waits to be called.
Agent Demon
───────────────────────── ─────────────────────────
you trigger it it triggers itself
driven by a task driven by a purpose
finishes and exits runs continuously
starts fresh each time accumulates memory
the LLM runs the show the process calls the LLMQuickstart
npm install -g @josharsh/demon-cliDescribe what to watch in plain English. On first run, demon asks once which AI provider to use (or detects a key you already have):
demon start "watch open PRs on my-org/my-repo and flag the ones that need attention"
demon ps # what's running
demon logs my-demon --follow # watch it think
demon stop my-demonThat's the whole loop. A process, watching, with a purpose — quiet until something matters.
It's real. Here's one running.
This is unedited output from a demon watching a live GitHub repo, reasoning with Claude:
cycle #1 ACTED PR #564 is a dependabot bump (shell-quote 1.8.3→1.8.4),
one file, +6/−6, sitting since yesterday — flagged as a baseline.
cycle #2 WAITED Nothing new. #564 was already flagged last cycle.
cycle #3 WAITED No change. Still just #564, no movement.It acted once, then held — because it remembered it had already flagged that PR. That restraint is the point. A demon that pings you every cycle is just a cron job with a language model stapled on.
How it works
Every cycle, the process does one thing: observe → judge → act, or wait.
- Watchers observe a domain — GitHub PRs / CI / commits, an HTTP endpoint, a filesystem path.
- The LLM is asked a single question: given my purpose and what I just observed, does anything warrant acting? Most cycles, the honest answer is no, and it sleeps.
- Actions fire only when it decides to act — a notification, a webhook, a Slack message, a script, a GitHub comment.
- Memory is three layers: a raw event log, a bi-temporal fact store (when a fact stops being true is recorded, not erased — so nothing drifts), and a periodically-compressed narrative. A demon that's watched your repo for a week is worth more than one started this morning.
Define it in five lines if you'd rather be precise than describe:
name: pr-sentinel
purpose: Flag open PRs that are stale, oversized, or missing tests.
watch:
- type: github_prs
repo: my-org/my-repo
interval: 15m
action_mode: dry_run # log what it *would* do; switch to `auto` when you trust it
reasoning: on_change # cheap when idle: only call the model when something changed
heartbeat: 6h # ...but re-check at least this often so aging conditions aren't missed
actions:
- provider: notify
action: send
model: claude-sonnet-4-6A demon is cheap when idle. With reasoning: on_change (the default) it spends zero tokens on quiet cycles and only calls the model when a watcher reports a real change — so a mostly-quiet demon costs a few calls a day, not one per cycle. Set reasoning: always for maximum vigilance, or heartbeat: off for pure cost-saving.
Providers: Anthropic, OpenAI, OpenRouter, Groq, Gemini, Mistral, or local via Ollama — demon providers add <name> (it'll use a key from your environment or gh if you have one).
Embed it
The CLI is a host around a portable engine. The engine runs anywhere — Node, the browser, the edge — with no native dependencies:
npm install @josharsh/demonimport { runLoop, createLLMProvider, createMemoryKV } from '@josharsh/demon'
// supply your own watchers, actions, storage, clock — and it's a demon.The website demo is the same engine, running a demon in your browser that watches you.
What this is, and isn't
- It is infrastructure for long-running, judgment-driven AI processes. Bring your own model.
- It is not an agent framework, a workflow engine, or a model provider. It wraps the loop, the memory, and the action layer around whatever LLM you point it at.
- It is v0.1. The engine is tested and has run against real repositories. Rough edges remain:
cautiousmode currently behaves likeauto, reboot-persistence (demon install) is macOS/Linux only, and running a demon against a paid model on a short interval costs real tokens — set intervals deliberately.
Commands
| | |
|---|---|
| demon start <intent\|file.yaml> | start a demon (plain English or a manifest) |
| demon ps | list demons and their state |
| demon logs <name> [--follow] | stream its decisions |
| demon inspect <name> | reasoning + action history |
| demon stop <name> | stop it |
| demon install <name> | auto-start on reboot (launchd / systemd) |
| demon providers add <name> | connect an AI provider |
License
MIT.
Agents do work. Demons are present.
