Watches your prompts at zero token cost: suggests the installed mod that fits, sets your focus goal, turns prompts you repeat into one-word slash commands, and…

Claude Code mods (hooks-module plugins) for subagent visibility, task tracking, token savings and daily workflow. Needs Claude Code 2.1.304 or newer (built and tested on 2.1.304; the mod API is early access and can change between releases).
At the prompt of a Claude Code terminal session:
/plugin install agent-monitor --marketplace samarthsh-git/claude-mods
/plugin install task-inbox --marketplace samarthsh-git/claude-mods
/plugin install token-saver --marketplace samarthsh-git/claude-mods
/plugin install daily-driver --marketplace samarthsh-git/claude-mods
/plugin install clarify-first --marketplace samarthsh-git/claude-mods
/plugin install mod-scout --marketplace samarthsh-git/claude-mods
Answer y to add the marketplace, then pick the user scope. From a shell: claude plugin install <mod> --marketplace samarthsh-git/claude-mods.
In the desktop app, where /plugin does not run: click + next to the prompt box, then Plugins, Add marketplace with samarthsh-git/claude-mods, then Add plugin.
If this repository is private, the machine needs a git credential for github.com (gh auth login, then gh auth setup-git); the install never prompts for one.
/subagents) that opens by itself when the first subagent starts.$ npm test, reading auth.ts, searching "TODO")./subagents clear wipes the history.⚙ N subagents running, and a toast appears when one fails.The Tasks pane (/todo or /inbox) has three parts:
/todo <text> adds, /todo done 2 ticks, /todo rm 2 removes, /todo clear drops finished ones./inbox <text>, then press Send (or /inbox send). Claude spawns one background subagent per task, in parallel, on the cheapest model that fits. You keep working while they run, and agent-monitor shows their progress.| Feature | What it does | Setting |
|---|---|---|
| Subagent model routing | balanced: search and lookup subagents (Explore, guides) run on Haiku. aggressive: general-purpose and Plan subagents spawned from an Opus-class session also run on Sonnet. A model Claude or you set explicitly is never changed, and neither are forks or plugin agents. | routing |
| Lean-output rules | Adds about 150 tokens to the system prompt: no preamble or recaps, locate before reading, batch tool calls, delegate broad searches, prefer Edit, never trade correctness for brevity. It sits in the cached part of the prompt. | leanRules |
| Duplicate-read guard | Refuses once a Read of a file Claude already read with the same range in the same context while the file is unchanged on disk. Repeating the exact call goes through, so the worst case is one extra round trip. It resets on compaction. | dedupeReads |
| Usage HUD | Status line: ctx 42% · $1.23 · 5h 37%. Toasts at 70% context (suggests /compact or /clear) and at 80% of a rate limit. | always on |
| Model advisor | A toast when a prompt looks trivial for an Opus-class model, or hard for Haiku. Pure regex, costs no tokens, at most once per 15 min. | advisor |
/tokens | Report: fresh, cached and output tokens, cache hit rate, subagent share, what token-saver saved, and concrete tips. |
Change settings with /config (or pluginConfigs in settings.json).
What saves the most, from experience: /clear between unrelated tasks, keeping the prompt cache warm (no model switches mid-thread), delegating read-heavy searches to subagents, and using Sonnet as the default with Opus only for hard problems. The mod nudges toward these; it cannot measure your exact savings.
/aim <goal> sets a focus goal (/aim auto <goal> sets one only when none is set), shown above the prompt with time spent and today's file and commit counts. Press Done or run /aim done to log it.notifyAfterSeconds), you get a desktop or phone notification with Claude's answer. Hand Claude long work and switch tasks./eod (or /eod yesterday) prints a zero-token work log: Claude time, prompts, files changed, commits, test runs, subagents and goals done. Paste it into a standup./handoff has Claude write .claude/handoff.md (goal, done, state, next steps, gotchas). /pickup in the next session continues from it, and a toast reminds you when one exists.On a complex task or a deep, open-ended question, Claude first checks the repo and the chat for what it can learn itself. If something that would change the result is still missing, it asks you up to 4 multiple-choice questions in one round, with a recommended option on each. It then states the agreed spec in 3 lines or fewer, does the work, and checks the result against that spec. Everyday prompts ("fix the typo", "what is X") pass straight through.
Cost:
detector to heuristic for zero detection tokens.Commands: /clarify on (default) · off · always · now <task> (force questions for one task) · /clarify alone shows status. Settings: detector (hybrid / heuristic / model), maxQuestions (1 to 4).
It reads each prompt locally, with no model call and zero tokens, and never changes what Claude sees:
/tokens, a list of 3 or more tasks suggests /inbox, and "wrapping up for today" suggests /handoff. It knows every mod here plus Podium and Grounded Engineering, and stays quiet about mods you have not installed./aim) from your first real task of the session, unless you already set one./run-test, say). This costs no tokens to make or to use beyond the prompt itself. /scout rm <name> removes one./scout build <idea> has Claude write, validate and test a new mod, with zero run-time token cost wherever the feature allows. This is the one part that spends tokens, and only when you ask./scout shows recent suggestions and your macros. /scout off pauses it. Settings: autoMacros, autoFocus.
The mods above run only in Claude Code: the chat app has no plugin system. For chat, chat/ holds the closest equivalents, and both work on every device once set up:
chat/preferences.txt: paste it into Settings → Profile → personal preferences. It holds the lean-output rules, clarifying questions on complex tasks only, and a model-fit hint. It is about 200 words, shorter than a typical preferences block, and preferences ride on every message, so shorter is cheaper.chat/deep-work-skill.zip: upload it in Settings → Capabilities → Skills. It is a gather → ask → spec → deliver → self-check procedure that Claude loads only for complex work, so everyday chats pay just its one-line description.Chat habits that save the most: start a new chat per topic (each message resends the whole chat), put reference files in a Project instead of pasting them again, and use a lighter model for simple asks.
.claude/settings.json registers this marketplace and enables the six mods, so a Claude Code session opened on this repository (from claude.ai/code or the phone app) offers to install them with no commands typed.
These are other people's projects. Install them from their own repositories:
| Mod | What it is | Install |
|---|---|---|
| Podium | Hands jobs to other agent CLIs (Codex, Gemini, other Claude models) and tracks them in one pane, with sprints and PRs. Needs Node 22+, git 2.28+ and at least one other agent CLI. | /plugin marketplace add madjagstudios/plugins, then /plugin install podium@madjagstudios |
| Grounded Engineering | /grounded pane: engineering practices that fit your repo, with sources, plus a reviewed shelf of skill repositories. | /plugin marketplace add madjagstudios/grounded-engineering, then /plugin install grounded-engineering@grounded-engineering |
| claude-ui-mod | Not a Claude Code plugin. It patches the files of the Claude Desktop app on Windows: subagent HUD, task rail, usage meters, provider routing. | Windows only. Quit Claude Desktop, then git clone https://gitlab.com/blockshooter/claude-ui-mod && cd claude-ui-mod && npm ci && node scripts/verify-frontend.cjs && node patch.js apply. Read its SECURITY.md first: it rewrites your Claude Desktop install, and app updates can break it. node patch.js revert undoes it. |
The "optimaizr" and "inbox monitor" mods mentioned in the thread had no links. task-inbox and token-saver cover the same ground.
claude plugin validate mods/<mod>
claude plugin test mods/<mod>
To run a working copy, add "env": { "CLAUDE_CODE_PLUGIN_DIRS": "/abs/path/mods/agent-monitor:/abs/path/mods/task-inbox" } to ~/.claude/settings.json, or start with claude --plugin-dir mods/<mod>.
MIT
hooks/register.ts 197 lines1import type { EngineInterface, Register } from 'claude-code'
2
3type Macro = { name: string; text: string; createdAt: number }
4type Count = { n: number; text: string; at: number }
5type Rule = { id: string; command: string; test: RegExp; tip: string }
6
7// Origins that are a person typing: the terminal, Remote Control (phone), an app host.
8const PERSON = new Set(['composer', 'bridge', 'sdk'])
9
10// Each rule fires only when its command is installed, at most once a session.
11const RULES: readonly Rule[] = [
12 { id: 'tokens', command: 'tokens', test: /\b(tokens?|cost|spend|usage|limit|expensive|cheaper|quota)\b/i, tip: '/tokens shows where tokens go and what to change' },
13 { id: 'subagents', command: 'subagents', test: /\b(sub-?agents?|agents? (are|is) (doing|running)|background (jobs?|agents?))\b/i, tip: '/subagents shows every subagent and what it is doing now' },
14 { id: 'todo', command: 'todo', test: /\b(remind me|don'?t forget|to-?do|later i (need|have) to|note to self)\b/i, tip: '/todo <text> keeps it on your list across sessions' },
15 { id: 'eod', command: 'eod', test: /\b(stand-?up|end of (the )?day|what did i (do|get done)|daily (summary|report)|status update)\b/i, tip: '/eod prints today\'s work log for free' },
16 { id: 'pickup', command: 'pickup', test: /\b(where (was i|did we leave)|pick up|continue (from )?(yesterday|last time)|resume (the|my) work)\b/i, tip: '/pickup continues from the last /handoff note' },
17 { id: 'handoff', command: 'handoff', test: /\b(wrap(ping)? up|calling it a day|done for (today|the day)|stop(ping)? for now|tomorrow i)\b/i, tip: '/handoff saves where you are so the next session resumes exactly here' },
18 { id: 'podium', command: 'podium', test: /\b(codex|gemini|muse|other agents?|second opinion|cross-?review)\b/i, tip: '/podium hands the job to another agent CLI and tracks it' },
19 { id: 'grounded', command: 'grounded', test: /\b(best practices?|ci gate|repo (structure|hygiene)|engineering (standards|practices)|agents\.md|claude\.md)\b/i, tip: '/grounded lists practices that fit this repo, with sources' },
20 { id: 'clarify', command: 'clarify', test: /\b(ask me (questions|first)|clarify|before you start)\b/i, tip: '/clarify now <task> makes Claude ask its questions first' },
21]
22
23const STOP = new Set('a an the to of in on for and or but is are be it this that with my me i you we please can could would should do make just then also from at by as so what how'.split(' '))
24
25function normalize(text: string): string {
26 return text.toLowerCase().replace(/\d+/g, '#').replace(/[^\w#\s]/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 200)
27}
28
29function slugOf(text: string, taken: ReadonlySet<string>): string {
30 const words = normalize(text).replace(/#/g, '').split(' ').filter(w => w.length > 1 && !STOP.has(w))
31 const base = (words.slice(0, 2).join('-') || 'macro').slice(0, 24)
32 let name = base
33 for (let i = 2; taken.has(name); i++) name = `${base}-${i}`
34 return name
35}
36
37// Independent items in one prompt: 3+ numbered or bulleted lines.
38function listCount(text: string): number {
39 return text.match(/^\s*([-*•]|\d+[.)])\s+\S/gm)?.length ?? 0
40}
41
42const TASK = /\b(build|implement|add|create|fix|refactor|migrate|write|design|set ?up|debug|optimi[sz]e|integrate|ship|deploy)\b/i
43
44async function commandNames($: EngineInterface): Promise<Set<string>> {
45 const list = await $.command.list().catch(() => [])
46 return new Set(list.map(c => c.name))
47}
48
49function clip(text: string, n: number): string {
50 const one = text.replace(/\s+/g, ' ').trim()
51 return one.length > n ? one.slice(0, n - 1) + '…' : one
52}
53
54function buildPrompt(idea: string): string {
55 return [
56 `Build a Claude Code mod for this: ${idea}`,
57 'Load the plugin-authoring skill and follow it: write the mod in this session\'s mods folder, then validate, type-check and test it.',
58 'Constraints: zero model tokens at run time wherever possible (no model calls in hooks unless the feature needs one, and then the small fast model);',
59 'never rewrite or trim prompts, system prompt or tool results in a way that could lower answer quality; prefer suggestions over silent actions.',
60 'Check the installed commands first so it does not duplicate an existing mod. Finish with 3 lines: what it does, its commands, its cost.',
61 ].join(' ')
62}
63
64async function registerMacro($: EngineInterface, m: Macro) {
65 await $.command.register({ name: m.name, description: `Macro: ${clip(m.text, 90)}` })
66}
67
68export const register: Register = (on, options) => {
69 const isAutoMacros = options.autoMacros !== false
70 const isAutoFocus = options.autoFocus !== false
71 let isOn = true
72 let macros: Macro[] = []
73 let installed = new Set<string>()
74 const shown = new Set<string>()
75 const recent: string[] = []
76 let lastToast = -Infinity
77 let isFocusDone = false
78
79 function remember(line: string) {
80 recent.push(line)
81 if (recent.length > 10) recent.shift()
82 }
83
84 on('session.start', async ($, e, next) => {
85 await $.command.register({
86 name: 'scout',
87 description: 'mod-scout: suggestions so far, your macros, and /scout build <idea> to have Claude make a new mod',
88 argumentHint: '[build <idea> | macros | rm <macro> | on | off]',
89 })
90 isOn = (await $.store.get('isOn')) !== false
91 const saved = await $.store.get('macros')
92 macros = Array.isArray(saved) ? (saved as Macro[]) : []
93 installed = await commandNames($)
94 for (const m of macros) if (!installed.has(m.name)) await registerMacro($, m).catch(() => undefined)
95
96 return next(e)
97 })
98
99 on('command.run', async ($, e, next) => {
100 const macro = macros.find(m => m.name === e.command)
101 if (macro === undefined) return next(e)
102 const extra = e.args.trim()
103 const text = extra ? `${macro.text}\n\n${extra}` : macro.text
104 $.clock.after(50, () => void $.prompt.submit({ text, asUser: true }))
105
106 return { text: `/${macro.name} sent its prompt.` }
107 }).catch(($, e, next) => next(e))
108
109 on('command.run', { command: 'scout' }, async ($, e) => {
110 const args = e.args.trim()
111 const [word = '', ...rest] = args.split(/\s+/)
112 if (word === 'build' && rest.length > 0) {
113 const idea = rest.join(' ')
114 $.clock.after(50, () => void $.prompt.submit({ text: buildPrompt(idea), asUser: true }))
115 return { text: `Asking Claude to build a mod for: ${idea}` }
116 }
117 if (word === 'on' || word === 'off') {
118 isOn = word === 'on'
119 await $.store.set('isOn', isOn)
120 return { text: `mod-scout ${word}.` }
121 }
122 if (word === 'rm' && rest[0]) {
123 const name = rest[0].replace(/^\//, '')
124 if (!macros.some(m => m.name === name)) return { text: `No macro /${name}.` }
125 macros = macros.filter(m => m.name !== name)
126 await $.store.set('macros', macros)
127 return { text: `Removed /${name}. It disappears from the command list next session.` }
128 }
129 const lines = [
130 `mod-scout is ${isOn ? 'on' : 'off'}.`,
131 `Macros (${macros.length}): ${macros.map(m => `/${m.name}`).join(', ') || 'none yet. Send the same prompt 3 times and one is made.'}`,
132 `Recent suggestions: ${recent.length === 0 ? 'none' : ''}`,
133 ...recent.map(r => `• ${r}`),
134 'Make a new mod: /scout build <what it should do>',
135 ]
136
137 return { text: lines.join('\n') }
138 })
139
140 on('prompt.submit', async ($, e, next) => {
141 if (!isOn || e.command || e.isMeta || !PERSON.has(e.origin.kind)) return next(e)
142 const text = e.text.trim()
143 const now = await $.clock.now()
144 // Other mods register their commands as they start: read the list fresh.
145 installed = new Set([...(await commandNames($)), ...macros.map(m => m.name)])
146
147 // 1. The one rule that fits, if its mod is installed and it was not shown yet.
148 const rule = RULES.find(r => !shown.has(r.id) && installed.has(r.command) && r.test.test(text))
149 let tip: string | undefined = rule?.tip
150 if (rule) shown.add(rule.id)
151 if (!tip && !shown.has('inbox') && installed.has('inbox') && listCount(text) >= 3) {
152 shown.add('inbox')
153 tip = 'Several independent tasks: /inbox each one, then Send, and they run in parallel subagents'
154 }
155 if (tip) {
156 remember(tip)
157 if (now - lastToast > 60_000) {
158 lastToast = now
159 $.ui.toast(`💡 ${tip}`)
160 }
161 }
162
163 // 2. The session's first real task becomes the focus goal, when none is set.
164 if (isAutoFocus && !isFocusDone && installed.has('aim') && TASK.test(text) && text.length >= 30 && text.length <= 600) {
165 isFocusDone = true
166 const goal = clip(text.split(/(?<=[.?!])\s|\n/)[0] ?? text, 70)
167 $.clock.after(50, () => void $.command.run({ command: 'aim', args: `auto ${goal}` }).catch(() => undefined))
168 }
169
170 // 3. A prompt sent for the third time becomes a slash command.
171 if (isAutoMacros && text.length >= 15 && text.length <= 1500) {
172 const key = normalize(text)
173 const counts = ((await $.store.get('counts')) ?? {}) as Record<string, Count>
174 const seen = counts[key]
175 counts[key] = { n: (seen?.n ?? 0) + 1, text, at: now }
176 const keys = Object.keys(counts)
177 if (keys.length > 300) {
178 keys.sort((a, b) => (counts[a]?.at ?? 0) - (counts[b]?.at ?? 0))
179 for (const old of keys.slice(0, keys.length - 300)) delete counts[old]
180 }
181 await $.store.set('counts', counts)
182 if (counts[key]?.n === 3 && !macros.some(m => normalize(m.text) === key)) {
183 const taken = new Set([...installed, ...macros.map(m => m.name)])
184 const macro: Macro = { name: slugOf(text, taken), text, createdAt: now }
185 macros = [...macros, macro]
186 await $.store.set('macros', macros)
187 installed.add(macro.name)
188 $.clock.after(50, () => void registerMacro($, macro).catch(() => undefined))
189 remember(`Made /${macro.name} for a prompt you sent 3 times`)
190 $.ui.toast(`⚡ You sent this 3 times: /${macro.name} now sends it. /scout rm ${macro.name} undoes it.`)
191 }
192 }
193
194 return next(e)
195 }).catch(($, e, next) => next(e))
196}
197