Agent Generation

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:

  1. Observes which skills activate together across your sessions
  2. Detects clusters of skills that consistently co-activate
  3. Suggests agent compositions when patterns stabilize
  4. 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.

For Students — What Is an AI Agent?

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 session
  • startTime — epoch timestamp
  • activeSkills — 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 size2 skills
Max cluster size5 skills
Min co-activations5 per edge
Stability period7 days

All thresholds are configurable via ClusterConfig.

The algorithm:

  1. Filter co-activations to those with ≥ 5 occurrences (the edge threshold)
  2. Build an adjacency graph from the surviving skill pairs
  3. Run BFS to find connected components
  4. Discard components smaller than 2 or larger than 5 skills
  5. Score each cluster by average co-activation strength, normalized to 0–1
  6. 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:

  1. Loads session observations from sessions.jsonl
  2. Runs the CoActivationTracker to find significant skill pairs
  3. Runs the ClusterDetector to find skill clusters
  4. Compares against existing suggestions (avoids duplicates)
  5. Creates pending suggestions for new clusters
  6. Saves everything to .planning/patterns/agent-suggestions.json

Suggestions have four possible states:

State Meaning
pendingNewly detected, awaiting your decision
acceptedYou approved it; agent file was generated
deferredYou chose to revisit later
dismissedYou 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:

  1. Name sanitization — lowercase, alphanumeric + hyphens only, max 64 characters
  2. Path safety validation — the resolved path must stay within the agents directory (prevents path traversal)
  3. Tool correction — tool names are validated and auto-corrected for casing (e.g., "read" → "Read") using Levenshtein distance matching
  4. Frontmatter validation — Zod schema checks all fields before writing
  5. Skill description loading — each referenced skill's description is fetched from the skill store
  6. 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
For Professionals — Composition Patterns

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
ReadRead files from the filesystemAlmost never — most agents need to read files
WriteCreate or overwrite entire filesRestrict for read-only analysis or audit agents
EditMake targeted string replacements in filesRestrict for reporting-only agents
BashExecute shell commandsRestrict when the agent should not run commands (e.g., documentation agents)
GlobFind files by name patternRarely restricted; safe, read-only operation
GrepSearch file contents with regexRarely restricted; safe, read-only operation
WebFetchFetch and analyze web contentRestrict when agents should only work with local files
WebSearchSearch the webRestrict for agents that must not access external information
TaskSpawn sub-agents for parallel workRestrict when agent should not delegate (prevents unbounded recursion)
NotebookEditEdit Jupyter notebook cellsOnly include for data science or notebook-focused agents
mcp__*__*MCP (Model Context Protocol) server toolsInclude 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., haiku for 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
ScopeLoaded into main contextRuns in isolated sub-context
Tool controlInherits parent's toolsExplicit tools / disallowedTools fields
ModelUses parent's modelCan specify inherit, opus, sonnet, or haiku
CompositionCan extend other skills (extends:)Bundles multiple skills via skills: array
GenerationCreated from observed corrections or patternsCreated 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:

  1. Score — rank all available skills by relevance to the current phase
  2. Resolve — merge inherited skill content (parent + child via SkillResolver)
  3. ModelFilter — filter skills incompatible with the target model
  4. CacheOrder — optimize loading order for context caching
  5. Budget — enforce the 2–5% token budget cap
  6. 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:

  1. GSD command identifies the appropriate agent for the phase
  2. Skills from the agent's skills: array are resolved and loaded
  3. The agent runs in its own context with its specified tools and model
  4. Results return to the parent session
  5. Post-phase hooks check for skill-triggered actions (dashboard regeneration, state updates, etc.)
For Sysadmins — Agent File Management

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:

  1. Use project-level agents (.claude/agents/) instead
  2. Use the /agents UI command within Claude Code
  3. Pass agents via --agents CLI flag when starting Claude Code