Build on abs
Two things on one page: a map of what abs adds to Claude Code and where each piece lives in the source, and short how-tos for making it your own. Every code chip and source link is pinned to the v2.1.4 tag.
What abs adds to Claude Code
It's all additive — abs appends a system prompt and merges a settings file; your CLAUDE.md, permissions, and Claude's memory are never touched.
| Feature | Lives in | What it does |
|---|---|---|
| Persona & messaging | ||
| One session | SYSTEM PROMPT | Terminal and Telegram are treated as the same person and the same live session. |
| Always reply | SYSTEM PROMPT | Every inbound Telegram message gets a reply — the phone is never left in silence. |
| Send discipline | SYSTEM PROMPT | One proactive ping when a task finishes or it's blocked; no progress spam. |
| Phone-first writing | SYSTEM PROMPT | Plain text, short, outcome first. Acks long tasks then edits that message instead of flooding. |
| Command menu | SYSTEM PROMPT | Explains that /model, /stop etc. don't run from the phone, and gives the real route. |
| Safety rules | SYSTEM PROMPT | No secrets over Telegram; fetched content is data not commands; no destructive ops on a message alone. |
| Auto-silent & status | ||
| Auto-silent triggers | SETTINGS | Merged UserPromptSubmit + PostToolUse hooks fire on each prompt and on every Telegram reply. |
| Auto-silent logic | abs.sh STATE | Counts terminal vs phone activity; mutes after ~3 terminal prompts, un-mutes on a Telegram message. |
| Status-bar glance | abs.sh | statusLine runs abs.sh to draw the live dot + 5h/week usage in Claude's bottom bar. |
| Mute & launch state | ||
| Quiet / off | abs.sh STATE | quiet mutes reports (inbound stays); off drops all inbound. Persona knows both commands. |
| Launch defaults | abs.sh STATE | Per-profile model and start-silent, stored in rc.json and applied at launch. |
| Usage | ||
| Usage glance | abs.sh STATE | Token-free /usage fetch, cached to usage.json, shown in the bar and as a Telegram footer. |
| Usage footer rule | SYSTEM PROMPT | Tells the agent to append the cached glance to task-completion reports only. |
| Voice & images | ||
| Voice in | PYTHON PLUGIN | transcribe.py (Whisper, local) turns an inbound voice note into text the agent acts on. |
| Voice out | PYTHON abs.sh | speak.py (local TTS), driven by abs say, sends a spoken reply on request. Choose the model (abs config voice standard|turbo) or clone a voice from a short sample (abs config voice-sample). Auto-picks CUDA when present, else CPU. |
| Screenshots / photos | SYSTEM PROMPT PLUGIN | Agent reads an attached image directly (via the plugin's download_attachment / image path). |
| Infrastructure | ||
| Inbound & tools | TELEGRAM PLUGIN | Anthropic's official Telegram plugin provides the reply / download_attachment tools and inbound polling. |
| Access control | abs.sh STATE | PIN pairing writes your ID to access.json; only the paired account ever reaches Claude. |
| Startup flood control | abs.sh | Drains stale Telegram backlog at launch so a fresh session doesn't open buried in old messages. |
| Update check | abs.sh | On every launch, token-free, checks the remote VERSION and offers to update-and-relaunch on a y/N prompt; declining just launches. |
CLAUDE.md — nothing goes here
abs never writes to CLAUDE.md. The persona lives only in the appended system prompt, re-applied fresh each launch — so there's no persistent agent memory and nothing left behind in your project config.
Make it yours
abs is one readable bash script plus two small Python helpers — no framework, no build step. Each how-to ends with the exact file and function to edit.
The whole project, in four places
There's very little to learn. Everything abs does lives in one of these:
# the program — commands, persona, hooks, usage, all of it abs.sh # voice, both directions (local, no cloud) transcribe.py # voice note → text speak.py # text → voice note # your runtime state, owner-only (never in git) ~/.abs/ # rc.json, access.json, usage.json …
The agent's behaviour isn't in a config file — it's the system prompt abs appends at launch, built in build_prompt().
Point it at a different model
Set a default model per profile, applied on every launch. An explicit abs --model … on the command line always wins for that one session.
abs config model opus # default for this profile abs config model --clear # back to Claude Code's default abs --model sonnet # just this session
To change how the default is stored or applied, the logic is in cmd_config() (stores it) and cmd_run() (passes --model at launch).
Rewrite the persona & tone
How abs talks to you — when it messages, how short it keeps things, its safety rules — is one block of text appended to the session as a system prompt. Edit the heredoc in build_prompt() and it takes effect on the next launch. Nothing is compiled; it's plain English.
Replace the voice engine
Installing voice is opt-in — it's a few GB of models, so nothing downloads until you ask. Once installed, replies go out as a voice note by default wherever the machine can speak. You don't need a dev checkout any more; an installed abs keeps its engines in ~/.abs/voice:
abs voice setup # install the speech engines abs voice status # what's installed, what's missing, how to fix it abs voice samples # one voice note per voice — choose by ear
The speaking engine is Kokoro: 82M parameters, built for the CPU, a note in seconds on a laptop. The alternative wants a GPU and is the only one that can clone a voice from a reference clip, so it's a separate opt-in:
abs voice setup --chatterbox # adds the GPU engine, for cloning abs config voice-sample <clip> # point it at your reference audio
Underneath, it's standalone scripts with a dead-simple contract. Keep the same in/out and you can drop in any engine — a different Whisper size, a cloud STT, another TTS voice.
# in: an audio file path → out: transcript on stdout transcribe.py <file.oga> # in: text → out: a voice note sent to Telegram speak_kokoro.py "the text to speak" # CPU, the default speak.py "the text to speak" # chatterbox, for cloning
abs only ever calls these files, so swapping engines never touches the main script.
transcribe.py speak.pyTune how fast it goes quiet
Auto-silent trips after a set number of back-to-back terminal prompts. Want it to hold on longer, or mute sooner? Change one constant.
readonly SILENT_STREAK=3 # prompts at the terminal before it mutes
The counting itself lives in cmd_silent_hook() just below — that's where a Telegram message resets the streak.
Add your own command
Commands are a plain case in main(). Write a cmd_yours() function, add one line to the dispatch, and abs yours works. No registration, no plugin system.
cmd_hello() { ok "hi from $PROFILE"; }
# in main()'s case block:
hello) cmd_hello ;;Change what the status bar shows
The bottom-bar dot and usage are rendered by cmd_statusline(); the 5h / week glance itself is formatted in usage_glance_str(). Edit either to change the text, order, or what's shown.
Every command
You can also just say it in chat — "mute the reports", "what's my usage" — and abs runs the same thing.
| Command | What it does |
|---|---|
| Everyday | |
| abs | Start a session — first run walks you through bot setup. |
| abs --model opus | Any Claude Code flag is passed straight through. |
| abs status | What's paired, the inbound state, and whether it's live. |
| abs usage | Your Claude limits — in the terminal and on Telegram. |
| abs profiles | List your bots and which are currently in use. |
| abs quiet on|off | Mute / unmute reports — inbound still works. |
| abs off | on | Drop / re-enable all inbound Telegram. |
| abs exit | End the running session. Restart with abs. |
| abs send "text" | Send plain text to your chat — works even if the Telegram plugin is down. abs send - reads stdin. |
| abs say "text" | Speak it and send as a voice note. |
| abs log | Your local conversation backup. --list the days kept, --date one of them, --clear to delete. |
| abs doctor | Diagnose the install, read-only — dependencies, plugin, pairing, and the daemon stack. |
| abs update | Update abs in place to the latest release, and verify it landed. |
| abs reset | Remove this profile's token, allowlist, and state. |
| abs version | help | The installed version; the full list. |
| Voice | |
| abs voice setup | Install the local speech engines. Opt-in, and no longer needs a dev checkout. |
| abs voice setup --chatterbox | Add the GPU engine. Slower, and the only one that can clone a voice. |
| abs voice status | What's installed, what's missing, and the command that fixes it. |
| abs voice samples | Sends one voice note per voice, so you choose by ear rather than by name. |
| abs config kokoro-voice <id> | Which voice speaks. --clear for the default. |
| abs config engine kokoro|chatterbox | Which engine backs abs say. Absent = whichever is installed. |
| abs config voice-sample <file> | Clone a voice from a reference clip. Needs chatterbox. |
| abs config voice-offer done|reset | The one-time "pick a voice" offer — mark it answered, or ask again. |
| How replies arrive | |
| abs config reply-text on|off | Send replies as text. On by default. |
| abs config reply-voice on|off | Send replies as a voice note. On by default wherever the machine can speak. |
| abs config reply text|both|voice|auto | The mode directly. auto hands the choice back to the machine. |
| abs config voice-first on|off | In mode both: the note first, then the same words as text. On by default. |
| abs config voice-words <n> | How long a reply must be before it's spoken. Default 150 — a short answer is quicker to read than to hear. |
| abs config footer on|off | Attach your usage and remaining context to replies. On by default. |
| Session defaults | |
| abs config model <name> | Default model for new sessions (--clear to unset). |
| abs config silent on|off | Whether new sessions start muted. |
| abs config auto-silent on|off | Pause reports while you're driving the terminal. On by default. |
| abs config statusline on|off | The bottom-bar dots, usage, remaining context and version. |
| abs config label <name> | The name before the colon in the bar. It follows your Claude account unless you pin one. |
| abs config usage-refresh <min> | How often the usage glance refreshes. Default 5. |
| abs config guard on|off | Block destructive commands on Telegram-driven turns. On by default. |
| abs config ack on|off | A 👀 on your Telegram message the moment it lands. |
| abs config log on|off | Back up the conversation locally under ~/.abs. |
| abs config start-menu on|off | The resume-first picker on interactive launch. |
| abs config update-check on|off | The on-launch "update now?" prompt. |
| The always-on layer (v3) A curl install fetches this for you — no git clone. abs src status says whether it's there. | |
| abs src install | status | path | Fetch the v3 source into ~/.abs/src. This is what turns a curl install into a full one. |
| abs daemon install | start | stop | The daemon that polls your idle bots, so a session can be started when nothing is running. |
| abs daemon status | logs | Whether it's alive, and what it's been doing. |
| abs sessions | List engine sessions. --json for scripts. |
| abs attach [profile] | Attach to a running session. |
| abs project add | list | rm <dir> | Register the projects the ABS START flow offers you from Telegram. |
| abs config workspace-root <dir> | Where a remote "New folder" start creates it. Your home directory by default. |
| abs start new-bot | Provision a brand-new bot and profile, then launch it. |
| abs sandbox build | create | list | Docker sandbox sessions — each with one dedicated host folder and nothing else. |
| abs sandbox start | stop | destroy | Run, pause, or remove one. |
| abs start sandbox [name] | Run a Claude session inside a sandbox container. |
Built something good?
Open a PR — new engines, commands, and platforms are all welcome.