diff --git a/CLAUDE.md b/CLAUDE.md index 6a9cac65..1d0724ea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -59,6 +59,7 @@ Overwritten by CI on push to main when sources change. Edits here lost. `.github/workflows/sync-skill.yml` triggers on main push when `skills/caveman/SKILL.md` or `rules/caveman-activate.md` changes. What it does: + 1. Copies `skills/caveman/SKILL.md` to all agent-specific SKILL.md locations 2. Rebuilds `caveman.skill` as a ZIP of `skills/caveman/` 3. Rebuilds all agent rule files from `rules/caveman-activate.md`, prepending agent-specific frontmatter (Cursor needs `alwaysApply: true`, Windsurf needs `trigger: always_on`) @@ -88,12 +89,14 @@ All hooks honor `CLAUDE_CONFIG_DIR` for non-default Claude Code config locations ### `hooks/caveman-config.js` — shared module Exports: + - `getDefaultMode()` — resolves default mode from `CAVEMAN_DEFAULT_MODE` env var, then `$XDG_CONFIG_HOME/caveman/config.json` / `~/.config/caveman/config.json` / `%APPDATA%\caveman\config.json`, then `'full'` - `safeWriteFlag(flagPath, content)` — symlink-safe flag write. Refuses if flag target or its immediate parent is a symlink. Opens with `O_NOFOLLOW` where supported. Atomic temp + rename. Creates with `0600`. Protects against local attackers replacing the predictable flag path with a symlink to clobber files writable by the user. Used by both write hooks. Silent-fails on all filesystem errors. ### `hooks/caveman-activate.js` — SessionStart hook Runs once per Claude Code session start. Three things: + 1. Writes the active mode to `$CLAUDE_CONFIG_DIR/.caveman-active` via `safeWriteFlag` (creates if missing) 2. Emits caveman ruleset as hidden stdout — Claude Code injects SessionStart hook stdout as system context, invisible to user 3. Checks `settings.json` for statusline config; if missing, appends nudge to offer setup on first interaction @@ -105,7 +108,9 @@ Silent-fails on all filesystem errors — never blocks session start. Reads JSON from stdin. Three responsibilities: **1. Slash-command activation.** If prompt starts with `/caveman`, writes mode to flag file via `safeWriteFlag`: + - `/caveman` → configured default (see `caveman-config.js`, defaults to `full`) +- `/caveman status` → read-only query, emits current mode via `hookSpecificOutput`, no flag write - `/caveman lite` → `lite` - `/caveman ultra` → `ultra` - `/caveman wenyan` or `/caveman wenyan-full` → `wenyan` @@ -122,6 +127,7 @@ Reads JSON from stdin. Three responsibilities: ### `hooks/caveman-statusline.sh` — Statusline badge Reads flag file at `$CLAUDE_CONFIG_DIR/.caveman-active`. Outputs colored badge string for Claude Code statusline: + - `full` or empty → `[CAVEMAN]` (orange) - anything else → `[CAVEMAN:]` (orange) @@ -181,6 +187,7 @@ For agents without hook systems, minimal always-on snippet lives in README under ## Evals `evals/` has three-arm harness: + - `__baseline__` — no system prompt - `__terse__` — `Answer concisely.` - `` — `Answer concisely.\n\n{SKILL.md}` diff --git a/hooks/caveman-mode-tracker.js b/hooks/caveman-mode-tracker.js index 50a665fd..a76f8861 100644 --- a/hooks/caveman-mode-tracker.js +++ b/hooks/caveman-mode-tracker.js @@ -45,7 +45,20 @@ process.stdin.on('end', () => { } else if (cmd === '/caveman-compress' || cmd === '/caveman:caveman-compress') { mode = 'compress'; } else if (cmd === '/caveman' || cmd === '/caveman:caveman') { - if (arg === 'lite') mode = 'lite'; + if (arg === 'status') { + // Status query — don't change mode, just report current state + const current = readFlag(flagPath); + const statusMsg = current + ? 'CAVEMAN STATUS: Active mode is "' + current + '". Tell the user their current caveman mode is: ' + current + : 'CAVEMAN STATUS: Caveman is not active. Tell the user caveman mode is currently off.'; + process.stdout.write(JSON.stringify({ + hookSpecificOutput: { + hookEventName: "UserPromptSubmit", + additionalContext: statusMsg + } + })); + return; // skip all other processing — status is read-only + } else if (arg === 'lite') mode = 'lite'; else if (arg === 'ultra') mode = 'ultra'; else if (arg === 'wenyan-lite') mode = 'wenyan-lite'; else if (arg === 'wenyan' || arg === 'wenyan-full') mode = 'wenyan'; diff --git a/skills/caveman-help/SKILL.md b/skills/caveman-help/SKILL.md index 078e4879..0348bd9d 100644 --- a/skills/caveman-help/SKILL.md +++ b/skills/caveman-help/SKILL.md @@ -32,6 +32,10 @@ Mode stick until changed or session end. | **caveman-compress** | `/caveman:compress ` | Compress .md files to caveman prose. Saves ~46% input tokens. | | **caveman-help** | `/caveman-help` | This card. | +## Check Active Mode + +`/caveman status` — show which mode active right now (lite/full/ultra/wenyan-*/off). + ## Deactivate Say "stop caveman" or "normal mode". Resume anytime with `/caveman`. diff --git a/skills/caveman/SKILL.md b/skills/caveman/SKILL.md index 2ab498bd..c29fdd2a 100644 --- a/skills/caveman/SKILL.md +++ b/skills/caveman/SKILL.md @@ -14,7 +14,7 @@ Respond terse like smart caveman. All technical substance stay. Only fluff die. ACTIVE EVERY RESPONSE. No revert after many turns. No filler drift. Still active if unsure. Off only: "stop caveman" / "normal mode". -Default: **full**. Switch: `/caveman lite|full|ultra`. +Default: **full**. Switch: `/caveman lite|full|ultra`. Check: `/caveman status`. ## Rules