A minimal, readable Claw-like agent system in Go, built for learning core architecture.
This is a learning implementation, not a production platform.
中文版本:README.zh.md
- Learner-first Claw architecture: understand the full loop without complex service splitting.
- Small enough to read end-to-end: currently 3000 Go LOC (excluding tests).
- Pure Go, zero third-party package dependencies in this repo (
go.modhas no external modules). - Local-first and middleware-free: no database, no message broker, no cache layer required.
- One core path reused everywhere: CLI, heartbeat, and cron all trigger the same
AgentLoop. - Transparent state for learning: sessions, memory, state, and cron jobs are plain files in
workspace/. - Safe-by-default tooling: workspace sandbox boundaries + basic dangerous command blocking.
flowchart LR
U["User / CLI"] --> BI["Bus Inbound"]
BI --> AL["AgentLoop"]
AL --> CB["ContextBuilder"]
AL --> SS["SessionStore"]
AL --> P1["Provider.Chat"]
P1 -->|tool_calls| TR["ToolRegistry"]
TR --> TOOLS["Tools: read_file/write_file/list_dir/exec/remember/read_skill/cron"]
TOOLS --> P2["Provider.Chat (continue)"]
P1 -->|no tool_calls| BO["Bus Outbound"]
P2 --> BO
BO --> O["Assistant Output"]
HB["Heartbeat Service"] --> AL
CR["Cron Service"] --> AL
SS --> FS1[("workspace/sessions/*.json")]
AL --> FS2[("workspace/state/state.json")]
CB --> FS3[("workspace/memory + bootstrap + skills metadata")]
CR --> FS4[("workspace/cron/jobs.json")]
- Beginners learning how Claw-style agents work under the hood.
- Go developers who want a runnable reference without framework-heavy abstractions.
- Builders who need a local playground to test tool-calling loops quickly.
- Go
1.25.6(seego.mod) - An OpenAI-compatible chat completion endpoint
- A valid API key
git clone <your-repo-url> minimal-claw
cd minimal-clawcp .env.example .envEdit .env and set:
MINIMAL_CLAW_API_KEY(required)- Optional model/base URL overrides
Notes:
- This minimal implementation currently ships with only an OpenAI-compatible provider (
internal/provider/openai.go). - If you need another provider, implement
provider.LLMProviderininternal/provider/interface.goand wire it incmd/minimal-claw/main.go. - For beginners, a good low-cost learning setup is Zhipu AI's free
glm-4.7-flashmodel via their OpenAI-compatible chat API docs.
go build -o ./bin/minimal-claw ./cmd/minimal-clawRun:
set -a; source .env; set +a
./bin/minimal-claw -s demoset -a; source .env; set +a
go run ./cmd/minimal-claw -s demo -m "Say hello in one sentence."set -a; source .env; set +a
go run ./cmd/minimal-claw -s demoThen type messages interactively. Exit with /exit, exit, or quit.
When running in REPL, outbound messages from cron/heartbeat are also printed as push updates.
make rungo build ./...
go test ./...MINIMAL_CLAW_API_KEY=your_key \
MINIMAL_CLAW_MODEL=gpt-4o-mini \
MINIMAL_CLAW_API_BASE=https://api.openai.com/v1 \
go run ./cmd/minimal-claw -s devfind . -name '*.go' -not -name '*_test.go' -not -path './vendor/*' -print0 | xargs -0 wc -l | tail -n 1- Beginner-friendly architecture: no hidden orchestration, no distributed runtime, no complex framework layers.
- Core agent loop:
LLM -> tool calls -> tool execution -> LLM(iterative until done or max iterations) - Session persistence: JSON history in
workspace/sessions/<session>.json - Skill system for guided behavior:
- auto-discover skill metadata from
workspace/skills/<name>/SKILL.md - load full skill docs on demand via
read_skill(supports referenced files)
- auto-discover skill metadata from
- Context builder:
- system identity
- optional bootstrap files from workspace:
AGENTS.md,SOUL.md,USER.md,IDENTITY.md - optional skills metadata injection
- optional long-term memory injection (
workspace/memory/MEMORY.md)
- Tool registry with built-in tools:
read_filewrite_filelist_direxecrememberread_skillcron
- Sandbox boundaries for file/exec tools (workspace-root constrained, symlink-escape guard)
- Basic command blocking in
exec(for examplesudo,shutdown,rm -rf) - Decoupled runtime bus (
inbound/outbound) - State persistence in
workspace/state/state.jsonfor last target routing - Heartbeat service (optional, timer-based, routes through the same
AgentLoop) - Cron service (persistent scheduled jobs in
workspace/cron/jobs.json, also routes throughAgentLoop) - Pure local runtime for learning: no external middleware setup needed
Real sample run (executed on 2026-03-05):
Command:
set -a; source .env; set +a
go run ./cmd/minimal-claw -s readme-demo-en -m "Create demo-note.txt in workspace with content: hello minimal claw. Then reply with done."
go run ./cmd/minimal-claw -s readme-demo-en -m "Read demo-note.txt and return the exact content."Output:
done
The content of demo-note.txt is 'hello minimal claw'.
Command:
set -a; source .env; set +a
go run ./cmd/minimal-claw -s readme-demo-en -m "Use exec to run pwd and show output."
go run ./cmd/minimal-claw -s readme-demo-en -m "Use exec to run rm -rf /tmp/test and tell me the result."Output:
The output of the 'pwd' command is '/Users/congregalis/Code/minimal-claw/workspace'.
The command 'rm -rf /tmp/test' is blocked due to security reasons.
cat workspace/sessions/readme-demo-en.jsonCommand:
set -a; source .env; set +a
go run ./cmd/minimal-claw -s skill-browser-demo -m "Use read_skill to load the 'agent-browser' skill, then use exec to run browser automation: open https://example.com, wait for network idle, save screenshot to browser-example-by-agent.png in workspace root, close browser, and finally report the screenshot file path and size."
go run ./cmd/minimal-claw -s skill-browser-demo -m "Try again. Use read_skill if needed, but run the browser commands via npx: npx --yes agent-browser open https://example.com && npx --yes agent-browser wait --load networkidle && npx --yes agent-browser screenshot browser-example-by-agent.png && npx --yes agent-browser close && ls -l browser-example-by-agent.png"Output:
The 'agent-browser' command is not found in the system. Please ensure that the agent-browser tool is installed and properly added to your system's PATH.
The screenshot has been successfully saved as 'browser-example-by-agent.png' in the workspace root. The file size is 16,578 bytes.
Inspect tool-call history:
cat workspace/sessions/skill-browser-demo.jsonThis demo shows the "always-on" path: keep one process running, let cron trigger every few seconds, and let heartbeat trigger every 5 minutes.
- Prepare heartbeat prompt:
cat > workspace/HEARTBEAT.md <<'EOF'
Check recent session activity and cron outcomes.
If a job failed or anything needs intervention, explain what to do next.
EOF- Start the long-running bot (leave this process alive):
set -a; source .env; set +a
MINIMAL_CLAW_HEARTBEAT_ENABLED=true \
MINIMAL_CLAW_HEARTBEAT_INTERVAL=5m \
go run ./cmd/minimal-claw -s always-on-demo- In that REPL session, add a recurring cron job:
Use the cron tool to add a recurring job every 30 seconds named "ops-check" with message:
"Use exec to run 'df -h'. If usage is normal, respond briefly. If not, tell me what is wrong."
Then list cron jobs.
- In another terminal, verify that it behaves like a continuous bot:
cat workspace/cron/jobs.json
cat workspace/state/state.json
tail -n 80 workspace/sessions/always-on-demo.jsonExpected signals:
workspace/cron/jobs.jsonhasnext_run/last_runupdates.workspace/state/state.jsonkeepslast_targetfor heartbeat routing.workspace/sessions/always-on-demo.jsonaccumulates messages from scheduled runs over time.
Environment variables:
| Variable | Required | Default | Notes |
|---|---|---|---|
MINIMAL_CLAW_WORKSPACE_PATH |
No | workspace |
Data root for sessions/state/memory/cron/skills |
MINIMAL_CLAW_MODEL |
No | gpt-4o-mini |
Chat model name |
MINIMAL_CLAW_API_BASE |
No | https://api.openai.com/v1 |
OpenAI-compatible base URL |
MINIMAL_CLAW_API_KEY |
Yes | (empty) | Required at startup |
MINIMAL_CLAW_MAX_ITERATIONS |
No | 8 |
Tool-call loop upper bound |
MINIMAL_CLAW_MAX_TOKENS |
No | 2048 |
Provider request parameter |
MINIMAL_CLAW_TEMPERATURE |
No | 0.2 |
Provider request parameter |
MINIMAL_CLAW_HEARTBEAT_ENABLED |
No | false |
Enable heartbeat service |
MINIMAL_CLAW_HEARTBEAT_INTERVAL |
No | 5m |
Must be >= 5m when heartbeat is enabled |
System-managed and optional files under the workspace path:
workspace/
├── AGENTS.md # optional bootstrap prompt file
├── SOUL.md # optional bootstrap prompt file
├── USER.md # optional bootstrap prompt file
├── IDENTITY.md # optional bootstrap prompt file
├── HEARTBEAT.md # optional heartbeat prompt file
├── sessions/
│ ├── default.json # default session history
│ └── <session-id>.json # per-session history
├── state/
│ └── state.json # last_target and timestamp
├── memory/
│ ├── MEMORY.md # long-term memory
│ └── YYYYMM/
│ └── YYYYMMDD.md # daily memory log
├── cron/
│ └── jobs.json # scheduled jobs
├── skills/
│ └── <skill-name>/
│ ├── SKILL.md # skill body with frontmatter
│ └── <optional-reference-file> # file read via read_skill path
└── <user-files> # files created/read by tools