SLOPSHOPPER

plain-language

Claude's chat replies to you in plain, CEFR-level English: no code unless asked, short task reports. Files, reviews and agent hand-offs keep full technical…

newprompt
v1.0.0MITupdated 2026-10-25EfiPerez/claude-code-mods/plain-language
A shopper browsing a rack in a slop shop
README

claude-code-mods

Claude Code mods for everyday workflows, by Efi Perez (@EfiPerez).

A mod is a Claude Code plugin with a hooks module: code that runs inside Claude Code on events such as a prompt being sent, a tool being called or a model request going out.

Install

Add the marketplace once:

/plugin marketplace add EfiPerez/claude-code-mods

Then install only the mods you want:

ModWhat it doesInstall command
effort-pickerSets each request's effort level from the task, not the prompt: Claude picks a level once it understands the work, and helpers get a level per job./plugin install effort-picker@efi-mods
plain-languageClaude's chat replies to you in plain, CEFR-level English (A2 or B1): no code unless asked, short task reports. Files, reviews and agent hand-offs keep full technical detail./plugin install plain-language@efi-mods

Alternatively download the repo and load a mod locally using claude --plugin-dir <downloaded-repo>/<mod> on a compatible Claude Code runtime. This uses the plugin loader, not ~/.claude/skills/.

Third-party marketplace auto-update is off by default. Run /plugin marketplace update or enable auto-update for this marketplace.

effort-picker

  • Every automatic prompt starts at a baseline effort (setting baseline, default high).
  • Once Claude understands the task it calls the tool mcp__effort-picker__set_effort {level, category} in the same message as its next tool calls. The mod then rewrites the effort of every later request in that loop. Categories (chat, lookup, trivial edit, contained change, multi-file change, unknown root cause, review/plan, strict rules, long agentic work, architecture/security/data, prior attempt failed) each carry a default level.
  • xhigh and max need your approval. The mod asks with a question card; a "no", a dismissed card or a headless run (claude -p) falls back to high. max is only accepted with the category prior_attempt_failed.
  • Helpers (subagents): the parent sets the Agent tool's effort per job. Start an Agent description with [effort-locked] to lock the child at that level.
  • Your manual choice wins for that prompt: /effort, ultrathink, a skill whose frontmatter declares effort:. Setting CLAUDE_CODE_EFFORT_LEVEL turns the mod off for the whole session.
  • A one-line footer under the answer shows the level when it changed.
  • Notices: a toast when the built-in effort rules are older than staleDays (default 90, 0 turns it off), or when a session runs on a Claude model the rules were not checked against. Either means: check for a plugin update.
  • Files it writes: a per-session log at ~/.claude/effort-picker/logs/<YYYY-MM-DD>_<session>.jsonl holding only levels, categories, ids and counts (never prompt or file text), and one $.store value (the day the stale notice was last shown).
  • The per-category defaults follow Anthropic's effort guidance (<https://platform.claude.com/docs/en/build-with-claude/effort>) as of the date in hooks/rubric.ts (checkedAt).

plain-language

  • Adds style rules for chat replies only: CEFR B1 by default, A2 via the cefrLevel setting. Files Claude writes, prompts for subagents or other AI tools, and subagent results keep full technical detail.
  • Two delivery paths: a system-prompt section (prompt.compose), and the same rules attached once as hidden context to the first accepted prompt of each conversation (again after /clear). On compaction the summarizer is asked to keep the rules.
  • Skipped on every path for headless runs (claude -p, the SDK); the system-prompt section is also skipped for --bare and teammates.

