Picks opus, sonnet or haiku for each built-in subagent from keywords in its task.

A Claude Code mod that picks opus, sonnet or haiku for each built-in subagent (general-purpose, Explore, Plan) from keywords in its description and prompt. An explicit model and custom agent types are left alone.
Plan agent.Explore agent.The keyword lists are in hooks/register.ts.
hooks/register.ts 45 lines1import type { Register } from 'claude-code'
2
3type Tier = 'opus' | 'sonnet' | 'haiku'
4
5// Agent types the router decides for; custom types (plugin agents) keep their own model.
6const ROUTED = new Set(['general-purpose', 'Explore', 'Plan'])
7
8// A prompt this long carries a real task: never send it to haiku.
9const HAIKU_MAX_CHARS = 1200
10
11const OPUS = [
12 'architecture', 'arquitetura', 'design decision', 'root cause', 'causa raiz', 'race condition',
13 'concurrency', 'concorrência', 'concorrencia', 'deadlock', 'security', 'segurança', 'seguranca',
14 'vulnerab', 'audit', 'auditoria', 'threat model', 'contract', 'contrato', 'prove', 'proof',
15 'demonstre', 'matemática', 'matematica', 'algorithm', 'algoritmo', 'complexity', 'complexidade',
16 'migration plan', 'plano de migração', 'plano de migracao', 'trade-off', 'tradeoff', 'redesign',
17 'unclear', 'intermittent', 'intermitente', 'critically', 'criticamente',
18]
19
20const HAIKU = [
21 'classify', 'classifique', 'categorize', 'categorise', 'categoriza', 'summarize', 'summarise',
22 'resuma', 'sumariz', 'list the files', 'list files', 'liste os arquivos', 'liste arquivos',
23 'find files', 'find where', 'locate', 'localize', 'onde está', 'grep for', 'count the', 'conte os',
24 'extract the', 'extraia', 'add tags', 'rename the', 'renomeie', 'translate',
25 'traduza', 'what does', 'o que faz',
26]
27
28const has = (text: string, words: string[]) => words.some(w => text.includes(w))
29
30export const pickModel = (subagentType: string, description: string, prompt: string): Tier => {
31 const text = `${description}\n${prompt}`.toLowerCase()
32 if (has(text, OPUS)) return 'opus'
33 if (subagentType === 'Plan') return 'opus'
34 if (subagentType === 'Explore') return 'haiku'
35 if (prompt.length <= HAIKU_MAX_CHARS && has(text, HAIKU)) return 'haiku'
36 return 'sonnet'
37}
38
39export const register: Register = on => {
40 on('agent.spawn', ($, e, next) => {
41 if (e.model !== undefined || !ROUTED.has(e.subagentType)) return next(e)
42 return next({ ...e, model: pickModel(e.subagentType, e.description, e.prompt) })
43 }).catch(($, e, next) => next(e))
44}
45