Skip to main content

kit plugin

The repository is also a Claude Code marketplace (claude-config) that contains the kit plugin: twelve personal hooks that enforce some rules of the template at a technical level and keep Claude aligned with the updated rules. The plugin is installed once per machine and applies to all projects; some checks apply only to projects that have .claude/rules/core.md (see Activation). The rules (.claude/rules/) are not part of it: they are copied into the projects as described in How to use.

What it contains​

The twelve hooks fall into three families:

  • blocks: they stop the operation; Claude receives the reason and must fix it;
  • warnings: the operation goes through; Claude and you receive a warning to fix in the next step;
  • reminders: they remind a rule at the start or end of a session, without blocking.

Blocks​

HookEventWhat it does
limit-one-questionPreToolUse on AskUserQuestionBlocks calls with more than one question.
guard-commandsPreToolUse on Bash and PowerShellAlways blocks git init, git push --force and variants, deleting remote branches, reset --hard, clean -f, --no-verify, rm -rf; and blocks git push, tag creation, gh pr create/merge and gh release when writing until the project approves them (see GitHub integration). It also blocks commits on main/develop (with the instruction to propose a branch from develop), git add -A, git add --all and git add ., adding sensitive files (.env other than .env.example, secrets.json, *.pfx, *.p12, *.key, *.pem, *.jks) and commits whose message does not follow Conventional Commits (subject of at most 72 characters, lowercase description, no final period).
guard-secretsPreToolUse on Write, Edit, MultiEditBlocks writing real secrets: values under keys such as Password, Secret, Token, Key, or passwords in a connection string inside appsettings*.json; plaintext passwords in docker-compose*.yml and Dockerfile; PEM private keys, AWS keys and complete JWT tokens. A placeholder (CHANGE-ME…, ${VAR}, placeholder…) passes only when the whole value has that form: a word such as example inside a value is not enough. JWT tokens are not checked in .md files, where the documentation uses them as examples.
guard-patternsPreToolUse on Write, Edit, MultiEditBlocks eight patterns forbidden by the rules: skipped tests (skip, Ignore, .only), legacy Angular (NgModule, *ngIf, *ngFor), forbidden libraries (MediatR, AutoMapper, Jest, Karma, Jasmine), EF Core InMemory, insecure APIs (AllowAnyOrigin, BinaryFormatter, MD5/SHA1, eval), interpolated SQL (FromSqlRaw($"...")), npm install or npm ci without --ignore-scripts in the Dockerfile, disabled TLS in appsettings*.json. It accepts kit-allow waivers.

Warnings​

HookEventWhat it does
check-planPostToolUse on Write, Edit, MultiEdit (only for PIANO.md)Flags a step ticked [x] that does not report the result of build and tests (core.md §6.21).
check-qualityPostToolUse on Write, Edit, MultiEditWarns about Docker (images without a tag or with latest, host ports of databases and brokers exposed), dependencies (version ranges * and latest in package.json; ^ and ~ are no longer reported; a reminder to run npm audit or dotnet list package --vulnerable when packages change) and the frontend (any, innerHTML, bypassSecurityTrust*, hardcoded hexadecimal colours: in .ts only within quotes, in .html only inside style).
check-practicePostToolUse on Write, Edit, MultiEditWarns about bulk writes (ExecuteUpdate, ExecuteDelete), analyzer suppressions without a reason, AllowAnonymous endpoints, Regex without matchTimeout, console.log and Console.Write in the code, public C# members without a /// comment, new service file (*Service, *Handler, *Validator, .service.ts, .component.ts, .pipe.ts) without a test. It accepts kit-allow waivers.

Reminders​

