Skip to content
13 changes: 8 additions & 5 deletions docs/claude/global-claude-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ In addition to the terminal panel, the hook always injects a message into Claude

The source name is included so a stale mirror is visible at a glance: `GitHub` for the raw-URL method; for the git-fetch fallback the host is inferred from the local clone's `origin` URL (`GitHub` / `GitLab` / `Codeberg` / `git origin`).

**Update required** (prominently displayed, cannot be missed):
**Update required** (prominently displayed, cannot be missed; short since v2.8.0):

> NEW SESSION — GLOBAL CLAUDE SETTINGS: UPDATE REQUIRED
> Installed: v0.1.0 (outdated) | Latest: v1.0.0 (on origin/main)
> Say "update my global settings to 1.0.0" to apply the update.
> Afterwards, relock with (A) full lock, or (B) lock without settings.json (keeps the VSCode model picker working, but the main file loses the kernel lock and is then guarded only by the regex hooks).
> GLOBAL CLAUDE SETTINGS: UPDATE REQUIRED
> Installed: v2.7.2 (outdated). Latest: v2.8.0 (on origin/main)
> What this update adds: v2.8.0 — one sentence from `global-settings/CHANGELOG.md`
> Unlocking takes two commands, `sudo chattr -i` and `chmod u+w`. Afterwards you must re-apply `sudo chattr +i`.
> Ask me how to update for the full steps, or for more details about what these changes do.

