cclaw is a compact agent harness for software engineering tasks.
Early MVP. The harness supports:
- one-shot CLI runs
- interactive chat sessions
- Feishu bot mode over Feishu WebSocket events
- project instruction loading from
AGENTS.md/Agents.md - project skill discovery using Agent Skills-style
SKILL.mdfiles - persistent interactive and Feishu conversation sessions in
~/.cclaw/sessions - structured per-run JSONL traces in
~/.cclaw/runs - automatic session compaction that summarizes older turns while preserving the full transcript
- fake provider for local smoke tests
- OpenAI-compatible, Azure OpenAI, and Codex subscription providers
- built-in
read_file,write_file,edit_file,bash,web_search,memory,todo, andsubagenttools - workspace-scoped file-backed persistent memory and todo state
- workspace path safety for file and shell tools
- context-aware tool error recovery hints for validation, policy, not-found, conflict, timeout, and system failures
Install the latest GitHub Release binary:
curl -fsSL https://raw.githubusercontent.com/chenminhua/cclaw/main/scripts/install.sh | sh
cclaw --helpSafer inspect-first install:
curl -fsSL https://raw.githubusercontent.com/chenminhua/cclaw/main/scripts/install.sh -o install.sh
sh install.shPin a version or explicit install directory when needed:
CCLAW_VERSION=v0.1.0 sh install.sh
CCLAW_INSTALL_DIR="$HOME/bin" sh install.shUpgrade by rerunning the installer. Uninstall by deleting the installed binary, usually /usr/local/bin/cclaw or ~/.local/bin/cclaw.
Show help from source:
go run ./cmd/cclaw --helpOne-shot mode (defaults to workspace ~/.cclaw when --workspace is omitted):
go run ./cmd/cclaw --workspace . "inspect this project"Interactive chat mode:
go run ./cmd/cclaw --workspace .In interactive mode, type /exit or /quit to quit. Conversation history is restored per workspace from ~/.cclaw/sessions when you restart cclaw.
Feishu bot mode:
go run ./cmd/cclaw feishu --workspace .Feishu mode connects to Feishu's WebSocket event stream, receives text messages, runs the same agent loop, and sends progress/results back to the originating chat. Conversation history is restored per Feishu chat from ~/.cclaw/sessions after process restarts.
cclaw persists conversational state in a user-local session store:
~/.cclaw/sessions/
index.json
<session-id>.jsonl
index.json maps stable session keys, such as a workspace interactive chat or a Feishu chat, to transcript files. Transcript files are JSONL so they are inspectable and recoverable without a database. One-shot CLI prompts keep their existing stateless behavior and do not automatically resume the interactive workspace session.
When a session grows large, cclaw keeps the complete JSONL transcript on disk but sends a compacted context view to the model: original system prompt, a deterministic summary of older turns, and the recent message tail. Compaction entries are persisted as type: "compaction" records and tracked in index.json with compactionCount and contextTokens. If a provider reports a context-overflow error, cclaw forces a compaction checkpoint and retries the model request once through the normal turn loop.
cclaw also persists structured runtime events for observability and debugging:
~/.cclaw/runs/
<run-id>.jsonl
Each JSONL record includes the event kind, run ID, timestamp, turn number, tool name, tool call ID, duration, and error details when available. Traces are emitted through the same surface event boundary used by terminal, chat, and Feishu output, so the agent runner stays independent from storage and presentation details.
cclaw stores durable agent state as inspectable workspace files:
.cclaw/
memory/
USER.md
MEMORY.md
daily/YYYY-MM-DD.md
todos/
todos.json
history.jsonl
The memory tool supports read, add, replace, and remove against user, memory, or today's daily log. Entries are separated with §, written atomically, and basic prompt-injection/exfiltration patterns are rejected because memory is injected into future prompts.
The todo tool supports list, add, update, complete, cancel, remove, and replace. Active items (pending, in_progress, blocked) are loaded into the startup prompt; completed and cancelled items remain on disk but are not injected as active context.
At runner startup, cclaw loads a frozen snapshot of USER.md, MEMORY.md, today/yesterday daily notes, and active todos into the system prompt. Mid-session writes persist immediately and are visible via the tools; they become startup context for new runner sessions.
The subagent tool delegates a complex subtask to a fresh, isolated agent session. The child agent receives the normal system prompt plus a subagent guardrail and workspace-scoped built-in tools, but it does not inherit the parent conversation history and it does not receive the subagent tool itself. This keeps broad inspections, research, and multi-step local analysis out of the main context while preserving the same workspace and tool safety boundaries.
Subagent runs are sequential, capped at a smaller turn budget, and return only the final child answer or a concise failure summary to the parent conversation. High-risk child shell commands still use the active run approver when one is available, such as Feishu text approval.
Tool failures are returned to the model with structured recovery context when the error is actionable. A tool result may include an error kind (validation, policy, not_found, conflict, timeout, or system) plus concise recovery hints. The runner formats those fields into the next tool-result message, for example:
Error: could not find old_text in notes.txt; it must match exactly including whitespace and newlines
Error kind: conflict
Recovery hints:
- Re-read the file before retrying because it may have changed.
- Use exact old_text including whitespace and newlines.
- Use enough surrounding context to make each replacement unique, or merge overlapping edits.
This keeps recovery knowledge close to the component that understands the failure: built-in file tools explain path, missing-file, directory, truncation, and edit-conflict problems; bash explains policy rejections, timeouts, and non-zero exits; the tool registry explains unknown tool names. The agent runner only preserves and formats this context, so provider-neutral orchestration stays separated from tool-specific behavior.
Long-running ReAct agents can get stuck repeating the same tool call after an error, especially when the model keeps assuming that one more retry will work. cclaw keeps MaxTurns as the hard safety stop, but the runner also uses soft system reminders before that stop so the model has a chance to recover and finish cleanly.
The reminder logic is deterministic and provider-neutral. During a run, internal/agent watches tool results and turn usage. If the same single tool call fails three times in a row with the same arguments and error, the runner appends a host-generated system message telling the model to stop blindly retrying, re-read state if needed, choose a different approach, or explain the blocker. If a run reaches roughly three quarters of its turn budget, the runner appends a separate reminder to prioritize finishing. At most two reminders are injected per run; if the model still does not converge, the normal run exceeded max turns failure remains the final boundary.
System reminders are intentionally implemented in the runner rather than in providers or tools: they are orchestration policy, not vendor protocol or tool behavior. They are appended to the conversation transcript as system messages so persisted sessions show exactly when the host intervened.
By default, cclaw uses workspace ~/.cclaw and the fake provider. This is useful for verifying that the CLI, workspace, tools, runner, and surface are wired correctly without network access or API keys.
go run ./cmd/cclaw --workspace . "hello"Configure providers in ~/.cclaw/config.toml:
[provider]
name = "azure-openai"
base_url = "https://xxx.openai.azure.com"
api_key = "..."
model = "your-deployment-name"
api_version = "2025-01-01-preview"Then run:
go run ./cmd/cclaw --workspace .For a non-Azure OpenAI-compatible endpoint, use:
[provider]
name = "openai"
base_url = "https://api.openai.com/v1"
api_key = "..."
model = "gpt-4.1"Flags can override user config:
go run ./cmd/cclaw \
--workspace . \
--provider azure-openai \
--base-url "https://xxx.openai.azure.com" \
--api-key "$AZURE_OPENAI_API_KEY" \
--model "your-deployment-name" \
--api-version "2025-01-01-preview"You can reuse a local OpenAI Codex CLI ChatGPT subscription login instead of an OpenAI Platform API key. First sign in with the official Codex CLI:
codex loginThen configure and run cclaw with the Codex subscription provider:
[provider]
name = "codex-subscription"
model = "gpt-5.4"go run ./cmd/cclaw --workspace .By default cclaw reads $CODEX_HOME/auth.json or ~/.codex/auth.json. Override that path with codex_auth_file in ~/.cclaw/config.toml or --codex-auth-file:
go run ./cmd/cclaw \
--workspace . \
--provider codex-subscription \
--codex-auth-file "$HOME/.codex/auth.json" \
--model "gpt-5.4"The Codex auth file contains bearer tokens. Do not commit it, paste it into logs, or share it.
Do not commit API keys, ~/.cclaw/config.toml, or Codex auth files.
cclaw discovers local skills from the skills/ directory under the workspace. A skill is a directory containing SKILL.md with frontmatter:
---
name: code-review
description: Reviews code changes. Use when the user asks for review before merging.
---
# Code Review
Follow these review steps...At startup, cclaw adds only the skill name, description, and workspace-relative location to the system prompt. When a task matches, the model can use read_file to load the full SKILL.md.
Disable workspace skill discovery:
go run ./cmd/cclaw --workspace . --no-skills "hello"Explicit skill paths are not supported; put skills under <workspace>/skills/.
Skills can instruct the model to run commands or edit files. Review third-party skill content before adding it to a workspace.
cclaw registers a web_search tool by default. It searches through Exa:
- If
web_search.exa_api_keyis set in~/.cclaw/config.toml, it uses the Exa Search API. - If no key is set, it falls back to Exa's public MCP endpoint.
Optional API-key configuration:
[web_search]
exa_api_key = "exa-..."The tool accepts query, optional num_results (default 5, max 20), and optional include_content.
Configure a Feishu app with WebSocket event subscriptions enabled for message receive events, then add credentials to ~/.cclaw/config.toml:
[feishu]
app_id = "cli_xxx"
app_secret = "..."Start the bot with your model provider configured:
go run ./cmd/cclaw feishu --workspace .You may pass credentials as flags for local testing, but avoid putting secrets in shell history:
go run ./cmd/cclaw feishu \
--workspace . \
--feishu-app-id "$FEISHU_APP_ID" \
--feishu-app-secret "$FEISHU_APP_SECRET"Safety note: Feishu messages can trigger read_file, write_file, edit_file, bash, web_search, memory, todo, and subagent. File, memory, todo, shell, and subagent child file/shell tools operate inside the selected workspace; web_search can make external network requests. Obviously dangerous shell commands such as sudo, filesystem-root deletes, disk formatting, shutdown, reboot, and fork-bomb patterns are rejected. High-risk workspace commands such as rm -rf build, git reset --hard, git clean -fd, find ... -delete, and recursive permission/ownership changes require Feishu text approval: reply with approve TOKEN to continue or reject TOKEN to deny before the approval timeout.
Start an interactive session with a real provider:
go run ./cmd/cclaw --workspace .Try prompts like:
Use bash to run: go test ./...
Use write_file to create tmp/hello.txt with exactly:
hello old world
Then use edit_file to replace "old" with "new" in tmp/hello.txt.
Then use read_file to show the final content.
Expected tool flow:
tool> bash started
tool> bash completed
or:
tool> write_file started
tool> write_file completed
tool> edit_file started
tool> edit_file completed
tool> read_file started
tool> read_file completed
For web search:
Search the web for recent Go agent harness examples.
All built-in tools operate inside the configured workspace:
go run ./cmd/cclaw --workspace /path/to/projectFile tools resolve paths through the workspace boundary and reject traversal outside it. The bash tool also resolves its working directory through the same boundary, applies a timeout, limits captured output, rejects obviously dangerous commands, and can require user approval for high-risk destructive workspace commands. The subagent tool creates an isolated child session but reuses these same workspace-scoped tools and policies.
Tool calls from one assistant response run concurrently by default. A tool can opt into sequential execution with tool.ExecutionModeSequential, which makes the whole batch run one call at a time. Built-in write_file and edit_file keep the default parallel mode, but serialize mutations for the same resolved file path so different files can still be updated concurrently without racing on one file. The subagent tool runs sequentially to avoid overlapping nested model calls and to keep delegated work bounded.
go test ./...If Go reports a toolchain/cache version mismatch, clear the local Go test cache:
go clean -cache -testcache
go test ./...