By erans
Mid-session model routing for Claude Code. Set a sticky route tag that your proxy reads to direct requests to different LLMs.
npx claudepluginhub erans/lunaroute-cc-pluginLunaRoute is a Claude Code plugin for mid-session model routing. Users type #!sonnet (or any model name) to set a sticky route tag for the current session. Every subsequent API request — including requests made by subagents — carries a [LUNAROUTE:sonnet] marker injected into the conversation context. An external proxy reads this marker and routes the request to the appropriate LLM backend. Use #!clear to remove the tag and return to default routing.
Via plugin install:
claude plugin install github.com/<owner>/lunaroute-cc-plugin
Via ~/.claude/settings.json:
{
"extraKnownMarketplaces": {
"lunaroute-marketplace": {
"source": {
"source": "github",
"repo": "<owner>/lunaroute-cc-plugin"
}
}
}
}
| Command | Effect |
|---|---|
#!sonnet | Set route to sonnet |
#!gpt-4o | Set route to gpt-4o |
#!claude-3-haiku | Set route to claude-3-haiku |
#!clear | Clear the active route |
The tag can be standalone or inline with a message:
#!sonnet rewrite this function
Confirmation is shown after each command:
[Route set: sonnet]
[Route cleared]
Subagents automatically inherit the session's active route tag. To override routing for a specific subagent, include a #!model tag in the subagent's prompt directly.
#!sonnet → main session → sonnet
├── subagent A (no override) → inherits sonnet
├── subagent B (prompt has #!haiku) → routes to haiku
└── subagent C (no override) → inherits sonnet
Critical: The proxy receives the full conversation history, not just the latest message. When a user changes routes mid-session, earlier messages carry stale markers. The proxy must extract the route from the last user message only.
function extractRoute(requestBody) {
const messages = requestBody.messages || [];
// Find the last user message
for (let i = messages.length - 1; i >= 0; i--) {
if (messages[i].role !== 'user') continue;
const content = messages[i].content;
const text = typeof content === 'string'
? content
: content.map(b => b.text || '').join('\n');
const match = text.match(/\[LUNAROUTE:([a-zA-Z0-9._-]+)\]/);
if (match) return match[1];
break; // Only check the last user message
}
return null; // No route — use default model
}
UserPromptSubmit hook — detects #! tags in user input, persists state to /tmp/lunaroute-{session_id}, and injects [LUNAROUTE:model] into subsequent requests via additionalContext.PreToolUse hook (Agent matcher) — injects the active route marker into subagent prompts via updatedInput, enabling inherited routing without user intervention.#!model tag remains visible in the prompt text. Hooks can add context but cannot modify the prompt text itself.#!/usr/bin/env node are correctly ignored. The regex requires whitespace before #! to distinguish route commands from shell shebangs.Smart LLM routing with Claude subscription monitoring, complexity-first model selection, and 20+ AI providers
Automatic model switching for Claude Code based on prompt complexity. No API calls.
Intelligent model routing for Claude Code - routes queries to optimal Claude model (Haiku/Sonnet/Opus) based on complexity, with persistent knowledge system, context forking, and multi-turn awareness
Inject SubagentStart marker context for copilot-api initiator override.
Flagship+ skill pack for OpenRouter - 30 skills for multi-model routing, fallbacks, and LLM gateway mastery
Share bugs, ideas, or general feedback.
Model routing and workflow patterns for Claude Code. Subagents on Sonnet/Haiku for execution work, hooks for security, slash commands for workflow.
Own this plugin?
Verify ownership to unlock analytics, metadata editing, and a verified badge.
Sign in to claimOwn this plugin?
Verify ownership to unlock analytics, metadata editing, and a verified badge.
Sign in to claim