Marker handling, both directions. `installOpencode` and caveman-init treated
"both markers present" as a fence, so an orphan BEGIN or an END above a
BEGIN ran the slice arithmetic on end === -1: AGENTS.md re-appended itself
from byte 19 and compounded on every re-run, and a per-repo refresh would
have replaced everything between an orphan BEGIN and a later block's END,
user content included. Both now require exactly one matched pair, in order,
and report damage instead of writing.
Uninstall ordering. uninstall.sh/uninstall.ps1 deleted the hook files first
and edited settings.json second, so a failed or refused edit left Claude
Code pointing at scripts that no longer exist — `Cannot find module
…caveman-activate.js` on every session start (#471). Settings first; files
only once nothing references them. Neither script overwrites an existing
.bak any more (it is the user's only pre-caveman copy), and a JSONC
settings.json now aborts the edit with the working command instead of
half-uninstalling.
install.ps1's "already wired" probe matched the bare substring `caveman`,
which any user hook mentioning the word in a path satisfies (#593) — it
silently skipped wiring and left caveman inactive. It now probes for the
exact script we write per event, mirroring install.sh.
The SessionStart watchdog no longer claims `startup` when it fires. That is
the one source that resets the mode, so a payload slow enough to time out on
a compact/resume event dropped a mid-session `ultra` back to the default.
An unknown source preserves a valid existing flag, and the deadline moves to
3s — still well inside the host's 5s budget, but above a cold Windows/AV
start.
caveman-init writes retry a rename that Windows loses to OneDrive/AV
handles (EPERM/EBUSY/EACCES), the shrink MCP refuses an implausible .cmd
shim before reading it, and pruneOrphanedManagedHooks no longer prunes a
hook whose path is absolute on the other platform — existence is only
knowable for our own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmbqWDtNexzDfH5V3BMRz
Whole-repo review of the surfaces that mutate a user's agent config, plus a
cross-platform sweep focused on Windows.
Hooks (Windows):
- caveman-mode-tracker finished its work in ~50ms then blocked until the host
killed it at the 5s budget: the 'data' listener REFERENCES the stdin handle,
so pause() alone keeps the event loop alive until the host closes the write
end, and on Windows that close lags arbitrarily. Reproduced off-Windows.
This is the shape of #819. Fixed with process.stdin.unref().
- caveman-activate used readFileSync(0), which blocks INSIDE the read syscall
until EOF — no deadline can interrupt it. The #729/#833 fix had been applied
to the tracker and never to its sibling. Rewritten event-driven, acting on
the first complete payload, with a watchdog so a payload that never arrives
still activates instead of forfeiting the session.
- hookCommand emitted PowerShell call-operator syntax into settings.json, but
Claude Code's Windows hook shell is Git Bash, which rejects it outright:
`syntax error near unexpected token '&'`. Every standalone Windows install
had two dead hooks (#835). Both emitters now route through one bash-safe
shape.
- activate now threads the payload cwd, so a checked-in .caveman.json
defaultMode "off" is honored for the session's directory (#634), and a JSONC
settings.json no longer suppresses the statusline nudge.
Config ownership:
- src/hooks/uninstall.{sh,ps1} matched hooks by the substring "caveman" and
filtered whole entries, deleting user hooks that merely mention the word and
dropping foreign handlers that shared a matcher group (#593). Ported the
exact-basename matcher from bin/lib/settings.js and switched to filtering the
inner handler list. Verified against real PowerShell: the old code left zero
user hooks standing.
- install.{sh,ps1} backed up settings.json unguarded, so a --force reinstall
overwrote the only pre-caveman copy with the merged file. Back up once.
- OpenClaw overwrote a hand-edited workspace skill with no backup.
- caveman-init appended unfenced blocks to tracked repo files, so they could
be neither refreshed nor removed; now marker-fenced and written atomically.
- opencode AGENTS.md and OpenClaw SOUL.md were presence-checked, so a ruleset
edit never reached anyone already installed. Both refresh in place.
- opencode's system-prompt hook had no idempotency guard; it now rewrites its
line rather than stacking another onto the context window.
- readSettings reports when it stripped comments so both write paths warn and
point at the backup instead of silently dropping a user's annotations.
Skills: skills/ is auto-discovered wholesale by the plugin loader, so 13
undocumented skills would have shipped into every user's agent on the next
release. Documented them and added a verify_repo check that fails when a
skills/ directory is named in neither README.md nor CLAUDE.md.
CI: a windows-powershell job covering the surface engine-ci never exercised —
the PowerShell installer round trip, and a bash -n gate over every hook command
the installer writes. That gate alone catches #835.
Tests: every fix above is pinned by a test that fails against the old code,
including tests/test_hook_stdin_lifecycle.js, which reproduces the Windows
lagging-close condition on any platform.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ez7w7BBtKrMZCiZz9ea4cb
Single-commit port of the Caveman 2 tree onto the original repo history
(candidate caveman-2@88878a4 applied over main@3098342). No unrelated-history
merge; original history untouched.
Preserved v1 surfaces:
- skills/caveman/SKILL.md byte-for-byte, plus plugin mirror and caveman.skill zip
- legacy CI (Node 18/20/22 installer + Python 3.11 suites) alongside engine-ci
- cli/install.js compatibility shim forwarding to bin/install.js
- .codex/config.toml ships both hooks and codex_hooks for older Codex (#617)
- README Trendshift + Skills.sh badges and local star-history asset
- v1.10.0-pinned curl installs; benchmarks/tmp-starcharts ignore rules
Fixed for public same-repo release:
- SECURITY.md vulnerability reporting points at this repository
- release-binaries.yml publishes to this repo via job-scoped GITHUB_TOKEN
(cross-repo PAT removed); binary-release environment still gates signing
- packages/cli/PUBLISHING.md rewritten for packages/ layout and same-repo flow
- profile-contract and provider-catalog lanes skip lane-scope gates on
initial import so the launch PR can gate on their real validation steps
- ANNOUNCEMENT.md network claim corrected: compression is local; requests
still go to the provider you configured
Caveman 2 stays opt-in: bin-v1.0.0 binaries and @caveman-ai/cli publish
separately after this lands and soaks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BLvb5QniwYjmcawyssQNw9
Marketplace fix (#712, #705): Claude Desktop rejects plugins containing a
top-level bin/ directory, and .claude-plugin/marketplace.json packages the
repo root, so the installer directory is now cli/. Every reference updated
(package.json bin entry + files, shims, docs, tests, caveman-init require
path). Supersedes PR #726.
Security (PR #717 verified): quoteWinArg only quoted on whitespace/quotes,
leaving cmd.exe metacharacters (& | ^ < > % parens) unescaped on the
shell:true Windows spawn path. Attacker-influenced arguments (--with-init
cwd, --with-mcp-shrink value) could chain commands. Trigger regex now
covers the metacharacter set; quoting logic split into a platform-
independent, unit-tested helper.
Also:
- uninstall removes .caveman-active.prev, .caveman-mode-log.jsonl,
.caveman-statusline-suffix, .caveman-nudge-shown; keeps
.caveman-history.jsonl with a printed note; dry-run now says
'would remove' instead of lying (#635, supersedes PRs #693#636)
- Array.isArray guard in rewriteLegacyManagedHookCommands — malformed
hook event no longer crashes the installer mid-run (supersedes PR #646)
- gemini extensions install --consent: the security prompt hung every
piped/non-interactive install forever (#676, part of PR #664)
- OpenClaw skill stamps the real PINNED_REF version instead of hardcoded
1.0.0; new --no-always flag for load-on-demand installs (supersedes
PR #720)
- shims scope NPM_CONFIG_ALLOW_GIT=all to the npx call — npm >=12
defaults allow-git to none and EALLOWGITs github: installs (#698)
- .codex/config.toml ships hooks + codex_hooks keys so auto-activation
works on both sides of the codex-cli rename (#617)
- caveman-help card shows the Windows config path (%APPDATA%) (#723)
- caveman-parse.js added to HOOK_FILES, opencode payload (.cjs), and the
regenerated checksums.sha256; manifest now matches shipped hook
contents — release must bump PINNED_REF to a tag containing these files
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
Replace the ~50-65% range and the ~50%-vs-terse figure with a single 65%
(the measured average output reduction vs verbose baseline) on every product
surface: README hero + ASCII card + benchmark blurb, plugin.json,
marketplace.json, gemini-extension.json, caveman SKILL.md + skill README,
caveman-init rule frontmatter, and the docs site (index.html telemetry widget
+ marquee still showed the old 75%). Trim HONEST-NUMBERS.md to match. Raw
benchmark/eval snapshots and the eval-harness methodology docs are untouched.
Bump PINNED_REF v1.9.0 -> v1.9.1 so the standalone hook-download fallback
fetches cavecrew-model-overrides.js from the release tag instead of 404ing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0163QczkBHKpYgxx3sBc7X3b
README headline now states the measured range with method (65% avg vs
verbose default from benchmarks/, ~50% median vs 'Answer concisely.'
control from evals/) plus explicit caveat: input tokens untouched,
session-level savings smaller. Stats box shows output vs input split,
drops the unmeasured ~3x speed row. Same ~75% claim corrected in
SKILL.md description (+ synced plugin copy and dist/caveman.skill),
plugin.json, marketplace.json, gemini-extension.json, caveman-init
rule frontmatter, skill README, and CLAUDE.md.
Note for maintainer: the GitHub repo description still says 'cuts 65%
of tokens' — should read 'cuts ~50-65% of output tokens (measured)'.
Not changeable from a local commit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
commands/caveman-init.toml told every installed Codex/Gemini user to run
node src/tools/caveman-init.js — a path that only exists inside the
caveman clone, so the command failed with Cannot find module for
everyone but caveman developers.
- toml prompt now gates the repo-relative path on the file existing and
falls back to the standalone script via curl | node -
- src/tools/caveman-init.js: the documented curl|node stdin path was
silently a no-op — require.main is undefined under node -, so the
require.main === module guard never ran main() (exit 0, no output,
no files). Guard now also matches module.id === '[stdin]'.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
OpenClaw is a self-hosted gateway that orchestrates multiple agents and
loads workspace skills on-demand. To make caveman always-on through it we
write a spec-correct skill folder plus a marker-fenced bootstrap block in
SOUL.md (which OpenClaw auto-injects every turn). Both writes are
idempotent and reachable from `bin/install.js --only openclaw` and
`caveman-init.js --only openclaw`. Side-effect: `--only <id>` now bypasses
the detect-match guard so explicit opt-in works for any provider whose
preconditions can't be probed (e.g. custom OPENCLAW_WORKSPACE paths).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces opencode's Tier-4 npx-skills entry with a native in-repo plugin
that mirrors the Claude Code hook architecture (session.created +
tui.prompt.append) using opencode's lifecycle hook system. Reaches Tier-1
parity minus the statusline (opencode TUI exposes no plugin-writable
badge). Skill files drop in unchanged — opencode reads SKILL.md natively.
Plugin reuses src/hooks/caveman-config.js for the symlink-safe flag-write
helpers via createRequire (renamed .cjs post-install to coexist with the
plugin dir's "type":"module"). AGENTS.md provides a Tier-3 always-on
fallback if the plugin runtime breaks.
5 new tests cover fresh install, idempotency, JSONC tolerance of
pre-existing opencode.json, uninstall, and a plugin smoke test that fires
synthetic tui.prompt.append events. All 38 installer tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>