SLOPSHOPPER

wave-check

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.

new
A shopper browsing a rack in a slop shop
README

claude-code-kit

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.

Install

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.

What's inside

Skills

SkillWhat it doesNeeds
insta360-reelBuilds 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-reelBuilds 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-evidenceCaptures 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
handoffWrites a handoff note so you can continue the current work on your other computer, and picks it up there.the sync engine

Mods

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>.

ModWhat it does
paste-previewShows 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-promptsShows your last two messages above the prompt, and a searchable history pane; click one to scroll the transcript to it.
wave-checkWhen a reply doesn't start with your marker emoji, asks Claude to check that reply against your CLAUDE.md and fix what slipped.

Hooks

HookEventsWhat it does
file-tools-guardPreToolUse (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-activityStop, SessionStartKeeps 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.zshSessionStart, SessionEndRecords open sessions. In a new JetBrains or Ghostty terminal tab (zsh), offers to resume the session that was left open in that folder.
wave-checkUserPromptSubmit, StopWarns when a reply didn't start with your marker emoji, and reminds Claude on the next prompt.
nocturne-notifymost eventsSends Claude's state to Agent Nocturne (keyboard-backlight signals) without the ~2 s per tool call its own adapter adds.

Status line

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 engine

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 marker emoji

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.

Installing by hand

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).

License

MIT

Source 1 files
hooks/register.ts 38 lines
1import 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