SLOPSHOPPER

cc-model-effort-switcher

Shift Claude Code model and reasoning effort in one command: /m opus high

newcommandtoasttimer
v0.1.0MITupdated 2026-10-14kuzmany/cc-model-effort-switcher
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · cc-model-effort-switcher
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /m ⎿ cc-model-effort-switcher: Usage: /m <model> <effort>, /m <model>, or /m <effort>. Models: haiku, sonnet, opus, fable, default, or a clau ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

cc-model-effort-switcher

Claude Code mod Claude Code 2.1.287+ License: MIT

Shift Claude Code's model and reasoning effort in one command.

/m haiku low      # quick question, cheap and fast
/m opus high      # hard refactor
/m sonnet         # model only
/m xhigh          # effort only

Without cc-model-effort-switcher it takes two commands, /model opus and then /effort high, or a trip through the model picker. /m does both at once.

Install

At the Claude Code prompt:

/plugin install cc-model-effort-switcher --marketplace kuzmany/cc-model-effort-switcher

Or from a shell:

claude plugin marketplace add kuzmany/cc-model-effort-switcher
claude plugin install cc-model-effort-switcher@cc-model-effort-switcher

Usage

/m <model> <effort>, in either order, or just one of them.

ModelEffort
haiku, sonnet, opus, fable, default, or a full claude-* idlow, medium, high, xhigh, max

Anything else prints a usage line and changes nothing.

How it works

/m runs the built-in /model and /effort for you, in that order, right after it finishes. The result is exactly what typing them would do. That also means /m opus … saves Opus as your default for new sessions, as /model opus does.

cc-model-effort-switcher changes no files or settings of its own and makes no network calls.

Requirements

Claude Code with mods (function hooks), 2.1.287 or later. Tested with 2.1.299.

Develop

claude plugin validate .
claude plugin test .
claude --plugin-dir .

License

MIT

Source 1 files
hooks/register.ts 61 lines
1import type { Register } from 'claude-code'
2
3const MODELS = ['haiku', 'sonnet', 'opus', 'fable', 'default'] as const
4const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'] as const
5const FULL_MODEL_ID = /^claude-[a-z0-9.-]+(\[1m\])?$/
6const USAGE = 'Usage: /m <model> <effort>, /m <model>, or /m <effort>. Models: haiku, sonnet, opus, fable, default, or a claude-* id. Effort: low, medium, high, xhigh, max.'
7
8type Plan = { model?: string; effort?: string } | { error: string }
9
10export function plan(args: string): Plan {
11  const words = args.trim().toLowerCase().split(/\s+/).filter(Boolean)
12  if (words.length === 0 || words.length > 2) {
13    return { error: USAGE }
14  }
15
16  let model: string | undefined
17  let effort: string | undefined
18  for (const word of words) {
19    if ((EFFORTS as readonly string[]).includes(word) && effort === undefined) {
20      effort = word
21    } else if (((MODELS as readonly string[]).includes(word) || FULL_MODEL_ID.test(word)) && model === undefined) {
22      model = word
23    } else {
24      return { error: `Unknown value "${word}". ${USAGE}` }
25    }
26  }
27
28  return { model, effort }
29}
30
31export const register: Register = on => {
32  on('session.start', async ($, e, next) => {
33    await $.command.register({ name: 'm', description: 'Switch model and effort at once: /m haiku low' })
34
35    return next(e)
36  })
37
38  on('command.run', { command: 'm' }, async ($, e) => {
39    const chosen = plan(e.args)
40    if ('error' in chosen) {
41      return { text: chosen.error }
42    }
43
44    // A command.run hook cannot run another command itself; a timer runs them once this one has finished.
45    $.clock.after(0, async () => {
46      try {
47        if (chosen.model !== undefined) {
48          await $.command.run({ command: 'model', args: chosen.model })
49        }
50        if (chosen.effort !== undefined) {
51          await $.command.run({ command: 'effort', args: chosen.effort })
52        }
53      } catch (err) {
54        await $.ui.toast(`/m failed: ${String(err)}`)
55      }
56    })
57
58    return { text: `Switching to ${[chosen.model, chosen.effort].filter(Boolean).join(' · ')}` }
59  })
60}
61