cc-dev-template 0.1.73 → 0.1.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/done.md +29 -2
- package/src/scripts/task-output-guard.js +4 -66
- package/src/skills/agent-browser/SKILL.md +0 -1
- package/src/skills/creating-agent-skills/references/create-step-1-understand.md +9 -0
- package/src/skills/creating-agent-skills/references/create-step-2-design.md +24 -0
- package/src/skills/creating-agent-skills/references/create-step-3-write.md +9 -0
- package/src/skills/creating-agent-skills/references/create-step-5-install.md +29 -5
- package/src/skills/creating-agent-skills/scripts/validate-skill.js +39 -13
- package/src/skills/creating-agent-skills/templates/router-skill.md +6 -51
- package/src/skills/creating-agent-skills/templates/simple-skill.md +0 -10
- package/src/skills/creating-sub-agents/SKILL.md +18 -0
- package/src/skills/creating-sub-agents/references/create-step-1-understand.md +46 -0
- package/src/skills/creating-sub-agents/references/create-step-2-design.md +181 -0
- package/src/skills/creating-sub-agents/references/create-step-3-write.md +154 -0
- package/src/skills/creating-sub-agents/references/create-step-4-review.md +67 -0
- package/src/skills/creating-sub-agents/references/create-step-5-install.md +76 -0
- package/src/skills/creating-sub-agents/references/fix-step-1-diagnose.md +99 -0
- package/src/skills/creating-sub-agents/references/fix-step-2-apply.md +77 -0
- package/src/skills/creating-sub-agents/references/fix-step-3-validate.md +52 -0
- package/src/skills/creating-sub-agents/references/principles.md +157 -0
- package/src/skills/creating-sub-agents/scripts/find-subagents.js +217 -0
- package/src/skills/creating-sub-agents/scripts/validate-subagent.js +336 -0
- package/src/skills/creating-sub-agents/templates/basic-subagent.md +21 -0
- package/src/skills/creating-sub-agents/templates/domain-specialist.md +52 -0
- package/src/skills/creating-sub-agents/templates/restricted-subagent.md +29 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* validate-subagent.js
|
|
5
|
+
*
|
|
6
|
+
* Validates a Claude Code sub-agent definition against best practices.
|
|
7
|
+
* Returns detailed findings with severity levels.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node validate-subagent.js <agent-file-path> [--json]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const agentPath = args.find(a => !a.startsWith('--'));
|
|
17
|
+
const jsonOutput = args.includes('--json');
|
|
18
|
+
|
|
19
|
+
if (!agentPath) {
|
|
20
|
+
console.error('Usage: node validate-subagent.js <agent-file-path> [--json]');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const resolvedPath = path.resolve(agentPath);
|
|
25
|
+
|
|
26
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
27
|
+
console.error(`Error: File not found at ${resolvedPath}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!resolvedPath.endsWith('.md')) {
|
|
32
|
+
console.error(`Error: Sub-agent file must be a .md file`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const findings = [];
|
|
37
|
+
|
|
38
|
+
function addFinding(category, severity, message, line = null, suggestion = null) {
|
|
39
|
+
findings.push({ category, severity, message, line, suggestion });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseFrontmatter(content) {
|
|
43
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
+
if (!match) return { valid: false, data: null, raw: null };
|
|
45
|
+
|
|
46
|
+
const raw = match[1];
|
|
47
|
+
const data = {};
|
|
48
|
+
const lines = raw.split('\n');
|
|
49
|
+
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
const colonIndex = line.indexOf(':');
|
|
52
|
+
if (colonIndex > 0 && !line.startsWith(' ') && !line.startsWith('\t')) {
|
|
53
|
+
const key = line.slice(0, colonIndex).trim();
|
|
54
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
55
|
+
|
|
56
|
+
if (value === '' || value === '|' || value === '>') continue;
|
|
57
|
+
|
|
58
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
59
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
60
|
+
value = value.slice(1, -1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
data[key] = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { valid: true, data, raw };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getBody(content) {
|
|
71
|
+
const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
|
|
72
|
+
return match ? match[1] : content;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
76
|
+
const frontmatter = parseFrontmatter(content);
|
|
77
|
+
const body = getBody(content);
|
|
78
|
+
const lines = content.split('\n');
|
|
79
|
+
|
|
80
|
+
// === VALIDATION CHECKS ===
|
|
81
|
+
|
|
82
|
+
// Check 1: Frontmatter exists
|
|
83
|
+
if (!frontmatter.valid) {
|
|
84
|
+
addFinding('STRUCTURE', 'error', 'Missing or invalid YAML frontmatter');
|
|
85
|
+
} else {
|
|
86
|
+
// Check 2: Required fields
|
|
87
|
+
if (!frontmatter.data.name) {
|
|
88
|
+
addFinding('STRUCTURE', 'error', 'Missing required field: name');
|
|
89
|
+
} else {
|
|
90
|
+
const namePattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
91
|
+
if (!namePattern.test(frontmatter.data.name)) {
|
|
92
|
+
addFinding('STRUCTURE', 'error',
|
|
93
|
+
`Invalid name format: "${frontmatter.data.name}" - must be hyphen-case`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (frontmatter.data.name.length > 64) {
|
|
97
|
+
addFinding('STRUCTURE', 'error',
|
|
98
|
+
`Name exceeds 64 characters (${frontmatter.data.name.length})`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check name matches filename
|
|
102
|
+
const fileName = path.basename(resolvedPath, '.md');
|
|
103
|
+
if (frontmatter.data.name !== fileName) {
|
|
104
|
+
addFinding('STRUCTURE', 'warning',
|
|
105
|
+
`Name "${frontmatter.data.name}" doesn't match filename "${fileName}.md"`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!frontmatter.data.description) {
|
|
110
|
+
addFinding('STRUCTURE', 'error', 'Missing required field: description');
|
|
111
|
+
} else {
|
|
112
|
+
if (frontmatter.data.description.length < 10) {
|
|
113
|
+
addFinding('DESCRIPTION', 'warning',
|
|
114
|
+
'Description is very short — may not provide enough context for delegation');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const desc = frontmatter.data.description.toLowerCase();
|
|
118
|
+
if (desc.startsWith('a ') || desc.startsWith('an ') || desc.startsWith('the ')) {
|
|
119
|
+
addFinding('DESCRIPTION', 'info',
|
|
120
|
+
'Description could be more actionable — consider starting with a verb or role');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check model validity
|
|
125
|
+
if (frontmatter.data.model) {
|
|
126
|
+
const validModels = ['haiku', 'sonnet', 'opus', 'inherit'];
|
|
127
|
+
if (!validModels.includes(frontmatter.data.model)) {
|
|
128
|
+
addFinding('STRUCTURE', 'warning',
|
|
129
|
+
`Model "${frontmatter.data.model}" may not be valid. Expected: ${validModels.join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check permission mode validity
|
|
134
|
+
if (frontmatter.data.permissionMode) {
|
|
135
|
+
const validModes = ['default', 'acceptEdits', 'dontAsk', 'delegate', 'bypassPermissions', 'plan'];
|
|
136
|
+
if (!validModes.includes(frontmatter.data.permissionMode)) {
|
|
137
|
+
addFinding('STRUCTURE', 'error',
|
|
138
|
+
`Invalid permissionMode: "${frontmatter.data.permissionMode}". Valid: ${validModes.join(', ')}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (frontmatter.data.permissionMode === 'bypassPermissions') {
|
|
142
|
+
addFinding('SECURITY', 'warning',
|
|
143
|
+
'Using bypassPermissions — ensure tools are restricted to limit risk');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Check for both tools and disallowedTools
|
|
148
|
+
if (frontmatter.data.tools && frontmatter.data.disallowedTools) {
|
|
149
|
+
addFinding('STRUCTURE', 'warning',
|
|
150
|
+
'Both tools and disallowedTools are set — use one approach, not both');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check for XML in frontmatter
|
|
154
|
+
if (frontmatter.raw && /[<>]/.test(frontmatter.raw)) {
|
|
155
|
+
addFinding('STRUCTURE', 'error',
|
|
156
|
+
'Frontmatter contains XML angle brackets (< >) — move XML tags to the body');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check 3: Body quality
|
|
161
|
+
const wordCount = body.split(/\s+/).filter(w => w.length > 0).length;
|
|
162
|
+
const bodyLineCount = body.split('\n').length;
|
|
163
|
+
|
|
164
|
+
if (wordCount < 10) {
|
|
165
|
+
addFinding('CONTENT', 'error',
|
|
166
|
+
'System prompt body is nearly empty — sub-agent needs instructions');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (bodyLineCount > 200) {
|
|
170
|
+
addFinding('SIZE', 'warning',
|
|
171
|
+
`System prompt is ${bodyLineCount} lines — consider splitting scope or using skills preloading`,
|
|
172
|
+
null,
|
|
173
|
+
'Move domain knowledge into skills and preload with the skills field');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check 4: Output format defined
|
|
177
|
+
const hasOutputSection = /##?\s*(output|return|result|format|response)/i.test(body);
|
|
178
|
+
const hasOutputGuidance = /(return|provide|report|output|summarize)\s+(only|a |the |concise|structured)/i.test(body);
|
|
179
|
+
|
|
180
|
+
if (!hasOutputSection && !hasOutputGuidance) {
|
|
181
|
+
addFinding('CONTENT', 'warning',
|
|
182
|
+
'No output format guidance found — sub-agent may return verbose or unstructured results',
|
|
183
|
+
null,
|
|
184
|
+
'Add an "## Output" section defining what the sub-agent should return');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check 5: Role statement
|
|
188
|
+
const firstNonEmpty = body.split('\n').find(l => l.trim().length > 0);
|
|
189
|
+
if (firstNonEmpty && !/(you are|as a|expert|specialist|senior|responsible for)/i.test(firstNonEmpty)) {
|
|
190
|
+
addFinding('CONTENT', 'info',
|
|
191
|
+
'Consider starting with a role statement (e.g., "You are a senior code reviewer...")');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check 6: Second person violations
|
|
195
|
+
const secondPersonPatterns = [
|
|
196
|
+
/\byou should\b/gi,
|
|
197
|
+
/\byou can\b/gi,
|
|
198
|
+
/\byou need\b/gi,
|
|
199
|
+
/\byou might\b/gi
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
let secondPersonCount = 0;
|
|
203
|
+
const secondPersonExamples = [];
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < lines.length; i++) {
|
|
206
|
+
const line = lines[i];
|
|
207
|
+
if (i < 3) continue;
|
|
208
|
+
|
|
209
|
+
for (const pattern of secondPersonPatterns) {
|
|
210
|
+
const matches = line.match(pattern);
|
|
211
|
+
if (matches) {
|
|
212
|
+
secondPersonCount += matches.length;
|
|
213
|
+
if (secondPersonExamples.length < 3) {
|
|
214
|
+
secondPersonExamples.push({ line: i + 1, text: line.trim().slice(0, 60) });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (secondPersonCount > 3) {
|
|
221
|
+
addFinding('STYLE', 'warning',
|
|
222
|
+
`Found ${secondPersonCount} instance(s) of "you should/can/need/might" — prefer imperative form`,
|
|
223
|
+
secondPersonExamples[0]?.line,
|
|
224
|
+
'"You should check..." becomes "Check..."');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Check 7: Negative framing
|
|
228
|
+
const negativePatterns = [
|
|
229
|
+
/\bdon't\b/gi,
|
|
230
|
+
/\bdo not\b/gi,
|
|
231
|
+
/\bnever\b/gi,
|
|
232
|
+
/\bavoid\b/gi
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
let negativeCount = 0;
|
|
236
|
+
|
|
237
|
+
for (let i = 0; i < lines.length; i++) {
|
|
238
|
+
if (i < 3) continue;
|
|
239
|
+
for (const pattern of negativePatterns) {
|
|
240
|
+
const matches = lines[i].match(pattern);
|
|
241
|
+
if (matches) negativeCount += matches.length;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (negativeCount > 5) {
|
|
246
|
+
addFinding('STYLE', 'warning',
|
|
247
|
+
`Found ${negativeCount} negative framing instances — prefer positive framing`,
|
|
248
|
+
null,
|
|
249
|
+
'"Don\'t modify X" becomes "Only modify Y"');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check 8: Memory instructions
|
|
253
|
+
if (frontmatter.valid && frontmatter.data.memory) {
|
|
254
|
+
const hasMemoryInstructions = /memory|MEMORY\.md|agent-memory/i.test(body);
|
|
255
|
+
if (!hasMemoryInstructions) {
|
|
256
|
+
addFinding('CONTENT', 'warning',
|
|
257
|
+
'Memory is enabled but no memory instructions found in system prompt',
|
|
258
|
+
null,
|
|
259
|
+
'Add instructions for reading/updating the memory directory');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Check 9: Skills existence check
|
|
264
|
+
if (frontmatter.valid && frontmatter.raw && /skills:/i.test(frontmatter.raw)) {
|
|
265
|
+
addFinding('CONTENT', 'info',
|
|
266
|
+
'Skills preloading is configured — verify listed skills exist at user or project level');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// === OUTPUT ===
|
|
270
|
+
|
|
271
|
+
const result = {
|
|
272
|
+
agent: {
|
|
273
|
+
path: resolvedPath,
|
|
274
|
+
name: frontmatter.data?.name || path.basename(resolvedPath, '.md'),
|
|
275
|
+
wordCount,
|
|
276
|
+
lineCount: bodyLineCount
|
|
277
|
+
},
|
|
278
|
+
findings,
|
|
279
|
+
summary: {
|
|
280
|
+
errors: findings.filter(f => f.severity === 'error').length,
|
|
281
|
+
warnings: findings.filter(f => f.severity === 'warning').length,
|
|
282
|
+
info: findings.filter(f => f.severity === 'info').length,
|
|
283
|
+
passed: findings.filter(f => f.severity === 'error').length === 0
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
if (jsonOutput) {
|
|
288
|
+
console.log(JSON.stringify(result, null, 2));
|
|
289
|
+
} else {
|
|
290
|
+
console.log(`\n=== Sub-Agent Validation: ${result.agent.name} ===\n`);
|
|
291
|
+
console.log(`Path: ${result.agent.path}`);
|
|
292
|
+
console.log(`Words: ${result.agent.wordCount}`);
|
|
293
|
+
console.log(`Lines: ${result.agent.lineCount}`);
|
|
294
|
+
console.log();
|
|
295
|
+
|
|
296
|
+
if (findings.length === 0) {
|
|
297
|
+
console.log('All checks passed!\n');
|
|
298
|
+
} else {
|
|
299
|
+
const errors = findings.filter(f => f.severity === 'error');
|
|
300
|
+
const warnings = findings.filter(f => f.severity === 'warning');
|
|
301
|
+
const infos = findings.filter(f => f.severity === 'info');
|
|
302
|
+
|
|
303
|
+
if (errors.length > 0) {
|
|
304
|
+
console.log('ERRORS:');
|
|
305
|
+
for (const f of errors) {
|
|
306
|
+
console.log(` [${f.category}] ${f.message}`);
|
|
307
|
+
if (f.line) console.log(` Line ${f.line}`);
|
|
308
|
+
if (f.suggestion) console.log(` Suggestion: ${f.suggestion}`);
|
|
309
|
+
}
|
|
310
|
+
console.log();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (warnings.length > 0) {
|
|
314
|
+
console.log('WARNINGS:');
|
|
315
|
+
for (const f of warnings) {
|
|
316
|
+
console.log(` ! [${f.category}] ${f.message}`);
|
|
317
|
+
if (f.line) console.log(` Line ${f.line}`);
|
|
318
|
+
if (f.suggestion) console.log(` Suggestion: ${f.suggestion}`);
|
|
319
|
+
}
|
|
320
|
+
console.log();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (infos.length > 0) {
|
|
324
|
+
console.log('INFO:');
|
|
325
|
+
for (const f of infos) {
|
|
326
|
+
console.log(` i [${f.category}] ${f.message}`);
|
|
327
|
+
}
|
|
328
|
+
console.log();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
console.log(`Summary: ${result.summary.errors} error(s), ${result.summary.warnings} warning(s), ${result.summary.info} info(s)`);
|
|
332
|
+
console.log(`Status: ${result.summary.passed ? 'PASSED' : 'FAILED'}\n`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
process.exit(result.summary.passed ? 0 : 1);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{AGENT_NAME}}
|
|
3
|
+
description: {{What it does and when Claude should delegate to it}}
|
|
4
|
+
tools: {{Tool list, e.g., Read, Grep, Glob, Bash}}
|
|
5
|
+
model: {{haiku|sonnet|opus|inherit}}
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a {{role description}}.
|
|
9
|
+
|
|
10
|
+
When invoked:
|
|
11
|
+
1. {{First action}}
|
|
12
|
+
2. {{Second action}}
|
|
13
|
+
3. {{Third action}}
|
|
14
|
+
|
|
15
|
+
## Guidelines
|
|
16
|
+
|
|
17
|
+
{{Domain knowledge, conventions, constraints}}
|
|
18
|
+
|
|
19
|
+
## Output
|
|
20
|
+
|
|
21
|
+
{{What to return — format, structure, level of detail}}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{AGENT_NAME}}
|
|
3
|
+
description: "Domain specialist for {{domain description}}. Owns and maintains code quality for the {{domain-name}} domain."
|
|
4
|
+
tools: Read, Write, Edit, Grep, Glob, Bash, LSP
|
|
5
|
+
memory: project
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are the **{{domain-name}}** domain specialist on a development team.
|
|
9
|
+
|
|
10
|
+
<role>
|
|
11
|
+
You are a software engineer who owns the {{domain-name}} domain. You write code, maintain tests, enforce quality, and deeply understand your part of the codebase. You are responsible for the code quality and architectural integrity of your domain.
|
|
12
|
+
</role>
|
|
13
|
+
|
|
14
|
+
<domain>
|
|
15
|
+
**Name:** {{domain-name}}
|
|
16
|
+
**Description:** {{What this domain covers}}
|
|
17
|
+
**Ownership:** {{Directory paths this agent owns}}
|
|
18
|
+
</domain>
|
|
19
|
+
|
|
20
|
+
<memory>
|
|
21
|
+
**On startup, read your memory file at `.claude/agent-memory/{{AGENT_NAME}}/MEMORY.md`.**
|
|
22
|
+
This contains accumulated knowledge from previous sessions. Update it when you learn something that would help future sessions.
|
|
23
|
+
|
|
24
|
+
Memory is tribal knowledge — patterns, gotchas, decisions, integration points. Not documentation of what code does, but how to work effectively in this codebase.
|
|
25
|
+
|
|
26
|
+
Review and curate your memory. Remove entries that are no longer accurate. Keep it under 100 lines.
|
|
27
|
+
</memory>
|
|
28
|
+
|
|
29
|
+
<team>
|
|
30
|
+
You are part of a team coordinated by a team lead.
|
|
31
|
+
|
|
32
|
+
- **Team Lead** (team-lead): Assigns work, coordinates between domains, reviews output
|
|
33
|
+
- **Other domain specialists**: Message them directly when your work affects their domain
|
|
34
|
+
|
|
35
|
+
Always refer to teammates by name when using SendMessage.
|
|
36
|
+
</team>
|
|
37
|
+
|
|
38
|
+
<responsibilities>
|
|
39
|
+
1. Deeply understand your domain before making changes
|
|
40
|
+
2. Write high-quality code that follows established patterns
|
|
41
|
+
3. Write and maintain tests for changes
|
|
42
|
+
4. Coordinate with the team lead for cross-domain changes
|
|
43
|
+
5. Update your memory file with tribal knowledge
|
|
44
|
+
6. Mark tasks as completed via TaskUpdate when finished
|
|
45
|
+
7. After completing a task, check TaskList for available work
|
|
46
|
+
</responsibilities>
|
|
47
|
+
|
|
48
|
+
<boundaries>
|
|
49
|
+
- Only modify files within your domain's ownership zone
|
|
50
|
+
- Coordinate with the team lead for changes outside your domain
|
|
51
|
+
- All user communication goes through the team lead
|
|
52
|
+
</boundaries>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{AGENT_NAME}}
|
|
3
|
+
description: {{What it does and when Claude should delegate to it}}
|
|
4
|
+
tools: {{Restricted tool list, e.g., Read, Grep, Glob}}
|
|
5
|
+
model: {{haiku|sonnet|opus|inherit}}
|
|
6
|
+
permissionMode: dontAsk
|
|
7
|
+
hooks:
|
|
8
|
+
PreToolUse:
|
|
9
|
+
- matcher: "Bash"
|
|
10
|
+
hooks:
|
|
11
|
+
- type: command
|
|
12
|
+
command: "{{./path/to/validation-script.sh}}"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
You are a {{role description}} with restricted access.
|
|
16
|
+
|
|
17
|
+
When invoked:
|
|
18
|
+
1. {{First action}}
|
|
19
|
+
2. {{Second action}}
|
|
20
|
+
3. {{Third action}}
|
|
21
|
+
|
|
22
|
+
## Constraints
|
|
23
|
+
|
|
24
|
+
- Only access files within {{scope}}
|
|
25
|
+
- {{Additional restrictions}}
|
|
26
|
+
|
|
27
|
+
## Output
|
|
28
|
+
|
|
29
|
+
{{What to return — format, structure, level of detail}}
|