Skip to content

Releases: crisandrews/ClawCode

v1.7.6 — Orphaned reminders now self-heal (mechanical reconcile audit)

11 Jun 17:21

Choose a tag to compare

Fixes

  • Skills/crons (writeback): new audit subcommand — a mechanical CronList-vs-registry diff that refreshes lastSeenAlive, relinks reminders that survived under a new task id, flags ambiguous live duplicates as blocked instead of guessing, and persists the result for every other surface to read. Closes #32: a partially-executed reconcile could leave reminders "registered" but never firing, with no alert anywhere.
  • Hooks/reconcile-crons: the SessionStart envelope now verifies itself — audit first, recreate only what is actually missing, re-audit, retry once, and report the mechanical count to the user, with explicit "single unit of work" wording so an interleaved chat reply can't strand the reconcile halfway.
  • Skills/heartbeat: always-on cron-health step (runs even outside active hours) — prunes fired one-shots, audits, and re-creates orphans every 30 minutes, so a long-running service session no longer waits days for the next restart to heal. Notifications are throttled.
  • Skills/crons (writeback): set-alive no longer resurrects a stale .reconciling marker — the CronCreate bypass window only slides while a reconcile is making real progress.
  • Lib/doctor: /agent:doctor reads the persisted audit and warns when reminders are registered but not firing (or blocked by ambiguous duplicates), with the exact keys and the fix; the doctor skill refreshes the audit before printing the card so the two can never contradict each other.
  • Docs/crons: corrected the two-sessions claim — the registry lock serializes writes; it never made a second session skip its reconcile.

Changes

  • CI: new GitHub Actions workflow validates dependency install, jq availability, and shell-entrypoint parsing on ubuntu + macos for every push/PR.

Thanks @wrahman0 for the detailed #32 field report — the audit and orphan-count design follow those suggestions closely.

Full changelog: CHANGELOG.md.

v1.7.5 — reminders stop resurrecting after they fire; long reconciles no longer stall

09 Jun 22:42

Choose a tag to compare

Changes

  • Skills/crons (writeback): new prune-expired subcommand — bin/cron-from.sh now records each one-shot's target epoch (stamp line 3), the PostToolUse hook persists it into the registry as targetEpoch, and every SessionStart reconcile retires fired one-shots deterministically instead of resurrecting them scheduled a year out. Legacy date-shaped reminders (created before the field existed) are reported as suspects in the session banner for the user to verify — never auto-removed, and intentional annuals stay quiet.
  • Skills/channels + Lib/channel-detector: paired with claude-whatsapp 1.21.0 — guidance now says the waiting session takes over the WhatsApp lock automatically when the holder exits (full relaunch only needed on older channel versions), and the detector recognizes the new connect_error runtime state as a problem instead of an unknown status.
  • Skills/crons + Skills/import: OpenClaw at-job imports now pass --target-epoch, so imported dated reminders are properly retired after firing.

