Lets Claude hand tasks to Codex (gpt-6.1-sol)

Four small mods for Claude Code. Install the ones you want.
| Mod | What it does |
|---|---|
codex | Claude can hand a task to Codex (OpenAI's gpt-6.1-sol), like a code review or a bug hunt. It runs in the background so the chat isn't blocked. |
imagegen | Claude can make and edit images through Codex, and shows the finished images right in the chat. |
devserver-limit | Stops Claude from starting a dev server when 2 are already running. Good for laptops with little memory. |
done-sound | Plays a chime when a task that took over a minute finishes. |
In a Claude Code terminal session, type one line per mod:
/plugin install codex --marketplace arjunkambj/claude-mods
/plugin install imagegen --marketplace arjunkambj/claude-mods
/plugin install devserver-limit --marketplace arjunkambj/claude-mods
/plugin install done-sound --marketplace arjunkambj/claude-mods
The first time, it asks to add the marketplace: press y. Then pick a scope (Enter picks "user", so it works in every project).
imagegen and done-sound use macOS tools and sounds.codex and imagegen: the Codex CLI installed and signed in (codex login). Images count against your Codex plan.hooks/register.ts 101 lines1import type { Register } from 'claude-code'
2
3const CODEX_TOOL = /^mcp__codex__codex$/
4const CODEX_MODEL = 'gpt-6.1-sol'
5const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'] as const
6// The codex tool runs only inside this background agent, so a long Codex run never blocks the main chat.
7const RUNNER = 'codex:runner'
8const RUNNER_PROMPT =
9 'You pass one task to Codex and bring back its answer. Call the codex tool exactly once with the task you were ' +
10 'given, word for word, as `task`. If the task names an effort level or says Codex may edit files, set `effort` or ' +
11 '`canEdit` to match. Then reply with Codex\'s answer exactly as it came back: add nothing, cut nothing.'
12
13type CodexInput = {
14 task: string
15 description?: string
16 effort?: (typeof EFFORTS)[number]
17 canEdit?: boolean
18}
19
20export const register: Register = on => {
21 on('session.start', async ($, e, next) => {
22 const started = await next(e)
23 await $.tool.register({
24 name: 'codex',
25 description:
26 `Only works inside the ${RUNNER} agent: from the main chat, spawn that agent with the Agent tool instead. ` +
27 `Hands a task to Codex (OpenAI's ${CODEX_MODEL}) running as a separate agent in this project, and returns its final answer. ` +
28 'Codex is great at code review and bug finding: use it for a second opinion on a diff, a hunt for bugs in a ' +
29 'file or feature, or checking a fix. Give it a self-contained task (it does not see this conversation): what to ' +
30 'look at, what to check, and how to report. Effort defaults to xhigh, which suits reviews; use max for the ' +
31 'hardest bugs and lower levels for quick checks. It runs read-only unless canEdit is true.',
32 inputSchema: {
33 type: 'object',
34 properties: {
35 task: { type: 'string', description: 'The full, self-contained instructions for Codex.' },
36 description: { type: 'string', description: 'A 3-5 word label for the task.' },
37 effort: { type: 'string', enum: EFFORTS, description: 'Reasoning effort; default xhigh.' },
38 canEdit: { type: 'boolean', description: 'Let Codex edit files in the project; default false.' },
39 },
40 required: ['task'],
41 },
42 isDeferred: false,
43 })
44 await $.agent.register({
45 name: 'runner',
46 description:
47 `Runs a task on Codex (OpenAI's ${CODEX_MODEL}) in the background and returns its answer; use this, never the ` +
48 'codex tool directly. Great for code review, bug hunts and checking a fix. The prompt is the whole task for ' +
49 'Codex (it does not see this conversation): what to look at, what to check, how to report. Add "effort: max" ' +
50 'for the hardest bugs or a lower level for quick checks (default xhigh), and "canEdit: true" to let it edit files.',
51 prompt: RUNNER_PROMPT,
52 tools: ['mcp__codex__codex'],
53 model: 'haiku',
54 effort: 'low',
55 background: true,
56 omitClaudeMd: true,
57 })
58 return started
59 })
60
61 on('tool.call', { tool: CODEX_TOOL }, async ($, e, next) => {
62 if (e.agentId === undefined) {
63 return { deny: `Run Codex through the Agent tool with subagent_type "${RUNNER}" so it runs in the background.` }
64 }
65 const input = e as unknown as CodexInput
66 const effort = input.effort ?? 'xhigh'
67
68 const it = $.process
69 .spawn({
70 argv: [
71 'codex', 'exec',
72 '-m', CODEX_MODEL,
73 '-c', `model_reasoning_effort="${effort}"`,
74 '-s', input.canEdit ? 'workspace-write' : 'read-only',
75 '--skip-git-repo-check', '--color', 'never',
76 input.task,
77 ],
78 })
79 [Symbol.asyncIterator]()
80 let answer = ''
81 let errors = ''
82 for (;;) {
83 if (next.signal.aborted) {
84 await it.return?.(undefined as never)
85 return { result: 'Codex run stopped.' }
86 }
87 const step = await it.next()
88 if (step.done) {
89 if (step.value.code !== 0) {
90 return { result: `Codex failed (exit ${step.value.code ?? step.value.signal}).\n${errors.slice(-4000)}` }
91 }
92 break
93 }
94 if (step.value.stream === 'stdout') answer += step.value.text
95 else errors += step.value.text
96 }
97
98 return { result: answer.trim() || 'Codex finished without an answer.' }
99 })
100}
101