The notice returns in every new session until you update, so it is short by default. Ask how to update and Claude shows the full steps: unlock, verify, the phrase to say ("update my global settings to 2.8.0"), and the relock with (A) full lock or (B) lock without settings.json (keeps the VSCode model picker working, but the main file loses the kernel lock and is then guarded only by the regex hooks). Ask about the changes and Claude looks up the commits and pull requests behind each version and links them. See [A short notice, and what the update adds](../../global-settings/README.md#a-short-notice-and-what-the-update-adds-v280).

The relock step always offers both options, each with its own command, and says why (B) is there. It is the same choice described under [Troubleshooting → keep `settings.json` unlocked](#model-or-the-model-picker-fails-with-eperm-operation-not-permitted-open-claudesettingsjson). Since v2.5.2 it is part of the update message itself, not a note in the docs.

Expand Down
161 changes: 161 additions & 0 deletions global-settings/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Global settings changelog

One entry per released `VERSION`, newest first. The heading is the version number and the date it reached `main` (`## X.Y.Z — YYYY-MM-DD`). The first line under it is **one sentence** that says what the update adds or fixes, in words a developer understands without opening the code.

`check-settings-version.sh` shows that sentence in the session-start UPDATE REQUIRED notice for every version between the installed one and the latest (since v2.8.0). The details live in the commit and the pull request. When a user asks for more, Claude looks those up from the version number. This file is not installed into `~/.claude/` and needs no `VERSION` bump of its own, but every `VERSION` bump needs an entry here (see [Bumping the version](README.md#️-bumping-the-version--required-on-every-change)).

Versions 1.0.0 to 1.3.0 were released from the now archived `ConductionNL/claude-code-config` repository, and their date is the day they reached that repository's `main`. A few versions reached `main` together with the next one instead of on their own; their entry says so, and carries the date they reached `main`. Numbers that `main` never carried (1.5.0, 1.5.2, 1.5.3, 2.4.3) are listed under a `###` heading with a note on where their changes went. The update notice reads only `##` headings, so it skips them.

## 2.8.0 — 2026-10-02

The update notice is now short at session start and says in one sentence what each pending version adds. Ask Claude how to update for the full steps, or for the details of the changes with links to the pull requests.

## 2.7.3 — 2026-10-02

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Concern — The 2.7.3 entry makes #808 depend on #813 merging first

This changelog carries a ## 2.7.3 entry for #813, which is still open (main is at 2.7.2; VERSION here goes to 2.8.0, and this PR does not touch block-write-commands.sh). If #808 merges first, a 2.7.2 install updating to 2.8.0 gets this sentence in its update notice for a hard-deny change it does not receive, and #813 would then set VERSION back to 2.7.3, below the installed 2.8.0. The PR body says the entry belongs to #813 but not that #813 has to merge first. Fix: state the merge order in the body (merge #813, then update this branch). If #808 has to go first, move this entry to #813 and have #813 bump to 2.8.1.

Verification: git show origin/main:global-settings/VERSION → 2.7.2; gh pr view 813 -R ConductionNL/.github --json state,files → OPEN, bumps VERSION to 2.7.3 and changes block-write-commands.sh; this PR's file list has no block-write-commands.sh.


A hard block anywhere in a chained command now wins over a prompt for another part of it, and a git alias that pushes (or a command that defines one) needs the push phrase just like `git push`.

## 2.7.2 — 2026-10-01

An unauthorized `git push` is now denied in every form, including `git -C <path> push`, `git -c k=v push` and a push chained after a command Claude was asked to approve.

## 2.7.1 — 2026-10-01

Picking a model or an effort level no longer reports `settings.json` as out of sync, because the keys Claude Code itself writes for that choice are ignored in the file check.

## 2.7.0 — 2026-10-01

The update notice now installs every hook `settings.json` registers instead of a hand-kept list, and a session start checks that the installed files match the canonical copies.

## 2.6.0 — 2026-09-29

Changes to production (`kubectl`, `oc` or `helm` writes against a `*-prod` namespace or context) are now hard-blocked, and text inside commit messages or heredocs no longer counts as a command.

## 2.5.2 — 2026-09-28

The relock step now offers two options, a full lock or a lock without `settings.json`, so the VSCode model picker can keep working.

## 2.5.1 — 2026-09-28

A push phrase typed next to a pasted image is recognised again, and a skill's text can no longer authorize a push by itself.

## 2.5.0 — 2026-09-12

Hand-rolled waiting (CI watch commands, poll loops, long sleeps and idle heartbeats) is now blocked.

## 2.4.6 — 2026-09-11

The unlock step now includes the `chmod u+w` that every update after the first needs, so an update no longer stalls halfway with `Permission denied`.

## 2.4.5 — 2026-09-10

Seven `Write(...)` deny rules that Claude Code never applied are removed; the `Edit(...)` rules next to them already cover every file-editing tool.

## 2.4.4 — 2026-09-10

The in-place edit guard no longer hard-denies a chain of harmless commands because the tool, its `-i` flag and the protected path came from different commands.

### 2.4.3 — never released

Two pull requests both took 2.4.3 on 2026-09-08, and `main` never carried it. Each branch was renumbered when `main` was merged into it, before its own merge on 2026-09-10. The destructive-guard fix ([#720](https://github.com/ConductionNL/.github/pull/720)) shipped as 2.4.4 and the removal of the inert `Write(...)` deny rules ([#716](https://github.com/ConductionNL/.github/pull/716)) as 2.4.5.

## 2.4.2 — 2026-09-08

The update notice now also installs `sound-notify.sh` and `user-hooks-dispatch.sh`, which it had left out since they were added.

## 2.4.1 — 2026-09-08

The `mcpServers` block is removed from `settings.json`, because Claude Code never read it there and the browsers it listed never loaded.

## 2.4.0 — 2026-09-04

You can register your own hooks in `~/.claude/user-hooks.json`, and they survive every settings update.

## 2.3.1 — 2026-09-04

The "up to date" message names its source ("via GitHub") again.

## 2.3.0 — 2026-09-04

The canonical source moves back from Codeberg to GitHub (`ConductionNL/.github`), for the version check and for the update itself.

## 2.2.2 — 2026-07-10

The permission-request sound now plays, because it is wired to the event Claude Code actually fires for a permission dialog.

## 2.2.1 — 2026-07-09

Short notification sounds are no longer cut off when the hook exits.

## 2.2.0 — 2026-07-09

Optional notification sounds when Claude asks a question, needs a permission or finishes, off by default and set in `~/.claude/sound-config.sh`.

## 2.1.0 — 2026-06-12

`--legacy-peer-deps` is hard-denied for npm, pnpm, yarn and bun installs.

## 2.0.0 — 2026-06-12

Breaking: the canonical source moves from GitHub to Codeberg (`codeberg.org/Conduction/.github`), so the version check and the update pull from there.

It was committed on 2026-05-29 on the `feature/codeberg-global-settings-flip` branch and reached `main` in the same merge as 2.1.0, so `main` went from 1.7.0 straight to 2.1.0.

## 1.7.0 — 2026-05-13

Claude's config files get a kernel-level lock (`chattr +i`), and a new hook stops the Write and Edit tools from changing them.

## 1.6.1 — 2026-05-12

A push phrase now stays valid after Claude has run other tools in the same turn.

## 1.6.0 — 2026-05-04

Tighter guards on writes to `~/.claude/`, `settings-repo-path` is no longer unlocked during an update, and the guard hook gets a test suite.

### 1.5.3 — never released

This number appears only in the commit message of the 1.6.0 change ("Bump VERSION 1.5.3 → 1.6.0"). That commit changed `VERSION` from 1.5.1 to 1.6.0, and no commit with `VERSION` 1.5.3 exists, so its changes are the ones listed under 1.6.0.

### 1.5.2 — never released

The push-phrase fix first took 1.5.2 on 2026-05-07, on a branch of a copy of `global-settings` kept in another repository. That branch was never merged. The same fix reached `main` here as 1.6.1 ([#43](https://github.com/ConductionNL/.github/pull/43)).

## 1.5.1 — 2026-05-04

You can track a branch other than `main` with `~/.claude/settings-repo-ref`, and destructive commands such as `sudo`, `rm -rf /`, `git reset --hard` and `gh pr merge` are denied.

It reached `main` together with 1.6.0, in [#22](https://github.com/ConductionNL/.github/pull/22), so `main` went from 1.4.0 straight to 1.6.0.

### 1.5.0 — never released

On 2026-04-13 a copy of `global-settings` kept in another repository took 1.5.0 for the destructive-command deny rules and went to 1.5.1 the same day. This repository went from 1.4.0 straight to 1.5.1, so those deny rules shipped as part of 1.5.1.

## 1.4.0 — 2026-04-16

The version check reads the latest version straight from GitHub (`~/.claude/settings-repo-url`), with the local clone as fallback.

## 1.3.0 — 2026-04-08

Write requests through `curl` are caught in more forms, including combined flags such as `-sX POST` and a `curl` behind a pipe.

## 1.2.2 — 2026-04-03

`gh pr`, `gh issue`, `gh repo`, `gh release`, `gh workflow` and `gh run` write commands now ask for approval, also when chained after a read.

## 1.2.1 — 2026-04-03

More read-only commands run without a prompt, and any output redirect to a file now asks for approval.

It reached `main` of `claude-code-config` together with 1.2.2 (PR #20 there), so that `main` went from 1.2.0 straight to 1.2.2.

## 1.2.0 — 2026-03-28

Claude can no longer change its own settings files or make them writable, and the version check retries once when the remote is briefly unreachable.

## 1.1.0 — 2026-03-28

Commands that would leave the WSL workspace (Windows drives under `/mnt/<drive>/`, `.exe` programs, `wsl`) are hard-blocked.

## 1.0.0 — 2026-03-27

The first mandatory, versioned global settings: a shared `settings.json`, the write-guard hook and a session-start version check.
13 changes: 13 additions & 0 deletions global-settings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Current version: see [`VERSION`](VERSION)
| `sound-notify.sh` | `~/.claude/hooks/sound-notify.sh` | Optional notification-sound wrapper. Reads `~/.claude/sound-config.sh` and plays a sound on question / permission / stop events. Silent by default (added in v2.2.0) |
| `user-hooks-dispatch.sh` | `~/.claude/hooks/user-hooks-dispatch.sh` | Reads `~/.claude/user-hooks.json` and executes per-user hooks for every Claude Code event. Lets each user register private hooks that survive a settings update (added in v2.4.0) |
| `VERSION` | `~/.claude/settings-version` | Installed version tracker (semver) |
| `CHANGELOG.md` | not installed | One sentence per version; shown in the update notice as "What this update adds" (added in v2.8.0) |
| `settings-repo-url.example` | `~/.claude/settings-repo-url` | GitHub repo slug for online version checking |
| `settings-repo-ref.example` | `~/.claude/settings-repo-ref` | Branch to track (defaults to `main` when absent; the GitHub raw URL uses `raw.githubusercontent.com/<slug>/<ref>/`) |
| `sound-config.sh.example` | `~/.claude/sound-config.sh` (optional) | Opt-in sound configuration. Only install if you want notification sounds. User-editable — **not** `chattr +i`-locked |
Expand Down Expand Up @@ -275,6 +276,16 @@ When the installed version already matches the online one, the hook also compare

A missing or changed file turns the session-start message into **FILES OUT OF SYNC**. It names the files, the phrase for step 3 is **"repair my global settings"**, and the unlock, contract and relock steps are the same as for an update. Only the affected files are rewritten; `VERSION` is left alone. On the GitHub path the check costs two extra fetches per session (`settings.json`, then every registered hook in one `curl` call); on the git-fetch path it costs none. A canonical file that could not be fetched is reported as not verified, never as fine.

### A short notice, and what the update adds (v2.8.0)

The update notice comes back in every new session until you update, so Claude shows it **short** at session start: the header, the installed and latest version, what the update adds, a reminder that unlocking takes two commands (`sudo chattr -i` and `chmod u+w`) and relocking one (`sudo chattr +i`), and that you can ask for more. Ask Claude how to update and you get the full steps 1 to 4 above, with every command in its own block and both relock options. After the update Claude always shows the relock step, whether or not you asked for the full steps. The FILES OUT OF SYNC notice is still shown in full.

The UPDATE REQUIRED notice has a **What this update adds** block under the version lines: one sentence per version after the installed one, up to the latest, newest first, taken from [`CHANGELOG.md`](CHANGELOG.md). It shows at most five versions and counts the rest. A version without an entry is listed as such, not left out. If `CHANGELOG.md` cannot be fetched, the block says so, and the update itself is unaffected. On the GitHub path this costs one extra fetch, and only when an update is pending.

Ask Claude for more details and it looks up the commit that set `VERSION` to each listed version, reads its message, changed files and pull request, and explains the change with a link to that pull request (or the commit when none merged it). The notice gives Claude the `gh api` and `git log` commands for this, so the answer comes from the history, not from the summary sentence.

The notice is printed by the *installed* hook, so the short form and the block first appear when you update **from** v2.8.0 or later to a newer version.

### The update contract

Claude reinstalls each file with `chmod 555` (hooks) or `chmod 444` (`settings-version`) because `block-write-commands.sh` permits only those two modes on protected paths — `chmod 644`, `chmod u+w` and `chattr` are all hard-denied to Claude, by design, so it can never widen its own access. The consequence is the one step 1 covers: read-only mode is *sticky* across updates and only you can clear it.
Expand Down Expand Up @@ -334,6 +345,8 @@ Now `ask()` only records its reason. Every guard runs, a hard deny anywhere wins

Failing to bump the version means users will not be warned to update, and their installed settings will silently fall behind.

**Every bump also adds an entry to [`CHANGELOG.md`](CHANGELOG.md)**: a `## X.Y.Z — YYYY-MM-DD` heading, dated the day the version reaches `main`, with one sentence under it that says what the update adds, in words a developer understands without opening the code. The update notice shows that sentence. A missing entry, or a heading without a date, fails the version-check tests.

Changes to `global-settings/README.md` alone do **not** require a bump: nothing installed changes, so a bump would push every developer through the unlock/relock cycle to copy a file they don't have. Documentation under `docs/claude/` is likewise never a trigger.

Semver rules:
Expand Down
2 changes: 1 addition & 1 deletion global-settings/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.7.3
2.8.0
Loading
Loading