This guide covers development workflows, planning tooling, and contributor
conventions. For end-user setup and usage, see README.md or
visit netclaw.dev.
dotnet build Netclaw.slnx
dotnet test Netclaw.slnx
dotnet slopwatch analyzeThese files define how planning and implementation work should be routed:
AGENTS.md— agent personas and routing rulesPROJECT_CONTEXT.md— current product direction and constraintsTOOLING.md— tool and infrastructure contextCLAUDE.md— agent constitution and quality bar
docs/prd/— product requirements and acceptance criteriadocs/spec/— engineering specifications and contractsdocs/ui/— management UI mockupsopenspec/specs/— capability specs for ongoing evolutionopenspec/changes/— change proposals, design notes, and execution tasks
OpenSpec is initialized for OpenCode in this repository.
Common commands:
/opsx:new— create a new change/opsx:continue— create next artifact in the change workflow/opsx:ff— fast-forward: generate all remaining artifacts at once/opsx:apply— implement tasks from a change/opsx:verify— verify implementation matches change artifacts/opsx:archive— archive a completed change
CLI equivalents are available via openspec --help.
Netclaw-specific helper skills:
.opencode/skills/netclaw-openspec-planning/SKILL.md.opencode/skills/netclaw-openspec-milestones/SKILL.md
RALPH infrastructure is available in this repo and tuned for OpenSpec-traceable execution.
ralph-opencode.sh— OpenCode loop runnerralph.sh— Claude Code loop runner.claude/skills/ralph-loop.md— loop discipline with OpenSpec gates.claude/skills/ralph-run-diagnostics.md— process diagnostics.claude/skills/ralph-output-adversarial-review.md— adversarial reviewIMPLEMENTATION_PLAN.md— RALPH task queueBACKLOG_PARKING_LOT.md— parked items requiring human decisions
Developer-only integration harness for daemon lifecycle, gateway checks,
and interactive TUI flows. It runs the real netclaw / netclawd
binaries natively — no Docker for the binary — against a native Ollama
process. It is script-driven (not a user-facing netclaw test smoke
command).
# Light suite: all PR-gating flow tapes + non-interactive scenarios.
# Publishes the binary, installs vhs, starts Ollama, and pulls the
# smoke models automatically.
scripts/smoke/run-smoke.sh light
# Single tape or scenario by short name (fastest inner loop).
scripts/smoke/run-smoke.sh init-wizard
# Screenshot regression: capture + byte-compare against the baselines.
scripts/smoke/run-smoke.sh screenshotsOptional model override:
SMOKE_OLLAMA_MODEL=qwen2:0.5b scripts/smoke/run-smoke.sh lightTo iterate against a binary you already built, export its path so the harness skips the publish step:
NETCLAW_SMOKE_CLI=./publish/cli/netclaw \
NETCLAW_SMOKE_DAEMON=./publish/daemon/netclawd \
scripts/smoke/run-smoke.sh lightsmoke.yml runs the harness on every pull request to dev, on dev
pushes, and nightly. It has three legs, all running in parallel:
Native Smoke (Linux)— flow tapes + non-interactive scenarios.Screenshot Regression (Linux)— TUI screenshot byte-comparison.Native Smoke (macOS)— the Apple Silicon leg (flow tapes + scenarios; screenshots stay Linux-only).
Each leg uploads a smoke-*logs-* artifact (run log, daemon logs,
failure GIFs, screenshot diffs) for debugging.
- Solution:
Netclaw.slnx - Daemon:
src/Netclaw.Daemon/(Web API host,netclawd) - CLI:
src/Netclaw.Cli/(thin client,netclaw) - Actors:
src/Netclaw.Actors/(session management, persistence, tools) - Configuration:
src/Netclaw.Configuration/(paths, providers, models) - Channels:
src/Netclaw.Channels/(channel abstractions) - Slack:
src/Netclaw.Channels.Slack/(Slack Socket Mode gateway) - Discord:
src/Netclaw.Channels.Discord/(Discord gateway) - Mattermost:
src/Netclaw.Channels.Mattermost/(Mattermost gateway) - Providers:
src/Netclaw.Providers/(LLM provider implementations) - OpenAI Compatible:
src/Netclaw.OpenAICompatible/(OpenAI-compatible API layer) - Search:
src/Netclaw.Search/(web search backends) - Security:
src/Netclaw.Security/(ACL, device pairing, token management) - Tools:
src/Netclaw.Tools.Abstractions/andsrc/Netclaw.Tools.Generators/
For a step-by-step guide to adding a new chat channel integration (e.g., Microsoft Teams, WhatsApp, Signal), see Adding a Channel.
Netclaw uses a daemon + thin client architecture:
-
netclawd(src/Netclaw.Daemon/) — always-on daemon process hosting the Akka actor system, LLM sessions, tool execution, and persistence. Exposes a SignalR hub at/hub/sessionplus authenticated management endpoints for remote clients. -
netclaw(src/Netclaw.Cli/) — thin CLI client for interactive chat, daemon management, and configuration. Connects to the daemon via SignalR and authenticated HTTP.
- Gall's Law: build the simplest working system first — single-process runtime, actor-driven, persistence-backed
- Default-deny security: explicit ACL and policy checks everywhere
- .NET 10 runtime baseline with Google Protobuf for persistence serialization
- Session identity is Slack thread:
{channelId}/{threadTs} - MCP server integration included from the start
# Build everything
dotnet build Netclaw.slnx
# Publish both binaries to a shared output folder
dotnet publish src/Netclaw.Daemon/Netclaw.Daemon.csproj -c Release -o ./out
dotnet publish src/Netclaw.Cli/Netclaw.Cli.csproj -c Release -o ./outAdd the output folder to your PATH:
export PATH="$PWD/out:$PATH"Or point the CLI at the daemon binary explicitly:
export NETCLAW_DAEMON_PATH="$PWD/out/netclawd"
alias netclaw="$PWD/out/netclaw"Releases are cut by pushing a git tag; .github/workflows/publish_release_binaries.yml
does the rest (GitHub release, signed binary manifest + archives to R2, multi-arch Docker
image, and the system skills feed). The pushed tag MUST match Directory.Build.props —
<VersionPrefix> for a stable tag, and <VersionPrefix> + <VersionSuffix> for a
prerelease — or the workflow's version gate fails the release.
- Bump
<VersionPrefix>inDirectory.Build.props(e.g.0.22.1→0.22.2); leave<VersionSuffix>empty. - Add a release-notes section to
RELEASE_NOTES.md(#### X.Y.Z YYYY-MM-DD ####). - Commit, then tag and push the bare version:
git tag 0.22.2 && git push origin 0.22.2 - The workflow publishes a normal GitHub release, moves Docker
:latestand:major.minor, and sets the manifestlatestpointer to the new version.
Prereleases ship to opt-in testers without touching any stable surface.
- Set BOTH halves in
Directory.Build.props:Use the dotted<VersionPrefix>0.23.0</VersionPrefix> <VersionSuffix>beta.1</VersionSuffix>
beta.Nform (beta.1,beta.2, …beta.10) — neverbeta1. A non-dotted identifier compares lexically (sobeta10would rank belowbeta2), and the release version gate rejects it. - Add a
RELEASE_NOTES.mdsection for0.23.0-beta.1. - Commit, then tag and push the full version (prefix
-suffix):git tag 0.23.0-beta.1 && git push origin 0.23.0-beta.1 - The workflow detects the
-and: marks the GitHub release a prerelease; pushes only the exact-version Docker tag (leaving:latest/:major.minoron the newest stable); and advances the rolling:betatag and the manifestlatestPrereleasepointer.
Testers opt in:
curl -sSL https://releases.netclaw.dev/install.sh | bash -s -- --channel beta # -Channel beta on Windows
docker pull ghcr.io/netclaw-dev/netclaw:beta
NETCLAW_VERSION=0.23.0-beta.1 curl -sSL https://releases.netclaw.dev/install.sh | bash # pin exactWhen the matching stable ships, beta installs and :beta automatically roll onto it (the
beta channel resolves to the newest of {stable, prerelease}). Stable installs are never
offered a prerelease.