calebwin/pgclaw: A “Clawdbot” in every row with 400 lines of Postgres SQL

✨ Read this must-read post from Hacker News 📖

📂 **Category**:

📌 **What You’ll Learn**:

“What if I had an AI assistant in every row of my Postgres table?”

I asked myself this a few weeks back and pgclaw is what I came up with: an open-source Postgres extension that introduces a claw data type to instantiate an AI agent – either a simple LLM or an “OpenClaw” agent – as a Postgres column.

With pgclaw, store your AI agents with the rest of your data. Supports:

  • Simple LLM agents and more stateful “OpenClaw” agents
  • Any LLM provider via rig: Anthropic, OpenAI, Ollama, Gemini, Groq, Together, DeepSeek, Mistral, Perplexity, Cohere, xAI, OpenRouter, and more
  • A “Claude Code” in each row via claude-agent-sdk: agents that can read/write files, run code, and use tools
  • ACID compliance, point-in-time recovery, JOINs, and all of the other great features of Postgres
  • any language with a Postgres client

Prerequisites: Rust toolchain and PostgreSQL 17 dev headers.

cargo install cargo-pgrx
cargo pgrx init --pg17 download
git clone https://github.com/calebwin/pgclaw.git
cd pgclaw
cargo pgrx install

Then in postgresql.conf:

shared_preload_libraries="pgclaw"
pgclaw.api_key = 'sk-ant-...'
pgclaw.database="mydb"
CREATE EXTENSION pgclaw;

CREATE TABLE tickets (
    id       serial PRIMARY KEY,
    title    text,
    body     text,
    priority text,
    agent    claw DEFAULT claw('Set priority to low/medium/high/critical based on urgency.')
);
SELECT claw_watch('tickets');

INSERT INTO tickets (title, body) VALUES ('Login broken', '500 error on login page');
-- ~1-2s later: priority = 'critical'

A claw value binds an LLM agent to a row. Two modes:

Inline — just a prompt:

claw('Summarize this ticket in one sentence.')
claw('Classify sentiment.', model => 'claude-sonnet')

Agent reference — points to a reusable agent definition in claw.agents:

claw(agent => 'ticket-triage')
claw(agent => 'ticket-triage', model => 'claude-opus')

Define reusable agents with identity, instructions, and memory:

INSERT INTO claw.agents (id, soul, instructions, memory) VALUES (
    'ticket-triage',
    'You are a meticulous support ticket classifier.',
    'Read the ticket. Set priority (low/medium/high/critical) and summary.
     If unsure, set medium and explain in summary.',
    '💬'
);

CREATE TABLE tickets (
    id       serial PRIMARY KEY,
    title    text,
    body     text,
    priority text,
    summary  text,
    agent    claw DEFAULT claw(agent => 'ticket-triage')
);
SELECT claw_watch('tickets');

The agent can update its own memory by including 🔥 in its response.

Agents with Workspaces (Claude Code)

Agents with a workspace field get their own filesystem directory and run via Claude Code:

INSERT INTO claw.agents (id, soul, instructions, workspace) VALUES (
    'engineer',
    'You are a senior engineer. You write clean, tested code.',
    'Implement the change. Run tests. Update status to pass/fail.',
    'auto'
);

CREATE TABLE tasks (
    id serial PRIMARY KEY, task text, status text DEFAULT 'pending', diff text,
    agent claw DEFAULT claw(agent => 'engineer')
);
SELECT claw_watch('tasks');

Requires Claude Code CLI (npm install -g @anthropic-ai/claude-code). Workspace paths: 'auto' uses {pgclaw.workspace_dir}/{agent}/{table}/{pk}/, or use a custom template like '/data/{agent}/{table}/{pk}'.

Channels, Sessions, Heartbeats

INSERT INTO claw.agents (id, soul, instructions) VALUES (
    'support-bot', 'You are the support bot for Acme Inc.', 'Help users with orders.'
);
INSERT INTO claw.bindings (channel, agent_id) VALUES ('telegram', 'support-bot');
INSERT INTO claw.heartbeats (agent_id, every) VALUES ('support-bot', '30 minutes');

SELECT claw.route('telegram', '*', 'user123', NULL, 'My order is late');
-- Pick up responses from claw.outbox
# Required
pgclaw.api_key = 'sk-ant-...'                      # API key for your provider
pgclaw.database="mydb"                            # Database for the background worker

# Optional
pgclaw.api_provider="anthropic"                   # see supported providers below
pgclaw.api_url=""                                 # override API endpoint
pgclaw.default_model="claude-sonnet-4-5-20250929" # default model for inline claw values
pgclaw.enabled = true                               # enable/disable processing
pgclaw.workspace_dir="/tmp/pgclaw/workspaces"     # base dir for Claude Code workspaces

Supported providers: anthropic (default), openai, ollama, gemini, groq, together, deepseek, perplexity, cohere, mistral, moonshot, openrouter, xai, hyperbolic, mira, galadriel

# OpenAI
pgclaw.api_provider="openai"
pgclaw.api_key = 'sk-...'
pgclaw.default_model="gpt-4o"

# Ollama (local, no API key needed)
pgclaw.api_provider="ollama"
pgclaw.default_model="llama3.1"

# OpenAI-compatible (vLLM, LM Studio, etc.)
pgclaw.api_provider="openai"
pgclaw.api_url="http://localhost:8000/v1"
  1. You add a claw column to a table and call claw_watch()
  2. On INSERT/UPDATE, a Postgres trigger enqueues the row to claw.queue
  3. A Postgres background worker polls the queue:
    • LLM agents (no workspace): builds prompt from agent config + row data, calls LLM via rig
    • Claude Code agents (with workspace): creates a workspace directory with SOUL.md, AGENTS.md, context.json, spawns Claude Code via claude-agent-sdk
  4. The response is parsed for column updates (JSON {"column": "value"}) and applied back to the row
  5. Conversation history is preserved in claw.history for multi-turn interactions

MIT of course!

{💬|⚡|🔥} **What’s your take?**
Share your thoughts in the comments below!

#️⃣ **#calebwinpgclaw #Clawdbot #row #lines #Postgres #SQL**

🕒 **Posted on**: 1770923718

🌟 **Want more?** Click here for more info! 🌟

By

Leave a Reply

Your email address will not be published. Required fields are marked *