objectify
Write a class. Drop it in a folder. Every method becomes a shell command. Every call takes JSON arguments. Every write is versioned. State is persistent, optional, and backed by SQLite. The whole thing is a single binary — no server, no SDK, no configuration.
objectify gives LLM agents the easiest possible way to create and use tools. An agent writes a class, and immediately has a stateful, versioned, self-documenting CLI it can call through bash. No boilerplate, no glue code, no infrastructure. Just classes in, tools out.
It’s also a pretty great way for humans to build CLI tools without all the argument parsing.
objectify create "sprint tasks" --class=TaskList# → 3fa8
objectify use 3fa8 add -p:title "write tests"# → {"id": "9a3f", "title": "write tests", "done": false, ...}
objectify use 3fa8 pending# → [{"id": "9a3f", "title": "write tests", "done": false}]
objectify log 3fa8# VERSION METHOD AT# 1 create 2 hours ago# 2 add 1 hour agoInstallation
Section titled “Installation”Requires Rust 1.70+.
git clone <repo>cd objectifycargo build --releaseOptional: Deno (for TypeScript classes)
Section titled “Optional: Deno (for TypeScript classes)”Required only for TypeScript class method execution.
curl -fsSL https://deno.land/install.sh | shOptional: Python 3 (for Python classes)
Section titled “Optional: Python 3 (for Python classes)”Required only for Python class method execution. Python 3.10+ recommended.
# macOSbrew install python
# Linuxapt install python3For schema extraction from typed Python classes, install Pydantic:
pip install pydanticQuick start
Section titled “Quick start”# Create a local .objectify/ directory in your projectobjectify init
# Or initialize globally (available everywhere)objectify init --global
# Create an untyped objectobjectify create "my config"# → a1b2
# Write state (full replacement)objectify use a1b2 set '{"theme": "dark", "fontSize": 14}'
# Read stateobjectify use a1b2 get# → {"theme": "dark", "fontSize": 14}
# See historyobjectify log a1b2
# Rewind to version 1 (non-destructive)objectify rewind a1b2 1Every object gets a random 32-character hex ID at creation. You never need to type the whole thing — any unique prefix resolves to the full ID, same model as git. The shortest unambiguous prefix is shown in all output (usually 4 characters).
objectify create "my thing"# → a3f1
objectify use a3f1 get # worksobjectify use a3 get # works if uniqueobjectify use a get # error if ambiguousBecause IDs are fully random, even short prefixes are almost always unique. With a few hundred objects, 4 characters gives you 65,536 possible prefixes — collisions are rare.
Directory resolution
Section titled “Directory resolution”objectify looks for .objectify/ walking up from the current directory, then falls back to ~/.objectify/. Local always wins. Classes defined in either location are available to all objects in that scope.
Both TypeScript and Python classes live together in the same classes/ directory:
.objectify/ classes/ TaskList.ts ← TypeScript class (requires Deno) TaskList.py ← Python class (requires python3; .ts takes precedence if both exist) Memory.ts Counter.py deno.json ← optional: Deno import map / npm package declarations deno.lock objectify.db ← SQLite database; all objects + full event history