PreToolUse + Task tool: deterministic subagent model routing works today — SubagentStart updatedInput still needed for stability

Status Open
Maintainer reply None cached
Activity 0 comments · opened Sep 21, 2026

Context

This issue follows up on #69545 (closed as stale). The problem is real and partially solved, but a proper fix from the CC side would make it stable.

What works today

PreToolUse with matcher: "Task" fires for subagent spawns and honors updatedInput — the subagent starts with the rewritten model. The previous issue tested matcher: "Agent" (which does not fire); "Task" does.

Minimal hook:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Task", "hooks": [{ "type": "command", "command": "downshift claude-code" }] }
    ]
  }
}

The hook reads tool_input.prompt, classifies complexity deterministically (no LLM call), and returns updatedInput with the right model. This is working in practice across Claude Code, Cursor, and Codex.

I built and open-sourced a binary that implements this: harness-downshift (https://github.com/tiagovilasboas/harness-downshift) — single Go binary, version-agnostic model matching, effort routing, zero deps.

Why a CC-side fix is still needed

The current workaround has one fragility: if CC renames the tool from Task to Agent (or any other name) in a future version, the hook silently stops firing and routing breaks without any error. Users have no way to detect this.

Requested fix (same as #69545 Option A)

Make SubagentStart support updatedInput.model and expose the requested model + task description in the hook payload:

{
  "session_id": "...",
  "agent_id": "...",
  "agent_type": "general-purpose",
  "model": "claude-sonnet-4-6",
  "task_description": "...",
  "hook_event_name": "SubagentStart"
}

This would make model routing an officially supported, stable hook surface — independent of tool name changes.

Environment

— Tiago de Carvalho Vilas Boas, author of harness-downshift

View original on GitHub ↗