AI agent runtime — a Claude Code-native Slack bot with persistent memory.
Goldfish allows you to use Claude Code from Slack — but it's also much more than that. Think of it like the best parts of the OpenClaw AI assistant harness but dramatically simpler, polished for a specific use case. It's your agent that knows you.
This gives you:
- Full tool access: Bash, file read/write, web search
- Thread-based sessions: Multiple simultaneous Slack threads across different channels
- Persistent memory: Your agent remembers the most important details of your conversations over time and gets to know you personally, details about your life, and what you're working on
- Proactive outreach: Morning briefings, hourly heartbeat checks, and optional daily exploration sessions via scheduled tasks and reminders
- Zero API cost: Conversations run through Claude Code on a Max subscription
OpenClaw is great, but it has some issues:
- It is complex to set up and keep working
- It had many bugs in its Slack integration
And crucially:
- Anthropic stopped allowing SSO auth with third-party harnesses like OpenClaw, so it became enormously expensive
- Claude Code "just works" with a Claude Max subscription; OpenClaw had reliability issues with Claude models
While OpenClaw could technically work via ACP bridges to Claude Code, there were many problems including zombie claude sessions and it still required significant API usage (at full API costs) for certain features. This made it unusable in practice.
If you only want to use Claude over Slack, Goldfish does 90% of what OpenClaw did with 10% of the complexity:
| Feature | OpenClaw | Goldfish |
|---|---|---|
| Conversations | ACP bridge (fragile) | Claude Code CLI (solid) |
| Session continuity | ACP session management | --resume flag |
| Memory | Built-in indexer + embeddings | FTS5 + semantic vectors + scheduled synthesis |
| Channels | Slack, Telegram, Signal | Slack |
| Cost | API Cost | Max Plan |
Goldfish is compatible with OpenClaw agent workspaces. It can use the same identity files (SOUL.md, IDENTITY.md, USER.md, FOCUS.md, etc.), memory directory structure, and tools. The key difference is the entry point: OpenClaw reads AGENTS.md as its primary instruction file, while Goldfish uses Claude Code's native CLAUDE.md. An OpenClaw workspace needs a CLAUDE.md that mirrors its AGENTS.md bootstrap sequence to work with Goldfish.
Slack message → Goldfish daemon → spawns claude CLI → reads agent config → responds
→ saves transcript to JSONL
<workspace>/schedule.yaml → schedule run (every minute)
→ morning / heartbeat / exploration / weekly → Claude → Slack
→ daily-synthesis (1 AM) → Claude summarizes → memory/YYYY-MM-DD.md
→ index-memory (1:15 AM) → FTS5 + vectors → memory/search.sqlite
Two launchd agents. One config file. That's the whole thing. See docs/deployment-macos.md for the full setup.
Prerequisites: Node.js 22+, Claude Code CLI, a Slack app with Socket Mode enabled.
This project uses pnpm. If you don't have it, enable it via Node's built-in Corepack:
corepack enableThen:
# Install dependencies
pnpm install
# Copy .env and fill in Slack tokens
cp .env.example .env
# Create an agent workspace (interactive — sets up identity, memory, prompts, schedule)
pnpm cli init
# Test connection
pnpm cli auth test
# Start the bot
pnpm cli startEnvironment variables (in .env):
| Variable | Description |
|---|---|
SLACK_APP_TOKEN |
Slack Socket Mode app-level token (Required) |
SLACK_BOT_TOKEN |
Slack bot OAuth token (Required) |
GOLDFISH_WORKSPACE |
Path to agent workspace (default: ~/goldfish-workspace) |
GOLDFISH_CHANNELS |
Comma-separated Slack channel IDs to listen on (in addition to DMs) |
GOLDFISH_DM_CHANNEL_ID |
Default DM channel for proactive outreach |
GOLDFISH_MAX_TURNS |
Max Claude Code turns per message (default: 50) |
GOLDFISH_TIMEOUT_MS |
Claude Code timeout in ms (default: 300000) |
GOLDFISH_SESSION_EXPIRY_MS |
Session expiry in ms (default: 86400000 / 24h) |
GOLDFISH_SHOW_THINKING |
Show "Thinking..." indicator (default: true) |
GOLDFISH_EFFORT |
Default Claude thinking effort for all channels (see below) |
GOLDFISH_EFFORT_BY_CHANNEL |
Per-channel effort overrides as a JSON map (see below) |
Claude Code accepts a --effort level per session: low, medium, high, xhigh, or max.
Lower effort means faster, shallower replies; higher effort means slower, more deliberate
reasoning. Goldfish lets you pick a level per Slack channel — keep a casual channel snappy
on low while a work channel stays sharp on high.
GOLDFISH_EFFORTsets a default applied to every channel.GOLDFISH_EFFORT_BY_CHANNELis a JSON object mapping channel ID → level; it overrides the default.- A channel with no override (and no default) uses Claude Code's own default effort.
- An unrecognized level or malformed JSON is ignored, falling back to the CLI default — it never errors.
# Default everything to medium, but run one casual channel fast:
GOLDFISH_EFFORT=medium
GOLDFISH_EFFORT_BY_CHANNEL='{"C0A7VB1U6EA":"low","C0WORKCHAN1":"high"}'
⚠️ Quote the JSON with single quotes. The launchd wrapper (launchd/goldfish-env.sh) loads.envbysource-ing it in bash, which strips double quotes out of the value — turning{"C123":"low"}into invalid{C123:low}that silently parses to an empty map. Wrapping the whole value in single quotes preserves the inner double quotes through both bash sourcing and dotenv. (This bites any JSON-valued variable in.env, not just this one.)
After editing .env, restart the daemon so the new environment is picked up:
launchctl kickstart -k gui/$(id -u)/com.jdlien.goldfish.daemon (run npm run build first
if you also changed source — the daemon runs the compiled dist/, not src/).
Goldfish is agent-agnostic. The agent's identity comes from the workspace it points at, not from Goldfish itself. Claude Code reads CLAUDE.md in the workspace root, which bootstraps whatever identity files, tools, and context you configure.
This means you can use Goldfish as:
- A personal assistant with memory, opinions, and persistent personality
- A project-specific agent with domain context
- A team bot with shared knowledge
- A drop-in replacement for an OpenClaw agent (same workspace, simpler runtime)
- Anything else you can define in a
CLAUDE.md
The workspace pattern (identity as markdown files, memory as a searchable archive, personality that evolves through conversation) is the core of what makes a persistent agent feel persistent. See docs/agent-identity.md for the full design philosophy, workspace anatomy, and migration guide from OpenClaw.
goldfish start # Start the bot daemon
goldfish auth status # Check token configuration
goldfish auth test # Test Slack API connection
goldfish send -m "Hello" -c <channel> # Send a message manually
goldfish upload -f report.pdf -c <ch> # Upload a file
goldfish initiate -t morning # Trigger a morning briefing
goldfish initiate -t weekly # Trigger a weekly review
goldfish initiate -t heartbeat # Silent urgency check (pings only if actionable)
goldfish initiate -t exploration # Agent picks a topic and goes deep
goldfish initiate --reminder "Call back about the service agreement"
goldfish schedule list # Show all scheduled tasks
goldfish schedule run # Run any tasks due now
goldfish schedule run --dry-run # Preview what would runGoldfish uses a single schedule.yaml in your workspace (default: ~/goldfish-workspace/schedule.yaml) to define scheduled tasks: briefings, heartbeats, maintenance, whatever you need. A launchd agent fires every 60 seconds and runs any due tasks:
tasks:
- type: morning
at: "8:30am"
channel: C0ABC123DEF
- type: heartbeat
every: hour
between: "10am-5pm"
days: weekdays
channel: C0ABC123DEF
- type: daily-synthesis
at: "1:00am"
- type: index-memory
at: "1:15am"See docs/scheduling.md for the full reference: all fields, task types, timing syntax, locking, and configuration options.
Goldfish maintains memory through three layers:
- In-session: The agent writes to memory files during conversations (daily notes, project files, etc)
- Post-session transcripts: Every message exchange is appended to
memory/sessions/YYYY-MM-DD.jsonl - Daily synthesis: A scheduled task consolidates the day's transcripts into a narrative daily log
Search memory from within a Claude session — keyword, semantic, or both (fused):
goldfish search "what you're looking for" # hybrid: keyword ∪ semantic (default)
goldfish search "exact tokens" --mode fts # keyword only, no model load
goldfish search "a conceptual question" --json # stable JSON for tools
# Semantic search needs a one-time model download:
goldfish embeddings setupThe index is a plain SQLite DB, so raw FTS5 still works as a fallback:
sqlite3 memory/search.sqlite \
"SELECT path, snippet(chunks_fts, 0, '>>>', '<<<', '...', 40) \
FROM chunks_fts WHERE chunks_fts MATCH 'search terms' \
ORDER BY rank LIMIT 10;"Goldfish are commonly thought to have 3-second memories. It turns out that's a myth — they can remember for months. But an LLM-based AI-agent is kind of like that. Almost no short-term memory, but we create a long-term memory by writing everything down obsessively.
MIT
