One small Claude Code exercise a day, with your streak and a year of 365 challenges that never repeat.

A small collection of Claude Code mods (hooks-module plugins), shared as a plugin marketplace. Use any of them, change them, pass them on: they're MIT-licensed.
claude plugin marketplace add hearsawho25/roberts-mods
claude plugin install <mod>@roberts-mods
Install as many as you like, then run /reload-plugins or start a new session.
| Mod | Command | What it does | |
|---|---|---|---|
roberts-toolbox | /toolbox | One button at the end of the prompt hint line that opens a menu of your mods and the commands that change Claude Code. | |
clean-view | `/simple on\ | off` | Hides tool calls, diffs and command output, and shows one plain-English checklist above the prompt instead. |
prompt-upgrade | /better | Turns a rough prompt into a better one in a pane: up to three quick questions, a rewrite, and a side-by-side review before anything is sent. | |
daily-challenge | /challenge | One small Claude Code exercise a day, with a streak and a year of challenges that never repeat. | |
work-report | /report | Counts the tasks Claude finished for you and estimates the hours and dollars saved, by day, week, month and year. | |
copy-blocks | /copyblock [n] | A Copy button under every code block in Claude's replies, so you get clean text without the frame. | |
relay | /relay, /handoff, /keepwarm | A context and prompt-cache meter above the prompt, one-click handoffs to a fresh session, and cache warming with a cold-cache guard. See relay/README.md. | |
git-ship | /ship | Repo status above the prompt, and a pane whose buttons fill ready-to-run git and gh commands (commit messages and PRs drafted for you). It never runs a write: you press Enter. See git-ship/README.md. | |
mod-doctor | /mod-doctor | Checks that every enabled mod actually loaded. Stays quiet until one didn't, then shows a red row and the reason from claude plugin validate. See mod-doctor/README.md. |
Several of these started as other people's ideas, and they deserve the credit:
The mods themselves were built with Claude Code.
MIT.
hooks/register.tsx 363 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Progress } from '../types'
5import { TOTAL, themeOf } from './challenges'
6import {
7 COLORS,
8 TILE_LABELS,
9 TITLE,
10 bigNumber,
11 chipRows,
12 dayGrid,
13 dayLabel,
14 fit,
15 heading,
16 tileLayout,
17 titleRule,
18 wrap,
19 yearBar,
20} from './layout'
21import {
22 bestStreak,
23 canSwap,
24 coachPrompt,
25 completeToday,
26 dayKey,
27 doneCount,
28 formatDay,
29 lastDays,
30 normalize,
31 streak,
32 swapToday,
33 todayOf,
34 undoToday,
35} from './logic'
36
37const PANE = 'daily-challenge'
38const PANE_TITLE = 'Daily challenge'
39const STORE_KEY = 'progress'
40const FOOTNOTE = 'If a command is not on your version, ask Claude how to do it.'
41const FAILED = 'Daily challenge could not do that just now. Please try again in a moment.'
42
43const progressAtom = atom({ plugin: 'daily-challenge', key: 'progress' } as const, null)
44
45/** The session's copy if there is one, else what the store kept. A read only. */
46async function readProgress($: EngineInterface): Promise<Progress> {
47 const held = await read($, progressAtom)
48 if (held !== null) return held
49 return normalize(await $.store.get(STORE_KEY))
50}
51
52async function today($: EngineInterface): Promise<string> {
53 return dayKey(await $.clock.now())
54}
55
56/** Applies one change to the progress and keeps it in the store; null when nothing changed. */
57async function change(
58 $: EngineInterface,
59 apply: (progress: Progress, day: string) => Progress | null,
60): Promise<{ progress: Progress; day: string; isChanged: boolean }> {
61 const day = await today($)
62 const stored = await readProgress($)
63 const outcome = { isChanged: false }
64 const progress = await update($, progressAtom, held => {
65 const base = held ?? stored
66 const next = apply(base, day)
67 outcome.isChanged = next !== null
68 return next ?? base
69 })
70 const settled = progress ?? stored
71 if (outcome.isChanged) await $.store.set(STORE_KEY, settled)
72 return { progress: settled, day, isChanged: outcome.isChanged }
73}
74
75const complete = ($: EngineInterface) => change($, completeToday)
76const undo = ($: EngineInterface) => change($, undoToday)
77const swap = ($: EngineInterface) =>
78 change($, (progress, day) => swapToday(progress, day, Math.random))
79
80async function openPane($: EngineInterface): Promise<void> {
81 await $.ui.open({ id: PANE, title: PANE_TITLE })
82}
83
84async function coach($: EngineInterface): Promise<void> {
85 const progress = await readProgress($)
86 const now = todayOf(progress, await today($))
87 if (now.challenge === undefined) return
88 await $.ui.close({ id: PANE })
89 await $.prompt.fill({ text: coachPrompt(now.challenge) })
90}
91
92function quietly(work: Promise<unknown>): void {
93 work.catch(() => undefined)
94}
95
96function tally(progress: Progress, day: string): string {
97 const days = streak(progress, day)
98 return `Streak: ${days} ${days === 1 ? 'day' : 'days'}. Completed: ${doneCount(progress)} of ${TOTAL}.`
99}
100
101async function runCommand($: EngineInterface, args: string): Promise<string> {
102 const word = args.trim().toLowerCase()
103
104 if (word === '' || word === 'open') {
105 const progress = await readProgress($)
106 const day = await today($)
107 const now = todayOf(progress, day)
108 quietly(openPane($))
109 if (now.challenge === undefined) return `All ${TOTAL} challenges are complete. ${tally(progress, day)}`
110 const state = now.isDone ? 'done for today' : 'waiting for you'
111 return `Today's challenge (${state}): ${now.challenge.title}. ${tally(progress, day)}`
112 }
113
114 if (word === 'done' || word === 'complete') {
115 const before = todayOf(await readProgress($), await today($))
116 const { progress, day, isChanged } = await complete($)
117 if (isChanged && before.challenge !== undefined) {
118 return `Marked complete: ${before.challenge.title}. ${tally(progress, day)}`
119 }
120 if (before.isDone && before.challenge !== undefined) {
121 return `You already completed today's challenge (${before.challenge.title}). ${tally(progress, day)}`
122 }
123 return `All ${TOTAL} challenges are complete. ${tally(progress, day)}`
124 }
125
126 if (word === 'another' || word === 'swap') {
127 const { progress, day, isChanged } = await swap($)
128 const now = todayOf(progress, day)
129 if (isChanged && now.challenge !== undefined) return `Swapped. Today's challenge is now: ${now.challenge.title}.`
130 if (now.isDone) return `Today's challenge is already complete. A new one is waiting tomorrow.`
131 return `There is no other challenge left to swap to.`
132 }
133
134 return `Try /challenge to open the pane, /challenge done to mark today's complete, or /challenge another to swap it.`
135}
136
137export const register: Register = on => {
138 let shownDay = ''
139
140 on('session.start', async ($, e, next) => {
141 await $.command.register({
142 name: 'challenge',
143 description: 'Open your daily Claude Code challenge',
144 argumentHint: '[done | another]',
145 })
146 try {
147 const stored = normalize(await $.store.get(STORE_KEY))
148 await update($, progressAtom, held => held ?? stored)
149 shownDay = await today($)
150 $.clock.every(60_000, () => {
151 quietly(
152 today($).then(day => {
153 if (day === shownDay) return
154 shownDay = day
155 $.ui.invalidate('ui.render')
156 }),
157 )
158 })
159 } catch {
160 // The pane falls back to the store on its own; nothing here needs the person.
161 }
162
163 return next(e)
164 })
165
166 on('command.run', { command: 'challenge' }, async ($, e) => {
167 try {
168 return { text: await runCommand($, e.args) }
169 } catch {
170 return { text: FAILED }
171 }
172 }).catch(() => ({ text: FAILED }))
173
174 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
175 const { Box, Button, Text } = $.ui.resolve(e)
176 const progress = await readProgress($)
177 const day = await today($)
178 const cols = Math.max(16, Math.floor(e.props.bodyColumns))
179 const now = todayOf(progress, day)
180 const done = doneCount(progress)
181
182 const headingRow = (label: string) => {
183 const parts = heading(label, cols)
184 return (
185 <Text wrap="truncate">
186 <Text color={COLORS.muted} bold>{parts.label}</Text>
187 <Text color={COLORS.line}>{parts.hairline}</Text>
188 </Text>
189 )
190 }
191
192 const rule = titleRule(cols)
193 const longDate = formatDay(day)
194 const date = TITLE.length + 1 + longDate.length <= cols ? longDate : formatDay(day, true)
195 const showDate = TITLE.length + 1 + date.length <= cols
196
197 const tiles = tileLayout(cols)
198 const tileValues = [streak(progress, day), done, bestStreak(progress)]
199 const tileInner = tiles.width - 4
200
201 const cardInner = cols - 4
202 const card = now.challenge
203 const theme = card === undefined ? undefined : themeOf(card)
204 const themeName = card === undefined ? 'Finished' : card.isCloser ? `${theme?.name} · Closer` : (theme?.name ?? '')
205 const cardTitle = card === undefined ? `All ${TOTAL} complete` : card.title
206 const cardTask =
207 card === undefined
208 ? 'You finished every challenge in the year. Thank you for practising with Claude Code.'
209 : card.task
210 const isGreen = now.isDone || card === undefined
211
212 const chips = chipRows(
213 [
214 { key: 'complete', label: 'Mark complete' },
215 { key: 'coach', label: 'Coach me through it' },
216 ...(canSwap(progress, day) ? [{ key: 'another', label: 'Give me another' }] : []),
217 ],
218 cols,
219 )
220
221 const grid = dayGrid(cols)
222 const cells = lastDays(progress, day, grid.count)
223 const bar = yearBar(done, TOTAL, cols)
224 const doneText = `${done} done`
225 const leftText = `${TOTAL - done} to go`
226
227 const chip = (key: string, label: string, isMain: boolean) => (
228 <Button key={key} plain onPress={() => quietly(press($, key))}>
229 <Text
230 backgroundColor={isMain ? COLORS.gold : COLORS.line}
231 color={isMain ? COLORS.ink : COLORS.text}
232 bold={isMain}
233 >
234 {` ${label} `}
235 </Text>
236 </Button>
237 )
238
239 return (
240 <Box flexDirection="column" width={cols}>
241 <Box flexDirection="row" justifyContent="space-between" width={cols}>
242 <Text bold color={COLORS.text} wrap="truncate">{fit(TITLE, cols)}</Text>
243 {showDate && <Text color={COLORS.muted}>{date}</Text>}
244 </Box>
245 <Text wrap="truncate">
246 <Text color={COLORS.gold}>{rule.gold}</Text>
247 <Text color={COLORS.line}>{rule.hairline}</Text>
248 </Text>
249
250 <Box flexDirection={tiles.direction} columnGap={1} marginTop={1}>
251 {TILE_LABELS.map((label, index) => {
252 const rows = bigNumber(tileValues[index] ?? 0)
253 return (
254 <Box
255 key={`tile-${index}`}
256 borderStyle="round"
257 borderColor={COLORS.line}
258 paddingX={1}
259 width={tiles.width}
260 flexDirection="column"
261 alignItems="center"
262 >
263 {rows.map(row => (
264 <Text color={index === 0 ? COLORS.gold : COLORS.text}>{fit(row, tileInner)}</Text>
265 ))}
266 <Text color={COLORS.muted}>{fit(label, tileInner)}</Text>
267 </Box>
268 )
269 })}
270 </Box>
271
272 <Box marginTop={1} flexDirection="column">
273 {headingRow("Today's challenge")}
274 </Box>
275 <Box
276 borderStyle="round"
277 borderColor={isGreen ? COLORS.green : COLORS.gold}
278 paddingX={1}
279 flexDirection="column"
280 width={cols}
281 >
282 <Text color={card === undefined ? COLORS.green : (theme?.color ?? COLORS.text)}>
283 {fit(themeName.toUpperCase(), cardInner)}
284 </Text>
285 {wrap(cardTitle, cardInner).map(line => (
286 <Text bold color={COLORS.text}>{line}</Text>
287 ))}
288 {wrap(cardTask, cardInner).map(line => (
289 <Text color={COLORS.text}>{line}</Text>
290 ))}
291 </Box>
292
293 {card !== undefined && !now.isDone && (
294 <Box flexDirection="column" marginTop={1}>
295 {chips.map(row => (
296 <Box flexDirection="row" columnGap={1}>
297 {row.map(one => chip(one.key, one.label, one.key === 'complete'))}
298 </Box>
299 ))}
300 </Box>
301 )}
302
303 {now.isDone && (
304 <Box flexDirection="column" marginTop={1}>
305 <Text bold color={COLORS.green}>{fit('Completed today', cols)}</Text>
306 {wrap('A new challenge is waiting for you tomorrow.', cols).map(line => (
307 <Text color={COLORS.text}>{line}</Text>
308 ))}
309 <Box>
310 <Button key="undo" plain dimColor onPress={() => quietly(press($, 'undo'))}>
311 <Text color={COLORS.muted}>Undo</Text>
312 </Button>
313 </Box>
314 </Box>
315 )}
316
317 <Box marginTop={1} flexDirection="column">
318 {headingRow("Last 14 days")}
319 <Text>
320 {cells.map(cell => (
321 <Text color={cell.isDone ? COLORS.green : COLORS.line}>
322 {(cell.isDone ? '■' : '□').padEnd(grid.cell)}
323 </Text>
324 ))}
325 </Text>
326 <Text>
327 {cells.map(cell => (
328 <Text color={cell.isToday ? COLORS.text : COLORS.muted}>
329 {dayLabel(cell.weekday, grid.cell).padEnd(grid.cell)}
330 </Text>
331 ))}
332 </Text>
333 </Box>
334
335 <Box marginTop={1} flexDirection="column">
336 {headingRow("Your year")}
337 <Text>
338 <Text color={COLORS.gold}>{'━'.repeat(bar.filled)}</Text>
339 <Text color={COLORS.line}>{'━'.repeat(bar.empty)}</Text>
340 </Text>
341 <Box flexDirection="row" justifyContent="space-between" width={cols}>
342 <Text color={COLORS.green}>{doneText}</Text>
343 {doneText.length + 1 + leftText.length <= cols && <Text color={COLORS.muted}>{leftText}</Text>}
344 </Box>
345 </Box>
346
347 <Box marginTop={1} flexDirection="column">
348 {wrap(FOOTNOTE, cols).map(line => (
349 <Text color={COLORS.muted}>{line}</Text>
350 ))}
351 </Box>
352 </Box>
353 )
354 })
355}
356
357async function press($: EngineInterface, key: string): Promise<void> {
358 if (key === 'complete') await complete($)
359 else if (key === 'undo') await undo($)
360 else if (key === 'another') await swap($)
361 else if (key === 'coach') await coach($)
362}
363hooks/challenges.ts 461 lines1export type ThemeId =
2 | 'prompting'
3 | 'files'
4 | 'commands'
5 | 'memory'
6 | 'agents'
7 | 'build'
8 | 'habits'
9
10export type Theme = { id: ThemeId; name: string; color: string }
11
12export type Challenge = {
13 id: string
14 theme: ThemeId
15 title: string
16 task: string
17 isCloser: boolean
18}
19
20export const THEMES: readonly Theme[] = [
21 { id: 'prompting', name: 'Prompting', color: '#79C0FF' },
22 { id: 'files', name: 'Files', color: '#D2A8FF' },
23 { id: 'commands', name: 'Commands', color: '#56D4DD' },
24 { id: 'memory', name: 'Memory', color: '#FFA657' },
25 { id: 'agents', name: 'Agents', color: '#FF7B72' },
26 { id: 'build', name: 'Build', color: '#F778BA' },
27 { id: 'habits', name: 'Habits', color: '#A5B4FC' },
28]
29
30type Entry = readonly [title: string, task: string]
31
32const PROMPTING: readonly Entry[] = [
33 ['Lead with the goal', 'Ask Claude for a packing list for a weekend away, and start your message with what the trip is for. Notice how stating the purpose first shapes the list.'],
34 ['Name the audience', 'Ask Claude to explain what a spreadsheet formula is, once for a ten-year-old and once for an accountant. Compare the two answers.'],
35 ['Set a length limit', 'Ask Claude to summarise a topic you care about in exactly three sentences. Check that it kept to the limit.'],
36 ['Ask for a table', 'Ask Claude to compare three ways of getting to work as a table with columns for cost, time and comfort.'],
37 ['Show an example', 'Give Claude one example of a friendly reminder text you like, then ask for three more in the same style.'],
38 ['Ask what it needs', 'Describe a small plan you have, then ask Claude what extra information would help it help you.'],
39 ['Request three options', 'Ask Claude for three subject lines for an email to a neighbour, each in a different tone.'],
40 ['Name the format', 'Ask Claude for a recipe as a numbered shopping list followed by numbered steps.'],
41 ['Add two constraints', 'Ask Claude to plan a dinner menu with two limits of your choosing, such as no nuts and under thirty minutes.'],
42 ['Let it interview you', 'Tell Claude you want help planning a celebration, and ask it to question you one point at a time before it suggests anything.'],
43 ['Refine in one step', 'Ask Claude to rewrite a sentence you wrote recently, then reply with one specific change and watch it adjust.'],
44 ['Ask for the reasoning', 'Ask Claude which of two phone plans is better value for you, and ask it to show the steps behind its choice.'],
45 ['Choose the tone', 'Ask Claude for a short thank-you note in a warm tone, then for the same note in a formal tone.'],
46 ['Shrink a big task', 'Pick a chore that feels large and ask Claude to break it into steps of ten minutes or less.'],
47 ['Say what to avoid', 'Ask Claude for weekend activity ideas and tell it what you do not want, such as anything that costs money.'],
48 ['Make it a checklist', 'Ask Claude to turn your morning routine into a short checklist you could print.'],
49 ['Give it a role', 'Ask Claude to act as a patient librarian and recommend three books based on one you loved.'],
50 ['Weigh a decision', 'Pick a decision you are weighing and ask Claude for an even list of pros and cons, then for the one question that matters most.'],
51 ['Ask for plain words', 'Paste a confusing paragraph from a bill, letter or policy and ask Claude to explain it in plain words.'],
52 ['Quiz yourself', 'Ask Claude to quiz you on a topic you know, one question at a time, waiting for your answer before the next.'],
53 ['Draft then edit', 'Ask Claude to draft a polite message declining an invitation, then change one line of it yourself.'],
54 ['Point to the error', 'Ask Claude a factual question, and if any part looks off, tell it exactly which part and ask it to check again.'],
55 ['Ask how sure', 'Ask Claude a question about history, then ask how confident it is in each part of its answer.'],
56 ['Get a one-liner', 'After the next long answer you get today, ask Claude to sum it up in one line.'],
57 ['Compare two versions', 'Ask Claude for two versions of a short bio of you, and ask it to say what makes them different.'],
58 ['Give context first', 'Before asking for advice on a hobby, tell Claude your experience level in two sentences and see how the advice changes.'],
59 ['Ask for the next step', 'Describe something you are stuck on and ask Claude for only the very next step, not the whole plan.'],
60 ['Cap the bullets', 'Ask Claude for tips on sleeping better, in no more than five bullets.'],
61 ['Ask for a rubric', 'Ask Claude how it would judge whether the opening of a short story is good, as a simple checklist.'],
62 ['Argue the other side', 'Ask Claude to make the best short case for a view you disagree with, fairly and calmly.'],
63 ['Ask for examples', 'Ask Claude to explain a word you find tricky, with three example sentences.'],
64 ['Describe the reader', 'Ask Claude to write a notice for a shared fridge, and tell it who will read it and where it will hang.'],
65 ['Keep the best line', 'Ask Claude for a paragraph, tell it which sentence you like most, and ask it to rewrite the rest around that sentence.'],
66 ['Ask for a timeline', 'Ask Claude to lay out a two-week plan for learning a new skill as a day-by-day timeline.'],
67 ['Prepare good questions', 'Ask Claude for five good questions to ask at an appointment or meeting you have coming up.'],
68 ['Notes into prose', 'Type a few rough notes about your day and ask Claude to turn them into a tidy paragraph.'],
69 ['Prose into notes', 'Paste a long paragraph and ask Claude to turn it into short notes you could scan in ten seconds.'],
70 ['Find a simpler way', 'Describe how you do a weekly task today and ask Claude whether there is a simpler way.'],
71 ['Request a template', 'Ask Claude for a fill-in-the-blanks template for a message you send often.'],
72 ['Set a word budget', 'Ask Claude to describe your town in fifty words or fewer.'],
73 ['Score your ideas', 'Ask Claude to rate three of your ideas from one to five on effort and on payoff.'],
74 ['Ask for a rough draft', 'Ask Claude for a rough first draft of a short toast and say you will polish it yourself.'],
75 ['Define what good means', 'Before asking Claude to write a product review, describe what a helpful review looks like to you.'],
76 ['Ask for the catch', 'Describe a deal or offer you have seen and ask Claude what the catch might be.'],
77 ['Separate known from unsure', 'Ask Claude about a topic and ask it to say which parts are well known and which you should double-check.'],
78 ['Ask for an analogy', 'Ask Claude to explain how the internet reaches your home using an everyday comparison.'],
79 ['Chain two requests', 'Ask Claude for a list of gift ideas, then in your next message ask it to rank them for one particular person.'],
80 ['Halve an answer', 'Take an answer you got today and ask Claude to make it half as long without losing the main point.'],
81 ['Have it repeat back', 'Before Claude starts a task for you, ask it to say back what it thinks you want.'],
82 ['Ask what could slip', 'Describe a simple plan, such as a watering schedule, and ask Claude what could go wrong with it.'],
83 ['Ask like a friend', 'Ask Claude something the way you would ask a friend, with no special phrasing, and see how well it copes.'],
84 ['Keep your best prompt', 'Look back over this week, find the prompt that worked best, and save it in a note for next time.'],
85]
86
87const FILES: readonly Entry[] = [
88 ['Point at a file', 'Type @ in the prompt box, pick any file in your folder, and ask Claude what it contains.'],
89 ['Summarise a document', 'Ask Claude to read a document in your folder and summarise it in five bullets.'],
90 ['List your folder', 'Ask Claude to list the files in your current folder, grouped by type.'],
91 ['Read a PDF', 'Ask Claude to read a PDF you have and pull out its three most important points.'],
92 ['Find the deadlines', 'Ask Claude to find every date or deadline mentioned in a PDF or letter you have.'],
93 ['Describe an image', 'Point Claude at an image in your folder and ask it to describe what it shows.'],
94 ['Paste a screenshot', 'Copy a screenshot to your clipboard, press Ctrl+V in the prompt box, and ask Claude about it.'],
95 ['Total a column', 'Ask Claude to open a spreadsheet or CSV file and tell you the total of one column.'],
96 ['Start an ideas file', 'Ask Claude to create a file called ideas.md with three things you want to try this month.'],
97 ['Fix the spelling', 'Ask Claude to fix the spelling in a document you wrote, and review each change before it saves.'],
98 ['Rename with care', 'Ask Claude to suggest clearer names for three files, and approve each rename one at a time.'],
99 ['Plan your folders', 'Ask Claude to suggest a simple folder structure for your personal documents, without moving anything yet.'],
100 ['Compare two drafts', 'Ask Claude to compare two versions of a document and list what changed between them.'],
101 ['Lift out a table', 'Ask Claude to pull a table out of a PDF or document and save it as a spreadsheet.'],
102 ['Clean up a list', 'Give Claude a file holding a messy list and ask it to remove duplicates and sort what is left.'],
103 ['Tidy the formatting', 'Ask Claude to turn a plain text file into a neatly formatted Markdown document.'],
104 ['Search your files', 'Ask Claude to find every file in your folder that mentions a word you choose.'],
105 ['Read a trend', 'Ask Claude to read a spreadsheet and describe the trend in one column in plain words.'],
106 ['Write a README', 'Ask Claude to write a short README file explaining what is in your current folder.'],
107 ['Find the biggest files', 'Ask Claude which files in your folder are the largest, without deleting anything.'],
108 ['Read text from a photo', 'Show Claude a photo of a sign, receipt or handwritten note and ask it to type out the words.'],
109 ['Check a form', 'Ask Claude to read a form you are filling in and list anything you still need to provide.'],
110 ['Merge two notes', 'Ask Claude to combine two short notes files into one tidy document.'],
111 ['Make a small spreadsheet', 'Ask Claude to create a CSV file tracking five things you want to buy, with a price column.'],
112 ['Newest first', 'Ask Claude to list the files in a folder from newest to oldest.'],
113 ['Outline a long document', 'Ask Claude to read a long document and give you its outline as headings only.'],
114 ['Quote it exactly', 'Ask Claude to find a particular sentence in a document and quote it word for word, saying where it appears.'],
115 ['Catch mismatches', 'Ask Claude to check a document for names, dates or numbers that do not agree with each other.'],
116 ['Add another folder', 'Run /add-dir to let Claude see a second folder in this session, then ask it to list what is there.'],
117 ['Explain a chart', 'Show Claude a picture of a chart or graph and ask what it says.'],
118 ['Build a glossary', 'Ask Claude to read a document and list its special terms, each with a short definition.'],
119 ['Split by topic', 'Ask Claude to propose how to split a long notes file into separate files by topic, and approve the plan before it acts.'],
120 ['Fill in a template', 'Write a short template with blanks, then ask Claude to make a filled-in copy for a real case.'],
121 ['Find empty cells', 'Ask Claude to look through a spreadsheet for empty cells and tell you where they are.'],
122 ['Hear the rough spots', 'Ask Claude to read a document and point out the sentences that would be hard to read aloud.'],
123 ['Add contents', 'Ask Claude to add a short table of contents to the top of a Markdown document.'],
124 ['Count the words', 'Ask Claude how many words a document has and roughly how long it takes to read.'],
125 ['Gather the contacts', 'Ask Claude to pull every name and email address from a document into a list saved on your machine.'],
126 ['Keep a backup copy', 'Before any edit today, ask Claude to copy the file first with -backup added to its name.'],
127 ['Write alt text', 'Ask Claude to write a short description of an image, the kind a screen reader would read aloud.'],
128 ['Decode a formula', 'Ask Claude to explain in plain words what one formula in your spreadsheet is doing.'],
129 ['Study notes from PDF', 'Ask Claude to read a PDF and write study notes into a new file.'],
130 ['Plan a photo rename', 'Ask Claude to show you a plan for renaming a group of photos by date, and approve it only if it looks right.'],
131 ['What changed this week', 'Ask Claude which files in your folder changed in the last seven days.'],
132 ['A tick-off trip file', 'Ask Claude to create a checklist file for an upcoming trip that you can tick off as you pack.'],
133 ['Translate a document', 'Ask Claude to translate a short document into another language and save it as a new file.'],
134 ['One line per file', 'Ask Claude to skim every document in a small folder and write one line about each.'],
135 ['Compare two months', 'Ask Claude to compare two columns or months in a spreadsheet and say what went up and what went down.'],
136 ['Action items from email', 'Save a long email as a text file and ask Claude to list the action items in it.'],
137 ['Match the headings', 'Ask Claude to make all the headings in a document consistent in style.'],
138 ['Collect the links', 'Ask Claude to list every web link mentioned in a document you have.'],
139 ['Undo a file change', 'Make a small change to a file with Claude, then run /rewind to go back to before it.'],
140]
141
142const COMMANDS: readonly Entry[] = [
143 ['Open the help', 'Run /help and read through the list of commands available to you.'],
144 ['See the shortcuts', 'With the prompt box empty, type ? to see the keyboard shortcuts.'],
145 ['Browse with slash', 'Type / in the prompt box and scroll through the commands that appear.'],
146 ['Check your status', 'Run /status to see your version, model and account.'],
147 ['Look at context', 'Run /context to see how much of Claude\'s working memory this conversation is using.'],
148 ['Compact a long chat', 'In a long conversation, run /compact to summarise it and free up room.'],
149 ['Start with a clean slate', 'Run /clear to start a new conversation with empty context.'],
150 ['Pick up where you left', 'Run /resume, look through your past conversations, and open one.'],
151 ['Name this session', 'Run /rename and give this conversation a name you will recognise later.'],
152 ['Meet the models', 'Run /model to see the models available to you and what each is for.'],
153 ['Try another effort level', 'Run /effort to see the effort levels, then ask a question at a different level.'],
154 ['Pick a theme', 'Run /theme and try a theme that is easy on your eyes.'],
155 ['Colour the prompt bar', 'Run /color to give this session\'s prompt bar a colour of its own.'],
156 ['Open the settings', 'Run /config and read through the settings you can change.'],
157 ['Copy the last answer', 'Run /copy to put Claude\'s last response on your clipboard, then paste it somewhere.'],
158 ['Export a conversation', 'Run /export to save this conversation to a file or to your clipboard.'],
159 ['See your usage', 'Run /usage to see your session cost or plan usage.'],
160 ['Interrupt and redirect', 'Ask Claude something long, press Esc to stop it partway, and tell it what you want instead.'],
161 ['Clear the box quickly', 'Type a sentence in the prompt box, then press Esc twice to clear it.'],
162 ['Search past prompts', 'Press Ctrl+R and type a word to find something you asked before.'],
163 ['Bring back a prompt', 'Press the up arrow in an empty prompt box to bring back your last prompt.'],
164 ['Add a line break', 'While typing, press Ctrl+J to start a new line without sending.'],
165 ['Write in your editor', 'Press Ctrl+G to write a long prompt in your text editor, then save and close it to bring the text back.'],
166 ['Open the transcript', 'Press Ctrl+O to see the detailed transcript, then press it again to return.'],
167 ['Cycle the modes', 'Press Shift+Tab a few times and watch the mode shown under the prompt change.'],
168 ['Plan without changes', 'Run /plan and ask Claude to plan a small task without changing anything.'],
169 ['Ask by the way', 'While Claude is working, use /btw to ask a quick side question without interrupting it.'],
170 ['Get a recap', 'Run /recap to get a one-line recap of this session.'],
171 ['Take a lesson', 'Run /powerup and complete one of the short interactive lessons.'],
172 ['Read the release notes', 'Run /release-notes and find one change you did not know about.'],
173 ['Run a checkup', 'Run /doctor to check that your Claude Code setup is healthy.'],
174 ['Stash a draft', 'Type a half-finished prompt, press Ctrl+S to stash it, send something else, and see your draft come back.'],
175 ['Run a shell command', 'Type ! followed by a simple command such as dir or ls to run it directly and let Claude see the result.'],
176 ['See background tasks', 'Run /tasks to see what is running in the background right now.'],
177 ['List your skills', 'Run /skills to see every skill Claude can use in this session.'],
178 ['Review permissions', 'Run /permissions to see which actions Claude may take without asking you.'],
179 ['Read your insights', 'Run /insights to get a report on how you have been using Claude Code.'],
180 ['Branch the conversation', 'Run /branch to copy this conversation at this point, so you can try a different direction.'],
181 ['Set a goal', 'Run /goal with a short goal, such as finishing a document, and see Claude check it before stopping.'],
182 ['Try focus view', 'Run /focus to see only your prompt, the summary and the response, then run it again to switch back.'],
183 ['Show the to-do list', 'Ask Claude to do a three-step task, then press Ctrl+T to show or hide its to-do list.'],
184 ['Send to background', 'While a long command runs, press Ctrl+B to send it to the background and keep chatting.'],
185 ['Mention a file fast', 'Type @ and the first letters of a file name, then pick it from the suggestions.'],
186 ['Change the output style', 'Run /output-style to see the response styles available, and try one.'],
187 ['Set up a status line', 'Run /statusline and ask for a status line that shows your folder and model.'],
188 ['Read your keybindings', 'Run /keybindings to open your keyboard shortcuts file and read it, without changing anything.'],
189 ['Browse your plugins', 'Run /plugin to see the plugins and mods you have installed.'],
190 ['Look at connectors', 'Run /mcp to see which connectors are set up in this session.'],
191 ['Check your hooks', 'Run /hooks to see whether any automatic actions are set up.'],
192 ['Try the other layout', 'Run /tui to read about the default and fullscreen layouts, and try the one you are not using.'],
193 ['Make an alias', 'Run /alias to give a command you use often a shorter name.'],
194 ['Check your version', 'Run /version to see exactly which version of Claude Code this session runs.'],
195]
196
197const MEMORY: readonly Entry[] = [
198 ['Open your memory', 'Run /memory to see which instruction files Claude reads in this folder.'],
199 ['Create a CLAUDE.md', 'Ask Claude to create a CLAUDE.md file in a folder you use, with one line saying what the folder is for.'],
200 ['Prefer short answers', 'Add a line to your instructions asking Claude to keep answers short unless you ask for detail.'],
201 ['Test a saved preference', 'Run /clear to start fresh, then check that Claude follows a preference you have saved.'],
202 ['Let init draft it', 'In a folder with some documents, run /init and read the CLAUDE.md it drafts before keeping it.'],
203 ['Ask what it follows', 'Ask Claude which instructions it is following in this folder right now.'],
204 ['Save your name', 'Add a line to your personal instructions with the name you like to be called.'],
205 ['Remove an old rule', 'Run /memory, find an instruction that no longer fits, and remove it.'],
206 ['Personal or folder', 'Ask Claude to explain the difference between your personal instructions and a folder\'s CLAUDE.md.'],
207 ['Write a house style', 'Add three bullet points to a CLAUDE.md describing how you like documents formatted.'],
208 ['Read about a skill', 'Run /skills, pick one skill, and ask Claude what it does.'],
209 ['Use a skill by name', 'Ask Claude to use one of your listed skills, by name, for a small task.'],
210 ['What a skill is', 'Ask Claude to explain in plain words what a skill is and when one gets used.'],
211 ['Draft a tiny skill', 'Ask Claude to help you write a skill that turns rough notes into a meeting summary.'],
212 ['Test a skill', 'Use one of your skills on a real piece of work and decide whether it helped.'],
213 ['Improve a skill', 'Ask Claude to read one of your skills and suggest one improvement to its instructions.'],
214 ['Reload your skills', 'After a skill changes on disk, run /reload-skills so this session picks up the change.'],
215 ['Find unused skills', 'Run /skill-doctor to see which skills you never use.'],
216 ['Save tricky names', 'Add a short list of names or terms Claude tends to get wrong for you to your CLAUDE.md.'],
217 ['Choose your spelling', 'Add a line asking Claude to use British or American spelling, whichever you prefer.'],
218 ['Name your apps', 'Tell Claude in your CLAUDE.md which apps you use for notes and calendars, so its suggestions fit.'],
219 ['Review your instructions', 'Ask Claude to read your CLAUDE.md and point out anything unclear or contradictory.'],
220 ['Trim to essentials', 'Ask Claude to shorten your CLAUDE.md to the rules that matter most.'],
221 ['Ask before deleting', 'Add a line asking Claude to always ask before deleting any file.'],
222 ['Set a date format', 'Add a line asking Claude to write dates in the format you prefer.'],
223 ['A rule for one folder', 'Create a CLAUDE.md in one folder with a rule that applies only there, and test it.'],
224 ['Which rule wins', 'Ask Claude what happens when your personal instructions and a folder\'s instructions disagree.'],
225 ['Log a decision', 'Ask Claude to add today\'s date and one decision you made to a decisions note in your folder.'],
226 ['Save a repeat checklist', 'Put a checklist you repeat often into your CLAUDE.md so Claude can follow it when you ask.'],
227 ['Map the key files', 'Ask Claude to add a short section to CLAUDE.md describing the most important files in this folder.'],
228 ['Save a correction', 'Correct Claude on something small, then ask it to save that correction to your instructions.'],
229 ['Read every saved line', 'Open your instruction files with /memory and read every line you have saved so far.'],
230 ['Learn the memory settings', 'Run /memory, look at the memory settings, and ask Claude what each one does.'],
231 ['Save your abbreviations', 'Add three abbreviations you use to your CLAUDE.md, with what each stands for.'],
232 ['End with a summary', 'Add a line asking Claude to finish longer answers with a one-line summary.'],
233 ['Describe yourself briefly', 'Add one sentence about who you are, such as your work or a hobby, so Claude\'s examples fit you.'],
234 ['Find a skill idea', 'Ask Claude what kinds of skills people often add, and choose one idea for yourself.'],
235 ['Skills from plugins', 'Run /plugin and see which installed plugins bring skills of their own.'],
236 ['A checking skill', 'Ask Claude to write a skill that checks a document against a short checklist you give it.'],
237 ['Prepare to share', 'Ask Claude what you should remove from a CLAUDE.md before sharing it with someone else.'],
238 ['Date your rules', 'Add today\'s date in brackets beside one rule, so you remember when you added it.'],
239 ['Remove repeated rules', 'Ask Claude to find any instructions that say the same thing twice across your memory files.'],
240 ['Ask about import', 'Ask Claude what /import does and whether it would be useful to you.'],
241 ['Save a handy link', 'Add a link you use often to your CLAUDE.md, with a note on when Claude should use it.'],
242 ['Remove something saved', 'Ask Claude how to remove something it saved for you, then remove one item.'],
243 ['Write a never list', 'Add two things you never want Claude to do, such as sending messages without asking.'],
244 ['Check a rule holds', 'Pick one rule in your instructions and write a short prompt that tests whether Claude follows it.'],
245 ['Sharpen a description', 'Ask Claude to rewrite one skill\'s description so it is clear when that skill should be used.'],
246 ['Note a project', 'Ask Claude to add a short project note to CLAUDE.md with the goal, the deadline and who it is for.'],
247 ['Ask what slows it', 'Ask Claude whether anything in your instructions makes its job harder.'],
248 ['Copy before changing', 'Ask Claude to make a dated copy of your CLAUDE.md before you change it.'],
249 ['Habit into a skill', 'Pick something you ask for every week and ask Claude to turn it into a skill.'],
250]
251
252const AGENTS: readonly Entry[] = [
253 ['Meet the helpers', 'Ask Claude what a subagent is and when it would use one for you.'],
254 ['Two at once', 'Ask Claude to research two unrelated questions at the same time using helpers, and compare the answers.'],
255 ['Send a subtask', 'Run /subtask with a small research question and wait for its answer to come back here.'],
256 ['Fork this session', 'Run /fork to copy this conversation into a background session, then keep working here.'],
257 ['Watch it finish', 'Start something in the background and use /tasks to watch it finish.'],
258 ['Look at schedules', 'Run /schedule and see how a scheduled agent is set up, without creating one yet.'],
259 ['Sketch a routine', 'Ask Claude to help you plan a weekly scheduled agent, writing down what it would do before you decide to create it.'],
260 ['Try a loop', 'Ask Claude how /loop works, set one up to check something every few minutes, then stop it.'],
261 ['Pick a connector', 'Run /mcp, choose one connector, and ask Claude what it can do with it.'],
262 ['What connectors do', 'Ask Claude which connectors you have and what each could do for you.'],
263 ['Next three events', 'If you have a calendar connector, ask Claude what your next three events are.'],
264 ['Find an email', 'If you have an email connector, ask Claude to find the last message from someone you choose, without replying.'],
265 ['Find a document online', 'If you have a Drive or documents connector, ask Claude to find a document by its topic.'],
266 ['Split a question three ways', 'Ask Claude to split a question into three parts and send a helper after each one.'],
267 ['Compare two helpers', 'Ask two helpers the same question in different words, and ask Claude to compare what came back.'],
268 ['Fresh eyes review', 'After Claude writes something for you, ask it to send a helper to review it with fresh eyes.'],
269 ['Check the workflows', 'Run /workflows to see whether any multi-step workflows are running or finished.'],
270 ['Ask what it is doing', 'While a helper is working, ask Claude what the helper is doing right now.'],
271 ['Stop a background job', 'Ask Claude how to stop a background job you no longer need, then stop one.'],
272 ['Save the findings', 'Ask a helper to gather facts on a topic, then ask Claude to save the findings to a file.'],
273 ['Ask about the cloud', 'Ask Claude what /teleport does and when sending a session to the cloud would help you.'],
274 ['Keep a job running', 'Ask Claude how to keep a long job running in the background while you do something else.'],
275 ['Summaries in parallel', 'Ask Claude to use helpers to summarise several documents at the same time.'],
276 ['Plan, then delegate', 'Ask Claude to plan a job first, then hand each step to its own helper.'],
277 ['Read a web page', 'If Claude in Chrome is connected, ask Claude to open a web page and tell you its headline.'],
278 ['Know a connector\'s reach', 'Ask Claude what a connector can read and what it can change before you use it.'],
279 ['Design a morning brief', 'Ask Claude what a scheduled morning briefing could include for you, and write the list down.'],
280 ['Meet the advisor', 'Run /advisor, read what it does, and decide whether you want it on.'],
281 ['Write the helper\'s brief', 'Ask Claude to send a helper with a one-sentence job description that you write yourself.'],
282 ['Estimate the job', 'Before a multi-step job, ask Claude how long it might take and how many helpers it would use.'],
283 ['Cap the helpers', 'Ask Claude to do a research task using no more than two helpers.'],
284 ['Three current facts', 'Ask Claude to look up three current facts about a topic and give you the links.'],
285 ['Compare three products', 'Ask Claude to have helpers research three products you are considering, one each, and compare them.'],
286 ['Report surprises', 'Ask Claude to tell you as soon as a helper finds something surprising.'],
287 ['List every helper', 'At the end of a session, ask Claude to list every helper it used and what each did.'],
288 ['Draft, do not send', 'If you have a messaging connector, ask Claude to draft a message there without sending it.'],
289 ['Set a check-in', 'Ask Claude to remind you in this session, in thirty minutes, to say how a task is going.'],
290 ['Cancel a repeat job', 'Ask Claude to list any scheduled or repeating jobs you have, and cancel one you no longer need.'],
291 ['List your routines', 'Run /schedule and look at the routines you already have, if any.'],
292 ['Connector rules', 'Run /permissions and look for any rules about connector tools.'],
293 ['Three-line status', 'While a long job runs, ask Claude for a three-line status report.'],
294 ['Peek at a helper', 'While a helper runs, open /tasks and look at what it is working on.'],
295 ['Cloud environments', 'Run /remote-env to see the environments cloud agents can use.'],
296 ['Three viewpoints', 'Ask Claude to have three helpers each argue a different view on a question, then summarise them.'],
297 ['Fact-check a claim', 'Ask Claude to send a helper to fact-check one claim in a document you have.'],
298 ['Research in the background', 'Ask Claude to run a research question in the background and tell you when it is done.'],
299 ['Find a new connector', 'Ask Claude what connectors exist for an app you use and how you would add one.'],
300 ['Meeting prep notes', 'If you have a calendar connector, ask Claude to prepare short notes for your next meeting.'],
301 ['Write agent instructions', 'Ask Claude to draft the instructions a weekly summary agent would follow, and save them to a file.'],
302 ['Helpers and usage', 'Ask Claude how running several helpers affects your usage, then check /usage.'],
303 ['Write a handoff note', 'Ask Claude to write a short handoff note so a helper could pick up a task without the whole conversation.'],
304 ['Review a helper\'s work', 'When a helper finishes, ask Claude what it checked and what it did not.'],
305]
306
307const BUILD: readonly Entry[] = [
308 ['A hobby page', 'Ask Claude to make a one-page HTML file about a hobby of yours, then open it in your browser.'],
309 ['A packing checklist page', 'Ask Claude to build a simple web page checklist you can tick off for your next trip.'],
310 ['A bill splitter', 'Ask Claude to make a small web page that splits a bill between friends and adds a tip.'],
311 ['A countdown page', 'Ask Claude to build a web page that counts down the days to an event you care about.'],
312 ['A recipe card', 'Ask Claude to make a printable recipe card page for a dish you cook.'],
313 ['A habit tracker', 'Ask Claude to make a spreadsheet that tracks one habit across this month.'],
314 ['A monthly budget', 'Ask Claude to make a simple budget spreadsheet with income, spending and what is left.'],
315 ['A reading list', 'Ask Claude to make a reading list file with title, author and a done column.'],
316 ['A quiz page', 'Ask Claude to make a five-question quiz web page on a topic you love.'],
317 ['A dinner picker', 'Ask Claude to build a page that picks a random dinner idea from a list you give it.'],
318 ['A birthday card', 'Ask Claude to make a birthday card as a web page for someone you know.'],
319 ['A focus timer', 'Ask Claude to make a web page with a ten-minute focus timer.'],
320 ['A chore chart', 'Ask Claude to make a printable weekly chore chart for your household.'],
321 ['A watering schedule', 'Ask Claude to make a watering schedule table for your plants.'],
322 ['A gift tracker', 'Ask Claude to make a spreadsheet for gift ideas and budgets for the people you buy for.'],
323 ['A baking converter', 'Ask Claude to make a page that converts cups to grams for common baking ingredients.'],
324 ['A flashcard set', 'Ask Claude to make a flashcard web page for ten words you want to learn.'],
325 ['A meal planner', 'Ask Claude to make a week-long meal plan table and a shopping list drawn from it.'],
326 ['An emergency contacts sheet', 'Ask Claude to make a one-page emergency contacts sheet you can print, with blanks to fill in.'],
327 ['A mood log', 'Ask Claude to make a simple spreadsheet for noting your mood each day.'],
328 ['A word counter', 'Ask Claude to make a page where you paste text and see how many words it has.'],
329 ['A colour palette', 'Ask Claude to make a page showing five colours that go well together, with their codes.'],
330 ['A trip itinerary', 'Ask Claude to turn a trip plan into a tidy day-by-day itinerary document.'],
331 ['A savings goal', 'Ask Claude to make a spreadsheet showing how long it takes to save for something at a set amount each week.'],
332 ['A party plan', 'Ask Claude to make a one-page party plan with guests, food and a timeline.'],
333 ['A stretching card', 'Ask Claude to make a printable card with a gentle ten-minute stretching routine.'],
334 ['Book club questions', 'Ask Claude to make a simple page of discussion questions for a book you have read.'],
335 ['A garden bed map', 'Ask Claude to draw a simple layout of a small garden bed showing what grows where.'],
336 ['A film night picker', 'Ask Claude to make a page that shuffles a list of films and shows one.'],
337 ['A family calendar', 'Ask Claude to make a printable monthly calendar page with a column for each person.'],
338 ['A price per unit table', 'Ask Claude to make a table comparing the price per unit of a few products you buy.'],
339 ['A thank-you list', 'Ask Claude to make a file listing the people to thank after an event, with a done column.'],
340 ['A study timetable', 'Ask Claude to make a study timetable for an exam or course, as a table.'],
341 ['A talk outline', 'Ask Claude to make a five-slide outline for a short talk on something you know well.'],
342 ['A family newsletter', 'Ask Claude to make a short newsletter about your month for friends or family, as a document.'],
343 ['Name badges', 'Ask Claude to make a printable page of name badges for a small event.'],
344 ['A score keeper', 'Ask Claude to make a page for keeping score in a board game with two to four players.'],
345 ['Box labels', 'Ask Claude to make printable labels for boxes when moving or storing things.'],
346 ['A care guide', 'Ask Claude to make a one-page guide for someone looking after your pet or plants.'],
347 ['A journal template', 'Ask Claude to make a journal page template with three short prompts for each day.'],
348 ['A bookmarks page', 'Ask Claude to make a web page with links to the ten sites you visit most.'],
349 ['A recipe scaler', 'Ask Claude to make a page that doubles or halves the amounts in a recipe.'],
350 ['A wish list page', 'Ask Claude to make a simple wish list page you could share with family.'],
351 ['A walking log', 'Ask Claude to make a spreadsheet to log walks or runs with distance and time.'],
352 ['A loan calculator', 'Ask Claude to make a spreadsheet that shows the monthly payment for a loan amount and rate you enter.'],
353 ['A seating plan', 'Ask Claude to make a seating plan for a dinner, keeping two people you name apart.'],
354 ['A playlist with notes', 'Ask Claude to make a document listing songs for an occasion, with a note on why each fits.'],
355 ['A priorities page', 'Ask Claude to make a web page showing today\'s date and your three priorities.'],
356 ['An errand route', 'Ask Claude to make a checklist of stops for a day of errands, in a sensible order.'],
357 ['An activity sheet', 'Ask Claude to make a printable puzzle or activity sheet for a child.'],
358 ['A personal FAQ', 'Ask Claude to make a short document answering questions people often ask you.'],
359 ['Polish a build', 'Pick something you have built with Claude and ask it to make it look more polished; if you have nothing yet, ask for a simple page first.'],
360]
361
362const HABITS: readonly Entry[] = [
363 ['Read before approving', 'The next time Claude asks permission, read exactly what it wants to do before you approve.'],
364 ['Ask for a self-check', 'After Claude finishes a task, ask it to check its own work and list anything it is unsure of.'],
365 ['Verify one fact', 'Pick one fact Claude told you today and check it against another source.'],
366 ['Plan before acting', 'Before a bigger task, ask Claude for a plan and approve it before anything changes.'],
367 ['Keep secrets out', 'Ask Claude what kinds of information you should never paste into a conversation.'],
368 ['Read the change', 'After Claude edits a file, open it yourself and read what changed.'],
369 ['Know what rewind undoes', 'Ask Claude what /rewind can and cannot undo, so you know your safety net.'],
370 ['Track usage over a session', 'Run /usage at the start and end of a session and compare the two.'],
371 ['Clear between topics', 'When you switch topics today, run /clear first and notice whether answers stay sharper.'],
372 ['Compact in time', 'Run /context, and if the conversation is getting full, run /compact before it slows down.'],
373 ['Ask where to confirm', 'When Claude states a fact you will rely on, ask where you could confirm it.'],
374 ['Describe moves first', 'Before letting Claude reorganise any folder, ask it to describe exactly what will move where.'],
375 ['Match model to task', 'Ask Claude which model suits a quick question and which suits a long task, then choose one with /model.'],
376 ['Lighter effort for small asks', 'For a quick question today, use /effort to pick a lower level and see whether the answer is still good.'],
377 ['Stop early', 'Practise pressing Esc as soon as Claude heads the wrong way, then tell it what you wanted.'],
378 ['Spot the guesses', 'Ask Claude to mark any part of its answer that is a guess rather than something it knows.'],
379 ['Define done', 'Before a task, tell Claude exactly what finished will look like.'],
380 ['Prune permissions', 'Run /permissions and remove any rule you do not remember adding.'],
381 ['Protect passwords', 'Ask Claude how it should treat a file that contains a password, and add a rule if you want one.'],
382 ['Question the risky step', 'In plan mode, read Claude\'s plan and ask one question about the riskiest step before approving.'],
383 ['Batch small asks', 'Put three small requests into one clear message instead of sending three separate ones.'],
384 ['Close with a summary', 'Before you end a session, ask Claude what it changed and what is left to do.'],
385 ['Keep a session log', 'Ask Claude to add a dated line to a log file describing what you did together today.'],
386 ['Test its confidence', 'Ask Claude a question you already know the answer to, and check that its confidence matches its accuracy.'],
387 ['Prefer a dry run', 'Ask Claude to describe what it would do to a folder without doing it, then decide.'],
388 ['Pick a permission mode', 'Ask Claude what each permission mode allows, then choose the one that matches how careful you want to be today.'],
389 ['Read before it goes', 'If Claude drafts any message for you today, read it in full before it goes anywhere.'],
390 ['Pause with a recap', 'After a long session, run /recap, note where you are, and step away for five minutes.'],
391 ['Ask how to undo', 'Before approving a change, ask Claude what could go wrong and how to undo it.'],
392 ['Spot-check a summary', 'Ask Claude to summarise a document, then read one section yourself and compare.'],
393 ['Restate the goal', 'In a long chat, ask Claude to restate your original goal and check it still matches.'],
394 ['Right tool for the job', 'Ask Claude whether a task you have in mind is better done here, in a spreadsheet app, or by hand.'],
395 ['Check a download', 'Ask Claude how to tell whether a file or link someone sent you is safe to open.'],
396 ['Share only what is needed', 'Before pasting anything about another person, ask Claude whether it needs that detail at all.'],
397 ['Review your week', 'Run /insights and pick one habit to keep and one to change.'],
398 ['Read the error', 'When something fails, ask Claude to explain the error message in plain words before trying again.'],
399 ['One change at a time', 'Ask Claude to make changes one at a time today, checking each before the next.'],
400 ['Make a backup', 'Ask Claude how to back up an important folder, and make one backup now.'],
401 ['Test on a copy', 'Ask Claude to try a risky change on a copy of a file first.'],
402 ['Weigh a big job', 'Before a large job with several helpers, ask Claude to estimate the effort and decide whether it is worth it.'],
403 ['Send feedback', 'Run /feedback and send one honest note about something that helped or confused you.'],
404 ['Check what is current', 'Ask Claude what today\'s date is and whether its knowledge might be out of date for your question.'],
405 ['Choose the safer path', 'When Claude offers two approaches, ask which one is easier to undo.'],
406 ['Look before cleanup', 'Ask Claude to list exactly which files would be removed before any cleanup.'],
407 ['Check the sandbox', 'Run /sandbox to see whether commands run in a protected sandbox on your machine.'],
408 ['Open the link', 'Ask Claude for a web link, then check that it opens to the page it described.'],
409 ['Hold the scope', 'If Claude starts doing more than you asked, press Esc and restate the scope in one sentence.'],
410 ['Set a time box', 'Tell Claude you have ten minutes and ask it to aim for the most useful result in that time.'],
411 ['Close open loops', 'At the end of the day, ask Claude for a list of anything you started but did not finish.'],
412 ['Stay up to date', 'Run /status to see your version, then ask Claude how updates reach you.'],
413 ['Argue against itself', 'For an answer that matters, ask Claude to argue against its own conclusion.'],
414 ['Choose what to practise', 'Ask Claude to look at what you learned this week and suggest what to practise next.'],
415]
416
417const BY_THEME: Record<ThemeId, readonly Entry[]> = {
418 prompting: PROMPTING,
419 files: FILES,
420 commands: COMMANDS,
421 memory: MEMORY,
422 agents: AGENTS,
423 build: BUILD,
424 habits: HABITS,
425}
426
427export const PER_THEME = 52
428export const TOTAL = THEMES.length * PER_THEME + 1
429
430const CLOSER: Challenge = {
431 id: 'closer',
432 theme: 'prompting',
433 title: 'Write your next year',
434 task: 'Ask Claude to look back at what you have practised this year and help you write five challenges of your own for the year ahead.',
435 isCloser: true,
436}
437
438function deal(): readonly Challenge[] {
439 const list: Challenge[] = []
440 for (let round = 0; round < PER_THEME; round++) {
441 for (const theme of THEMES) {
442 const entry = BY_THEME[theme.id][round]
443 if (entry === undefined) continue
444 const [title, task] = entry
445 const id = `${theme.id}-${String(round + 1).padStart(2, '0')}`
446 list.push({ id, theme: theme.id, title, task, isCloser: false })
447 }
448 }
449 list.push(CLOSER)
450 return list
451}
452
453/** All challenges in the order they are dealt: one of each theme in turn, then the closer. */
454export const CHALLENGES: readonly Challenge[] = deal()
455
456export const BY_ID: ReadonlyMap<string, Challenge> = new Map(CHALLENGES.map(one => [one.id, one]))
457
458export function themeOf(challenge: Challenge): Theme {
459 return THEMES.find(theme => theme.id === challenge.theme) ?? THEMES[0]!
460}
461hooks/layout.ts 111 lines1// Pure sizing for the pane: every row is measured against the body width here,
2// so the drawing never relies on the surface wrapping or cutting text.
3
4export const COLORS = {
5 gold: '#F2C46D',
6 line: '#262B33',
7 muted: '#7D8590',
8 text: '#E6EDF3',
9 green: '#3DD68C',
10 amber: '#E3B341',
11 ink: '#0D1117',
12} as const
13
14export const TITLE = 'DAILY CHALLENGE'
15
16const DIGITS: Record<string, readonly [string, string, string]> = {
17 '0': ['┏━┓', '┃ ┃', '┗━┛'],
18 '1': ['╺┓ ', ' ┃ ', '╺┻╸'],
19 '2': ['╺━┓', '┏━┛', '┗━╸'],
20 '3': ['╺━┓', ' ━┫', '╺━┛'],
21 '4': ['╻ ╻', '┗━┫', ' ╹'],
22 '5': ['┏━╸', '┗━┓', '╺━┛'],
23 '6': ['┏━╸', '┣━┓', '┗━┛'],
24 '7': ['╺━┓', ' ┃', ' ╹'],
25 '8': ['┏━┓', '┣━┫', '┗━┛'],
26 '9': ['┏━┓', '┗━┫', '╺━┛'],
27}
28
29/** A number drawn three rows tall in heavy box-drawing characters. */
30export function bigNumber(value: number): [string, string, string] {
31 const glyphs = String(Math.max(0, Math.floor(value)))
32 .split('')
33 .map(digit => DIGITS[digit] ?? DIGITS['0']!)
34 return [0, 1, 2].map(row => glyphs.map(glyph => glyph[row]).join(' ')) as [string, string, string]
35}
36
37/** Greedy word wrap to `width` columns; a word longer than the width is cut. */
38export function wrap(text: string, width: number): string[] {
39 const room = Math.max(1, width)
40 const lines: string[] = []
41 let line = ''
42 for (const word of text.split(/\s+/).filter(Boolean)) {
43 const piece = word.length > room ? word.slice(0, room) : word
44 if (line === '') line = piece
45 else if (line.length + 1 + piece.length <= room) line += ` ${piece}`
46 else {
47 lines.push(line)
48 line = piece
49 }
50 }
51 if (line !== '') lines.push(line)
52 return lines.length === 0 ? [''] : lines
53}
54
55export function fit(text: string, width: number): string {
56 return text.length <= width ? text : text.slice(0, Math.max(0, width))
57}
58
59/** The gold rule under the title, as long as the title, then the hairline to the edge. */
60export function titleRule(cols: number): { gold: string; hairline: string } {
61 const goldWidth = Math.min(TITLE.length, cols)
62 return { gold: '━'.repeat(goldWidth), hairline: '─'.repeat(Math.max(0, cols - goldWidth)) }
63}
64
65/** A section heading in capitals followed by a hairline to the edge. */
66export function heading(label: string, cols: number): { label: string; hairline: string } {
67 const text = fit(label.toUpperCase(), cols)
68 const rest = cols - text.length - 1
69 return { label: text, hairline: rest > 0 ? ` ${'─'.repeat(rest)}` : '' }
70}
71
72export type TileLayout = { direction: 'row' | 'column'; width: number }
73
74export const TILE_LABELS = ['DAY STREAK', 'COMPLETED / 365', 'BEST STREAK'] as const
75
76/** Tiles side by side when three fit, else stacked at full width. */
77export function tileLayout(cols: number): TileLayout {
78 const inner = Math.max(...TILE_LABELS.map(label => label.length), bigNumber(365)[0].length)
79 const minimum = inner + 4
80 const sideBySide = Math.floor((cols - 2) / 3)
81 if (sideBySide >= minimum) return { direction: 'row', width: sideBySide }
82 return { direction: 'column', width: cols }
83}
84
85export type Chip = { key: string; label: string }
86
87/** Buttons on one row with a space between when they fit, else one per row. */
88export function chipRows(chips: readonly Chip[], cols: number): Chip[][] {
89 const total = chips.reduce((sum, chip) => sum + chip.label.length + 2, 0) + Math.max(0, chips.length - 1)
90 if (total <= cols) return [chips.slice()]
91 return chips.map(chip => [chip])
92}
93
94/** How many day cells fit and how wide each is: up to 14, two to four columns each. */
95export function dayGrid(cols: number): { count: number; cell: number } {
96 const count = Math.max(1, Math.min(14, Math.floor(cols / 2)))
97 const cell = Math.max(2, Math.min(4, Math.floor(cols / count)))
98 return { count, cell }
99}
100
101export function dayLabel(weekday: string, cell: number): string {
102 return weekday.slice(0, cell >= 3 ? 2 : 1)
103}
104
105/** The year bar's filled and empty widths; any progress shows at least one cell. */
106export function yearBar(done: number, total: number, cols: number): { filled: number; empty: number } {
107 const raw = Math.round((done / total) * cols)
108 const filled = Math.min(cols, done > 0 ? Math.max(1, raw) : 0)
109 return { filled, empty: cols - filled }
110}
111hooks/logic.ts 186 lines1import type { Progress } from '../types'
2
3import { BY_ID, CHALLENGES, TOTAL, type Challenge } from './challenges'
4
5export const EMPTY: Progress = { completed: {}, swap: null }
6
7const DAY = /^\d{4}-\d{2}-\d{2}$/
8
9/** Reads whatever the store held into a clean Progress, dropping anything unknown. */
10export function normalize(raw: unknown): Progress {
11 if (typeof raw !== 'object' || raw === null) return EMPTY
12 const source = raw as { completed?: unknown; swap?: unknown }
13 const completed: Record<string, string> = {}
14 if (typeof source.completed === 'object' && source.completed !== null) {
15 for (const [id, day] of Object.entries(source.completed)) {
16 if (BY_ID.has(id) && typeof day === 'string' && DAY.test(day)) completed[id] = day
17 }
18 }
19 const swap = source.swap as { day?: unknown; id?: unknown } | null | undefined
20 const isSwap =
21 typeof swap === 'object' &&
22 swap !== null &&
23 typeof swap.day === 'string' &&
24 typeof swap.id === 'string' &&
25 BY_ID.has(swap.id)
26
27 return { completed, swap: isSwap ? { day: swap.day as string, id: swap.id as string } : null }
28}
29
30function pad(value: number): string {
31 return String(value).padStart(2, '0')
32}
33
34function keyOf(date: Date): string {
35 return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
36}
37
38function dateOf(day: string): Date {
39 const [year = 1970, month = 1, date = 1] = day.split('-').map(Number)
40 return new Date(year, month - 1, date, 12)
41}
42
43/** The local calendar day of an instant, as YYYY-MM-DD. */
44export function dayKey(ms: number): string {
45 return keyOf(new Date(ms))
46}
47
48export function shiftDay(day: string, days: number): string {
49 const date = dateOf(day)
50 date.setDate(date.getDate() + days)
51 return keyOf(date)
52}
53
54const WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
55const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
56
57export function weekdayName(day: string): string {
58 return WEEKDAYS[dateOf(day).getDay()] ?? ''
59}
60
61/** `Mon 12 Oct 2026`, or `12 Oct` when short. */
62export function formatDay(day: string, isShort = false): string {
63 const date = dateOf(day)
64 const month = MONTHS[date.getMonth()] ?? ''
65 if (isShort) return `${date.getDate()} ${month}`
66 return `${weekdayName(day).slice(0, 3)} ${date.getDate()} ${month} ${date.getFullYear()}`
67}
68
69export function remaining(progress: Progress): Challenge[] {
70 return CHALLENGES.filter(one => progress.completed[one.id] === undefined)
71}
72
73export function doneCount(progress: Progress): number {
74 return Object.keys(progress.completed).length
75}
76
77export function completedOn(progress: Progress, day: string): Challenge | undefined {
78 const id = Object.keys(progress.completed).find(key => progress.completed[key] === day)
79 return id === undefined ? undefined : BY_ID.get(id)
80}
81
82export type Today = {
83 /** Today's challenge; undefined once all are done and none was completed today. */
84 challenge: Challenge | undefined
85 isDone: boolean
86 isFinished: boolean
87}
88
89/**
90 * Today's challenge: the one completed today if any, else today's swap if it
91 * is still open, else the first in dealt order that was never completed.
92 */
93export function todayOf(progress: Progress, today: string): Today {
94 const done = completedOn(progress, today)
95 const isFinished = doneCount(progress) >= TOTAL
96 if (done !== undefined) return { challenge: done, isDone: true, isFinished }
97 const swap = progress.swap
98 if (swap !== null && swap.day === today && progress.completed[swap.id] === undefined) {
99 return { challenge: BY_ID.get(swap.id), isDone: false, isFinished }
100 }
101 return { challenge: remaining(progress)[0], isDone: false, isFinished }
102}
103
104export function canSwap(progress: Progress, today: string): boolean {
105 const now = todayOf(progress, today)
106 return !now.isDone && now.challenge !== undefined && remaining(progress).length > 1
107}
108
109/** Swaps today's challenge for a random one never completed; null when there is nothing to swap to. */
110export function swapToday(progress: Progress, today: string, random: () => number): Progress | null {
111 if (!canSwap(progress, today)) return null
112 const current = todayOf(progress, today).challenge
113 const pool = remaining(progress).filter(one => one.id !== current?.id)
114 const pick = pool[Math.min(pool.length - 1, Math.floor(random() * pool.length))]
115 if (pick === undefined) return null
116 return { ...progress, swap: { day: today, id: pick.id } }
117}
118
119/** Marks today's challenge complete; null when today is already done or nothing is left. */
120export function completeToday(progress: Progress, today: string): Progress | null {
121 const now = todayOf(progress, today)
122 if (now.isDone || now.challenge === undefined) return null
123 return { ...progress, completed: { ...progress.completed, [now.challenge.id]: today } }
124}
125
126/** Takes back today's completion; null when nothing was completed today. */
127export function undoToday(progress: Progress, today: string): Progress | null {
128 const ids = Object.keys(progress.completed).filter(id => progress.completed[id] === today)
129 if (ids.length === 0) return null
130 const completed = { ...progress.completed }
131 for (const id of ids) delete completed[id]
132 return { ...progress, completed }
133}
134
135function activeDays(progress: Progress): Set<string> {
136 return new Set(Object.values(progress.completed))
137}
138
139/** Days in a row with a completion, ending today, or ending yesterday while today is still open. */
140export function streak(progress: Progress, today: string): number {
141 const days = activeDays(progress)
142 let day = days.has(today) ? today : shiftDay(today, -1)
143 let count = 0
144 while (days.has(day)) {
145 count += 1
146 day = shiftDay(day, -1)
147 }
148 return count
149}
150
151export function bestStreak(progress: Progress): number {
152 const days = [...activeDays(progress)].sort()
153 let best = 0
154 let run = 0
155 let previous: string | undefined
156 for (const day of days) {
157 run = previous !== undefined && shiftDay(previous, 1) === day ? run + 1 : 1
158 best = Math.max(best, run)
159 previous = day
160 }
161 return best
162}
163
164export type DayCell = { day: string; weekday: string; isDone: boolean; isToday: boolean }
165
166/** The last `count` days, oldest first, ending today. */
167export function lastDays(progress: Progress, today: string, count: number): DayCell[] {
168 const days = activeDays(progress)
169 const cells: DayCell[] = []
170 for (let back = count - 1; back >= 0; back--) {
171 const day = shiftDay(today, -back)
172 cells.push({ day, weekday: weekdayName(day), isDone: days.has(day), isToday: back === 0 })
173 }
174 return cells
175}
176
177export function coachPrompt(challenge: Challenge): string {
178 return [
179 `Please coach me through today's Claude Code challenge, one step at a time.`,
180 `After each step, stop and wait for me to tell you I am ready before you give the next one.`,
181 '',
182 `Challenge: ${challenge.title}`,
183 challenge.task,
184 ].join('\n')
185}
186types/index.d.ts 13 lines1export type Swap = { day: string; id: string }
2
3export type Progress = {
4 completed: Record<string, string>
5 swap: Swap | null
6}
7
8declare module 'claude-code' {
9 interface PluginState {
10 'daily-challenge': { progress: Progress | null }
11 }
12}
13