Never writes the em-dash character. Uses a comma, colon, period or parentheses.

The catalog behind https://baselane-sh.github.io/mods-catalog/. Every mod here passed automated checks and is pinned to the commit that passed them.
/plugin marketplace add baselane-sh/mods-catalog /plugin install <name>@baselane-mods
Turn on auto-update (/plugin > Marketplaces > baselane-mods) so that updates and removals reach you.
v1.0.0.entries/<name>.json:{ "name": "<name>", "source": { "source": "github", "repo": "<owner>/<repo>", "ref": "v1.0.0" }, "category": "guard", "tags": ["git"], "screenshots": ["docs/screenshot.png"] }
For a mod in a subfolder, use { "source": "git-subdir", "url": "<owner>/<repo>", "path": "<folder>", "ref": "<tag>" }.
Your first pull request waits for a maintainer to approve the run. To update a mod, raise version in plugin.json, push a new tag, and change ref.
npm test runs the test suite.node scripts/lock.mjs --submitter <login> <name...> checks entries in the working tree and writes their lock files.node scripts/publish.mjs writes .claude-plugin/marketplace.json.entries/<name>.json and lock/<name>.json, and add "<name>": null to renames.json.hooks/register.ts 7 lines1import type { Register } from 'claude-code'
2
3import { registerStyles } from './engine'
4import { rule as noEmDash } from './rules/no-em-dash'
5
6export const register: Register = (on, options) => registerStyles(on, [noEmDash], options)
7hooks/engine.ts 28 lines1import type { On, PluginOptions } from 'claude-code'
2
3// One style rule: an id and the text it adds to the system prompt. A rule
4// that depends on a userConfig value gives a function of the options.
5export type StyleRule = {
6 id: string
7 section: string | ((options: PluginOptions) => string)
8}
9
10// The section id for a rule. A plugin's own id is `<plugin>:<name>`, so two
11// style mods installed together never collide.
12export const sectionId = (rule: StyleRule): string => `${rule.id}:style`
13
14const textOf = (rule: StyleRule, options: PluginOptions): string =>
15 typeof rule.section === 'function' ? rule.section(options) : rule.section
16
17// Answers prompt.compose with everything the engine and the plugins beneath
18// composed, plus one section per rule at the end. A section that already has
19// the same id is replaced, so the ids in the list stay unique.
20export const registerStyles = (on: On, rules: readonly StyleRule[], options: PluginOptions): void => {
21 on('prompt.compose', async (_$, e, next) => {
22 const { sections } = await next(e)
23 const added = rules.map(rule => ({ id: sectionId(rule), text: textOf(rule, options), scope: 'session' as const }))
24 const ids = new Set(added.map(section => section.id))
25 return { sections: [...sections.filter(section => !ids.has(section.id)), ...added] }
26 })
27}
28hooks/rules/no-em-dash.ts 9 lines1import type { StyleRule } from '../engine'
2
3// The rule names the character by its code point and never contains it.
4export const rule: StyleRule = {
5 id: 'no-em-dash',
6 section:
7 'Style: never write the em-dash character (Unicode U+2014, the long horizontal dash). Use a comma, a colon, a period or parentheses instead. This covers prose, lists, headings, code comments and any file content you write. A hyphen inside a word stays allowed.',
8}
9