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 source link is pinned to the v3.7.4 tag, so the line it opens is the line that shipped — not a line number that moves under you.
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_kokoro.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. |
| Editable prompt layers | abs.sh PYTHON STATE | abs prompt opens every layer — persona, hook wording, CLAUDE.md, memory — read the locked ones, edit yours, ^R to reset. Built on Textual. |
| Named personas | abs.sh STATE | ~/.abs/personas/<name>.md replaces the middle slot only; ceo, cto and friend ship. Chosen at launch and shown in the status bar. |
| Commit & ship policy | SYSTEM PROMPT STATE | Finished work stays uncommitted until you say so, and push, deploy and tag each ask one plain question first. abs config commits auto relaxes the commit half; push still waits. |
| Bridge health | abs.sh | Two sessions polling one bot is the one failure the plugin can't heal, so the status bar says so and a per-turn hook clears the stray. abs reconnect does it on purpose. |
| 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 — abs doesn't write there
abs never edits your CLAUDE.md, global or project. Its own behaviour lives in the system prompt it appends fresh at every launch, so nothing is left behind in your project config. What Claude chooses to remember across sessions is Claude Code's own memory, not abs's — and because it still shapes every reply you get, abs prompt gives you a tab to read it, edit any fact, or delete one.
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, what it calls itself — is plain English in a file you own, and you don't need to touch the source to change it. abs prompt opens it on the Persona tab; ^R puts the shipped wording back.
abs prompt # the page: read it, edit it, reset it abs persona list # abs, ceo, cto, friend, and yours abs persona create mine # start one from the shipped text abs --persona cto # just this session ~/.abs/persona.md # the default persona, if you'd rather edit by hand ~/.abs/personas/<name>.md # the named ones
The prompt is assembled in three slots — bridge mechanics, then your persona, then safety. Safety is appended last on purpose, so a persona can change the voice and the priorities but never talk abs past a destructive-command rail. Over 16,000 characters, or containing a <channel tag, is refused at save rather than silently swapped.
See everything it tells Claude
Nothing abs adds to a session is hidden. One command opens a tabbed page over every layer: the locked bridge mechanics, your persona, the wording each remote command injects, this project's CLAUDE.md, your global one, and what Claude has remembered.
abs prompt # the page — F1…F7 or ^PgUp/^PgDn to move abs prompt show persona # or do it without the page at all abs prompt diff persona # what you changed vs what ships abs prompt reset persona # back to the shipped wording
^S saves, ^R resets, ^N adds a hook phrase or a memory fact, ^T deletes one, ^Q or Esc leaves — and if anything is unsaved it asks first. The System tab is read-only: it is what makes the bridge work, so breaking it would break the tool rather than personalise it.
Add a remote command of your own
The ABS … phrases you send from Telegram are handled by a session hook, not by the model, which is why they work even when a message tries to talk the model out of them. The wording each one injects lives in ~/.abs/hooks.json, and you can add your own.
# ~/.abs/hooks.json — send "ABS REVIEW" as a whole message
{ "ABS REVIEW": "Stop and review the diff before going further." }Three phrases can't be reworded — the two that mute and the two that cut the bridge act in the hook and never reach the model at all, so an entry under those names is ignored. Everything else is yours, and the Hooks tab of abs prompt is the editor.
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 reconnect | Telegram stopped arriving? Says what is holding the bot, clears a stray, and names the one thing only a restart fixes. Also abs telegram-reset. |
| abs menu | Re-register the Telegram "/" command menu if it goes missing. |
| 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. |
| From Telegram — send as a whole message | |
| A session hook acts on these itself, before the model sees them — so they work even if a message tries to talk the model out of them. Anything else you type is simply said to Claude. The wording each one injects, and phrases of your own, live in abs prompt. | |
| ABS MUTE / ABS UNMUTE | Pause or resume the proactive reports. Inbound keeps working. |
| ABS STOP | Halt the current plan at the next step and wait for you. |
| ABS OFF | Cut all Telegram, in and out — the session keeps working locally. Terminal-only to re-enable. |
| ABS EXIT | Close the Claude session; it confirms first if it is mid-task. |
| ABS BLOCK | Lock the bot out until you re-pair at the terminal. |
| /usage | Post your full Claude usage limits back into the chat. |
| Persona & what it tells Claude | |
| abs prompt | The tabbed page: persona, hook wording, project and global CLAUDE.md, memory. Read the locked layers, edit yours. |
| abs prompt show|edit|diff|reset <part> | The same, without the page — for a machine without Textual, or a script. |
| abs persona list | show | Which personas exist, and what one says. |
| abs persona use <name> | Make it the default for every session from now on. |
| abs persona create|edit|rename|delete | Yours, in ~/.abs/personas/. A new one starts from the shipped text. |
| abs --persona cto | Just this session, skipping the picker at launch. |
| abs config persona-menu on|off | Ask which persona at every launch. On by default. |
| abs config commits ask|auto | ask (default): nothing is committed until you say so. auto: commit each finished task — pushing still waits for your word either way. |
The abs prompt tabs — F1…F7 | |
| Overview | What each layer is, where it lives on disk, and how big it is. |
| System | The bridge mechanics abs must send. Read-only, on purpose. |
| Persona | Who it is and how it writes to you. Pick one, or edit it here. |
| Hooks | The wording each remote command injects, plus phrases of your own. |
| Project | This repo's CLAUDE.md — with a warning that it is committed. |
| Global | Your ~/.claude/CLAUDE.md, which shapes every Claude session. |
| Memory | The facts Claude has kept about you and the work, one per entry. |
| 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.