Getting Started: What Are Agents and Why Would You Want One?
Quick Concept
A skill teaches Claude one thing well. An agent bundles several skills that you always use together into a single specialist that Claude can delegate entire tasks to.
Think of skills as individual tools in a workshop. An agent is a trained apprentice who knows which tools to reach for and when.
In the GSD skill-creator system, agents are auto-generated from observed patterns in your workflow. You do not need to write agent files by hand. Instead, the system:
- Observes which skills activate together across your sessions
- Detects clusters of skills that consistently co-activate
- Suggests agent compositions when patterns stabilize
- Generates the agent file after you approve the suggestion
The result is a .claude/agents/<name>.md file that Claude Code discovers at session startup and can delegate work to automatically.
An AI agent is a focused sub-instance of Claude that has specific knowledge (from skills), specific tools it can use, and a defined purpose. When Claude encounters a task that matches an agent's description, it can “delegate” the work to that agent — similar to how a team lead assigns a task to the person best qualified. The agent runs in its own context, does its work, and returns the result.
Creating Your First Agent: Step by Step
Agent creation in GSD skill-creator follows an observe → detect → suggest → approve → generate pipeline. Here is every stage, from the raw session data to a working agent file.
Step 1 — Session Observation
Every time you work in a Claude Code session, the system records which skills were active. These observations are written as append-only JSONL entries in .planning/patterns/sessions.jsonl. Each entry includes:
sessionId— unique identifier for the sessionstartTime— epoch timestampactiveSkills— array of skill names loaded during that session
You do not need to do anything manually. Observation is automatic when the .planning/patterns/ directory exists.
Step 2 — Co-Activation Tracking
The CoActivationTracker analyzes session history and finds skill pairs that activate together frequently. From the source code:
// co-activation-tracker.ts
export interface SkillCoActivation {
skillPair: [string, string]; // Alphabetically sorted for consistency
coActivationCount: number;
sessions: string[]; // Session IDs where both were active
firstSeen: number; // Timestamp of first co-activation
lastSeen: number; // Timestamp of most recent
}
The tracker operates within a 14-day recency window (configurable via recencyDays) and requires a minimum of 3 co-activations before reporting a pair. It generates all combinatorial pairs from each session's active skills, then filters by threshold.
Each pair also receives a co-activation score between 0 and 1, calculated as a weighted blend of frequency (70%) and recency (30%):
// Frequency relative to total sessions, plus recency boost
return Math.min(1, frequencyScore * 0.7 + recencyBoost * 0.3);
Step 3 — Cluster Detection
Individual skill pairs are useful, but agents combine groups of skills. The ClusterDetector builds a graph from co-activation edges and finds connected components using BFS traversal.
Cluster Thresholds
| Min cluster size | 2 skills |
| Max cluster size | 5 skills |
| Min co-activations | 5 per edge |
| Stability period | 7 days |
All thresholds are configurable via ClusterConfig.
The algorithm:
- Filter co-activations to those with ≥ 5 occurrences (the edge threshold)
- Build an adjacency graph from the surviving skill pairs
- Run BFS to find connected components
- Discard components smaller than 2 or larger than 5 skills
- Score each cluster by average co-activation strength, normalized to 0–1
- Calculate stability — days since the cluster's earliest edge was first observed
Each cluster receives an auto-generated name (from common skill name prefixes) and a description. These become the defaults for the agent if you approve it.
Step 4 — Agent Suggestion
The AgentSuggestionManager orchestrates the full pipeline. When you run skill-creator suggest or at session startup, it:
- Loads session observations from
sessions.jsonl - Runs the
CoActivationTrackerto find significant skill pairs - Runs the
ClusterDetectorto find skill clusters - Compares against existing suggestions (avoids duplicates)
- Creates pending suggestions for new clusters
- Saves everything to
.planning/patterns/agent-suggestions.json
Suggestions have four possible states:
| State | Meaning |
|---|---|
pending | Newly detected, awaiting your decision |
accepted | You approved it; agent file was generated |
deferred | You chose to revisit later |
dismissed | You rejected it (with optional reason) |
Agents are never auto-generated. You must explicitly accept a suggestion before any file is created. This is a core safety principle of the bounded learning system.
Step 5 — Agent File Generation
When you accept a suggestion, the AgentGenerator produces a Markdown file in .claude/agents/. The generation process includes:
- Name sanitization — lowercase, alphanumeric + hyphens only, max 64 characters
- Path safety validation — the resolved path must stay within the agents directory (prevents path traversal)
- Tool correction — tool names are validated and auto-corrected for casing (e.g.,
"read"→"Read") using Levenshtein distance matching - Frontmatter validation — Zod schema checks all fields before writing
- Skill description loading — each referenced skill's description is fetched from the skill store
- Duplicate check — refuses to overwrite an existing agent file
The generated file lands at .claude/agents/<sanitized-name>.md and is automatically discovered by Claude Code at the next session startup.
The Composition Workflow: 5+ Co-Activations Over 7+ Days
The agent composition guardrails require both frequency and stability before a suggestion is generated. This is intentional — it prevents agents from being created based on a single productive session that may not represent a lasting pattern.
The Two Requirements
Frequency: 5+ co-activations
Each pair of skills in the cluster must have been loaded together in at least 5 separate sessions. This is the minCoActivations threshold on the ClusterDetector — it filters graph edges before BFS runs.
Stability: 7+ days
The pattern must persist for at least 7 days (the stabilityDays config). The cluster's stability is measured as the number of days between the earliest firstSeen timestamp across all edges and the current date.
In practice, this means:
- A skill pair activated 10 times in a single day will not trigger a suggestion (stability too low)
- A skill pair activated once a day for 14 days will not trigger a suggestion (frequency too low, only reaching 2 within the default 14-day window needs at least 5)
- A pair activated 5+ times across sessions spanning 7+ days will reach the threshold
The cluster detector uses a graph-based approach: skill pairs are edges, skills are nodes, and connected components are candidate agents. The algorithm deliberately caps cluster size at 5 skills (maxClusterSize) to prevent “mega-agents” that try to do everything. If you have 8 skills that frequently co-activate, the system will identify the densest 2–5 node sub-clusters rather than creating one monolithic agent.
The co-activation score is normalized as avgCount / 10 (capped at 1.0), meaning a cluster where every pair co-activated 10+ times gets a perfect score. Clusters are returned sorted by this score, highest first.
Model Selection Guide
Each agent specifies a model in its frontmatter. This controls which Claude model processes the agent's work when it is delegated a task.
| Model | When to Use | Tradeoff |
|---|---|---|
inherit |
Default. Use the same model as the parent session. Best for most agents where you want consistent behavior with your main workflow. | No cost or speed change; whatever the user picked. |
opus |
Complex reasoning tasks: architectural decisions, multi-file refactors, code review agents that need deep understanding of trade-offs. | Highest capability but slowest and most expensive. |
sonnet |
Balanced workloads: code generation, testing agents, documentation agents. Good when you need both speed and quality. | Strong performance at moderate cost and speed. |
haiku |
Fast, simple tasks: file renaming, boilerplate generation, running repetitive linting passes, simple search-and-report agents. | Fastest and cheapest but less capable on nuanced tasks. |
The default config uses inherit:
export const DEFAULT_AGENT_GENERATOR_CONFIG: AgentGeneratorConfig = {
agentsDir: '.claude/agents',
model: 'inherit',
tools: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'],
};
Tool Restrictions: What Each Tool Does and When to Restrict
Agents receive an explicit list of tools they are allowed to use. This is a security boundary — an agent cannot access tools not in its tools field. You can also use the disallowedTools field to subtract tools from the inherited set.
| Tool | Purpose | When to Restrict |
|---|---|---|
Read | Read files from the filesystem | Almost never — most agents need to read files |
Write | Create or overwrite entire files | Restrict for read-only analysis or audit agents |
Edit | Make targeted string replacements in files | Restrict for reporting-only agents |
Bash | Execute shell commands | Restrict when the agent should not run commands (e.g., documentation agents) |
Glob | Find files by name pattern | Rarely restricted; safe, read-only operation |
Grep | Search file contents with regex | Rarely restricted; safe, read-only operation |
WebFetch | Fetch and analyze web content | Restrict when agents should only work with local files |
WebSearch | Search the web | Restrict for agents that must not access external information |
Task | Spawn sub-agents for parallel work | Restrict when agent should not delegate (prevents unbounded recursion) |
NotebookEdit | Edit Jupyter notebook cells | Only include for data science or notebook-focused agents |
mcp__*__* | MCP (Model Context Protocol) server tools | Include only specific MCP tools needed; accepted without validation |
Tool names are validated with case-insensitive matching and Levenshtein-distance fuzzy correction. If you type "bash", it is corrected to "Bash". If you type "Bah", it suggests "Bash". MCP tools (matching mcp__[server]__[tool]) are accepted without validation.
Agent vs. Skill: When to Use Which
Use a Skill When…
- The knowledge is a single, focused concern
- It applies to many different tasks
- It does not need its own tool restrictions
- It can be loaded into any context as supplementary knowledge
- It needs to be inherited by other skills via
extends
Use an Agent When…
- Multiple skills are always used together (the 5+/7+ pattern)
- The combined task is delegatable — Claude can hand it off entirely
- You want specific tool restrictions for that workflow
- You want a different model for that workload (e.g.,
haikufor lint passes) - The work benefits from isolated context (no cross-contamination with the main session)
| Dimension | Skill | Agent |
|---|---|---|
| Location | .claude/commands/*.md | .claude/agents/*.md |
| Scope | Loaded into main context | Runs in isolated sub-context |
| Tool control | Inherits parent's tools | Explicit tools / disallowedTools fields |
| Model | Uses parent's model | Can specify inherit, opus, sonnet, or haiku |
| Composition | Can extend other skills (extends:) | Bundles multiple skills via skills: array |
| Generation | Created from observed corrections or patterns | Created from skill co-activation clusters |
How Agents Work with GSD Phases
GSD commands like /gsd:execute-phase and /gsd:verify-work fork sub-agent contexts to run plans with fresh context windows. Agents integrate into this lifecycle at two key points:
Skill Loading Before Phase Execution
Before any GSD phase runs, the skill loading pipeline applies:
- Score — rank all available skills by relevance to the current phase
- Resolve — merge inherited skill content (parent + child via
SkillResolver) - ModelFilter — filter skills incompatible with the target model
- CacheOrder — optimize loading order for context caching
- Budget — enforce the 2–5% token budget cap
- Load — inject selected skills into the phase context
When an agent is delegated work during a phase, the skills listed in its skills: frontmatter array are preloaded into the agent's context. The SkillResolver handles inheritance chains, merging parent content before child content with a separator:
// SkillResolver merges inherited skill content
private mergeContent(parent: string, child: string): string {
return `${parent.trim()}\n\n---\n\n${child.trim()}`;
}
Context Forking: Clean Context, Not Empty Context
When GSD forks a sub-agent context for execute-phase or verify-work, “clean context” means free of stale conversation history, not free of learned knowledge. Agents that run without their skills loaded are discarding everything the skill-creator system has captured.
The practical flow is:
- GSD command identifies the appropriate agent for the phase
- Skills from the agent's
skills:array are resolved and loaded - The agent runs in its own context with its specified tools and model
- Results return to the parent session
- Post-phase hooks check for skill-triggered actions (dashboard regeneration, state updates, etc.)
Agent files live in two possible locations:
.claude/agents/*.md— Project-level (recommended). Discovered automatically at session startup. Commit these to version control for team sharing.~/.claude/agents/*.md— User-level. Personal agents that apply across all projects. Note: there is a known discovery bug (GitHub #11205) at this location.
Agent suggestions are stored in .planning/patterns/agent-suggestions.json. This file tracks the full suggestion lifecycle. Add .planning/patterns/ to .gitignore if the repository is shared — these contain session-specific observation data.
The AgentGenerator enforces path safety with two layers: validateSafeName() rejects names containing traversal sequences, and assertSafePath() verifies the resolved absolute path stays within the agents directory. Both throw PathTraversalError on violation.
Permission modes (default, acceptEdits, dontAsk, bypassPermissions, plan) control how much autonomy an agent has. Use plan for read-only agents, default for most cases, and approach bypassPermissions with extreme caution.
Note: For the complete official Claude Code agent format specification, see the OFFICIAL-FORMAT documentation.
Generated agents follow Claude Code's .claude/agents/ format:
---
name: react-fullstack-agent
description: Combines expertise from: react-hooks, react-components, api-patterns.
tools: Read, Write, Edit, Bash, Glob, Grep
model: inherit
skills:
- react-hooks
- react-components
- api-patterns
---
Agent Frontmatter
| Field | Type | Description |
|---|---|---|
name |
string | Agent identifier (required) |
description |
string | What the agent does (required) |
tools |
string | Comma-separated string of allowed tools |
model |
string | Model to use (inherit, sonnet, opus, haiku) |
Troubleshooting: Safety Validation and Debug Logging
When agent or skill generation fails, the safety validation and audit logging systems provide diagnostic information. Below are the real implementations.
Dangerous Command Detection: The Deny List
From src/validation/generation-safety.ts — the deny list prevents generated skills from containing destructive bash commands:
export const DANGEROUS_COMMANDS: DangerousCommandPattern[] = [
{
name: 'recursive-delete',
pattern: /rm\s+(?:-[a-zA-Z]*)?r[a-zA-Z]*\s+(?:-[a-zA-Z]*\s+)?[\/~*]/,
description: 'Recursive delete of root, home, or wildcard paths',
},
{
name: 'piped-download',
pattern: /(?:curl|wget)\s+[^|]*\|\s*(?:ba)?sh/,
description: 'Download piped directly to shell execution',
},
{
name: 'sudo-usage',
pattern: /\bsudo\s+/,
description: 'Elevated privilege command execution',
},
{
name: 'credential-manipulation',
pattern: /chmod\s+777|\.ssh\/|\/etc\/passwd|\/etc\/shadow/,
description: 'Modification of credentials, SSH keys, or system auth files',
},
{
name: 'fork-bomb',
pattern: /:\(\)\s*\{.*\|.*&\s*\}\s*;/,
description: 'Fork bomb that exhausts system resources',
},
];
Content Scanning: Line-by-Line Safety Validation
From src/validation/generation-safety.ts — scans every line of generated content against the deny list and reports findings:
export function scanForDangerousCommands(content: string): DangerousFinding[] {
const findings: DangerousFinding[] = [];
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
for (const { pattern, name, description } of DANGEROUS_COMMANDS) {
pattern.lastIndex = 0;
const match = pattern.exec(line);
if (match) {
findings.push({
name,
match: match[0],
line: i + 1,
description,
});
}
}
}
return findings;
}
Audit Logger: Append-Only JSONL Mutation Tracking
From src/safety/audit-logger.ts — every skill/agent mutation is recorded with timestamps and source identification for forensic debugging:
export class AuditLogger {
private readonly logPath: string;
private writeQueue: Promise<void> = Promise.resolve();
constructor(logPath: string = '.claude/.audit-log.jsonl') {
this.logPath = logPath;
}
async log(entry: Omit<AuditEntry, 'timestamp'>): Promise<void> {
const fullEntry = {
...entry,
timestamp: new Date().toISOString(),
} as AuditEntry;
await this.appendLine(fullEntry);
}
async getEntries(options?: GetEntriesOptions): Promise<AuditEntry[]> {
let content: string;
try {
content = await readFile(this.logPath, 'utf-8');
} catch (err) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];
throw err;
}
const entries: AuditEntry[] = [];
for (const line of content.split('\n')) {
if (!line.trim()) continue;
const result = AuditEntrySchema.safeParse(JSON.parse(line));
if (result.success) {
if (options?.since && result.data.timestamp < options.since) continue;
if (options?.operation && result.data.operation !== options.operation) continue;
entries.push(result.data);
}
}
return entries;
}
}
Audit Entry Schema: Zod-Validated Log Format
From src/safety/audit-logger.ts — each audit entry is validated via Zod with .passthrough() for forward compatibility:
export const AuditOperationSchema = z.enum([
'create',
'update',
'delete',
'migrate',
'refine',
'rollback',
]);
export const AuditEntrySchema = z.object({
timestamp: z.string(),
operation: AuditOperationSchema,
filePath: z.string(),
source: z.string(),
details: z.record(z.string(), z.unknown()).optional(),
}).passthrough();
Content Sanitization: Blocking Dangerous Commands in Generated Output
From src/validation/generation-safety.ts — when dangerous commands are found, they are replaced with HTML comment warnings:
export function sanitizeGeneratedContent(content: string): SanitizeResult {
const findings = scanForDangerousCommands(content);
if (findings.length === 0) {
return { sanitized: content, findings: [], scriptsExtracted: 0 };
}
const blockedLines = new Map<number, DangerousFinding>();
for (const finding of findings) {
if (!blockedLines.has(finding.line)) {
blockedLines.set(finding.line, finding);
}
}
const lines = content.split('\n');
const sanitizedLines: string[] = [];
for (let i = 0; i < lines.length; i++) {
if (blockedLines.has(i + 1)) {
const finding = blockedLines.get(i + 1)!;
sanitizedLines.push(
`<!-- BLOCKED: ${finding.name} - ${finding.description} -->`
);
} else {
sanitizedLines.push(lines[i]);
}
}
return {
sanitized: sanitizedLines.join('\n'),
findings,
scriptsExtracted,
};
}
Known Issues
User-Level Agent Discovery (GitHub #11205)
There is a known bug where agents in ~/.claude/agents/ may not be automatically discovered by Claude Code at session startup.
Workarounds:
- Use project-level agents (
.claude/agents/) instead - Use the
/agentsUI command within Claude Code - Pass agents via
--agentsCLI flag when starting Claude Code

