feat: /caveman <level> persists across sessions (symmetric)#450
Open
dead-developers wants to merge 2 commits into
Open
feat: /caveman <level> persists across sessions (symmetric)#450dead-developers wants to merge 2 commits into
dead-developers wants to merge 2 commits into
Conversation
The slash command previously only switched intensity for the current
session. Saying "stop caveman" or running `/caveman off` would silence
the bot mid-conversation but the SessionStart hook would re-activate
caveman on the next session resume — even after the user explicitly
opted out.
This surprised users (verified against real chats: "i thought i turned
caveman off?" / "the caveman skill itself says you can turn it off and
that should persist??"). SKILL.md said level persists "until changed or
session end", but that's a documentation of the limitation, not a
desirable behavior for the off case.
The plumbing for persistent off already exists upstream:
- `src/hooks/caveman-config.js::getDefaultMode()` reads
`~/.config/caveman/config.json` (or the platform equivalent) and
honors a `defaultMode` field.
- `src/hooks/caveman-activate.js` skips activation entirely when
`defaultMode === "off"`.
What was missing: a path from the user typing `/caveman off` to that
config file getting written. The slash command's prompt didn't mention
the config layer; users had to know about it and edit the JSON by hand.
This commit closes that gap:
1. `commands/caveman.toml` — the prompt now special-cases `off` (and
accepts the synonyms `stop`, `disable`, `persist-off`). For those
args the model writes `{"defaultMode": "off"}` to the platform's
caveman config file, merging into any existing JSON rather than
clobbering. All other args (lite/full/ultra/wenyan-*) keep the
existing session-only behavior.
2. `skills/caveman/SKILL.md` — the Persistence and Boundaries sections
now make the session-vs-persistent distinction explicit and document
the new `/caveman off` behavior plus how to re-enable.
The change is additive — any user who never types `off` sees no
behavior change.
Re-enable path: `/caveman full` (or another non-off level) puts the
session back in caveman mode immediately. To clear the persistent-off
config, the user can edit `~/.config/caveman/config.json` and remove
the `defaultMode` field, or delete the file entirely.
The first revision of this PR only persisted `/caveman off`. Other levels (full/lite/ultra/wenyan-*) stayed session-scope. That's asymmetric and surprising: after a persistent off, running `/caveman full` would re-enable caveman for the current session but leave `defaultMode: "off"` in the config, so the next session resume was silent again. Users had to know to delete the config file. Symmetric semantics are cleaner: any `/caveman <level>` writes `defaultMode: "<level>"` to the config file. `off` saves off; `full` saves full; etc. The persistent choice is whatever the user last asked for. Falling back to the built-in default of `full` is a single action: delete the config file. Changes: - `commands/caveman.toml`: rewrote the prompt around a single "resolve + persist + act" flow. Accepts aliases (stop/disable/persist-off → off, on/enable → full, wenyan → wenyan-full) and rejects unknown levels loudly with the valid set. Writes the resolved level to the config every time, merging into any existing JSON. - `skills/caveman/SKILL.md`: updated Persistence + Boundaries sections to describe symmetric persistence and call out that the slash command itself is the persistence mechanism — no separate "/caveman persist" or hand-editing required. The hook layer (`src/hooks/caveman-config.js`, `caveman-activate.js`) already handles every `defaultMode` value this command might write — no hook changes needed.
dead-developers
added a commit
to dead-developers/vibespeak
that referenced
this pull request
May 27, 2026
Caveman shipped a persistence fix (JuliusBrussee/caveman#450) that lets `/caveman <level>` write a config file the SessionStart hook honors on every resume — symmetric in both directions, including off. Vibespeak has the structurally identical bug today: `/vibespeak off` is session-only, the SessionStart hook re-fires the announcement on every reload, and SKILL.md claims "off" persists when it doesn't. This file is a self-contained porting guide for the next Claude Code session that opens this repo. It maps each caveman PR change to its vibespeak equivalent, lists the files to add and modify, and provides the test plan + suggested commit message. Once the work lands, delete the file — README + SKILL.md are the durable docs.
dead-developers
pushed a commit
to dead-developers/vibespeak
that referenced
this pull request
May 27, 2026
Mirrors the persistence fix shipped for the sibling caveman plugin (JuliusBrussee/caveman#450). /vibespeak off and the level switches now write {"defaultMode": "<level>"} to ~/.config/vibespeak/config.json (or the platform equivalent). The SessionStart hook reads this and either skips activation (off) or applies the saved level on every resume. Same shape as the caveman PR: add hooks/vibespeak-config.js with getDefaultMode() + safeWriteFlag(), gate vibespeak-activate.js on the saved mode, teach commands/vibespeak{,-off,-short,-chatty}.md to write the config when invoked. Symmetric in both directions - /vibespeak off saves off, /vibespeak normal saves normal. Clearing the persistent choice is a single action: delete the config file. Test plan executed before commit: 1. No config: hook emits VIBESPEAK MODE ACTIVE reminder (default). 2. {"defaultMode":"off"} written: hook outputs "OK" only, no reminder. 3. Config deleted: reverts to default behavior. 4. VIBESPEAK_DEFAULT_MODE=off env var: honored over config file. 5. Pre-existing fields in config: preserved (hook only reads defaultMode). 6. Invalid defaultMode value: falls through to built-in "normal" default. See HANDOFF-persistence.md for the full porting notes (deleted in a follow-up commit once the work landed).
Author
|
fixes persistence. turn it on, and it stays on. turn it off and it stays off. across all sessions. currently using. hope it helps anyone ¯_(ツ)_/¯ |
dead-developers
added a commit
to dead-developers/vibespeak
that referenced
this pull request
Jun 7, 2026
Mirrors the persistence fix shipped for the sibling caveman plugin (JuliusBrussee/caveman#450). /vibespeak off and the level switches now write {"defaultMode": "<level>"} to ~/.config/vibespeak/config.json (or the platform equivalent). The SessionStart hook reads this and either skips activation (off) or applies the saved level on every resume. Same shape as the caveman PR: add hooks/vibespeak-config.js with getDefaultMode() + safeWriteFlag(), gate vibespeak-activate.js on the saved mode, teach commands/vibespeak{,-off,-short,-chatty}.md to write the config when invoked. Symmetric in both directions - /vibespeak off saves off, /vibespeak normal saves normal. Clearing the persistent choice is a single action: delete the config file. Test plan executed before commit: 1. No config: hook emits VIBESPEAK MODE ACTIVE reminder (default). 2. {"defaultMode":"off"} written: hook outputs "OK" only, no reminder. 3. Config deleted: reverts to default behavior. 4. VIBESPEAK_DEFAULT_MODE=off env var: honored over config file. 5. Pre-existing fields in config: preserved (hook only reads defaultMode). 6. Invalid defaultMode value: falls through to built-in "normal" default. See HANDOFF-persistence.md for the full porting notes (deleted in a follow-up commit once the work landed).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's broken today
The slash command
/cavemanonly switches caveman for the current session. The SessionStart hook re-activates whatever the SKILL/hook default is on every session resume — even after the user explicitly asked for a different level (most painfully,off).Verbatim user reactions from real sessions:
SKILL.md said "Level persist until changed or session end" — which is a documentation of the limitation, not a desirable property. Persistent levels via
~/.config/caveman/config.jsonalready work (getDefaultMode()reads it,caveman-activate.jshonorsdefaultMode: "off"and would honor any other valid level the user wrote there). But the user-facing slash command never wrote that file, so anyone who didn't know about the config layer just saw the bug.What this PR changes
/caveman <level>now writesdefaultMode: "<level>"to the platform's caveman config file. The choice persists across sessions in both directions —/caveman offsaves off,/caveman fullsaves full, etc. The persistent choice is whatever the user last asked for. Clearing it is a single action: delete the config file.commands/caveman.toml— replaced the one-liner prompt with a "resolve → persist → act" flow:stop/disable/persist-off→off,on/enable→full,wenyan→wenyan-full). Reject unknown levels loudly.{"defaultMode": "<level>"}into~/.config/caveman/config.json(or%APPDATA%\caveman\config.jsonon Windows, or$XDG_CONFIG_HOME/caveman/...). Existing JSON fields preserved.skills/caveman/SKILL.md— Persistence and Boundaries sections rewritten to describe symmetric persistence and clarify that the slash command itself is the persistence mechanism (no separate/caveman persistand no hand-editing JSON required).Why symmetric
The first revision of this PR (90cd5ac) only persisted
off— other levels stayed session-scope. That produced the next confusing UX: after/caveman off, running/caveman fullre-enabled caveman for the current session, but the config still saidoff, so the next session resume was silent again. Users had to know to delete the config file by hand.The current revision (66f98a8) makes it symmetric. The mental model becomes: "
/caveman <level>sets your caveman level. It persists. Delete the config to reset."Compatibility
The hook layer already handles every
defaultModevalue this command writes — nocaveman-config.jsorcaveman-activate.jschanges. Users who never type/cavemansee no behavior change.Users who were relying on slash commands being session-scope (probably few — there's no obvious upside) can still set
CAVEMAN_DEFAULT_MODEin their shell env, which overrides the config file per the existing precedence ingetDefaultMode().Test plan
/caveman offin a session: bot drops caveman style; config file contains{"defaultMode": "off"}.OKonly, no "CAVEMAN MODE ACTIVE" reminder./caveman fullin that session: caveman resumes; config now{"defaultMode": "full"}./caveman ultra: caveman switches to ultra; config now{"defaultMode": "ultra"}. Next session is ultra./caveman nonsense: command refuses with the valid level list, no file write.~/.config/caveman/config.json(e.g. user-addedtheme: ...) are preserved after every write.