Prerequisites and supported surfaces

  • Requires Claude Code with the mods/hooks engine.
  • On machines with managed settings or a Team/Enterprise organization, the built-in cc-plugin-sec-default skips the user-mod events classic.*, prompt.section, prompt.context, prompt.compose, skill.prompt, attribution.text and settings.read. There is no user-side switch; an administrator must manage the plugin to lift it.
  • effort-picker uses none of those events.
  • plain-language loses its system-prompt section there and falls back to the first-prompt context path described above. The rules then reach the model as part of the first message's context rather than the system prompt.
  • VS Code panel support depends on the extension version. Question cards (effort-picker's approval) and toasts show in the panel; do not expect terminal/Desktop UI parity.
  • Tests are not included in this published copy. Engine-generated .claude-plugin/types/ declarations are regenerated at load time.

Licence

MIT — see LICENSE.

Credit

Built by Efi Perez.

Source 1 files
hooks/register.ts 73 lines
1// plain-language: Claude's chat replies to you are plain English at a CEFR
2// level, with no code unless asked. It lives in a mod and not in CLAUDE.md on
3// purpose: other AI tools that read CLAUDE.md (code reviewers, coding agents)
4// would pick up these rules too and give vague answers with no file:line
5// evidence. A mod's prompt text reaches Claude Code only.
6
7import type { EngineInterface, Register } from 'claude-code'
8
9export const SECTION_ID = 'plain-language:style'
10
11export function styleText(level: string): string {
12  return [
13    '# How to write to the user in chat',
14    '',
15    'These rules apply only to the messages you write to the user in this chat, and they override any',
16    'instruction that says to assume a technical reader. They do NOT apply to: files you write',
17    '(code, docs, CLAUDE.md, handoffs, review reports), prompts you write for subagents or other AI tools,',
18    'or what you return when you are a subagent. Those keep full technical detail and file:line',
19    'evidence.',
20    '',
21    `- Write in CEFR ${level} English: short sentences, common words, one idea per sentence. The user is`,
22    '  smart and busy, not a beginner; the goal is easy to follow, not talking down.',
23    '- Explain everything in plain English for a non-programmer. If a technical term is',
24    '  unavoidable, define it in a few words the first time it appears.',
25    "- Don't paste code into the chat unless the user asks; say what it does and where it lives (a file",
26    '  link is fine).',
27    '- After finishing a task, report: what changed, where, and how the user can test it. 5 lines max.',
28    '- Keep answering BLUF: the conclusion first, in one or two sentences.',
29    '- When the user asks for code, a diff, exact commands, or "the technical version", give it in full.',
30  ].join('\n')
31}
32
33// Headless runs (claude -p, the SDK) have no person reading the chat. VS Code reports
34// "not interactive", so the entrypoint is the test: headless ones are sdk-*.
35async function headless($: EngineInterface): Promise<boolean> {
36  return ((await $.env.get('CLAUDE_CODE_ENTRYPOINT')) ?? '').startsWith('sdk')
37}
38
39export const register: Register = (on, options) => {
40  const level = options?.cefrLevel === 'A2' ? 'A2' : 'B1'
41
42  on('prompt.compose', async ($, e, next) => {
43    const result = await next(e)
44    // --bare keeps its one section; a teammate writes to its lead, not to the user; print is -p / SDK.
45    if (e.traits.includes('bare') || e.traits.includes('teammate') || e.traits.includes('print')) return result
46    return { sections: [...result.sections, { id: SECTION_ID, text: styleText(level), scope: 'session' }] }
47  })
48
49  // prompt.compose above is skipped on managed / Team / Enterprise machines (cc-plugin-sec-default),
50  // so the rules also ride on the first prompt of each conversation as hidden context: the model
51  // reads them, the chat shows nothing and no extra turn is spent. Keyed on the session id because
52  // /clear starts a new conversation in the same process with no session.start; recorded only once
53  // the prompt actually entered, so a prompt another hook dropped does not use up the delivery.
54  let attachedFor = ''
55  on('prompt.submit', async ($, e, next) => {
56    const sid = await $.session.id()
57    if (attachedFor === sid || (await headless($))) return next(e)
58    const r = await next({ ...e, context: [...(e.context ?? []), styleText(level)] })
59    if (r.drop === undefined) attachedFor = sid
60    return r
61  })
62
63  // A compaction summary would drop that message; ask the summarizer to keep the rules.
64  on('session.compact', async ($, e, next) => {
65    if (e.agentId || (await headless($))) return next(e)
66    return next({ ...e, instructions: [e.instructions, keepText(level)].filter(Boolean).join('\n\n') })
67  })
68}
69
70export function keepText(level: string): string {
71  return `Copy this block word for word at the end of the summary; it is the user's standing chat style:\n\n${styleText(level)}`
72}
73