hobkin
A small unattended agent: one process per agent, run through plain files. A hobkin is a little hob, the house spirit that does the chores while the house sleeps.
- One file, standard library only.
hobkin.py, Python 3.12+. Nothing to install. - No ports. It listens on nothing; it only makes outgoing calls to the model API.
- Files are the interface. Talk to it with
echo, watch it withtail -f, and see exactly what the model sees withcat. - Many per account. Each agent has its own id and folder, and a lock stops two processes running the same one.
Install
pipx install hobkin # or: uv tool install hobkin
That gives you the hobkin command. Python 3.9 or newer on Linux (or another
POSIX system); no dependencies. You can also skip installing and run the file
directly: python3 hobkin.py.
Quick start
hobkin # a new agent: prints its id, e.g. 7f3a91c2
hobkin 7f3a91c2 # resume that agent later (its context is kept)
The first run creates ~/.hobkins/_shared/config.json. Set your model there (see
Models) and make sure the API key is reachable.
Then, from another terminal, chat with it:
hobkin chat 7f3a91c2
Its output appears as it comes, and each line you type (or paste) is sent;
/stop and the other commands work, and Ctrl-D leaves. Piped, it sends the
input, prints the reply and exits: echo "status?" | hobkin chat 7f3a91c2.
Or with plain files:
echo "Clone https://example.com/repo and get its tests passing" > ~/.hobkins/7f3a91c2/in
tail -f ~/.hobkins/7f3a91c2/out
Run it in the background however you like: nohup hobkin 7f3a91c2 &,
tmux, or a systemd user service.
An agent's folder
~/.hobkins/ID/ (or $HOBKIN_HOME/ID/):
| File | What it is |
|---|---|
in |
A named pipe. echo text > in sends a message. A write waits until the agent is running; hobkin send ID text queues it instead. |
out |
Everything it says and does, with timestamps: messages, commands, output. tail -f out |
context.jsonl |
Exactly what the model is sent: line 1 is the system prompt, then one message per line. Rewritten after every change. |
MEMORY.md |
The agent's own notes. Part of its instructions on every turn, and kept when the conversation is compacted. |
state.json |
Model, context window (and where that number came from), status, pid, compaction count. |
work/ |
Its working directory. |
shell.sh |
Its shell's saved state (folder, variables, functions, aliases), restored before each command. Secrets are never saved here. |
secrets.env |
Optional KEY=value lines, set as environment variables in its shell (never put in its context). |
config.json |
Optional per-agent settings over the shared ones. |
*.md |
Optional per-agent instructions; replace shared files of the same name. |
archive/ |
Old contexts, kept when it compacts. |
queue, lock |
Messages sent while it was stopped; the process lock. |
~/.hobkins/_shared/ holds what all agents share: config.json, AGENTS.md (how
to work as a hobkin: a task routine with checklists, failure handling,
organization, memory and tidying up; created on first run, edit freely. Once
edited it's never touched; an untouched copy of an older default is upgraded), an optional SOUL.md,
any other .md instructions, and secrets.env.
The system prompt is:
SOUL.md, thenAGENTS.md, then the other.mdfiles in name order;- the list of skills (see below);
MEMORY.md.
Each .md file is taken from the agent's folder if it has one, otherwise from
_shared/. The prompt is rebuilt every turn, so edits take effect on the next one.
Skills
Skills use the same format as Claude Code and other agents: a folder holding a
SKILL.md with a name and description in its front matter, plus any scripts
or reference files. Install one by copying its folder into
~/.hobkins/_shared/skills/, and every agent can use it.
~/.hobkins/_shared/skills/agent-tickets/SKILL.md
~/.hobkins/_shared/skills/release/SKILL.md
~/.hobkins/_shared/skills/release/build.sh
The prompt lists only each skill's name and description. When a task calls for
one, the agent opens it with the skill tool. That gives it the instructions,
the skill's folder as a full path, and its list of files, so it never has to
work out where scripts live. Dozens of skills cost a line each.
- An agent's own
skills/folder can add skills, or replace a shared one with the same name. skill_dirsin config adds more folders at the lowest priority, e.g.["~/.claude/skills"]to reuse skills you already have.- New skills are picked up on the next turn; no restart needed.
Talking to it
Anything you write to in is a message. If it's busy running a command, the
message is added to the result of that command, marked "[message from
operator]". A line that is just one of these is a command instead:
| Command | Effect |
|---|---|
/stop |
Finish cleanly and exit (Ctrl-C and SIGTERM do the same). |
/pause, /resume |
Stop and restart work; messages wait until /resume. |
/interrupt |
Kill the command it's running now (it's told it was interrupted). |
/compact |
Summarise and clear the conversation now. |
/status |
Write its status to out. |
hobkin chat ID puts both in one terminal (see Quick start). hobkin list shows all agents, their status and their last line of output.
Models
config.json names the model to use and describes each one:
{
"model": "claude",
"models": {
"claude": {"api": "anthropic", "base_url": "https://api.anthropic.com",
"model": "claude-sonnet-5-5", "key_env": "ANTHROPIC_API_KEY", "key_file": "~/.anthropic.key"},
"openrouter": {"api": "openai", "base_url": "https://openrouter.ai/api/v1",
"model": "anthropic/claude-sonnet-5.5", "key_file": "~/openrouter.key"},
"local": {"api": "openai", "base_url": "http://127.0.0.1:8080/v1", "model": "local-model"}
}
}
api:anthropic(the Messages API) oropenai(Chat Completions: OpenAI, OpenRouter, vLLM, llama.cpp, Ollama, ...).- The key comes from the environment variable
key_env, or else from the filekey_file. It's removed from the agent's shell environment. headers(optional): extra HTTP headers.requests_per_minute(optional): a cap on calls to this model. It's shared by every agent using the same API, model and key, because that's what the provider counts. The agents coordinate through a small lock file in_shared/(holding a hash of the key, never the key). An agent at the cap waits, and still answers commands such as/stopwhile it does.
The context window is asked for, not hard-coded. At start-up hobkin asks the API:
- Anthropic:
GET /v1/models/{model}(max_input_tokens,max_tokens). - OpenAI-style: the
/modelslist (context_lengthas on OpenRouter,max_model_lenas on vLLM, and similar). - llama.cpp's
/propsand Ollama's/api/show.
context_tokens on a model is optional: set it and that number is used as
given; leave it out (or null) and it's detected. If the API doesn't say,
set it. Failing that, hobkin
learns the limit from the first "context too long" error and remembers it.
state.json records where the number came from.
When the conversation passes compact_at (80%) of the window, the agent writes
itself a handoff note. The conversation is replaced by that note, with the
newest unanswered message carried over unchanged, and the old context goes to
archive/.
Other settings (shared or per agent): bash_timeout (600 s per command),
output_limit (30,000 characters of command output, head and tail kept),
max_output_tokens (16,000, capped at what the API reports), and
idle_prompt / idle_seconds (see below).
Its tools
bashruns as your user, starting inwork/. It behaves like one shell that stays open:- The first command runs as a login shell (your profile's PATH and so on).
- After every command, its state is saved to
shell.sh: the folder, variables, exports, functions and aliases. The next command starts from that state. Socd,exportandsource .venv/bin/activatecarry over, even across restarts. - Each command is still its own process, so a hung or crashed command can't
take the shell's state with it. Shell options like
set -edon't carry over. reset: truestarts a fresh login shell inwork/.- Every result ends with the folder it's now in.
- The environment has
secrets.envplusHOBKIN_ID,HOBKIN_DIR,HOBKIN_WORKandHOBKIN_MEMORY.
read_fileshows a file with line numbers (in pages), or lists a folder.write_filewrites a whole file.edit_filereplaces one exact piece of text. The text goes in as is, with no shell quoting, which is where smaller models most often go wrong.- Errors are written to help: "appears 2 times (lines 3, 5)", or the closest matching lines when the text isn't found.
- Line numbers pasted from
read_fileare stripped automatically, and Windows line endings are handled. - Paths are relative to the shell's folder;
$HOBKIN_WORK,$HOBKIN_MEMORYand~work.
skillopens a skill by name.
Nothing asks for approval: an agent can do anything your account can. Agents in one account stay apart by convention (their own folders), not by enforcement. Use a separate account or container when that matters.
Waking up when idle
When it has nothing to do, it waits for a message. With idle_prompt set, it
also gets that message after idle_seconds of quiet. For example,
"Check for new work." every 300 seconds.
As an agent-tickets worker
Nothing in hobkin knows about agent-tickets; you give it the instructions and the token:
hobkin # note the id, then /stop it (Ctrl-C)
ID=7f3a91c2 URL=http://127.0.0.1:43917 TOKEN=the-token-for-this-agent
printf 'AGENT_TICKETS_URL=%s\nAGENT_TICKETS_TOKEN=%s\n' "$URL" "$TOKEN" > ~/.hobkins/$ID/secrets.env
chmod 600 ~/.hobkins/$ID/secrets.env
curl -s -H "Authorization: Bearer $TOKEN" "$URL/api/skill" --create-dirs \
-o ~/.hobkins/_shared/skills/agent-tickets/SKILL.md
echo '{"idle_prompt": "Run the agent-tickets wait loop (GET /api/wait) and do what it says.", "idle_seconds": 60}' \
> ~/.hobkins/$ID/config.json
hobkin $ID
The agent-tickets skill is shared: it reads the token from
AGENT_TICKETS_TOKEN, so one copy serves every worker, each with its own
secrets.env. The idle prompt keeps it
checking for work, and it can also hold /api/wait open itself inside a
command. Give each worker its own token, so claims and comments show which
agent did what.
Releasing
Releases go to PyPI from GitHub Actions with Trusted Publishing (no token stored):
- Bump
__version__inhobkin.py, commit and push. - Publish a GitHub release tagged
vX.Y.Z(matching__version__). The workflow runs the tests on Python 3.9, 3.12 and 3.14, builds, checks the tag matches, and uploads to PyPI.
Running the workflow by hand (Actions → Publish → Run workflow) uploads to TestPyPI instead, for a dry run.
Tests
python3 tests/test_hobkin.py
These start real hobkin processes against tests/fake_api.py: a local
stand-in for both APIs that rejects malformed conversations. So the tests cost
nothing and don't need a key. They cover both API styles:
- commands, secrets, and hiding the model key from the shell;
- live and queued messages, the lock,
/interrupt, messages arriving mid-command; - stopping and resuming, and recovery after a crash mid-command;
- compaction, learning the window from an error, and the newest request surviving compaction;
- the shell's saved state (variables, arrays, functions, aliases, PATH, folder,
login profile,
reset), with secrets never saved; - the file tools (exact text with quotes,
$and backslashes; repeated and missing matches; pasted line numbers; paging) and the skill tool; - skills (listing, which copy wins, front matter);
requests_per_minuteshared across two agents.
Metadata
Release files for hobkin 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| hobkin-0.1.0.tar.gz | 37.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hobkin-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 65.4 kB
Release files / hobkin-0.1.0.tar.gz
| Download URL | hobkin-0.1.0.tar.gz |
|---|---|
| Size | 37.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
200088013d3e2958dcdc754b132a9c674071b414803c646347345981a2b2f4c3
|
|
BLAKE2b-256 checksum How to use checksums |
e9248ff308641a95cc8220628bdf0bdde323a59e77cf7ae4064bfd78e3a5422d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / hobkin-0.1.0-py3-none-any.whl
| Download URL | hobkin-0.1.0-py3-none-any.whl |
|---|---|
| Size | 27.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
37f331168f452fd68d5211705be98859ae7885275c95da460dc361036662cfa9
|
|
BLAKE2b-256 checksum How to use checksums |
fadf2d2980e6ebfd4f9313eeea3701afdb0a5e9a0387fe4a76a01c153af7a908
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logSecurity for hobkin 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for hobkin-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.