13 engineering skills, the nova output style and mod, and settings for Claude Code, with cross-platform installers, backups, and clean replacement.

English · Українська · Русский
A portable snapshot of the author's local Claude Code setup, synchronized on 2026-10-26. It contains 13 engineering skills, the nova output style, and the nova mod, plus portable settings.
Skills cover code changes, tests and defect diagnosis, security, server-side work, databases, frontend implementation, visual and UX design, architecture, implementation plans, builds and operations, commit messages, instructions for AI agents, and triage of problems on live sites and services. Every rule in a skill states when it applies, what to do, and why, followed by an example.
The nova output style keeps Claude silent between tool calls and sends one final answer in the user's language that opens with the result and usually fits in one short paragraph. Plan steps and questions to the user use the user's language too, while tool descriptions and briefs for other agents stay in English. The answer stays in plain paragraphs even when agent reports, tool results, or fetched pages arrive with tables or lists, and several rules use the wording of the built-in Concise style.
The nova mod is a Claude Code plugin with a hooks module:
The portable settings set CLAUDE_CODE_THRIFTY_SONIC to 0, which turns off a Claude Code experiment that shortens the Bash tool's guidance and steers Claude toward shell commands and scripts for file work.
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. 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 mod loads 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. No agents are bundled, so agents from an earlier version of this setup are removed after the backup. 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 nova, and env.CLAUDE_CODE_PLUGIN_DIRS, which lists the installed mod folder.
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 the mod's .claude-plugin/types folder is left out because Claude Code generates those type definitions for the version it runs. The empty claude/agents folder holds only a .gitkeep file, so the installer still manages that folder.
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. Skills load 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 249 lines1import type { CoreEngineInterface, Register } from 'claude-code'
2
3type Configuration = {
4 discoveryTool: string
5 promptSectionFiles: Readonly<Record<string, string>>
6 upfrontTools: readonly string[]
7 deferredTools: readonly string[]
8 deferredPrefixes: readonly string[]
9 windowsDeferredTools?: readonly string[]
10 removedPromptSections: readonly string[]
11 removedInstructionPatterns: readonly string[]
12 removedAttachmentPatterns: Readonly<Record<string, readonly string[]>>
13 removedContextPatterns?: Readonly<Record<string, readonly string[]>>
14 attachmentReminders?: Readonly<Record<string, { pattern: string, file: string }>>
15 toolDescriptionReminders?: Readonly<Record<string, string>>
16 windowsToolDescriptionReminders?: Readonly<Record<string, string>>
17 agentRules?: string
18 skillReminder?: {
19 file: string
20 editTools: readonly string[]
21 filePattern: string
22 }
23 emptyTurnNudge?: {
24 pattern: string
25 file: string
26 }
27 narrationReminder?: {
28 file: string
29 closingTools: readonly string[]
30 limit: number
31 }
32}
33
34type LoopState = {
35 skillReminded: boolean
36 narrated: boolean
37 narrationReminders: number
38}
39
40const WINDOWS_PATH = /^[A-Za-z]:[\\/]/
41const ACTIVE_AGENT_STATUSES: readonly string[] = ['pending', 'running', 'waiting']
42const FINISHED_TASK = /<task-id>([^<]+)<\/task-id>[\s\S]*?<status>(?:completed|failed|killed|stopped)<\/status>/g
43
44function getLoopState(states: Map<string | undefined, LoopState>, agentId: string | undefined): LoopState {
45 let state = states.get(agentId)
46 if (!state) {
47 state = { skillReminded: false, narrated: false, narrationReminders: 0 }
48 states.set(agentId, state)
49 }
50 return state
51}
52
53function textOf(content: readonly unknown[]): string {
54 return content.map(block => stringField(block, 'text') ?? '').join('\n')
55}
56
57function isRecord(value: unknown): value is Record<string, unknown> {
58 return typeof value === 'object' && value !== null && !Array.isArray(value)
59}
60
61function stringField(value: unknown, key: string): string | undefined {
62 if (!isRecord(value)) return undefined
63 const field = value[key]
64 return typeof field === 'string' ? field : undefined
65}
66
67async function skillLoaded($: CoreEngineInterface, skillAgents: ReadonlySet<string | undefined>, agentId: string | undefined): Promise<boolean> {
68 if (skillAgents.has(agentId)) return true
69 if (agentId !== undefined) return false
70 const messages = await $.session.messages()
71 return messages.some(message => message.toolUses.some(use => use.tool === 'Skill'))
72}
73
74async function backgroundWorkRunning($: CoreEngineInterface, backgroundCommands: ReadonlySet<string>): Promise<boolean> {
75 if (backgroundCommands.size > 0) return true
76 const agents = await $.agent.list()
77 return agents.some(agent => agent.parentId === undefined && ACTIVE_AGENT_STATUSES.includes(agent.status))
78}
79
80function readConfiguration($: CoreEngineInterface): Promise<Configuration> {
81 return $.fs.read(`${$.plugin.root}/config.json`).then(text => JSON.parse(text) as Configuration)
82}
83
84async function readPromptSections($: CoreEngineInterface, files: Readonly<Record<string, string>>): Promise<Map<string, string>> {
85 const sections = new Map<string, string>()
86 for (const [id, path] of Object.entries(files)) {
87 sections.set(id, await $.fs.read(`${$.plugin.root}/${path}`))
88 }
89 return sections
90}
91
92function readReminder($: CoreEngineInterface, path: string, files: Map<string, Promise<string>>): Promise<string> {
93 let text = files.get(path)
94 if (!text) {
95 text = $.fs.read(`${$.plugin.root}/${path}`)
96 files.set(path, text)
97 }
98 return text
99}
100
101export const register: Register = on => {
102 let configuration: Promise<Configuration> | undefined
103 let promptSections: Promise<Map<string, string>> | undefined
104 const reminderFiles = new Map<string, Promise<string>>()
105 const loopStates = new Map<string | undefined, LoopState>()
106 const removeInstructions = (text: string, patterns: readonly string[]) => patterns.reduce((result, pattern) => result.replace(new RegExp(pattern, 'g'), ''), text)
107 const skillAgents = new Set<string | undefined>()
108 const backgroundCommands = new Set<string>()
109 let discoveryAvailable = false
110 let windowsHost = false
111
112 on('turn.start', async ($, event, next) => {
113 loopStates.delete(event.agentId)
114 return next(event)
115 })
116
117 on('agent.spawn', async ($, event, next) => {
118 const config = await (configuration ??= readConfiguration($))
119 if (!config.agentRules || event.fork || event.resume) return next(event)
120 const rules = await readReminder($, config.agentRules, reminderFiles)
121 return next({ ...event, prompt: `${event.prompt.trimEnd()}\n\n${rules.trimEnd()}` })
122 })
123
124 on('turn.step', async function* ($, event, next) {
125 const result = yield* next(event)
126 if (event.agentId !== undefined) return result
127 const config = await (configuration ??= readConfiguration($))
128 const narration = config.narrationReminder
129 if (narration && result.answer.trim() && result.toolUses.some(use => !narration.closingTools.includes(use.name))) {
130 getLoopState(loopStates, undefined).narrated = true
131 }
132 return result
133 })
134
135 on('session.append', async ($, event, next) => {
136 const content = event.message.content
137 for (const match of textOf(content).matchAll(FINISHED_TASK)) backgroundCommands.delete(match[1]!)
138 const config = await (configuration ??= readConfiguration($))
139 const nudge = config.emptyTurnNudge
140 if (!nudge || event.agentId !== undefined || event.message.type !== 'user' || !event.message.isMeta ||
141 !new RegExp(nudge.pattern).test(textOf(content)) || !await backgroundWorkRunning($, backgroundCommands)) return next(event)
142 const text = await readReminder($, nudge.file, reminderFiles)
143 return next({ ...event, message: { ...event.message, content: [{ type: 'text', text: text.trimEnd() }] } })
144 })
145
146 on('tool.call', async ($, event, next) => {
147 const config = await (configuration ??= readConfiguration($))
148 const state = getLoopState(loopStates, event.agentId)
149 const result = await next(event)
150 if (result.deny !== undefined || loopStates.get(event.agentId) !== state) return result
151 const paths: string[] = []
152 if (event.tool === 'Skill' && !result.isError) skillAgents.add(event.agentId)
153 const backgroundTask = !result.isError ? stringField(result.result, 'backgroundTaskId') : undefined
154 if (backgroundTask) backgroundCommands.add(backgroundTask)
155 const stoppedTask = event.tool === 'TaskStop' ? stringField(event, 'task_id') : undefined
156 if (stoppedTask) backgroundCommands.delete(stoppedTask)
157 const filePath = stringField(event, 'file_path')
158 const skill = config.skillReminder
159 if (skill && filePath && !result.isError && !result.isReadOnly && !state.skillReminded && skill.editTools.includes(event.tool) &&
160 new RegExp(skill.filePattern, 'i').test(filePath) && !await skillLoaded($, skillAgents, event.agentId)) {
161 state.skillReminded = true
162 paths.push(skill.file)
163 }
164 const narration = config.narrationReminder
165 if (narration && state.narrated && event.agentId === undefined) {
166 state.narrated = false
167 if (state.narrationReminders < narration.limit) {
168 state.narrationReminders += 1
169 paths.push(narration.file)
170 }
171 }
172 if (!paths.length) return result
173 const reminders: string[] = []
174 for (const file of new Set(paths)) reminders.push(await readReminder($, file, reminderFiles))
175 if (loopStates.get(event.agentId) !== state) return result
176 return { ...result, context: [...(result.context ?? []), ...reminders] }
177 })
178
179 on('session.start', async ($, event, next) => {
180 const config = await (configuration ??= readConfiguration($))
181 windowsHost = WINDOWS_PATH.test(event.cwd)
182 discoveryAvailable = (await $.tool.list()).some(tool => tool.name === config.discoveryTool)
183 $.ui.invalidate('tool.describe')
184 return next(event)
185 })
186
187 on('tool.describe', async ($, event, next) => {
188 const config = await (configuration ??= readConfiguration($))
189 const result = await next(event)
190 let description = removeInstructions(result.description, config.removedInstructionPatterns)
191 const reminder = config.toolDescriptionReminders?.[event.tool] ??
192 (windowsHost ? config.windowsToolDescriptionReminders?.[event.tool] : undefined)
193 if (reminder) description += `\n\n${await readReminder($, reminder, reminderFiles)}`
194 if (config.upfrontTools.includes(event.tool)) return { ...result, description, isDeferred: false }
195 const defer = discoveryAvailable && event.tool !== config.discoveryTool && (
196 config.deferredTools.includes(event.tool) ||
197 config.deferredPrefixes.some(prefix => event.tool.startsWith(prefix)) ||
198 (windowsHost && (config.windowsDeferredTools?.includes(event.tool) ?? false))
199 )
200 return defer ? { ...result, description, isDeferred: true } : { ...result, description }
201 })
202
203 on('prompt.context', async ($, event, next) => {
204 const config = await (configuration ??= readConfiguration($))
205 const result = await next(event)
206 return {
207 ...result,
208 blocks: result.blocks.map(block => ({
209 ...block,
210 text: removeInstructions(block.text, config.removedContextPatterns?.[block.name] ?? []),
211 })),
212 }
213 })
214
215 on('prompt.compose', async ($, event, next) => {
216 const config = await (configuration ??= readConfiguration($))
217 const result = await next(event)
218 const replacements = await (promptSections ??= readPromptSections($, config.promptSectionFiles))
219 const sections = result.sections
220 .filter(section => !config.removedPromptSections.includes(section.id))
221 .map(section => ({
222 ...section,
223 text: replacements.get(section.id) ?? removeInstructions(section.text, config.removedInstructionPatterns),
224 }))
225 for (const [id, text] of replacements) {
226 if (!sections.some(section => section.id === id)) {
227 sections.push({ id: `${$.plugin.name}:${id}`, text, scope: 'session' })
228 }
229 }
230 return { ...result, sections }
231 })
232
233 on('prompt.attachment', async ($, event, next) => {
234 const config = await (configuration ??= readConfiguration($))
235 const result = await next(event)
236 if (result.text === null) return result
237 const patterns = config.removedAttachmentPatterns[event.type]
238 const text = patterns ? removeInstructions(result.text, patterns) : result.text
239 if (text !== result.text && !text.trim()) return { ...result, text: null }
240 const reminder = config.attachmentReminders?.[event.type]
241 return {
242 ...result,
243 text: reminder && event.origin.kind === 'engine' && new RegExp(reminder.pattern).test(text)
244 ? await readReminder($, reminder.file, reminderFiles)
245 : text,
246 }
247 })
248}
249