The Claude Code setup I run across three companies, packaged so a blank Claude Code can become it in about five minutes.
It is an onboarding, not a reading list. You install one plugin, run one command, answer a few questions, and Claude sets itself up: the hooks, the operating rules, and the protective settings, adapted to how you work. Then it runs a self-test to prove every piece works.
In Claude Code:
/plugin marketplace add mattzimak/matts-claude-code-setup
/plugin install matts-setup@mattzimak
Then start a new session and run:
/matts-setup:onboard
That is the whole install. Requires jq. Onboarding checks for it and tells you how to install it if it is missing.
Onboarding is a skill Claude runs for you. It only ever starts when you ask for it, never on its own.
- Checks your machine for
jq(required),gitleaksandnpx(each needed for one optional feature). - Asks at most two rounds of questions, each with a recommended answer, so agreeing is one click. Is this one project or a multi-domain workspace? Any brand names that must always be spelled a certain way? Should knowledge repos auto-commit? Notifications on or off?
- Writes your answers to
~/.claude/matts-setup/config.json. Every hook reads that one file, so nothing you change later means editing a script. - Merges two protections into your own settings, showing you the change before it writes: transcripts kept for 365 days instead of 30, and a block on Claude reading
.envand private key files. It backs the file up first and keeps every rule you already had. - Adds the operating rules to your
CLAUDE.md, inside marker comments, using a script rather than a model edit, so running onboarding again updates them in place and never duplicates them. - Runs the self-test and shows you every result.
- Reports what is now active, every file it touched, and exactly how to undo each change.
Run /matts-setup:onboard again any time to change an answer.
| Hook | When | Effect | Default |
|---|---|---|---|
| Evidence audit | every prompt | Every progress report must point to a real tool result | on |
| Docs first | prompt names a fast-moving tool | Read current docs before answering, not training memory | on, always covers Claude Code |
| Execute yourself | prompt about setup, deploys, keys | Try it, try it another way, show the command, before handing anything off | on |
| Domain router | prompt matches one of your domains | Read that domain's CLAUDE.md first |
on once you add domains |
| Brand casing | Claude writes a file | Flags a brand or name in the wrong form | on once you add names |
| Setup log | Claude edits your setup | Reminds it once per session to record what changed | on |
| Skill telemetry | a skill runs | Logs which skills fire, so you can prune the ones that never do | on |
| Plan to PDF | a plan is saved under plans/ |
Writes a PDF next to it | on, needs npx |
| Notification | Claude finishes | Desktop notification | on, macOS or Linux |
| Root hygiene | a file is created in the workspace root | Denies the write and says where it belongs | opt-in |
| Knowledge-repo auto-commit | Claude finishes | Commits and pushes your knowledge repos, gated by gitleaks | opt-in |
The last two are off until onboarding switches them on, because one blocks writes and the other pushes to git remotes without asking each time. Neither should ever turn itself on.
Plus two skills: onboard, which only you can start, and operating-rules, which Claude consults when it plans work, reports progress or decides whether to ask you something.
| Count | Where | |
|---|---|---|
| Hooks | 11, on 5 events (UserPromptSubmit, PreToolUse, PostToolUse, SessionStart, Stop) | hooks/hooks.json |
| Scripts | 15 shell scripts, all readable, all exit 0 | scripts/ |
| Skills | 2 (onboard, operating-rules) |
skills/ |
| Templates | 3 (CLAUDE.md block, config, path-scoped rule) | templates/ |
| Self-test | 28 checks | scripts/selftest.sh |
Claude Code only, 2.1 or later (plugins load hooks/hooks.json by convention from that version). macOS and Linux; the notification hook uses osascript on macOS and notify-send on Linux. It does not ship adapters for Codex, Cursor or other harnesses; if you need those, see ECC below.
Instructions drift. Hooks do not.
CLAUDE.md loads at the start of a session and then competes for attention with everything else. Over a long session a rule written there can simply stop being followed. A hook fires on an event, every time, however long the session has run.
That gives a clean line for where every rule belongs:
- If an event can detect it, make it a hook. A file written to the wrong place, a prompt that mentions a tool, a brand misspelled in copy.
- If it needs judgment, write it down. "Is this plan still ambiguous?" cannot be detected by any event, so it lives in the operating rules.
This setup is that line applied case by case.
Every piece here was added after something went wrong.
Transcripts kept for 365 days. Claude Code deletes session transcripts after 30 days by default. I found out when it deleted the session that had built my personal website. The evidence was an identical cutoff exactly 30 days back across five unrelated local folders, with nothing older surviving anywhere, and no way to recover it. Two things I observed but have not seen documented: the sweep appeared to key on when a transcript was last modified rather than when the session started, and it did not appear to reach into per-session subagent folders.
Brand casing lint. A full client outreach pack shipped with the brand in the wrong casing, even though the rule already existed in two places: the assistant's memory and the client's knowledge base. Written rules get missed. A hook fires every time. It warns rather than blocks, because by the time a file is written the useful thing is to say exactly what to fix.
Execute yourself. The most common failure in agent work is not a missing capability. It is the agent assuming a limit instead of testing it, or reading one refused command as proof the whole goal is blocked, and then telling you to go do it by hand.
Domain router. A per-domain CLAUDE.md only loads when Claude reads a file in that folder, so working from a workspace root means the rules are never in context. The media route checks docker image and favicon before it checks image, so an infrastructure prompt never gets routed to an image-generation skill.
Auto-commit. The first version wrote its log to the current directory, which was inside the repo it was syncing, so it committed its own log. The log now lives outside every repo. It also refuses to commit at all if gitleaks is missing, on the principle that a silent safety gate is worse than none.
Secrets deny list. A rule saying "never read .env" is advice; a permission rule is enforced. The list covers nested .env files too, because subfolder repos carry their own and Read(.env) only protects the root.
What building these taught me.
- Always exit 0. A hook that errors must never block the session it is watching.
- Stay silent unless it matters. A hook that talks on every prompt trains everyone to ignore it.
- Check negative cases before positive ones. Broad keywords collide across domains.
- Deny when the damage can still be prevented; warn when it is already done.
- Nest
additionalContextinsidehookSpecificOutput. At the top level it is silently ignored, so the hook runs, exits cleanly, and does nothing. - Keep logs outside whatever the hook acts on.
- Fire once when once is enough, with a flag file keyed on the session id.
- Test the silent cases, not only the firing ones. Half a hook's value is what it correctly ignores.
- Do not read regex config with jq's
@tsv. It doubles backslashes, so\bAcme\bsilently matches nothing. This setup joins fields on a unit separator instead. - Distrust a green test run. A check that received no input passes every silent case for the wrong reason. Plant a deliberate bug and make sure the suite catches it.
- The plugin and marketplace manifests pass
claude plugin validate. - It was installed into a blank Claude Code (an empty config directory): marketplace added, plugin installed and enabled, all 15 scripts still executable afterwards.
- The self-test passes 28 of 28, run from the installed copy rather than the source.
- The self-test itself was checked by planting two bugs, a router that always fires and a brand lint that never does. It caught both.
- In a live session, Claude Code's own debug log shows the plugin loading, both skills registering, and the evidence-audit and docs-first hooks injecting their context on a real prompt.
Run the self-test yourself at any time:
bash ~/.claude/plugins/cache/mattzimak/matts-setup/*/scripts/selftest.shHooks run shell commands on your machine, so read them before you install them. Everything this plugin runs is in scripts/, plain bash, about 15 files.
- No network calls, with one opt-in exception: knowledge-repo auto-commit runs
git pushon the repos you name, and only aftergitleakspasses. - No secrets are read. Onboarding adds a permission rule that blocks Claude from reading
.envand private key files. - Nothing runs on install. The only entry points are Claude Code events and the
/matts-setup:onboardcommand you start yourself. - Scanned with NVIDIA SkillSpector (static analysis,
--no-llm): score 100/100, no findings. - Do not copy
hooks/hooks.jsoninto your ownsettings.json. Claude Code loads plugin hooks itself, and a second copy makes them fire twice.
Found a problem? Open an issue, or email hello@mattzimak.com for anything you would rather not post publicly.
| Symptom | Cause | Fix |
|---|---|---|
| Onboarding stops at "jq not found" | jq is required by every hook |
brew install jq or your package manager, then run onboarding again |
| A hook seems to fire twice | hooks/hooks.json copied into settings.json |
remove the copy; the plugin already loads it |
| No PDF next to a saved plan | npx missing or the file is not under plans/ |
install Node, or move the plan; the hook only watches plans/ |
| No desktop notification on Linux | notify-send missing |
install libnotify; the hook stays silent without it |
| Auto-commit does nothing | gitleaks missing, or the feature is still opt-out |
install gitleaks and re-run onboarding; it refuses to commit without the gate |
| A brand name is not flagged | the name is not in ~/.claude/matts-setup/config.json |
run onboarding again and add it |
ECC is the big one: hundreds of skills, agents and rules, adapters for Codex, Cursor, OpenCode and more, a hosted GitHub App. If you want breadth, or you run several harnesses, start there.
This repo is the opposite shape on purpose: one founder's setup, 11 hooks and 2 skills, every piece added after something went wrong, self-tested, readable in an afternoon. It is for people who run companies with Claude Code rather than build software with it all day, and it is a starting point you are meant to trim, not a platform. The two coexist; nothing here conflicts with an ECC install.
Onboarding tells you each of these with your exact paths. In general:
- Restore the settings backup it made:
~/.claude/settings.json.bak.<timestamp>. - Delete
~/.claude/matts-setup/. - Remove the block between the
matts-setup:rulesmarkers in yourCLAUDE.md. /plugin uninstall matts-setup.
MIT. See LICENSE.