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
PR #560 shipped the regression test but its source change never made it
into the diff — the branch only adds tests/test_mode_tracker_stdin.js.
Applied the one-line silent-exit error listener the PR describes and
took the author's test verbatim.
Co-authored-by: ousamabenyounes <ousamabenyounes@users.noreply.github.com>
--force on an AGENTS.md containing the legacy un-fenced sentinel
replaced the entire file with the fenced block — destroying all
user-authored content around the legacy block. The installer's own
hint told users with mixed files to run exactly that.
Migrate instead: back up once to AGENTS.md.bak, remove the legacy
block (exact match of the current rule body when possible, otherwise
cut from the sentinel's paragraph start — the legacy path appended the
block, so user content precedes it), and write the fenced block after
the preserved user content.
Fixes#594
A truncated/stray marker (interrupted write, partial user edit) chained
into deleting user content: appendBootstrapToSoul saw 'no complete
block' and appended a second one; stripBootstrapFromSoul then cut from
the FIRST begin to the FIRST end — spanning everything between the
stray marker and the appended block. Reported reproduction ended with
the whole SOUL.md deleted.
Replace the single-span cut with a scan that pairs each begin with the
nearest end before the next begin; unpaired markers are removed as just
the marker text, never as a span. Append now detects damaged markers
(orphans, duplicates), strips them safely, and writes one clean block.
Fixes#596
Natural-language matching (#598):
- Deactivation computed first, word-order tolerant: 'turn caveman mode
off' used to ACTIVATE caveman (and reset the level to default),
'turn caveman off' was a no-op.
- 'enable caveman and stop apologizing' no longer deactivates (the old
stop-guard fired on 'stop' anywhere, then the deactivation regex
matched 'caveman and stop').
- Questions ('what is caveman mode?') no longer arm the mode.
- 'normal mode' deactivates only as a command or with caveman context
('how do I exit vim normal mode' no longer kills the session mode).
- Prompts normalized to one line so multiline input matches.
- Scoped brevity ('be brief in the summary section') is a one-off
instruction, not a session-wide switch.
One-shot modes (#599):
- /caveman-commit|-review|-compress save the displaced prose mode to
.caveman-active.prev and the next ordinary prompt restores it (or
deactivates if caveman wasn't active before) — SKILL.md's 'level
persist until changed or session end' holds again.
- Plugin-namespaced /caveman:caveman-commit and -review recognized
(only compress and stats had the variant).
- Deactivation clears the saved prev so nothing resurrects the mode.
Fixes#598, fixes#599
The trailing-comma sweep ran a global regex over the whole
comment-stripped output, including string contents — a JSONC
settings.json with a value containing ',}' or ',]' (shell brace
expansion, inline JSON in hook args) was silently corrupted on read
and persisted corrupted on the next write.
Replace the regex with a scan that tracks string state (same approach
as the comment stripper above it) and only drops commas outside
strings.
Fixes#595
detect.py listed .dockerfile/.makefile in SKIP_EXTENSIONS, but real
files are named Dockerfile/Makefile with no extension, so they fell
through to the content heuristic and came back compressible —
/caveman-compress Dockerfile overwrote a Dockerfile with caveman prose.
Add a basename guard (Dockerfile, Makefile, Jenkinsfile, Vagrantfile,
CMakeLists.txt, ...) checked before any extension rule — CMakeLists.txt
would otherwise ride the compressible .txt rule — and a shebang check
in the extensionless branch so executable scripts are always code.
Mirror synced to plugins/caveman (sync workflow only triggers on
SKILL.md changes, so scripts/ must ride along).
Fixes#600
removeCavemanHooks stripped any settings.json hook whose command
contained the substring 'caveman' anywhere — a user-authored hook like
'node ~/Projects/caveman-notes/my-hook.js' was silently deleted by
--uninstall.
Match tokens against MANAGED_HOOK_BASENAMES by exact basename instead
(win32.basename so Windows-written configs match anywhere), the same
pattern pruneOrphanedManagedHooks already uses. Hoist the tokenizer to
module scope and reuse it in the prune pass. Add caveman-statusline.ps1
to the managed set so the Windows statusline wiring is covered by
removal and orphan-pruning too.
Fixes#593
spawnSync reports a missing binary as { status: null, error }, and the
(r.status || 0) === 0 checks coerced that null to success. On a machine
without the claude CLI, --only claude printed 'installed: claude',
skipped the standalone-hook fallback (which works offline), and left
nothing installed. Same pattern at 8 sites: claude, gemini, npx-skills
providers, mcp-shrink, runInit (both paths), uninstall, skills-auto.
Route every spawn result through spawnOk() (!r.error && r.status === 0)
and warn when the claude CLI itself could not be spawned. Regression
test runs the installer with an empty PATH and asserts failure is
reported and standalone hooks get wired.
Fixes#592
Hook lives at <plugin_root>/src/hooks/ but read SKILL.md via a single
'..' — resolving to nonexistent src/skills/, so every plugin install
silently fell back to the stale hardcoded ruleset (missing language
preservation, no-self-reference, intensity table).
Resolve via candidates in order: $CLAUDE_PLUGIN_ROOT/skills/,
__dirname/../../skills/ (plugin + repo layout), __dirname/../skills/
(standalone $CLAUDE_CONFIG_DIR layout). Sync the two missing rules
into the fallback for installs with no SKILL.md at all.
Fixes#587, fixes#589
This fixes the edge case for the following error by instantiating the
paths differently.
✘ Failed to install plugin "caveman@caveman": EXDEV: cross-device link not permitted
withProtectedSegments built sentinels in a single pass and restored them
in a single pass. When two PROTECTED_PATTERNS matched the same span
(e.g. path rule swallows STARTER/BUSINESS, then function-call rule
swallows the resulting type ( 0 )), the outer sentinel restored to
"type ( 0 )" but the inner " 0 " was never substituted back — enum
values reaching the model became literal "( 0 )".
Restore now loops with MAX_RESTORE_PASSES bound; same depth is reached
on the actual #444 inputs in <=2 passes.
Co-Authored-By: Claude <noreply@anthropic.com>
Adversarial review against a live Hermes Agent install (v0.11.0) found the
forward install was correct (skills land in the real ~/.hermes/skills/
productivity/<skill>/ layout, all 7 load as 'enabled' via 'hermes skills
list' — verified empirically, no version: field required), but uninstall had
NO Hermes handling: --uninstall silently orphaned all 7 skill folders forever.
- add Hermes block to uninstall() honoring HERMES_HOME (mirrors opencode/openclaw)
- tests/installer/hermes.test.mjs: install lands 7 skills, uninstall removes
them (regression guard for the asymmetry), dry-run uninstall is a no-op
- INSTALL.md: add Hermes Agent row to the per-agent install table (CLAUDE.md
mandates the install table stay complete)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- restore 2-space PROVIDERS indent and 4-space dispatch-loop indent (PR
re-indented existing gemini/opencode/openclaw lines as churn)
- correct mech label: 'native hermes skills copy' (it does a native dir
copy via installHermes, not 'npx skills add')
- drop dead profile: 'hermes' — shadowed by the installHermes special-case
and not a valid upstream vercel-labs/skills slug
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR #527 added cavecrew-model-overrides.js with a wrong sha256 (eae3... vs
actual 9f26...) and modified caveman-activate.js without regenerating its
entry. bin/install.js verifies downloaded hooks against this manifest and
aborts on mismatch, so both were latent install-breakers on the remote path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds Hermes Agent as a supported provider with native skill installation.
Hermes Agent uses SKILL.md format (same as upstream), so the installer
copies all 7 caveman skills directly to ~/.hermes/skills/productivity/.
Closes#45914 (related)
First tag shipping src/hooks/checksums.sha256 — with PINNED_REF now
pointing at it, curl|bash / detached installs fetch hook files from the
immutable v1.9.0 ref and SHA-256 enforcement activates fully (#261,
#262). Manifest verified current against src/hooks/ before the bump.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Smoke-tested against real opencode 1.4.0 (the missing verification
flagged in 22f75e3) — the plugin never loaded: opencode runs plugins
inside a compiled Bun binary where require() of on-disk files is
rejected ('require() async module is unsupported') and await import()
of a CJS file returns an empty namespace. caveman-config.cjs is now
evaluated as CommonJS by hand (readFileSync + Function wrapper with a
createRequire shim — built-ins still resolve fine).
Three more real-runtime gaps found and fixed in the same pass:
- session-init flag write now also happens at plugin factory time; in
one-shot 'opencode run' the first session.created publishes before
plugin event dispatch is wired, so the event handler alone missed it
- the TUI expands '/caveman <level>' into the command template before
chat.message fires; the parser now recovers the level from the
expanded 'Activate caveman mode: <level>' text
- the non-interactive run path wraps messages in literal quotes
('"/caveman ultra"'); the parser unwraps symmetric quotes
Verified end-to-end against opencode 1.4.0: plugin loads clean,
session-init writes the flag, /caveman ultra flips it, 'stop caveman'
deletes it, and the reinforcement line shows up in the outgoing LLM
system prompt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Counts were stale after the opencode + openclaw targets landed (5 -> 6
repo files, 7 with the openclaw line). Tests also ran the openclaw
installer against the developer's real ~/.openclaw/workspace; runInit()
now pins OPENCLAW_WORKSPACE inside the fixture tmp dir so nothing
escapes the sandbox. verify_repo.py drops the removed
.agents/plugins/marketplace.json from its manifest list.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
.junie/, .kiro/, .roo/, .agents/ held pre-cleanup cavecrew/SKILL.md
mirrors (plus an unused .agents/plugins/marketplace.json). Nothing in
the current install path reads them — bin/install.js handles Codex via
npx skills add, and Claude Code marketplace discovery uses
.claude-plugin/marketplace.json. CLAUDE.md marks these remove-on-sight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a Sponsors section to README promoting Atlas Cloud with a centered logo, link, and a call to sponsor the project. Include two new SVG assets (atlas-cloud.svg and atlas-cloud-dark.svg) under docs/assets for light/dark mode logo display.
Without commands/caveman-stats.toml on disk Claude Code rejects
/caveman-stats as 'Unknown command' before the UserPromptSubmit
hook in src/hooks/caveman-mode-tracker.js can intercept it. README
and INSTALL.md both advertise the command; this brings the slash
command registry in line with the docs.
Fixes#470
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verbatim copies of edited sources into the CI-managed plugins/caveman mirror and rebuilt dist/caveman.skill, so local main is self-consistent without a push. CI's sync-skill.yml regenerates these on push; this just keeps verify_repo green locally.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folds in #419 (replace the non-existent session.created/tui.prompt.append hooks with real opencode hooks: event dispatcher for session.created, chat.message for mode parsing, experimental.chat.system.transform for reinforcement; fixes #418/#421), #398 (ship the missing caveman-compress.md command + un-ignore it; fixes #426/#451/#464), and #376 plugin-side (drop %APPDATA% branch). Smoke test rewritten for the new hook shapes. NOTE: not smoke-tested against a real opencode runtime here — verify before release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Installer fixes: #414 (rename PS1 $Args->$InstallerArgs), #437 (detect Copilot via extension dirs, fixes#336), #395 (--skill '*' instead of --all so -a <agent> is honored, fixes#389), #472 (prune orphaned managed hooks from settings.json, fixes#471), #393 (don't double-wire hooks when the plugin manifest already does, fixes#392), #380 (MCP-shrink off by default, requires an upstream, fixes#474), #376 install-side (opencode uses ~/.config/opencode, drop %APPDATA%), #443 (strip tools: from cavecrew agent copies for opencode, #386), #434 (existsSync guard on command copy), #396 (doc: discover profile slugs via --list).
Security hardening: #261 (pin remote fetch to release tag PINNED_REF=v1.8.2, not moving main) and #262 (SHA-256-verify downloaded hook files against src/hooks/checksums.sha256 before they execute; abort on mismatch). #260 (inspect-before-run note). NOTE: enforcement activates fully once a release tag shipping checksums.sha256 is published and PINNED_REF is bumped; v1.8.2 predates the manifest so downloads there warn-and-proceed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folds in #429 (repo-local <repo>/.caveman/config.json resolution layer between env and user config; symlink-safe, bounded walk) and #248 intent (recognize 'less tokens'/'be brief'/'be terse' as natural-language caveman activation).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folds in #387 — extract spawn options to spawn-options.js; shell:true only on win32 (PATHEXT resolution), POSIX unchanged. Args still come from installer-controlled config (trust boundary unchanged).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folds in #438 — the docs demo terminal interpolated user input via innerHTML (real reflected/DOM XSS); build nodes with textContent instead. (PR title 'CLI input handler' was a misnomer.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folds in #466 (correct Opus 4.5-4.8 output price to $25/M, keep legacy 4.0/4.1 carve-outs at $75/M; fixes#465) and #459 (UTF-8 console + Get-Content so the pickaxe renders on Windows; +spacing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>