Replaces Claude Code prompt blocks that conflict with the Brief output style

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.ts 274 lines1import type { EngineInterface, PromptComposeSection, Register } from 'claude-code'
2import type { TurnMarks } from '../types'
3
4const DROPPED_SECTIONS = ['tone']
5const DROPPED_REMINDERS = ['auto_mode', 'silent_turn_reminder', 'task_reminder', 'todo_reminder']
6const MCP_INSTRUCTIONS = 'mcp_instructions_delta'
7const STATUS_LINE_REQUEST = /they['’]re likely watching it fill[^)]*\);\s*/g
8
9const PERSON_TOOLS = ['AskUserQuestion', 'EnterPlanMode', 'ExitPlanMode']
10const REQUEST_EXCERPT_LENGTH = 160
11const USER_ORIGINS: string[] = ['composer', 'bridge', 'sdk', 'unclassified']
12const NOTIFICATION_ORIGIN = 'task-notification'
13const EMPTY_REPLY_NUDGE = '[Your previous response had no visible output'
14
15const TURN = { plugin: 'brief-guard', key: 'turn' } as const
16const FRESH_TURN: TurnMarks = { request: '', silenceAllowed: false }
17
18const readTurn = async ($: EngineInterface): Promise<TurnMarks> => ({ ...FRESH_TURN, ...(await $.state.get(TURN)).value })
19
20const markTurn = async ($: EngineInterface, marks: Partial<TurnMarks>) => {
21 const current = await readTurn($)
22 const next = { ...current, ...marks }
23 if (Object.entries(marks).some(([key, value]) => current[key as keyof TurnMarks] !== value)) await $.state.set(TURN, next)
24}
25
26const nonEmptyLines = (text: string) => text.split(/\r?\n/).map(l => l.trim()).filter(Boolean)
27
28const typedTextOf = (text: string) =>
29 text
30 .replace(/<!--[\s\S]*?-->/g, ' ')
31 .replace(/<([A-Za-z][\w-]*)\b[^>]*>[\s\S]*?<\/\1\b[^>]*>/g, ' ')
32 .split(/\r?\n/)
33 .filter(line => !line.trimStart().startsWith('>'))
34 .join('\n')
35
36const excerptOf = (text: string) =>
37 typedTextOf(text)
38 .replace(/```[\s\S]*?```/g, ' ')
39 .replace(/\s+/g, ' ')
40 .trim()
41 .slice(0, REQUEST_EXCERPT_LENGTH)
42
43const SHELL_TOOLS = ['Bash', 'PowerShell']
44const FILE_READERS = ['cat', 'head', 'tail', 'less', 'more', 'Get-Content', 'gc', 'type']
45const FILE_WRITERS = ['Set-Content', 'Add-Content', 'Out-File', 'tee']
46const SCRIPT_RUNNERS = /^(python3?|node|perl|ruby|php)$/
47
48const hasOperand = (words: string[]) => words.slice(1).some(w => !w.startsWith('-') && !/^\d+$/.test(w))
49
50const programOf = (stage: string): string | undefined => {
51 const words = stage.trim().split(/\s+/).filter(w => !/^\w+=/.test(w))
52 const [name = ''] = words
53 if (FILE_READERS.includes(name) && hasOperand(words)) return name
54 if (FILE_WRITERS.includes(name)) return name
55 if (name === 'sed' && words.some(w => /^-[a-zA-Z]*[ni]/.test(w))) return 'sed'
56 if (name === 'awk' && words.length > 2) return 'awk'
57 if (SCRIPT_RUNNERS.test(name) && words.slice(1).some(w => w === '-' || /^-[a-zA-Z]*[ceE]$/.test(w) || w.startsWith('<<'))) return `inline ${name}`
58 return undefined
59}
60
61const localFileProgram = (command: string): string | undefined => {
62 if (/\b(ssh|scp|docker|kubectl)\b/.test(command)) return undefined
63 const body = command.replace(/<<-?\s*'?(\w+)'?[\s\S]*?\n\1\b/g, '<<heredoc')
64 for (const part of body.split(/&&|\|\||;|\n/)) {
65 const [first = '', ...piped] = part.split('|')
66 const found = programOf(first.replace(/^\s*(cd\s+\S+|\(|\{)\s*/, '')) ?? piped.map(stage => programOf(stage)).find(p => p === 'tee' || p === 'Out-File' || p === 'Set-Content' || p === 'Add-Content')
67 if (found) return found
68 }
69 return undefined
70}
71
72const fill = (template: string, values: Record<string, string>) =>
73 Object.entries(values).reduce((text, [key, value]) => text.split(`{${key}}`).join(value), template)
74
75const hasEmptyValue = (line: string, values: Record<string, string>) =>
76 Object.entries(values).some(([key, value]) => !value && line.includes(`{${key}}`))
77
78const fillLines = (template: string, values: Record<string, string>) =>
79 nonEmptyLines(template)
80 .filter(line => !hasEmptyValue(line, values))
81 .map(line => fill(line, values))
82 .join('\n')
83
84const CHANGE_TOOLS = ['Edit', 'Write', 'NotebookEdit']
85const SKILL_TOOL = 'Skill'
86const WEB_TOOLS = /^(WebSearch|WebFetch)$|^mcp__.*(search|fetch|docs)/i
87const REFERENCE_DIRS = ['reference', 'references']
88const SKILL_OWN_FILES = /^(skill|license|readme)\b/i
89
90type SkillReferences = { files: string[]; read: Set<string>; reminded: boolean }
91type LoopState = {
92 webRead: boolean
93 webReminded: boolean
94 skillReferences: Map<string, SkillReferences>
95}
96
97const mainLoop: LoopState = {
98 webRead: false,
99 webReminded: false,
100 skillReferences: new Map(),
101}
102let skillCallerAgentId: string | undefined
103
104const isLaunchOnly = (output: unknown) =>
105 typeof output === 'object' && output !== null && 'status' in output && String(output.status).endsWith('_launched')
106
107const matchesAny =(patterns: string[], tool: string) =>
108 patterns.some(pattern => (pattern.endsWith('*') ? tool.startsWith(pattern.slice(0, -1)) : tool === pattern))
109
110const samePath = (a: string, b: string) => a.replace(/\\/g, '/').toLowerCase() === b.replace(/\\/g, '/').toLowerCase()
111
112const skillBaseOf = (text: string) => /Base directory for this skill: (.+)/.exec(text)?.[1]?.trim()
113
114const referenceFilesOf = async ($: EngineInterface, base: string) => {
115 const separator = base.includes('\\') ? '\\' : '/'
116 const join = (...parts: string[]) => parts.join(separator)
117 const markdownIn = async (dir: string, keep: (name: string) => boolean) =>
118 (await $.fs.exists(dir)) ? (await $.fs.list(dir)).filter(f => f.kind === 'file' && /\.md$/i.test(f.name) && keep(f.name)).map(f => join(dir, f.name)) : []
119 const files: string[] = []
120 for (const dir of REFERENCE_DIRS) files.push(...(await markdownIn(join(base, dir), () => true)))
121 files.push(...(await markdownIn(base, name => !SKILL_OWN_FILES.test(name))))
122 return files
123}
124
125const markReferenceRead = (skillReferences: Map<string, SkillReferences>, path: string) => {
126 for (const skill of skillReferences.values()) for (const file of skill.files) if (samePath(file, path)) skill.read.add(file)
127}
128
129const takeUnremindedSkills = (skillReferences: Map<string, SkillReferences>) => {
130 const unread = [...skillReferences].filter(([, skill]) => !skill.reminded && !skill.read.size && skill.files.length)
131 for (const [, skill] of unread) skill.reminded = true
132 return unread.length ? { names: unread.map(([name]) => name).join(' and '), files: unread.flatMap(([, skill]) => skill.files) } : undefined
133}
134
135export const register: Register = on => {
136 on('prompt.compose', async ($, e, next) => {
137 let sections: PromptComposeSection[] = (await next(e)).sections.filter(s => !DROPPED_SECTIONS.includes(s.id))
138 const dir = `${$.plugin.root}/instructions`
139 const mdIn = async (path: string) =>
140 (await $.fs.list(path)).filter(f => f.kind === 'file' && f.name.endsWith('.md')).map(f => f.name).sort()
141
142 for (const name of await mdIn(dir)) {
143 const ids = name.slice(0, -3).split('+')
144 const text = (await $.fs.read(`${dir}/${name}`)).trim()
145 sections = sections.map(s => (ids.includes(s.id) ? { ...s, text } : s))
146 }
147
148 for (const name of await mdIn(`${dir}/added`)) {
149 const text = (await $.fs.read(`${dir}/added/${name}`)).trim()
150 sections.push({ id: `brief-guard:${name.slice(0, -3)}`, text, scope: 'session' })
151 }
152
153 return { sections }
154 })
155
156 on('prompt.attachment', async ($, e, next) => {
157 if (DROPPED_REMINDERS.includes(e.type)) return { text: null }
158 if (e.type === MCP_INSTRUCTIONS) return next({ ...e, text: e.text.replace(STATUS_LINE_REQUEST, '') })
159 const path = `${$.plugin.root}/instructions/reminders/${e.type}.md`
160 const { request } = await readTurn($)
161 return (await $.fs.exists(path)) ? { text: fillLines(await $.fs.read(path), { request }) } : next(e)
162 })
163
164 on('skill.prompt', async ($, e, next) => {
165 const { text } = await next(e)
166 if (!skillCallerAgentId) {
167 const base = skillBaseOf(text)
168 const files = base ? await referenceFilesOf($, base).catch(() => []) : []
169 mainLoop.skillReferences.set(e.skill, { files, read: new Set(), reminded: false })
170 }
171 const footer = (await $.fs.read(`${$.plugin.root}/instructions/skills/footer.md`).catch(() => '')).trim()
172 return { text: footer ? `${text}\n\n${footer}` : text }
173 })
174
175 on('prompt.submit', async ($, e, next) => {
176 const request = USER_ORIGINS.includes(e.origin.kind) ? excerptOf(e.text) : ''
177 if (request) await markTurn($, { request })
178 if (e.origin.kind !== NOTIFICATION_ORIGIN) return next(e)
179 const template = await $.fs.read(`${$.plugin.root}/instructions/reminders/task_notification.md`).catch(() => '')
180 const note = fillLines(template, { request: (await readTurn($)).request })
181 return next(note ? { ...e, context: [...(e.context ?? []), note] } : e)
182 })
183
184 on('turn.start', async ($, e, next) => {
185 await markTurn($, { silenceAllowed: false })
186 mainLoop.webRead = false
187 mainLoop.webReminded = false
188 return next(e)
189 })
190
191
192 on('tool.call', async ($, e, next) => {
193 if (e.tool === SKILL_TOOL) skillCallerAgentId = e.agentId
194 const result = await next(e)
195 if (e.agentId || result.deny !== undefined) return result
196 const command = 'command' in e && typeof e.command === 'string' ? e.command : ''
197 const filePath = 'file_path' in e && typeof e.file_path === 'string' ? e.file_path : ''
198 if (e.tool === 'Read' && filePath) markReferenceRead(mainLoop.skillReferences, filePath)
199 if (WEB_TOOLS.test(String(e.tool))) mainLoop.webRead = true
200 const isChange = CHANGE_TOOLS.includes(String(e.tool))
201 const shellProgram = SHELL_TOOLS.includes(String(e.tool)) ? localFileProgram(command) : undefined
202 const unreadSkill = isChange ? takeUnremindedSkills(mainLoop.skillReferences) : undefined
203 const needsWeb = isChange && !mainLoop.webRead && !mainLoop.webReminded
204 if (needsWeb) mainLoop.webReminded = true
205 const names = [
206 result.isError && 'tool_error',
207 e.tool === 'Agent' && (isLaunchOnly(result.result) ? 'agent_launched' : 'agent_result'),
208 shellProgram && 'shell_files',
209 unreadSkill && 'skill_refs',
210 needsWeb && 'web_sources',
211 ].filter((name): name is string => Boolean(name))
212 if (!names.length) return result
213 const templates = await Promise.all(
214 names.map(name => $.fs.read(`${$.plugin.root}/instructions/reminders/${name}.md`).catch(() => '')),
215 )
216 const values = {
217 request: (await readTurn($)).request,
218 program: shellProgram ?? '',
219 skill: unreadSkill?.names ?? '',
220 files: unreadSkill?.files.join('\n') ?? '',
221 }
222 const text = fillLines(templates.join('\n'), values)
223 return text ? { ...result, context: [...(result.context ?? []), text] } : result
224 }).catch(($, e, next) => next(e))
225
226 on('classic.PermissionRequest', async ($, e, next) => {
227 const result = await next(e)
228 const isBypassed = e.permission_mode === 'bypassPermissions' && !PERSON_TOOLS.includes(e.tool_name)
229 return isBypassed && !result.decision ? { ...result, decision: { behavior: 'allow' } } : result
230 })
231
232 on('session.compact', async ($, e, next) => {
233 const extra = (await $.fs.read(`${$.plugin.root}/instructions/compact/summary.md`).catch(() => '')).trim()
234 const instructions = [e.instructions, extra].filter(Boolean).join('\n\n')
235 return next(instructions ? { ...e, instructions } : e)
236 }).catch(($, e, next) => next(e))
237
238 on('session.append', { door: 'compaction' }, async ($, e, next) => {
239 if (e.agentId || e.message.type !== 'user') return next(e)
240 const note = (await $.fs.read(`${$.plugin.root}/instructions/compact/after.md`).catch(() => '')).trim()
241 if (!note) return next(e)
242 const content = typeof e.message.content === 'string' ? [{ type: 'text' as const, text: e.message.content }] : e.message.content
243 return next({ ...e, message: { ...e.message, content: [...content, { type: 'text', text: note }] } })
244 }).catch(($, e, next) => next(e))
245
246 on('session.append', async ($, e, next) => {
247 if (e.agentId || e.message.type !== 'user') return next(e)
248 const content = typeof e.message.content === 'string' ? [{ type: 'text' as const, text: e.message.content }] : e.message.content
249 const isEmptyReplyNudge = content.some(block => block.type === 'text' && typeof block.text === 'string' && block.text.startsWith(EMPTY_REPLY_NUDGE))
250 if (!isEmptyReplyNudge) return next(e)
251 const turn = await readTurn($)
252 const name = turn.silenceAllowed ? 'empty_reply_again' : 'empty_reply'
253 const template = await $.fs.read(`${$.plugin.root}/instructions/reminders/${name}.md`).catch(() => '')
254 const note = fillLines(template, { request: turn.request })
255 if (!note) return next(e)
256 if (!turn.silenceAllowed) await markTurn($, { silenceAllowed: true })
257 const kept = turn.silenceAllowed ? content : []
258 return next({ ...e, message: { ...e.message, content: [...kept, { type: 'text', text: note }] } })
259 }).catch(($, e, next) => next(e))
260
261 on('tool.describe', async ($, e, next) => {
262 const dir = `${$.plugin.root}/instructions/tools`
263 const readList = async (name: string) => nonEmptyLines(await $.fs.read(`${dir}/${name}`).catch(() => ''))
264 const readNote = async (name: string) => (await $.fs.read(`${dir}/${name}`).catch(() => '')).trim()
265 const isDeferred = matchesAny(await readList('deferred.md'), e.tool)
266 const ownGuidance = /^[\w-]+$/.test(e.tool) ? await readNote(`${e.tool}.md`) : ''
267 const callNote = matchesAny(await readList('silent.txt'), e.tool) ? await readNote('silent.md') : ''
268 const guidance = [ownGuidance, callNote].filter(Boolean).join('\n\n')
269 const result = await next(e)
270 const described = guidance ? { ...result, description: `${result.description}\n\n${guidance}` } : result
271 return isDeferred ? { ...described, isDeferred: true } : described
272 })
273}
274types/index.d.ts 11 lines1export type TurnMarks = {
2 request: string
3 silenceAllowed: boolean
4}
5
6declare module 'claude-code' {
7 interface PluginState {
8 'brief-guard': { turn: TurnMarks }
9 }
10}
11