Plan limits, cost and token totals above the prompt.

English · Українська · Русский
A portable snapshot of the author's local Claude Code setup, synchronized on 2026-10-15. It contains 18 engineering skills, 14 specialist agents, the Brief output style, and 2 mods, plus portable settings.
Skills cover clean and secure code, testing, documentation, architecture, design patterns, implementation plans, HTTP APIs, databases, interfaces, 3D, observability, resilient services, dependencies and CI, releases, commit messages, and LLM security. Agents provide codebase exploration, planning, web research, diagnosis, implementation, review, security audits, database analysis, documentation, test runs, and verification. The Brief output style keeps every final answer to one short, plain paragraph.
The two mods are Claude Code plugins with a hooks module:
instructions folder, adds the author's standing rules, extends tool descriptions, carries the rules through compaction, and adds reminders after tool errors, agent results, shell commands that read or write local files, and changes made before a skill's reference files or any web source were read. In bypassPermissions mode it also allows every permission request that still reaches you, except AskUserQuestion, EnterPlanMode, and ExitPlanMode.This is a personal configuration, not an official Anthropic distribution. Credentials, account state, chat history, projects, sessions, plugins, synced skills, caches, and machine paths are not included.
[!WARNING] When your settings don't set them, the installer adds
"permissions": { "defaultMode": "bypassPermissions" }and"skipDangerousModePermissionPrompt": true. Claude Code then runs every tool call without asking and skips the confirmation dialog for that mode, and brief-guard allows the remaining permission requests. The permission modes documentation recommends this mode only for isolated environments, such as containers and virtual machines without internet access. Review the settings before installing, or set your ownpermissions.defaultModefirst, because the installer keeps existing values.
Download and extract the ZIP archive, or clone the repository:
git clone https://github.com/dayfinggg/anthropic-claude-code-agent-skills.git
cd anthropic-claude-code-agent-skills
Run one installer from the extracted or cloned folder. It uses the included files without downloading another copy.
Requires Windows PowerShell 5.1 or PowerShell 7.
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\install.ps1
Requires a POSIX shell, python3 for the settings merge, and a SHA-256 utility, such as sha256sum, shasum, or openssl.
sh scripts/install.sh
Restart Claude Code after installation. The mods load through CLAUDE_CODE_PLUGIN_DIRS, which requires Claude Code v2.1.280 or later.
Replace is the default. The installer backs up and replaces all skills except skills/synced, all agents, the output-styles folder, and the mods folder, removing the old set. It keeps synced skills, credentials, sessions, projects, plugins, and unrelated files. Existing settings values are preserved and missing keys are added, except outputStyle, which becomes Brief, and env.CLAUDE_CODE_PLUGIN_DIRS, which lists the installed mod folders.
To download and install the latest GitHub revision without updating your checkout:
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\update.ps1
sh scripts/update.sh
Updates require internet access, with curl and tar additionally required for the shell updater. The last install mode is reused. If you maintain a Git clone instead, run git pull --ff-only and rerun the installer.
The payload files are copied byte for byte from the local setup. The settings copy leaves out env.CLAUDE_CODE_PLUGIN_DIRS, which the installer writes for each platform, and each mod's .claude-plugin/types folder is left out because Claude Code generates those type definitions for the version it runs.
Claude Code keeps its configuration in ~/.claude, or in the folder named by CLAUDE_CONFIG_DIR, as described in Environment variables and Explore the .claude directory. Personal subagents load from its agents folder (Create custom subagents), skills from skills/<skill-name>/SKILL.md (Extend Claude with skills), and output styles from output-styles (Output styles). CLAUDE_CODE_PLUGIN_DIRS loads each listed folder the way --plugin-dir does, with absolute paths separated by ; on Windows and : elsewhere (Mods reference).
Requires Python 3.11 or newer:
python -m unittest discover -s tests -v
Tests use temporary directories, not your real Claude Code setup. Available Windows PowerShell, PowerShell 7, and POSIX shell implementations are tested; unavailable runtimes are reported as skipped.
Report problems through GitHub issues. Repository instructions and scripts use the MIT License.
hooks/register.tsx 200 lines1import { atom, read, update } from 'claude-code'
2import type {
3 ElementConstructor,
4 EngineInterface,
5 Register,
6 RenderChildren,
7 SessionCost,
8 SessionRateLimit,
9 TextProps,
10 TurnUsage,
11} from 'claude-code'
12
13import type { LimitGauge, TokenTotals, UsageSummary } from '../types'
14import { CACHE_READ_ICON, CACHE_WRITE_ICON, COST_ICON, iconForLimit, INPUT_ICON, OUTPUT_ICON } from './icons'
15import type { Icon } from './icons'
16
17const SEPARATOR = ' '
18const HIDDEN_LIMITS = new Set(['spend_limit'])
19const ICON_SIZE = 16
20const BAR_CELLS = 10
21const BAR_LINE = '─'
22const FILL_COLORS = [
23 { from: 0.8, color: '#ef4444' },
24 { from: 0.7, color: '#f97316' },
25 { from: 0.4, color: '#eab308' },
26]
27
28const summary = atom({ plugin: 'usage-line', key: 'summary' } as const, null as UsageSummary)
29const NO_TOKENS: TokenTotals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
30const tokenTotals = atom({ plugin: 'usage-line', key: 'tokenTotals' } as const, NO_TOKENS)
31
32type BandItem = { key: string; icon: Icon; label: string; draw: (Text: ElementConstructor<TextProps>) => RenderChildren }
33
34export const register: Register = on => {
35 on('session.start', async ($, e, next) => {
36 $.ui.status(undefined)
37 await refreshSummary($)
38
39 return next(e)
40 })
41
42 on('turn.step', async function* ($, e, next) {
43 const response = yield* next(e)
44 const { usage } = response
45
46 if (usage !== null) {
47 await update($, tokenTotals, totals => addUsage(totals ?? NO_TOKENS, usage))
48 }
49
50 if (e.agentId === undefined) {
51 await refreshSummary($)
52 }
53
54 return response
55 })
56
57 on('session.measure', async ($, e, next) => {
58 await update($, summary, () => summarizeUsage(e.rateLimits, e.cost))
59
60 return next(e)
61 })
62
63 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
64 const usage = await read($, summary)
65
66 if (e.props.hasSurvey || usage === null) {
67 return next(e)
68 }
69
70 const rows = bandRows(usage, await read($, tokenTotals))
71
72 if (e.surface === 'desktop') {
73 const { Box, Svg, Text } = $.ui.resolve(e)
74
75 return (
76 <Box flexDirection="column">
77 {rows.map((row, rowIndex) => (
78 <Box key={`row-${rowIndex}`} flexDirection="row" alignItems="center" flexWrap="wrap">
79 {row.flatMap((item, index) => [
80 index === 0 ? null : <Text key={`separator-${item.key}`}>{SEPARATOR}</Text>,
81 <Box key={item.key} flexDirection="row" alignItems="center" flexShrink={0}>
82 <Svg source={item.icon.svg} alt={item.label} width={ICON_SIZE} height={ICON_SIZE} />
83 <Text>
84 {' '}
85 <Text dimColor>{item.label}</Text> {item.draw(Text)}
86 </Text>
87 </Box>,
88 ])}
89 </Box>
90 ))}
91 </Box>
92 )
93 }
94
95 const { Box, Text } = $.ui.resolve(e)
96
97 return (
98 <Box flexDirection="column">
99 {rows.map((row, rowIndex) => (
100 <Text key={`row-${rowIndex}`}>
101 {row.flatMap((item, index) => [
102 index === 0 ? '' : SEPARATOR,
103 `${item.icon.emoji} `,
104 <Text dimColor>{item.label}</Text>,
105 ' ',
106 item.draw(Text),
107 ])}
108 </Text>
109 ))}
110 </Box>
111 )
112 })
113}
114
115function bandRows(usage: NonNullable<UsageSummary>, tokens: TokenTotals): BandItem[][] {
116 const limits = usage.limits.map(
117 (limit): BandItem => ({
118 key: limit.kind,
119 icon: iconForLimit(limit.kind),
120 label: limit.label,
121 draw: Text => drawGauge(Text, limit),
122 }),
123 )
124 const cost: BandItem[] =
125 usage.costUsd === null ? [] : [valueItem('cost', COST_ICON, 'Cost', formatUsd(usage.costUsd))]
126 const tokenItems = [
127 valueItem('input', INPUT_ICON, 'Input', formatTokenCount(tokens.input)),
128 valueItem('output', OUTPUT_ICON, 'Output', formatTokenCount(tokens.output)),
129 valueItem('cache-read', CACHE_READ_ICON, 'Cache read', formatTokenCount(tokens.cacheRead)),
130 valueItem('cache-write', CACHE_WRITE_ICON, 'Cache write', formatTokenCount(tokens.cacheWrite)),
131 ]
132
133 return [limits, [...cost, ...tokenItems]].filter(row => row.length > 0)
134}
135
136function valueItem(key: string, icon: Icon, label: string, value: string): BandItem {
137 return { key, icon, label, draw: () => value }
138}
139
140function addUsage(totals: TokenTotals, usage: TurnUsage): TokenTotals {
141 return {
142 input: totals.input + usage.input_tokens,
143 output: totals.output + usage.output_tokens,
144 cacheRead: totals.cacheRead + usage.cache_read_input_tokens,
145 cacheWrite: totals.cacheWrite + usage.cache_creation_input_tokens,
146 }
147}
148
149function drawGauge(Text: ElementConstructor<TextProps>, limit: LimitGauge) {
150 const color = colorForFill(limit.fill)
151 const filledCells = Math.min(BAR_CELLS, Math.max(0, Math.round(limit.fill * BAR_CELLS)))
152
153 return [
154 <Text color={color}>{BAR_LINE.repeat(filledCells)}</Text>,
155 <Text dimColor>{BAR_LINE.repeat(BAR_CELLS - filledCells)}</Text>,
156 ' ',
157 <Text color={color}>{limit.value}</Text>,
158 ]
159}
160
161async function refreshSummary($: EngineInterface): Promise<void> {
162 const { rateLimits, cost } = await $.session.usage()
163 await update($, summary, () => summarizeUsage(rateLimits, cost))
164}
165
166function summarizeUsage(rateLimits: SessionRateLimit[], cost: SessionCost | undefined): UsageSummary {
167 return {
168 limits: rateLimits.filter(limit => !HIDDEN_LIMITS.has(limit.kind)).map(describeLimit),
169 costUsd: cost?.usd ?? null,
170 }
171}
172
173function describeLimit(limit: SessionRateLimit): LimitGauge {
174 return {
175 kind: limit.kind,
176 label: iconForLimit(limit.kind).label,
177 value: `${limit.percentUsed}%`,
178 fill: limit.percentUsed / 100,
179 }
180}
181
182function colorForFill(fill: number): string | undefined {
183 return FILL_COLORS.find(level => fill > level.from)?.color
184}
185
186function formatUsd(usd: number): string {
187 return `$${usd.toFixed(2)}`
188}
189
190function formatTokenCount(tokens: number): string {
191 return `${formatTokens(tokens)} tok`
192}
193
194function formatTokens(tokens: number): string {
195 if (tokens >= 1_000_000) return `${Number((tokens / 1_000_000).toFixed(1))}M`
196 if (tokens >= 1_000) return `${Math.round(tokens / 1_000)}K`
197
198 return `${tokens}`
199}
200hooks/icons.ts 50 lines1export type Icon = { svg: string; emoji: string }
2
3export type LimitIcon = Icon & { label: string }
4
5function lucide(body: string): string {
6 return `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-2 1 28 28"><g fill="none" stroke="#e8e8e8" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">${body}</g></svg>`
7}
8
9const HOURGLASS: Icon = {
10 emoji: '⏳',
11 svg: lucide(
12 '<path d="M5 22h14M5 2h14m-2 20v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"/>',
13 ),
14}
15
16const CALENDAR: Icon = {
17 emoji: '📅',
18 svg: lucide(
19 '<path d="M8 2v3m8-3v3"/><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18M8 13h.01M12 13h.01M16 13h.01M8 17h.01M12 17h.01M16 17h.01"/>',
20 ),
21}
22
23export const COST_ICON: Icon = {
24 emoji: '💵',
25 svg: lucide('<path d="M12 2v20m5-17H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>'),
26}
27
28export const INPUT_ICON: Icon = { emoji: '⬇️', svg: lucide('<path d="M12 17V3m-6 8l6 6l6-6m1 10H5"/>') }
29
30export const OUTPUT_ICON: Icon = { emoji: '⬆️', svg: lucide('<path d="m18 9l-6-6l-6 6m6-6v14m-7 4h14"/>') }
31
32export const CACHE_READ_ICON: Icon = {
33 emoji: '📖',
34 svg: lucide('<path d="M12 2v8m4-4l-4 4l-4-4"/><rect width="20" height="8" x="2" y="14" rx="2"/><path d="M6 18h.01M10 18h.01"/>'),
35}
36
37export const CACHE_WRITE_ICON: Icon = {
38 emoji: '💾',
39 svg: lucide('<path d="m16 6l-4-4l-4 4m4-4v8"/><rect width="20" height="8" x="2" y="14" rx="2"/><path d="M6 18h.01M10 18h.01"/>'),
40}
41
42const LIMIT_ICONS: Record<string, LimitIcon> = {
43 five_hour: { ...HOURGLASS, label: '5h limit' },
44 seven_day: { ...CALENDAR, label: '7d limit' },
45}
46
47export function iconForLimit(kind: string): LimitIcon {
48 return LIMIT_ICONS[kind] ?? { ...HOURGLASS, label: kind }
49}
50types/index.d.ts 12 lines1export type LimitGauge = { kind: string; label: string; value: string; fill: number }
2
3export type UsageSummary = { limits: LimitGauge[]; costUsd: number | null } | null
4
5export type TokenTotals = { input: number; output: number; cacheRead: number; cacheWrite: number }
6
7declare module 'claude-code' {
8 interface PluginState {
9 'usage-line': { summary: UsageSummary; tokenTotals: TokenTotals }
10 }
11}
12