When a reply does not start with your marker emoji (CLAUDE_REPLY_MARKER), asks Claude to check that reply against CLAUDE.md and fix what slipped.

Skills, mods, hooks and a status line for Claude Code, plus a sync engine that keeps your Claude Code config the same on all your computers.
git clone https://github.com/robinalexandre/claude-code-kit.git
cd claude-code-kit
./install.sh # asks about each part
./install.sh --list # lists the parts
./install.sh --all # installs every part
./install.sh statusline file-tools-guard # installs the named parts
Needs jq. Works on macOS and Linux, and on Windows from Git Bash. The installer copies each part into ~/.claude, adds its hooks to ~/.claude/settings.json and saves whatever it replaces in ~/.claude/backups/before-kit-<date>/. Running it again is safe. Start a new Claude Code session afterwards.
The sync engine installs differently: see sync/README.md.
| Skill | What it does | Needs |
|---|---|---|
| insta360-reel | Builds a vertical Instagram Reel straight from raw Insta360 X-series .insv files, no Insta360 Studio: calibrates the camera mount, finds every action with a motion analysis, lets you pick from numbered candidate clips, then renders them with a rider-locked horizon. Tuned on wingfoil sessions. | ffmpeg, python3 |
| instagram-reel | Builds a Reel from a folder of phone videos: reviews every clip, proposes numbered candidates, assembles once, checks the result frame by frame. Can save the result to the Photos library with write-only access. | ffmpeg; Photos saving needs macOS and Xcode's swiftc |
| test-evidence | Captures proof that a ticket works locally: browser screenshots, a short video of the flow, the network requests and console behind it (through the chrome-devtools MCP), or one app window or screen region, never the whole screen unless asked. Masks tokens and cookies, writes a short test report and, after your yes, attaches it to the Jira issue or as a resolved comment on the Azure DevOps PR. | chrome-devtools MCP, jq, perl; video needs ffmpeg |
| handoff | Writes a handoff note so you can continue the current work on your other computer, and picks it up there. | the sync engine |
Mods change Claude Code's own interface. The installer loads them through CLAUDE_CODE_PLUGIN_DIRS. Run a mod's tests with claude plugin test mods/<name>.
| Mod | What it does |
|---|---|
| paste-preview | Shows pasted text in full in the prompt box and the transcript, and previews pasted images above the prompt and under the sent message (macOS, Windows). |
| recent-prompts | Shows your last two messages above the prompt, and a searchable history pane; click one to scroll the transcript to it. |
| wave-check | When a reply doesn't start with your marker emoji, asks Claude to check that reply against your CLAUDE.md and fix what slipped. |
| Hook | Events | What it does |
|---|---|---|
| file-tools-guard | PreToolUse (Bash) | Denies shell workarounds for files (cat/head/tail to read, sed -i, sed -n, heredocs, cat > file, python -c) so Claude uses its Read, Edit and Write tools. Pipelines are still allowed. |
| recent-activity | Stop, SessionStart | Keeps a small record of each session under your dev folder (CLAUDE_DEV_ROOT, default ~/dev) and starts every session with a digest of the last 14 days: titles, branches, PRs, last asks. Lets Claude resolve "the bug from yesterday" across repos. |
| session-registry + claude-resume-here.zsh | SessionStart, SessionEnd | Records open sessions. In a new JetBrains or Ghostty terminal tab (zsh), offers to resume the session that was left open in that folder. |
| wave-check | UserPromptSubmit, Stop | Warns when a reply didn't start with your marker emoji, and reminds Claude on the next prompt. |
| nocturne-notify | most events | Sends Claude's state to Agent Nocturne (keyboard-backlight signals) without the ~2 s per tool call its own adapter adds. |
statusline-command.sh: folder, git branch (and worktree), model, context used, effort level and the 5-hour rate limit with its reset time, colour-coded.
sync/: keeps CLAUDE.md, skills, mods, hooks, settings, MCP servers (without their secrets), memory and the untracked agent files of your repos the same on every computer, through a private GitHub repo. Secrets are replaced by __SECRET__ before anything leaves the computer.
The wave-check mod and hook rest on one line in ~/.claude/CLAUDE.md: "Start every message with 🌊" (or any emoji you like). A reply without it is a cheap, visible sign that Claude has started to drift from your instructions, usually deep in a long task. The installer asks for your emoji, stores it as CLAUDE_REPLY_MARKER in the env of ~/.claude/settings.json and offers to add the rule to your CLAUDE.md. Until the variable is set, both parts stay idle.
Copy the part into the same place under ~/.claude (skills/<name>, mods/<name>, hooks/<file>, statusline-command.sh). For a hook or the status line, merge settings/\<part\>.json into ~/.claude/settings.json, replacing __HOME__ with your home folder. For a mod, add its folder to CLAUDE_CODE_PLUGIN_DIRS in the env of settings.json (:-separated, ; on Windows).
hooks/register.ts 38 lines1import type { Register } from 'claude-code'
2
3const TAG = '[wave-check]'
4
5export const checkPrompt = (marker: string) =>
6 [
7 `Your last reply didn't start with ${marker}. ~/.claude/CLAUDE.md asks for it on every message,`,
8 `so a missing ${marker} means the user's instructions may have slipped during that reply.`,
9 `Check that reply, and anything you changed while writing it, against ~/.claude/CLAUDE.md`,
10 `and the project's CLAUDE.md, and fix what slipped. Then answer in a line or two, starting`,
11 `with ${marker}: what you corrected, or that the rest holds. ${TAG}`,
12 ].join(' ')
13
14export const register: Register = on => {
15 // A self-check reply that misses the marker too must not trigger another check.
16 const checkTurns = new Set<string>()
17
18 on('turn.start', (_$, e, next) => {
19 if (e.text.includes(TAG)) checkTurns.add(e.turnId)
20 return next(e)
21 })
22
23 on('turn.complete', async ($, e, next) => {
24 const result = await next(e)
25 const isCheckTurn = checkTurns.delete(e.turnId)
26 const marker = (await $.env.get('CLAUDE_REPLY_MARKER'))?.trim()
27 if (!marker) return result
28
29 const answer = e.answer.trimStart()
30 const isMissing =
31 e.agentId === undefined && e.reason === 'answer' && answer !== '' && !answer.startsWith(marker)
32
33 if (isMissing && !isCheckTurn) void $.prompt.submit({ text: checkPrompt(marker) })
34
35 return result
36 })
37}
38