261 Commits
Author SHA1 Message Date
github-actions[bot] fcf7663366 chore: sync SKILL.md copies [skip ci] v1.10.0 2026-08-03 10:57:48 +00:00
Julius BrusseeandClaude Opus 5 e07431f127 release: pin install ref to v1.10.0
The sweep adds src/hooks/caveman-parse.js and regenerates
checksums.sha256 to include it. The detached install path fetches
hooks from RAW_BASE = raw.githubusercontent.com/<repo>/<PINNED_REF>,
and the integrity gate refuses any hook that doesn't match the pinned
release. Left at v1.9.1 that ref has no caveman-parse.js, so
curl | bash 404s and fails integrity for new users.

Bump must land inside the tagged commit so v1.10.0 contains both the
new hooks and a PINNED_REF pointing at itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012SSMrNKRooMGmJ1yNFpLMy
2026-08-03 12:57:36 +02:00
Julius Brussee 52bbbcdd86 docs: clarify token savings by workload
Update README to separate the 65% chat-style prose benchmark from full agentic coding runs, adding JetBrains’ independent 86-task result (8.5% output-token savings). Adds context on why both numbers are valid, explains workload-dependent ceilings, and aligns Caveman 2 positioning around measuring real savings on each team’s own traffic.
2026-08-03 12:51:36 +02:00
Julius BrusseeandClaude Fable 5 6566ec23d3 ci: run the full test suite on every push and PR
Tests existed but nothing ran them in CI. Node 18/20/22 matrix runs the
installer suite plus every standalone tests/test_*.js runner; a python job
runs unittest discover (compress tests mock the Claude call, no network).
Node is set up in the python job too — several python tests shell out to
node for hook checks. Inspired by PR #695, minus the scanner extras.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
2026-07-21 02:01:27 +02:00
Julius BrusseeandClaude Fable 5 ed1fbb7f4c fix(install): rename bin/ to cli/, harden Windows quoting, clean uninstall
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
2026-07-21 02:01:27 +02:00
Julius BrusseeandClaude Fable 5 dcd51f16fe fix(compress): stop Windows data loss — UTF-8 + atomic writes (#652, #655, #686)
Every read/write resolved to the locale codec (cp1252/cp949 on Windows):
non-ASCII files were silently mojibake'd, and because Path.write_text
truncates before encoding, a UnicodeEncodeError left the target at 0 bytes.
The backup readback check couldn't catch it — it read back with the same
wrong codec.

- encoding=utf-8 pinned on every I/O call site (compress, validate,
  detect, benchmark); validate now decodes strict — it's the fidelity gate
- write_text_atomic: encode first, temp file in same dir, fsync, preserve
  permissions, os.replace; temp unlinked on any failure
- fix-retry pass gains the same empty-output guard as the first pass
- fix-retry preamble leak (#588): output must start at the original's
  structural anchor (frontmatter/heading) or the attempt is rejected
- primary-write failure now prints the backup path — users hitting the
  crash had no idea a backup existed
- extract_inline_codes: strip fences via the CommonMark-aware extractor;
  the old column-0 regex leaked indented fences into inline-code pairing,
  causing false validation failures (extracted from PR #619's diagnosis)
- SKILL.md/README/SECURITY corrected: backups live in the out-of-tree data
  dir (#420), not beside the source file

Supersedes PRs #683 #678 #626 #534 and the fence fix from #619 with a
local implementation. 58 python tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
2026-07-21 02:00:59 +02:00
Julius BrusseeandClaude Fable 5 d833f4adab feat(stats): surface net savings, not just gross (#145, #677)
/caveman-stats only reported gross output savings, hiding the regime where
the ~1,250-token/turn rule injection costs more than compression saves —
the exact case docs/HONEST-NUMBERS.md warns about.

- Est. rule overhead: per-turn injected-rule input cost x turns
  (CAVEMAN_RULE_OVERHEAD_TOKENS overrides the default 1250)
- Est. net: saved minus overhead; when negative, says plainly that caveman
  cost more than it saved for this workload
- shown only for attributed uniform spans (follows #601 attribution);
  history rows without turn counts are excluded from net, never guessed
- statusline suffix stays gross savings, unchanged semantics

Reimplements the idea from PR #718 locally. 46 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
2026-07-21 02:00:59 +02:00
Julius BrusseeandClaude Fable 5 7693a046ee fix(hooks): shared parser, envelope unwrap, resume-safe SessionStart
- extract mode parsing into src/hooks/caveman-parse.js, consumed by both
  the Claude tracker and the opencode plugin — fixes the three #602 drifts
  (brevity triggers missing, bogus level overwrote flag with default,
  independent modes unreachable via expanded templates)
- unwrap Claude Code's <command-name>/<command-args> slash envelope: real
  slash-UI /caveman <level> and /caveman off were silent no-ops (#537);
  foreign envelopes skip natural-language detection entirely
- SessionStart branches on payload source: startup resets to configured
  default, resume/clear/compact preserve a valid existing flag (#691)
- scheduled-task prompts (<scheduled-task marker) skip flag mutation and
  reinforcement so unattended runs aren't hijacked
- per-turn reinforcement honors repo-local defaultMode off via
  getDefaultMode(cwd) gate — read-only, never deletes the shared flag
  (#634; rejects #532's cross-session flag deletion)
- reinforcement anchor shrunk ~57%, opencode line kept identical (#660)
- statusline setup nudge shown once, gated by .caveman-nudge-shown (#661)
- /caveman-stats delivered via hookSpecificOutput.additionalContext so the
  macOS desktop app renders it (#618)
- safeWriteFlag: retry rename on Windows sharing violations, always unlink
  temp in finally — no more .caveman-active.<pid>.<ts> litter (#511 #578)
- statusline.sh exits 0 on empty suffix file — non-zero exit was hiding
  the whole status bar (#711)
- cavecrew-model-overrides resolves plugin root across layouts; env model
  overrides were a silent no-op (#645)
- opencode dev-tree loader: base require on the loaded file so
  caveman-parse's relative require resolves in both layouts

Supersedes PRs #623 #674 #700 #691 #634 #660 #661 #692 #632 #622 #657
#578 #511 #645 #590 #498 #501 with local implementations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
2026-07-21 02:00:43 +02:00
Julius BrusseeandClaude Fable 5 710173f965 fix(skill): negation safety, tool-call silence, language-drift hardening
Verified against filed reports and applied minimal rewordings:
- never-drop-negations rule — dropping not/never inverts instructions (#670)
- tool-call silence defined: no preamble/progress notes around calls (#713, #714)
- language rule de-named: removes Portuguese/Spanish attractor tokens that
  drove drift under compaction; every emitted line in session language
  (#665, #654, #701, #539)
- particles/postpositions exempt from article-dropping — Korean/Japanese
  case markers are grammar, not filler (#680)
- classical chars scoped to wenyan modes only; Auto-Clarity example marked
  format-only so warnings render in session language (#679, #680)
- switch line covers all six levels + off (#549, #670)
- persisted-text boundary: docs/issues/PR text/memory files write normal
  (#483, #562, #582, #670)
- wenyan-full row: character reduction labeled chars-not-tokens

Supersedes PRs #715 #702 #684 #685 #667 #654 #658 #549 #670 #483 #562 #582
with local implementations. Eval snapshots need regeneration (API-gated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
2026-07-21 02:00:27 +02:00
github-actions[bot] 0d95a81d35 chore: sync SKILL.md copies [skip ci] v1.9.1 2026-07-03 11:08:51 +00:00
Julius BrusseeandClaude Opus 4.8 e2c09c9a7e docs: flatten savings claim to a flat 65% across all surfaces; bump PINNED_REF to v1.9.1
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
2026-07-03 13:07:25 +02:00
Julius Brussee dc95e915c5 Caveman Updated Skill + Readme 2026-07-03 12:29:48 +02:00
Julius Brussee 686c0cce64 Teaser; Caveman 2
(It'll be good)
2026-07-03 00:36:08 +02:00
Julius BrusseeandClaude Fable 5 e9cb8435d6 fix(#601): attribute session tokens to the mode active when each message happened
/caveman-stats credited ALL output tokens since session start to whatever
mode the flag held at stats time: activating caveman late inflated the
estimate (verbose tokens back-computed as compressed), deactivating it
zeroed honest savings — and the wrong number compounded into the lifetime
history and statusline.

The flag files carried no timestamps, so past sessions cannot be exactly
reconstructed. Fix going forward + honest fallback:

- caveman-config: recordModeChange() appends {ts, mode, prev} to
  .caveman-mode-log.jsonl on every actual flag transition (deduped,
  symlink-safe via appendFlag, best-effort)
- mode tracker + SessionStart activate hook log every flag mutation
  (set, off, NL deactivation, one-shot restore, session-start reset)
- caveman-stats joins the log timestamps against the session JSONL
  message timestamps and computes savings per mode span; the first
  row's prev covers the pre-inception span
- with no log coverage: a flag written mid-session means the earlier
  tokens have UNKNOWN mode — they are excluded and labeled, never
  guessed (no-fake-savings); with no evidence of a mid-session change,
  whole-session attribution stays (correct when the mode never changed)
- history rows, --share, and the statusline suffix all use the
  attributed figure; mixed sessions render a per-mode breakdown with
  the estimate basis stated

Old inflated history rows cannot be retroactively corrected (the data to
re-attribute them was never recorded).

Tests: mid-session activation (inflation case), mid-session deactivation
(zeroed case), tracker transition-log dedup, unattributable-prefix
exclusion. Checksums manifest refreshed for the four changed hook files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 16:09:27 +02:00
Julius BrusseeandClaude Fable 5 a8846964b5 fix(#597): ship spawn-options.js in caveman-shrink npm package
The files array omitted spawn-options.js, which index.js requires at
startup — the next npm publish would have shipped a package that crashes
with MODULE_NOT_FOUND on every launch (verified via npm pack --dry-run).

- add spawn-options.js to package.json files
- bump 0.1.1 (registry 0.1.0 is stale; the fix is unpublishable without
  a version bump anyway — NOT published here)
- add a static packaging test that walks every relative require reachable
  from the package entry points (bin + main) and fails if any resolved
  module is missing from files

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 16:03:04 +02:00
Julius BrusseeandClaude Fable 5 6919dc2c4f fix(stats): budget-saved % was output reduction mislabeled as usage share
The limit-headroom meter (8a5ab60) printed saved/(saved+output) as
'Session budget saved: ~X% of your usage this session' and 'Est. budget
saved: ~X% of tracked usage'. That ratio is algebraically the output
reduction (always ~65% in full mode) with an output-only denominator —
input + cache tokens, which dominate agentic sessions and count against
Pro/Max limits, were excluded. docs/HONEST-NUMBERS.md on this same
branch says real session-level totals land ~14-21% and below zero on
terse workloads, so the label overstated limit relief.

Fix: say only what the math computes.
- Session view: drop the budget line; the saved line now reads
  '(~X% of output)' and the footer states input/cache usage is
  unchanged.
- Lifetime view: relabel to 'Est. output reduction: ~X% (output tokens
  only, est.)'.
- budgetSavedPct -> outputReductionPct with a comment forbidding
  usage/budget relabeling; tests assert no usage/budget claim appears.
- Hook checksum refreshed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:47:02 +02:00
Julius BrusseeandClaude Fable 5 e6cce23ed6 feat: bridge to Caveman Cloud — verified savings link in README + install summary
README gets a two-line Caveman Cloud section near the bottom; the
installer summary now points at /caveman-stats (labeled estimates)
and getcaveman.dev. One link, one honest sentence each — local
numbers are estimates, Cloud measures and verifies them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:33:28 +02:00
Julius BrusseeandClaude Fable 5 ec4f664910 docs(security): no-telemetry statement, privacy facts, scanner-warning notes (#347, #504, #383, #28)
Extends the #614 SECURITY.md scaffolding: verified zero-network-after-
install statement (skill is a prompt; hooks/stats/statusline/shrink
have no http/https/fetch), exact install-time fetch list incl. the
detached curl fallback (release-tag-pinned, SHA-256-verified), what
stays local, air-gapped/enterprise note, and honest explanations for
the Defender (#383) and Snyk caveman-compress (#28) flags. Adds a
top-level README Privacy section (#504) and corrects INSTALL.md's
'no network calls' claim to name the curl-fallback exception.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:33:08 +02:00
Julius BrusseeandClaude Fable 5 d28be46e50 fix(#234): honest savings claim — ~50-65% measured output reduction, not ~75%
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
2026-07-02 15:32:47 +02:00
Julius BrusseeandClaude Fable 5 335ab56dea docs: HONEST-NUMBERS.md — when caveman saves, when caveman costs (#145, #550, #506)
Plain-truth page: measured output reduction (65% avg vs verbose,
~50% median vs terse control), the ~1-1.5k/turn input cost of the
injected rules, net-negative cases (terse Q&A per #145, per-request
billing per #506, adverse tool-side counters per #550), and how to
A/B it yourself. /caveman-stats savings labeled as estimates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:31:53 +02:00
Julius BrusseeandClaude Fable 5 8a5ab60ef0 feat(stats): limit-headroom meter — session budget saved % for subscription users
Claude Code Pro/Max users don't spend dollars, they spend a 5-hour /
weekly usage limit. /caveman-stats now also reports the savings in
their currency: 'Session budget saved: ~X% of your usage this session
(est.)', and the lifetime view gets 'Est. budget saved: ~X% of tracked
usage (est.)'.

Honesty rules baked in:
- % = saved / (saved + used) from tokens we actually count — nothing
  else. budgetSavedPct returns null (line omitted) when no savings are
  measured: honest zero, no claim.
- no plan-limit sizes are assumed or hardcoded (Anthropic doesn't
  publish token quotas); the footer says so explicitly.
- clearly labeled (est.); USD lines stay for API users.

Checksums refreshed for the caveman-stats.js change. 5 new tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:17:35 +02:00
Julius BrusseeandClaude Fable 5 bceaa0cf6d fix(#528): benchmarks/run.py reads only ANTHROPIC_API_KEY from .env.local
The old loader setdefault'ed EVERY key found in repo-root .env.local
into os.environ. Security scanners (Hermes skill scan, issue #528) flag
that as a high-severity exfiltration surface: install caveman into a
profile with secrets in .env.local and the benchmark quietly pulls all
of them into its process environment.

The benchmark only ever needs ANTHROPIC_API_KEY (anthropic.Anthropic()
reads it implicitly), so read that one key and nothing else — skip the
file entirely when the var is already set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:14:12 +02:00
Julius BrusseeandClaude Fable 5 19f7b5a0c0 fix(#565): install.ps1 works when piped to iex
irm .../install.ps1 | iex crashed with "Cannot bind argument to
parameter 'Path' because it is null": under iex there is no script
file, so $MyInvocation.MyCommand.Path is $null and Split-Path threw.
The top-level param() block also can't receive arguments through a
pipe.

- wrap all logic in Install-Caveman, invoked at the bottom with the
  automatic $args (populated for file runs, empty under iex)
- replace $MyInvocation.MyCommand.Path with $PSCommandPath, guarded —
  pipe installs skip the local-clone branch and go straight to npx
- static regression tests in tests/installer/ps1-pipe.test.mjs (CI has
  no pwsh, so pin the pipe-safety contract textually)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:13:19 +02:00
Julius BrusseeandClaude Fable 5 5f079ab7d8 fix(#571): ship commands/*.md so Claude Code discovers /caveman slash commands
Claude Code only scans commands/*.md (YAML frontmatter) for plugin slash
commands — the commands/*.toml files are the Gemini extension format and
are ignored, so /caveman, /caveman-commit, /caveman-review,
/caveman-stats and /caveman-init all returned Unknown command after a
plugin install.

- add commands/{caveman,caveman-commit,caveman-review,caveman-stats,
  caveman-init}.md mirroring the toml prompts ({{args}} -> $ARGUMENTS)
- caveman-stats.md body still matches the mode-tracker intercept regex
  so the hook injects real numbers, model computes nothing
- caveman-init.md uses the standalone-fallback body from #603
- keep every .toml — Gemini CLI extensions only read TOML
- pin the two-format contract + hook-regex + no-{{args}}-leak in
  tests/installer/slash-commands.test.mjs

The namespaced-command side of #599 (/caveman:caveman-commit etc.) is
already handled by the mode-tracker (both bare and namespaced forms,
one-shot restore) — this closes the discovery half.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011kmm2umRGb5nLxdimrwweZ
2026-07-02 15:11:52 +02:00
Julius BrusseeandClaude Fable 5 51e1990340 fix(#603): caveman-init command no longer needs the caveman repo checkout
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
2026-07-02 15:11:38 +02:00
Julius Brussee 15465d30b5 Merge PR #499: fix(shrink): restore nested protected-segment sentinels (fixes #444) 2026-07-02 15:00:17 +02:00
Julius Brussee daa8ec3732 fix: refresh src/hooks checksums.sha256 after #608/#591/#538 hook changes 2026-07-02 14:57:21 +02:00
Julius Brusseeandousamabenyounes e7ee55f936 fix(#538): handle stdin 'error' in mode-tracker (hand-applied from PR #560)
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>
2026-07-02 14:57:10 +02:00
Julius Brussee bd36cdc953 Merge PR #614: add SECURITY.md, CODE_OF_CONDUCT.md, .editorconfig repository standards 2026-07-02 14:56:16 +02:00
Julius Brussee 452055463b Merge PR #591: fix(hooks): SessionStart SKILL.md path off-by-one (fixes #587, #589) 2026-07-02 14:55:48 +02:00
Julius Brussee a2cb2f95e8 Merge PR #608: fix(hooks): NL trigger misfires + one-shot independent modes (fixes #598, #599) 2026-07-02 14:55:36 +02:00
Julius Brussee c1e1ffdd47 Merge PR #606: fix(compress): never classify build files or shebang scripts as prose (fixes #600) 2026-07-02 14:55:36 +02:00
Julius Brussee 14eea5d5ee Merge PR #586: fix(install): avoid EXDEV cross-device link on claude plugin install (fixes #585)
# Conflicts:
#	bin/install.js
2026-07-02 14:55:21 +02:00
Julius Brussee cdb0d0c613 Merge PR #604: fix(install): treat spawn ENOENT as failure, not success (fixes #592)
# Conflicts:
#	tests/installer/e2e.freshinstall.test.mjs
2026-07-02 14:54:36 +02:00
Julius Brussee 9663344b79 Merge PR #607: fix(settings): string-aware trailing-comma removal in JSONC reader (fixes #595) 2026-07-02 14:52:58 +02:00
Julius Brussee d900443b84 Merge PR #605: fix(uninstall): match managed hook basenames, not substring (fixes #593) 2026-07-02 14:52:58 +02:00
Julius Brussee 5b875b8e2e Merge PR #609: fix(openclaw): stray SOUL.md marker no longer chains into data loss (fixes #596)
# Conflicts:
#	tests/installer/e2e.freshinstall.test.mjs
2026-07-02 14:52:49 +02:00
Julius Brussee 79a14ee79f Merge PR #610: fix(opencode): --force migrates legacy AGENTS.md, never wipes it (fixes #594) 2026-07-02 14:51:43 +02:00
Shiva Vinodkumar c0e3c1e140 Add/Update .editorconfig to improve repository standards 2026-07-02 17:57:43 +05:30
Shiva Vinodkumar 79285a4c00 Add/Update SECURITY.md to improve repository standards 2026-07-02 17:57:40 +05:30
Shiva Vinodkumar 7dc87d0a32 Add/Update CODE_OF_CONDUCT.md to improve repository standards 2026-07-02 17:57:38 +05:30
AmirF194 516f917619 fix(opencode): --force migrates legacy AGENTS.md, never wipes it
--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
2026-07-01 23:52:36 -06:00
AmirF194 ae02d2a560 fix(openclaw): stray SOUL.md marker no longer chains into data loss
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
2026-07-01 23:50:28 -06:00
AmirF194 2fb7c91183 fix(hooks): NL trigger misfires + one-shot independent modes
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
2026-07-01 23:47:59 -06:00
AmirF194 704a460ef8 fix(settings): string-aware trailing-comma removal in JSONC reader
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
2026-07-01 23:44:57 -06:00
AmirF194 4dad1afe84 fix(compress): never classify build files or scripts as prose
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
2026-07-01 23:36:17 -06:00
AmirF194 70ea40dced fix(uninstall): match managed hook basenames, not substring
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
2026-07-01 23:34:41 -06:00
AmirF194 959b943ad4 fix(install): treat spawn ENOENT as failure, not success
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
2026-07-01 23:31:35 -06:00
AmirF194 6d71b9ec6f fix(hooks): SessionStart SKILL.md path off-by-one
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
2026-07-01 22:42:53 -06:00
Paul Jobson e0bb39f267 Fix for: https://github.com/JuliusBrussee/caveman/issues/585
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
2026-06-30 21:44:27 -04:00