24 KiB
codex-crew Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Плагин Claude Code, делающий Codex CLI полноценным сабагентом (исполнение / ревью / консультации) с resumable-сессиями и детерминированной детекцией зависаний.
Architecture: Тонкий сабагент-транспорт (agents/codex.md, sonnet) запускает scripts/codex-run.sh в фоне; скрипт гонит codex exec --json, следит за стагнацией потока событий (watchdog → kill), пишет status/thread_id/last.md в job_dir. Follow-up оркестратора = SendMessage → codex exec resume <thread_id>. Три скилла учат оркестратора формировать задания.
Tech Stack: bash, Claude Code plugin (agents + skills), codex CLI ≥0.142.
Спека: docs/design.md. При реализации Task 1/6/7 сверяйся со скиллами
plugin-dev (plugin-dev:plugin-structure, plugin-dev:agent-development,
plugin-dev:skill-development) — валидация форматов manifest/frontmatter.
Факты, проверенные заранее: --json и -o поддерживаются всеми тремя режимами (exec, exec resume, exec review); thread_id приходит в событии thread.started; у review нет -C — рабочая директория задаётся через cwd запуска; без </dev/null codex читает stdin.
Task 1: Скаффолд плагина
Files:
-
Create:
~/Projects/codex-crew/.claude-plugin/plugin.json -
Create:
~/Projects/codex-crew/.claude-plugin/marketplace.json -
Step 1: plugin.json
{
"name": "codex-crew",
"version": "0.1.0",
"description": "Codex as a first-class Claude Code subagent: delegate tasks, review code, consult on specs/plans. Inherits user codex config (no sandboxes), resumable sessions, deterministic stall detection."
}
- Step 2: marketplace.json
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "codex-crew",
"description": "Codex as a Claude Code subagent",
"owner": { "name": "smartass" },
"plugins": [
{
"name": "codex-crew",
"description": "Codex as a first-class subagent: delegate, review, consult",
"source": "./",
"category": "productivity"
}
]
}
- Step 3: Commit
cd ~/Projects/codex-crew && git add -A && git commit -m "feat: plugin scaffold"
Task 2: scripts/codex-run.sh
Files:
-
Create:
~/Projects/codex-crew/scripts/codex-run.sh(chmod +x) -
Step 1: Написать скрипт целиком
#!/usr/bin/env bash
# codex-crew: deterministic wrapper around `codex exec` with stall watchdog.
#
# Usage:
# codex-run.sh <job_dir> exec [codex-flags...] [prompt]
# codex-run.sh <job_dir> resume <thread_id> [codex-flags...] [prompt]
# codex-run.sh <job_dir> review [--base X|--commit Y|--uncommitted] [codex-flags...] [prompt]
#
# If <job_dir>/prompt.md exists and no positional prompt given, it is fed via stdin.
# Review runs against the git repo at the CURRENT WORKING DIRECTORY (no -C support).
#
# Files written to <job_dir>: events.jsonl last.md err.log thread_id status
# status: ok | stalled | error: <reason>. Exit code 0 only on ok.
#
# Env knobs:
# CODEX_STALL_TIMEOUT seconds of event silence before kill (default 600)
# CODEX_WATCH_INTERVAL watchdog poll interval seconds (default 15)
# CODEX_BIN codex binary (default codex; tests use a stub)
set -u
JOB_DIR=$1; MODE=$2; shift 2
mkdir -p "$JOB_DIR"
JOB_DIR=$(cd "$JOB_DIR" && pwd)
EV=$JOB_DIR/events.jsonl LAST=$JOB_DIR/last.md ERR=$JOB_DIR/err.log
STALL=${CODEX_STALL_TIMEOUT:-600}
TICK=${CODEX_WATCH_INTERVAL:-15}
BIN=${CODEX_BIN:-codex}
case $MODE in
exec) CMD=("$BIN" exec --json --skip-git-repo-check -o "$LAST") ;;
resume) TID=$1; shift; printf '%s\n' "$TID" > "$JOB_DIR/thread_id"
CMD=("$BIN" exec resume "$TID" --json --skip-git-repo-check -o "$LAST") ;;
review) CMD=("$BIN" exec review --json -o "$LAST") ;;
*) echo "usage: codex-run.sh <job_dir> exec|resume|review ..." >&2; exit 2 ;;
esac
rm -f "$JOB_DIR/status"; : > "$EV"; : > "$ERR"; rm -f "$LAST"
if [[ $# -eq 0 && -s $JOB_DIR/prompt.md ]]; then
"${CMD[@]}" - < "$JOB_DIR/prompt.md" > "$EV" 2> "$ERR" &
else
"${CMD[@]}" "$@" < /dev/null > "$EV" 2> "$ERR" &
fi
PID=$!
( while kill -0 "$PID" 2>/dev/null; do
sleep "$TICK"
now=$(date +%s); mt=$(stat -c %Y "$EV" 2>/dev/null || echo "$now")
if (( now - mt > STALL )); then
echo stalled > "$JOB_DIR/status"
kill "$PID" 2>/dev/null; sleep 3; kill -9 "$PID" 2>/dev/null
break
fi
done ) &
WATCH=$!
wait "$PID"; RC=$?
kill "$WATCH" 2>/dev/null; wait "$WATCH" 2>/dev/null
TID_FOUND=$(grep -m1 -o '"thread_id":"[^"]*"' "$EV" | cut -d'"' -f4)
[[ -n $TID_FOUND ]] && printf '%s\n' "$TID_FOUND" > "$JOB_DIR/thread_id"
if [[ -s $JOB_DIR/status ]]; then
: # stalled, written by watchdog
elif [[ $RC -eq 0 && -s $LAST ]]; then
echo ok > "$JOB_DIR/status"
else
echo "error: exit=$RC $(tail -c 300 "$ERR" | tr '\n' ' ')" > "$JOB_DIR/status"
fi
ST=$(head -n1 "$JOB_DIR/status")
echo "status: $ST"
echo "thread_id: $(cat "$JOB_DIR/thread_id" 2>/dev/null || echo none)"
[[ $ST == ok ]]
- Step 2: chmod и commit
chmod +x ~/Projects/codex-crew/scripts/codex-run.sh
cd ~/Projects/codex-crew && git add -A && git commit -m "feat: codex-run.sh watchdog wrapper"
Task 3: Тест скрипта на стабах (happy path + stall)
Files:
-
Create:
~/Projects/codex-crew/tests/test-run.sh(chmod +x) -
Create:
~/Projects/codex-crew/tests/stub-ok/codex(chmod +x) -
Create:
~/Projects/codex-crew/tests/stub-hang/codex(chmod +x) -
Step 1: Стаб happy path —
tests/stub-ok/codex:
#!/usr/bin/env bash
# fake codex: emits thread.started, writes last message file, exits 0
out=""
prev=""
for a in "$@"; do [[ $prev == "-o" ]] && out=$a; prev=$a; done
echo '{"type":"thread.started","thread_id":"test-thread-123"}'
echo '{"type":"turn.completed"}'
echo "stub answer" > "$out"
exit 0
- Step 2: Стаб зависания —
tests/stub-hang/codex:
#!/usr/bin/env bash
# fake codex: emits one event then hangs silently
echo '{"type":"thread.started","thread_id":"hang-thread-456"}'
sleep 300
- Step 3: Тест —
tests/test-run.sh:
#!/usr/bin/env bash
set -eu
HERE=$(cd "$(dirname "$0")" && pwd)
RUN=$HERE/../scripts/codex-run.sh
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
# 1. happy path -> status ok, thread_id captured, exit 0
CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/ok" exec "hello"
[[ $(cat "$TMP/ok/status") == ok ]]
[[ $(cat "$TMP/ok/thread_id") == test-thread-123 ]]
[[ $(cat "$TMP/ok/last.md") == "stub answer" ]]
# 2. stall -> watchdog kills, status stalled, exit nonzero
if CODEX_BIN=$HERE/stub-hang/codex CODEX_STALL_TIMEOUT=4 CODEX_WATCH_INTERVAL=2 \
"$RUN" "$TMP/hang" exec "hello"; then
echo "FAIL: stall run must exit nonzero"; exit 1
fi
[[ $(cat "$TMP/hang/status") == stalled ]]
[[ $(cat "$TMP/hang/thread_id") == hang-thread-456 ]]
# 3. prompt.md via stdin path
mkdir -p "$TMP/stdin"; echo "prompt from file" > "$TMP/stdin/prompt.md"
CODEX_BIN=$HERE/stub-ok/codex "$RUN" "$TMP/stdin" exec
[[ $(cat "$TMP/stdin/status") == ok ]]
echo "ALL PASS"
- Step 4: Запустить, убедиться в провале/успехе честно
chmod +x ~/Projects/codex-crew/tests/*.sh ~/Projects/codex-crew/tests/stub-*/codex
~/Projects/codex-crew/tests/test-run.sh
Expected: ALL PASS (stall-кейс занимает ~6-10 с). Если FAIL — чинить скрипт, не тест.
- Step 5: Commit
cd ~/Projects/codex-crew && git add -A && git commit -m "test: stub-based watchdog tests"
Task 4: Живой smoke: exec + resume
Ничего не коммитим — проверка против реального codex.
- Step 1: exec
J=$(mktemp -d)
~/Projects/codex-crew/scripts/codex-run.sh "$J" exec "Ответь одним словом: пинг"
cat "$J/last.md"
Expected: status: ok, thread_id: <uuid>, last.md содержит «понг» (или похожее).
- Step 2: resume той же сессии
~/Projects/codex-crew/scripts/codex-run.sh "$J" resume "$(cat $J/thread_id)" "Процитируй моё прошлое сообщение"
cat "$J/last.md"
Expected: status: ok, цитата «пинг» — сессия непрерывна.
Task 5: Живой smoke: review
- Step 1: review --uncommitted на тестовом репо
R=$(mktemp -d) && cd "$R" && git init -q
printf 'def div(a, b):\n return a / b\n' > calc.py
git add -A && git commit -qm init
printf 'def div(a, b):\n return a / b # may divide by zero\n\ndef unused():\n pass\n' > calc.py
J=$(mktemp -d)
~/Projects/codex-crew/scripts/codex-run.sh "$J" review --uncommitted
cat "$J/last.md"; cat "$J/thread_id"
Expected: status: ok, last.md с находками, thread_id записан. Если thread.started у review отсутствует — зафиксировать в agents/codex.md, что ревью-итерации идут новым exec-заданием (спека это допускает).
Task 6: agents/codex.md
Files:
-
Create:
~/Projects/codex-crew/agents/codex.md -
Step 1: Написать агента целиком
---
name: codex
description: Codex (GPT) как полноценный сабагент — исполнение задач, ревью кода, второе мнение. Держит живую codex-сессию, follow-up через SendMessage продолжает тот же диалог. Промпт = задание; опционально строки `cwd: <абсолютный путь>`, `codex options: <флаги>`, `review scope: --base X|--commit Y|--uncommitted`.
model: sonnet
tools: Bash, Read, Write
---
Ты — транспорт между оркестратором и Codex CLI. Задачу сам НЕ решаешь, ответы
codex не редактируешь и не дополняешь. Запустил → дождался → вернул как есть.
Скрипт: `${CLAUDE_PLUGIN_ROOT}/scripts/codex-run.sh` (дальше — codex-run.sh).
Он пишет в job_dir: `status` (ok|stalled|error), `thread_id`, `last.md`
(финальный ответ codex), `events.jsonl` (живой прогресс), `err.log`.
## Первое задание
1. `JOB=$(mktemp -d /tmp/codex-job-XXXXXX)` — запомни путь.
2. Write в `$JOB/prompt.md`: транспортная преамбула (ниже) + задание
оркестратора ДОСЛОВНО (не пересказывай).
3. Собери команду:
- обычное задание: `codex-run.sh $JOB exec <codex options если были>`
- ревью с `review scope:`: `codex-run.sh $JOB review <scope> <codex options>`
- если оркестратор дал `cwd:` — префикс `cd <cwd> && `.
4. Запусти через Bash с `run_in_background: true`.
5. Заверши ход коротко: «codex запущен, job_dir: $JOB». Когда фоновая
команда завершится — тебя разбудят.
## Пробуждение (фоновая команда завершилась)
Прочитай `$JOB/status`:
- **ok** → верни содержимое `$JOB/last.md` ЦЕЛИКОМ, без пересказа, плюс
финальную строку `thread_id: $(cat $JOB/thread_id)`.
- **stalled / error** → ретрай, суммарно не больше 3 попыток:
1. Write в `$JOB/prompt.md`: `You were interrupted mid-task. Continue where
you left off. If you had already finished, reply with your final report.`
2. `codex-run.sh $JOB resume $(cat $JOB/thread_id)` в фоне.
3. Нет `thread_id` (упал до старта) → один повтор `exec` с исходным промптом.
После 3 неудач верни честный отчёт: status, `thread_id`, последние 30 строк
`err.log`. Не выдумывай результат.
## SendMessage от оркестратора
- **Codex ещё работает** → не прерывай. Ответь статусом: `tail -5
$JOB/events.jsonl` — что codex делает сейчас.
- **Codex свободен** → это новая реплика (ответ на вопрос codex, доработка,
следующая итерация консультации): Write текст в `$JOB/prompt.md` (дословно),
запусти `codex-run.sh $JOB resume $(cat $JOB/thread_id)` в фоне, по
завершении верни ответ по правилам «Пробуждения».
## Транспортная преамбула (вставляй перед первым заданием)
```
You are operating non-interactively as an executor for an orchestrating agent.
If you hit a blocker or need a decision, end your turn with a clear question —
the dialogue continues (your session will be resumed with the answer).
Your final message is your report: what was done, which files were touched,
how it was verified.
```
К follow-up-репликам (resume) преамбулу НЕ добавляй.
## Переопределения (строка `codex options:` от оркестратора)
Пробрасывай флаги в codex-run.sh как есть, после режима. Сам ничего не
добавляй. Частые:
| Что | Флаг |
|---|---|
| Модель | `-m <model>` |
| Reasoning effort | `-c model_reasoning_effort="minimal|low|medium|high|xhigh"` |
| Права | `-s read-only` / `-s workspace-write` (default: полный доступ из конфига юзера) |
| Доп. write-директории | `--add-dir <dir>` |
| Картинки | `-i <file>` |
| Структурированный ответ | `--output-schema <schema.json>` |
| Любой конфиг-ключ | `-c key=value` |
Никогда: `--ephemeral` (ломает resume). Долгая задача ожидается —
`CODEX_STALL_TIMEOUT=<сек>` перед командой (default 600 — стагнация вывода,
не общее время; трогать редко).
- Step 2: Commit
cd ~/Projects/codex-crew && git add -A && git commit -m "feat: codex transport subagent"
Task 7: Три скилла
Files:
-
Create:
~/Projects/codex-crew/skills/codex-agent/SKILL.md -
Create:
~/Projects/codex-crew/skills/codex-review/SKILL.md -
Create:
~/Projects/codex-crew/skills/codex-consult/SKILL.md -
Step 1: codex-agent/SKILL.md
---
name: codex-agent
description: Делегировать Codex'у исполнение — написать код, починить баг, исследовать. Use when user says «отдай кодексу», «пусть codex сделает/напишет/починит», «делегируй в codex», or when a plan step is assigned to Codex as the implementer.
---
# Делегация задачи Codex
Спавни сабагента `codex` (Agent tool). Он держит живую codex-сессию:
доработки и ответы на его вопросы — SendMessage тому же агенту, НЕ новый спавн.
## Промпт сабагенту — обязательные части
- `cwd: <абсолютный путь проекта>`
- Задание: что сделать. Конкретно, с релевантными файлами/путями.
- Критерии приёмки: что должно работать, какие тесты/команды пройти.
- Что вернуть: отчёт, список тронутых файлов, как проверено.
- Опционально: `codex options: -m o3 -s read-only ...`
## Пример
```
cwd: /home/smartass/Projects/nomcrm-api
Задание: почини N+1 в эндпоинте GET /orders — queries лог показывает
по запросу на каждую позицию заказа. Смотри app/controllers/OrdersController.php.
Критерии: существующие тесты проходят (composer test), запросов к БД ≤ 3 на запрос.
Верни: список изменённых файлов, суть фикса, вывод composer test.
```
## После завершения
- Ответ codex = отчёт + `thread_id: ...`. Не верь на слово: проверь diff,
прогони тесты сам.
- Замечания → SendMessage тому же сабагенту (та же codex-сессия, контекст цел).
- Codex закончил вопросом (блокер) → ответь через SendMessage, он продолжит.
- Пока работает, можно спросить статус SendMessage'ем — не прерывает.
- Step 2: codex-review/SKILL.md
---
name: codex-review
description: Ревью кода силами Codex — ветка, коммит, uncommitted-изменения или произвольные файлы. Use when user says «ревью у кодекса», «пусть codex посмотрит ветку/коммит/файл», «codex review», «второй ревьюер».
---
# Ревью через Codex
Спавни сабагента `codex`. Скоуп задаёт режим:
- ветка против базы: `review scope: --base main`
- один коммит: `review scope: --commit <sha>`
- незакоммиченное: `review scope: --uncommitted`
- произвольные файлы / абстрактный скоуп: БЕЗ `review scope:` — обычное
задание «сделай ревью файлов X, Y и их производных».
## Промпт сабагенту — обязательные части
- `cwd: <абсолютный путь репозитория>` (review работает от cwd)
- `review scope: ...` (если применимо)
- Фокус, если есть: безопасность / перфоманс / корректность / всё.
- Формат находок: `file:line — 🔴|🟡|🔵 — проблема — предлагаемый фикс`,
без похвал и пересказа кода.
## Пример
```
cwd: /home/smartass/Projects/proxy-gate
review scope: --base main
Фокус: корректность и безопасность.
Формат находок: file:line — 🔴|🟡|🔵 — проблема — фикс. Без похвал.
```
## После
- Находки отдай юзеру / обработай по задаче.
- Спорная находка → SendMessage в ту же сессию: попроси обосновать или
показать репро. Не принимай и не отвергай молча.
- Step 3: codex-consult/SKILL.md
---
name: codex-consult
description: Второе мнение Codex по артефакту — спека, план, дизайн, документ, идея. Итерации до консенсуса. Use when user says «проверь у кодекса», «спроси codex», «второе мнение», «пусть codex ревьюнет спеку/план», or when validating a spec/plan before execution.
---
# Консультация с Codex
Спавни сабагента `codex`, итерируй SendMessage'ами. Жёсткий лимит: 5 итераций.
## Первый промпт — обязательные части
- Роль: «Ты — второе мнение, НЕ исполнитель. Артефакт не переписывай.
Верни находки с severity и отдельно вопросы.»
- Требования юзера — дословно (что артефакт должен удовлетворять).
- Сам артефакт: текст целиком или `cwd:` + путь к файлу.
## Цикл итераций
1. Получил находки → оцени критически сам (используй
superpowers:receiving-code-review, не соглашайся слепо).
2. Внёс правки в артефакт → SendMessage: что принял, что отклонил и ПОЧЕМУ,
плюс обновлённый артефакт или diff.
3. Повторяй до «существенных замечаний нет» или лимита 5.
## Финал — резюме юзеру
- Что согласовано и внесено.
- Что отклонено и почему.
- Нерешённые расхождения, если упёрлись (решает юзер).
- Step 4: Commit
cd ~/Projects/codex-crew && git add -A && git commit -m "feat: codex-agent, codex-review, codex-consult skills"
Task 8: Установка плагина
- Step 1: Подключить marketplace и плагин
claude plugin marketplace add ~/Projects/codex-crew
claude plugin install codex-crew@codex-crew
(Если CLI-команды нет в этой версии — интерактивно: /plugin marketplace add ~/Projects/codex-crew, затем /plugin install codex-crew@codex-crew.)
- Step 2: Выключить старый плагин
claude plugin disable codex@openai-codex
- Step 3: Проверить
claude plugin list 2>/dev/null | grep -iE 'codex'
Expected: codex-crew enabled, codex@openai-codex disabled.
Task 9: Интеграционный тест (новая сессия)
Проверяет открытый вопрос спеки: доставку результата при re-invoke фоновой таской.
- Step 1: Тестовый проект
T=$(mktemp -d) && cd "$T" && git init -q && echo "# demo" > README.md && git add -A && git commit -qm init
- Step 2: В НОВОЙ сессии Claude Code (перезапуск — плагин подхватился) спавнить сабагента
Промпт оркестратору: «Через сабагента codex: cwd: . Задание: создай файл hello.py, печатающий "hi", запусти его, верни вывод.»
Expected: сабагент запускает фон, возвращает результат codex с thread_id.
Файл hello.py существует, работает.
- Step 3: Follow-up через SendMessage
«Спроси у того же сабагента: добавь в hello.py вторую строку "bye" — тем же thread'ом.»
Expected: сабагент делает resume (не новый exec), codex помнит контекст, файл обновлён.
- Step 4: Если результат при re-invoke НЕ дошёл до оркестратора сам
Добавить в конец agents/codex.md и в секцию «После завершения» всех трёх
скиллов fallback-абзац: оркестратор пингует SendMessage'ем «статус?», сабагент
отвечает по $JOB/status + last.md. Commit: fix: polling fallback.
Task 10: Финал
- Step 1: Удалить одноразовые доки
cd ~/Projects/codex-crew && git rm -q docs/design.md docs/plan.md && rmdir docs 2>/dev/null; git commit -m "chore: drop one-off spec and plan"
- Step 2: Memory-заметка — записать в память факт: плагин codex-crew в
~/Projects/codex-crew(сабагент codex + 3 скилла), старый openai-codex отключён.