HookEventWhat it does
ask-one-questionUserPromptSubmitAt every message, recalls the rule "when in doubt, one question at a time" (core.md, the section at the top).
rules-watchUserPromptSubmitAt every message, checks whether the project's rules changed during the session (for example after a git pull) and, if so, tells Claude to re-read the changed files, attaching the git diff. Once per session it also compares the rules with the template's.
session-planSessionStart (startup, resume, clear, compact)At the start of a session, inserts the project's PIANO.md (if it exists) and the list of rules changed since the last session, with the git diff of the changed files if the folder is a repository.
plan-reminderStopAt the end of a session, if PIANO.md still has steps with an open box [ ] or [~], tells the user.
check-closeStopAt the end of a session, on the changes not yet committed, reminds: code changed without an updated CHANGELOG.md, EF model changed without a new migration, it.json and en.json keys that differ. Uses git status.

Blocking of forbidden commands is absolute: whoever wants an exception runs the command by hand (prefix ! in the prompt) or disables the plugin. Integration operations (push, PR, tags, releases) are instead unlocked by the project's approval, described below.

session-plan and rules-watch save the fingerprints of the rules (CLAUDE.md and .claude/rules/*.md) in ~/.claude/session-rules-state.json (one entry per project folder, plus the hash of HEAD and a session marker). The scripts need Node.js; the diff also needs git. A hook cannot force Claude to re-read the files: it inserts into the context the instruction to do so.

Activation​

  • In every project: guard-commands, guard-secrets, check-quality, check-plan, plan-reminder, session-plan, rules-watch, ask-one-question, limit-one-question.
  • Only in projects with .claude/rules/core.md: guard-patterns, check-practice, check-close. The file is the sign that the project uses the template's rules; without it these three hooks do nothing.

kit-allow waivers​

When a pattern is right in a specific case, the waiver is written in the code, on the same line or on the line above, with the rule's id and a reason of at least eight characters:

// kit-allow: skip — rerun in the nightly CI
  • For guard-patterns the ids are: skip, angular-legacy, libreria-vietata, inmemory, api-vietata, sql-interpolato, npm-install, tls-disattivato.
  • For check-practice the ids are: scrittura-blocco, soppressione, anonimo, regex, log.
  • A wrong id, or an empty or too short reason, is not enough: the block or warning stays.
  • guard-commands, guard-secrets and limit-one-question have no waivers.

Every waiver added in a pull request is listed by the kit-allow CI check (the example workflow is in _template/ci/), with file and line, and must be approved in review like any exception (core.md §6, point 23).

GitHub integration​

Push, PRs, tags and releases pass only if the project has approved them. The approval is a fixed line in the project's CLAUDE.md, in the section "Exceptions to the global rules":

- **Integrazione GitHub approvata:** PR sì, push no, tag sì, rilasci no (2026-11-04)
  • Each operation is unlocked separately. The date at the end is optional.
  • Without the line, or with a line in another format, no operation is unlocked: the block stays.
  • The line is read from the CLAUDE.md at the root of the repository, even when the command runs from a subfolder. Outside a repository, the CLAUDE.md of the folder the command runs from is read.
  • Even with approval, git push --force (and variants), deleting remote branches, reset --hard, clean -f, --no-verify and git init stay always blocked.
  • The questions to Claude (whether the GitHub CLI is installed and authenticated, which operations to approve) are in the git.md rule, not in the hook.

Limits of the checks​

  • The checks read the text that Claude writes with Write, Edit and MultiEdit. They do not see changes made from Bash, for example with sed or a redirection.
  • With Edit the hook sees only the new text: if a change touches a line that already has a waiver, the waiver must be repeated in the change.
  • The C# checks are regular expressions, not a parser: members spanning several lines or complex attributes can slip through or give false positives.
  • Warnings do not stop the work; blocks do. A false positive on a block is solved with a waiver, not by disabling the hook.

Testing the hooks​

node --test "plugins/kit/tests/*.test.mjs"

The tests use temporary folders and a fake home (they do not touch ~/.claude); the cases that use git clone this repository, so they must be run inside it. The cases that clone the repository (rules-watch.test.mjs) copy the rules into the clone when they are not committed yet, so they pass even before the first commit.

Installation​

Once per machine:

claude plugin marketplace add <path or repository of the template>
claude plugin install kit@claude-config
  • From a local folder: <path> is the template folder (the one with .claude-plugin/).
  • From GitHub: AngeloDotNet/claude-config.
  • Coming from kit-config: the plugin was called kit-config until it was renamed to kit (commit ae66ee1). If you installed it under the old name, install kit@claude-config and remove kit-config; then restart Claude Code, because hooks are loaded when the session starts.

In a session that is already open, /reload-plugins loads the hooks without restarting. With claude plugin list you check that kit@claude-config is enabled.

Updating​

The plugin has an explicit version in plugins/kit/.claude-plugin/plugin.json: those who installed it receive the changes only when the version goes up. After updating the template (from a local folder: git pull):

claude plugin marketplace update claude-config
claude plugin update kit@claude-config

The procedure for those who maintain the template is in Maintenance.

Updated rules: what rules-watch checks​

  • Rules changed during the session. If a rules file changes (or is added or removed) between one message and the next, Claude receives the list and the instruction to re-read it. The reminder appears only once per change; if nothing changed, no extra text.
  • Diff with git. In a repository the warning includes the git diff of the changed files only, computed from the saved hash of HEAD (later commits and uncommitted changes). The diff is truncated and, at the start of a session, limited so that the output stays under 10,000 characters. Without git, or with an invalid hash, only the list of files remains. New untracked files appear in the list but not in the diff.
  • More recent template rules. Once per session and per project, rules-watch compares the version of the project's core.md with the template's. If the template is ahead, Claude receives the different versions and files and must propose the alignment with a single question: it copies nothing without your OK. A difference in content with the same version is not reported (it may be an intentional waiver, to be noted in CLAUDE.md).
  • Template folder. It is read from the environment variable KIT_CONFIG_TEMPLATE_DIR, to be set once per machine with the path of the template folder (for example in the env section of ~/.claude/settings.json). The plugin has no default path: without the variable the comparison does not run and Claude receives a warning asking for the path. If the folder does not exist or does not contain .claude/rules, Claude receives a warning (once per session) and asks you for the correct path. The comparison is skipped in the template itself and in projects without rule files.

Moving the marketplace's local folder​

If the marketplace was added from a local folder and the folder moves (or another copy of the repository is used), the registered path must be updated: claude plugin marketplace update does not do it. The path appears in extraKnownMarketplaces in ~/.claude/settings.json and in ~/.claude/plugins/known_marketplaces.json (managed by Claude Code): change it with the CLI, not by hand.

claude plugin marketplace remove claude-config
claude plugin marketplace add <new path> --scope user
claude plugin install kit@claude-config --scope user

Then /reload-plugins (or a restart) and claude plugin list to check that kit@claude-config is enabled.

remove also uninstalls the plugin and deletes, where possible, its options and saved data: that is why reinstalling is needed. session-rules-state.json is not affected. The installed plugin is a copy in ~/.claude/plugins/cache/, so it does not depend on the old folder until remove is run.

Disabling and uninstalling​

claude plugin disable kit@claude-config
claude plugin uninstall kit@claude-config

disable suspends the whole plugin: together with the blocks of guard-commands, guard-secrets, guard-patterns and limit-one-question, the warnings and reminders stop too. There is no way to suspend a single hook without changing the plugin.

Migrating from hooks copied by hand​

If the first four hooks (rules-watch was born in the plugin and has no manual copies) had been copied into ~/.claude/ and registered in ~/.claude/settings.json, they must be removed before installing the plugin, otherwise each hook runs twice (and session-plan does not report the changed rules):

  1. remove from ~/.claude/settings.json the hooks entries of the four scripts;
  2. install the plugin (see above);
  3. delete from the ~/.claude/ folder the copies of the four scripts (ask-one-question.mjs, limit-one-question.mjs, guard-commands.mjs, session-plan.mjs).

session-rules-state.json is kept: the plugin keeps using it.

What stays outside the plugin​

Automatic linting after each change and docker compose config also stay outside the hooks: the plugin's checks are text-based and do not run external tools.

Versioning​

SemVer. The version goes up only at release, together with a release subsection in CHANGELOG.md, under the day's heading.