2026-02-08 13:34:47 +09:00
import type { AgentPromptMetadata } from "./types"
2025-12-30 23:56:09 +09:00
export interface AvailableAgent {
2026-02-08 13:34:47 +09:00
name : string
2025-12-30 23:56:09 +09:00
description : string
metadata : AgentPromptMetadata
}
export interface AvailableTool {
name : string
category : "lsp" | "ast" | "search" | "session" | "command" | "other"
}
2026-01-01 15:37:24 +09:00
export interface AvailableSkill {
name : string
description : string
location : "user" | "project" | "plugin"
}
2026-01-20 16:53:46 +09:00
export interface AvailableCategory {
name : string
description : string
2026-02-06 17:31:13 +09:00
model? : string
2026-01-20 16:53:46 +09:00
}
2025-12-30 23:56:09 +09:00
export function categorizeTools ( toolNames : string [ ] ) : AvailableTool [ ] {
return toolNames . map ( ( name ) = > {
let category : AvailableTool [ "category" ] = "other"
if ( name . startsWith ( "lsp_" ) ) {
category = "lsp"
} else if ( name . startsWith ( "ast_grep" ) ) {
category = "ast"
} else if ( name === "grep" || name === "glob" ) {
category = "search"
} else if ( name . startsWith ( "session_" ) ) {
category = "session"
2026-02-18 18:40:10 +08:00
} else if ( name === "skill" ) {
2025-12-30 23:56:09 +09:00
category = "command"
}
return { name , category }
} )
}
function formatToolsForPrompt ( tools : AvailableTool [ ] ) : string {
const lspTools = tools . filter ( ( t ) = > t . category === "lsp" )
const astTools = tools . filter ( ( t ) = > t . category === "ast" )
const searchTools = tools . filter ( ( t ) = > t . category === "search" )
const parts : string [ ] = [ ]
if ( searchTools . length > 0 ) {
parts . push ( . . . searchTools . map ( ( t ) = > ` \` ${ t . name } \` ` ) )
}
if ( lspTools . length > 0 ) {
parts . push ( "`lsp_*`" )
}
if ( astTools . length > 0 ) {
parts . push ( "`ast_grep`" )
}
return parts . join ( ", " )
}
2026-01-22 22:45:41 +09:00
export function buildKeyTriggersSection ( agents : AvailableAgent [ ] , _skills : AvailableSkill [ ] = [ ] ) : string {
2025-12-30 23:56:09 +09:00
const keyTriggers = agents
. filter ( ( a ) = > a . metadata . keyTrigger )
. map ( ( a ) = > ` - ${ a . metadata . keyTrigger } ` )
2026-01-22 22:45:41 +09:00
if ( keyTriggers . length === 0 ) return ""
2025-12-30 23:56:09 +09:00
return ` ### Key Triggers (check BEFORE classification):
2026-01-01 15:37:24 +09:00
2026-01-22 22:45:41 +09:00
${ keyTriggers . join ( "\n" ) }
2025-12-30 23:56:09 +09:00
- **"Look into" + "create PR"** → Not just research. Full implementation cycle expected. `
}
2026-01-01 15:37:24 +09:00
export function buildToolSelectionTable (
agents : AvailableAgent [ ] ,
tools : AvailableTool [ ] = [ ] ,
2026-01-22 22:45:41 +09:00
_skills : AvailableSkill [ ] = [ ]
2026-01-01 15:37:24 +09:00
) : string {
2025-12-30 23:56:09 +09:00
const rows : string [ ] = [
2026-01-22 22:45:41 +09:00
"### Tool & Agent Selection:" ,
2025-12-30 23:56:09 +09:00
"" ,
]
if ( tools . length > 0 ) {
const toolsDisplay = formatToolsForPrompt ( tools )
2026-02-17 13:26:37 +09:00
rows . push ( ` - ${ toolsDisplay } — **FREE** — Not Complex, Scope Clear, No Implicit Assumptions ` )
2025-12-30 23:56:09 +09:00
}
const costOrder = { FREE : 0 , CHEAP : 1 , EXPENSIVE : 2 }
const sortedAgents = [ . . . agents ]
. filter ( ( a ) = > a . metadata . category !== "utility" )
. sort ( ( a , b ) = > costOrder [ a . metadata . cost ] - costOrder [ b . metadata . cost ] )
for ( const agent of sortedAgents ) {
const shortDesc = agent . description . split ( "." ) [ 0 ] || agent . description
2026-02-17 13:26:37 +09:00
rows . push ( ` - \` ${ agent . name } \` agent — ** ${ agent . metadata . cost } ** — ${ shortDesc } ` )
2025-12-30 23:56:09 +09:00
}
rows . push ( "" )
2026-01-22 22:45:41 +09:00
rows . push ( "**Default flow**: explore/librarian (background) + tools → oracle (if required)" )
2025-12-30 23:56:09 +09:00
return rows . join ( "\n" )
}
export function buildExploreSection ( agents : AvailableAgent [ ] ) : string {
const exploreAgent = agents . find ( ( a ) = > a . name === "explore" )
if ( ! exploreAgent ) return ""
const useWhen = exploreAgent . metadata . useWhen || [ ]
const avoidWhen = exploreAgent . metadata . avoidWhen || [ ]
return ` ### Explore Agent = Contextual Grep
Use it as a **peer tool**, not a fallback. Fire liberally.
2026-02-17 13:26:37 +09:00
**Use Direct Tools when:**
${ avoidWhen . map ( ( w ) = > ` - ${ w } ` ) . join ( "\n" ) }
**Use Explore Agent when:**
${ useWhen . map ( ( w ) = > ` - ${ w } ` ) . join ( "\n" ) } `
2025-12-30 23:56:09 +09:00
}
export function buildLibrarianSection ( agents : AvailableAgent [ ] ) : string {
const librarianAgent = agents . find ( ( a ) = > a . name === "librarian" )
if ( ! librarianAgent ) return ""
const useWhen = librarianAgent . metadata . useWhen || [ ]
return ` ### Librarian Agent = Reference Grep
Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved.
2026-02-17 13:26:37 +09:00
**Contextual Grep (Internal)** — search OUR codebase, find patterns in THIS repo, project-specific logic.
**Reference Grep (External)** — search EXTERNAL resources, official API docs, library best practices, OSS implementation examples.
2025-12-30 23:56:09 +09:00
**Trigger phrases** (fire librarian immediately):
${ useWhen . map ( ( w ) = > ` - " ${ w } " ` ) . join ( "\n" ) } `
}
export function buildDelegationTable ( agents : AvailableAgent [ ] ) : string {
const rows : string [ ] = [
"### Delegation Table:" ,
"" ,
]
for ( const agent of agents ) {
for ( const trigger of agent . metadata . triggers ) {
2026-02-17 13:26:37 +09:00
rows . push ( ` - ** ${ trigger . domain } ** → \` ${ agent . name } \` — ${ trigger . trigger } ` )
2025-12-30 23:56:09 +09:00
}
}
return rows . join ( "\n" )
}
2026-02-04 17:47:08 +01:00
2026-01-20 16:53:46 +09:00
export function buildCategorySkillsDelegationGuide ( categories : AvailableCategory [ ] , skills : AvailableSkill [ ] ) : string {
if ( categories . length === 0 && skills . length === 0 ) return ""
const categoryRows = categories . map ( ( c ) = > {
const desc = c . description || c . name
2026-02-17 13:26:37 +09:00
return ` - \` ${ c . name } \` — ${ desc } `
2026-01-20 16:53:46 +09:00
} )
2026-02-04 17:27:32 +01:00
const builtinSkills = skills . filter ( ( s ) = > s . location === "plugin" )
const customSkills = skills . filter ( ( s ) = > s . location !== "plugin" )
2026-02-19 17:41:29 +09:00
const builtinNames = builtinSkills . map ( ( s ) = > s . name ) . join ( ", " )
const customNames = customSkills . map ( ( s ) = > {
const source = s . location === "project" ? "project" : "user"
return ` ${ s . name } ( ${ source } ) `
} ) . join ( ", " )
2026-02-04 17:27:32 +01:00
let skillsSection : string
if ( customSkills . length > 0 && builtinSkills . length > 0 ) {
2026-02-19 17:41:29 +09:00
skillsSection = ` #### Available Skills (via \` skill \` tool)
2026-02-04 17:27:32 +01:00
2026-02-19 17:41:29 +09:00
**Built-in**: ${ builtinNames }
**⚡ YOUR SKILLS (PRIORITY)**: ${ customNames }
2026-02-04 17:27:32 +01:00
2026-02-19 17:41:29 +09:00
> User-installed skills OVERRIDE built-in defaults. ALWAYS prefer YOUR SKILLS when domain matches.
> Full skill descriptions → use the \` skill \` tool to check before EVERY delegation. `
2026-02-04 17:27:32 +01:00
} else if ( customSkills . length > 0 ) {
2026-02-19 17:41:29 +09:00
skillsSection = ` #### Available Skills (via \` skill \` tool)
2026-02-04 17:27:32 +01:00
2026-02-19 17:41:29 +09:00
**⚡ YOUR SKILLS (PRIORITY)**: ${ customNames }
2026-02-04 17:27:32 +01:00
2026-02-19 17:41:29 +09:00
> User-installed skills OVERRIDE built-in defaults. ALWAYS prefer YOUR SKILLS when domain matches.
> Full skill descriptions → use the \` skill \` tool to check before EVERY delegation. `
} else if ( builtinSkills . length > 0 ) {
skillsSection = ` #### Available Skills (via \` skill \` tool)
**Built-in**: ${ builtinNames }
> Full skill descriptions → use the \` skill \` tool to check before EVERY delegation. `
} else {
skillsSection = ""
2026-02-04 17:27:32 +01:00
}
2026-01-20 16:53:46 +09:00
return ` ### Category + Skills Delegation System
2026-02-06 16:01:54 +09:00
**task() combines categories and skills for optimal task execution.**
2026-01-20 16:53:46 +09:00
#### Available Categories (Domain-Optimized Models)
2025-12-30 23:56:09 +09:00
2026-01-20 16:53:46 +09:00
Each category is configured with a model optimized for that domain. Read the description to understand when to use it.
2025-12-30 23:56:09 +09:00
2026-01-20 16:53:46 +09:00
${ categoryRows . join ( "\n" ) }
2025-12-30 23:56:09 +09:00
2026-02-04 17:27:32 +01:00
${ skillsSection }
2025-12-30 23:56:09 +09:00
2026-01-20 16:53:46 +09:00
---
### MANDATORY: Category + Skill Selection Protocol
**STEP 1: Select Category**
- Read each category's description
- Match task requirements to category domain
- Select the category whose domain BEST fits the task
2026-02-19 17:41:29 +09:00
**STEP 2: Evaluate ALL Skills**
Check the \` skill \` tool for available skills and their descriptions. For EVERY skill, ask:
2026-01-20 16:53:46 +09:00
> "Does this skill's expertise domain overlap with my task?"
2026-01-22 22:45:41 +09:00
- If YES → INCLUDE in \` load_skills=[...] \`
2026-02-19 17:41:29 +09:00
- If NO → OMIT (no justification needed)
2026-02-04 17:27:32 +01:00
${ customSkills . length > 0 ? `
2026-02-19 17:41:29 +09:00
> **User-installed skills get PRIORITY.** When in doubt, INCLUDE rather than omit. ` : "" }
2026-01-15 16:32:14 +09:00
2026-01-20 16:53:46 +09:00
---
2026-01-15 16:32:14 +09:00
2026-01-20 16:53:46 +09:00
### Delegation Pattern
2026-01-15 16:32:14 +09:00
2026-01-20 16:53:46 +09:00
\` \` \` typescript
2026-02-06 16:01:54 +09:00
task(
2026-01-20 16:53:46 +09:00
category="[selected-category]",
2026-02-04 17:27:32 +01:00
load_skills=["skill-1", "skill-2"], // Include ALL relevant skills — ESPECIALLY user-installed ones
2026-01-20 16:53:46 +09:00
prompt="..."
)
\` \` \`
2026-01-15 16:32:14 +09:00
2026-01-20 16:53:46 +09:00
**ANTI-PATTERN (will produce poor results):**
\` \` \` typescript
2026-02-06 16:01:54 +09:00
task(category="...", load_skills=[], run_in_background=false, prompt="...") // Empty load_skills without justification
2026-01-20 16:53:46 +09:00
\` \` \` `
2025-12-30 23:56:09 +09:00
}
export function buildOracleSection ( agents : AvailableAgent [ ] ) : string {
const oracleAgent = agents . find ( ( a ) = > a . name === "oracle" )
if ( ! oracleAgent ) return ""
const useWhen = oracleAgent . metadata . useWhen || [ ]
const avoidWhen = oracleAgent . metadata . avoidWhen || [ ]
return ` <Oracle_Usage>
2026-01-09 02:24:43 +09:00
## Oracle — Read-Only High-IQ Consultant
2025-12-30 23:56:09 +09:00
2026-01-09 02:24:43 +09:00
Oracle is a read-only, expensive, high-quality reasoning model for debugging and architecture. Consultation only.
2025-12-30 23:56:09 +09:00
2026-02-17 13:26:37 +09:00
### WHEN to Consult (Oracle FIRST, then implement):
2025-12-30 23:56:09 +09:00
2026-02-17 13:26:37 +09:00
${ useWhen . map ( ( w ) = > ` - ${ w } ` ) . join ( "\n" ) }
2025-12-30 23:56:09 +09:00
### WHEN NOT to Consult:
${ avoidWhen . map ( ( w ) = > ` - ${ w } ` ) . join ( "\n" ) }
### Usage Pattern:
Briefly announce "Consulting Oracle for [reason]" before invocation.
**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
2026-02-17 03:53:02 +09:00
### Oracle Background Task Policy:
2026-02-17 13:26:37 +09:00
**You MUST collect Oracle results before your final answer. No exceptions.**
- Oracle may take several minutes. This is normal and expected.
- When Oracle is running and you finish your own exploration/analysis, your next action is \` background_output(task_id="...") \` on Oracle — NOT delivering a final answer.
- Oracle catches blind spots you cannot see — its value is HIGHEST when you think you don't need it.
- **NEVER** cancel Oracle. **NEVER** use \` background_cancel(all=true) \` when Oracle is running. Cancel disposable tasks (explore, librarian) individually by taskId instead.
2025-12-30 23:56:09 +09:00
</Oracle_Usage> `
}
2026-01-20 16:53:46 +09:00
export function buildHardBlocksSection ( ) : string {
2025-12-30 23:56:09 +09:00
const blocks = [
2026-02-17 13:26:37 +09:00
"- Type error suppression (`as any`, `@ts-ignore`) — **Never**" ,
"- Commit without explicit request — **Never**" ,
"- Speculate about unread code — **Never**" ,
"- Leave code in broken state after failures — **Never**" ,
"- `background_cancel(all=true)` when Oracle is running — **Never.** Cancel tasks individually by taskId." ,
"- Delivering final answer before collecting Oracle result — **Never.** Always `background_output` Oracle first." ,
2025-12-30 23:56:09 +09:00
]
return ` ## Hard Blocks (NEVER violate)
${ blocks . join ( "\n" ) } `
}
2026-01-20 16:53:46 +09:00
export function buildAntiPatternsSection ( ) : string {
2025-12-30 23:56:09 +09:00
const patterns = [
2026-02-17 13:26:37 +09:00
"- **Type Safety**: `as any`, `@ts-ignore`, `@ts-expect-error`" ,
"- **Error Handling**: Empty catch blocks `catch(e) {}`" ,
"- **Testing**: Deleting failing tests to \"pass\"" ,
"- **Search**: Firing agents for single-line typos or obvious syntax errors" ,
"- **Debugging**: Shotgun debugging, random changes" ,
"- **Background Tasks**: `background_cancel(all=true)` — always cancel individually by taskId" ,
"- **Oracle**: Skipping Oracle results when Oracle was launched — ALWAYS collect via `background_output`" ,
2025-12-30 23:56:09 +09:00
]
return ` ## Anti-Patterns (BLOCKING violations)
${ patterns . join ( "\n" ) } `
}
2026-01-03 12:45:18 +09:00
2026-02-22 03:20:57 +09:00
export function buildDeepParallelSection ( model : string , categories : AvailableCategory [ ] ) : string {
const isNonClaude = ! model . toLowerCase ( ) . includes ( 'claude' )
const hasDeepCategory = categories . some ( c = > c . name === 'deep' )
if ( ! isNonClaude || ! hasDeepCategory ) return ""
return ` ### Deep Parallel Delegation
For implementation tasks, actively decompose and delegate to \` deep \` category agents in parallel.
1. Break the implementation into independent work units
2. Maximize parallel deep agents — spawn one per independent unit ( \` run_in_background=true \` )
3. Give each agent a GOAL, not step-by-step instructions — deep agents explore and solve autonomously
4. Collect results, integrate, verify coherence `
}
2026-01-20 16:53:46 +09:00
export function buildUltraworkSection (
agents : AvailableAgent [ ] ,
categories : AvailableCategory [ ] ,
skills : AvailableSkill [ ]
) : string {
2026-01-03 12:51:04 +09:00
const lines : string [ ] = [ ]
2026-01-20 16:53:46 +09:00
if ( categories . length > 0 ) {
lines . push ( "**Categories** (for implementation tasks):" )
for ( const cat of categories ) {
const shortDesc = cat . description || cat . name
lines . push ( ` - \` ${ cat . name } \` : ${ shortDesc } ` )
}
lines . push ( "" )
}
if ( skills . length > 0 ) {
2026-02-04 17:27:32 +01:00
const builtinSkills = skills . filter ( ( s ) = > s . location === "plugin" )
const customSkills = skills . filter ( ( s ) = > s . location !== "plugin" )
if ( builtinSkills . length > 0 ) {
lines . push ( "**Built-in Skills** (combine with categories):" )
for ( const skill of builtinSkills ) {
const shortDesc = skill . description . split ( "." ) [ 0 ] || skill . description
lines . push ( ` - \` ${ skill . name } \` : ${ shortDesc } ` )
}
lines . push ( "" )
}
if ( customSkills . length > 0 ) {
lines . push ( "**User-Installed Skills** (HIGH PRIORITY - user installed these for their workflow):" )
for ( const skill of customSkills ) {
const shortDesc = skill . description . split ( "." ) [ 0 ] || skill . description
lines . push ( ` - \` ${ skill . name } \` : ${ shortDesc } ` )
}
lines . push ( "" )
2026-01-20 16:53:46 +09:00
}
}
if ( agents . length > 0 ) {
const ultraworkAgentPriority = [ "explore" , "librarian" , "plan" , "oracle" ]
const sortedAgents = [ . . . agents ] . sort ( ( a , b ) = > {
const aIdx = ultraworkAgentPriority . indexOf ( a . name )
const bIdx = ultraworkAgentPriority . indexOf ( b . name )
if ( aIdx === - 1 && bIdx === - 1 ) return 0
if ( aIdx === - 1 ) return 1
if ( bIdx === - 1 ) return - 1
return aIdx - bIdx
} )
lines . push ( "**Agents** (for specialized consultation/exploration):" )
for ( const agent of sortedAgents ) {
2026-02-07 18:45:29 +09:00
const shortDesc = agent . description . length > 120 ? agent . description . slice ( 0 , 120 ) + "..." : agent . description
2026-01-20 16:53:46 +09:00
const suffix = agent . name === "explore" || agent . name === "librarian" ? " (multiple)" : ""
lines . push ( ` - \` ${ agent . name } ${ suffix } \` : ${ shortDesc } ` )
}
2026-01-03 12:45:18 +09:00
}
2026-01-03 12:51:04 +09:00
return lines . join ( "\n" )
2026-01-03 12:45:18 +09:00
}