Fixes

  • Skills/crons (writeback): set-alive refreshes the .reconciling marker on every recreated entry, so a reconcile interleaved with live channel chat no longer outlives the 10-minute bypass window and gets blocked mid-flight with a misleading stale-stamp error. Every cron-pretool rejection now also teaches the mid-reconcile recovery (re-touch the marker and retry).
  • Hooks/cron-posttool: ad-hoc capture no longer coerces recurring: false to true (jq // swallows boolean false) — without this, captured one-shots were invisible to the new prune and the resurrection bug would have survived its own fix.
  • Bin/cron-from: BSD/GNU date detection is value-checked (GNU first), so a file named 1 in the working directory can no longer misclassify the platform.
  • Lib/doctor: the stale-tombstone warning no longer suggests a command that doesn't prune; tombstones are deliberately kept so deletions stay resurrection-proof.

Full changelog: CHANGELOG.md.

v1.7.4 — surface locked-out WhatsApp channel

31 May 04:35

Choose a tag to compare

Fixed

  • /agent:channels now surfaces a locked-out WhatsApp server instead of reporting it as fine. The channel detector previously only checked that status.json existed; it never read the live status. So when a second session held the WhatsApp single-device lock (common after an in-session update/reload, or when a background/service/scheduled session was still alive), /agent:channels showed active: ❓ and the agent stayed silent while the user saw "typing…" on their phone but got no reply. The detector now reads status.json, exposes a runtime field (status, holder PID, inboundActive, remediation), marks the channel active: no on a problem state (idle_other_instance / logged_out / lock_error), and channels_detect({ format: "table" }) prints a ⚠️ Runtime block with the fix. The channels skill now leads with this warning and never suggests /whatsapp:configure reset for a lock-ownership problem.
  • Rebuilt the exec-gate bundle so its integrity header (source-sha) matches the current source. The committed bundle had drifted since 1.7.3 — the compiled code was identical, only the integrity header was stale — which tripped the tier-1 drift guard. Full suite is back to green.

Compatibility

  • Pairs with claude-whatsapp 1.20.1, which writes the enriched idle_other_instance status. Degrades gracefully against older claude-whatsapp that writes only holder. No config or tool-signature changes.

v1.7.3 — Import channel-content guard (scope provenance)

30 May 22:52

Choose a tag to compare

Fixes

  • Skills/import: new "channel-content guard" (Step 5a) flags copied OpenClaw daily logs that contain channel-derived content (WhatsApp/Telegram JIDs, t.me/, voice-note phrasing, wacli/baileys) and offers quarantine (to a non-indexed ./import-quarantine/), skip, or import-as-local-with-acknowledgment — instead of silently landing chat summaries in memory/*.md, where they'd be classified local provenance and bypass channel-scope filtering even under enforce. MEMORY.md is scanned warn-only; flagged files are recorded in IMPORT_BACKLOG.md and surfaced in the import report. Step F also clarifies that memory.extraPaths is the correct provenance lane for channel history. The scope engine (lib/scope/*) is unchanged.

Full changelog: CHANGELOG.md.

v1.7.2 — Never trust a display name (anti-spoofing identity rules)

30 May 14:12

Choose a tag to compare

Changes

  • Agents/messaging: the agent's runtime instructions and workspace templates now teach JID-based sender identity — owner trust comes from the channel's is_owner flag, never a display / quoted-author / contact-card name; group participants are non-owner unless is_owner is true; and the agent must never record a "this JID is the owner" or "these two JIDs are the same person" fact from a chat name. Delivered via the runtime MCP instructions, so existing agents pick it up next session. Pairs with claude-whatsapp 1.20.0, which emits the authoritative identity fields.

Full changelog: CHANGELOG.md.

v1.7.1 — Bash-heredoc fix for first-run bootstrap + setup writes

19 May 16:55

Choose a tag to compare

ClawCode 1.6.0's always-on protected-paths defense refuses MCP Write to agent-config.json and channel access.json regardless of mode — by design for security, but two legitimate setup flows broke as a side effect. The first-run bootstrap wizard (every new user) couldn't write its own agent-config.json, and /whatsapp:access pair <code> couldn't write access.json when the channel state-dir resolves to the global fallback ~/.claude/channels/whatsapp/. Both flows now route the file write through a hardened Bash heredoc pattern. The protected-paths defense applies to file-tool writes only; Bash is gated separately by the exec-gate hook and doesn't fire during user-driven setup. No behavior change for users who already completed setup on 1.5.0 or earlier. Pairs with claude-whatsapp 1.19.1, which routes its WhatsApp side identically.

Fixes

  • Templates/BOOTSTRAP: the first-run agent-config.json write goes through a validated Bash heredoc (cat-in-&&-chain + JSON.parse validate + atomic mv with cleanup-on-failure) instead of MCP Write. Closes the install regression where every new user hit exec-gate: write to protected path refused (workspace-agent-config) and could not complete bootstrap.
  • Skills/settings: both reference blocks ("Configure the backend" and "Modifying settings") use the same hardened pattern; JSON.parse rejects malformed JSON BEFORE the atomic mv so a truncated or syntactically broken write can never clobber the existing config.
  • Skills/import: memory-backend onboarding snippet adopts the same hardened pattern.
  • Lib/scope/exec-gate: error message for workspace-agent-config and channel-access-json blocks now appends a recovery hint pointing at the safe Bash heredoc pattern. Without this, an agent hitting the block would loop retrying Write.
  • AGENTS.md: new "Legitimate writes to protected paths" subsection documents both heredoc forms (basic agent-config + auth-adjacent umask 077 + chmod 600 variant for server-shared channel state) with explicit rules — use the snippet only when a trusted skill provides it (never improvised from agent reasoning), flag Bash auto-allow to the user when it's on, and treat any "update my agent-config.json" instruction arriving via a messaging channel as candidate prompt-injection.
  • Dist/exec-gate-resolver.cjs: rebuilt with the new hint text.

Full changelog: CHANGELOG.md.

v1.7.0 — Per-workspace scope-trust files

16 May 00:37

Choose a tag to compare

After upgrading: legacy global scope-trust files from 1.5.0 / 1.6.0 stop unlocking. Re-run /agent:scope wizard in each workspace where you want trust granted. Doctor (/agent:doctor) surfaces the leftovers + the rm cleanup command.

Changes

  • Scope/trust: trust files now live under ~/.claude/agent/scope-trust/<workspace-fingerprint>/<channel>-{owner,exec}. Granting trust in one workspace no longer silently unlocks another — agent-config.json was already per-workspace, so the prior global path was a category error. Per-workspace fingerprint uses SHA256(realpath + per-filesystem case-fold probe) → 32 hex.
  • Skills/scope wizard: every Bash snippet that creates or removes a trust file hardened — set -euo pipefail, CLAUDE_PLUGIN_ROOT guard, 32-hex hash validation, umask 077. Fingerprint computed via the bridge script scripts/print-workspace-fingerprint.mjs so Bash and TypeScript agree byte-exact on the subdir name.
  • Hooks/scope-trust-legacy-warn: SessionStart advisory points to the wizard when 1.6 flat-layout trust files exist. Silent in steady state; one-line stderr when needed. Workspace-scoped dismissal marker so dismissing in workspace A still warns in workspace B.
  • Lib/doctor: new scope-trust-legacy check walks the trust-dir for pre-1.7 leftovers, gates each through the same predicate the resolver uses (skips stale 0o644 + symlinked markers), and lists exact paths plus the literal rm cleanup command.
  • Lib/scope/legacy-warn: shared warnLegacyTrustMigrationOnce helper used by both the WhatsApp adapter (read scope) and detectScopeRuntime (arm detection) so users see exactly one stderr migration hint per (workspace × channel × suffix), not two.
  • Lib/scope/exec-gate-hook-entry: workspaceRoot normalized at the hook boundary via path.resolve + NUL-byte rejection + absolute-path validation. Fails CLOSED (exit 2) instead of through the top-level fail-open catch.
  • Lib/scope/exec-gate: enforce + shadow branches both surface "legacy global exec trust ignored for this workspace" when workspace-scoped exec trust is missing AND a valid 1.6 global file exists. Shadow event payload gains legacyGlobalExecTrustIgnored?: boolean.

Fixes

  • Read-scope owner unlock no longer silently degrades to guest when a 1.6 user upgrades — three surfaces (SessionStart hook, runtime warn, doctor row) make the migration cliff loud rather than silent.
  • Cross-workspace isolation regression closed: 1.6 trust files acted globally despite per-workspace config.

Full changelog: CHANGELOG.md.

v1.6.0 — Execution gate (opt-in)

15 May 16:04

Choose a tag to compare

What this release brings

ClawCode now ships an opt-in execution gate. If you've paired the agent with a group chat (WhatsApp via claude-whatsapp), this layer prevents the agent from running destructive tools — Bash, Write, Edit, Task, and more — when the current turn was triggered by a non-owner message from that chat. Think of it as the action-side companion to the channel-scope filter shipped in 1.5.0: read scope governs what the agent sees, the execution gate governs what it can do.

The default is off, so existing users see zero change. Turn it on through /agent:scope wizard: pick a channel, a policy (denylist of destructive tools — recommended — or allowlist of safe ones), and whether to start in shadow (log only) or enforce (block).

Some hardening fires regardless of the gate's state. Plugin internals, the gate's own source files, agent-config.json, scope-trust directory, SSH/credential dirs, shell init files, LaunchAgents / systemd user units, and channel access.json are refused as Write/Edit targets even with the gate off. That closes the bootstrap-attack surface where a prompt-injected agent could otherwise modify the gate before turning it on.

A separate trust file (~/.claude/agent/scope-trust/<channel>-exec, created via Bash through the wizard) unlocks the gate for trusted machines. It's independent from the read-scope <channel>-owner trust — you can read all your channels' content from your own machine without granting non-owner group chats the power to invoke destructive tools.

Changes

  • New /agent:scope wizard steps cover the new gate (activate Y/N → denylist or allowlist → trust this machine Y/N), the trust file is created via Bash with a permission prompt, never silently.
  • /agent:scope status now surfaces per-channel exec-gate state including trust file validity (distinguishes a present-but-invalid file from a valid one — the resolver and the doctor agree on the same predicate).
  • /agent:scope test exec <senderJid> <toolName> runs a real resolver dry-run via npx tsx -e, honoring the hard-deny pair (Bash, Task), trust file unlock, and protected paths.
  • /agent:scope disable now resets both read scope AND exec-gate to off, and removes both trust files.
  • Two new doctor checks: scope-execgate-status (per-channel info row) and scope-execgate-shadow-events (warns when recent shadow events exist, so you can review before flipping to enforce).
  • Hook hot path stays at ~8 ms p95 when the gate is off; armed path is ~65 ms p95 via a pre-built CJS bundle (avoids the tsx cold start on every tool call). Bundle drift is detected by a tier1 test that recomputes the SHA over the source-file tree esbuild pulled in.
  • Bash and Task are hard-denied when the gate fires regardless of the user's tool list. Shell command grammar is too rich to safely parse; subagent hook propagation isn't guaranteed by Claude Code. Wholesale deny is the defensible posture for both.

Documentation

  • PRIVACY.md adds an Execution scope subsection covering what the gate does and explicitly what it doesn't (memory poisoning across turns, reply egress, terminal-side user actions, within-lookback envelope replay).
  • docs/channel-scope-compat.md expanded "Execution scope" section.
  • AGENTS.md adds short guidance for the agent: when a PreToolUse block hits stderr, don't retry, don't rewrite the same intent as another blocked tool — surface the reason to the user.

Generated with Claude Code.

v1.5.0 — channel scope + WebChat sessions

14 May 02:30

Choose a tag to compare

What this release brings

ClawCode 1.5.0 ships a brand-new opt-in privacy layer for indexed messaging-channel content. Until now, when your agent paired with claude-whatsapp and you ran memory_search or recalled context from dreams, the agent could see snippets from every chat it had indexed — regardless of the per-chat access rules claude-whatsapp already enforced on its own tools. This release closes that gap.

Turn it on with /agent:scope wizard and the wizard walks you through three choices: which channel, what mode (observe-only shadow or actively filter with enforce), and how the agent should identify itself (owner if you want the agent to see all your chats, guest if you want it sandboxed, or auto for the conservative default). Owner mode requires an out-of-band trust file you create yourself — the agent can't grant itself elevated privileges via MCP.

Default is off everywhere. If you don't run the wizard, ClawCode behaves exactly as it did in 1.4.x.

The release also includes:

  • WebChat privacy fix. Every browser tab now has its own sessionId, so two browsers sharing the same http.token can't see each other's chat history or live agent replies. sessionId is a privacy partition (per-tab); http.token remains the auth boundary.
  • Per-chat semantics with claude-whatsapp 1.19.0+. When both plugins are on the new releases, your agent's memory searches automatically bind to the specific WhatsApp chat that triggered the turn — mirroring upstream historyScope rules byte-exact.
  • Security hardening. A dedicated blocklist refuses MCP-side writes to security-sensitive config keys (scope policy, voice output dir, memory paths, QMD command). Voice output paths are canonicalised and allowlisted. Skill install/remove now validates names + path containment. Live-config refuses to hot-reload privileged keys.

If you don't pair WhatsApp or don't opt in to scope, none of this changes anything for you — the existing behavior is preserved.


Changes

  • Skills/scope: new /agent:scope skill suite — wizard, enable, disable, status, test <chatId>, audit. Interactive opt-in for per-channel scope filtering at the MCP boundary.
  • Lib/scope: per-channel opt-in scope tree under config.scope.<channel> (default off everywhere). Modes: off | shadow | enforce.
  • Lib/scope/synthetic-indexer: per-chat synthetic chunks generated from upstream messages.db (read-only, WAL-respecting). Replaces daily-transcript chunks for per-chat semantics.
  • Lib/scope/dreams: dual-lane promotion — local candidates promote to memory/MEMORY.md; armed-channel candidates route to memory/.scoped/<channel>/MEMORY.<encoded-chat>.md with PII redaction markers.
  • Lib/scope/envelope: cross-plugin request envelope contract. memory_search / memory_get / memory_context / voice_transcribe accept an optional requestEnvelopeToken arg for per-chat binding when paired with claude-whatsapp 1.19.0+.
  • Lib/http-bridge: WebChat per-tab session model. Every /v1/chat/send / /v1/chat/history / /v1/chat/stream request requires sessionId (UUID v4). Per-session message cap (500), pin-on-active LRU, SSE-clients-per-session cap (8).
  • Lib/doctor: new scope checks — scope-status, scope-stale, scope-owner-assertion, scope-schema-drift, scope-bypasses, scope-quarantine-pending, scope-wizard-available, scope-indexer-health.
  • Server: defensive chat_inbox_read gate on runtime.channels.webchat?.armed && mode === "enforce".

Fixes

  • Lib/scope/runtime: applyPreventivePromoteGuard now resolves the live runtime instead of defaulting to no-armed — closes a path where channel chunks could leak into MEMORY.md mid-session.
  • Lib/scope/whatsapp: explicit guest / deny config now beats bootstrap fail-open. Malformed access.json with missing ownerJids field is rejected.
  • Lib/voice: voice output path canonicalised (realpath + case-fold + allowlisted to outputDir / os.tmpdir() / /tmp). Refuses any path resolving under the trust-file directory.
  • Lib/skill-manager: skill_install and skill_remove validate name (slug charset, no separators/NUL, length cap, reject . / ..) and path containment.
  • Lib/live-config: hot-reload refuses to update privileged scope/voice/memory keys. Preserves prior in-memory value and surfaces a notification.

Security hardening

  • New out-of-band trust file primitive ~/.claude/agent/scope-trust/<channel>-owner for owner unlock — the agent cannot grant itself owner privileges via MCP.
  • agent_config(action='set') blocklist refuses scope keys, prototype-pollution segments, oversize keys, and privileged keys (voice.outputDir, memory.extraPaths, memory.qmd.command and their ancestors/descendants).
  • Voice text-hash binding removed — was a wrong primitive (false positives on benign transforms, false negatives on paraphrases).

Compatibility

  • Zero behavior change when config.scope.* is absent.
  • Old claude-whatsapp paired with 1.5.0: per-chat semantics fall back to the owner-only ceiling. Owner unlock via trust file unaffected. Update claude-whatsapp to 1.19.0+ for full per-chat semantics.
  • New claude-whatsapp paired with old ClawCode: silently ignores envelope token; no upstream breakage.

Full changelog: CHANGELOG.md

v1.4.14 — Windows via WSL2 documented

21 Apr 20:57

Choose a tag to compare

Changes

  • Docs/wsl2: publish docs/wsl2.md walking Windows users from zero to a running agent via WSL2. Opens with a "where is your Claude Code running right now?" diagnostic (uname -a tells them whether they're already inside WSL2 or stuck on native Windows), so a Windows user with an existing native Claude Code install doesn't silently install ClawCode into an environment where the Linux code paths can't run. Then PowerShell wsl --install, Ubuntu deps (nodejs npm jq git), a separate Claude Code install inside WSL2 (coexists with any native install), ClawCode plugin install, systemd user service with loginctl enable-linger, and a "what works / what doesn't" section (iMessage is the one exclusion; say TTS auto-skips; memory.extraPaths inherits the native-Linux recursive-watch caveat). Closes the recurring "does this run on Windows?" question — nothing in the plugin needed to change architecturally because WSL2 reports process.platform === "linux" and every platform-gated path already takes the Linux branch.

  • Docs/service: replace the "Windows not supported" row with two rows — Windows (via WSL2) → systemd (--user) and a separate Native Windows / BSD / other → Not supported.

  • Plugin/readme: badge now advertises macOS | Linux | Windows (WSL2) — "Windows" explicit so scanning users on Windows see their platform listed — and Prerequisites adds a line telling Windows users to run Claude Code inside WSL2 (not native PowerShell / cmd).

Fixes

  • Lib/service-generator: the unsupported-OS error emitted by /agent:service install on native Windows now points users at WSL2 + docs/wsl2.md first (the path that actually works), keeping Task Scheduler as the native-only fallback. User-visible string only — no behavior change.

Full changelog: CHANGELOG.md.