SLOPSHOPPER

no-em-dash

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

newprompt
A shopper browsing a rack in a slop shop
README

Baselane mods catalog

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.

Install a mod

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

Publish a mod

  1. Put your mod in a public github.com repo and push a tag, for example v1.0.0.
  2. Fork this repo and add one file, 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>" }.

  1. Open a pull request. The bot runs the checks and comments the result. When every check passes, it merges.

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.

Maintainers

  • 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.
  • To take a mod down: delete entries/<name>.json and lock/<name>.json, and add "<name>": null to renames.json.
Source 3 files
hooks/register.ts 7 lines
1import 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)
7
hooks/engine.ts 28 lines
1import 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}
28
hooks/rules/no-em-dash.ts 9 lines
1import 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