π macOS Β· π§ Linux Β· WSL
Recommended β npm
npm install -g sanook-cli
# or run once without installing:
npx sanook-cli
One-liner install script (checks Node, then installs via npm)
# GitHub raw (default)
curl -fsSL https://raw.githubusercontent.com/Sir-chawakorn/sanook-cli/main/scripts/install.sh | bash
# GitHub Pages mirror
curl -fsSL https://sir-chawakorn.github.io/sanook-cli/install.sh | bash
# jsDelivr CDN
curl -fsSL https://cdn.jsdelivr.net/gh/Sir-chawakorn/sanook-cli@main/scripts/install.sh | bash
Homebrew
brew trust Sir-chawakorn/tap # once on newer Homebrew
brew tap Sir-chawakorn/tap
brew install sanook-cli
πͺ Windows
Recommended β npm (PowerShell or cmd)
npm install -g sanook-cli
# or:
npx sanook-cli
One-liner install script
# GitHub raw (default)
irm https://raw.githubusercontent.com/Sir-chawakorn/sanook-cli/main/scripts/install.ps1 | iex
# GitHub Pages mirror
irm https://sir-chawakorn.github.io/sanook-cli/install.ps1 | iex
WinGet (after PR #391114 merges)
winget install Sanook.SanookCLI
Portable zip (no WinGet yet): sanook-cli-win-x64.zip
| Method | Command | Status |
|---|---|---|
| npm / npx | npm install -g sanook-cli |
β npm Β· latest 0.5.7 |
| curl | bash | curl -fsSL β¦/install.sh | bash |
β raw Β· Pages Β· jsDelivr |
| irm | iex | irm β¦/install.ps1 | iex |
β raw Β· Pages |
| Homebrew | brew tap Sir-chawakorn/tap && brew install sanook-cli |
β homebrew-tap |
| GitHub Pages | curl -fsSL https://sir-chawakorn.github.io/sanook-cli/install.sh | bash |
β Live |
| WinGet | winget install Sanook.SanookCLI |
β³ PR #391114 Β· CLA signed Β· release zip ready |
| sanook.ai (optional short URL) | curl -fsSL https://sanook.ai/install.sh | bash |
β³ DNS at GoDaddy β run bash scripts/configure-sanook-ai-dns.sh |
Full maintainer guide: docs/INSTALL_INFRA.md
Troubleshooting
Symptom Fix 'sanook' is not recognizedYou ran npm i sanook-cliwithout-gβ reinstall withnpm install -g sanook-cli, or usenpx sanookNode too old Install Node β₯ 22 Β· scripts refuse older versions Homebrew refuses tap Run brew trust Sir-chawakorn/taponce, then retryNot sure what's wrong npx sanook doctorβ prints OS-specific fixes (incl. Windows PATH one-liner)
Overview
Close the terminal. Come back tomorrow. Sanook still knows what you decided.
Sanook is a transparent coding agent for your terminal β one loop, no magic:
prompt β LLM β tool call β result β loop β answer
Wrapped in the things real work needs: a permission gate, structured memory across sessions, a long-running gateway with cron and chat channels, on-demand skills, MCP servers, and first-class git awareness.
BYOK by design. Every provider uses a direct API key from that provider's console β Sanook never reuses OAuth or subscription credentials, because that violates provider terms and gets accounts banned.
How it compares
The agent loop, BYOK, and MCP are table stakes now. What Sanook has that the big vendor CLIs don't is memory that survives the session β a structured Obsidian "second brain" the agent reads at the start of every run.
| Sanook | Claude Code | Codex CLI | Gemini CLI | |
|---|---|---|---|---|
| Open-source | β | β | β | β |
| Bring your own key | β | β | β | β |
| Providers | 9 | 1 | 1 | 1 |
| Local models (Ollama / LM Studio) | β | β | β | β |
| MCP (stdio + remote HTTP) | β | β | β | β |
| OS sandbox (Seatbelt / bubblewrap) | β | β | β | β |
| Checkpoint / rewind | β | β | β | β |
| Image / vision input | β | β | β | β |
| Prompt caching | β | β | β | β |
| Durable cross-session memory | β | β | β | β |
| Local gateway + cron + messaging | β | β | β | β |
On raw benchmark scores the frontier vendors win β Sanook's bet is portability + persistent memory, not beating Opus on SWE-bench. Use whatever fits; this one remembers.
Memory & second brain
Sanook does not dump everything into one folder. Memory is layered β some lives in your Obsidian vault, some in ~/.sanook/ for speed and resume. Both are wired together at runtime.
flowchart LR
START([Start<br/>sanook / gateway / cron / resume]):::start
CFG["Load config<br/>~/.sanook/config.json"]:::local
MODE{"brainPath is set<br/>and vault is reachable?"}:::gate
LOCALONLY([Fallback end<br/>answer works with local session + usage only]):::warn
subgraph read["1. Read path Β· gather memory before the turn"]
direction TB
HIER["Hierarchical SANOOK.md<br/>global -> repo -> cwd instructions"]:::inject
AM["Auto-memory<br/>~/.sanook/memory/memory.json"]:::local
RESUME["Resume context<br/>~/.sanook/sessions/*.json"]:::local
HOT["Vault hot files<br/>AI-Context-Index + current-state + inbox"]:::vault
PROJECT["Project hot context<br/>Projects/<slug>/ matched from cwd"]:::vault
PACK["Context pack<br/>Shared/Context-Packs/*.md selected by task"]:::vault
SEARCH["Per-turn retrieval<br/>search/index.json over vault + memory + sessions + skills"]:::local
end
subgraph inject["2. Injection Β· build the model input"]
direction TB
STATIC["Static brain block<br/>stable context injected at session start"]:::inject
RECALLED["Recalled context block<br/>fresh matches for this prompt"]:::inject
SYS["System prompt<br/>policy + tools + memory + repo state"]:::inject
USER["User prompt<br/>or incoming gateway message"]:::process
end
subgraph loop["3. Agent loop Β· act, observe, answer"]
direction TB
LLM["LLM reasoning step"]:::process
NEED{"Need tools?"}:::gate
TOOLS["Tool calls<br/>files, shell, git, MCP, skills"]:::process
RESULT["Tool results<br/>fed back into context"]:::process
ANSWER["Final answer<br/>terminal or gateway reply"]:::finish
end
subgraph write["4. Write-back Β· make the next session smarter"]
direction TB
REMEMBER["remember tool<br/>redact -> merge -> rank"]:::write
MEMJSON["Persist auto-memory<br/>memory.json + MEMORY.md mirror"]:::write
INBOX["Append candidate fact<br/>Shared/Memory-Inbox/memory-inbox.md"]:::write
WORKLOG["Append daily worklog<br/>Sessions/YYYY-MM-DD-worklog.md"]:::write
CHAT["Append full transcript<br/>Sessions/*-chat.md when enabled"]:::write
SESSION["Save session JSON<br/>~/.sanook/sessions/*.json"]:::write
USAGE["Record usage<br/>~/.sanook/usage/events.jsonl"]:::write
INDEX["Re-index changed sources<br/>sanook index -> search/index.json"]:::write
END([End<br/>answer returned + memory ready for resume/retrieval]):::finish
end
START -->|entrypoint starts| CFG
CFG --> MODE
MODE -->|yes| HOT
MODE -->|no vault path| LOCALONLY
CFG --> AM
CFG --> RESUME
CFG --> SEARCH
HIER --> STATIC
AM --> STATIC
HOT --> STATIC
PROJECT --> STATIC
PACK --> RECALLED
RESUME --> RECALLED
SEARCH --> RECALLED
STATIC --> SYS
RECALLED --> SYS
USER --> LLM
SYS -->|injected every turn| LLM
LLM --> NEED
NEED -->|yes| TOOLS
TOOLS --> RESULT
RESULT --> LLM
NEED -->|no| ANSWER
LLM -->|durable fact found| REMEMBER
REMEMBER --> MEMJSON
REMEMBER --> INBOX
ANSWER --> WORKLOG
ANSWER --> CHAT
ANSWER --> SESSION
ANSWER --> USAGE
MEMJSON --> INDEX
INBOX --> INDEX
WORKLOG --> INDEX
CHAT --> INDEX
SESSION --> INDEX
ANSWER --> END
classDef start fill:#064e3b,stroke:#34d399,color:#ffffff,stroke-width:3px
classDef finish fill:#0f766e,stroke:#5eead4,color:#ffffff,stroke-width:3px
classDef gate fill:#7c2d12,stroke:#fdba74,color:#ffffff,stroke-width:2px
classDef local fill:#172554,stroke:#60a5fa,color:#ffffff
classDef vault fill:#312e81,stroke:#a78bfa,color:#ffffff
classDef inject fill:#164e63,stroke:#22d3ee,color:#ffffff
classDef process fill:#1f2937,stroke:#94a3b8,color:#ffffff
classDef write fill:#78350f,stroke:#fbbf24,color:#ffffff
classDef warn fill:#7f1d1d,stroke:#fca5a5,color:#ffffff,stroke-width:2px
How to read the flow: green nodes mark start/end, orange diamonds are decisions, blue nodes are local runtime state, purple nodes live inside the Obsidian-style vault, cyan nodes are injected into the system prompt, dark nodes are the active agent loop, amber nodes write memory back after/during the turn, and the red node is the safe fallback when no vault is configured.
What goes where
| Data | Stored at | When | Purpose |
|---|---|---|---|
| Worklog | {brainPath}/Sessions/YYYY-MM-DD-worklog.md |
After every agent turn (REPL + headless) | Daily trace of prompts & cost summary |
| Full transcript (opt-in) | {brainPath}/Sessions/YYYY-MM-DD-<id>-chat.md |
After every turn, when brainTranscript is on |
The actual conversation β your prompt and the AI's reply |
| Session note | {brainPath}/Sessions/YYYY-MM-DD-<id>-session.md |
On REPL exit (/quit or Ctrl+C at empty prompt) |
AI/heuristic summary + key facts |
| Memory inbox | {brainPath}/Shared/Memory-Inbox/memory-inbox.md |
When the agent calls remember |
Candidate facts for vault curation |
| Project workspace | {brainPath}/Projects/<slug>/ |
Setup wizard / sanook brain init links your repo |
Project-scoped notes & hot context |
| Persona profile | {brainPath}/Shared/User-Persona/persona.md |
sanook persona or /persona in REPL |
Who you are + how you want the agent to work |
| Auto-memory | ~/.sanook/memory/memory.json |
When the agent calls remember, or sanook persona (protected) |
Structured facts (merge, rank, inject) |
| Session JSON | ~/.sanook/sessions/*.json |
Every turn | --continue / --resume transcript |
| Search index | ~/.sanook/search/ |
sanook index (incremental) |
BM25 / hybrid retrieval |
| Usage ledger | ~/.sanook/usage/events.jsonl |
Every turn | Token/cost tracking β not semantic memory |
Read at session start: hierarchical SANOOK.md (global β project root β cwd), auto-memory block, vault hot files (AI-Context-Index, current-state, inbox, matched Projects/<slug>/), plus per-turn retrieval over vault + memory + past sessions.
Setup links your repo to the vault: the first-run wizard (or sanook brain init) saves brainPath in ~/.sanook/config.json, scaffolds Projects/<slug>/ inside the vault, and creates SANOOK.md in your repo if missing.
sanook brain init # pick vault folder + identity
sanook brain context # inspect what gets injected from cwd
sanook brain projects list # vault projects β repo paths
sanook memory stats # auto-memory store overview
sanook usage daily # token/cost ledger (ccusage-style)
Want the entire conversation in your vault? Turn on full transcripts β every prompt and reply is appended to Sessions/*-chat.md per session:
sanook config set brainTranscript on # or env: SANOOK_BRAIN_TRANSCRIPT=1
It is opt-in because verbatim transcripts of long coding sessions grow a vault fast. Worklog (always on) keeps a lighter daily trace; the full transcript is the complete record.
Disable persistence: SANOOK_DISABLE_PERSISTENCE=1 Β· worklog only: SANOOK_DISABLE_WORKLOG=1 Β· usage ledger: SANOOK_DISABLE_USAGE=1
Quickstart
Already installed? Jump straight to setup β see Install if you haven't yet.
Run the setup wizard or set an API key manually:
sanook setup # provider + model wizard; offers to create a second brain
sanook model # re-run provider/model setup later
sanook persona # tell the agent who you are + how you like to work
sanook auth add anthropic --api-key sk-ant-... --use
export ANTHROPIC_API_KEY=sk-ant-... # macOS / Linux
setx ANTHROPIC_API_KEY "sk-ant-..." # Windows (export won't work in cmd) β then open a NEW terminal
sanook "read package.json and list the dependencies"
Run sanook with no task to drop into an interactive REPL. On the very first run with no key configured, a setup wizard walks you through picking a provider, pasting a key, and choosing a model.
sanook # interactive REPL
sanook "fix the failing test" # one-shot, headless
sanook chat -q "fix the failing test" --provider anthropic
sanook -z "summarise the diff" # one-shot, final output only
sanook --json "..." # JSONL output for CI / scripts
sanook status # redacted provider/key/brain/gateway status
sanook web status # true web-search/fetch readiness + grounding policy
sanook sessions # list saved sessions for this project
sanook --resume <session_id> "continue here"
sanook dump # support snapshot; raw secrets are never printed
Features
| Area | What you get |
|---|---|
| Agent loop | Built on the Vercel AI SDK 6 (streamText + stopWhen + fullStream), with streamed output, a cost meter, a budget cap, Anthropic prompt caching on the static preamble, and rate-limit-aware retry/backoff (distinct from auth failures) with model fallback. |
| Tools | read_file Β· write_file Β· edit_file (multi-tier matcher) Β· list_dir Β· glob Β· grep Β· run_bash Β· run_python Β· run_rust, plus git tools β gated by a permission layer that denies destructive commands, protected paths, and paths outside the workspace/brain by default. Non-bash tools are timeout-guarded so a runaway read/grep can't hang the loop. |
| Sandbox | run_bash is confined by an OS sandbox β Seatbelt on macOS, bubblewrap on Linux β so shell writes stay inside the workspace/brain/tmp (reads + network unaffected). Opt out with SANOOK_NO_SANDBOX=1. |
| Approval | ask mode is the default and prompts y/n before any file write or shell command. --yes for auto-approve; headless ask-mode safely denies mutations when no approval UI exists. |
| Input | Multiline editing, β/β persisted prompt history, readline keys (Ctrl-A/E/U/K/W), and @file mentions that inline a file's contents (or attach an image for vision-capable models). |
| Checkpoint | A shadow-git snapshot is taken before each turn; /rewind restores the files and truncates the conversation β recoverable (it stashes the current state first). |
| Memory | Layered memory: remember / recall, hierarchical SANOOK.md, Obsidian vault injection, per-turn retrieval, --continue / --resume, session notes on exit, daily worklogs, and sanook memory / sanook usage inspect commands. |
| Familiar CLI surfaces | sanook setup, sanook model, sanook auth, sanook chat -q, sanook gateway, sanook status, sanook sessions, sanook dump, sanook tools, and sanook send provide familiar management entry points, all Sanook-branded. |
| Startup cockpit | The interactive REPL opens with a Sanook-branded wordmark, service routes (Code, Brain, Connect, Ship), and live readiness signals for second-brain, MCP servers, installed skills, and the current git branch. |
| Web grounding | sanook web status separates local brain search from true internet search, detects configured MCP web/search/fetch candidates, and prints Sanook's citation + prompt-injection policy for current external facts. |
| Repo map | A lightweight symbol map of the repo (zero-dep, git-aware) is injected at session start so the agent picks the right files without blind grepping. |
| Skills | Built-in skills + install your own from a GitHub repo, URL, or local path. The agent can also author new skills after a repeatable task. |
| Custom commands | Drop a .sanook/commands/<name>.md prompt template and call it as /<name> (project commands require trust). Arguments replace $ARGUMENTS or {{ args }}; without a placeholder they are appended. |
| Subagents | A task tool spawns a fresh-context sub-agent for scoped exploration without bloating the main context β read-only by default, depth-guarded. |
| Gateway + cron | sanook gateway run (alias: sanook serve) runs a long-lived daemon: a loopback OpenAI-compatible HTTP endpoint plus a cron scheduler. Scheduled tasks can use --to to deliver results back through the messaging gateway. |
| Channels | `sanook gateway setup telegram |
| MCP | Connect any Model Context Protocol server over stdio or remote Streamable-HTTP (filesystem, GitHub, Postgres, hosted servers, β¦) via ~/.sanook/mcp.json. |
| Git | Branch, uncommitted changes, and recent commits are injected automatically, with git_status / git_diff / git_log / git_commit tools. |
| Hooks | Run your own command before/after any tool. A non-zero PreToolUse exit blocks the tool β enforce lint, format, or policy. |
| Plan mode | --plan restricts the agent to read-only tools and asks it to produce a plan before touching anything. |
| Auto-compaction | A token-aware sliding window keeps long sessions under the context limit with zero extra LLM cost. |
| Prompt budget inspectability | sanook prompt-size [--json] reports the offline size of Sanook's system prompt, personality overlay, auto-memory, skills index, second-brain context, project memory, repo map, git context, and built-in tool schemas. |
| Polyglot runtime surface | sanook runtimes [--json] shows optional Python/Rust readiness. The agent gets run_python for data/document/ML-style helper scripts and run_rust for small performance/safety-critical helpers, both approval-gated and no-shell. |
| Second brain | sanook brain init scaffolds a structured Obsidian "second-brain" workspace (folders + _Index + a portable AI operating constitution) for organising work and giving the agent durable, cross-session memory. |
| Self-improvement | Sanook watches for repeated tasks and, past a threshold, auto-authors a skill for the pattern β announced in the terminal and surfaced on the Dashboard. Tune with SANOOK_SELF_IMPROVE_THRESHOLD; disable with SANOOK_DISABLE_SELF_IMPROVE=1. |
| Persona | sanook persona runs a short questionnaire (who you are, language, tone, autonomy, stack, preferences) and stores the answers as protected memory plus a vault profile so every session starts in context. |
Persona
Teach the agent who you are once, and it carries that context into every session β name, language, tone, autonomy, your stack, and what you like (or don't like) it to do.
sanook persona # short questionnaire (A/B/C/D + free-text)
# or /persona inside the REPL β pre-fills from existing profile
A mix of multiple-choice and free-text questions. Answers are saved in two places, wired into the agent automatically:
- Auto-memory (
~/.sanook/memory/memory.json) as protected, owner-trust facts β injected at the start of every run, so the agent immediately knows how to address you and how you want it to work. - Second brain (
{brainPath}/Shared/User-Persona/persona.md) as a readable profile note you can edit by hand β written only when a vault exists.
Re-run any time to update; existing answers are pre-filled from persona.md and protected memory. Blanks are skipped, and the vault profile is rewritten in place. The brain wizard (sanook brain init) already seeds a lighter identity (name + AI name + autonomy); sanook persona is the deeper, standalone pass.
Dashboard
A local, Hermes-style admin panel for everything the CLI knows β open it in a browser and drive Sanook without leaving the page.
sanook dashboard # http://127.0.0.1:9119
sanook dashboard --port 8080
| Page | What it shows |
|---|---|
| Terminal | A real web terminal β an Agent console (the Sanook REPL, streamed over SSE with live tool activity + color-coded diffs) and an optional Raw shell (a system PTY via xterm.js, enabled when the node-pty + ws optional deps are installed). |
| Skills | Built-in and installed skills, including the ones Sanook auto-authored from repeated tasks. |
| Memory | Your structured auto-memory facts (incl. persona) with tier/trust. |
| Persona | Your saved persona profile (sanook persona / /persona) β view answers and profile path. |
| Usage | Token/cost ledger, daily/weekly/monthly. |
| Self-improve | The task ledger β what's repeating and which skills were created. |
| Install | The multi-platform install commands (see Quickstart), with copy buttons. |
| Sessions Β· Models Β· Files Β· Logs Β· Cron Β· Channels Β· Config Β· MCP Β· Brain | Inspect and manage the same surfaces as the CLI. |
It binds to loopback only and reads the same ~/.sanook/ state as the CLI, so the Dashboard and terminal always agree.
Providers
One model spec, nine providers. Switch with -m <spec> on the command line or /model in the REPL.
| Provider | Spec example | Key |
|---|---|---|
| Anthropic (Claude) | -m sonnet, -m opus, -m haiku |
ANTHROPIC_API_KEY |
| Google (Gemini) | -m gemini, -m google:gemini-2.5-flash |
GOOGLE_GENERATIVE_AI_API_KEY |
| OpenAI | -m gpt, -m openai:gpt-5.5 |
OPENAI_API_KEY |
| xAI (Grok) | -m grok |
XAI_API_KEY |
| Mistral | -m mistral |
MISTRAL_API_KEY |
| Groq | -m groq:fast |
GROQ_API_KEY |
| Ollama | -m ollama |
β (local) |
| LM Studio | -m lmstudio |
β (local) |
| OpenAI Codex | -m codex |
via the official Codex CLI |
A spec is an alias (sonnet), a provider:model-id pair (openai:gpt-5.5), or a bare model id. sanook models <provider> lists the curated ids and, when a key is set, verifies them against the provider's live /models endpoint.
sanook models # list all providers
sanook models anthropic # curated ids (+ live verification if a key is set)
sanook auth list # redacted key status for every provider
sanook auth status openai # env/store/console details
Usage
sanook "<task>" run one task (headless)
sanook -z "<task>" one-shot final output (script-friendly)
sanook chat -q "<query>" direct one-shot query
sanook interactive REPL
sanook setup [section] setup model/gateway/tools/agent/brain
sanook model choose provider + model
sanook persona questionnaire β durable persona (memory + second brain)
sanook dashboard [--port] local web admin UI (terminal, skills, memory, usage, install)
sanook status redacted install/config status
sanook auth list redacted provider key status
sanook auth add openai --api-key <key> [--use]
sanook sessions list saved sessions for this project
sanook sessions show <id>
sanook sessions export <id> [--format json|markdown] [--output path]
sanook sessions rename <id> <title>
sanook sessions stats [--all]
sanook sessions prune --keep N [--all] [--yes]
sanook sessions rm <id>
sanook dump [--show-keys] support dump (keys are still redacted)
sanook usage [daily|weekly|monthly|session] [--days N] [--since YYYY-MM-DD] [--until YYYY-MM-DD] [--json]
sanook prompt-size [--json] inspect system/brain/skill/tool context budget
sanook runtimes [--json] inspect optional Python/Rust runtime surface
sanook web status [--json] inspect true web-search/fetch readiness
sanook web doctor [--json] probe web/search/fetch MCP candidates
sanook -c "<task>" resume the latest session for this project
sanook --resume <id> resume a specific saved session
sanook --continue-any resume the newest session across all projects
sanook --plan "<task>" plan mode (read-only)
sanook --json "<task>" JSONL output for scripts / CI
sanook update update the CLI to the latest npm release
-m, --model <spec> model or provider:model-id
--provider <id> provider shortcut for `sanook chat`
-b, --budget <usd> stop when estimated cost exceeds this
-y, --yes auto-approve tool calls (skip ask-mode)
--yolo alias for --yes
-v, --version print version
-h, --help show help
REPL slash commands: /new Β· /reset Β· /status Β· /model Β· /personality Β· /platforms Β· /tools Β· /skills Β· /cost Β· /usage Β· /insights Β· /diff Β· /retry Β· /stop Β· /undo Β· /rewind Β· /clear Β· /compact Β· /compress Β· /help Β· /quit β plus your own .sanook/commands/*.md. Input supports β/β history, @file mentions (text or image), and multiline (trailing \ or Alt+Enter).
Updating
Use the built-in updater whenever a new CLI version is available:
sanook update
sanook update --check # check only
It checks the npm latest release for sanook-cli and, when newer than your installed version, runs npm install -g sanook-cli@latest.
When you launch the interactive TUI with plain sanook, the CLI checks for updates at most once per day. If a newer version exists, it asks Yes/No before running the same updater. Set SANOOK_DISABLE_UPDATE_CHECK=1 to silence the prompt.
Gateway & scheduling
sanook gateway run starts a single long-lived foreground process that hosts an HTTP API, a cron scheduler, and optional chat channels β all driving the same agent core. sanook gateway start runs the same gateway in the background and records its pid/log path under ~/.sanook/gateway/. sanook serve remains as a compatibility alias.
sanook gateway status # redacted gateway config + token path
sanook gateway run --port 8787 # HTTP (127.0.0.1 only) + scheduler
sanook gateway start --port 8787 # background process + pid/log tracking
sanook gateway stop
sanook gateway restart
sanook gateway install # write launchd/systemd helper file
sanook serve --port 8787 # compatibility alias
sanook cron add "every 30m" "check the CI" # also "09:00", an ISO time, or "now"
sanook cron add "every 30m" "check the CI" --to slack:C01ABCDEF
sanook cron add "09:00" "summarise calendar" --to email:[email protected] --model openai:gpt-5.1
sanook cron list
sanook cron rm <id>
The HTTP server binds to loopback only and authenticates every endpoint (except /health) with a bearer token stored at ~/.sanook/gateway/token (chmod 600). It runs mutating tools in ask mode by default; opt into unattended writes with sanook config set permissionMode auto or SANOOK_GATEWAY_ALLOW_WRITE=1. It speaks the OpenAI chat-completions shape, so existing clients work unchanged:
curl http://127.0.0.1:8787/v1/chat/completions \
-H "Authorization: Bearer $(cat ~/.sanook/gateway/token)" \
-H 'content-type: application/json' \
-d '{"messages":[{"role":"user","content":"summarise today's commits"}]}'
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/health |
liveness (public) |
POST |
/v1/chat/completions |
run the agent (OpenAI-compatible) |
GET / POST |
/tasks |
list / enqueue scheduled tasks |
Messaging channels
Use the setup command, or set environment variables before sanook gateway run. Telegram uses long-polling (no public URL needed), Discord uses the Gateway websocket, Slack uses Socket Mode, Mattermost uses REST API v4 plus websocket events, Home Assistant uses /api/websocket for watched state_changed events plus REST persistent_notification.create for replies, Email uses IMAP polling plus SMTP threaded replies, LINE uses the official Messaging API webhook + Reply/Push endpoints, SMS uses Twilio Programmable Messaging with X-Twilio-Signature validation, ntfy uses the HTTP JSON stream + publish API, Signal uses signal-cli daemon --http with JSON-RPC + Server-Sent Events, WhatsApp Cloud uses Meta's official webhook + Graph Messages API, Matrix uses the Matrix Client-Server sync/send API, Google Chat uses incoming webhooks or service-account Chat REST API sends with Pub/Sub config saved for future inbound, BlueBubbles/iMessage uses the BlueBubbles REST API for outbound text with webhook settings saved for inbound parity, Microsoft Teams supports Incoming Webhook delivery and Graph chat/channel delivery, and generic Webhooks accept GitHub/GitLab/Jira/Stripe-style events with HMAC validation.
sanook gateway setup # platform menu
sanook gateway setup telegram --bot-token 123:abc --allowed-chats 5222385839
sanook gateway setup discord --bot-token "$DISCORD_BOT_TOKEN" --channel 123456789012345678
sanook gateway setup slack --bot-token "$SLACK_BOT_TOKEN" --app-token "$SLACK_APP_TOKEN" --channel C01ABCDEF
sanook gateway setup mattermost --url https://mm.example.com --token "$MATTERMOST_TOKEN" \
--allowed-users user_id_1 --home-channel chan_home_id --thread-replies
sanook gateway setup homeassistant --url http://homeassistant.local:8123 --token "$HASS_TOKEN" \
--home-channel sanook_agent --watch-domains light,binary_sensor,climate
sanook gateway setup email --address [email protected] --password "$EMAIL_PASSWORD" \
--imap-host imap.example.com --smtp-host smtp.example.com --home-address [email protected]
sanook gateway setup line --channel-access-token "$LINE_CHANNEL_ACCESS_TOKEN" \
--channel-secret "$LINE_CHANNEL_SECRET" --home-channel U1234567890abcdef
sanook gateway setup sms --account-sid "$TWILIO_ACCOUNT_SID" --auth-token "$TWILIO_AUTH_TOKEN" \
--phone-number "$TWILIO_PHONE_NUMBER" --home-channel +15551234567 \
--webhook-url https://your-tunnel.example.com/sms/webhook
sanook gateway setup ntfy --topic sanook-yourname-2026 --token "$NTFY_TOKEN" --markdown
sanook gateway setup signal --account +15550000000 --home-channel +15551234567 \
--http-url http://127.0.0.1:8080
sanook gateway setup whatsapp --phone-number-id "$WHATSAPP_CLOUD_PHONE_NUMBER_ID" \
--access-token "$WHATSAPP_CLOUD_ACCESS_TOKEN" --app-secret "$WHATSAPP_CLOUD_APP_SECRET" \
--home-channel 15551234567 --public-url https://your-tunnel.example.com
sanook gateway setup matrix --homeserver https://matrix.example.org \
--access-token "$MATRIX_ACCESS_TOKEN" --allowed-users @alice:matrix.org \
--home-room '!abc123:matrix.example.org'
sanook gateway setup googlechat --service-account-json "$GOOGLE_CHAT_SERVICE_ACCOUNT_JSON" \
--home-channel spaces/AAAA --allowed-spaces spaces/AAAA
sanook gateway setup googlechat --incoming-webhook-url "$GOOGLE_CHAT_INCOMING_WEBHOOK_URL"
sanook gateway setup bluebubbles --server-url http://localhost:1234 --password "$BLUEBUBBLES_PASSWORD" \
--home-channel [email protected] --allowed-users [email protected],+15551234567
sanook gateway setup teams --incoming-webhook-url "$TEAMS_INCOMING_WEBHOOK_URL"
sanook gateway setup teams --delivery-mode graph --graph-access-token "$TEAMS_GRAPH_ACCESS_TOKEN" \
--chat-id '19:[email protected]'
sanook gateway setup webhooks --secret "$WEBHOOK_SECRET" --public-url https://your-tunnel.example.com
sanook webhook subscribe github-issues --events issues \
--prompt "New issue #{issue.number}: {issue.title}\n{issue.html_url}" --to slack:C01ABCDEF
sanook webhook subscribe deploy-notify --events push --deliver-only \
--prompt "Push to {repository.full_name}: {head_commit.message}" --to sms
sanook gateway run
sanook send --to telegram "deploy finished"
sanook send --to discord "deploy finished"
sanook send --to slack:C01ABCDEF "deploy finished"
sanook send --to mattermost:chan_home_id "deploy finished"
sanook send --to homeassistant:doorbell "deploy finished"
sanook send --to email:[email protected] --subject "[CI]" "deploy finished"
sanook send --to line "deploy finished"
sanook send --to sms "deploy finished"
sanook send --to ntfy "deploy finished"
sanook send --to signal "deploy finished"
sanook send --to whatsapp "deploy finished"
sanook send --to matrix "deploy finished"
sanook send --to matrix:'!ops:matrix.example.org' "deploy finished"
sanook send --to googlechat "deploy finished"
sanook send --to googlechat:spaces/AAAA/threads/thread-1 "threaded update"
sanook send --to bluebubbles "deploy finished"
sanook send --to bluebubbles:'iMessage;-;[email protected]' "deploy finished"
sanook send --to teams "deploy finished"
sanook send --to teams:'19:[email protected]' "deploy finished"
sanook cron add "every 30m" "check the CI" --to line
sanook cron add "09:00" "daily check-in" --to sms
sanook cron add "09:00" "daily check-in" --to ntfy
sanook cron add "09:00" "daily check-in" --to mattermost
sanook cron add "09:00" "daily check-in" --to homeassistant
sanook cron add "09:00" "daily check-in" --to signal
sanook cron add "09:00" "daily check-in" --to whatsapp
sanook cron add "09:00" "daily check-in" --to matrix
sanook cron add "09:00" "daily check-in" --to googlechat
sanook cron add "09:00" "daily check-in" --to bluebubbles
sanook cron add "09:00" "daily check-in" --to teams
sanook send --to telegram --subject "[CI]" --file build.log
echo "RAM 92%" | sanook send --to telegram --quiet
sanook send --to telegram:5222385839:17585 "threaded reply"
sanook webhook list
sanook webhook test github-issues --payload '{"event_type":"issues","issue":{"number":42,"title":"Test"}}'
sanook send --list --json
Environment overrides still work:
export TELEGRAM_BOT_TOKEN=123:abc
export TELEGRAM_ALLOWED_CHATS=5222385839 # required β comma-separated chat ids
export LINE_CHANNEL_ACCESS_TOKEN=xxx
export LINE_HOME_CHANNEL=U1234567890abcdef
export TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export TWILIO_AUTH_TOKEN=xxx
export TWILIO_PHONE_NUMBER=+15550000000
export SMS_HOME_CHANNEL=+15551234567
export SMS_WEBHOOK_URL=https://your-tunnel.example.com/sms/webhook
export NTFY_TOPIC=sanook-yourname-2026
export NTFY_ALLOWED_USERS=sanook-yourname-2026
export NTFY_HOME_CHANNEL=sanook-yourname-2026
export MATTERMOST_URL=https://mm.example.com
export MATTERMOST_TOKEN=xxx
export MATTERMOST_HOME_CHANNEL=chan_home_id
export MATTERMOST_ALLOWED_USERS=user_id_1,user_id_2
export MATTERMOST_ALLOWED_CHANNELS=chan_home_id,chan_ops_id
export MATTERMOST_REQUIRE_MENTION=true
export MATTERMOST_REPLY_MODE=thread
export HASS_URL=http://homeassistant.local:8123
export H
No comments yet
Be the first to share your take.