An autonomous AI assistant framework built on Anthropic's Claude Code CLI, designed for 24/7 unattended operation on macOS.
Pickle Agent turns a Mac into a persistent, autonomous AI teammate. It watches for iMessages, monitors infrastructure health, performs scheduled research and writing tasks, manages email, and maintains its own memory across sessions — all without human intervention.
Originally built for a mental health occupational therapy practice, the architecture is fully generalizable to any domain where you want an always-on AI agent that communicates via iMessage and manages services.
- Overview
- Architecture
- File Structure
- Prerequisites
- Quick Start
- Configuration Reference
- MCP Servers
- Tool Allowlist
- Permissions Model
- Memory System
- Skills System
- Contact Hours System
- Session Lifecycle
- Claude Code CLI Flags
- Adapting for Your Own Use
- Troubleshooting
- License
Pickle Agent is an autonomous AI agent framework that:
- Runs continuously on macOS using
launchdfor process management — the same system macOS itself uses to manage its own services. No cron jobs, noscreensessions, no Docker containers for the agent itself. - Communicates via iMessage by reading the native
chat.dbSQLite database and sending replies through an MCP (Model Context Protocol) server. This means the agent has a real phone number and appears as a normal iMessage contact. - Monitors infrastructure including Docker containers, native services, disk usage, and network endpoints. It detects issues, attempts remediation, and alerts humans only when necessary.
- Performs autonomous tasks on a schedule: research scans, email triage, blog post drafting, memory maintenance, and custom domain-specific work.
- Maintains persistent memory across sessions via a structured file-based memory system. Each Claude Code CLI session is ephemeral, but the agent's knowledge, task list, contact notes, and incident history survive between invocations.
- Enforces contact hours so it never texts people outside business hours (except when replying to someone who texted first).
- Uses a three-daemon architecture where each daemon has a distinct responsibility, budget, and cadence.
The agent is not a single long-running process. Instead, it is a collection of shell scripts managed by launchd that spawn short-lived Claude Code CLI sessions in response to events (new message), schedules (health check, heartbeat), or conditions (detected issue). Each session receives the full system prompt, MCP configuration, and tool allowlist, executes its task, writes to the shared memory system, and exits. This design is intentionally stateless at the process level and stateful at the filesystem level.
+---------------------------+
| launchd |
| (macOS process mgr) |
+---------------------------+
/ | \
/ | \
+-----------+ +-------+------+ +----------+
| WATCH | | HEALTH | | HEARTBEAT|
| Daemon | | Daemon | | Daemon |
+-----------+ +--------------+ +----------+
| Poll every | | Run every | | Run every|
| 2 seconds | | 30 minutes | | 60 mins |
| for new | | Check Docker | | Read |
| iMessages | | + ports + | | task |
| | | disk usage | | checklist|
+-----+------+ +------+-------+ +-----+----+
| | |
v v v
+-----------+ +--------------+ +----------+
| Claude | | Claude Code | | Claude |
| Code CLI | | CLI session | | Code CLI |
| session | | (if new | | session |
| (per msg) | | issues) | | (per |
| | | | | beat) |
+-----+------+ +------+-------+ +-----+----+
| | |
+--------+--------+--------+--------+
| |
v v
+---------------+ +-------------+
| MCP Servers | | Memory |
| (iMessage, | | System |
| Mail, APIs) | | (files) |
+---------------+ +-------------+
The three daemons share:
- CLAUDE.md — the system prompt that defines the agent's identity, rules, and capabilities
- mcp-config.json — configuration for all MCP server integrations
- pickle-tools.conf — the shared tool allowlist
- memory/ — the persistent memory directory
But they differ in:
- Trigger — event-driven (watch) vs. time-driven (health, heartbeat)
- Budget — per-session spending cap in USD
- Prompt — each daemon constructs a different prompt for its Claude session
- Cadence — 2s polling vs. 30min interval vs. 60min interval
The message watcher is the reactive daemon. It gives the agent the ability to receive and respond to iMessages from approved contacts.
How it works:
-
Polling loop. The script runs an infinite
while trueloop with asleep $PICKLE_POLL_INTERVAL(default: 2 seconds) between iterations. Each iteration queries the macOS Messages database. -
Database query. It reads
~/Library/Messages/chat.dbusingsqlite3. The query selects messages with aROWIDgreater than the last processed ID, filtering for inbound messages only (messages whereis_from_me = 0). The query joins themessage,chat_message_join, andchattables to extract the sender's phone number and the message text. -
Contact filtering. Each message's sender phone number is checked against
contacts.conf. This file contains one phone number per line in E.164 format (e.g.,+61400000000). Messages from unknown numbers are silently ignored. This prevents the agent from responding to spam, group chats, or unexpected contacts. -
State tracking. The last processed
ROWIDis stored in a.last_rowidfile in the agent's working directory. On startup, if this file does not exist, the script initializes it to the current maximumROWID(so it does not process historical messages). This means the agent only responds to messages that arrive after it starts. -
Message parsing. Raw message text is extracted using
python3for safe handling of Unicode, emoji, multiline text, and special characters. Shell-based parsing is unreliable for iMessage content, which frequently contains curly quotes, emoji, newlines, and non-ASCII characters. The Python parser handles all of these correctly and escapes the text for safe injection into a shell command. -
Session spawning. For each new message, the watcher spawns a fresh Claude Code CLI session. The prompt includes:
- The sender's phone number
- The message text
- Instructions to reply via the iMessage MCP tool
- The current date and time
- A reminder of the agent's identity and rules (from CLAUDE.md)
-
Session isolation. Each message gets its own Claude Code process. Sessions do not share state except through the filesystem (memory directory). This means a slow response to one message does not block processing of the next message, and a crash in one session does not affect others.
-
Log capture. The stdout and stderr of each Claude Code session are captured to
logs/claude-sessions/with a timestamp and sender identifier in the filename.
Key design decisions:
- Why polling instead of filesystem events? The
chat.dbWAL (Write-Ahead Log) file changes on every message, butfswatch/kqueueon SQLite databases is unreliable because the OS may batch writes. A 2-second poll is cheap (single SQLite query) and guarantees we never miss a message. - Why one session per message? Claude Code CLI sessions are stateless — they do not maintain conversation history between invocations. By giving each message its own session, we avoid context window pollution and ensure each response is grounded in the system prompt and memory files, not a drifting conversation.
- Why Python for parsing? A line like
I'm feeling "great" today 😊\nHow are you?will break naïve shell quoting. Python'sjson.dumps()produces safe, escaped strings every time.
The health monitor is the proactive daemon. It periodically checks the status of infrastructure and alerts when things go wrong.
How it works:
-
Scheduled execution. The launchd plist runs the script at a fixed interval (default: 1800 seconds / 30 minutes). The script also supports a boot wait period (
PICKLE_BOOT_WAIT, default: 120 seconds) — on first execution after system boot, it sleeps to give Docker containers and services time to start before checking their health. -
Docker container checks. The script runs
docker ps --format '{{.Names}} {{.Status}}'and compares the running containers against an expected list:open-webuilitellmragflow-serverlitellm_dbragflow-redisragflow-es-01ragflow-miniocloudflared
Any container not in the
Upstate is flagged as an issue. -
Native service port checks. The script uses
nc -z localhost <port>(orcurl) to verify that non-Docker services are listening:Port Service Description 8000 MCPO MCP Orchestrator 8888 Jupyter Jupyter notebook server 11434 Ollama Local LLM inference 5151 Whisper Speech-to-text API 5050 TTS Text-to-speech (edge-tts) -
Disk usage check. The script runs
df -h /and parses the usage percentage. If disk usage exceeds 90%, it is flagged as a critical issue. -
Alert deduplication. This is a critical feature. The script maintains a
LAST_ISSUESvariable (persisted to a state file) containing the set of issues detected on the previous run. On each run, it compares the current issues to the previous issues. A Claude Code session is only spawned if there are new issues that were not present in the previous check. This prevents the agent from sending the same alert every 30 minutes for a persistent issue.For example:
- Run 1:
ragflow-serveris down → new issue → spawn Claude session → alert sent - Run 2:
ragflow-serveris still down → same issue → no session spawned - Run 3:
ragflow-serveris still down ANDlitellmis now down → new issue (litellm) → spawn Claude session - Run 4:
ragflow-serveris back up,litellmstill down → no new issues → no session
- Run 1:
-
Work hours awareness. Before sending an iMessage alert, the Claude session checks the current time against work hours (Mon–Fri 8am–6pm AEST). If outside work hours:
- The issue is still logged to
memory/incidents.jsonl - Automated remediation is still attempted (e.g.,
docker compose up -d) - But no iMessage is sent — the notification is saved for the morning briefing instead
- The issue is still logged to
-
Claude session prompt. When a session is spawned, the prompt includes:
- The list of detected issues (with details)
- Instructions to attempt remediation
- Instructions to alert via iMessage if during work hours
- Instructions to log the incident to memory
The heartbeat is the autonomous daemon. It gives the agent the ability to perform scheduled tasks without any human trigger.
How it works:
-
Scheduled execution. Runs every 60 minutes (configurable via
PICKLE_HEARTBEAT_INTERVAL). Has a longer boot wait (default: 180 seconds) because it depends on services that the health monitor also checks. -
Task checklist. The daemon reads
HEARTBEAT.md, which contains a structured checklist of tasks the agent should consider on each beat. The checklist is organized by frequency:- Every beat: Quick service health verification, check for unread emails
- Every 4 hours: Research scan (check configured topics for new papers/articles)
- Daily (morning): Morning briefing compilation, memory maintenance
- Weekly: Blog post drafting, knowledge base review
-
Higher budget. Heartbeat sessions have a higher budget (
PICKLE_HEARTBEAT_BUDGET, default: 0.75 USD) than watcher or health sessions (0.50 USD) because they may need to perform complex multi-step tasks like research synthesis or blog writing. -
Work hours enforcement. The heartbeat prompt includes the current time and instructs the agent to:
- Skip tasks that would generate notifications outside work hours
- Still perform silent tasks (memory maintenance, research) anytime
- Queue notification-generating tasks for the next work-hours beat
-
Session prompt. The heartbeat prompt includes:
- The full
HEARTBEAT.mdtask checklist - The current date, time, and day of week
- The contents of
memory/tasks.md(carry-over tasks) - Instructions on work hours and notification rules
- The full
pickle-agent/
├── CLAUDE.md — System prompt: the agent's identity, rules, capabilities,
│ and instructions. This is the "brain" — it defines who
│ Pickle is, what it can do, and how it should behave.
│ Loaded via --system-prompt on every Claude CLI session.
│
├── SOUL.md — Personality definition: communication style, values,
│ tone of voice, humor preferences, and interpersonal
│ guidelines. Referenced by CLAUDE.md. Separating soul
│ from instructions keeps both files focused.
│
├── HEARTBEAT.md — Autonomous task checklist read by the heartbeat daemon.
│ Structured as a markdown checklist with frequency tags
│ (every-beat, 4-hourly, daily, weekly). The agent reads
│ this file and decides which tasks to execute.
│
├── mcp-config.json — MCP (Model Context Protocol) server configuration.
│ Defines every external tool integration: iMessage,
│ Apple Mail, Paper Search, PubMed, ICD-11, Zanda,
│ Giphy. Contains API keys (not committed to git).
│
├── pickle-tools.conf — Shared tool allowlist. Sourced by all three daemon
│ scripts. Builds the comma-separated --allowedTools
│ string passed to Claude Code CLI. Controls exactly
│ which MCP tools and built-in tools the agent can use.
│
├── contacts.conf — Allowed phone numbers, one per line, E.164 format.
│ Only messages from these numbers are processed.
│ First entry is used as default alert contact.
│ Example: +61400000000
│
├── pickle-watch.sh — Message watcher daemon script. Polls chat.db,
│ filters messages, spawns Claude sessions.
│ Managed by launchd via com.pickle.watch plist.
│
├── pickle-health.sh — Health monitor daemon script. Checks Docker
│ containers, service ports, disk usage. Deduplicates
│ alerts. Managed by com.pickle.health plist.
│
├── pickle-heartbeat.sh — Heartbeat daemon script. Reads HEARTBEAT.md,
│ executes scheduled autonomous tasks. Managed by
│ com.pickle.heartbeat plist.
│
├── pickle-start.sh — Start script. Copies plist files to
│ ~/Library/LaunchAgents/, loads them with launchctl,
│ and verifies they are running.
│
├── pickle-stop.sh — Stop script. Unloads all three daemons via
│ launchctl and optionally removes plist files.
│
├── pickle-test.sh — End-to-end verification script. Tests each
│ component independently: chat.db access, sqlite3
│ availability, Claude CLI, MCP servers, contacts
│ file, directory structure, permissions.
│
├── .gitignore — Ignores logs/, memory/daily/, .last_rowid,
│ .env, mcp-config.json (contains secrets),
│ and other runtime state files.
│
├── skills/ — Reusable procedure library. Each file is a
│ │ markdown document describing a multi-step
│ │ procedure the agent can follow.
│ │
│ ├── README.md — How to create and use skills
│ ├── research-scan.md — Procedure for scanning academic databases
│ │ for new papers on configured topics
│ ├── email-draft.md — Procedure for drafting professional emails
│ │ with appropriate tone and structure
│ ├── service-restart.md — Procedure for safely restarting services
│ │ with pre/post health checks
│ ├── incident-report.md — Procedure for writing structured incident
│ │ reports after service outages
│ ├── morning-briefing.md — Procedure for compiling the daily morning
│ │ briefing from overnight events
│ ├── blog-writing.md — Procedure for researching and drafting
│ │ blog posts on mental health topics
│ └── zanda-lookup.md — Procedure for looking up client/appointment
│ data in the Zanda practice management system
│
├── memory/ — Persistent memory across sessions. This is
│ │ the agent's long-term storage. All Claude
│ │ sessions read from and write to these files.
│ │
│ ├── incidents.jsonl — Append-only structured log. One JSON object
│ │ per line. Records every significant event:
│ │ messages received, health issues, tasks done.
│ ├── knowledge.md — Accumulated learnings about the environment,
│ │ systems, people, and domain. Deduplicated.
│ ├── contacts.md — Personal notes about known contacts:
│ │ preferences, roles, past interactions.
│ ├── tasks.md — Carry-over task list. Items that couldn't
│ │ be completed in one session persist here.
│ ├── daily/ — Ephemeral daily notes. One file per day
│ │ (YYYY-MM-DD.md). Auto-cleaned after 30 days.
│ ├── blog/ — Blog post drafts in progress. Organized
│ │ by topic and date.
│ └── research/ — Research findings. Papers, summaries,
│ and synthesis documents.
│
├── launchd/ — macOS LaunchAgent plist templates
│ ├── com.pickle.watch.plist — Plist for message watcher (KeepAlive)
│ ├── com.pickle.health.plist — Plist for health monitor (StartInterval)
│ └── com.pickle.heartbeat.plist — Plist for heartbeat (StartInterval)
│
└── logs/ — Runtime logs (gitignored)
├── pickle-watch.log — Watcher daemon stdout/stderr
├── pickle-health.log — Health monitor stdout/stderr
├── pickle-heartbeat.log — Heartbeat daemon stdout/stderr
└── claude-sessions/ — Individual session transcripts, one file
per Claude Code CLI invocation, named with
timestamp and trigger context.
| Requirement | Why | How to verify |
|---|---|---|
| macOS (13 Ventura or later) | Uses launchd, iMessage, chat.db, Apple frameworks |
sw_vers |
| Claude Code CLI | The AI engine — every session is a Claude Code invocation | claude --version |
| Anthropic API key | Configured within Claude Code (not in agent config) | claude should work interactively |
| Full Disk Access for Terminal | Required to read ~/Library/Messages/chat.db |
System Settings → Privacy & Security → Full Disk Access → Terminal ✓ |
| sqlite3 | Queries chat.db — ships with macOS |
which sqlite3 |
| Python 3.12+ | Used for safe message text parsing and the Apple Mail MCP server | python3 --version |
| Node.js 18+ | Required for most MCP servers (they run as Node processes) | node --version |
| iMessage configured | The Mac must be signed into iMessage with an Apple ID | Messages.app → Settings → iMessage ✓ |
| Requirement | Why |
|---|---|
| Docker Desktop | Only if you are monitoring Docker containers |
| Ollama | Only if you run local LLMs that the agent monitors |
| Homebrew | Convenient for installing Node.js, Python, etc. |
The agent needs these permissions to function. All are configured in System Settings → Privacy & Security:
- Full Disk Access — for Terminal.app (or whichever terminal runs launchd jobs). Without this, reading
chat.dbreturns a permission error. - Automation — Terminal may need permission to control Messages.app if using AppleScript-based iMessage sending (depends on your MCP server implementation).
- Accessibility — not required for standard operation, but some MCP servers may request it.
git clone https://github.com/your-org/pickle-agent.git
cd pickle-agentEdit contacts.conf with the phone numbers that should be able to message the agent. One number per line, E.164 format:
+61400000000
+61400000001
The first number in the file is used as the default alert contact (where health alerts are sent).
Copy the example config and fill in your API keys:
cp mcp-config.example.json mcp-config.jsonEdit mcp-config.json and provide:
- API keys for any external services (Semantic Scholar, PubMed, ICD-11, Zanda, Giphy)
- Paths to MCP server executables/scripts
- Any per-server configuration
See the MCP Servers section for details on each integration.
Each plist file in launchd/ contains hardcoded paths that must match your system. Edit each file and update:
<key>WorkingDirectory</key>
<string>/Users/YOUR_USERNAME/pickle-agent</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/YOUR_USERNAME/pickle-agent/pickle-watch.sh</string>
</array>
<key>StandardOutPath</key>
<string>/Users/YOUR_USERNAME/pickle-agent/logs/pickle-watch.log</string>
<key>StandardErrorPath</key>
<string>/Users/YOUR_USERNAME/pickle-agent/logs/pickle-watch.log</string>Replace /Users/YOUR_USERNAME/pickle-agent with the absolute path to your clone.
mkdir -p logs/claude-sessions
mkdir -p memory/{daily,blog,research}
touch memory/incidents.jsonl
touch memory/knowledge.md
touch memory/contacts.md
touch memory/tasks.mdcp launchd/com.pickle.watch.plist ~/Library/LaunchAgents/
cp launchd/com.pickle.health.plist ~/Library/LaunchAgents/
cp launchd/com.pickle.heartbeat.plist ~/Library/LaunchAgents/chmod +x pickle-test.sh
./pickle-test.shThis verifies every component: chat.db access, sqlite3, Claude CLI, directory structure, contacts.conf format, MCP config validity, and disk permissions. Fix any failures before proceeding.
chmod +x pickle-start.sh
./pickle-start.shThis loads all three daemons into launchd. The watcher starts polling immediately (after its boot wait). The health monitor and heartbeat will fire on their respective intervals.
Verify they are running:
launchctl list | grep pickleYou should see three entries:
- 0 com.pickle.watch
- 0 com.pickle.health
- 0 com.pickle.heartbeat
Send an iMessage from one of your allowed contacts to the Mac's iMessage account. Within a few seconds, you should receive a reply. Check logs/pickle-watch.log for debug output.
All configuration is done through environment variables. These can be set in the launchd plist files (via EnvironmentVariables dict), exported in the daemon shell scripts, or set in a .env file sourced by the scripts.
| Variable | Default | Description |
|---|---|---|
PICKLE_POLL_INTERVAL |
2 |
Seconds between chat.db polls. Lower values mean faster response but more CPU. Values below 1 are not recommended. |
PICKLE_MAX_BUDGET |
0.50 |
Maximum USD spend per watcher Claude session. If the agent exceeds this budget on a single message response, the session is terminated. Prevents runaway costs from complex queries. |
PICKLE_WATCH_BOOT_WAIT |
30 |
Seconds to wait after daemon start before beginning to poll. Gives the system time to initialize after boot. |
PICKLE_MODEL |
sonnet |
Claude model identifier. Passed to --model. Options: sonnet, opus, haiku. Sonnet is recommended for the best cost/performance balance. |
PICKLE_ALERT_CONTACT |
First entry in contacts.conf |
Phone number (E.164) used for outbound alerts. Overrides the default if set. |
| Variable | Default | Description |
|---|---|---|
PICKLE_HEALTH_INTERVAL |
1800 |
Seconds between health checks (30 minutes). Set in the launchd plist as StartInterval. The shell script itself does not loop — launchd re-invokes it. |
PICKLE_HEALTH_BUDGET |
0.50 |
Maximum USD spend per health check session. Health sessions are usually cheap (just sending an alert), but remediation attempts (restarting services) can use more tokens. |
PICKLE_BOOT_WAIT |
120 |
Seconds to wait on first invocation after boot. Docker containers, databases, and network services need time to start. The health daemon should not raise false alarms during boot. |
PICKLE_DISK_THRESHOLD |
90 |
Disk usage percentage that triggers an alert. Integer, no percent sign. |
| Variable | Default | Description |
|---|---|---|
PICKLE_HEARTBEAT_INTERVAL |
3600 |
Seconds between heartbeat invocations (60 minutes). Set in launchd plist as StartInterval. |
PICKLE_HEARTBEAT_BUDGET |
0.75 |
Maximum USD spend per heartbeat session. Higher than other daemons because heartbeat tasks (research, blog writing) are more complex and token-intensive. |
PICKLE_BOOT_WAIT |
180 |
Seconds to wait on first invocation after boot. Longer than health daemon because the heartbeat depends on services that the health daemon also checks. |
| Variable | Default | Description |
|---|---|---|
PICKLE_MODEL |
sonnet |
Claude model for all sessions. Can be overridden per-daemon by setting it in the specific plist. |
PICKLE_AGENT_DIR |
Script's directory | Base directory for the agent. All relative paths resolve from here. Auto-detected from the script location. |
PICKLE_LOG_DIR |
$PICKLE_AGENT_DIR/logs |
Directory for log files. |
PICKLE_MEMORY_DIR |
$PICKLE_AGENT_DIR/memory |
Directory for persistent memory files. |
<key>EnvironmentVariables</key>
<dict>
<key>PICKLE_POLL_INTERVAL</key>
<string>2</string>
<key>PICKLE_MAX_BUDGET</key>
<string>0.50</string>
<key>PICKLE_MODEL</key>
<string>sonnet</string>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
</dict>Note: The PATH variable is important in launchd plists because launchd jobs run with a minimal environment. You must include the paths to claude, python3, node, docker, and sqlite3.
MCP (Model Context Protocol) servers extend Claude's capabilities by providing tool integrations. Each server runs as a separate process and communicates with Claude Code CLI via stdio or HTTP. The agent's mcp-config.json defines all available MCP servers.
- Purpose: Send and read iMessages. This is the agent's primary communication channel.
- Transport: Native macOS integration via AppleScript or direct Messages framework access.
- Tools provided:
send_message— send an iMessage to a phone number or emailread_messages— read recent messages from a conversationlist_conversations— list active iMessage conversations
- API keys: None (uses local macOS iMessage account)
- Permissions: The process running Claude Code must have access to Messages.app automation.
- Purpose: Read, search, and draft emails via the macOS Mail.app.
- Transport: Python MCP server using the
pyobjcframework to access Apple Mail via ScriptingBridge. - Tools provided:
list_mailboxes— list available mailboxessearch_mail— search emails by subject, sender, date rangeread_email— read the full content of a specific emailcreate_draft— create a new email draft in Mail.applist_unread— list unread emails across mailboxes
- API keys: None (uses local Mail.app)
- Requirements: Python 3.12+,
pyobjc-framework-ScriptingBridge,pyobjc-framework-Cocoa - Note: Mail.app must be configured with at least one email account.
- Purpose: Search for academic papers across multiple databases. Used for research scans and blog post research.
- Transport: Node.js MCP server wrapping the Semantic Scholar API.
- Tools provided:
search_papers— keyword/phrase search across papersget_paper— retrieve full paper details by IDget_citations— get papers citing a given paperget_references— get papers referenced by a given papersearch_authors— search for authors
- API keys: Semantic Scholar API key (free tier available, recommended for rate limits)
- Databases searched: Semantic Scholar (aggregates PubMed, ArXiv, ACL, DBLP, and others)
- Purpose: Direct PubMed integration for medical/health literature search.
- Transport: Node.js MCP server wrapping the NCBI E-Utilities API.
- Tools provided:
search_pubmed— search PubMed with MeSH terms and keywordsget_abstract— retrieve abstract for a given PMIDget_full_details— retrieve full bibliographic details
- API keys: NCBI API key (optional but recommended; without it, rate limited to 3 req/sec)
- Purpose: WHO International Classification of Diseases, 11th revision. Useful for looking up diagnostic codes in a health practice context.
- Transport: Node.js MCP server wrapping the WHO ICD-11 API.
- Tools provided:
search_icd11— search for ICD-11 codes by termget_code_details— get full details for a specific ICD-11 codelinearization_search— search within a specific linearization (e.g., MMS)
- API keys: WHO ICD API
client_idandclient_secret(free registration at icd.who.int/icdapi)
- Purpose: Practice management system integration. Look up appointments, clients, invoices, and practitioner schedules.
- Transport: Node.js MCP server wrapping the Zanda/Power Diary API.
- Tools provided:
search_clients— search for clients by name or IDget_appointments— get appointments for a date rangeget_practitioner_schedule— view practitioner availabilityget_invoice— retrieve invoice details
- API keys: Zanda API key (generated in Zanda admin panel)
- Special note: This server uses a "code-mode pattern" where sensitive operations require confirmation via the permissions model. The agent can look up data freely but cannot modify records without approval.
- Purpose: Search for and send GIFs. Adds personality to iMessage conversations.
- Transport: Node.js MCP server wrapping the Giphy API.
- Tools provided:
search_gifs— search for GIFs by keywordget_trending— get trending GIFsget_random— get a random GIF for a tag
- API keys: Giphy API key (free tier available at developers.giphy.com)
{
"mcpServers": {
"imessage": {
"command": "node",
"args": ["/path/to/imessage-mcp/index.js"],
"env": {}
},
"apple-mail": {
"command": "python3",
"args": ["/path/to/apple-mail-mcp/server.py"],
"env": {}
},
"paper-search": {
"command": "node",
"args": ["/path/to/paper-search-mcp/index.js"],
"env": {
"SEMANTIC_SCHOLAR_API_KEY": "your-key-here"
}
},
"pubmed": {
"command": "node",
"args": ["/path/to/pubmed-mcp/index.js"],
"env": {
"NCBI_API_KEY": "your-key-here"
}
},
"icd11": {
"command": "node",
"args": ["/path/to/icd11-mcp/index.js"],
"env": {
"ICD_CLIENT_ID": "your-id",
"ICD_CLIENT_SECRET": "your-secret"
}
},
"zanda": {
"command": "node",
"args": ["/path/to/zanda-mcp/index.js"],
"env": {
"ZANDA_API_KEY": "your-key-here"
}
},
"giphy": {
"command": "node",
"args": ["/path/to/giphy-mcp/index.js"],
"env": {
"GIPHY_API_KEY": "your-key-here"
}
}
}
}The file pickle-tools.conf controls exactly which tools the agent is allowed to use. It is sourced (. pickle-tools.conf) by all three daemon scripts and exports a variable containing a comma-separated list of tool identifiers.
- The conf file defines arrays of tools by category.
- The script joins them into a single comma-separated string.
- This string is passed to Claude Code CLI via the
--allowedToolsflag. - Claude Code will refuse to call any tool not in this list, even if an MCP server offers it.
The allowlist is a security boundary. Even though --dangerously-skip-permissions is set (required for autonomous operation), the allowlist prevents the agent from using tools that could cause damage. For example, you might allow docker ps and docker restart but not docker rm or docker compose down.
The allowlist is organized into logical categories:
Built-in Claude Code tools:
Read— read files from diskWrite— write files to diskEdit— edit files with find-and-replaceBash— execute shell commandsGlob— find files by patternGrep— search file contents
iMessage tools:
mcp__imessage__send_message— send iMessagesmcp__imessage__read_messages— read iMessage conversations
Email tools:
mcp__apple-mail__search_mail— search emailsmcp__apple-mail__read_email— read email contentmcp__apple-mail__create_draft— draft new emailsmcp__apple-mail__list_unread— list unread emails
Research tools:
mcp__paper-search__search_papers— search academic papersmcp__paper-search__get_paper— get paper detailsmcp__pubmed__search_pubmed— search PubMedmcp__pubmed__get_abstract— get paper abstractsmcp__icd11__search_icd11— search ICD-11 codes
Practice management tools:
mcp__zanda__search_clients— search Zanda clientsmcp__zanda__get_appointments— view appointments
Fun tools:
mcp__giphy__search_gifs— search for GIFs
To add a new tool, add its identifier to the appropriate category array in pickle-tools.conf. The identifier format for MCP tools is mcp__<server-name>__<tool-name>. For built-in tools, use the tool name directly (e.g., Bash, Read).
To find available tool identifiers for an MCP server, run Claude Code interactively with the MCP config and ask it to list available tools.
The agent operates in --dangerously-skip-permissions mode, which disables Claude Code's interactive approval prompts. This is necessary for autonomous operation (there is no human at the keyboard to click "Allow"). Instead, the agent enforces a three-tier permissions model through its system prompt (CLAUDE.md) and the tool allowlist.
These actions can be performed autonomously at any time:
| Action | Examples |
|---|---|
| Diagnostics | docker ps, docker logs, df -h, curl health endpoints, ping, nc -z |
| Service restarts | docker restart <container>, brew services restart <service> |
| Email operations | Reading emails, drafting emails (drafts only — never auto-sends) |
| Research | Searching academic databases, reading papers, synthesizing findings |
| Memory operations | Writing to memory/, reading from memory/, appending to incidents.jsonl |
| iMessage replies | Responding to incoming messages from allowed contacts |
| File reads | Reading any file in the agent directory or monitored services |
| Log inspection | Reading log files for debugging |
These actions are allowed but only after confirming with the human via iMessage. The agent must ask and receive explicit approval before proceeding:
| Action | Why confirmation needed |
|---|---|
| Git operations | git commit, git pull, git checkout — code changes should be reviewed |
| Configuration edits | Editing docker-compose.yaml, nginx configs, .env files — could break services |
| Stopping services | docker stop, brew services stop — intentional downtime needs approval |
| Installing packages | brew install, pip install, npm install — system modifications |
| Sending emails | Actually sending (not drafting) emails — should be reviewed first |
These actions are hardcoded as forbidden in the system prompt. The agent will refuse to perform them regardless of instructions:
| Action | Why forbidden |
|---|---|
docker compose down |
Destroys all containers and networks — catastrophic |
| Secret/key modification | Editing .env files containing API keys, modifying mcp-config.json secrets |
git push |
Pushing code to remote — should always be done by a human |
git push --force |
Destructive force push — never |
File deletion outside memory/ |
Deleting code, configs, or data files |
| System modification | Modifying launchd plists, system configs, crontabs |
| Network configuration | Changing DNS, firewall rules, Cloudflare settings |
| Database writes | Direct writes to MySQL, PostgreSQL, Elasticsearch |
- Tool allowlist (
pickle-tools.conf) — first line of defense. Tools not in the list simply cannot be called. - System prompt (
CLAUDE.md) — contains explicit rules about what requires confirmation and what is forbidden. Claude follows these instructions. - Budget cap (
--max-budget-usd) — limits total spend per session, preventing runaway operations. - Monitoring — all sessions are logged, providing an audit trail.
This is a defense-in-depth approach. No single mechanism is relied upon exclusively.
The memory system is what gives Pickle continuity across sessions. Each Claude Code CLI session is ephemeral — it starts fresh with no memory of previous sessions. The memory directory bridges this gap by providing persistent, file-based storage that every session can read from and write to.
Purpose: Append-only structured event log.
Format: One JSON object per line (JSON Lines format). Each entry records a significant event.
Schema:
{
"timestamp": "2026-03-19T10:30:00+11:00",
"type": "message|health|task|error|alert",
"source": "watch|health|heartbeat",
"summary": "Brief description of what happened",
"details": "Extended details, error messages, etc.",
"action_taken": "What the agent did in response",
"contact": "+61400000000",
"resolved": true
}Usage rules:
- Append only — never edit or delete existing entries
- Every Claude session should log its significant actions here
- Used by the morning briefing skill to compile overnight summaries
- Used by the heartbeat to track patterns (recurring failures, etc.)
Purpose: Accumulated learnings about the environment, systems, and domain.
Structure: Markdown with sections for different knowledge categories. Each entry is a bullet point.
Usage rules:
- Before adding a new entry, the agent must check for duplicates
- Entries should be factual and specific (not "Docker is useful" but "ragflow-server needs ragflow-redis and ragflow-es-01 to be healthy before it will start")
- Periodically reviewed and pruned by the heartbeat daemon
Example entries:
## Infrastructure
- ragflow-server takes ~90 seconds to become healthy after container start
- litellm_db must be fully ready before litellm can connect (check pg_isready)
- Cloudflare tunnel restarts automatically but takes ~30s to reconnect
## Domain
- OT = Occupational Therapy, not Operations/Other
- NDIS reporting deadlines are quarterly
- Zanda API rate limit is 100 requests per minutePurpose: Personal notes about known contacts. Helps the agent maintain continuity in conversations.
Structure: Markdown with a section per contact (identified by phone number and name).
Example:
## Sarah (+61400000000)
- Practice owner, occupational therapist
- Prefers brief updates, not lengthy reports
- Usually checks messages between 8-9am
- Interested in sensory processing researchPurpose: Carry-over task list. When a session cannot complete a task (ran out of budget, needs information, waiting on something), it records the task here for a future session to pick up.
Structure: Markdown checklist.
## Pending Tasks
- [ ] Finish blog post on sensory diets (draft in memory/blog/)
- [ ] Follow up on ragflow indexing issue from 2026-03-18
- [ ] Research new NDIS pricing framework changes
## Completed (last 7 days)
- [x] 2026-03-17: Sent weekly research digest to Sarah
- [x] 2026-03-16: Fixed litellm_db connection timeoutPurpose: Ephemeral daily notes. One markdown file per day, named YYYY-MM-DD.md.
Usage: Used for tracking within-day state, such as "already sent morning briefing today" or "ragflow was restarted at 2pm." The heartbeat daemon auto-cleans files older than 30 days.
Purpose: Blog post drafts. Organized by topic. Posts go through multiple stages: outline → draft → review → final.
Purpose: Research findings. When the agent performs a research scan, it saves summaries and paper references here for later synthesis.
Skills are reusable, structured procedures stored as markdown files in the skills/ directory. They serve as the agent's "playbook" — step-by-step instructions for complex, multi-step tasks.
Claude is capable of figuring out how to do things from scratch, but:
- Consistency — skills ensure the same task is done the same way every time
- Quality — skills encode best practices and lessons learned
- Efficiency — skills reduce token usage by providing a clear plan instead of requiring the agent to reason from first principles
- Auditability — skills make it clear what the agent will do before it does it
Each skill is a markdown file with the following structure:
# Skill Name
## Purpose
What this skill accomplishes.
## When to use
Triggers or conditions under which this skill should be invoked.
## Prerequisites
What must be true before starting.
## Steps
1. First step with specific details
2. Second step
- Sub-detail
- Sub-detail
3. Third step
## Output
What the skill produces (e.g., a file, a message, a log entry).
## Error handling
What to do if something goes wrong at each step.Skills are referenced in the system prompt (CLAUDE.md) and task checklists (HEARTBEAT.md). When the agent encounters a task that matches a skill, it reads the skill file and follows its steps. For example:
- HEARTBEAT.md says: "If it's Monday morning, execute the blog-writing skill"
- The agent reads
skills/blog-writing.md - It follows the steps: choose topic → search papers → outline → draft → save to
memory/blog/
- Create a new markdown file in
skills/with a descriptive name (e.g.,client-report.md) - Follow the format above
- Reference it in CLAUDE.md, HEARTBEAT.md, or another skill as appropriate
- Test it by sending the agent a message asking it to perform the skill
The agent enforces work hours to avoid sending iMessages at inappropriate times.
Work hours are defined in the system prompt (CLAUDE.md) and daemon scripts:
- Days: Monday through Friday
- Hours: 8:00 AM to 6:00 PM
- Timezone: AEST (Australia/Eastern, UTC+10/+11 depending on DST)
Message Watcher: No hours restriction. If someone texts the agent at 2am, it replies. The principle is: if a human initiated contact, they want a response regardless of the hour.
Health Monitor: Checks the current time before sending alerts.
- During work hours: Sends iMessage alert to the configured contact.
- Outside work hours: Logs the issue to
memory/incidents.jsonl, attempts automated remediation (restart containers, etc.), but does not send an iMessage. The issue is queued for the next morning briefing. - Exception: Critical issues (all services down, disk at 99%) may override this rule if configured.
Heartbeat Daemon: Checks the current time before executing notification-generating tasks.
- During work hours: Executes all applicable tasks, sends results via iMessage if needed.
- Outside work hours: Only executes silent tasks (memory maintenance, research that saves to files). Skips tasks that would generate messages.
Each daemon script checks the time using shell date commands:
current_hour=$(date +%H)
current_dow=$(date +%u) # 1=Monday, 7=Sunday
if [[ $current_dow -ge 1 && $current_dow -le 5 && $current_hour -ge 8 && $current_hour -lt 18 ]]; then
WORK_HOURS=true
else
WORK_HOURS=false
fiThis is also reinforced in the Claude session prompt, so even if the shell check is bypassed, the agent's system prompt instructs it not to send messages outside hours.
1. launchd keeps pickle-watch.sh running (KeepAlive=true)
2. pickle-watch.sh polls chat.db every PICKLE_POLL_INTERVAL seconds
3. New message detected (ROWID > .last_rowid, is_from_me=0)
4. Sender phone number checked against contacts.conf
5. ✓ Match → continue / ✗ No match → skip, update .last_rowid
6. Message text extracted via python3 (safe unicode handling)
7. .last_rowid updated to current ROWID
8. Claude Code CLI spawned:
claude -p "You received a message from {phone} at {time}: '{text}'.
Reply via iMessage." \
--system-prompt "$(cat CLAUDE.md)" \
--mcp-config mcp-config.json \
--allowedTools "$ALLOWED_TOOLS" \
--dangerously-skip-permissions \
--model "$PICKLE_MODEL" \
--max-budget-usd "$PICKLE_MAX_BUDGET" \
2>&1 | tee "logs/claude-sessions/watch-{timestamp}-{phone}.log"
9. Claude session:
a. Reads system prompt (CLAUDE.md) → understands identity and rules
b. Reads memory files (incidents.jsonl, knowledge.md, contacts.md)
c. Formulates response
d. Calls mcp__imessage__send_message to reply
e. Logs interaction to memory/incidents.jsonl
f. Session ends (budget exhausted or task complete)
10. pickle-watch.sh returns to polling loop
1. launchd invokes pickle-health.sh (StartInterval=1800)
2. Script checks if boot wait is needed (first run after boot)
3. Docker container checks run:
docker ps --format '{{.Names}} {{.Status}}'
4. Port checks run:
nc -z localhost 8000 (MCPO)
nc -z localhost 8888 (Jupyter)
nc -z localhost 11434 (Ollama)
nc -z localhost 5151 (Whisper)
nc -z localhost 5050 (TTS)
5. Disk usage checked:
df -h / | awk '{print $5}' | tail -1
6. Issue list compiled: ["ragflow-server: Exited", "port 5151: unreachable"]
7. Compared against LAST_ISSUES from previous run
8. New issues found? → Continue / No new issues? → Exit
9. Work hours checked
10. Claude Code CLI spawned:
claude -p "Health check detected issues:
- ragflow-server: container exited
- port 5151 (Whisper): unreachable
Attempt remediation. Alert via iMessage if work hours." \
--system-prompt "$(cat CLAUDE.md)" \
--mcp-config mcp-config.json \
--allowedTools "$ALLOWED_TOOLS" \
--dangerously-skip-permissions \
--model "$PICKLE_MODEL" \
--max-budget-usd "$PICKLE_HEALTH_BUDGET" \
2>&1 | tee "logs/claude-sessions/health-{timestamp}.log"
11. Claude session:
a. Reads skills/service-restart.md for remediation procedure
b. Attempts to restart affected services
c. Verifies health post-restart
d. Logs incident to memory/incidents.jsonl
e. If work hours: sends iMessage alert with status
f. If outside hours: saves alert for morning briefing
g. Session ends
12. LAST_ISSUES updated with current issue set
13. Script exits (launchd will reinvoke after interval)
1. launchd invokes pickle-heartbeat.sh (StartInterval=3600)
2. Script checks if boot wait is needed
3. Current date, time, day-of-week determined
4. HEARTBEAT.md read into variable
5. memory/tasks.md read into variable
6. Claude Code CLI spawned:
claude -p "Heartbeat at {datetime} ({dayname}).
Task checklist:
{HEARTBEAT.md contents}
Carry-over tasks:
{tasks.md contents}
Execute applicable tasks." \
--system-prompt "$(cat CLAUDE.md)" \
--mcp-config mcp-config.json \
--allowedTools "$ALLOWED_TOOLS" \
--dangerously-skip-permissions \
--model "$PICKLE_MODEL" \
--max-budget-usd "$PICKLE_HEARTBEAT_BUDGET" \
2>&1 | tee "logs/claude-sessions/heartbeat-{timestamp}.log"
7. Claude session:
a. Evaluates task checklist against current time/day
b. Checks work hours for notification-generating tasks
c. Executes applicable tasks (research, email check, etc.)
d. Updates memory files with results
e. Updates tasks.md (mark completed, add new)
f. If morning + work hours: compiles and sends briefing
g. Session ends
8. Script exits (launchd will reinvoke after interval)
Every Claude Code CLI invocation uses the following flags. Understanding each one is essential for debugging and customization.
Non-interactive prompt mode. Instead of opening an interactive REPL, Claude Code executes the given prompt and exits. This is what enables autonomous operation — there is no human typing in a terminal.
The prompt is a string that tells Claude what to do. For the watcher, it contains the incoming message. For health, it contains detected issues. For heartbeat, it contains the task checklist.
Custom system prompt. Overrides Claude Code's default system prompt with the contents of CLAUDE.md. This is the agent's "brain" — it defines identity, rules, capabilities, and behavioral guidelines.
The $(cat CLAUDE.md) shell substitution reads the entire file into the argument. This means changes to CLAUDE.md take effect on the next session without restarting any daemons.
MCP server configuration. Tells Claude Code which MCP servers to start and connect to. Each server provides tools (functions) that Claude can call. The config file specifies the command, arguments, and environment variables for each server.
Tool allowlist. A comma-separated list of tool identifiers that Claude is permitted to use. Any tool not in this list will be unavailable, even if an MCP server provides it. This is the primary security mechanism in autonomous mode.
Built from pickle-tools.conf at session start time.
Disable interactive approval prompts. Normally, Claude Code asks for human approval before performing actions like writing files, running commands, or calling tools. In autonomous mode, there is no human to approve, so this flag disables all prompts.
This is why the tool allowlist, system prompt rules, and budget cap are critical — they replace human-in-the-loop approval with policy-based controls.
Warning: This flag means the agent can perform any allowed action without asking. Ensure your allowlist and system prompt are correctly configured before enabling this.
Model selection. Specifies which Claude model to use. Options:
| Model | Use case | Cost | Speed |
|---|---|---|---|
haiku |
Simple, fast responses | Lowest | Fastest |
sonnet |
Balanced — recommended default | Medium | Fast |
opus |
Complex reasoning, long research | Highest | Slowest |
The default (sonnet) is recommended for most sessions. The heartbeat daemon might benefit from opus for complex tasks like blog writing, but the budget increase must be considered.
Per-session spending cap. If the session's API costs exceed this amount, Claude Code terminates the session. This prevents runaway costs from complex prompts, infinite loops, or pathological inputs.
Budget recommendations:
- Watcher: $0.50 — most messages need a simple response
- Health: $0.50 — diagnostics and alerts are straightforward
- Heartbeat: $0.75 — research and writing tasks need more room
Pickle Agent is designed to be forked and customized. The architecture separates identity (who the agent is) from infrastructure (how it runs), making it straightforward to adapt.
Replace CLAUDE.md with your agent's system prompt. This should define:
- Who the agent is (name, role, organization)
- What it can do (capabilities, integrations)
- How it should behave (rules, constraints, tone)
- What it knows about its environment (services, infrastructure)
Replace SOUL.md with your agent's personality definition:
- Communication style (formal/informal, verbose/concise)
- Tone (professional, friendly, technical)
- Values (what the agent prioritizes)
- Humor and personality traits
Replace HEARTBEAT.md with your agent's task checklist. Consider:
- What should happen every hour? (health checks, email triage)
- What should happen every few hours? (research, data collection)
- What should happen daily? (briefings, reports)
- What should happen weekly? (summaries, content creation)
Update mcp-config.json with your MCP servers. You might:
- Keep iMessage (most universally useful)
- Replace domain-specific servers (Zanda, ICD-11) with your own
- Add new integrations (Slack, Discord, Jira, GitHub, etc.)
Update contacts.conf with the phone numbers that should be able to reach your agent.
Replace or add to the skills/ directory with procedures relevant to your domain. Examples:
- A DevOps agent might have skills for incident response, deployment, monitoring
- A personal assistant might have skills for scheduling, travel planning, expense tracking
- A research agent might have skills for literature review, data analysis, report writing
Adjust intervals, budgets, and thresholds:
- High-urgency environments: lower
PICKLE_POLL_INTERVAL, raise budgets - Cost-sensitive environments: raise
PICKLE_HEALTH_INTERVAL, lower budgets - Complex tasks: raise
PICKLE_HEARTBEAT_BUDGET, consideropusmodel
Edit pickle-health.sh to check your specific services:
- Replace Docker container names with your stack
- Replace port numbers with your services
- Adjust disk threshold for your storage situation
- Add custom health checks (HTTP endpoints, database connectivity, etc.)
Cause: The process (Terminal, launchd job) does not have Full Disk Access.
Fix:
- Open System Settings → Privacy & Security → Full Disk Access
- Add Terminal.app (or iTerm, or whichever terminal app launchd runs under)
- If running via launchd, the parent process needs access. Try adding
/usr/libexec/launchdor/usr/sbin/cron - Restart the daemon after granting access
Verify:
sqlite3 ~/Library/Messages/chat.db "SELECT COUNT(*) FROM message;"Cause: The claude binary is not in the PATH used by launchd.
Fix:
- Find where Claude is installed:
which claude - Add that directory to the
PATHin your launchd plist:<key>EnvironmentVariables</key> <dict> <key>PATH</key> <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:/Users/YOU/.nvm/versions/node/v20.0.0/bin</string> </dict>
- Unload and reload the plist
Possible causes and fixes:
-
contacts.conf format wrong. Numbers must be in E.164 format with
+prefix and country code. No spaces, no dashes. Example:+61400000000 -
.last_rowidis ahead of current messages. If.last_rowidcontains a number higher than the latest message ROWID, no messages will be processed. Delete the file and restart the watcher:rm .last_rowid launchctl unload ~/Library/LaunchAgents/com.pickle.watch.plist launchctl load ~/Library/LaunchAgents/com.pickle.watch.plist
-
Messages are from a group chat. The watcher only processes individual (1:1) messages by default. Group chat messages have a different
chat_identifierformat. -
iMessage vs SMS. The agent reads from
chat.db, which includes both iMessage and SMS. However, the sender phone format may differ. Check the raw database:sqlite3 ~/Library/Messages/chat.db "SELECT ROWID, text, handle_id FROM message ORDER BY ROWID DESC LIMIT 5;"
Possible causes:
-
Alert deduplication. The health monitor only alerts on new issues. If the issue has been present since the last check, no alert fires. Check the state file for current
LAST_ISSUES. -
Work hours. Outside Mon–Fri 8am–6pm AEST, the health monitor silently logs issues without sending iMessages. Check
memory/incidents.jsonlfor logged events. -
Interval too long. If
PICKLE_HEALTH_INTERVALis set very high, checks are infrequent. Reduce the interval for testing. -
Boot wait. After system restart, the health monitor waits
PICKLE_BOOT_WAITseconds before its first check. Check logs for "waiting for boot" messages.
Diagnose:
# Check if loaded
launchctl list | grep pickle
# Check for load errors
launchctl load -w ~/Library/LaunchAgents/com.pickle.watch.plist 2>&1
# Check daemon logs
cat ~/pickle-agent/logs/pickle-watch.logCommon plist issues:
- Wrong paths. Every path in the plist must be absolute and correct. No
~expansion — use/Users/yourname/... - Missing directories. The log directory must exist before the daemon starts. Create it:
mkdir -p logs/claude-sessions - File permissions. The scripts must be executable:
chmod +x pickle-*.sh - Plist syntax error. Validate:
plutil -lint ~/Library/LaunchAgents/com.pickle.watch.plist
Check session logs:
ls -la logs/claude-sessions/
tail -50 logs/claude-sessions/watch-latest.logCommon causes:
- Budget exhausted. The session hit
--max-budget-usdbefore completing. Increase the budget. - MCP server not starting. Check that MCP server paths in
mcp-config.jsonare correct and dependencies are installed. - API key expired/invalid. Check MCP server logs for authentication errors.
- Model unavailable. If using a specific model (e.g.,
opus), ensure your API key has access.
Diagnose: Check memory/incidents.jsonl for session frequency and types. Look at Claude Code's built-in cost tracking.
Reduce costs:
- Lower
PICKLE_HEARTBEAT_BUDGETandPICKLE_MAX_BUDGET - Increase
PICKLE_HEALTH_INTERVALandPICKLE_HEARTBEAT_INTERVAL - Use
haikumodel for simple tasks - Review skills for unnecessary verbosity
- Ensure alert deduplication is working (not spawning sessions for repeated issues)
This project is released under the MIT License.
MIT License
Copyright (c) 2026 Pickle Agent Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Built with Claude Code by Anthropic.