Forward each you-should-know heads-up to Claude, so you no longer paste it in yourself

<strong>Claude hears the heads-up too.</strong><br> A Claude Code mod that forwards each heads-up from the built-in you-should-know plugin to Claude, so you no longer copy and paste it.
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/Version-1.0.0-green.svg" alt="Version 1.0.0"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License"></a> <a href="https://code.claude.com/docs"><img src="https://img.shields.io/badge/Claude%20Code-2.1.295%2B-D97757.svg" alt="Claude Code 2.1.295+"></a> <a href="hooks/register.ts"><img src="https://img.shields.io/badge/Type-Function%20hooks%20mod-6E7681.svg" alt="Function hooks mod"></a> <a href="hooks/register.test.ts"><img src="https://img.shields.io/badge/Tests-7%20passing-3FB950.svg" alt="7 tests passing"></a>
<a href="#how-it-works">How it works</a> · <a href="#install">Install</a> · <a href="#usage">Usage</a> · <a href="#limitations">Limitations</a> · <a href="#development">Development</a> · <a href="CHANGELOG.md">Changelog</a>
Illustration of a session before and after.
Claude Code's built-in you-should-know plugin watches a session and, above the prompt, points out something Claude has not told you yet: a failing check, a file left staged, a decision with a cost. That heads-up is shown to you only. To make Claude act on it, you had to copy it into the prompt yourself. you-should-know-relay hands the same heads-up to Claude the moment it appears, while the heads-up stays on your screen as before.
[!NOTE] This mod is built on Claude Code's early-access function-hooks API and reads how the built-in you-should-know plugin stores its heads-up, which is not a public interface. A Claude Code update may change either one. Built and tested against Claude Code 2.1.295.
| Step | What happens |
|---|---|
| 1. you-should-know shows a heads-up | Unchanged: it still appears above the prompt, with its own buttons. |
| 2. The mod picks it up | Only that plugin's heads-up for the current session, and each one once. |
| 3. Claude receives it | As a conversation note Claude reads, not as text you typed. |
| 4. You get a confirmation | A short toast: you-should-know:已把這則提醒轉給 Claude. |
When Claude sees it: during a running turn, at Claude's next step. After the turn has ended, together with your next message. The mod never starts a turn by itself.
The note tells Claude where the heads-up came from, quotes it, and asks Claude to address it in its next reply, or briefly say why it does not apply. The note, the toast and the command replies are written in Traditional Chinese.
In a Claude Code terminal session, run:
/plugin install you-should-know-relay --marketplace AnnihilationWizard/you-should-know-relay
y to add the github:AnnihilationWizard/you-should-know-relay marketplace.Installed you-should-know-relay. Plugin is now active. No restart is needed.To update later, run claude plugin update, then /reload-plugins.
git clone https://github.com/AnnihilationWizard/you-should-know-relay.git
claude --plugin-dir ./you-should-know-relay
To load a clone in every session, add it to the env block of ~/.claude/settings.json (separate several folders with :):
{
"env": { "CLAUDE_CODE_PLUGIN_DIRS": "/path/to/you-should-know-relay" }
}
The setting is read when a session starts, so sessions already open need a restart.
There is nothing to configure. Keep working, and each new heads-up reaches Claude on its own.
| Command | Effect |
|---|---|
/you-should-know-relay | Pause forwarding. Heads-ups still show; Claude no longer receives them. |
/you-should-know-relay again | Resume forwarding. |
| The pause | Behavior |
|---|---|
| Scope | One setting for every session, kept after Claude Code restarts. |
| Takes effect | Immediately, including in sessions that are already open. |
| Heads-ups shown while paused | Never sent later, even after you resume. |
/plugin install is available in terminal sessions only.claude plugin validate . # manifest, marketplace file and hooks module
claude plugin test . # hooks/*.test.ts against the engine
After Claude Code first loads the mod, it writes the API types to .claude-plugin/types/ (git-ignored). Type-check against them with TypeScript 5.4 or newer:
npx -p typescript@5 tsc -p .
Run claude --plugin-dir . to try changes. In an interactive session the folder is watched, and saving a file reloads the mod.
.claude-plugin/ plugin.json manifest and marketplace.json
hooks/ hooks.json, the hooks module and its tests
types/ state contract for heads-ups already forwarded
docs/images/ README illustration
Bug reports and ideas are welcome. Read CONTRIBUTING.md before opening an issue or pull request, and follow SECURITY.md for security reports.
MIT License. Copyright © 2026 AnnihilationWizard.
hooks/register.ts 108 lines1import { atom, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Forwarded } from '../types'
5
6// The built-in plugin that draws the heads-up above the prompt. It sits in the
7// builtin tier, beneath this mod, so its `$.store.set` calls pass through here.
8const SOURCE = 'cc-plugin-you-should-know'
9const KEEP = 50
10
11const forwarded = atom(
12 { plugin: 'you-should-know-relay', key: 'forwarded' } as const,
13 [] as Forwarded,
14)
15// Kept in `$.store`, so a pause holds in every session until resumed.
16const PAUSED = 'isPaused'
17const readPaused = async ($: EngineInterface) => (await $.store.get(PAUSED)) === true
18
19type Offer = { line: string; explanation: string | undefined; shownAt: number | undefined }
20
21const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null
22
23// The built-in keeps one entry per session under its "sessions" key; an entry
24// whose `view.kind` is "offer" is the heads-up it shows now.
25export const offerFor = (sessions: unknown, sessionId: string): Offer | undefined => {
26 if (!Array.isArray(sessions)) return undefined
27 const entry: unknown = sessions.find(s => isRecord(s) && s.id === sessionId)
28 const view = isRecord(entry) ? entry.view : undefined
29 if (!isRecord(view) || view.kind !== 'offer' || typeof view.line !== 'string') return undefined
30 // Its lines end in an extra "." after a full-width stop ("…不到。.").
31 const line = view.line.trim().replace(/([。!?])\.$/, '$1')
32 if (line === '') return undefined
33 return {
34 line,
35 explanation:
36 typeof view.explanation === 'string' && view.explanation.trim() !== ''
37 ? view.explanation.trim()
38 : undefined,
39 shownAt: typeof view.shownAt === 'number' ? view.shownAt : undefined,
40 }
41}
42
43export const reminder = (offer: Offer) =>
44 [
45 '[you-should-know 轉送] Claude Code 內建的 you-should-know 外掛剛在提示框上方提醒使用者一件事,它判斷這是你還沒有明確告訴使用者的重點:',
46 '',
47 `「${offer.line}」`,
48 ...(offer.explanation === undefined ? [] : ['', `外掛的補充說明:${offer.explanation}`]),
49 '',
50 '這段文字由 you-should-know-relay mod 自動轉送,不是使用者打的字。請在接下來的回覆中處理這一點:若屬實,向使用者明確說明,並考慮它對目前工作的影響;若你判斷不成立或已經說明過,簡短說明理由即可。',
51 ].join('\n')
52
53export const register: Register = on => {
54 on('session.start', async ($, e, next) => {
55 await $.command.register({
56 name: 'you-should-know-relay',
57 description: '暫停或恢復把 you-should-know 的提醒轉給 Claude',
58 })
59
60 return next(e)
61 })
62
63 on('command.run', { command: 'you-should-know-relay' }, async $ => {
64 const paused = !(await readPaused($))
65 await $.store.set(PAUSED, paused)
66
67 return {
68 text: paused
69 ? '已暫停:you-should-know 的提醒不再轉給 Claude(再執行一次 /you-should-know-relay 恢復)'
70 : '已恢復:you-should-know 的提醒會自動轉給 Claude',
71 }
72 })
73
74 on('store.set', { key: 'sessions' }, async ($, e, next) => {
75 const result = await next(e)
76 if (next.origin.plugin !== SOURCE) return result
77
78 try {
79 const offer = offerFor(e.value, await $.session.id())
80 if (offer === undefined) return result
81
82 // The built-in rewrites "sessions" many times while one heads-up shows.
83 const id = `${offer.shownAt ?? ''}:${offer.line}`
84 let isNew = false
85 await update($, forwarded, list => {
86 isNew = !list.includes(id)
87 return isNew ? [...list, id].slice(-KEEP) : list
88 })
89 // A heads-up shown while paused is marked too, so resuming never sends it.
90 if (!isNew || (await readPaused($))) return result
91
92 try {
93 await $.session.append({
94 message: { type: 'user', content: [{ type: 'text', text: reminder(offer) }] },
95 })
96 } catch (error) {
97 // Not appended: let a later write of the same heads-up try again.
98 await update($, forwarded, list => list.filter(x => x !== id))
99 throw error
100 }
101 $.ui.toast('you-should-know:已把這則提醒轉給 Claude')
102 } catch (error) {
103 $.ui.log(`you-should-know-relay: not forwarded: ${String(error)}`, { to: 'debug' })
104 }
105 return result
106 })
107}
108types/index.d.ts 9 lines1// `${shownAt}:${line}` of each heads-up already forwarded this session.
2export type Forwarded = string[]
3
4declare module 'claude-code' {
5 interface PluginState {
6 'you-should-know-relay': { forwarded: Forwarded }
7 }
8}
9