1日の開発の流れをスキル化してみたら、想像以上に快適だった
概要
ここ最近、すっかり同じような流れで開発をしている。
ざっくり書くと、以下のようになる。
GithubのIssueを取る → AIに設計と実装を任せる → 別のAIにレビューしてもらう → 動作確認してもらう → Pull Requestを作成してもらう
しかし、これを自動化できていなかった。その都度、AIに指示を出していた。
さすがに面倒になってきたので、スキル化してみた。
GithubでのIssue管理
まず前提として、今回作成したスキルでは、GithubでIssue管理を行っている必要がある。
Issueへの起票やタスク分解もAIを利用しているけど、それはこの記事の対象外となる。
が、NotionでもTrelloでも、AIとやり取りできるなら何でも良いと思う。AIに読ませ、理解させる部分を自動化することが重要。
利用するAI
次に、利用するAIについて。
自分はClaudeCodeとCodexを利用している。
今回はClaudeCodeを主とし、Codexを従とした。特にこだわりはないので、そのうち逆転させてもいいかもしれない。
主たるClaudeCodeが設計と実装を行う。外部(Github・Codex・人間)とのやり取りもClaudeCodeが行う。
Codexはレビューのみ行う。
もっとサブエージェントに切り出しても良いかもしれないけど、個人的にサブエージェントを使いこなせていないので採用していない。
コードレビュー方法
コードレビューでは、クロスレビューを行う。
ClaudeCodeは実装まで行い、レビューはCodexと人間で行う。
Codexとのやり取りはClaudeCodeに任せる。ただし、先入観なくレビューしてもらうよう気をつける。
人間のレビューには crit を使う。critについては、以下の記事で書いた。
スキルの置き場所
自分は複数の会社の複数のプロダクトに関わっている。
そのため、特定のレポジトリに閉じず、ユーザーレベルでスキルを配置することにした。
具体的には ~/.claude/skills/ だ。ここに置くと、どのレポジトリからも呼び出すことができる。
当然、スキルの中身はそれなりに汎用化させる必要がある。
作成したスキル
作成したスキルは以下の通り。
スキル名は今日のタスクという感じで、todays-workにしてみた。
---
name: todays-work
description: GitHub Issue を受け取り、Issue の把握、ブランチ作成、実装、動作確認、crit と Codex のレビュー、PR 作成までを進める。ユーザーが /todays-work で明示的に呼び出したときだけ使う。
disable-model-invocation: true
argument-hint: "<Issue番号 または IssueのURL>"
---
# todays-work
引数: `$ARGUMENTS`(Issue 番号、または Issue の URL)。空ならユーザーに聞く。
名前は語呂で付けただけで、今日中に終わらせる必要はない。複数日かかってもよい。
## 全体の流れ
1. Issue を把握する
2. ブランチを切る
3. 実装計画を立てる(大きい・複雑なときだけ)
4. 実装する
5. 動作確認する
6. レビューを依頼する(crit と Codex を同時に)
7. レビューに対応する
8. PR を作り、コメントを投稿する
9. 後片付けする
コミットメッセージ、PR の書式、テストの書き方などは、リポジトリの AGENTS.md / CLAUDE.md を優先する。
### 作業記録の置き場所
途中の記録は `~/.claude/todays-work/<repo名>/issue-<番号>/` に置く。リポジトリには入れない。9 で削除する。
| ファイル | 中身 |
|---|---|
| `issue.json` / `parent-issue.json` | Issue の生データ(Codex への依頼に使う) |
| `verification.md` | 動作確認の内容と結果 |
| `codex-prompt.md` / `codex.log` / `codex-review.md` | Codex への依頼文、実行ログ、返ってきたレビュー |
| `review-decisions.md` | 指摘ごとの対応結果(対応したコミット、または見送った理由) |
## 1. Issue を把握する
番号だけなら今いるリポジトリの Issue とみなす。URL なら URL の `<owner>/<repo>` を使う。
### 対象の Issue
説明文とすべてのコメントを読む。
```bash
gh issue view <番号> -R <owner>/<repo> --json number,title,state,body,labels,assignees,milestone,comments
```
出力は `issue.json` に保存する。
### 親 Issue と、同じ親を持つ子 Issue
```bash
gh api graphql -F owner=<owner> -F repo=<repo> -F number=<番号> -f query='
query($owner:String!,$repo:String!,$number:Int!){
repository(owner:$owner,name:$repo){
issue(number:$number){
number title
parent{ number title }
subIssuesSummary{ total completed percentCompleted }
subIssues(first:100){ nodes{ number title state } }
}
}
}'
```
- 親があれば、親の番号でも `gh issue view` を実行し、説明文とコメントを読む。出力は `parent-issue.json` に保存する。
- 親の番号で上の GraphQL を実行し、子 Issue の一覧と進捗(完了数 / 総数)を眺める。
- すべての子 Issue の中身は読まなくてよい。今回の作業と直接関係しそうなものだけ `gh issue view` で読む。たとえば、前提になる設計を決めた Issue、今回の成果物を使う Issue、同じファイルや API を触る Issue。
- 対象の Issue 自体に子 Issue があれば、それも一覧で確認する。
### ユーザーへの報告
把握した内容を短く伝える。
- 今回やることと、完了条件
- 親 Issue の中での位置づけと、全体の進捗
- 関係する Issue と、その状態
- ベースブランチ(2 で決めたもの)
- 不明点。実装方針が変わる不明点は、ここで聞いてから進む
## 2. ブランチを切る
作業ツリーに未コミットの変更(未追跡のファイルを含む)があれば、ユーザーに確認せず stash してから進める。破棄はしない。
```bash
git stash push -u -m "todays-work: issue-<番号> 開始前の退避(元ブランチ: <今のブランチ>)"
```
`-u` は未追跡のファイルも退避するが、gitignore 対象のファイル(`.env.local` など)は退避しない。stash したら、元のブランチ名と、戻し方(`git stash list` で確認し、元のブランチで `git stash pop`)をユーザーに伝える。
### ベースブランチを決める
子 Issue のタイトルに `P4` `P5` `P1a` のような接頭辞があれば、それを作業順とみなす。アルファベットが系列で、数字(と枝番)が系列内の順番。
1. 同じ系列で、対象の直前にある子 Issue を探す(`P5` なら `P4`、`P2` なら `P1a`)。
2. その Issue に紐づく PR を調べる。
```bash
gh api graphql -F owner=<owner> -F repo=<repo> -F number=<直前のIssue番号> -f query='
query($owner:String!,$repo:String!,$number:Int!){
repository(owner:$owner,name:$repo){
issue(number:$number){
number state
closedByPullRequestsReferences(first:5, includeClosedPrs:true){ nodes{ number state headRefName baseRefName } }
}
}
}'
```
3. 結果に応じてベースを決める。
| 直前の Issue の状態 | ベース |
|---|---|
| 系列の先頭で、直前がない | `develop` |
| PR がマージ済み、または Issue がクローズ済み | `develop` |
| PR が未マージ(OPEN) | その PR のブランチ(`headRefName`) |
| Issue が OPEN で、PR がない | ユーザーに聞く |
接頭辞が無いなど、判断ができない場合はユーザーに聞く。
### ブランチ名
`issue/<番号>-<英語の短い説明>`(例: `issue/84-cognito-user-deletion`)とする。
### ブランチを作る
**ベースが develop の場合**は、ローカルの develop を最新にしてから切る。
```bash
git fetch origin
git switch develop
git merge --ff-only origin/develop
git switch -c issue/<番号>-<説明>
```
`--ff-only` が失敗したら、ローカルの develop に独自のコミットがある。解消せずにユーザーに確認する。
**ベースが他の作業のブランチの場合**は、リモートのブランチから直接切る。そのブランチは別の worktree でチェックアウトされていることがあり、`git switch <ベース>` は失敗するため。
```bash
git fetch origin
git switch -c issue/<番号>-<説明> --no-track origin/<ベース>
```
## 3. 実装計画を立てる
次のどれかに当てはまれば、大きい・複雑とみなす。
- API、データモデル、画面など、複数の層にまたがる
- 設計上の判断が要る
- 変更するファイルが多くなりそう
- Issue の読み方が一通りに定まらない
当てはまれば、EnterPlanMode で計画を立て、承認を得てから実装する。計画には、変更するファイル、設計判断とその理由、テスト方針、動作確認の方法、更新するドキュメントを含める。
小さい作業なら、このステップは飛ばす。
## 4. 実装する
- 変更対象だけで判断せず、関連する既存の実装を読んでから書く。
- Issue の完了条件を網羅するテストを書く。
- README、docs/、AGENTS.md など、更新が要るドキュメントがないか確認する。
- CI で回るもの(テスト、ビルドなど)は手元でも通す。
- 区切りのよい単位でコミットする。レビューはコミット済みの差分を見るので、レビュー依頼の前にすべてコミットしておく。
## 5. 動作確認する
(割愛)
## 6. レビューを依頼する
crit と Codex を同時に、どちらもバックグラウンドで起動する。
差分の起点には、ベースブランチとの分岐点を使う。ベースが他の作業のブランチなら `origin/<ベース>` を指定する。
```bash
git merge-base <ベース> HEAD
```
### crit
1. `crit --version` で使えるか確認する。コマンドがない、または起動しない場合はセットアップする。crit 本体がなければ公式の手順でインストールする(手順が不明なら WebSearch で調べる)。Claude Code との連携は `crit install claude-code`、状態の確認は `crit check` で行う。
2. `crit --range <分岐点>..HEAD` をバックグラウンドで起動する。
3. 以降は crit スキル(`~/.claude/skills/crit/SKILL.md`)の Step 2〜5 に従う。URL をユーザーに伝え、Finish Review を待つ。
### Codex
**クロスレビューにするため、先入観を持たせない。** Claude が考えた実装の説明、懸念点、見てほしい箇所は書かない。依頼文は次のテンプレートだけで作る。
```text
ブランチ <作業ブランチ> の変更をコードレビューしてください。
対象の差分は `git diff <分岐点>..HEAD` で確認できます。
レビューの前に、変更に関連する既存の実装とドキュメントを読んでください。
観点:
- Issue の要件を満たしているか
- 実装内容に過不足はないか
- 網羅的なテストケースが作成されているか
- 関連するドキュメントは更新されているか
問題点のみを指摘してください。良い点や、細かい好みの指摘(nits)は不要です。
以下は GitHub から取得した Issue の内容です。
<issue>
(issue.json の中身をそのまま貼る)
</issue>
<parent-issue>
(parent-issue.json の中身をそのまま貼る。親がなければこのブロックごと消す)
</parent-issue>
```
これを `codex-prompt.md` に保存し、読み取り専用で実行する。途中経過のログは `codex.log` に逃がし、Claude の文脈に流し込まない。
```bash
codex exec -s read-only --color never -o <作業記録>/codex-review.md - < <作業記録>/codex-prompt.md > <作業記録>/codex.log 2>&1
```
Bash のバックグラウンド実行(`run_in_background: true`)で起動する。完了したら `codex-review.md` を読む。`codex.log` は、失敗したときの原因調査と、冒頭の `model:` 行(PR コメントに書くモデル名)の確認にだけ使う。
## 7. レビューに対応する
crit のコメントと Codex の指摘を、それぞれ次のように扱う。
- すべてに対応する必要はない。指摘ごとに「対応する」か「見送る」かを決める。
- 判断に迷うものは、AskUserQuestion でユーザーに聞く。
- 修正は新しいコミットとして追加する。amend はしない。
- 指摘ごとの結果(対応したコミットのハッシュ、または見送った理由)を `review-decisions.md` に書く。
- crit のコメントには、crit スキルの手順で返信する。ユーザーが Approve あるいはコメントなしで Finish Review するまで繰り返す。
### Codex の結果が先に届いたとき
たいていは、ユーザーの crit レビューより Codex の結果の方が先に届く。届いたら crit の完了を待たずに仕分け、修正してコミットする。修正し終わったら crit を再起動し、最新の差分でレビューしてもらう。
1. `crit comments --json` で、ユーザーがすでにコメントを書いていないか確認する。書いていたら再起動せず、どうするかユーザーに聞く。再起動でコメントが引き継がれるかは確かめていないため。
2. `crit stop` で止め、6 と同じ `crit --range <分岐点>..HEAD` をバックグラウンドで起動し直す。
3. Codex の指摘を直して crit を開き直したことと、新しい URL をユーザーに伝える。
crit が承認され、Codex の指摘をすべて仕分けたら、次へ進む。
## 8. PR を作り、コメントを投稿する
```bash
git push -u origin <作業ブランチ>
gh pr create --base <ベース> --title "<タイトル>" --body-file <本文ファイル>
```
本文の書き方:
- 書式はリポジトリの AGENTS.md に従う。
- Issue を閉じる PR なら `Closes #<番号>` を入れる。
- ベースが develop 以外なら、どの PR の上に積んでいるかを書く。
- **末尾に、実装に使ったモデルを書く**(例: 「実装: Claude Code(Claude Opus 5)」)。モデル名は、このセッションで実際に動いているモデルを書く。
PR を作ったら、`gh pr comment <PR番号> --body-file <ファイル>` でコメントを 2 件投稿する。
1. **Codex のレビュー結果**: 冒頭にレビューしたモデル(`codex.log` の `model:` 行)を書く。Codex の指摘は要約せず原文のまま載せ、指摘ごとに対応済み(コミット)か見送り(理由)かを書く。
2. **動作確認の結果**: `verification.md` の内容。確認できなかったことも書く。
## 9. 後片付けする
(割愛)
ちなみにこのスキルもClaudeに作ってもらった。
やりたいことを300文字くらいでドバーッと書いて、SKILL.mdにしてもらう。そのうえで、不要な箇所を消したり、誤った箇所を訂正したり。
ファーストインプレッション
/todays-work を何度か利用してみたところ、想像以上に快適だった。
何が楽かと言うと、作業の中心が自分からAIに移った感じがする。
もちろん、すべて自動化とは行かない。質問を求められることはあるし、crit のレビューは自分で行う。
しかし、全体の進行はAIが担う。こちらは求められたアクションをすれば良い。非常に楽だ。
驚いたのは、AIが先行して作業していたことだ。
自分がレビューしている間、「先んじてPull Requestの素案を作っておきます」と言っていて、驚いた。
これは、以前には無かった振る舞いだ。
AIが全体の工程を分かっているからこそ、先回りして気を配ることができる。これは地味にありがたかった。
あと、自分が相対するAIが1つになったのも嬉しい。
以前はClaudeCodeとCodexにそれぞれ指示を出していた。が、このスキルでは、Codexのことは一切に気にする必要がない。それが地味に楽だった。
課題感
もちろん、開発の仕事はこれだけではない。
本番環境のバグ対応を行う、大規模なリファクタリングに取り掛かる、利用ライブラリのメジャーアップデートを行う、など。
これらも定型作業が続くなら、どんどんスキルとして自動化していっても良いのかもしれない。
Discussion