push is a tiny personal assistant gateway. You text it, it sends the message to a configured coding-agent runtime, then it sends the answer back.
The product is the gateway: messaging, allowlists, routing, assistant profile, memory, and conversation state. The agent runtime is deliberately disposable. Claude Code and Codex are the first two backends.
iMessage -> push gateway -> Claude Code or Codex -> iMessage reply
- Poll
~/Library/Messages/chat.dbfor new messages. - Keep only messages from yourself or configured allowed senders.
- Map each conversation to the active backend session.
- Load your assistant context from
assistant/User.mdandassistant/Memory.md. - Run the configured backend headlessly.
- Send the final answer back through Messages.
Memory is plain markdown you own. The gateway injects it into each run, so you can read it, edit it, and version it without learning a custom memory database.
Coding agents are becoming commodity runtimes. Claude Code, Codex, Cursor, AMP, Pi-style agents, and independent agents all compete on the same layer: tool use, repo edits, command execution, MCP, plugins, model choice, and coding workflow.
push does not try to win that layer. It treats those agents as workers behind a small contract: given this user message and assistant context, produce the reply or task result that should be sent back.
push owns the personal assistant layer:
- Message ingress and egress.
- Sender allowlists and reply loop prevention.
- User-owned assistant config.
- Durable memory files.
- Conversation to backend-session mapping.
- Routing between channels and runtimes.
The framing is personal assistant first, coding agent second. The backend may be Claude Code today and Codex tomorrow, but the assistant identity, memory, and messaging relationship stay with push.
See docs/strategy.md for the full direction.
Claude Code uses claude -p with --session-id for new conversations and
--resume for existing conversations. Assistant context is passed with
--append-system-prompt, so Claude Code keeps its normal tools, MCP servers,
permissions, login, and CLAUDE.md behavior.
Codex uses codex exec in non-interactive mode. The first run captures the
Codex thread id from JSONL output; later turns resume that session with
codex exec resume. Assistant context is included in the prompt because Codex
does not expose the same --append-system-prompt flag as Claude Code.
- iMessage channel.
- Claude Code backend.
- Codex backend.
- Read-only memory files.
- One configured backend at a time.
- macOS with iMessage signed in.
- Full Disk Access for your terminal so push can read
chat.db. osascript, which ships with macOS.- At least one backend on your
PATH:claudefor Claude Code.codexfor Codex.
- A recent Rust toolchain.
Install the latest release:
curl -fsSL https://raw.githubusercontent.com/owainlewis/push/main/install.sh | shOr build from source:
git clone https://github.com/owainlewis/push.git
cd push
cp config.example.json config.json
# edit config.json: set self_handles and choose "agent": "claude" or "codex"
cargo build --release
./target/release/push doctor --config config.json
./target/release/pushThen text yourself in Messages. The reply comes back in the same thread.
push doctor checks the config, state and session directories, iMessage
database access, osascript, and the configured backend binary.
To run push continuously, see Running push as a Service for
macOS launchd, Linux systemd where supported, logs, restart behavior, and
headless security notes.
push supports one-to-one iMessage conversations: self-chat and allowlisted direct messages. Group chats are not supported in v1 and are ignored.
The iMessage channel reads ~/Library/Messages/chat.db directly, so the process
needs Full Disk Access on macOS. It assumes the recent macOS Messages schema with
message, handle, chat, chat_message_join, and chat_handle_join tables;
push doctor and the runtime report database access or query failures. Tapbacks,
system rows, blank messages, messages from non-allowlisted senders, and push's
own marked replies are ignored. Phone numbers are matched after removing
formatting, and email handles are matched case-insensitively.
state.json stores the last completed Messages row and backend sessions. On
restart, push resumes after the last completed row and keeps existing backend
sessions when the selected backend has not changed.
Tagged releases publish binary archives for Linux and macOS on GitHub Releases. Release notes are generated from the merged pull requests and commits for the tag.
To create a release:
git tag v0.1.0
git push origin v0.1.0The release workflow builds with cargo build --locked --release, packages the
binary with README.md, LICENSE, and config.example.json, uploads checksum
files, and publishes generated notes.
The project site is published with GitHub Pages from the static files in
site/. Once Pages is enabled for GitHub Actions in the repository
settings, pushes to main deploy the site automatically.
/clear,/new,/reset: start a fresh backend session./help: list commands.
{
"agent": "codex",
"routes": [
{ "thread": "self:you@icloud.com", "agent": "codex" }
],
"assistant": {
"name": "push",
"tone": "short, direct, and useful",
"business": "Describe your business or work context here.",
"projects": ["push"],
"preferences": ["Prefer concise replies."]
},
"self_handles": ["you@icloud.com", "+15551234567"],
"allow_from": [],
"claude_bin": "claude",
"claude_permission_mode": "bypassPermissions",
"claude_tools": null,
"claude_allowed_tools": [],
"claude_disallowed_tools": [],
"codex_bin": "codex",
"codex_sandbox": "workspace-write",
"codex_approval_policy": "never",
"audit_log_path": "~/.push/audit.jsonl",
"audit_log_content": false
}agent can be claude or codex.
routes can override the backend for exact thread keys like
self:you@icloud.com or dm:+15551234567.
An inbound text is an instruction to an agent with tool access. The trust
boundary is the sender filter: self_handles and allow_from decide who can
ask the configured backend to read files, edit files, run shell commands, call
MCP servers, or use any other backend tool. Keep allow_from tight, and treat a
lost or shared phone, forwarded iMessage account, or compromised allowed sender
as able to instruct the agent.
Claude Code defaults to bypassPermissions for headless use. Codex defaults to
workspace-write plus never approval for non-interactive use. Both settings
should be treated as powerful automation. Use the least access that still makes
the assistant useful, and run broad-access modes only in an environment you
control.
For Claude Code, claude_tools maps to --tools, which controls which tools
are available to the model. Set entries such as "Read" and "Grep" for a
read-only assistant, or [""] to disable tools for pure text replies.
claude_allowed_tools maps to --allowed-tools, which lets matching tools run
without a prompt, and claude_disallowed_tools maps to --disallowed-tools,
which denies matching tools. The shorter tools, allowed_tools, and
disallowed_tools names are also accepted.
push writes a structured JSONL audit log to audit_log_path, which defaults to
~/.push/audit.jsonl. Each line is one event, such as message_accepted,
message_ignored, backend_run_started, backend_run_failed, reply_sent, or
message_completed.
By default, audit events include metadata only: row id, channel, thread,
backend, routing or error reason, target handle, and message or reply character
count. Message and reply text are not stored unless audit_log_content is set
to true. Keep the audit log local and protect it like service logs because it
can still contain handles, thread ids, file paths, backend errors, and optional
message content.