Files
codex-crew/docs/plan.md
T
2026-07-07 15:54:02 +05:00

24 KiB
Raw Blame History

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 отключён.