Say "I'm lost" and Claude explains its last reply again in everyday words. A Claude Code plugin.

<img src="assets/logo/layman-icon.png" width="96" alt="layman">
<h1 align="center">layman</h1>
Say <em>I'm lost</em> and Claude explains it again, plainly.
<a href="https://github.com/varunmoka7/layman/releases"><img src="https://img.shields.io/github/v/release/varunmoka7/layman?style=flat-square&color=111111&label=release" alt="Release"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-111111?style=flat-square" alt="MIT license"></a> <a href="https://github.com/varunmoka7/layman/actions/workflows/test.yml"><img src="https://img.shields.io/github/actions/workflow/status/varunmoka7/layman/test.yml?style=flat-square&color=111111&label=tests" alt="Tests"></a>
Claude Code writes its replies for programmers. When you are not one, or it is late, a reply can be four paragraphs of words you do not know, and the easiest thing is to nod and move on.
layman is the friend who leans over and says "in other words". Type layman, I'm lost or explain again, and the next reply comes back in everyday words: one idea, one example from your own work, one comparison to something ordinary, and at most one yes-or-no question. Ask twice in a session and every reply stays plain until you switch it off.
The icon is a philosopher drawn with one line. That is the whole idea.
/plugin marketplace add varunmoka7/layman
/plugin install layman@layman
Needs Claude Code 2.1 or later. Nothing else to set up.
Type one of these as your prompt. Capital letters do not matter.
| Say | Also works |
|---|---|
layman | lay man |
I'm lost | im lost, i am lost |
explain again | explain it again, explain that again |
didn't understand | dont understand, do not understand, and the usual typos |
Only prompts of 200 characters or less count. A long bug report that quotes "I'm lost" is about something else.
| Command | What happens |
|---|---|
/layman on | Every reply stays plain. The status line shows plain mode. |
/layman off | Back to normal. |
/layman | Flips it. |
Plain mode also switches itself on at your second ask in a session.
You asked why your build fails and got a paragraph about peer dependency resolution. You type layman. The reply now reads:
Your project asked for two different versions of the same library and npm refused to pick one. It is like two people booking the same seat. Run
npm install react@18so both sides agree on one version. Want me to run it? I would.
One hook runs on every prompt you submit. It does three things:
Otherwise it adds nothing and your prompt goes through untouched. The note itself is six sentences in skills/layman/SKILL.md. Paste them into any Claude chat and you have layman without installing anything.
In the Claude apps and Cowork there are no hooks, so the same six sentences ship as a skill. The model applies them when you ask for a plain explanation. There is no counter there.
Everything runs on your computer. The plugin reads your prompt to check for a phrase and keeps one number, the ask count, for the session. No network calls, nothing stored, nothing sent.
claude plugin test . # phrases match, near misses do not, plain mode switches
claude plugin validate . # plugin files are well formed
.claude-plugin/ plugin.json, marketplace.json, icon
hooks/ the hook and the /layman command
skills/layman/ SKILL.md, the rules as a skill
assets/logo/ the logo as SVG
tests/ the test suite
Both commands run on GitHub for every change. Tested on macOS with Claude Code 2.1.288.
Issues and pull requests are welcome. If you add a phrase, add it to the hits list in tests/layman.test.ts and to the table above. Keep the plugin small: one hook, one command, no network. Security issues go through private vulnerability reporting.
hooks/register.ts 67 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4const asks = atom({ plugin: 'layman', key: 'asks' } as const, 0)
5
6// Longer prompts that mention these words are usually about something else.
7const MAX_CHARS = 200
8
9// Covers the spellings seen in the transcripts: "didt", "didit", "understadn", "undersatnd".
10const TRIGGER =
11 /lay\s?man|\b(i'?m|i am) lost\b|\b(did not|didn'?t|didt|didit|do not|don'?t)\s+unders\w*|explain (it |that |this )?again/i
12
13const EXPLAIN = [
14 'The user asked for a plain explanation.',
15 'If their message names no topic, re-explain your previous reply only; otherwise answer what it asks.',
16 'Rules for this reply: everyday words from the first line. One idea.',
17 'Say what any working term means in the same sentence, or leave the term out.',
18 'Use one real example from the work in hand (a real row, file or number) and one everyday comparison.',
19 'Number the steps if there is a sequence. No findings tables and no new topics.',
20 'End with at most one yes-or-no question, with your pick stated.',
21].join(' ')
22
23const STICKY =
24 'The user has asked for plain explanations more than once this session. Write this reply in everyday words from the first line, and say what any working term means in the same sentence.'
25
26// Plain mode is on from the second ask; /layman sets the count to this or to 0.
27const ON = 2
28
29export const register: Register = on => {
30 on('session.start', async ($, e, next) => {
31 await $.command.register({
32 name: 'layman',
33 description: 'Switch plain mode on or off: /layman on, /layman off, or /layman to flip it',
34 immediate: true,
35 })
36
37 return next(e)
38 })
39
40 on('command.run', { command: 'layman' }, async ($, e) => {
41 const arg = e.args.trim().toLowerCase()
42 const isOn = arg === 'on' || (arg !== 'off' && (await read($, asks)) < ON)
43 await update($, asks, () => (isOn ? ON : 0))
44 $.ui.status(isOn ? 'plain mode' : undefined)
45
46 return { text: isOn ? 'Plain mode on.' : 'Plain mode off.' }
47 })
48
49 on('prompt.submit', async ($, e, next) => {
50 const isAsk = e.text.length <= MAX_CHARS && TRIGGER.test(e.text)
51
52 if (isAsk) {
53 await update($, asks, n => n + 1)
54 }
55
56 const isPlain = (await read($, asks)) >= ON
57
58 if (isPlain) {
59 $.ui.status('plain mode')
60 }
61
62 const note = isAsk ? EXPLAIN : isPlain ? STICKY : null
63
64 return note === null ? next(e) : next({ ...e, context: [...(e.context ?? []), note] })
65 })
66}
67types/index.d.ts 9 lines1// How many times this session a prompt asked for a plain explanation
2export type Asks = number
3
4declare module 'claude-code' {
5 interface PluginState {
6 layman: { asks: Asks }
7 }
8}
9