Skip to content
Back to skills

Dotclaude

ASecurity

How the public mirror of ~/.claude works. Load it before changing ~/.claude/CLAUDE.md, settings.json, a hook, a mirrored skill, a mod, a workflow or the presence listener, before adding a hook, skill, command or agent under ~/.claude, before changing anything in ~/Projects/dotclaude or a directory it mirrors into, and when setting up a new machine. It covers sync.sh with its scan and mirrors, the commit and its identity, the pass over open PRs after every push, and what the repository may con...

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 6, 2026
ai-agentsgoshellbashgit

Works with

  • claude code
  • cli

Security analysis

A100/100

Scanned November 11, 2026

npx -y skills add zululima/dotclaude --skill dotclaude --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dotclaude?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dotclaude
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zululima-dotclaude/badge)](https://www.skillsdirectory.com/skills/zululima-dotclaude)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dotclaude
description: How the public mirror of ~/.claude works. Load it before changing ~/.claude/CLAUDE.md, settings.json, a hook, a mirrored skill, a mod, a workflow or the presence listener, before adding a hook, skill, command or agent under ~/.claude, before changing anything in ~/Projects/dotclaude or a directory it mirrors into, and when setting up a new machine. It covers sync.sh with its scan and mirrors, the commit and its identity, the pass over open PRs after every push, and what the repository may contain.
---

# Public configuration repository

These procedures sit beside the Public configuration repository rules in `~/.claude/CLAUDE.md`,
which still hold: the user's go before a change to that file or a mirrored skill, and the `sync.sh`
run and its commit in the same turn.

## Writing a rule

- A `dotclaude` ask is how the user asks for a rule change, alone or inside a prompt about other
  work. The Prompt feedback section never calls it unclear or a second job, and a rewritten prompt
  keeps it.
- A rule in a mirrored file states what holds now. The incident behind it, its date and the wording
  it replaced go into the commit message, never into the file, and a changed rule is rewritten in
  place rather than followed by a note on what it used to say.
- A rule written from a correction reaches as far as the reason in the user's words, never only as
  far as the form of their example, and adds no condition they did not give: a wish to paste a page
  into Firefox covers every file Firefox shows, not HTML alone, and a wish to type less covers the
  fewest letters that work, not a whole word. What the session already knows about that reach, such
  as a search that matches any part of a name, goes into the rule and into the work redone under it.
  A question on the rule's scope offers that full reach as one option, never only narrower ones.
- A session that adds or changes a rule in `~/.claude/CLAUDE.md` finds the memory notes that state
  the old behaviour, by what the rule governs rather than the words of the incident behind it, and
  rewrites or deletes them in the same turn. After each edit of that file the `PostToolUse` hook
  `~/.claude/hooks/claude-md-memory-index.sh` lists every project's memory index lines.
- An edit made to `~/.claude/CLAUDE.md`, to `~/.claude/skills/pull-requests/SKILL.md` or to the
  `commit-messages` skill from another session reaches a running one through
  `~/.claude/hooks/claude-md-changed.sh`, in place of a re-read before a commit message or a PR
  body: once one differs from the copy the session last saw, the hook adds the diff to the next
  prompt, or to the next `gh pr` create, edit or merge, and for `~/.claude/CLAUDE.md` and the
  `commit-messages` skill also to the next commit or `sync.sh --commit`. It blocks that command once
  with the diff, unless every change lies in a section of `~/.claude/CLAUDE.md` whose rules shape no
  commit message or PR body, such as Running things.

## The checkout

- The checkout at `~/Projects/dotclaude` holds the mirrored files, each named in the `mirrored` list
  of `sync.sh`, and its own `README.md`, `docs/`, licence texts and `sync.sh`.
- The mirrored copy keeps the name `CLAUDE.md`. A session started inside the checkout would load it
  as project memory on top of the global file, so `settings.json` lists `**/dotclaude/CLAUDE.md` in
  `claudeMdExcludes`. Claude Code reads no other mirrored file from a repository root, so nothing
  else needs a different name.
- When a new file under `~/.claude` would help other people (a hook, a skill, a command, an agent),
  add it to the list in `sync.sh` first and give it an entry in the checkout's
  `docs/what-you-get.md`. The `README.md` stays one screen long: it says what the repository is,
  shows its parts and links the docs.

## sync.sh

- `sync.sh` pulls the checkout fast-forward, then reconciles each mirrored file: one the pull
  changed is installed into `~/.claude`, one changed locally is copied into the checkout, and one
  changed on both sides stops the run for a manual merge. It then scans everything that would be
  published for the local user name, home paths, the git identity, email addresses and
  credential-shaped strings, exits non-zero on a hit, and prints `git status`. Literals listed one
  per line in `sync-allow.txt`, when that file exists, are exempt. Literals listed one per line in
  the git-ignored `sync-deny.txt` fail the scan in any case, and `sync.sh --check-message`, run from
  the untracked commit-msg hook of the checkout and of each mirror, holds commit messages to the
  same list, less the literals that `sync-deny-skip.txt` in that repository's git directory lists,
  such as an employer's name in a repository the employer owns.
- A file sync that shares `~/.claude` between two computers brings a mirrored file over from the
  one that committed it before the pull here, a run copies it into the checkout, and git then
  refuses the pull over that copy. `sync.sh` gets past that by itself: when every file in the
  pull's way is a mirrored file whose checkout copy holds the same bytes as its `~/.claude` copy,
  it puts those copies back to HEAD, fast-forwards and reconciles. A file whose `~/.claude` copy is
  the pulled version ends clean, and one that holds more, such as another session's uncommitted
  hunk, gets that back as a local change: a copy that removes no line the pull wrote and adds back
  none it removed holds the pulled change, and is never called changed on both sides. A copy that
  lacks the pulled change still stops the run, as does a pull refused for any other reason, such as
  a change to a file that is not mirrored or a commit made here that the remote lacks. No
  `~/.claude` copy is overwritten on the way.
- `hooks/dotclaude-sync.sh` runs a plain `sync.sh` in the background at the start of every prompt of
  every session, so the pull and the reconcile need no run of their own. It commits and pushes
  nothing: local changes wait in the checkout for the session that made them. What stops a run, such
  as a pull that would overwrite a change to a file that is not mirrored, reaches each session once
  as context at its next prompt, and the reply names it. Runs take `.git/sync.lock`, and one waits
  for another.
- `CLAUDE.md`, `README.md`, the files under `docs/` and the pull-requests skill wrap prose at 100
  columns, a list item's later lines indented under its text, and a plain `sync.sh` run stops on a
  longer line. After an edit of one of them, run `~/Projects/dotclaude/reflow.sh` bare in place of a
  reflow written for the edit, as a command of its own rather than chained with `sync.sh`, so the
  hook that watches `CLAUDE.md` counts the change as the session's own edit. It fills greedily each
  paragraph that differs from the checkout's last commit, in `~/.claude` and in the checkout, from
  the line before the first change, and leaves the other paragraphs as they are.
- `sync.sh` holds `CLAUDE.md` to the byte count in the checkout's `claude-md-budget.txt`: over it, a
  run lists the file's sections by size and `--commit` refuses `CLAUDE.md`. The session then makes
  room itself, in the same turn and without asking: it moves a block at least as large as the
  overage, unchanged, into the skill whose description loads it wherever the block applies, or cuts
  sentences such a skill already states, and the reply names each block moved or cut and where it
  went. Raising the budget is the user's call alone, so a session that finds nothing to move or cut
  takes its own edit back out of `CLAUDE.md` before it asks, and no uncommitted hunk waits on the
  answer. After a move, a cut or any other change that shrinks the file, write its new size rounded
  up to the next 1,000 into `claude-md-budget.txt` and commit that file with the rest, so the budget
  falls with every cut and rises only on the user's yes.
- `sync.sh --commit <file> <path>...` commits the named paths and pushes them to the default branch,
  then every mirror below. It refuses a run that names no path, or no path with a change. No PR: the
  repository has one owner, though several sessions work in the checkout at once, and every
  `sync.sh` run copies their edits into it too. So name only the files this session's own edits
  touched, never what a `git status` taken around them shows. `~/.claude/hooks/own-hunks.sh` records
  each edit a session makes to a published file, and `--commit` takes only this session's edits of a
  named file and leaves another session's in the work tree, so a file both changed needs no wait. It
  stops only when an edit changes lines another session wrote and has not committed, and that edit
  then waits for that commit. A named file with no recorded edit, such as one changed by a script
  the user ran, goes in whole: read `git -C ~/Projects/dotclaude diff -- <path>` first, and leave
  out a file with a hunk this session did not write and tell that session with SendMessage. A scan
  hit in a file the run does not name only keeps that file out of the mirror copy. A mirrored JSON
  file changed locally goes back into the checkout's key order first, so `claude plugin` and the
  `/plugin` dialog may change `settings.json`.
- `sync.sh` also copies every published file into each directory listed in `sync-mirrors.txt`, an
  optional file beside it that git ignores, removes there what the checkout has deleted, and prints
  each directory it copied into. With `--commit` it then commits and pushes in each of them with the
  same message and a line naming the checkout commit, so one message is trimmed and redacted for
  every copy. Which directories those are and what each is for lives in the dotclaude checkout's
  project memory, never in this skill, in `CLAUDE.md` or in another tracked file, so the public
  repository shows the mechanism and nothing it points at.
- A file under `~/.claude` that names a project, client or person can still reach the mirrors:
  `sync-private.txt`, beside `sync.sh` and ignored by git, lists such files one per line, and
  `sync.sh` copies each from `~/.claude` into every mirror, never into the checkout, scanning it
  for credentials alone. Named after `--commit`, they are committed in the mirrors only, and a run
  that names no other changed path commits nothing in the checkout. The mirrors' commit-msg hook
  still checks the deny list, so the message names such a file by what it does. A file that no
  copy may hold, such as one with a contract's figures, stays off the list. Which files the list
  names lives in the checkout's project memory, and each of them ends by saying it is mirrored.

## After a push

- Every push to the checkout is followed in the same turn by a pass over the user's open PRs,
  listed with `gh search prs --author @me --state open`. Each PR whose body carries a block the
  `pull-requests` skill puts there, the configuration link or the prompts block, gets the link
  pinned to the commit just pushed and the rest of the body brought into line with the rules as they
  now stand, as far as the skill lets an edit of the body change it. A PR with neither block was
  written by hand and stays as it is. Nothing is rebuilt from another session's context: what the
  new rules need beyond the body, such as prompts, a verify run or new screenshots, is named in the
  reply under its PR, next to what the edit changed. `sync.sh --commit` ends by listing the open PRs
  whose body links the repository but not the commit it just pushed. It is a safety net for the
  pass, not a substitute for it: a PR with only the prompts block is not on that list.
- When the user asked why a problem still shows in a teammate's work or setup, such as issues a
  teammate's merge left open, the reply that reports the push also gives a prompt the user can hand
  to that teammate, who runs a copy of this setup, and other pushes give none. It stands in a `text`
  block of its own, so the teammate fetches nothing. It carries the change itself: each new or
  rewritten rule as it now reads, or a new or changed script whole, with the file and the section or
  neighbouring rule it goes beside, since their copy may be older and its lines differ. It tells the
  teammate's Claude Code to put each one in place in their own copy under `~/.claude`, to keep their
  local changes, and then, if they keep a mirror of their own, to commit and push it as their
  `dotclaude` skill says, and it says go, which their copy of these rules waits for. It names no
  project, client or person, so it can be passed on as it is.

## The usage mod

- The `usage` mod in `~/.claude/mods/session-usage`, which `CLAUDE_CODE_PLUGIN_DIRS` in
  `settings.json` loads in every session and `CLAUDE_CODE_PLUGIN_DIR_WATCH` reloads on every edit,
  pins a status line under the prompt: a fire emoji and the minutes the prompt cache stays warm, an
  ice cube emoji and since when it is cold, or a cookie emoji and the minutes left of a sign-in in
  the session's Chrome tabs when that ends first, then a brain emoji and how full the context window
  is, then an hourglass emoji, or a lock emoji for a sign-in, the minutes left and the other open
  session whose cache goes cold, unless its turn is still running or was stopped, the sidebar marks
  it completed or it waits on background work, or whose sign-in ends soonest, named by its first
  word and those of its other words that fit and have Cmd+K in the app list it soonest: alone, or
  else first among the sessions, each word whole. It shows the tasks pane each time a window starts
  to show the session, from Claude's `show_pane` with `tasks` to its `close_pane`, reading the app's
  layout every 5 seconds meanwhile, and closes it on the first such read that finds a window showing
  the session after a `close_pane` that none showed. It also starts the title of each open session
  in the app with that fire or ice cube, or with a gear emoji while the session's last turn ended
  with background work in flight, which the `Stop` hook's input lists, after a rename too, unless
  the user set the title by hand.
- The desktop app cuts the line at about 40 characters, so it stays within that with the cache
  first, and no hook adds it to the transcript as a notice.
- The mod and its scripts run on macOS and on Linux alike. The app keeps its session records in
  `~/Library/Application Support/Claude` on macOS and in `~/.config/Claude` on Linux, and every
  path to them tries both. Every shell call works with the BSD and the GNU tools: GNU `stat -c`
  before BSD `stat -f`, `date -d @<seconds>` after `date -r`, `pgrep` without `-q`, and Chrome
  matched as `Google Chrome`, `chrome` or `chromium`. A change to the mod is run on this machine
  against the live sessions before it is committed, not only through `claude plugin test`, whose
  mocks never run the scripts.

## Hooks that back rules

- A `UserPromptSubmit` hook in `~/.claude/settings.json` runs `~/.claude/hooks/git-fetch-prune.sh`
  in the background on every prompt. Inside a repository with remotes it fetches all of them with
  pruning at most once every 15 minutes per repository, with every credential prompt disabled so
  it can never hang a prompt. It is a safety net, not a substitute for fetching before a decision.
  A `PreToolUse` hook, `~/.claude/hooks/git-fetch-before-remote-base.sh`, is that fetch for a
  command that branches, rebases, merges, hard-resets or adds a worktree from an `origin/` or
  `upstream/` ref: it runs the same script and waits, unless a fetch ran in the last 2 minutes.
- A `Stop` hook, `~/.claude/hooks/stop-unreachable-claim.sh`, sends a turn back once when its
  closing message says in the first person that something could not be opened, read, reached, found
  or uploaded, and no Claude in Chrome tool ran since the prompt. Its phrase list is a heuristic
  that backs the rule in `~/.claude/CLAUDE.md` that tries Claude in Chrome before calling a page out
  of reach.
- Two hooks back the two stack rules in Running things of `~/.claude/CLAUDE.md` and replace
  neither. A `Stop` hook, `~/.claude/hooks/stop-running-stack.sh`, sends a turn back once when it
  started a stack that is still up. A `PreToolUse` hook, `~/.claude/hooks/stack-held-by-peer.sh`,
  refuses another session's start, restart, stop or removal of a stack that an active session
  started.
- A `PreToolUse` hook, `~/.claude/hooks/cd-fall-through.sh`, refuses a Bash command with a part
  after a `cd` that runs when the `cd` failed, unless the `cd` goes into a directory that exists
  already. It backs the rule in `~/.claude/CLAUDE.md` on commands that run in another directory.
- A `PostToolUse` hook, `~/.claude/hooks/detach-started-session.sh`, hands the `detach_session`
  step back to the model after each `start_session` and `hand_off_to_session` call. It backs the
  rule in `~/.claude/CLAUDE.md` that no session nests under another.
- A `PostToolUse` hook, `~/.claude/hooks/git-submodule-update-after-switch.sh`, runs the update
  after every Bash command that switches and adds `git submodule status`, or the error that
  stopped the update, to the context. A switch given to the user still carries the update.
- While the screen is on and unlocked, the listeners in `~/.claude/presence` keep the file that
  `CLAUDE_CLIENT_PRESENCE_FILE` names there, and a `PreToolUse` hook,
  `~/.claude/hooks/push-only-when-locked.sh`, refuses a push while it is. They back the
  `PushNotification` rule in Running things of `~/.claude/CLAUDE.md`.

## Identity and contents

- Commits there carry the identity configured in the checkout, and the rule in `~/.claude/CLAUDE.md`
  about never overriding the git author applies to it as it stands: the user's own name with a
  disposable address.
- That repository never contains another person's name, an employer, client or customer name, a
  project name, a secret, or anything else that discloses what the user works on. The user's own
  name may stand in the history, as the author or in a quoted prompt, but in no published file,
  which the scan enforces. No email address either: the disposable address lives in the checkout's
  git config and nowhere else. Redact quoted prompts with square brackets where needed, as for
  upstream PRs, and quote only the part of a prompt that is about the configuration, as the
  `commit-messages` skill says; the mirrors follow the same rule. An example in a
  published file is a generic English placeholder, never a label, address, number or tool name from
  the session's project or employer, and a term that slips through goes into `sync-deny.txt`.

## New machine

- Follow the checkout's `docs/install.md`, taking everything (option b), then its identity step
  before the first commit, with the name and address `git log -1 --format='%an <%ae>'` prints for
  the existing history.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.