Agent Skill
2/7/2026

create-specialized-subagent

Create specialized subagents within the subagents extension. Use when asked to create a new subagent like scout, librarian, oracle, etc.

A
aliou
37GitHub Stars
1Views
npx skills add aliou/pi-extensions

SKILL.md

Namecreate-specialized-subagent
DescriptionCreate specialized subagents within the subagents extension. Use when asked to create a new subagent like scout, librarian, oracle, etc.

name: create-specialized-subagent description: Create specialized subagents within the subagents extension. Use when asked to create a new subagent like scout, librarian, oracle, etc.

Create Specialized Subagent

Create subagents within extensions/subagents/subagents/.

Subagents are autonomous agents with their own tools, system prompt, and model. They run as Pi tools, streaming progress and rendering results.

Directory Structure

extensions/subagents/subagents/<name>/
├── index.ts          # Main tool definition (createXxxTool, executeXxx, XXX_GUIDANCE)
├── config.ts         # Model config, provider constants
├── system-prompt.ts  # System prompt string
├── types.ts          # XxxInput, XxxDetails interfaces
├── tool-formatter.ts # formatXxxToolCall() for display
└── tools/
    ├── index.ts      # createXxxTools() aggregator
    └── <tool>.ts     # Individual tool definitions

Files Overview

types.ts

  • XxxInput: Parameters the parent agent provides
    • Include skills?: string[] for optional skill passthrough
  • XxxDetails: State for UI rendering, must include:
    • toolCalls: SubagentToolCall[]
    • spinnerFrame: number
    • response?: string
    • aborted?: boolean
    • error?: string
    • usage?: SubagentUsage
    • skills?: string[] - Requested skill names (from input)
    • skillsResolved?: number - Number of skills successfully resolved
    • skillsNotFound?: string[] - Skill names that were not found

config.ts

  • MODEL: Provider and model ID
  • Optional provider type aliases for external APIs

system-prompt.ts

  • Role definition
  • Available tools list
  • Behavior rules per input combination
  • Response format guidelines
  • Constraints and guardrails

tool-formatter.ts

  • formatXxxToolCall(tc): Returns { label, detail } for human-readable display
  • label: Action name ("Search", "Fetch")
  • detail: Context (hostname, query, repo path)

tools/

  • Individual tool definitions
  • Must return cost in details for cost aggregation:
    return {
      content: [...],
      details: { ..., cost: response.costDollars?.total },
    };
    

index.ts

Exports:

  • createXxxTool(): Tool definition with execute, renderCall, renderResult
  • executeXxx(): Direct execution without tool wrapper
  • XXX_GUIDANCE: Markdown guidance for parent agent's system prompt

Execute Function

  1. Resolve skills if provided:
    const { skills: skillNames } = args;
    let resolvedSkills: Skill[] = [];
    let notFoundSkills: string[] = [];
    if (skillNames && skillNames.length > 0) {
      const result = resolveSkillsByName(skillNames, ctx.cwd);
      resolvedSkills = result.skills;
      notFoundSkills = result.notFound;
    }
    
  2. Validate inputs, return error in details if invalid (include skill info)
  3. Set up spinner interval (80ms), clear in finally
  4. Build user message, append warning if skills not found:
    if (notFoundSkills.length > 0) {
      userMessage += `\n\n**Note:** The following skills were not found and could not be loaded: ${notFoundSkills.join(", ")}`;
    }
    
  5. Call executeSubagent() with:
    • skills: resolvedSkills in config
    • onTextUpdate and onToolUpdate callbacks that include skill info in details
  6. Handle abort/error states (include skill info in all return paths)
  7. Use final tool calls from result.toolCalls for failure checks
  8. Check if all tool calls failed → return error
  9. Return usage from result (include skill info)

Tool Rendering Guidelines (required)

Use shared UI abstractions from @aliou/pi-utils-ui. Do not hand-roll tool header/body/footer for new subagents.

renderCall pattern

Always use this header shape:

  • First line: [Tool Name]: [Action] [Main arg] [Option args]
  • Following lines: long args only (wrapped naturally)

In subagents, use ToolCallHeader:

return new ToolCallHeader(
  {
    toolName: "Scout", // display name, not snake_case tool id
    // action only when meaningful (e.g. process start/output/kill)
    mainArg: "short primary arg",
    optionArgs: [{ label: "cwd", value: args.cwd ?? "" }],
    longArgs: [{ label: "prompt", value: args.prompt }],
  },
  theme,
);

Rules:

  • Keep main arg short and useful.
  • Move long text (prompt/task/question/instructions/context) to longArgs.
  • Do not truncate when wrapping gives better readability (e.g. query/question).
  • Tool name should be human display text (Scout, Read Session), not raw tool id.

renderResult structure

Use ToolBody + footer component (SubagentFooter for model-backed subagents).

return new ToolBody({ fields, footer }, options, theme);

Footer spacing is standardized. Keep footer data concise and machine-skim-friendly.

renderResult States

StateDisplay
AbortedWarning "Aborted" + optional completion count
ErrorError message
Running + collapsedSpinner + current tool name
Running + expandedStatus line + all tool calls with indicators
Done + collapsed or (if all failed) + stats
Done + expandedStats + tool summary + failed tool details + markdown response

Registration

In extensions/subagents/index.ts:

  1. Import the tool and guidance
  2. Add guidance to SUBAGENT_GUIDANCES array
  3. Register tool with pi.registerTool(createXxxTool())
  4. If the subagent requires API keys, add them to checkApiKeys()

See the existing registration pattern in the file.

API Key Validation

If your subagent requires external API keys, validate them at extension load time. This prevents the extension from loading if required keys are missing.

Add your required keys to checkApiKeys() in extensions/subagents/index.ts. See the existing implementation for the pattern.

Checklist

  1. Create directory: subagents/<name>/
  2. Create types.ts with Input and Details interfaces
    • Add skills?: string[] to Input interface
    • Add skills?, skillsResolved?, skillsNotFound? to Details interface
  3. Create config.ts with model configuration
  4. Create system-prompt.ts with subagent instructions
  5. Create tools/ directory with subagent's tools
  6. Create tool-formatter.ts for display formatting
  7. Create index.ts with createXxxTool, executeXxx, XXX_GUIDANCE
    • Import resolveSkillsByName and Skill type from lib
    • Add skills parameter to TypeBox schema
    • Update tool description to mention skill support
    • Resolve skills in execute function
    • Pass skills: resolvedSkills to executeSubagent
    • Include skill info in all details returns
    • Use ToolCallHeader in renderCall and follow standard line pattern
    • Use ToolBody + SubagentFooter in renderResult
  8. Register in extensions/subagents/index.ts
  9. Run pnpm typecheck

Key Points

  • Details interface: Must include toolCalls, spinnerFrame, response, aborted, error, usage, and skill tracking fields
  • Skills support: All subagents should support optional skills parameter for specialized context
  • Cost tracking: Tools must return cost in details for aggregation
  • Spinner: 80ms interval, clear in finally block
  • Extensions disabled: Subagents don't run user extensions (extensions: [])
  • Failed tools: Show individually in done+expanded state
  • All failed: Show error indicator only when ALL tools failed (partial success = success indicator)
  • Final tool calls: Use result.toolCalls if present to decide failure state
  • Mark tool as failed: When all internal tools fail, return error so parent agent sees failure
  • Skill resolution: Use resolveSkillsByName() from lib to convert skill names to Skill objects
  • Skill warnings: Append warning to user message if skills not found (don't fail the request)

Notifications

Subagents can emit notifications to alert users. This is useful for long-running subagent tasks or when user attention is needed.

Emitting Notifications

const NOTIFICATION_EVENT = "ad:notification";

interface NotificationEvent {
  message: string;
  sound?: string;
}

// In execute function, after completion
pi.events.emit(NOTIFICATION_EVENT, {
  message: "Research completed",
  sound: "/System/Library/Sounds/Blow.aiff",
});

When to Notify

  • Subagent completes a long-running task
  • Subagent encounters errors that need user attention
  • Subagent needs user input (though prefer interactive tools like ask_user)

Reference

Refer to the scout subagent for complete implementation:

Skills Info
Original Name:create-specialized-subagentAuthor:aliou