diff --git a/.agents/skills/drive/SKILL.md b/.agents/skills/drive/SKILL.md index e996862..1eb1dfc 100644 --- a/.agents/skills/drive/SKILL.md +++ b/.agents/skills/drive/SKILL.md @@ -1,13 +1,51 @@ --- name: drive -description: Issue から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。Issue 番号またはテキスト指示を受け取る。オプションでマージまで実行可能。 +description: Issue または指示から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。単一/複数の Issue/PR と明示依存記法に対応。オプションでマージまで実行可能。 --- # drive - Issue から merge-ready な PR まで自律駆動 -Issue または指示を受け取り、実装 → ship → セルフレビュー → 修正を自動で繰り返して merge-ready な PR を作成する。オプション指定によりマージまで完結させることが可能。 +Issue または指示を受け取り、実装 → ship → セルフレビュー → 修正を自動で繰り返して merge-ready な PR を作成する。複数 Issue/PR の並列駆動、オプションでマージまで完結させることが可能。 -## ワークフロー +## 入力解析 + +引数を解析する。 + +### target の展開 + +以下の表記をすべて展開して target リストにする: + +- 単一: `#42` / `42` +- カンマ列: `#1,#2` +- 範囲: `#3-5` → `#3, #4, #5` +- 空白列: `#1 #2` +- 混合: `#1,#3-5` +- テキスト指示: 上記いずれにも該当しない場合、指示として扱う(target は単一) + +### 明示依存記法 + +`->` を含む引数は順次依存を表す: + +- `#1,#2 -> #3`: #1 と #2 は並列、#3 は両者の完了後 +- `#1 -> #2 -> #3`: 完全直列 + +### オプション + +- `--merge`: 自動マージを試行する +- `--concurrency N`: 並列度を上書きする(既定 `min(4, タスク数)`、N > 8 は警告のみ) +- `--review=`: review モード(既定 `quick`)。値は次のいずれか: + - `quick`(既定): 全 review pass で quick モード(最大 3 回) + - `final-deep`: quick で最大 2 回 loop し、最終 pass のみ deep に格上げ(quick 2 + deep 1) + - `deep`: 全 pass で deep モード(最大 1 回。コスト爆発防止) + + オーケストレーションモードでは `--review=quick` を強制し、`final-deep` / `deep` 指定時は警告を出して `quick` にフォールバックする。 + +### モード分岐 + +- target が 1 件かつ依存記法なし → **単一モード** +- target が 2 件以上、または依存記法あり → **オーケストレーションモード** + +## 単一モード ### Phase 1: implement @@ -23,31 +61,56 @@ implement スキルのワークフローを実行する。ただし以下の点 ship スキルのワークフロー(lint → commit → PR 作成)を実行する。完了報告・次のアクション確認は無視する。 - PR 番号を記録する(Phase 3 で使用) +- **冪等性:** 既存 PR を検出した場合は resume として扱い、新規作成せず Phase 3 から再開する。判定基準: + - target が PR 番号 → その PR を採用 + - target が issue 番号 → `gh pr list --search "in:body #" --state open` で取得した最新 1 件、または現在のブランチ名と一致する PR を採用 **中断条件:** lint が失敗し、自動修正できない場合 → エラーを報告して中断 -### Phase 3: review loop(最大 3 回) +### Phase 3: review loop(観点別終了基準で判定) + +review skill の観点別 `exit_criteria.drive_loop` を集約して終了判定する。loop 上限は `--review` モードで切替える: -以下のループを最大 3 回繰り返す: +| `--review` | quick の最大回数 | deep の最大回数 | 備考 | +| --- | --- | --- | --- | +| `quick`(既定) | 3 | 0 | 全 review pass で quick | +| `final-deep` | 2 | 1(最終 pass のみ) | quick で loop し、最終 pass のみ deep | +| `deep` | 0 | 1 | 全 pass で deep。コスト爆発防止のため最大 1 回 | -1. **レビュー実行:** review スキルで PR をレビューし、結果を PR コメントとして投稿する +各 pass の手順: + +1. **レビュー実行:** review スキルで PR をレビューし、結果を PR コメントとして投稿する。このとき PR コメント末尾の HTML コメント `` に JSON を埋め込む([ADR-0025](https://github.com/ozzy-labs/handbook/blob/main/adr/0025-skills-review-multi-perspective.md) Schema v1) 2. **判定:** - - Critical または Warning が 0 件 → ループを終了 - - Critical または Warning がある場合 → 修正に進む - - ループ上限(3 回)に到達 → ループを終了(残存指摘を報告に含める) -3. **修正:** Critical および Warning の指摘事項のみを修正する。Info は修正しない(報告のみ)。修正後、lint → commit → push を実行し、1 に戻る + - JSON を解析できる場合 → 観点別 `exit_criteria.drive_loop` を **すべて** 満たすか判定する。`exit_criteria` は対応する `perspectives/.md` の `exit_criteria.drive_loop` を参照する(観点ごとに critical / warning の許容しきい値が異なる) + - すべての適用観点が `exit_criteria` を満たす → ループを終了(merge-ready) + - 1 つでも未達観点がある → 修正に進む + - JSON 解析失敗 / `unknown_review_version` → fail-soft で人間可読部分のみ扱い、Critical または Warning が 0 件かどうかで判定(旧挙動互換) + - ループ上限に到達 → ループを終了(残存指摘を報告に含める) +3. **修正:** 未達観点の Critical および Warning の指摘事項のみを修正する。Info は修正しない(報告のみ)。修正後、lint → commit → push を実行し、1 に戻る + +`--review=final-deep` の場合、最後の pass(quick 上限到達直前または最終 1 回ぶん)のみ deep モードで再 review する。 + +`unknown_review_version` を検出した場合は、JSON を無視して人間可読部分のみで判定し、loop はそのまま継続する(schema bump 後の互換維持)。 + +#### 既存 PR コメントとの resume 互換 + +- 過去の PR コメントに `` が含まれない場合、その PR は **legacy comment** とみなし、新しい review pass を実行する(旧コメントは消さない) +- `` の場合も同様に新規 pass を実行する ### Phase 4: merge (optional) -ユーザーからマージの指示(`--merge` オプション等)がある場合に実行する。 +`--merge` 指定時に実行する。 1. **Auto-merge の有効化:** `gh pr merge --auto --squash --delete-branch` を実行する 2. **成否の確認:** - 成功(Auto-merge がセットされた、または即時マージされた)→ 次へ - - 失敗(Auto-merge がリポジトリで無効など)→ ユーザーに通知し、手動マージを促す(Phase 5 の状態を `merge-ready` にする) -3. **クリーンアップ(即時マージされた場合):** - - ローカルブランチが削除され、ベースブランチ(main等)に切り替わっていることを確認する。 - - ベースブランチで `git pull` を実行し、最新の状態に同期する。 + - 失敗(Auto-merge がリポジトリで無効など)→ ユーザーに通知し、手動マージを促す(状態を `merge-ready` にする) +3. **マージ完了の polling(オーケストレーションから呼ばれた場合のみ):** + - `gh pr view --json mergedAt,state` で mergedAt が立つ、または state が `MERGED` になるまで待つ + - polling 間隔 30 秒、最大 30 分。タイムアウト時は状態を `auto-merge enabled` として終了 +4. **クリーンアップ(即時マージされた場合):** + - ローカルブランチが削除され、ベースブランチ(main 等)に切り替わっていることを確認する + - ベースブランチで `git pull` を実行し、最新の状態に同期する ### Phase 5: 完了報告 @@ -56,13 +119,161 @@ drive 完了: Issue: # ブランチ: <branch-name> PR: <PR URL> - レビュー: N 回実施(Critical: 0, Warning: 0, Info: N) - 状態: <merged | merge-ready | auto-merge enabled> + レビュー: N 回実施 (mode: <quick|final-deep|deep>) + 総計 Critical: 0, Warning: 0, Info: N + by_axis: correctness:C0W0I0 security:C0W0I0 ... + 状態: <merged | merge-ready | auto-merge enabled | failed> +``` + +## オーケストレーションモード + +### Phase 0: 入力展開と DAG 構築 + +1. 引数を target リストに展開する +2. 各 target について GitHub から情報を取得する: + - issue: `gh issue view <N> --json number,title,body` + - PR: `gh pr view <N> --json number,title,body,baseRefName,headRefName` + - issue/PR の判別が曖昧な場合は両方を試し、ヒットした方を採用する +3. DAG を構築する: + - **明示依存記法(最優先・確実):** 引数の `->` から登録 + - **PR base ブランチ照合(確実):** ある PR の baseRefName が同セット内の別 PR の headRefName に一致する場合、stacked PR として依存登録 + - **issue 本文の自動検出(best-effort):** "depends on #X" / "blocked by #X" / "after #X" 等を grep で抽出。表記ゆれや日本語表現で取りこぼしてもエラーにせず並列扱いにフォールバック +4. DAG を wave に分割する(topological levels)。循環依存を検出した場合はエラー報告して中断する +5. wave 構成と target リストを表示する: + +```text +drive 開始: + Targets: #1, #2, #3, #4, #5 + 並列度: 4 (既定: min(4, タスク数)) + --merge: 有効 + Waves: + Wave 1: #1, #2 (並列) + Wave 2: #3 (← #1, #2) + Wave 3: #4, #5 (並列, ← #3) ``` +### Phase 1..N: wave 並列実行 + +wave を順に実行する。 + +#### 並列度 + +- 既定: `min(4, wave 内タスク数)` +- `--concurrency N` で上書き +- N > 8 の場合は警告を表示して続行(ハードキャップなし) + +#### subagent dispatch + +各 target に対し subagent を起動する。同時起動数は並列度まで、空きが出たら次を投入する semaphore 方式。 + +- **隔離:** worktree 隔離で起動する(必須。並列実行時の作業ディレクトリ衝突防止) +- **委譲粒度:** subagent には `.agents/skills/drive/SKILL.md` を Read させ、target #N について単一モードのワークフロー(Phase 1-5)を実行するよう指示する。slash command は subagent からは呼べないため、SKILL.md を直接実行する +- **main への checkout 禁止:** subagent は自 worktree branch で完結する。`git checkout main` / `git switch main` / `git checkout HEAD~` 等で worktree の HEAD を移動させない。worktree は親の Phase Final で削除されるため、main へ戻す必要はない。共有 git directory 経由で親 worktree の `HEAD` / `index` が汚染されるリスクを避けるため、自 branch 以外を触らないこと +- **ベースブランチ:** + - 依存元 wave がない target → main からブランチを作る + - 依存元 wave がある target → 依存元 PR の `headRefName` をベースにブランチを作る(stacked PR)。`--merge` 指定時は依存元がマージ済みのため main をベースにできるが、未指定時はこの stacked 構造が必須 +- **戻り値:** 各 subagent は完了時に以下の JSON を返す + +```json +{ + "target": "#<N>", + "title": "<issue/PR title>", + "branch": "<branch-name>", + "pr_url": "<URL>", + "pr_number": <N>, + "status": "merged" | "merge-ready" | "auto-merge enabled" | "failed", + "review": { + "mode": "quick" | "final-deep" | "deep", + "axes_applied": ["security", "..."], + "by_axis": {"security": {"critical": 0, "warning": 0, "info": 0}, ...}, + "total": {"critical": 0, "warning": 0, "info": 0}, + "iterations": <N> + }, + "error": "<message if failed>" +} +``` + +#### 観測性 + +- `Agent` tool は subagent 完了時に最終結果のみを返すため、ストリーム的な中間報告は不可 +- 親は wave 起動時刻 `<T>` を ISO 8601 で記録し、30 秒間隔で `gh pr list --author @me --state open --search "created:>=<T>" --json number,url,headRefName,title` を polling する +- 既知 PR との差分から新規作成 PR を検出し、URL を即時表示する +- 全 subagent 完了時に最終 JSON 戻り値で状態を確定する + +#### wave 完了待ち + +- すべての subagent が完了した時点で wave 完了 +- `--merge` 指定時、各 subagent は自 PR のマージ完了まで polling して終了するため、wave 完了 = wave 内全 PR のマージ完了 +- `--merge` 未指定時、wave 完了 = wave 内全 PR が merge-ready 以上になった時点。後続 wave は前段 PR の `headRefName` をベースに stacked PR として作成する + +#### 失敗・merge-ready task の処理 + +| 上流の状態 | downstream の扱い | +|---|---| +| merged(`--merge` 指定 + auto-merge 成功) | 進める(`git pull origin main` 後に main ベース) | +| auto-merge enabled(`--merge` + polling タイムアウト等で未マージ) | 進める(前段 PR の headRefName ベースで stacked PR) | +| merge-ready(`--merge` 未指定 / `--merge` 指定 + 残存指摘) | 進める(前段 PR の headRefName ベースで stacked PR) | +| failed | `skipped (upstream failed: #N)` として除外 | + +- 失敗した target は記録する +- 独立した(依存関係のない)他 task には影響させない + +### Phase Final: 集約レポート + +集約レポートを出力する前に、**親 worktree の整合性を確認する**。subagent が共有 git directory 経由で親の `HEAD` / `index` を汚染するケースに備えるための fail-safe([Issue #66](https://github.com/ozzy-labs/skills/issues/66) 由来)。 + +1. `git rev-parse HEAD` と `git rev-parse $(git symbolic-ref HEAD)` が一致するか(HEAD が detached でないこと) +2. `git diff HEAD --stat` が空か(index が HEAD と乖離していないか) +3. `git status --short` が空か(working tree が clean か) +4. 親のベースブランチ(通常 `main`)が `git rev-parse origin/<base-branch>` と一致するか、または `--merge` で merged された PR の SHA を含むか + +いずれかが不一致なら、集約レポート末尾に warning を出す: + +```text +⚠️ Parent worktree drift detected: + HEAD: <sha> (expected branch: <branch>) + index diff: <files> + working tree: <files> + Recovery: + git checkout HEAD -- . + git reset HEAD + # または変更を捨ててよい場合: + git reset --hard origin/main +``` + +整合性チェックが通った場合は、通常どおり集約レポートを出力する: + +```text +drive 完了 (3/5 merged, 1 merge-ready, 1 skipped): + #1 feat: ... | PR #100 | merged + #2 fix: ... | PR #101 | merged (Review: C0 W0 I2) + #3 feat: ... | PR #102 | merge-ready (Review: C0 W1 I0) + #4 chore: ... | skipped (upstream failed: #5) + #5 refactor: ... | failed (test loop) + +集計: + merged: 2 + merge-ready: 1 + skipped: 1 + failed: 1 + 総レビュー反復: 5 回 +``` + +## 失敗 semantics + +| 状況 | 扱い | downstream への影響 | +|---|---|---| +| review loop 上限後も観点別 exit_criteria 未達 | partial success(merge-ready) | 影響なし | +| auto-merge セット失敗(branch protection 等) | failed | skipped | +| implement / ship 中断(テスト失敗等) | failed | skipped | +| 独立 task の失敗 | 他並列 task に影響させない | - | + ## 注意事項 - .env ファイルは読み取り・ステージングしない - `gh` CLI が未認証の場合はエラーメッセージを表示して中断する - マージはデフォルトでは行わない。`--merge` 指定時のみ Auto-merge を試行する - Info 指摘は修正せず報告のみ(設計判断に関わる変更を機械的に行わない) +- オーケストレーションモードでは subagent を必ず worktree 隔離で起動する +- 並列度 8 超過は警告のみ。GitHub Actions 同時実行枠 / API rate limit / 観測性 / コストに注意 +- 循環依存を検出した場合はエラー報告して中断する diff --git a/.agents/skills/health/SKILL.md b/.agents/skills/health/SKILL.md new file mode 100644 index 0000000..a46a3ee --- /dev/null +++ b/.agents/skills/health/SKILL.md @@ -0,0 +1,505 @@ +--- +name: health +description: リポジトリ改修中に意図せず残る状態(working tree, stash, branch, worktree, PR, issue, actions など)と skill catalog 整合性を一発で確認し、16 領域のステータス表で俯瞰しつつ各項目に固定語彙の推奨アクションを inline で付与して報告する。`--deep` 指定時は `要確認` 項目を read-only コマンドで追加調査し、機械判定可能な範囲でラベルを格上げする。検査と提示のみで、削除・close 等の実行は行わない。 +--- + +# health - リポジトリ状態の確認と推奨アクション提示 + +リポジトリ改修中に意図せず残る状態(中断中の git op、未 push commit、stale branch、open PR/issue、failed CI など)と skill catalog の整合性を 16 領域に渡って確認し、各項目に **固定語彙の推奨アクション** を inline で付与して報告する。 + +判断と実行はユーザーが行う。本スキルは検査と提示のみを担当し、削除・drop・prune・close 等は実行しない。 + +## 入力 + +- 引数なし → Phase 1 のみ実行(routine 互換、決定論的) +- `--deep` → Phase 1 完了後、`要確認` フラグが付いた項目に Phase 2 の追加調査を行う(後述) + +`--deep` は明示時のみ有効。routine 経路(`/loop`, `schedule`)でも `--deep` がなければ Phase 1 のみ。 + +## 動作原則 + +- **並列実行:** 全領域のチェックコマンドを **同一メッセージ内の複数 Bash 呼び出し** で並列起動する(直列実行は禁止)。Phase 2 の追加調査も同様に並列起動する +- **per-check error handling:** あるチェックが失敗(gh 未認証、コマンド不在、network エラー等)しても他チェックは継続する。失敗した領域は section 内にエラー行を出力する +- **対話禁止:** AskUserQuestion を使わない +- **実行禁止:** 削除・drop・prune・close 等の解消アクションを実行しない(推奨を表示するのみ)。Phase 2 でも read-only コマンドのみ +- **推奨は固定語彙のみ:** 後述の語彙以外は使わない。Phase 2 でも語彙拡張はしない(既存ラベルへの書き換え or `要確認` 維持 + 根拠付与のみ)。Claude の自由判断で文言を生成しない +- **section 順序固定:** Broken state → Local artifacts → Triage(mine) → Triage(automation) の順で出力する。順序が暗黙の優先度を表現する +- **section 内ソート:** Routine 実行時の差分を安定化するため、各 section で **決定論的な順序** を採用する。具体的には: + - 元コマンドが自然順を返すもの(`git stash list`, `git worktree list`, `git status -s`, `git submodule status`, `git tag -l`)は **元コマンドの順序を維持** + - branch / PR / issue / failed run / draft release は **古い順(最終更新が古いものほど上)** で stale 項目を section 上部に集約する。具体的なソート方法は各 section で指定する(git は `--sort` 等のフラグ、gh は `--json` 結果の client side ソート) + +## 推奨アクション語彙(固定) + +| ラベル | 意味 / 推奨コマンド | 適用条件 | +|---|---|---| +| `delete` | `git branch -d <name>`(safe。force `-D` は推奨しない) | merged PR と紐づく local branch | +| `drop` | `git stash drop` | 紐づく branch なし、または閾値より古い stash | +| `prune` | `git remote prune origin` / `git worktree remove` | gone な tracking ref / orphaned worktree | +| `push` | `git push` | ahead で未 push、PR 未作成 | +| `fetch` | `git fetch --tags` | remote にあって local 未取得の tag | +| `要確認` | 機械判断不能、ユーザー目視 | 古い stash / 古い branch / failed CI run | +| `要対応` | human decision 必要 | open PR / open issue / review request / draft release | +| `abort or continue` | broken state の解消 | MERGE_HEAD / REBASE_HEAD / CHERRY_PICK_HEAD / BISECT_LOG | +| (なし) | 情報のみ表示 | working tree のファイル一覧 / submodule の通常状態 | + +「閾値より古い」の目安は 14 日。 + +## チェック対象(16 領域) + +各領域について「コマンド」「推奨アクションの判定ルール」を定義する。 + +### Broken state + +#### 1. interrupted git ops + +- コマンド: `ls .git/MERGE_HEAD .git/REBASE_HEAD .git/CHERRY_PICK_HEAD .git/BISECT_LOG 2>/dev/null` +- 存在するファイル名を表示し、推奨アクション `abort or continue` を付与する + +#### 2. conflict markers + +- コマンド: `git diff --check` +- 出力されたファイル/行を表示し、推奨アクション `要確認` を付与する + +### Local artifacts + +#### 3. working tree + +- コマンド: `git status -s` +- 出力をそのまま表示する。推奨アクションは付けない(情報のみ) + +#### 4. stash + +- コマンド: `git stash list --format='%gd %ci %gs'` +- 各 stash について経過日数を計算し: + - 元 branch が現存しない → `drop` + - 14 日以上経過 → `要確認` + - それ以外 → 推奨なし + +#### 5. local branch + +- コマンド: `git branch -vv` および `git for-each-ref --sort=committerdate --format='%(refname:short) %(upstream:track) %(committerdate:relative)' refs/heads/`(古い順) +- PR 検出(**1 度だけ batch 取得**): `gh pr list --state all --json number,state,mergedAt,headRefName --limit 100` を 1 回実行し、client side で local branch 名と `headRefName` を join する(branch ごとに gh を呼ばない) +- 各 branch について: + - merged 済みの PR が存在し、かつ merge base 以降に追加 commit が **ない** → `delete`(PR 番号を表示) + - merged 済みの PR が存在し、かつ merge base 以降に追加 commit が **ある** → `要確認`(PR 番号と追加 commit 数を表示。merge 後に作業継続したケース) + - upstream なし、かつ最終 commit から 14 日以上 → `要確認` + - upstream なし、かつ 1 commit 以上、かつ最終 commit から 14 日未満 → `push`(新規ブランチで未 push のケース) + - upstream あり、ahead で未 push、関連 PR なし → `push` + - それ以外 → 推奨なし + +「追加 commit の有無」の判定: PR の merge commit と local branch の `git rev-list --count <merge-commit>..<branch>` を比較し、結果が 0 なら追加なし、1 以上なら追加あり。 + +#### 6. remote tracking + +- コマンド: `git remote prune origin --dry-run` +- 表示された ref を列挙し、推奨アクション `prune` を付与する + +#### 7. worktree + +- コマンド: `git worktree list --porcelain` +- main worktree 以外を列挙する。関連 branch が merged または存在しない → `prune`、それ以外 → 推奨なし + +#### 8. submodule + +- コマンド: `git submodule status` +- submodule がない場合は `(none)` 表示 +- prefix が `+`(uncommitted)/ `-`(uninitialized)/ `U`(merge conflict)の場合は表示し、推奨アクション `要確認` を付与する + +#### 9. tag + +- コマンド: `git ls-remote --tags origin` と `git tag -l` +- local 側にあって remote にない → `push` +- remote 側にあって local にない → `fetch` + +### Triage(mine) + +#### 10. open PR (mine) + +- コマンド: `gh pr list --author @me --state open --json number,title,isDraft,updatedAt` +- **client side で `updatedAt` 昇順にソート**してから表示する(古いほど上) +- 各 PR について: + - draft → `要確認` + - それ以外 → `要対応` + +#### 11. open issue (assigned to me) + +- コマンド: `gh issue list --assignee @me --state open --json number,title,updatedAt` +- **client side で `updatedAt` 昇順にソート**してから表示する(古いほど上) +- 各 issue を表示し、推奨アクション `要対応` を一律付与する。経過日数は表示行の補足情報として含める + +#### 12. review request (waiting on me) + +- コマンド: `gh pr list --search "is:open review-requested:@me" --json number,title,author,updatedAt` +- **client side で `updatedAt` 昇順にソート**してから表示する(古いほど上) +- 各 PR を表示し、推奨アクション `要対応` を付与する + +#### 13. recent failed actions + +- 前提: `git branch --show-current` で現在ブランチを取得する。空文字(detached HEAD)の場合は section に `(skipped: detached HEAD)` を表示し、コマンドを実行しない +- コマンド: `gh run list --branch "<current-branch>" --status failure --limit 5 --json databaseId,name,conclusion,createdAt,url` +- **client side で `createdAt` 昇順にソート**してから表示する(古いほど上) +- 各 run を表示し、推奨アクション `要確認` を付与する + +#### 14. draft release + +- コマンド: `gh release list --limit 20 --json name,tagName,isDraft,createdAt` +- isDraft=true のみ抽出し、**client side で `createdAt` 昇順にソート**してから表示する +- 推奨アクション `要対応` を付与する + +### Triage(automation) + +#### 15. automation PR + +- コマンド: `gh pr list --state open --limit 100 --json number,title,author,updatedAt` +- **client side で author を判別**する(GitHub search の `author:` は OR 不可、AND になるため別アプローチを採る)。`author.login` が下記のパターンに一致するものを抽出: + - `app/renovate` / `renovate[bot]` + - `app/dependabot` / `dependabot[bot]` + - `app/release-please` / `release-please[bot]` + - その他 `*[bot]` または `app/*` 形式の機械作者 +- 抽出後、**`updatedAt` 昇順にソート**してから表示する(古いほど上) +- 各 PR について author 種別と経過日数を表示し、推奨アクション `要対応` を付与する +- 該当なしの場合は section 自体を `(none)` で表示する + +### Skill catalog consistency + +#### 16. perspective MD frontmatter + +review skill が参照する `src/skills/review/perspectives/<axis>.md`([ADR-0025](https://github.com/ozzy-labs/handbook/blob/main/adr/0025-skills-review-multi-perspective.md))の frontmatter スキーマと glob 妥当性を検証する。配信先(`.claude/skills/review/perspectives/` および `.agents/skills/review/perspectives/`)にも同じファイルが揃っているかを確認する。 + +- 検査対象ディレクトリ: + - `src/skills/review/perspectives/<axis>.md`(SSOT) + - `.claude/skills/review/perspectives/<axis>.md`(Claude Code 配信先) + - `.agents/skills/review/perspectives/<axis>.md`(codex / gemini 配信先) +- 検査項目(順に実施): + 1. SSOT に存在する `<axis>.md` のうち `README.md` 以外を列挙する + 2. 各観点 MD の frontmatter に **必須キー** `name`, `category`, `description`, `applies_when`, `default_enabled`, `severity_rules`, `exit_criteria` が揃っているか確認する(`skip_when` は任意) + 3. `name` がファイル名(拡張子なし)と一致するか + 4. `category` が `required` / `design` / `quality` / `ux` のいずれかか + 5. `applies_when` / `skip_when.diff_only_in` の各 glob が文字列として有効か(空でない、`/` ではじまらない、改行を含まない) + 6. `severity_rules` に `critical` / `warning` / `info` の 3 段階がすべて含まれているか + 7. `exit_criteria.drive_loop` に少なくとも `critical` の許容しきい値が含まれているか + 8. SSOT と配信先のファイル一覧が一致しているか(drift があれば `pnpm run build` の取り違えを示唆) +- 推奨アクション: + - 必須キー欠落 / 値不正 → `要確認`(観点 MD の frontmatter を修正) + - SSOT と配信先の drift → `要確認`(`pnpm run build` を実行して再生成) + - すべて整合 → 推奨なし(情報のみ表示) + +新しい観点 MD を追加した直後は **必ず本領域を確認** すること。SSOT のみで配信先に drift がある場合、review skill / `code-reviewer` agent が古い観点定義を読む。 + +## Phase 2: Investigation(`--deep` 時のみ) + +`--deep` 指定時、Phase 1 で `要確認` フラグが付いた項目に対し read-only な追加調査を行い、機械判定可能な範囲でラベルを格上げする。 + +### 起動条件 + +- `--deep` フラグ明示時のみ実行する。デフォルト無効 +- routine 経路(`/loop`, `schedule`)でも `--deep` がなければ起動しない(決定論性維持) +- Phase 1 の出力に `要確認` が 0 件なら自動スキップする + +### 対象範囲 + +Phase 1 で `要確認` が付いた項目のうち、機械判定可能なものに限定する。 + +| 領域 | 調査コマンド(read-only) | ラベル更新ルール | +|---|---|---| +| 4. stash(14d+) | `git stash show -p stash@{N} \| git apply --check`(HEAD への clean apply 可否、exit code 0 = 可、非 0 = 不可) | clean apply 不可 → `drop` に格上げ / 可能 → `要確認` 維持 | +| 5. local branch(upstream なし、14d+) | `git cherry <trunk> <branch>`(trunk への取り込み判定。trunk は `main` を仮定する。`master` 等を使うリポでは将来 `gh repo view --json defaultBranchRef` で動的検出する案あり) | 全 `-` 印(trunk 含み済み)→ `delete` に格上げ / それ以外 → `要確認` 維持 | +| 5. local branch(merged PR + 追加 commit) | `git cherry <trunk> <branch>` 同上 | 全 `-` → `delete` に格上げ / それ以外 → `要確認` 維持 | +| 13. failed CI run | `gh run view <id> --log-failed \| tail -200` | グループに 1 件のみ → `要確認` 維持 + 「root cause: <1 行抜粋>」付与 / グループに 2 件以上 → 各グループ代表 run を `要対応` に格上げ + 「N 件同一エラー」付与、グループ内の他 run は `same as <代表 id>` 表示 | + +### 対象外(Phase 2 でも `要確認` のまま、根拠も付与しない) + +| 領域 | 除外理由 | +|---|---| +| 2. conflict markers | fixture / docs を破壊するリスク。片側採用提案は scope 違反 | +| 8. submodule 異常 | プロジェクト固有の判断必要。一般化した自動提案は誤誘導しやすい | +| triviality / transient 等の主観判定全般 | LLM の苦手領域、誤判定で破壊的提案につながる | + +### 実行ルール + +- **対象絞り込み:** Phase 1 で `要確認` フラグが付いた項目のみ調査する(「最新 N 件」ではない) +- **並列実行:** 全調査を **同一メッセージ内の複数 Bash 呼び出し** で並列起動する +- **CI ログ取得上限:** 詳細ログ取得は **最大 3 件まで**。Phase 1 で取得した failed run リスト(同一 branch、最大 5 件)の先頭 3 件を fetch し、後述の same-error 判定で **グループ化**する。4 件目以降は fetch せず、items 1-3 の代表グループ(最も古い run の id)と同一とみなして `same as <id>` 表示にする(保守的判定。詳細はユーザーに委ねる) +- **ログクリップ:** `gh run view --log-failed | tail -200` で末尾 200 行に制限する +- **same-error 判定:** `gh run view --log-failed` の出力(stdout)から ANSI エスケープシーケンスを除去後、各行を正規表現 `(error|Error|failed)[\s:].*$` でマッチさせる。マッチした行のうち**最後の行のマッチ部分のみ**を比較キーとして抽出する(job 名 / step 名 / タイムスタンプ等の行頭 prefix は対象外)。この文字列をキーとして items 1-3 間で完全一致比較してグループ化する。マッチが 0 件の場合はその run を独立グループ扱いとする(決定論性のため fuzzy matching はしない) +- **連続性の判定スコープ:** 「N 件同一エラー」の N は Phase 1 で取得した failed run リスト内(同一 branch、最大 5 件)でのグループ件数。リストを跨いだ判定はしない + +### 語彙ポリシー + +- 既存固定語彙 8 種から拡張しない(`要対応: rerun` 等の verb 付き形式は採用しない) +- Phase 2 の出力は次の 2 通りのみ: + - **ラベル書き換え:** `要確認` → `delete` / `drop` / `要対応` のいずれか + - **`要確認` 維持 + 根拠付与:** ラベルは変えず、項目末尾に `│ <1 行根拠>` を付与 +- 根拠は各項目の右端に `│ <text>` 形式で 1 行付与する(パイプ文字 `│` は U+2502、矢印 `→` の後ろにスペースを挟んで配置) + +### エラーハンドリング(Phase 2 固有) + +| 状況 | 動作 | +|---|---| +| `git apply --check` 自体が失敗(patch corrupt 等) | 該当 stash は `要確認` 維持 + 根拠 `│ patch unreadable` | +| `gh run view --log-failed` が失敗 | 該当 run は `要確認` 維持 + 根拠 `│ log fetch failed` | +| `git cherry` がエラー(base ref 不在等) | 該当 branch は `要確認` 維持 + 根拠 `│ cherry check failed` | + +Phase 2 のいずれかの調査が失敗しても他の調査は継続する。Phase 1 の出力は影響を受けない(書き換えがなければ Phase 1 ラベルがそのまま残る)。 + +## 明示的に除外する項目 + +| 項目 | 除外理由 | +|---|---| +| lockfile drift | 「意図せず残る」ではなく correctness 問題。lint/test/CI が拾う領域。言語特化 | +| gitignored-but-tracked file | rare すぎてノイズ源 | +| GitHub Actions caches / artifacts | ストレージ管理の領域、leftover 状態とは別概念 | + +## 出力フォーマット + +レポートは 2 ブロック構成: + +1. **ステータス表**(先頭固定。16 領域を 1 表で俯瞰) +2. **非 clean section**(要対応事項のある領域のみ、compact list 形式) + +### ステータス表 + +レポート先頭に必ず 16 行のテーブルを出力する。行順は固定(後述の section 順)で、ステータスアイコンと詳細列で各領域の状態を 1 行ずつ示す。 + +```text +| # | 領域 | 状態 | 詳細 | +|---|---|---|---| +| 1 | Interrupted git ops | <icon> | <detail> | +| 2 | Conflict markers | <icon> | <detail> | +| ... | ... | ... | ... | +| 15 | Automation PRs | <icon> | <detail> | +| 16 | Perspective MD frontmatter | <icon> | <detail> | +``` + +#### ステータスアイコン(固定) + +| アイコン | 意味 | 適用条件 | +|---|---|---| +| `✅` | clean | 項目数 0 | +| `⚠️` | 非 clean | 推奨アクション付き項目が 1 件以上 | +| `❌` | error | 該当 section の取得が失敗 | +| `⏭️` | skipped | 該当 section が条件不成立で skip | + +#### 詳細列(決定論的生成) + +| 状態 | 詳細列の内容 | +|---|---| +| clean | `clean` | +| 非 clean(label 1 種類) | `<count> 件(<label>)` | +| 非 clean(label 複数) | `<count> 件(mixed: <l1>+<l2>+...)` ※ label は推奨アクション語彙の昇順固定(`abort or continue` / `delete` / `drop` / `fetch` / `prune` / `push` / `要対応` / `要確認`) | +| error | `error: <reason>` | +| skipped | `skipped: <reason>` | + +- `<count>` は推奨アクション付き項目の総数。Phase 2 で `same as <id>` に書き換わった行は集約済みのためカウントしない +- 注: H2 section の `(<count>)` は section 内の総項目数(`same as` 行も含む)を示す。表の `<count>` は推奨アクション付き項目のみを数えるため、Phase 2 で `same as` グルーピングが起きた場合のみ両者は異なる(例: Phase 2 で 5 件の failed run が 1 代表 + 4 件の `same as` に整理されたとき、表は `1 件(要対応)`、H2 は `## Recent failed actions (5)`) +- Phase 2 でラベルが書き換わった場合(`要確認` → `drop` など)、表の "詳細" 列も書き換え後ラベルで再計算する + +### 非 clean section + +要対応事項のある領域のみ H2 section として出力する。section 順序は固定: + +1. Interrupted git ops +2. Conflict markers +3. Working tree +4. Stash +5. Local branches +6. Remote tracking +7. Worktrees +8. Submodules +9. Tags +10. My open PRs +11. Issues assigned to me +12. Review requests on me +13. Recent failed actions +14. Draft releases +15. Automation PRs +16. Perspective MD frontmatter + +各 section 内は **compact list 形式** で 1 行 1 項目を出力する: + +```text +## <Section name> (<count>) +<item info> → <label> │ <Phase 2 rationale (--deep 時のみ)> +``` + +- 項目情報と推奨アクションの間は `→` で区切り、矢印の前後に半角スペースを 2 個ずつ挟む(列の整列と視認性のため) +- Phase 2 で根拠が付与された項目は末尾に `│ <text>` を付与する。パイプ `│` の前にスペース 2 個、`<text>` の前にスペース 1 個を挟む。Phase 1 のみの場合は付与しない +- `<count>` は section 内の項目数。section heading に件数のみ付与し、`(N → label)` 形式は採用しない(推奨アクションが mixed の場合に破綻するため) +- 列の整列は agent が項目幅から決める。表形式(markdown table)は項目部分には使わない(cell 幅と inline 根拠が衝突するため) + +エラー section は `(error: <reason>)` を 1 行表示する。skip section は `(skipped: <reason>)` を 1 行表示する。これら error / skipped の section は **H2 section 側にも出力**し、ステータス表とあわせて 2 箇所で示す(表は俯瞰、H2 は 1 行詳細)。clean な領域は H2 section を出さない。 + +全 section が clean な場合は H2 section を一切出力せず、ステータス表のみで完結する。 + +### 出力例(Phase 1 のみ、引数なし) + +```text +| # | 領域 | 状態 | 詳細 | +|---|---|---|---| +| 1 | Interrupted git ops | ✅ | clean | +| 2 | Conflict markers | ✅ | clean | +| 3 | Working tree | ✅ | clean | +| 4 | Stash | ⚠️ | 1 件(要確認) | +| 5 | Local branches | ✅ | clean | +| 6 | Remote tracking | ⚠️ | 5 件(prune) | +| 7 | Worktrees | ✅ | clean | +| 8 | Submodules | ✅ | clean | +| 9 | Tags | ⚠️ | 1 件(push) | +| 10 | My open PRs | ✅ | clean | +| 11 | Issues assigned to me | ✅ | clean | +| 12 | Review requests on me | ✅ | clean | +| 13 | Recent failed actions | ⚠️ | 5 件(要確認) | +| 14 | Draft releases | ✅ | clean | +| 15 | Automation PRs | ⚠️ | 2 件(要対応) | + +## Stash (1) +stash@{0} 18d feat/x WIP → 要確認 + +## Remote tracking (5) +origin/feat/old-1 → prune +origin/feat/old-2 → prune +origin/feat/old-3 → prune +origin/feat/old-4 → prune +origin/feat/old-5 → prune + +## Tags (1) +v0.2.0 local only → push + +## Recent failed actions (5) +24924393951 9d Sync commons → 要確認 +24971520228 7d Sync commons → 要確認 +25274616099 1d Sync commons → 要確認 +25274622229 1d Sync commons → 要確認 +25274636372 1d Sync commons → 要確認 + +## Automation PRs (2) +#39 github-actions[bot] 1d chore: sync commons defaults → 要対応 +#1 github-actions[bot] 0d chore(main): release 0.1.0 → 要対応 +``` + +### 出力例(`--deep` 時、Phase 2 適用後) + +```text +| # | 領域 | 状態 | 詳細 | +|---|---|---|---| +| 1 | Interrupted git ops | ✅ | clean | +| 2 | Conflict markers | ✅ | clean | +| 3 | Working tree | ✅ | clean | +| 4 | Stash | ⚠️ | 1 件(drop) | +| 5 | Local branches | ✅ | clean | +| 6 | Remote tracking | ⚠️ | 5 件(prune) | +| 7 | Worktrees | ✅ | clean | +| 8 | Submodules | ✅ | clean | +| 9 | Tags | ✅ | clean | +| 10 | My open PRs | ✅ | clean | +| 11 | Issues assigned to me | ✅ | clean | +| 12 | Review requests on me | ✅ | clean | +| 13 | Recent failed actions | ⚠️ | 1 件(要対応) | +| 14 | Draft releases | ✅ | clean | +| 15 | Automation PRs | ✅ | clean | + +## Stash (1) +stash@{0} 18d feat/x WIP → drop │ apply --check failed (conflicts with HEAD) + +## Remote tracking (5) +origin/feat/old-1 → prune +origin/feat/old-2 → prune +origin/feat/old-3 → prune +origin/feat/old-4 → prune +origin/feat/old-5 → prune + +## Recent failed actions (5) +24924393951 9d Sync commons → 要対応 │ rsync exit 23 (5 件同一エラー) +24971520228 7d Sync commons → same as 24924393951 +25274616099 1d Sync commons → same as 24924393951 +25274622229 1d Sync commons → same as 24924393951 +25274636372 1d Sync commons → same as 24924393951 +``` + +注: Phase 2 によって stash が `要確認 → drop` に格上げされ、CI failure 5 件が `要確認 → 要対応 (1 件) + same as ... (4 件)` に整理された結果、ステータス表の Stash 詳細は `1 件(drop)`、Recent failed actions は `1 件(要対応)`(`same as` 行はカウント外)になる。 + +### 全 clean な場合の出力例 + +```text +| # | 領域 | 状態 | 詳細 | +|---|---|---|---| +| 1 | Interrupted git ops | ✅ | clean | +| 2 | Conflict markers | ✅ | clean | +| 3 | Working tree | ✅ | clean | +| 4 | Stash | ✅ | clean | +| 5 | Local branches | ✅ | clean | +| 6 | Remote tracking | ✅ | clean | +| 7 | Worktrees | ✅ | clean | +| 8 | Submodules | ✅ | clean | +| 9 | Tags | ✅ | clean | +| 10 | My open PRs | ✅ | clean | +| 11 | Issues assigned to me | ✅ | clean | +| 12 | Review requests on me | ✅ | clean | +| 13 | Recent failed actions | ✅ | clean | +| 14 | Draft releases | ✅ | clean | +| 15 | Automation PRs | ✅ | clean | +``` + +### エラー時の出力例 + +error / skipped は表で `❌` / `⏭️` アイコンと `error:` / `skipped:` 詳細列で示し、H2 section にも 1 行で再掲する: + +```text +| # | 領域 | 状態 | 詳細 | +|---|---|---|---| +| 1 | Interrupted git ops | ✅ | clean | +| 2 | Conflict markers | ✅ | clean | +| 3 | Working tree | ✅ | clean | +| 4 | Stash | ✅ | clean | +| 5 | Local branches | ✅ | clean | +| 6 | Remote tracking | ✅ | clean | +| 7 | Worktrees | ✅ | clean | +| 8 | Submodules | ✅ | clean | +| 9 | Tags | ✅ | clean | +| 10 | My open PRs | ❌ | error: gh not authenticated | +| 11 | Issues assigned to me | ❌ | error: gh not authenticated | +| 12 | Review requests on me | ❌ | error: gh not authenticated | +| 13 | Recent failed actions | ⏭️ | skipped: detached HEAD | +| 14 | Draft releases | ❌ | error: gh not authenticated | +| 15 | Automation PRs | ❌ | error: gh not authenticated | + +## My open PRs +(error: gh not authenticated) + +## Issues assigned to me +(error: gh not authenticated) + +## Review requests on me +(error: gh not authenticated) + +## Recent failed actions +(skipped: detached HEAD) + +## Draft releases +(error: gh not authenticated) + +## Automation PRs +(error: gh not authenticated) +``` + +## エラーハンドリング + +| 状況 | 動作 | +|---|---| +| `gh` コマンド不在 | Triage 系 5 section に `(error: gh not installed)` を表示し、git 系チェックは継続する | +| `gh` 未認証 | Triage 系 5 section に `(error: gh not authenticated)` を表示し、git 系チェックは継続する | +| `git` 個別コマンド失敗 | 該当 section に `(error: <stderr 1 行目>)` を表示し、他 section は継続する | +| GitHub remote なし | Triage 系 5 section に `(error: no GitHub remote)` を表示し、git 系チェックは継続する | +| network エラー | 該当 section に `(error: network)` を表示する | +| detached HEAD | Section 13 に `(skipped: detached HEAD)` を表示し、他 section は継続する | + +全 section の実行は **失敗があっても中断しない**。 + +## 注意事項 + +- `.env` ファイルは読み取らない +- 削除・drop・prune・close 等の解消コマンドは **実行しない**(Phase 1/2 とも推奨表示のみ) +- branch 削除推奨は `git branch -d`(safe)。force delete `-D` は推奨に含めない +- severity ラベル(blocker / warning / info)は付与しない。section 順序が暗黙の優先度を表現する +- 推奨アクション語彙は固定。新規追加は SKILL.md 改訂で行う(Phase 2 でも語彙拡張はしない) +- `--deep` は明示時のみ有効。routine 実行で Phase 2 を走らせたい場合も `--deep` の明示が必須 diff --git a/.agents/skills/phase-issue/SKILL.md b/.agents/skills/phase-issue/SKILL.md new file mode 100644 index 0000000..806bd6f --- /dev/null +++ b/.agents/skills/phase-issue/SKILL.md @@ -0,0 +1,142 @@ +--- +name: phase-issue +description: Phase-N tracking issue を生成する。cross-session handoff context、決定事項表、PR ごとのタスク、DoD、Phase N+1 outlook を含む構造化された issue body を組み立てて gh issue create で起票する。引数で全項目を渡す非対話モードと、不足分を補う対話モード(Claude Code companion)に対応する。 +--- + +# phase-issue - Phase-N tracking issue 生成 + +ozzy-labs で繰り返し発生する Phase-N tracking issue(cross-session handoff context + 決定事項表 + PR ごとのタスク + DoD + Phase N+1 outlook)の構造をハードコードし、引数または対話で集めた内容から決定論的に issue body を組み立てる。`gh issue create --body-file` で起票するか、`--draft` 指定時は stdout に出力する。 + +このスキル単体で起票まで完結する。drive 連携や Phase 番号の自動採番は行わない(スコープ外)。 + +## 入力 + +```text +phase-issue <phase-number> "<title>" + --description "..." (project description; プロジェクト概要) + --refs "owner/repo1,owner/repo2" (参考実装。カンマ区切り) + --donts "..." (やってはいけないこと。改行区切り) + --decisions-file <path> (決定事項 YAML/Markdown ファイル) + --tasks-file <path> (PR ごとのタスクファイル) + --dod "..." (Definition of Done。改行区切り) + --outlook "..." (Phase N+1 outlook) + --related "..." (関連 issue/PR/ADR。改行区切り) + --label "<label>" (issue label。既定: "chore") + --repo "<owner/repo>" (起票先リポ。省略時はカレントリポ) + --draft (起票せず stdout に body を出力) +``` + +### 必須引数 + +- `<phase-number>`: 整数(例: `0`, `1`, `2`) +- `<title>`: 引用符でくくった文字列(例: `"agentic-watch foundation"`) + +### 任意引数の取り扱い(非対話モード) + +canonical SKILL.md は **非対話前提** で動作する。任意引数が不足している場合、対応するセクションは body から **省略**する(プレースホルダーでは埋めない)。「不明分は対話で集めたい」場合は Claude Code companion(`SKILL.claude-code.md`)を使う。 + +`--decisions-file` および `--tasks-file` が指定された場合、ファイル内容をそのまま該当セクションに転記する(解析・整形はしない。ユーザー側で markdown 整形済みであることを期待する)。 + +## ハードコードされた章立て + +issue body は以下の章立てで構築する。**順序は固定**で、変更しない: + +```markdown +# Phase {{N}}: {{title}} + +## Cross-session handoff + +このセクションは新しいセッション/エージェントが本 issue を読んだだけで作業を引き継げるよう、必要な context を集約する。 + +- **プロジェクト概要:** {{description}} +- **参考実装:** {{refs (linked)}} +- **やってはいけないこと:** {{donts (bulleted)}} + +## 決定事項 + +{{decisions-file の内容、または "(TBD)"}} + +## タスク(PR ごと) + +{{tasks-file の内容、または "(TBD)"}} + +## Definition of Done + +{{dod (bulleted)}} + +## Phase {{N+1}} outlook + +{{outlook、または "(未定)"}} + +## 関連 + +{{related (bulleted)}} +``` + +### セクションごとの整形ルール + +- **Cross-session handoff:** + - `--description` がない場合は `(未記入)` ではなく **行ごと省略**(bulleted item を出さない) + - `--refs` はカンマ区切りを bulleted list に展開し、`owner/repo` は `https://github.com/owner/repo` に linkify する + - `--donts` は改行区切りを bulleted list に展開する + - 3 項目すべてが空の場合、`## Cross-session handoff` セクション自体を省略する +- **決定事項 / タスク(PR ごと):** ファイルが指定されない場合、セクション本文を `(TBD)` とする(セクション自体は残す)。Phase issue で **決定事項とタスクは骨格** に当たるため、未記入でもプレースホルダーを残して後追いを促す +- **DoD:** 改行区切りを `- [ ] item` 形式の checkbox list に展開する。空の場合 `(TBD)` とする +- **Phase N+1 outlook:** 文字列をそのまま転記する。空の場合 `(未定)` とする +- **関連:** 改行区切りを bulleted list に展開する。`#N` / `owner/repo#N` / URL いずれも許容(linkify はせず原文を保持)。空の場合セクションを省略する + +### マーカーブロック注記 + +cross-session handoff の冒頭に以下の HTML コメントを必ず埋め込む。これは将来 phase-issue で再生成・更新する際の anchor として機能する: + +```markdown +<!-- phase-issue:v1 phase=N --> +``` + +`v1` は本スキルが生成する body のフォーマットバージョン。schema を変える場合は bump する。 + +## 手順 + +### 1. 引数解析 + +1. `<phase-number>` と `<title>` を取得する。どちらも必須。欠落時はエラーを表示して中断する +2. オプションを解析する。同じオプションが複数回指定された場合は最後のものを採用する +3. `--decisions-file` / `--tasks-file` が指定された場合、ファイルの存在と読み取り可否を確認する。失敗時はエラーを表示して中断する + +### 2. body 組み立て + +1. ハードコードされた章立てに従って body を組み立てる +2. プレースホルダー `{{N}}` / `{{title}}` / `{{N+1}}` を実値で置換する +3. 各セクションの整形ルールに従い、空セクションは省略 / TBD / 未定 を適切に出し分ける +4. マーカーブロックを cross-session handoff の冒頭に挿入する + +### 3. 起票または stdout 出力 + +- `--draft` 指定時: + - body を stdout に出力する + - `gh` コマンドは呼び出さない +- `--draft` 未指定時: + - body を一時ファイルに書き出す + - `gh issue create --title "Phase {{N}}: {{title}}" --label "<label>" --body-file <tmp>` を実行する + - `--repo` が指定されていれば `--repo` 引数を gh に渡す + - 起票成功時は issue URL を表示する + +### 4. 完了報告 + +```text +phase-issue 完了: + タイトル: Phase <N>: <title> + 起票先: <repo> + Issue: <URL> (--draft 指定時は "(stdout に出力)") +``` + +## 注意事項 + +- 引数で渡されない任意項目は body から省略するか TBD で埋める。Claude が想像で内容を補完しない +- `gh` CLI が未認証の場合はエラーメッセージを表示して中断する +- `--draft` モードでは外部副作用なし(ファイル書き込みも `gh` 呼び出しもない) +- title は double quote でくくる前提。コマンド側のシェルエスケープは呼び出し元の責任 +- canonical SKILL.md は非対話前提。対話的に項目を集めたい場合は Claude Code companion を使う +- **過去 issue からの style 学習はしない**: 章立ては SKILL.md 内に固定する(学習機構は実装複雑度に対し対価が小さい) +- **Phase 番号の自動採番はしない**: `<phase-number>` は呼び出し元が明示する +- **drive 連携はしない**: phase-issue は起票で完結する。生成された issue の分割・実装は別途 drive で回す diff --git a/.agents/skills/review/SKILL.md b/.agents/skills/review/SKILL.md index 4c90597..fef09be 100644 --- a/.agents/skills/review/SKILL.md +++ b/.agents/skills/review/SKILL.md @@ -1,11 +1,11 @@ --- name: review -description: コード変更や PR をレビューし、問題点・改善案を報告する。PR 番号または空(ワーキングツリー)を受け取る。 +description: コード変更や PR を 11 観点(perspectives)でレビューし、JSON 構造化出力 + 人間可読レポートで報告する。quick / deep モードを切替可能。PR 番号またはワーキングツリー差分を入力に取る。 --- -# review - コードレビュー +# review - 多観点コードレビュー -コードの変更や PR をレビューし、問題点・改善案を報告する。 +差分を 11 観点(perspectives)でレビューし、Critical / Warning / Info に分類して JSON + 人間可読レポートで報告する。[ADR-0025](https://github.com/ozzy-labs/handbook/blob/main/adr/0025-skills-review-multi-perspective.md) の hybrid 方式(quick: 単一エージェント / deep: 観点並列サブエージェント)を採用する。 ## 入力 @@ -17,50 +17,211 @@ description: コード変更や PR をレビューし、問題点・改善案を - 変更がなければ `git diff main...HEAD` でブランチ差分を取得 - それでも変更がなければ、レビュー対象がない旨を伝えて終了する +## オプション + +- `--axes=<axis,...>`: 適用観点を明示指定(自動選別を上書き、`default_enabled: false` 観点も明示時のみ有効化) +- `--deep`: deep モードで実行(観点ごとにサブエージェント並列起動。Claude Code 環境のみ。他アダプタでは quick にフォールバック) + +## 観点(11 軸) + +観点定義は `perspectives/<axis>.md` を SSOT とする。frontmatter で `category` / `applies_when` / `skip_when` / `default_enabled` / 検査項目 / severity ガイド / `exit_criteria.drive_loop` を宣言する。 + +| category | axis | 既定 | +| --- | --- | --- | +| required | correctness, security, conventions | 常に適用 | +| design | architecture, compatibility, maintainability | applies_when マッチ時 | +| quality | testing, performance, observability | applies_when マッチ時 | +| ux | usability, documentation | applies_when マッチ時 | + +### 観点選別ロジック + +`--axes` 未指定時は以下の順で適用観点を決定する: + +1. `category: required` → 常に適用(`applies_when` / `skip_when` を無視) +2. `default_enabled: false` → `--axes` で明示指定された場合のみ適用(experimental 観点用、現状なし) +3. `skip_when.diff_only_in` がマッチ(全変更ファイルが指定 glob 集合の部分集合)→ 不適用(最優先のスキップ条件) +4. `applies_when` のいずれかの glob にマッチ → 適用(OR) +5. それ以外 → 不適用 + +`--axes=security,architecture` のように明示指定した場合は、その観点のみを適用する(`applies_when` / `skip_when` を無視)。 + +`skip_when` でサポートする初期キーは `diff_only_in` のみ。未定義キーは reader が無視する(forward-compat)。 + ## 手順 -### 1. コンテキスト収集 +### 1. 適用観点の決定 -差分に含まれるファイルの周辺コードを確認し、変更の意図と影響範囲を把握する: +1. 差分から変更ファイル一覧を抽出する +2. `perspectives/` 配下の全観点 MD を Read し、frontmatter を解釈する +3. 上記の観点選別ロジックで適用観点リストを決定する +4. 適用観点リストを最初に表示する: -- 変更されたコンポーネント・ページの全体像を確認 -- インポート元やエクスポート先を確認 -- 変更に関連するドキュメントファイルを特定する +```text +適用観点 (5/11): + required: correctness, security, conventions + design: architecture + ux: documentation +``` ### 2. レビュー実施 -以下の観点で差分を分析する: +#### quick モード(デフォルト) -- **正確性:** ロジックの誤り、エッジケース -- **セキュリティ:** インジェクション、機密情報の露出 -- **パフォーマンス:** 不要なレンダリング、画像最適化 -- **コーディング規約:** コーディング規約との整合性 -- **ドキュメント整合性:** コード変更がドキュメントに正しく反映されているか +単一エージェントが適用観点を順に走査する。各観点について: -### 3. レポート出力 +1. 該当 `perspectives/<axis>.md` を Read(既に読み込み済みの場合は省略) +2. 検査項目・severity ガイドに従って差分をレビュー +3. findings を内部 JSON バッファに追加 -3 段階の深刻度で分類する: +すべての観点を走査し終えたら集約に進む。 -- **Critical** — 修正が必要(バグ、セキュリティ脆弱性) -- **Warning** — 修正を推奨(パフォーマンス問題、規約違反) -- **Info** — 任意の改善提案(リファクタリング、可読性向上) +#### deep モード(`--deep`) -各指摘には以下を含める: +観点ごとに `Agent({subagent_type: "code-reviewer"})` を **並列起動** する。各 subagent は対応する `perspectives/<axis>.md` を Read し、JSON で findings を返す。 + +呼び出しプロンプトの形式: ```text -[Critical] ファイル名:行番号 - 問題: <問題の説明> - 理由: <なぜ問題なのか> - 提案: <具体的な修正案> +axis: <axis-name> +mode: deep +context: + base: <base-ref> + head: <head-ref> + pr_number: <N (optional)> + +<diff> ``` -レポート末尾にサマリーを記載: +並列起動は **1 メッセージ複数 tool call** で行う。subagent からは slash command を呼べないため、観点 MD の Read と JSON 出力のみで完結させる。 + +deep モードは Claude Code 環境のみで利用可能。他アダプタ(codex-cli / gemini-cli)では quick にフォールバックする(adapter 別の挙動は `SKILL.<adapter>.md` で吸収)。 + +### 3. 集約 + +#### 重複統合 + +- 同一 `file:line` に複数観点が同じ issue を出した場合、1 件に統合し `axes` を併記する +- 同一 `file:line` で観点が異なるが issue 文言が同じ場合(例: security と correctness が両方「未サニタイズ入力」を指摘)も統合対象 + +#### 観点間衝突 + +- 原理的トレードオフ(例: security ↔ DX、observability ↔ performance)は **conflicts セクション** に別記し、severity を付けず判断委ねとする + +#### グルーピング + +レポートは「観点 → severity → ファイル」順でグループ化する。 + +### 4. JSON 構造化出力 + +内部表現は **必ず JSON**。findings はすべて JSON で保持し、PR コメント・標準出力は JSON からレンダラを通して人間可読フォーマットに変換する。 + +#### Schema (version "1") + +```json +{ + "version": "1", + "mode": "quick" | "deep", + "axes_applied": ["security", "correctness", "..."], + "findings": [ + { + "axis": "security", + "severity": "critical" | "warning" | "info", + "file": "src/x.ts", + "line": 42, + "issue": "...", + "why": "...", + "suggestion": "...", + "axes_merged": ["security", "correctness"] + } + ], + "conflicts": [ + { + "axes": ["security", "usability"], + "file": "src/y.ts", + "line": 10, + "description": "..." + } + ], + "summary": { + "by_axis": { + "security": {"critical": 0, "warning": 1, "info": 0} + }, + "total": {"critical": 0, "warning": 1, "info": 3} + } +} +``` + +`axes_merged` と `conflicts` は任意フィールド(重複統合・観点間衝突がある場合のみ)。 + +#### Version migration policy + +- `version` は単調増加の整数(文字列)。本 ADR で `"1"` を確立する +- reader 側(drive など)は `version` が現状コードの上限と一致する場合のみ機械判定を行う +- 未対応 version の場合は `unknown_review_version` として fail-soft 終了し、JSON を無視して人間可読部分のみ扱う +- 互換破壊変更は `version` を bump し、reader は最低 N-1 まで読める実装を維持する + +### 5. レポート出力 + +#### 人間可読フォーマット ```text -レビュー結果: +レビュー結果 (mode: <quick|deep>, axes: <list>): + +## correctness +[Critical] src/foo.ts:42 + 問題: <issue> + 理由: <why> + 提案: <suggestion> + +## security +[Warning] .github/workflows/release.yaml:10 + 問題: ... + +... + +## conflicts +[security ↔ usability] src/y.ts:10 + <description> + +## サマリー Critical: N 件 Warning: N 件 Info: N 件 + + by_axis: + correctness: C0 W0 I1 + security: C0 W1 I0 + ... +``` + +#### PR コメントへの埋め込み(PR レビュー時) + +人間可読レポートの末尾に JSON を **HTML コメント** として埋め込む(drive がこれを再読する): + +```markdown +... 人間可読レポート ... + +<!-- review-json:v1 +{ + "version": "1", + ... +} +--> ``` PR レビューの場合、レポート全体を `gh pr comment <N> --body "<レポート>"` で PR にコメントとして投稿する。 + +### 6. 過去 PR コメントとの互換(resume) + +drive が過去に投稿したコメントには JSON 埋込みがない場合がある。reader は次のように扱う: + +- `<!-- review-json:v1 ... -->` を含むコメント → JSON を解析して機械判定 +- JSON 不在のコメント → **legacy comment** として扱い、新規 review pass の trigger として無視する(過去コメントは消さない) +- `<!-- review-json:v<unknown> ... -->` → `unknown_review_version` として無視(fail-soft) + +## 注意事項 + +- `Critical` を 1 件でも報告する場合は明確な悪影響(バグ・脆弱性等)の根拠を示す +- 観点の severity 判定は対応する `perspectives/<axis>.md` の severity ガイドに従う。観点を超えて勝手に重要度を上げ下げしない +- deep モードは観点数 × 並列度ぶんのトークンを消費する。drive のオーケストレーションモードでは強制的に quick にフォールバック(コスト管理) +- `Info` は提案のみ。drive の review loop では `Info` を修正対象としない diff --git a/.agents/skills/review/perspectives/README.md b/.agents/skills/review/perspectives/README.md new file mode 100644 index 0000000..bb59293 --- /dev/null +++ b/.agents/skills/review/perspectives/README.md @@ -0,0 +1,59 @@ +--- +name: perspectives-index +description: review skill が参照する観点定義のインデックスとスキーマガイド。 +--- + +# review perspectives + +review skill / `code-reviewer` agent が参照する観点定義の SSOT([ADR-0025](https://github.com/ozzy-labs/handbook/blob/main/adr/0025-skills-review-multi-perspective.md))。各 `<axis>.md` がレビュー 1 観点を表し、frontmatter に観点メタデータ、本文に検査項目・severity ガイド・終了基準を持つ。 + +## 採用観点(11 軸) + +| category | axis | 既定 | +| --- | --- | --- | +| required | [correctness](./correctness.md) | 常に適用 | +| required | [security](./security.md) | 常に適用 | +| required | [conventions](./conventions.md) | 常に適用 | +| design | [architecture](./architecture.md) | applies_when マッチ時 | +| design | [compatibility](./compatibility.md) | applies_when マッチ時 | +| design | [maintainability](./maintainability.md) | applies_when マッチ時 | +| quality | [testing](./testing.md) | applies_when マッチ時 | +| quality | [performance](./performance.md) | applies_when マッチ時 | +| quality | [observability](./observability.md) | applies_when マッチ時 | +| ux | [usability](./usability.md) | applies_when マッチ時、consumer が opt-out 可 | +| ux | [documentation](./documentation.md) | 常に適用 | + +## frontmatter スキーマ + +```yaml +--- +name: <axis> # ファイル名と一致させる +category: required | design | quality | ux +description: <一行で観点の主旨> +applies_when: ["<glob>", ...] # diff にこの glob にマッチするファイルが含まれれば適用 +skip_when: { diff_only_in: ["<glob>", ...] } # 全変更ファイルがこの glob 部分集合なら不適用 +default_enabled: true | false # false の場合は --axes 明示時のみ適用 +severity_rules: { critical: "<...>", warning: "<...>", info: "<...>" } +exit_criteria: { drive_loop: { critical: <N>, warning: <N> } } # warning キーは省略可(許容を意味する) +--- +``` + +`skip_when` / `severity_rules` / `exit_criteria` は flow-style YAML の 1 行で記述する(既存の flat frontmatter parser と整合させるため)。本文には人間可読な severity ガイドや検査項目を冗長に記述してよいが、機械処理の SSOT は frontmatter とする。 + +未定義キーは reader が無視する(forward-compat)。互換破壊的なスキーマ変更(必須キーの削除等)は ADR を起こす。 + +## 観点選別ロジック + +review skill / `code-reviewer` agent は以下の順で適用観点を決定する: + +1. `category: required` → 常に適用(`applies_when` / `skip_when` を無視) +2. `default_enabled: false` → `--axes` で明示指定された場合のみ適用(experimental 用) +3. `skip_when.diff_only_in` がマッチ → 不適用(最優先のスキップ条件) +4. `applies_when` のいずれかの glob にマッチ → 適用(OR) +5. それ以外 → 不適用 + +## 観点 MD の追加・変更 + +新しい観点を追加する場合、本ディレクトリに `<axis>.md` を作成して frontmatter スキーマに従い記述する。観点 MD の lint(frontmatter 必須キー検証、`applies_when` / `skip_when` の glob 妥当性)は `health` skill で実施する。 + +互換破壊的なスキーマ変更(必須キーの削除等)を行う場合は ADR を起こす。`code-reviewer` agent と review skill 双方の reader が後方互換に追随できるよう、新キーは optional として導入する。 diff --git a/.agents/skills/review/perspectives/architecture.md b/.agents/skills/review/perspectives/architecture.md new file mode 100644 index 0000000..be4cd8b --- /dev/null +++ b/.agents/skills/review/perspectives/architecture.md @@ -0,0 +1,43 @@ +--- +name: architecture +category: design +description: レイヤリング・責務配置・抽象度・既存パターン整合 +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "既存アーキテクチャ判断 (ADR 等) に明確に反する、取り返しがつかない構造変更", warning: "責務違反、循環依存、既存パターンを破る無理筋、保守性を著しく下げる構造", info: "より良い分離・命名・抽象度の提案、リファクタリング候補" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# architecture — アーキテクチャ + +## 検査項目 + +- **レイヤリング**: 上位層が下位層を参照しているか、循環依存がないか +- **責務配置**: 1 モジュール / 関数の責務が明確で過剰膨張していないか +- **抽象度**: 適切な抽象化レベル、不要な抽象 / 早期最適化、漏れている抽象化 +- **既存パターンとの整合**: adapter / skill / agent などのリポジトリ既存パターンに沿っているか、独自パターン乱立していないか +- **データフロー**: 入出力の方向、グローバル状態の混入、純粋性の意図的な維持 +- **拡張性**: 将来の差し込みポイント、過剰な extension point の設置(YAGNI) +- **境界の明示**: モジュール / パッケージ / 内部 API / 外部 API の境界が明示されているか + +## severity ガイド + +- **critical**: 既存アーキテクチャ判断(ADR 等)に明確に反する、取り返しがつかない構造変更 +- **warning**: 責務違反、循環依存、既存パターンを破る無理筋、保守性を著しく下げる構造 +- **info**: より良い分離・命名・抽象度の提案、リファクタリング候補 + +## skip_when + +ドキュメント・設定ファイルのみの変更ではアーキテクチャ観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +設計レベルの critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.agents/skills/review/perspectives/compatibility.md b/.agents/skills/review/perspectives/compatibility.md new file mode 100644 index 0000000..b5117ad --- /dev/null +++ b/.agents/skills/review/perspectives/compatibility.md @@ -0,0 +1,43 @@ +--- +name: compatibility +category: design +description: 後方互換、スキーマ変更、commons-sync 経由のコンシューマ影響 +applies_when: ["src/**", "scripts/**", "dist/**", "package.json", "**/*.json", "**/*.yaml", "**/*.yml", "**/SKILL.md"] +skip_when: { diff_only_in: ["tests/**", "**/*.test.*"] } +default_enabled: true +severity_rules: { critical: "既存コンシューマが破壊される非互換変更 (rename / 削除 / 型変更) で migration path や CHANGELOG への記載がない", warning: "互換性は保てるがコンシューマ側で対応が必要、または default 変更で挙動が変わる", info: "より丁寧な deprecation の提案、注意喚起" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# compatibility — 互換性 + +## 検査項目 + +- **後方互換**: 公開 API / CLI フラグ / skill 引数の削除・改名、デフォルト値変更による既存利用者への影響 +- **スキーマ変更**: SKILL.md frontmatter / 設定ファイル / JSON schema のフィールド削除・型変更 +- **commons-sync 経由のコンシューマ影響**: `dist/` 出力構造の変更がコンシューマの sync で破壊的にならないか +- **agent / adapter API**: `AdapterBase.generate()` シグネチャ変更、`Skill` 型の必須フィールド追加 +- **package.json**: dependency / engines の互換性、major bump +- **legacy resume 互換**: 既存 PR コメント・既存 lock / state ファイルなどの読み取り互換 +- **version migration**: schema version をインクリメントする変更で reader 側のフォールバック処理を提供しているか + +## severity ガイド + +- **critical**: 既存コンシューマが破壊される非互換変更(rename / 削除 / 型変更)で migration path や CHANGELOG への記載がない +- **warning**: 互換性は保てるがコンシューマ側で対応が必要、または default 変更で挙動が変わる +- **info**: より丁寧な deprecation の提案、注意喚起 + +## skip_when + +テストのみの変更では互換性観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +非互換変更が未対処のまま merge-ready とは判定しない。 diff --git a/.agents/skills/review/perspectives/conventions.md b/.agents/skills/review/perspectives/conventions.md new file mode 100644 index 0000000..628e272 --- /dev/null +++ b/.agents/skills/review/perspectives/conventions.md @@ -0,0 +1,48 @@ +--- +name: conventions +category: required +description: Conventional Commits、lint、ファイル命名、`.yaml` 統一などのリポジトリ規約 +applies_when: ["**/*"] +default_enabled: true +severity_rules: { critical: "コミット / PR タイトルが Conventional Commits 違反 (commitlint で fail)、main への直接 push、--no-verify 利用", warning: "lint / formatter 違反、命名規約違反、.yaml / .yml 不整合", info: "より明示的な書き方への改善提案、命名の細かな統一" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# conventions — コーディング規約 + +## 検査項目 + +- **Conventional Commits**: type / scope / description の形式、`!` による破壊的変更マーキング +- **ブランチ命名**: `<type>/<short-description>` 形式 +- **ファイル命名・配置**: 既存パターンとの整合(`SKILL.md` / `SKILL.<adapter>.md`、`perspectives/<axis>.md` など) +- **YAML 拡張子**: `.yaml` に統一されているか(ツールが `.yml` を要求する場合のみ許容) +- **lint / formatter**: biome / markdownlint / yamllint / shellcheck / shfmt 等の出力に違反していないか +- **import / export 規約**: ESM / CJS の統一、明示的なファイル拡張子 +- **CLAUDE.md / AGENTS.md**: 記載されたプロジェクトルールに違反していないか +- **言語別規約**: tools/lint-rules.md などで定義された言語固有の規約 + +## severity ガイド + +- **critical**: コミット / PR タイトルが Conventional Commits 違反(commitlint で fail)、`main` への直接 push、`--no-verify` 利用 +- **warning**: lint / formatter 違反、命名規約違反、`.yaml` / `.yml` 不整合 +- **info**: より明示的な書き方への改善提案、命名の細かな統一 + +## skip_when + +```yaml +skip_when: + diff_only_in: [] +``` + +required 観点のため常に適用する。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +規約違反が残っている状態で merge-ready とは判定しない。 diff --git a/.agents/skills/review/perspectives/correctness.md b/.agents/skills/review/perspectives/correctness.md new file mode 100644 index 0000000..b9ecdf4 --- /dev/null +++ b/.agents/skills/review/perspectives/correctness.md @@ -0,0 +1,41 @@ +--- +name: correctness +category: required +description: ロジック誤り・エッジケース・並行性・エラーハンドリング +applies_when: ["**/*"] +default_enabled: true +severity_rules: { critical: "悪用・データ破壊・誤った状態遷移・無限ループ・回帰の温床になり得るバグ", warning: "通常パスは動くが特定入力で破綻するケース、未処理の例外", info: "防御的コーディングの改善余地、より堅牢な書き方の提案" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# correctness — 正確性 + +## 検査項目 + +- **ロジック誤り**: 条件分岐の取りこぼし、off-by-one、boolean 反転、誤った演算子 +- **エッジケース**: 空入力、null / undefined、最大/最小値、空配列・空文字列、Unicode、改行 +- **並行性**: race condition、競合する書き込み、shared state の取り扱い、await 漏れ、Promise の handling +- **エラーハンドリング**: 握りつぶし(catch して何もしない)、誤った再 throw、エラー型の取り違え、finally の副作用 +- **戻り値・副作用**: 関数が宣言した契約を満たしているか、未文書化の副作用がないか +- **型の整合**: `as` キャストや `any` 経由で実行時に破綻するパスがないか + +## severity ガイド + +- **critical**: 悪用・データ破壊・誤った状態遷移・無限ループ・回帰の温床になり得るバグ +- **warning**: 通常パスは動くが特定入力で破綻するケース、未処理の例外 +- **info**: 防御的コーディングの改善余地、より堅牢な書き方の提案 + +## skip_when + +なし(required 観点は常に適用)。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +正確性に関する critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.agents/skills/review/perspectives/documentation.md b/.agents/skills/review/perspectives/documentation.md new file mode 100644 index 0000000..c09bdd6 --- /dev/null +++ b/.agents/skills/review/perspectives/documentation.md @@ -0,0 +1,43 @@ +--- +name: documentation +category: ux +description: README・AGENTS.md・CLAUDE.md・SKILL.md・公開挙動の同期 +applies_when: ["**/*"] +skip_when: { diff_only_in: [] } +default_enabled: true +severity_rules: { critical: "公開 API / 公開 CLI の変更がドキュメントに一切反映されておらず利用者が破壊される", warning: "README / SKILL.md / AGENTS.md の陳腐化、ADR と実装の乖離、誤ったサンプル", info: "ドキュメント追記の提案、説明の改善、表記揺れの統一" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# documentation — ドキュメント整合性 + +## 検査項目 + +- **README**: 公開挙動の変更がドキュメントに反映されているか、サンプル / 使用例の陳腐化 +- **AGENTS.md / CLAUDE.md**: skill / agent の追加・改名・削除が反映されているか +- **SKILL.md**: frontmatter の `description` / `argument-hint` がコード実装と一致しているか +- **CHANGELOG**: 互換性に影響する変更が記録されているか(release-please 自動生成範囲を除く) +- **コメント / docstring**: 実装変更に追従しているか、誤った説明が残っていないか +- **ADR との整合**: ADR で決定された方針と実装が一致しているか、参照する ADR 番号が正確か +- **public な関数 / API のドキュメント**: 入力 / 出力 / 例外契約が記載されているか + +## severity ガイド + +- **critical**: 公開 API / 公開 CLI の変更がドキュメントに一切反映されておらず利用者が破壊される +- **warning**: README / SKILL.md / AGENTS.md の陳腐化、ADR と実装の乖離、誤ったサンプル +- **info**: ドキュメント追記の提案、説明の改善、表記揺れの統一 + +## skip_when + +ドキュメント整合性は全変更で適用する(コード変更にもドキュメント変更にも検査対象がある)。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +ドキュメント整合性の critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.agents/skills/review/perspectives/maintainability.md b/.agents/skills/review/perspectives/maintainability.md new file mode 100644 index 0000000..bf0e81b --- /dev/null +++ b/.agents/skills/review/perspectives/maintainability.md @@ -0,0 +1,42 @@ +--- +name: maintainability +category: design +description: 命名・複雑度・dead code・コメント負債 +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "取り返しのつかない命名 / 構造の選択 (公開 API として固定される名称等)", warning: "顕著な dead code、過剰な複雑度、誤解を招く命名、明らかな重複", info: "命名の細かな改善、コメント整理、軽微なリファクタ提案" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# maintainability — 保守性 + +## 検査項目 + +- **命名**: 識別子の意図が伝わるか、誤解を招く命名がないか、不要な略語 +- **複雑度**: 関数 / メソッドが過剰に長い、ネストが深すぎる、分岐爆発、cyclomatic complexity +- **dead code**: 未使用の export / 関数 / 変数 / import、コメントアウトされたコード +- **コメント負債**: WHAT を説明する冗長コメント、陳腐化したコメント、TODO / FIXME の放置 +- **重複**: 3 箇所以上に出現するロジックや、コピー&ペーストの兆候 +- **テスト容易性**: 過剰な隠蔽、副作用に依存した設計、テストしにくい依存注入 +- **ドキュメント**: 公開 API / skill / agent に最低限の説明があるか + +## severity ガイド + +- **critical**: 取り返しのつかない命名 / 構造の選択(公開 API として固定される名称等) +- **warning**: 顕著な dead code、過剰な複雑度、誤解を招く命名、明らかな重複 +- **info**: 命名の細かな改善、コメント整理、軽微なリファクタ提案 + +## skip_when + +ドキュメント・設定ファイルのみの変更では保守性観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +保守性の warning は許容(merge 後の継続改善対象)。critical は阻止する。 diff --git a/.agents/skills/review/perspectives/observability.md b/.agents/skills/review/perspectives/observability.md new file mode 100644 index 0000000..13a232d --- /dev/null +++ b/.agents/skills/review/perspectives/observability.md @@ -0,0 +1,42 @@ +--- +name: observability +category: quality +description: エラー文脈・ログ・失敗時の手がかり +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "tests/**", "**/*.test.*", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "失敗時に何も情報が出ず原因特定不可能、本番で silent failure になる経路", warning: "エラーメッセージが薄い、ログ過剰 / 不足、エラー種別の取り違え", info: "より親切なメッセージ、log level の調整提案" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# observability — 可観測性 + +## 検査項目 + +- **エラー文脈**: throw する error に十分な手がかり(入力値・どのファイル・どの操作)が含まれているか +- **ログ**: 失敗時に何が起きたか追える程度のログがあるか、過剰な debug ログを残していないか +- **エラーメッセージの粒度**: ユーザに表示する文言が actionable か(次の手が示唆されるか) +- **失敗の伝播**: 上位がエラー種別で分岐できる構造か、error code / typed error の活用 +- **副作用の追跡可能性**: 外部システム(GitHub API / fs / network)呼び出しの成否が可視化されているか +- **シークレット漏洩防止**: ログに secret が出ていないか +- **CI / Hook の失敗表示**: lefthook / hook の失敗時に原因がすぐ分かる出力か + +## severity ガイド + +- **critical**: 失敗時に何も情報が出ず原因特定不可能、本番で silent failure になる経路 +- **warning**: エラーメッセージが薄い、ログ過剰 / 不足、エラー種別の取り違え +- **info**: より親切なメッセージ、log level の調整提案 + +## skip_when + +テスト・ドキュメント・設定のみの変更では可観測性観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +可観測性の warning は許容。critical は阻止する。 diff --git a/.agents/skills/review/perspectives/performance.md b/.agents/skills/review/perspectives/performance.md new file mode 100644 index 0000000..7f7d72d --- /dev/null +++ b/.agents/skills/review/perspectives/performance.md @@ -0,0 +1,42 @@ +--- +name: performance +category: quality +description: hot path、不要な逐次 I/O、メモリ +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "tests/**", "**/*.test.*", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "顕著な性能退行、production で UX を損なう規模のリグレッション、無限ループ", warning: "hot path 上の非効率、逐次 I/O、明らかな再計算", info: "軽微な最適化提案、cache 化候補" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# performance — パフォーマンス + +## 検査項目 + +- **hot path**: 高頻度呼び出しパスにおける O(n²) 以上の処理、ネストループ、不要な allocation +- **逐次 I/O**: 並列化可能な独立 I/O を直列で実行していないか(`for await` 直列化、Promise.all 化漏れ) +- **メモリ**: 不要に巨大な中間配列、ストリーム化できる処理の一括読込、リーク要因(hold する closure) +- **不要なレンダリング / 再計算**: memo / cache を活用すべき箇所、毎回計算される定数 +- **ファイル I/O**: 同一ファイルの重複読込、無駄な fs stat、巨大ファイルの全読み +- **HTTP / fetch**: タイムアウト未設定、N+1 リクエスト、過度な polling 間隔 +- **依存の影響**: 重量級ライブラリの導入、tree-shaking 効きにくい構造 + +## severity ガイド + +- **critical**: 顕著な性能退行、production で UX を損なう規模のリグレッション、無限ループ +- **warning**: hot path 上の非効率、逐次 I/O、明らかな再計算 +- **info**: 軽微な最適化提案、cache 化候補 + +## skip_when + +テスト・ドキュメント・設定のみの変更ではパフォーマンス観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +性能 warning は許容(merge 後の継続改善対象)。critical は阻止する。 diff --git a/.agents/skills/review/perspectives/security.md b/.agents/skills/review/perspectives/security.md new file mode 100644 index 0000000..aac18f2 --- /dev/null +++ b/.agents/skills/review/perspectives/security.md @@ -0,0 +1,47 @@ +--- +name: security +category: required +description: 注入・秘密情報露出・権限昇格・サプライチェーン・スクリプト実行 +applies_when: ["**/*"] +default_enabled: true +severity_rules: { critical: "悪用可能な脆弱性、明示的な秘密情報の commit、認証回避、任意コード実行", warning: "防御層の欠落、未サニタイズ入力、過剰な権限、暗黙の信頼境界", info: "改善余地のある defensive coding、CSP / セキュリティヘッダの強化提案" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# security — セキュリティ + +## 検査項目 + +- **注入**: コマンドインジェクション、SQL/NoSQL injection、shell の `eval`、未エスケープのテンプレート展開、prompt injection +- **秘密情報の露出**: ハードコードされたトークン・API キー・パスワード、`.env` の commit、ログへの secret 漏洩 +- **権限昇格**: 過剰な権限を持つ token / IAM ポリシー、`sudo` の不要利用、root 実行 +- **サプライチェーン**: 信頼できないレジストリ、未固定の依存、unverified なスクリプト実行(`curl | bash`) +- **スクリプト実行**: 外部入力を eval / spawn に流す、unsanitized URL fetch、ユーザ入力を含む `child_process` +- **CI/CD ワークフロー**: `pull_request_target`、`run-on-pr` の secret 露出、third-party action の SHA 固定漏れ +- **データの取り扱い**: PII の不要な保持・送信、暗号化漏れ、TLS なしの通信 + +## severity ガイド + +- **critical**: 悪用可能な脆弱性、明示的な秘密情報の commit、認証回避、任意コード実行 +- **warning**: 防御層の欠落、未サニタイズ入力、過剰な権限、暗黙の信頼境界 +- **info**: 改善余地のある defensive coding、CSP / セキュリティヘッダの強化提案 + +## skip_when + +```yaml +skip_when: + diff_only_in: [] +``` + +required 観点のため常に適用する。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +security に関する critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.agents/skills/review/perspectives/testing.md b/.agents/skills/review/perspectives/testing.md new file mode 100644 index 0000000..d3e8bf5 --- /dev/null +++ b/.agents/skills/review/perspectives/testing.md @@ -0,0 +1,44 @@ +--- +name: testing +category: quality +description: 新コードのカバレッジ、回帰リスク、bats / Vitest / node:test の妥当性 +applies_when: ["src/**", "scripts/**", "tests/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "公開 API / バグ修正にテストがない、既存テストを根拠なく削除、tautology テスト", warning: "エッジケースの取りこぼし、不安定なテスト (flaky)、mock 過剰", info: "より良いアサーション、テストの整理 / 命名改善" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# testing — テスト + +## 検査項目 + +- **新コードのテスト**: 公開 API / 主要ロジックに対するテストが存在するか +- **エッジケース**: 空入力 / null / 異常系 / 境界値が含まれているか +- **回帰リスク**: 既存テストへの破壊的変更、テスト削除の妥当性、skip / disable の理由 +- **テストの質**: アサーションが意味を持つか、tautology テスト(`expect(true).toBe(true)`)になっていないか +- **mocking の境界**: 統合テストで重要な統合点を mock してしまっていないか +- **fixture / snapshot**: 不安定なデータ(時刻 / ランダム)に依存していないか、snapshot の更新理由 +- **テストランナー整合**: bats / node:test / Vitest など既存パターンに従っているか +- **CI 実行性**: ローカル専用の前提(特定パス・環境変数)に依存していないか + +## severity ガイド + +- **critical**: 公開 API / バグ修正にテストがない、既存テストを根拠なく削除、tautology テスト +- **warning**: エッジケースの取りこぼし、不安定なテスト(flaky)、mock 過剰 +- **info**: より良いアサーション、テストの整理 / 命名改善 + +## skip_when + +ドキュメント・設定ファイルのみの変更ではテスト観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +公開 API / バグ修正にテストがない状態で merge-ready とは判定しない。 diff --git a/.agents/skills/review/perspectives/usability.md b/.agents/skills/review/perspectives/usability.md new file mode 100644 index 0000000..09e3c17 --- /dev/null +++ b/.agents/skills/review/perspectives/usability.md @@ -0,0 +1,43 @@ +--- +name: usability +category: ux +description: CLI 文言・エラーメッセージ・skill argument-hint・README 即座理解性 +applies_when: ["src/**", "**/*.md", "**/SKILL.md", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["tests/**", "**/*.test.*"] } +default_enabled: true +severity_rules: { critical: "ユーザーが詰まる致命的な UX 不備 (無限ループ的な確認、復旧不可能な操作の無確認実行)", warning: "紛らわしいメッセージ、誤解を招く CLI 文言、argument-hint の欠落", info: "より親切な文言、説明追記、UX の細かな改善" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# usability — ユーザビリティ / DX + +## 検査項目 + +- **CLI 文言**: help / usage 表示、フラグ名の直感性、`--<flag>` の命名規則統一 +- **エラーメッセージ**: 何が原因で何をすればよいかが伝わるか、ユーザー操作で対処可能か +- **skill / agent の argument-hint**: 期待される引数形式が一目で分かるか +- **README 即座理解性**: 最初の画面で何を提供する skill / package か理解できるか +- **失敗時のリカバリ手順**: ハッピーパス以外で詰まらない設計、再実行可能性 +- **デフォルト値**: 一般的なケースで設定なしで動くか、安全側に倒れているか +- **AskUserQuestion**: ユーザーへの確認が AskUserQuestion を使っているか、テキスト出力で選択肢を列挙していないか(CLAUDE.md ルール) +- **国際化**: メッセージが日本語 / 英語のどちらかに統一されているか、混在していないか + +## severity ガイド + +- **critical**: ユーザーが詰まる致命的な UX 不備(無限ループ的な確認、復旧不可能な操作の無確認実行) +- **warning**: 紛らわしいメッセージ、誤解を招く CLI 文言、argument-hint の欠落 +- **info**: より親切な文言、説明追記、UX の細かな改善 + +## skip_when + +テストのみの変更ではユーザビリティ観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +ユーザビリティの warning は許容(merge 後の継続改善対象)。critical は阻止する。 diff --git a/.agents/skills/topics/SKILL.md b/.agents/skills/topics/SKILL.md new file mode 100644 index 0000000..439414f --- /dev/null +++ b/.agents/skills/topics/SKILL.md @@ -0,0 +1,138 @@ +--- +name: topics +description: GitHub topics 候補を制約検証・人気度測定・broad+narrow / 単数複数比較・ozzy-labs 慣行ハードコードで選定し、`gh repo edit --add-topic` で適用する。スコープは ozzy-labs 内利用のみ。 +--- + +# topics - research-driven GitHub topics setup(ozzy-labs scope) + +GitHub topics の選定は、毎リポで「候補列挙 → 人気度確認 → 公式制約 validation → 単数複数比較 → ozzy-labs 慣行と整合 → `gh repo edit --add-topic` 適用」の手作業を繰り返している。本スキルは選定段階の判断と適用段階の作業を一本化する。 + +**スコープ**: ozzy-labs 配下リポジトリの利用に限定する。クロス-org 汎用化・永続キャッシュ・他 org 用慣行は対象外([スコープ外](#スコープ外)参照)。 + +## 入力 + +```text +topics <candidate-list> + --repo owner/repo (省略時は cwd の origin) + --apply (確認なしで gh repo edit を実行) + --dry-run (適用せず分析だけ) +``` + +- `<candidate-list>` は `,` 区切り、または複数引数 +- `--apply` と `--dry-run` を同時指定した場合は `--dry-run` を優先する(誤適用防止) +- `--repo` 未指定時は `git remote get-url origin` から `owner/repo` を抽出する。GitHub remote が見つからない場合は中断する + +## 手順 + +### Step 1: GitHub 公式制約 validation + +GitHub topics の公式仕様に従って候補を篩い分ける(公式制約。Settings 画面でも同じ規則): + +| 制約 | 内容 | +| --- | --- | +| 文字種 | 半角 lowercase 英数字とハイフン `-` のみ(`a-z`, `0-9`, `-`) | +| 形式 | 先頭・末尾はハイフン不可 | +| 長さ | 最大 50 文字 | +| 個数 | 1 リポにつき最大 20 個 | + +違反した候補は **除外して報告** する。除外理由を明示する(例: `Foo-Bar → 除外(uppercase 含む)`)。20 個超過時は重複排除後の上位を採用し、超過分を報告する。 + +### Step 2: 人気度取得(session 内キャッシュ) + +各候補について GitHub Search API でリポジトリ件数を取得する: + +```bash +gh api "search/repositories?q=topic:<name>" --jq .total_count +``` + +**session 内キャッシュ**: 同一 session で同じ topic を複数回問い合わせる場合(broad+narrow 比較・単数複数比較などで再利用)、初回値をメモして再呼出を抑止する。永続化はしない([スコープ外](#スコープ外) 2 を参照)。 + +API 失敗時は当該候補のみ「人気度不明」と報告し、他候補の処理は継続する。判定上は `0` 扱いにせず、後続の 5x 比較や単数複数比較でも対象外とする。 + +### Step 3: broad+narrow 併記の閾値判定 + +候補内で意味的に重なる broad/narrow ペアを検出し、人気度比から推奨を決定する: + +| 関係 | 推奨 | +| --- | --- | +| broad ≥ narrow × 5 | broad-only を推奨(narrow は冗長) | +| broad / narrow が同じオーダー(5 倍未満かつ broad のほうが多い) | 併記を推奨 | +| narrow > broad | ozzy-labs ハードコード例外として扱う(Step 5 を優先) | + +**broad/narrow ペアの判定基準**: 候補リスト内で、片方が他方の prefix / 完全包含語であり、ハイフンで連結された派生語の関係にあるものを検出する(例: `ai` ⊃ `ai-agents`、`agent` ⊃ `multi-agent`)。完全一致ではない単純な共起(例: `news` と `release-notes`)は対象外。 + +### Step 4: 単数 / 複数の標準形比較 + +候補内に末尾 `-s` の付替えで意味が一致する組がある場合、両者の人気度を比較し優位な形を推奨する。 + +例: + +- `agent` vs `agents` → 人気度の高い方を採用 +- `topic` vs `topics` + +Step 3 と重複する場合(`agent` ⊃ `multi-agent` のような派生語ペアではない単純な単数複数)、Step 4 のみ適用する。 + +### Step 5: ozzy-labs 慣行のハードコード + +ozzy-labs リポ群で繰り返し採用してきた慣行をハードコードする。Step 3/4 の機械判定より優先する: + +1. **`claude-code` は `claude` の上位扱いとして許容**: Anthropic の製品名 topic として `claude` 単独より優先度が高い。両者が候補にあれば併記を推奨する(broad+narrow 5x 例外。実測値 `claude-code` ≈ 25k vs `claude` ≈ 21k で narrow > broad の典型) +2. **`multi-agent` 形を採用、`multi-agents` / `multiagent` は除外**: ハイフン付き単数形に統一する +3. **`*-cli` サフィックス除去ルール**: `codex-cli`, `gemini-cli`, `copilot-cli` は `codex`, `gemini`, `copilot` に変換する。**例外**: `claude-code` は製品名(ハイフンが `cli` を意味しない)ため変換しない + +ハードコードによる変換・除外は理由とともに報告する(例: `codex-cli → codex(*-cli サフィックス除去)`、`multiagent → 除外(multi-agent に統一)`)。 + +### Step 6: 出力と適用 + +#### 出力 + +```text +Candidates: 19 +Filtered (constraints): 19/19 valid +Popularity: + ai 120,879 + ai-agents 28,093 + claude-code 25,514 (>= claude × 1.2 — both retained per ozzy-labs convention) + claude 21,062 + ... +Final 16 topics: ai, ai-agents, agentic, multi-agent, cli, claude, claude-code, codex, gemini, copilot, rss, web-scraping, news, release-notes, research, markdown +``` + +#### 適用 + +- `--dry-run` 指定時: 出力のみ、API 呼び出しなし +- `--apply` 指定時: 確認なしで `gh repo edit <owner/repo> --add-topic <topic1>,<topic2>,...` を実行 +- どちらも未指定時: AskUserQuestion で適用可否を確認する(テキスト出力で `Apply? [Y/n]` のような選択肢を列挙しない) + +適用後、結果を確認する: + +```bash +gh repo view <owner/repo> --json repositoryTopics +``` + +期待値(適用候補)と実適用値の差分を最終レポートに含める。 + +## エラーハンドリング + +| 状況 | 動作 | +| --- | --- | +| `gh` CLI が未認証 | エラーメッセージを表示して中断(GitHub Search API も失敗するため) | +| GitHub Search API rate limit / network error | 該当候補のみ「人気度不明」と報告し、他は継続。判定上は対象外(0 扱いにしない) | +| `--repo` 未指定で GitHub remote 不在 | 中断し、`--repo owner/repo` の明示を促す | +| 制約違反候補 100% | 「適用可能な候補なし」と報告し中断(API は呼ばない) | +| `gh repo edit --add-topic` 失敗 | 失敗 topic を列挙して中断(部分成功した topic は再表示する) | + +## スコープ外 + +| 項目 | 除外理由 | +| --- | --- | +| 1. クロス-org 汎用化 | 現時点では ozzy-labs 専用。汎用化は別 issue で検討 | +| 2. 永続キャッシュ | session 内のみ。複数 session に跨る最適化は対象外 | +| 3. topics 適用部分の他リポ責務 | `commons/init-templates.sh` の `--topics` は「指定リストの適用」のみを担う。本スキルは選定支援、commons は適用、と責務を分離する。両者は人間オペレータ経由で連携する | + +## 注意事項 + +- `.env` ファイルは読み取り・ステージングしない +- `gh` CLI が未認証の場合はエラーメッセージを表示して中断する +- ハードコードされた ozzy-labs 慣行(Step 5)は機械判定より優先する。例外を増やす場合は本スキル MD の改訂で行う(Claude の自由判断で慣行拡張しない) +- `--apply` は確認をスキップするため、必ず `--dry-run` で内容を確認した後に使う運用を推奨する diff --git a/.claude/agents/code-reviewer.md b/.claude/agents/code-reviewer.md new file mode 100644 index 0000000..396ba9f --- /dev/null +++ b/.claude/agents/code-reviewer.md @@ -0,0 +1,75 @@ +--- +name: code-reviewer +description: 観点ベースのコードレビュー専用 agent。axis 名と diff を受け取り、該当 perspective MD を Read してレビューし、JSON で findings を返す read-only agent。 +tools: Read, Grep, Glob +--- + +# code-reviewer + +観点別コードレビューを担当する read-only agent。review skill の deep モード([ADR-0025](https://github.com/ozzy-labs/handbook/blob/main/adr/0025-skills-review-multi-perspective.md))から `Agent({subagent_type: "code-reviewer"})` で並列起動される。 + +## 役割 + +入力プロンプトに含まれる `axis: <name>` から、現在のリポジトリの `.claude/skills/review/perspectives/<name>.md` を Read し、その観点定義(検査項目・severity ガイド)に従って渡された diff をレビューする。 + +このエージェントは `Read`, `Grep`, `Glob` の **read-only allowlist** で動作する。`Bash` / `Edit` / `Write` は持たない。レビュー中にファイルを変更したり任意コマンドを実行することはできない。 + +## 入力フォーマット + +呼び出し元は次のフォーマットでプロンプトを渡す: + +```text +axis: <axis-name> +mode: deep +context: + base: <base-ref> + head: <head-ref> + pr_number: <N (optional)> + +<diff の本文 or "see PR diff via gh pr diff <N>"> +``` + +`pr_number` が与えられた場合、`Read` / `Grep` で diff を直接読むことはできない(gh は Bash 経由のため)ので、呼び出し元プロンプト中に diff が同梱されている前提で動作する。プロンプト中に diff がない場合は `findings` を空にして `notes` に "diff not provided" を返す(無理に推測しない)。 + +## 動作手順 + +1. `axis` の値で `.claude/skills/review/perspectives/<axis>.md` を Read する。読めない場合は findings を空にして `notes` に `"perspective not found: <axis>"` を返す。 +2. 必要に応じて `Read` / `Grep` / `Glob` で関連コードを参照し、diff の意図と影響範囲を把握する。 +3. 観点 MD の検査項目・severity ガイドに従って diff をレビューする。 +4. 出力は **JSON のみ**(前後にテキストを含めない): + +```json +{ + "axis": "<axis-name>", + "version": "1", + "findings": [ + { + "severity": "critical" | "warning" | "info", + "file": "<path>", + "line": <number | null>, + "issue": "<問題の要約>", + "why": "<なぜ問題か>", + "suggestion": "<具体的な修正案>" + } + ], + "notes": "<任意。解釈の留保や、適用観点に該当しない場合の理由>" +} +``` + +## 観点ごとの severity 判定 + +severity の判定は対象 perspective MD の severity ガイドに完全に従う。観点を超えて勝手に重要度を上げ下げしない。 + +`exit_criteria.drive_loop` は呼び出し元(review skill / drive skill)が集計する。本 agent は判定しない。 + +## 制限事項 + +- ファイルを変更しない(`Edit` / `Write` を持たない) +- 任意コマンドを実行しない(`Bash` を持たない) +- 観点を 1 つだけ担当する。複数 axis をまとめて見ない(呼び出し元が並列起動する) +- 修正パッチを生成しない(`suggestion` は文章での提案に留める) +- diff が同梱されていない場合は推測でレビューしない + +## 配信機構 + +本ファイルは `ozzy-labs/skills` リポジトリの SSOT(`src/agents/code-reviewer.md`)。consumer リポジトリへの配信は [ADR-0026](https://github.com/ozzy-labs/handbook/blob/main/adr/0026-agent-distribution-via-skills-sync.md) の `sync-skills.sh` 拡張を経由する。skills repo の build (`scripts/build.mjs`) は `dist/claude-code/.claude/agents/code-reviewer.md` に出力する。 diff --git a/.claude/skills/drive/SKILL.md b/.claude/skills/drive/SKILL.md index 654a20d..44699a2 100644 --- a/.claude/skills/drive/SKILL.md +++ b/.claude/skills/drive/SKILL.md @@ -1,8 +1,8 @@ --- -description: Issue から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。オプションでマージまで実行可能。 -argument-hint: <#issue-number | instruction> [--merge] +description: Issue または指示から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。単一/複数の Issue/PR と明示依存記法に対応。オプションでマージまで実行可能。 +argument-hint: <#N | #N,#N | #N-N | instruction> [--merge] [--concurrency N] [--review=quick|final-deep|deep] disable-model-invocation: true -allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUserQuestion +allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUserQuestion, Agent --- # drive @@ -15,12 +15,43 @@ allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUser ### 入力解析 -`$ARGUMENTS` を解析し、対象(Issue/指示)とオプション(`--merge` の有無)を特定する。 +`$ARGUMENTS` を解析し、target リスト(Issue/PR/指示)と依存記法、オプション(`--merge`, `--concurrency N`, `--review=<mode>`)を特定する。 + +- target が 1 件かつ依存記法(`->`)なし → 単一モード +- target が 2 件以上、または依存記法あり → オーケストレーションモード + +`--review` の取り扱い: + +- 既定は `quick` +- 単一モード: `quick` / `final-deep` / `deep` をすべて受け付ける +- オーケストレーションモード: `--review=quick` を強制し、`final-deep` / `deep` 指定時は警告を表示して `quick` にフォールバックする(コスト管理) ### 自律実行 計画承認を含め、マージ処理(またはマージ確認)まで AskUserQuestion を使用しない(完全自律実行)。 +### subagent dispatch(オーケストレーションモード) + +オーケストレーションモードでは `Agent` tool で各 target を並列実行する: + +- **isolation:** `"worktree"`(必須) +- **subagent_type:** `general-purpose` +- **prompt:** subagent から slash command は呼べないため、`.agents/skills/drive/SKILL.md` を Read させ、target #N について単一モードのワークフロー(Phase 1-5)を実行するよう指示する。`--merge` 指定時は Phase 4 まで完了し、自 PR の merged まで polling して終了させる。最終結果は JSON で返させる +- **main への checkout 禁止(必ず prompt に明記):** subagent は自 worktree branch で完結する。`git checkout main` / `git switch main` / `git checkout HEAD~` 等で HEAD を移動させない。worktree は親側で削除されるため main へ戻す必要はない。これを怠ると共有 git directory 経由で親 worktree の `HEAD` / `index` が汚染される([Issue #66](https://github.com/ozzy-labs/skills/issues/66) 参照) +- **依存元 wave がある場合のベースブランチ:** + - `--merge` 指定 + 依存元が merged → main から作成 + - `--merge` 指定 + 依存元が auto-merge enabled(未マージ)→ main を pull してから作成(取り込まれていれば main ベース、未取り込みなら依存元 headRefName ベース) + - `--merge` 未指定 → 依存元 PR の headRefName をベースに stacked PR として作成 +- **並列起動:** 同一 wave 内の独立 subagent は **1 メッセージ複数 tool call** で並列起動する +- **並列度:** `min(4, wave 内タスク数)`、`--concurrency N` で上書き、8 超は警告のみ +- **wave 内タスク数 > 並列度:** semaphore 方式で空きスロット待ち(先に起動した subagent の完了を待ってから次を起動) + +### 観測性 + +- Phase 0 完了時に wave 構成と target リストを表示する +- `Agent` tool は最終結果のみを返すためストリーム的な中間報告は不可。親は wave 起動時刻 `<T>` を ISO 8601 で記録し、30 秒間隔で `gh pr list --author @me --state open --search "created:>=<T>" --json number,url,headRefName,title` を polling する。既知 PR との差分から新規 PR を検出して URL を即時表示する +- Phase Final で集約レポートを出力する + ### 中断時 いずれかのフェーズで中断した場合、AskUserQuestion で次のアクションを確認する: @@ -28,11 +59,20 @@ allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUser - **「エラーを修正して再開する」** → 中断したフェーズから再開 - **「中断する」** → 終了 +オーケストレーションモードで一部 task のみ失敗の場合は、Phase Final レポート出力後に AskUserQuestion で再開対象を確認する。 + ### 完了後 -1. **自動マージが指定されている場合 (`--merge`):** - - Phase 4 の手順に従いマージを実行し、結果を報告して終了する。 -2. **自動マージが指定されていない場合:** - - AskUserQuestion を呼び出す(`answers` パラメータは設定しない): - - **「PR をマージする」** → `gh pr merge --squash --delete-branch` でマージを実行し、結果を報告する - - **「追加の変更を行う」** → 終了する +#### 単一モード + +1. **`--merge` 指定時:** Phase 4 の手順に従いマージを実行し、結果を報告して終了する +2. **`--merge` 未指定時:** AskUserQuestion を呼び出す(`answers` パラメータは設定しない) + - **「PR をマージする」** → `gh pr merge --squash --delete-branch` でマージを実行し、結果を報告する + - **「追加の変更を行う」** → 終了する + +#### オーケストレーションモード + +1. **`--merge` 指定時:** 各 subagent が自 PR のマージまで完了させているため、Phase Final 集約レポートを出力して終了する +2. **`--merge` 未指定時:** Phase Final レポート出力後、AskUserQuestion を呼び出す + - **「全 PR を一括マージする」** → 各 PR に対し順次 `gh pr merge --squash --delete-branch` を依存順に実行 + - **「個別に対応する」** → 終了する diff --git a/.claude/skills/health/SKILL.md b/.claude/skills/health/SKILL.md new file mode 100644 index 0000000..c0d38c4 --- /dev/null +++ b/.claude/skills/health/SKILL.md @@ -0,0 +1,31 @@ +--- +description: リポジトリ改修中に意図せず残る状態と skill catalog 整合性を 16 領域に渡って一発確認し、ステータス表で俯瞰しつつ固定語彙の推奨アクションを inline 付与して報告する。`--deep` 指定時は `要確認` 項目を追加調査してラベルを格上げする。検査と提示のみで実行はしない。Routine 互換。 +argument-hint: "[--deep]" +disable-model-invocation: true +allowed-tools: Bash, Read, Grep +--- + +# health + +`.agents/skills/health/SKILL.md` を Read し、ワークフロー手順に従う。 + +**重要:** 読み込んだスキル内の手順を忠実に実行する。section 順序、推奨アクション語彙、出力フォーマット(ステータス表・compact list)、エラーハンドリングは厳密に守る。 + +## Claude Code 固有の追加事項 + +### 引数解析 + +`--deep` フラグの有無を判定する: + +- `--deep` あり → Phase 1 完了後に Phase 2(追加調査)を実行する +- `--deep` なし → Phase 1 のみ実行する(routine 互換) + +`--deep` 以外のフラグは無視する(将来の拡張用)。 + +### 並列実行 + +16 領域のチェックコマンドは **同一メッセージ内に複数の Bash tool call** を並べることで並列実行する。直列に呼ばないこと。Phase 2 の追加調査も同様に **同一メッセージ内の複数 Bash tool call** で並列起動する。 + +### 完了報告・次のアクション提案 + +レポートを出力したら終了する。AskUserQuestion は使わない。次のアクション提案も行わない。ユーザーが推奨アクションを見て自ら判断・実行する。 diff --git a/.claude/skills/phase-issue/SKILL.md b/.claude/skills/phase-issue/SKILL.md new file mode 100644 index 0000000..b52fcd8 --- /dev/null +++ b/.claude/skills/phase-issue/SKILL.md @@ -0,0 +1,60 @@ +--- +description: Phase-N tracking issue を生成する。引数で渡された項目はそのまま使い、不足分は AskUserQuestion で対話的に補う。`--draft` で stdout 出力、それ以外は `gh issue create` で起票する。 +argument-hint: <phase-number> "<title>" [--description ...] [--refs ...] [--donts ...] [--decisions-file ...] [--tasks-file ...] [--dod ...] [--outlook ...] [--related ...] [--label ...] [--repo ...] [--draft] +disable-model-invocation: true +allowed-tools: Bash, Read, Write, AskUserQuestion +--- + +# phase-issue + +`.agents/skills/phase-issue/SKILL.md` を Read し、ワークフロー手順に従う。 + +**重要:** 章立て・整形ルール・マーカーブロックは canonical SKILL.md の規約に厳密に従う。Claude の自由判断で章を増減しない。 + +## Claude Code 固有の追加事項 + +### 引数解析 + +`$ARGUMENTS` を解析する: + +- `<phase-number>` と `<title>` が欠落している場合、AskUserQuestion でそれぞれ確認する(`answers` パラメータは設定しない) +- 任意オプション(`--description`, `--refs`, `--donts`, `--decisions-file`, `--tasks-file`, `--dod`, `--outlook`, `--related`, `--label`, `--repo`, `--draft`)は引数から取得する + +### 不足項目の対話補完 + +引数で渡されなかった任意項目について、AskUserQuestion で **個別に** 補完するか確認する(`answers` パラメータは設定しない)。 + +質問順序(章立て順に従う): + +1. **「プロジェクト概要を入力する」** / 「省略する」 → `--description` 相当 +2. **「参考実装を入力する」** / 「省略する」 → `--refs` 相当(カンマ区切り) +3. **「やってはいけないことを入力する」** / 「省略する」 → `--donts` 相当(改行区切り) +4. **「決定事項ファイルを指定する」** / 「TBD で残す」 → `--decisions-file` 相当 +5. **「タスクファイルを指定する」** / 「TBD で残す」 → `--tasks-file` 相当 +6. **「DoD を入力する」** / 「TBD で残す」 → `--dod` 相当(改行区切り) +7. **「Phase N+1 outlook を入力する」** / 「(未定) で残す」 → `--outlook` 相当 +8. **「関連を入力する」** / 「省略する」 → `--related` 相当(改行区切り) + +「入力する」を選んだ場合は AskUserQuestion で具体的な内容を尋ねる(自由記述は別の AskUserQuestion 呼び出しで行う)。 + +引数で既に渡された項目については **質問しない**(同じ情報を二重で集めない)。 + +### body プレビューと最終確認 + +body 組み立て後、起票または stdout 出力の前に、AskUserQuestion で確認する(`answers` パラメータは設定しない): + +- **「この内容で起票する」** → `gh issue create --body-file` で起票(`--draft` 指定時は stdout 出力) +- **「修正する」** → 修正対象セクションを尋ねて該当項目を再入力する +- **「キャンセル」** → 中断する + +**重要:** 承認なしに `gh issue create` を実行しない。`--draft` モードでも、stdout 出力前に内容確認を行う。 + +### 完了後の次のアクション + +完了報告の直後に AskUserQuestion を呼び出す(`answers` パラメータは設定しない): + +- **「この issue から `/drive` で実装を始める」** → 起票した issue 番号を引数として `/drive` を案内する(実行はしない) +- **「別の Phase issue を作成する」** → 本スキルを再度実行するよう案内する +- **「終了する」** → 終了する + +ネクストアクションはユーザーの確認なく実行しない。 diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md index f1e3508..e638f01 100644 --- a/.claude/skills/review/SKILL.md +++ b/.claude/skills/review/SKILL.md @@ -1,8 +1,8 @@ --- -description: コード変更や PR をレビューし、問題点・改善案を報告する -argument-hint: <#PR-number | (blank for working tree changes)> +description: コード変更や PR を 11 観点でレビューし、JSON 構造化出力 + 人間可読レポートで報告する。quick / deep モード対応。 +argument-hint: <#PR-number | (blank for working tree changes)> [--axes=<axis,...>] [--deep] disable-model-invocation: true -allowed-tools: Bash, Read, Edit, Write, Grep, Glob, AskUserQuestion +allowed-tools: Bash, Read, Edit, Write, Grep, Glob, Agent, AskUserQuestion --- # review @@ -11,11 +11,36 @@ allowed-tools: Bash, Read, Edit, Write, Grep, Glob, AskUserQuestion ## Claude Code 固有の追加事項 -完了報告の直後に AskUserQuestion を呼び出す(`answers` パラメータは設定しない): +### 引数解析 + +`$ARGUMENTS` を解析する: + +- 数字または `#N` → PR 番号として扱う +- `--axes=<axis,...>` → 適用観点の明示指定 +- `--deep` → deep モードで実行(観点ごとに `Agent({subagent_type: "code-reviewer"})` を **同一メッセージ複数 tool call** で並列起動する) + +### deep モードでの並列起動 + +deep モードでは観点ごとに subagent を並列起動する。Claude Code 固有の `Agent` tool を使用する: + +```text +Agent({ + subagent_type: "code-reviewer", + prompt: "axis: <axis>\nmode: deep\ncontext:\n base: <base>\n head: <head>\n pr_number: <N>\n\n<diff>" +}) +``` + +- 同一 wave 内の独立 subagent は **1 メッセージ複数 tool call** で並列起動する +- 集約は呼び出し元(review skill 内の純スクリプト処理)で行う。LLM 呼び出しを追加しない +- subagent の戻り値(JSON)をマージし、`findings[]` に投入する + +### 完了報告後 + +完了報告の直後に AskUserQuestion を呼び出す(`answers` パラメータは設定しない)。 **指摘ありの場合:** -- **「指摘事項を修正する」** → 指摘事項に基づきコードを修正する +- **「指摘事項を修正する」** → Critical / Warning の指摘事項に基づきコードを修正する(Info は対象外) - **「このまま進める」** → 終了する **指摘なしの場合:** diff --git a/.claude/skills/review/perspectives/README.md b/.claude/skills/review/perspectives/README.md new file mode 100644 index 0000000..bb59293 --- /dev/null +++ b/.claude/skills/review/perspectives/README.md @@ -0,0 +1,59 @@ +--- +name: perspectives-index +description: review skill が参照する観点定義のインデックスとスキーマガイド。 +--- + +# review perspectives + +review skill / `code-reviewer` agent が参照する観点定義の SSOT([ADR-0025](https://github.com/ozzy-labs/handbook/blob/main/adr/0025-skills-review-multi-perspective.md))。各 `<axis>.md` がレビュー 1 観点を表し、frontmatter に観点メタデータ、本文に検査項目・severity ガイド・終了基準を持つ。 + +## 採用観点(11 軸) + +| category | axis | 既定 | +| --- | --- | --- | +| required | [correctness](./correctness.md) | 常に適用 | +| required | [security](./security.md) | 常に適用 | +| required | [conventions](./conventions.md) | 常に適用 | +| design | [architecture](./architecture.md) | applies_when マッチ時 | +| design | [compatibility](./compatibility.md) | applies_when マッチ時 | +| design | [maintainability](./maintainability.md) | applies_when マッチ時 | +| quality | [testing](./testing.md) | applies_when マッチ時 | +| quality | [performance](./performance.md) | applies_when マッチ時 | +| quality | [observability](./observability.md) | applies_when マッチ時 | +| ux | [usability](./usability.md) | applies_when マッチ時、consumer が opt-out 可 | +| ux | [documentation](./documentation.md) | 常に適用 | + +## frontmatter スキーマ + +```yaml +--- +name: <axis> # ファイル名と一致させる +category: required | design | quality | ux +description: <一行で観点の主旨> +applies_when: ["<glob>", ...] # diff にこの glob にマッチするファイルが含まれれば適用 +skip_when: { diff_only_in: ["<glob>", ...] } # 全変更ファイルがこの glob 部分集合なら不適用 +default_enabled: true | false # false の場合は --axes 明示時のみ適用 +severity_rules: { critical: "<...>", warning: "<...>", info: "<...>" } +exit_criteria: { drive_loop: { critical: <N>, warning: <N> } } # warning キーは省略可(許容を意味する) +--- +``` + +`skip_when` / `severity_rules` / `exit_criteria` は flow-style YAML の 1 行で記述する(既存の flat frontmatter parser と整合させるため)。本文には人間可読な severity ガイドや検査項目を冗長に記述してよいが、機械処理の SSOT は frontmatter とする。 + +未定義キーは reader が無視する(forward-compat)。互換破壊的なスキーマ変更(必須キーの削除等)は ADR を起こす。 + +## 観点選別ロジック + +review skill / `code-reviewer` agent は以下の順で適用観点を決定する: + +1. `category: required` → 常に適用(`applies_when` / `skip_when` を無視) +2. `default_enabled: false` → `--axes` で明示指定された場合のみ適用(experimental 用) +3. `skip_when.diff_only_in` がマッチ → 不適用(最優先のスキップ条件) +4. `applies_when` のいずれかの glob にマッチ → 適用(OR) +5. それ以外 → 不適用 + +## 観点 MD の追加・変更 + +新しい観点を追加する場合、本ディレクトリに `<axis>.md` を作成して frontmatter スキーマに従い記述する。観点 MD の lint(frontmatter 必須キー検証、`applies_when` / `skip_when` の glob 妥当性)は `health` skill で実施する。 + +互換破壊的なスキーマ変更(必須キーの削除等)を行う場合は ADR を起こす。`code-reviewer` agent と review skill 双方の reader が後方互換に追随できるよう、新キーは optional として導入する。 diff --git a/.claude/skills/review/perspectives/architecture.md b/.claude/skills/review/perspectives/architecture.md new file mode 100644 index 0000000..be4cd8b --- /dev/null +++ b/.claude/skills/review/perspectives/architecture.md @@ -0,0 +1,43 @@ +--- +name: architecture +category: design +description: レイヤリング・責務配置・抽象度・既存パターン整合 +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "既存アーキテクチャ判断 (ADR 等) に明確に反する、取り返しがつかない構造変更", warning: "責務違反、循環依存、既存パターンを破る無理筋、保守性を著しく下げる構造", info: "より良い分離・命名・抽象度の提案、リファクタリング候補" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# architecture — アーキテクチャ + +## 検査項目 + +- **レイヤリング**: 上位層が下位層を参照しているか、循環依存がないか +- **責務配置**: 1 モジュール / 関数の責務が明確で過剰膨張していないか +- **抽象度**: 適切な抽象化レベル、不要な抽象 / 早期最適化、漏れている抽象化 +- **既存パターンとの整合**: adapter / skill / agent などのリポジトリ既存パターンに沿っているか、独自パターン乱立していないか +- **データフロー**: 入出力の方向、グローバル状態の混入、純粋性の意図的な維持 +- **拡張性**: 将来の差し込みポイント、過剰な extension point の設置(YAGNI) +- **境界の明示**: モジュール / パッケージ / 内部 API / 外部 API の境界が明示されているか + +## severity ガイド + +- **critical**: 既存アーキテクチャ判断(ADR 等)に明確に反する、取り返しがつかない構造変更 +- **warning**: 責務違反、循環依存、既存パターンを破る無理筋、保守性を著しく下げる構造 +- **info**: より良い分離・命名・抽象度の提案、リファクタリング候補 + +## skip_when + +ドキュメント・設定ファイルのみの変更ではアーキテクチャ観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +設計レベルの critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.claude/skills/review/perspectives/compatibility.md b/.claude/skills/review/perspectives/compatibility.md new file mode 100644 index 0000000..b5117ad --- /dev/null +++ b/.claude/skills/review/perspectives/compatibility.md @@ -0,0 +1,43 @@ +--- +name: compatibility +category: design +description: 後方互換、スキーマ変更、commons-sync 経由のコンシューマ影響 +applies_when: ["src/**", "scripts/**", "dist/**", "package.json", "**/*.json", "**/*.yaml", "**/*.yml", "**/SKILL.md"] +skip_when: { diff_only_in: ["tests/**", "**/*.test.*"] } +default_enabled: true +severity_rules: { critical: "既存コンシューマが破壊される非互換変更 (rename / 削除 / 型変更) で migration path や CHANGELOG への記載がない", warning: "互換性は保てるがコンシューマ側で対応が必要、または default 変更で挙動が変わる", info: "より丁寧な deprecation の提案、注意喚起" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# compatibility — 互換性 + +## 検査項目 + +- **後方互換**: 公開 API / CLI フラグ / skill 引数の削除・改名、デフォルト値変更による既存利用者への影響 +- **スキーマ変更**: SKILL.md frontmatter / 設定ファイル / JSON schema のフィールド削除・型変更 +- **commons-sync 経由のコンシューマ影響**: `dist/` 出力構造の変更がコンシューマの sync で破壊的にならないか +- **agent / adapter API**: `AdapterBase.generate()` シグネチャ変更、`Skill` 型の必須フィールド追加 +- **package.json**: dependency / engines の互換性、major bump +- **legacy resume 互換**: 既存 PR コメント・既存 lock / state ファイルなどの読み取り互換 +- **version migration**: schema version をインクリメントする変更で reader 側のフォールバック処理を提供しているか + +## severity ガイド + +- **critical**: 既存コンシューマが破壊される非互換変更(rename / 削除 / 型変更)で migration path や CHANGELOG への記載がない +- **warning**: 互換性は保てるがコンシューマ側で対応が必要、または default 変更で挙動が変わる +- **info**: より丁寧な deprecation の提案、注意喚起 + +## skip_when + +テストのみの変更では互換性観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +非互換変更が未対処のまま merge-ready とは判定しない。 diff --git a/.claude/skills/review/perspectives/conventions.md b/.claude/skills/review/perspectives/conventions.md new file mode 100644 index 0000000..628e272 --- /dev/null +++ b/.claude/skills/review/perspectives/conventions.md @@ -0,0 +1,48 @@ +--- +name: conventions +category: required +description: Conventional Commits、lint、ファイル命名、`.yaml` 統一などのリポジトリ規約 +applies_when: ["**/*"] +default_enabled: true +severity_rules: { critical: "コミット / PR タイトルが Conventional Commits 違反 (commitlint で fail)、main への直接 push、--no-verify 利用", warning: "lint / formatter 違反、命名規約違反、.yaml / .yml 不整合", info: "より明示的な書き方への改善提案、命名の細かな統一" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# conventions — コーディング規約 + +## 検査項目 + +- **Conventional Commits**: type / scope / description の形式、`!` による破壊的変更マーキング +- **ブランチ命名**: `<type>/<short-description>` 形式 +- **ファイル命名・配置**: 既存パターンとの整合(`SKILL.md` / `SKILL.<adapter>.md`、`perspectives/<axis>.md` など) +- **YAML 拡張子**: `.yaml` に統一されているか(ツールが `.yml` を要求する場合のみ許容) +- **lint / formatter**: biome / markdownlint / yamllint / shellcheck / shfmt 等の出力に違反していないか +- **import / export 規約**: ESM / CJS の統一、明示的なファイル拡張子 +- **CLAUDE.md / AGENTS.md**: 記載されたプロジェクトルールに違反していないか +- **言語別規約**: tools/lint-rules.md などで定義された言語固有の規約 + +## severity ガイド + +- **critical**: コミット / PR タイトルが Conventional Commits 違反(commitlint で fail)、`main` への直接 push、`--no-verify` 利用 +- **warning**: lint / formatter 違反、命名規約違反、`.yaml` / `.yml` 不整合 +- **info**: より明示的な書き方への改善提案、命名の細かな統一 + +## skip_when + +```yaml +skip_when: + diff_only_in: [] +``` + +required 観点のため常に適用する。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +規約違反が残っている状態で merge-ready とは判定しない。 diff --git a/.claude/skills/review/perspectives/correctness.md b/.claude/skills/review/perspectives/correctness.md new file mode 100644 index 0000000..b9ecdf4 --- /dev/null +++ b/.claude/skills/review/perspectives/correctness.md @@ -0,0 +1,41 @@ +--- +name: correctness +category: required +description: ロジック誤り・エッジケース・並行性・エラーハンドリング +applies_when: ["**/*"] +default_enabled: true +severity_rules: { critical: "悪用・データ破壊・誤った状態遷移・無限ループ・回帰の温床になり得るバグ", warning: "通常パスは動くが特定入力で破綻するケース、未処理の例外", info: "防御的コーディングの改善余地、より堅牢な書き方の提案" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# correctness — 正確性 + +## 検査項目 + +- **ロジック誤り**: 条件分岐の取りこぼし、off-by-one、boolean 反転、誤った演算子 +- **エッジケース**: 空入力、null / undefined、最大/最小値、空配列・空文字列、Unicode、改行 +- **並行性**: race condition、競合する書き込み、shared state の取り扱い、await 漏れ、Promise の handling +- **エラーハンドリング**: 握りつぶし(catch して何もしない)、誤った再 throw、エラー型の取り違え、finally の副作用 +- **戻り値・副作用**: 関数が宣言した契約を満たしているか、未文書化の副作用がないか +- **型の整合**: `as` キャストや `any` 経由で実行時に破綻するパスがないか + +## severity ガイド + +- **critical**: 悪用・データ破壊・誤った状態遷移・無限ループ・回帰の温床になり得るバグ +- **warning**: 通常パスは動くが特定入力で破綻するケース、未処理の例外 +- **info**: 防御的コーディングの改善余地、より堅牢な書き方の提案 + +## skip_when + +なし(required 観点は常に適用)。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +正確性に関する critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.claude/skills/review/perspectives/documentation.md b/.claude/skills/review/perspectives/documentation.md new file mode 100644 index 0000000..c09bdd6 --- /dev/null +++ b/.claude/skills/review/perspectives/documentation.md @@ -0,0 +1,43 @@ +--- +name: documentation +category: ux +description: README・AGENTS.md・CLAUDE.md・SKILL.md・公開挙動の同期 +applies_when: ["**/*"] +skip_when: { diff_only_in: [] } +default_enabled: true +severity_rules: { critical: "公開 API / 公開 CLI の変更がドキュメントに一切反映されておらず利用者が破壊される", warning: "README / SKILL.md / AGENTS.md の陳腐化、ADR と実装の乖離、誤ったサンプル", info: "ドキュメント追記の提案、説明の改善、表記揺れの統一" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# documentation — ドキュメント整合性 + +## 検査項目 + +- **README**: 公開挙動の変更がドキュメントに反映されているか、サンプル / 使用例の陳腐化 +- **AGENTS.md / CLAUDE.md**: skill / agent の追加・改名・削除が反映されているか +- **SKILL.md**: frontmatter の `description` / `argument-hint` がコード実装と一致しているか +- **CHANGELOG**: 互換性に影響する変更が記録されているか(release-please 自動生成範囲を除く) +- **コメント / docstring**: 実装変更に追従しているか、誤った説明が残っていないか +- **ADR との整合**: ADR で決定された方針と実装が一致しているか、参照する ADR 番号が正確か +- **public な関数 / API のドキュメント**: 入力 / 出力 / 例外契約が記載されているか + +## severity ガイド + +- **critical**: 公開 API / 公開 CLI の変更がドキュメントに一切反映されておらず利用者が破壊される +- **warning**: README / SKILL.md / AGENTS.md の陳腐化、ADR と実装の乖離、誤ったサンプル +- **info**: ドキュメント追記の提案、説明の改善、表記揺れの統一 + +## skip_when + +ドキュメント整合性は全変更で適用する(コード変更にもドキュメント変更にも検査対象がある)。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +ドキュメント整合性の critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.claude/skills/review/perspectives/maintainability.md b/.claude/skills/review/perspectives/maintainability.md new file mode 100644 index 0000000..bf0e81b --- /dev/null +++ b/.claude/skills/review/perspectives/maintainability.md @@ -0,0 +1,42 @@ +--- +name: maintainability +category: design +description: 命名・複雑度・dead code・コメント負債 +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "取り返しのつかない命名 / 構造の選択 (公開 API として固定される名称等)", warning: "顕著な dead code、過剰な複雑度、誤解を招く命名、明らかな重複", info: "命名の細かな改善、コメント整理、軽微なリファクタ提案" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# maintainability — 保守性 + +## 検査項目 + +- **命名**: 識別子の意図が伝わるか、誤解を招く命名がないか、不要な略語 +- **複雑度**: 関数 / メソッドが過剰に長い、ネストが深すぎる、分岐爆発、cyclomatic complexity +- **dead code**: 未使用の export / 関数 / 変数 / import、コメントアウトされたコード +- **コメント負債**: WHAT を説明する冗長コメント、陳腐化したコメント、TODO / FIXME の放置 +- **重複**: 3 箇所以上に出現するロジックや、コピー&ペーストの兆候 +- **テスト容易性**: 過剰な隠蔽、副作用に依存した設計、テストしにくい依存注入 +- **ドキュメント**: 公開 API / skill / agent に最低限の説明があるか + +## severity ガイド + +- **critical**: 取り返しのつかない命名 / 構造の選択(公開 API として固定される名称等) +- **warning**: 顕著な dead code、過剰な複雑度、誤解を招く命名、明らかな重複 +- **info**: 命名の細かな改善、コメント整理、軽微なリファクタ提案 + +## skip_when + +ドキュメント・設定ファイルのみの変更では保守性観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +保守性の warning は許容(merge 後の継続改善対象)。critical は阻止する。 diff --git a/.claude/skills/review/perspectives/observability.md b/.claude/skills/review/perspectives/observability.md new file mode 100644 index 0000000..13a232d --- /dev/null +++ b/.claude/skills/review/perspectives/observability.md @@ -0,0 +1,42 @@ +--- +name: observability +category: quality +description: エラー文脈・ログ・失敗時の手がかり +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "tests/**", "**/*.test.*", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "失敗時に何も情報が出ず原因特定不可能、本番で silent failure になる経路", warning: "エラーメッセージが薄い、ログ過剰 / 不足、エラー種別の取り違え", info: "より親切なメッセージ、log level の調整提案" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# observability — 可観測性 + +## 検査項目 + +- **エラー文脈**: throw する error に十分な手がかり(入力値・どのファイル・どの操作)が含まれているか +- **ログ**: 失敗時に何が起きたか追える程度のログがあるか、過剰な debug ログを残していないか +- **エラーメッセージの粒度**: ユーザに表示する文言が actionable か(次の手が示唆されるか) +- **失敗の伝播**: 上位がエラー種別で分岐できる構造か、error code / typed error の活用 +- **副作用の追跡可能性**: 外部システム(GitHub API / fs / network)呼び出しの成否が可視化されているか +- **シークレット漏洩防止**: ログに secret が出ていないか +- **CI / Hook の失敗表示**: lefthook / hook の失敗時に原因がすぐ分かる出力か + +## severity ガイド + +- **critical**: 失敗時に何も情報が出ず原因特定不可能、本番で silent failure になる経路 +- **warning**: エラーメッセージが薄い、ログ過剰 / 不足、エラー種別の取り違え +- **info**: より親切なメッセージ、log level の調整提案 + +## skip_when + +テスト・ドキュメント・設定のみの変更では可観測性観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +可観測性の warning は許容。critical は阻止する。 diff --git a/.claude/skills/review/perspectives/performance.md b/.claude/skills/review/perspectives/performance.md new file mode 100644 index 0000000..7f7d72d --- /dev/null +++ b/.claude/skills/review/perspectives/performance.md @@ -0,0 +1,42 @@ +--- +name: performance +category: quality +description: hot path、不要な逐次 I/O、メモリ +applies_when: ["src/**", "scripts/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "tests/**", "**/*.test.*", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "顕著な性能退行、production で UX を損なう規模のリグレッション、無限ループ", warning: "hot path 上の非効率、逐次 I/O、明らかな再計算", info: "軽微な最適化提案、cache 化候補" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# performance — パフォーマンス + +## 検査項目 + +- **hot path**: 高頻度呼び出しパスにおける O(n²) 以上の処理、ネストループ、不要な allocation +- **逐次 I/O**: 並列化可能な独立 I/O を直列で実行していないか(`for await` 直列化、Promise.all 化漏れ) +- **メモリ**: 不要に巨大な中間配列、ストリーム化できる処理の一括読込、リーク要因(hold する closure) +- **不要なレンダリング / 再計算**: memo / cache を活用すべき箇所、毎回計算される定数 +- **ファイル I/O**: 同一ファイルの重複読込、無駄な fs stat、巨大ファイルの全読み +- **HTTP / fetch**: タイムアウト未設定、N+1 リクエスト、過度な polling 間隔 +- **依存の影響**: 重量級ライブラリの導入、tree-shaking 効きにくい構造 + +## severity ガイド + +- **critical**: 顕著な性能退行、production で UX を損なう規模のリグレッション、無限ループ +- **warning**: hot path 上の非効率、逐次 I/O、明らかな再計算 +- **info**: 軽微な最適化提案、cache 化候補 + +## skip_when + +テスト・ドキュメント・設定のみの変更ではパフォーマンス観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +性能 warning は許容(merge 後の継続改善対象)。critical は阻止する。 diff --git a/.claude/skills/review/perspectives/security.md b/.claude/skills/review/perspectives/security.md new file mode 100644 index 0000000..aac18f2 --- /dev/null +++ b/.claude/skills/review/perspectives/security.md @@ -0,0 +1,47 @@ +--- +name: security +category: required +description: 注入・秘密情報露出・権限昇格・サプライチェーン・スクリプト実行 +applies_when: ["**/*"] +default_enabled: true +severity_rules: { critical: "悪用可能な脆弱性、明示的な秘密情報の commit、認証回避、任意コード実行", warning: "防御層の欠落、未サニタイズ入力、過剰な権限、暗黙の信頼境界", info: "改善余地のある defensive coding、CSP / セキュリティヘッダの強化提案" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# security — セキュリティ + +## 検査項目 + +- **注入**: コマンドインジェクション、SQL/NoSQL injection、shell の `eval`、未エスケープのテンプレート展開、prompt injection +- **秘密情報の露出**: ハードコードされたトークン・API キー・パスワード、`.env` の commit、ログへの secret 漏洩 +- **権限昇格**: 過剰な権限を持つ token / IAM ポリシー、`sudo` の不要利用、root 実行 +- **サプライチェーン**: 信頼できないレジストリ、未固定の依存、unverified なスクリプト実行(`curl | bash`) +- **スクリプト実行**: 外部入力を eval / spawn に流す、unsanitized URL fetch、ユーザ入力を含む `child_process` +- **CI/CD ワークフロー**: `pull_request_target`、`run-on-pr` の secret 露出、third-party action の SHA 固定漏れ +- **データの取り扱い**: PII の不要な保持・送信、暗号化漏れ、TLS なしの通信 + +## severity ガイド + +- **critical**: 悪用可能な脆弱性、明示的な秘密情報の commit、認証回避、任意コード実行 +- **warning**: 防御層の欠落、未サニタイズ入力、過剰な権限、暗黙の信頼境界 +- **info**: 改善余地のある defensive coding、CSP / セキュリティヘッダの強化提案 + +## skip_when + +```yaml +skip_when: + diff_only_in: [] +``` + +required 観点のため常に適用する。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +security に関する critical / warning が残っている状態で merge-ready とは判定しない。 diff --git a/.claude/skills/review/perspectives/testing.md b/.claude/skills/review/perspectives/testing.md new file mode 100644 index 0000000..d3e8bf5 --- /dev/null +++ b/.claude/skills/review/perspectives/testing.md @@ -0,0 +1,44 @@ +--- +name: testing +category: quality +description: 新コードのカバレッジ、回帰リスク、bats / Vitest / node:test の妥当性 +applies_when: ["src/**", "scripts/**", "tests/**", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["**/*.md", "docs/**", "**/*.yaml", "**/*.yml", "**/*.json"] } +default_enabled: true +severity_rules: { critical: "公開 API / バグ修正にテストがない、既存テストを根拠なく削除、tautology テスト", warning: "エッジケースの取りこぼし、不安定なテスト (flaky)、mock 過剰", info: "より良いアサーション、テストの整理 / 命名改善" } +exit_criteria: { drive_loop: { critical: 0, warning: 0 } } +--- + +# testing — テスト + +## 検査項目 + +- **新コードのテスト**: 公開 API / 主要ロジックに対するテストが存在するか +- **エッジケース**: 空入力 / null / 異常系 / 境界値が含まれているか +- **回帰リスク**: 既存テストへの破壊的変更、テスト削除の妥当性、skip / disable の理由 +- **テストの質**: アサーションが意味を持つか、tautology テスト(`expect(true).toBe(true)`)になっていないか +- **mocking の境界**: 統合テストで重要な統合点を mock してしまっていないか +- **fixture / snapshot**: 不安定なデータ(時刻 / ランダム)に依存していないか、snapshot の更新理由 +- **テストランナー整合**: bats / node:test / Vitest など既存パターンに従っているか +- **CI 実行性**: ローカル専用の前提(特定パス・環境変数)に依存していないか + +## severity ガイド + +- **critical**: 公開 API / バグ修正にテストがない、既存テストを根拠なく削除、tautology テスト +- **warning**: エッジケースの取りこぼし、不安定なテスト(flaky)、mock 過剰 +- **info**: より良いアサーション、テストの整理 / 命名改善 + +## skip_when + +ドキュメント・設定ファイルのみの変更ではテスト観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 + warning: 0 +``` + +公開 API / バグ修正にテストがない状態で merge-ready とは判定しない。 diff --git a/.claude/skills/review/perspectives/usability.md b/.claude/skills/review/perspectives/usability.md new file mode 100644 index 0000000..09e3c17 --- /dev/null +++ b/.claude/skills/review/perspectives/usability.md @@ -0,0 +1,43 @@ +--- +name: usability +category: ux +description: CLI 文言・エラーメッセージ・skill argument-hint・README 即座理解性 +applies_when: ["src/**", "**/*.md", "**/SKILL.md", "**/*.ts", "**/*.tsx", "**/*.mjs", "**/*.js", "**/*.py", "**/*.sh"] +skip_when: { diff_only_in: ["tests/**", "**/*.test.*"] } +default_enabled: true +severity_rules: { critical: "ユーザーが詰まる致命的な UX 不備 (無限ループ的な確認、復旧不可能な操作の無確認実行)", warning: "紛らわしいメッセージ、誤解を招く CLI 文言、argument-hint の欠落", info: "より親切な文言、説明追記、UX の細かな改善" } +exit_criteria: { drive_loop: { critical: 0 } } +--- + +# usability — ユーザビリティ / DX + +## 検査項目 + +- **CLI 文言**: help / usage 表示、フラグ名の直感性、`--<flag>` の命名規則統一 +- **エラーメッセージ**: 何が原因で何をすればよいかが伝わるか、ユーザー操作で対処可能か +- **skill / agent の argument-hint**: 期待される引数形式が一目で分かるか +- **README 即座理解性**: 最初の画面で何を提供する skill / package か理解できるか +- **失敗時のリカバリ手順**: ハッピーパス以外で詰まらない設計、再実行可能性 +- **デフォルト値**: 一般的なケースで設定なしで動くか、安全側に倒れているか +- **AskUserQuestion**: ユーザーへの確認が AskUserQuestion を使っているか、テキスト出力で選択肢を列挙していないか(CLAUDE.md ルール) +- **国際化**: メッセージが日本語 / 英語のどちらかに統一されているか、混在していないか + +## severity ガイド + +- **critical**: ユーザーが詰まる致命的な UX 不備(無限ループ的な確認、復旧不可能な操作の無確認実行) +- **warning**: 紛らわしいメッセージ、誤解を招く CLI 文言、argument-hint の欠落 +- **info**: より親切な文言、説明追記、UX の細かな改善 + +## skip_when + +テストのみの変更ではユーザビリティ観点は適用しない。 + +## exit_criteria.drive_loop + +```yaml +exit_criteria: + drive_loop: + critical: 0 +``` + +ユーザビリティの warning は許容(merge 後の継続改善対象)。critical は阻止する。 diff --git a/.claude/skills/topics/SKILL.md b/.claude/skills/topics/SKILL.md new file mode 100644 index 0000000..b941d84 --- /dev/null +++ b/.claude/skills/topics/SKILL.md @@ -0,0 +1,34 @@ +--- +description: GitHub topics 候補を制約検証・人気度測定・broad+narrow / 単数複数比較・ozzy-labs 慣行で選定し適用する。スコープは ozzy-labs 内利用のみ。 +argument-hint: "<candidate-list> [--repo owner/repo] [--apply | --dry-run]" +disable-model-invocation: true +allowed-tools: Bash, Read, AskUserQuestion +--- + +# topics + +`.agents/skills/topics/SKILL.md` を Read し、ワークフロー手順に従う。 + +**重要:** 公式制約 validation(lowercase / hyphen / 50 chars / max 20)、session 内キャッシュ、broad+narrow 5x 比較、単数/複数比較、ozzy-labs 慣行のハードコード(`claude-code` 例外、`*-cli` 除去ルール、`multi-agent` 形固定)はすべて忠実に実行する。 + +## Claude Code 固有の追加事項 + +### 引数解析 + +- `<candidate-list>`: `,` 区切り or 複数引数 +- `--repo owner/repo`: 省略時は `git remote get-url origin` から抽出 +- `--apply`: 確認なしで `gh repo edit --add-topic` を実行 +- `--dry-run`: 適用せず分析のみ +- `--apply` と `--dry-run` 同時指定時は `--dry-run` を優先する + +### 適用確認 + +`--apply` / `--dry-run` どちらも未指定の場合、最終 topics リストを提示した後に AskUserQuestion を呼び出す(`answers` パラメータは設定しない): + +- **「適用する」** → `gh repo edit --add-topic` を実行する +- **「適用しない」** → 分析結果のみ表示して終了する +- **「候補を編集する」** → 終了し、ユーザーに再実行を促す + +### 完了報告・次のアクション提案 + +適用結果(または dry-run 結果)を表示したら終了する。次のアクション提案は行わない。 diff --git a/.commons/sync.yaml b/.commons/sync.yaml index dce1bcd..63e6f39 100644 --- a/.commons/sync.yaml +++ b/.commons/sync.yaml @@ -2,7 +2,7 @@ # 'pinned' is user-editable — add or remove paths freely commit: 5d2e324b9caa3d8501aa3599ac3ad332f749a28e synced_at: 2026-05-06T09:21:50Z -skills_commit: 4d35fa3683789c8b4a8633bf6aa5bf8e5e7bc230 +skills_commit: 6e6c81a65b80ad46fd4e06d6a786b73b54594f70 skills_adapters: - claude-code - codex-cli diff --git a/.gemini/settings.json b/.gemini/settings.json index 8e9e288..843e00c 100644 --- a/.gemini/settings.json +++ b/.gemini/settings.json @@ -1,7 +1 @@ -{ - "context": { - "fileName": [ - "AGENTS.md" - ] - } -} +{ "context": { "fileName": ["AGENTS.md"] } } diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b6505f4..b8a3122 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -37,13 +37,16 @@ mise exec -- lefthook run pre-commit --all-files - `commit` — 変更をステージし、Conventional Commits でコミットする。プッシュや PR 作成は行わない。 - `commit-conventions` — Conventional Commits のメッセージ生成ルール(Type/Scope 判定表、フォーマット)。他スキルから参照される。 -- `drive` — Issue から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。Issue 番号またはテキスト指示を受け取る。オプションでマージまで実行可能。 +- `drive` — Issue または指示から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。単一/複数の Issue/PR と明示依存記法に対応。オプションでマージまで実行可能。 +- `health` — リポジトリ改修中に意図せず残る状態(working tree, stash, branch, worktree, PR, issue, actions など)と skill catalog 整合性を一発で確認し、16 領域のステータス表で俯瞰しつつ各項目に固定語彙の推奨アクションを inline で付与して報告する。`--deep` 指定時は `要確認` 項目を read-only コマンドで追加調査し、機械判定可能な範囲でラベルを格上げする。検査と提示のみで、削除・close 等の実行は行わない。 - `implement` — Issue または指示をもとに、ブランチ作成・実装計画・コード変更を行う。Issue 番号またはテキスト指示を受け取る。 - `lint` — 全リンターを自動修正付きで実行し、結果を報告する。コード品質チェック、フォーマット、型チェック、セキュリティスキャンを含む。 - `lint-rules` — 拡張子別リンター・フォーマッターのコマンド対応表と型チェックルール。他スキルから参照される。 +- `phase-issue` — Phase-N tracking issue を生成する。cross-session handoff context、決定事項表、PR ごとのタスク、DoD、Phase N+1 outlook を含む構造化された issue body を組み立てて gh issue create で起票する。引数で全項目を渡す非対話モードと、不足分を補う対話モード(Claude Code companion)に対応する。 - `pr` — コミット済みの変更をリモートにプッシュし、PR を作成・更新する。 -- `review` — コード変更や PR をレビューし、問題点・改善案を報告する。PR 番号または空(ワーキングツリー)を受け取る。 +- `review` — コード変更や PR を 11 観点(perspectives)でレビューし、JSON 構造化出力 + 人間可読レポートで報告する。quick / deep モードを切替可能。PR 番号またはワーキングツリー差分を入力に取る。 - `ship` — lint・コミット・PR 作成を一括実行する。変更に対して lint → コミット → PR 作成を順に実行する統合パイプライン。 - `test` — ビルド・テスト・型チェックを実行し、結果を報告する。 +- `topics` — GitHub topics 候補を制約検証・人気度測定・broad+narrow / 単数複数比較・ozzy-labs 慣行ハードコードで選定し、`gh repo edit --add-topic` で適用する。スコープは ozzy-labs 内利用のみ。 <!-- end: @ozzylabs/skills --> diff --git a/AGENTS.md b/AGENTS.md index f6874a8..a874802 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,14 +50,17 @@ pnpm run build # プロダクションビルド - `commit` — 変更をステージし、Conventional Commits でコミットする。プッシュや PR 作成は行わない。 - `commit-conventions` — Conventional Commits のメッセージ生成ルール(Type/Scope 判定表、フォーマット)。他スキルから参照される。 -- `drive` — Issue から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。Issue 番号またはテキスト指示を受け取る。オプションでマージまで実行可能。 +- `drive` — Issue または指示から実装・PR 作成・セルフレビュー・修正を自動で回し、merge-ready な PR を出す。単一/複数の Issue/PR と明示依存記法に対応。オプションでマージまで実行可能。 +- `health` — リポジトリ改修中に意図せず残る状態(working tree, stash, branch, worktree, PR, issue, actions など)と skill catalog 整合性を一発で確認し、16 領域のステータス表で俯瞰しつつ各項目に固定語彙の推奨アクションを inline で付与して報告する。`--deep` 指定時は `要確認` 項目を read-only コマンドで追加調査し、機械判定可能な範囲でラベルを格上げする。検査と提示のみで、削除・close 等の実行は行わない。 - `implement` — Issue または指示をもとに、ブランチ作成・実装計画・コード変更を行う。Issue 番号またはテキスト指示を受け取る。 - `lint` — 全リンターを自動修正付きで実行し、結果を報告する。コード品質チェック、フォーマット、型チェック、セキュリティスキャンを含む。 - `lint-rules` — 拡張子別リンター・フォーマッターのコマンド対応表と型チェックルール。他スキルから参照される。 +- `phase-issue` — Phase-N tracking issue を生成する。cross-session handoff context、決定事項表、PR ごとのタスク、DoD、Phase N+1 outlook を含む構造化された issue body を組み立てて gh issue create で起票する。引数で全項目を渡す非対話モードと、不足分を補う対話モード(Claude Code companion)に対応する。 - `pr` — コミット済みの変更をリモートにプッシュし、PR を作成・更新する。 -- `review` — コード変更や PR をレビューし、問題点・改善案を報告する。PR 番号または空(ワーキングツリー)を受け取る。 +- `review` — コード変更や PR を 11 観点(perspectives)でレビューし、JSON 構造化出力 + 人間可読レポートで報告する。quick / deep モードを切替可能。PR 番号またはワーキングツリー差分を入力に取る。 - `ship` — lint・コミット・PR 作成を一括実行する。変更に対して lint → コミット → PR 作成を順に実行する統合パイプライン。 - `test` — ビルド・テスト・型チェックを実行し、結果を報告する。 +- `topics` — GitHub topics 候補を制約検証・人気度測定・broad+narrow / 単数複数比較・ozzy-labs 慣行ハードコードで選定し、`gh repo edit --add-topic` で適用する。スコープは ozzy-labs 内利用のみ。 <!-- end: @ozzylabs/skills --> diff --git a/package.json b/package.json index 125767d..8906e7c 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ ], "overrides": { "dompurify@<3.4.0": "^3.4.0", + "mermaid": "^11.15.0", "postcss@^8.0.0": "^8.5.10", "uuid@^11.0.0": "^14.0.0", "vite": "^8.0.10" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ead4bfb..2b0fce8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,7 @@ settings: overrides: dompurify@<3.4.0: ^3.4.0 + mermaid: ^11.15.0 postcss@^8.0.0: ^8.5.10 uuid@^11.0.0: ^14.0.0 vite: ^8.0.10 @@ -104,20 +105,8 @@ packages: resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} engines: {node: '>=18'} - '@chevrotain/cst-dts-gen@12.0.0': - resolution: {integrity: sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==} - - '@chevrotain/gast@12.0.0': - resolution: {integrity: sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==} - - '@chevrotain/regexp-to-ast@12.0.0': - resolution: {integrity: sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==} - - '@chevrotain/types@12.0.0': - resolution: {integrity: sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==} - - '@chevrotain/utils@12.0.0': - resolution: {integrity: sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==} + '@chevrotain/types@11.1.2': + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} '@clack/core@1.2.0': resolution: {integrity: sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg==} @@ -485,8 +474,8 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - '@mermaid-js/parser@1.1.0': - resolution: {integrity: sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==} + '@mermaid-js/parser@1.1.1': + resolution: {integrity: sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==} '@napi-rs/wasm-runtime@1.1.4': resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} @@ -1105,15 +1094,6 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - chevrotain-allstar@0.4.1: - resolution: {integrity: sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA==} - peerDependencies: - chevrotain: ^12.0.0 - - chevrotain@12.0.0: - resolution: {integrity: sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==} - engines: {node: '>=22.0.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1445,6 +1425,9 @@ packages: es-module-lexer@2.0.0: resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-toolkit@1.46.1: + resolution: {integrity: sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==} + esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -1702,10 +1685,6 @@ packages: resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} engines: {node: '>= 8'} - langium@4.2.2: - resolution: {integrity: sha512-JUshTRAfHI4/MF9dH2WupvjSXyn8JBuUEWazB8ZVJUtXutT0doDlAv1XKbZ1Pb5sMexa8FF4CFBc0iiul7gbUQ==} - engines: {node: '>=20.10.0', npm: '>=10.2.3'} - layout-base@1.0.2: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} @@ -1893,8 +1872,8 @@ packages: playwright: optional: true - mermaid@11.14.0: - resolution: {integrity: sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==} + mermaid@11.15.0: + resolution: {integrity: sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==} micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -2666,26 +2645,6 @@ packages: jsdom: optional: true - vscode-jsonrpc@8.2.0: - resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} - engines: {node: '>=14.0.0'} - - vscode-languageserver-protocol@3.17.5: - resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - - vscode-languageserver-textdocument@1.0.12: - resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} - - vscode-languageserver-types@3.17.5: - resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - - vscode-languageserver@9.0.1: - resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} - hasBin: true - - vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -2877,20 +2836,7 @@ snapshots: dependencies: fontkitten: 1.0.3 - '@chevrotain/cst-dts-gen@12.0.0': - dependencies: - '@chevrotain/gast': 12.0.0 - '@chevrotain/types': 12.0.0 - - '@chevrotain/gast@12.0.0': - dependencies: - '@chevrotain/types': 12.0.0 - - '@chevrotain/regexp-to-ast@12.0.0': {} - - '@chevrotain/types@12.0.0': {} - - '@chevrotain/utils@12.0.0': {} + '@chevrotain/types@11.1.2': {} '@clack/core@1.2.0': dependencies: @@ -3176,9 +3122,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@mermaid-js/parser@1.1.0': + '@mermaid-js/parser@1.1.1': dependencies: - langium: 4.2.2 + '@chevrotain/types': 11.1.2 '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: @@ -3796,19 +3742,6 @@ snapshots: character-reference-invalid@2.0.1: {} - chevrotain-allstar@0.4.1(chevrotain@12.0.0): - dependencies: - chevrotain: 12.0.0 - lodash-es: 4.18.1 - - chevrotain@12.0.0: - dependencies: - '@chevrotain/cst-dts-gen': 12.0.0 - '@chevrotain/gast': 12.0.0 - '@chevrotain/regexp-to-ast': 12.0.0 - '@chevrotain/types': 12.0.0 - '@chevrotain/utils': 12.0.0 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -4133,6 +4066,8 @@ snapshots: es-module-lexer@2.0.0: {} + es-toolkit@1.46.1: {} + esast-util-from-estree@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 @@ -4546,15 +4481,6 @@ snapshots: klona@2.0.6: {} - langium@4.2.2: - dependencies: - '@chevrotain/regexp-to-ast': 12.0.0 - chevrotain: 12.0.0 - chevrotain-allstar: 0.4.1(chevrotain@12.0.0) - vscode-languageserver: 9.0.1 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 - layout-base@1.0.2: {} layout-base@2.0.1: {} @@ -4827,13 +4753,13 @@ snapshots: dependencies: '@fortawesome/fontawesome-free': 6.7.2 katex: 0.16.45 - mermaid: 11.14.0 + mermaid: 11.15.0 - mermaid@11.14.0: + mermaid@11.15.0: dependencies: '@braintree/sanitize-url': 7.1.2 '@iconify/utils': 3.1.0 - '@mermaid-js/parser': 1.1.0 + '@mermaid-js/parser': 1.1.1 '@types/d3': 7.4.3 '@upsetjs/venn.js': 2.0.0 cytoscape: 3.33.2 @@ -4844,9 +4770,9 @@ snapshots: dagre-d3-es: 7.0.14 dayjs: 1.11.20 dompurify: 3.4.0 + es-toolkit: 1.46.1 katex: 0.16.45 khroma: 2.1.0 - lodash-es: 4.18.1 marked: 16.4.2 roughjs: 4.6.6 stylis: 4.3.6 @@ -5866,23 +5792,6 @@ snapshots: transitivePeerDependencies: - msw - vscode-jsonrpc@8.2.0: {} - - vscode-languageserver-protocol@3.17.5: - dependencies: - vscode-jsonrpc: 8.2.0 - vscode-languageserver-types: 3.17.5 - - vscode-languageserver-textdocument@1.0.12: {} - - vscode-languageserver-types@3.17.5: {} - - vscode-languageserver@9.0.1: - dependencies: - vscode-languageserver-protocol: 3.17.5 - - vscode-uri@3.1.0: {} - web-namespaces@2.0.1: {} which-pm-runs@1.1.0: {}