Skip to content

chore: サブエージェントが PR レビューコメントを投稿できる権限を追加 - #142

Merged
fumtas1k merged 1 commit into
developfrom
chore/allow-subagent-pr-review-and-tmp-write
Apr 30, 2026
Merged

chore: サブエージェントが PR レビューコメントを投稿できる権限を追加#142
fumtas1k merged 1 commit into
developfrom
chore/allow-subagent-pr-review-and-tmp-write

Conversation

@fumtas1k

Copy link
Copy Markdown
Owner

概要

サブエージェントには対話 UI が無く、未承認の tool 呼び出しは即拒否される。
PR レビュー作業を並列化する際、レビュー本文の投稿および TMPDIR への一時ファイル
書き出しが拒否されていた問題を解消する。

背景

PR #133 / #134 のレビューを subagent 並列で実施した際、以下が拒否された:

  • gh pr review --comment -F <body> — 投稿用 gh コマンド
  • Write(/tmp/claude/...) — TMPDIR への一時本文ファイル書き出し

親エージェント(対話セッション)では承認 UI 経由で通せるが、subagent 経由では
ユーザー応答が無いため即拒否となり、本文を assistant message として返す
ワークアラウンドが必要だった。

変更内容

.claude/settings.jsonpermissions.allow に以下を追加:

1. gh pr 系コメント投稿コマンド

  • Bash(gh pr review*) — レビュー投稿(--comment / --approve / --request-changes
  • Bash(gh pr comment*) — Issue スタイルコメント投稿

PR メタデータを変更する gh pr edit / gh pr create / gh pr merge / gh pr close
は既存通り ask に残す(誤操作リスクが高いため)。

2. TMPDIR 配下への書き出し

  • Write(/tmp/claude/**)
  • Write(/tmp/claude-*/**)
  • Write(/private/tmp/claude/**) — macOS の /tmp symlink 先
  • Write(/private/tmp/claude-*/**) — 同上

サンドボックス自身は \$TMPDIR を allow しているが、Write tool 側の permission
で別途明示が必要なため。

トレードオフ

  • Bash(gh pr review*)--approve / --request-changes も含む。
    subagent が暴走して approve することは仕組み上は可能になる。
    ただし subagent の prompt 側で「approve しないこと」を明示しており、
    実用上のリスクは低いと判断。気になる場合はフォローアップで
    Bash(gh pr review --approve*)ask に追加する案も可。

動作確認

  • 本 PR を作成するまでの一連の動作(branch 作成 / commit / push)が pre-commit hook
    と JSON validity チェックを通過することを確認

関連

サブエージェントには対話 UI が無いため、未承認の tool 呼び出しは即拒否される。
PR レビュー作業を並列化する際にレビュー本文の投稿および TMPDIR への一時ファイル
書き出しが拒否されていた問題を解消する。

- Bash(gh pr review*) / Bash(gh pr comment*) を allow に追加
  - PR メタデータを変更する gh pr edit/create/merge/close は ask に残置
- Write(/tmp/claude*/**) / Write(/private/tmp/claude*/**) を allow に追加
  - サンドボックス TMPDIR 配下への一時ファイル書き出しを許可
@fumtas1k
fumtas1k changed the base branch from main to develop April 30, 2026 03:21
@fumtas1k fumtas1k self-assigned this Apr 30, 2026
@fumtas1k
fumtas1k merged commit 4daba17 into develop Apr 30, 2026
2 checks passed
@fumtas1k
fumtas1k deleted the chore/allow-subagent-pr-review-and-tmp-write branch April 30, 2026 03:36
fumtas1k added a commit that referenced this pull request Apr 30, 2026
* docs: Cloudflare Web Analytics 導入を却下した決定を [051] として記録

プロジェクトの「外部送信ゼロ・外部リソースゼロ・トラッキングなし」方針と
Cloudflare Web Analytics(beacon 自動注入)が直接矛盾するため、有効化しない判断とした。
その経緯と却下理由を decisions.md に [051] として追記する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: [051] サーバー側集計ログに関する誤記を修正

Cloudflare Pages のトラフィック集計は Web Analytics を有効化しないと
表示されない。beacon なしで見られるという誤った記述を削除し、
正確に「把握する手段が無い」と修正する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: dev server / Playwright をサンドボックス除外コマンドに追加

`.claude/settings.json` のサンドボックスは listen() を拒否するため、
`npm run dev` / `npm run test:e2e` 経由の Astro dev server が
EPERM で起動できなかった。`excludedCommands` に dev / preview /
e2e / playwright / astro 系コマンドを追加し、ローカル listen を
必要とするコマンドだけサンドボックス外で実行できるようにする。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: .claude/worktrees/ を gitignore に追加

Claude Code の worktrees ディレクトリは既に `.worktrees/` を ignore
しているが、Agent Teams 機能で生成される `.claude/worktrees/` が
別パスのため取り残されていた。誤コミット防止のため追加する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(tools): QRリーダーツールを新設(qr-reader)

カメラまたは画像ファイルからQRコードを読み取り、テキストを表示するツールを追加。

- `src/utils/qr-reader.ts`: URL/テキスト判別純粋関数 `detectQrContent()`
- `src/components/tools/QrReader.tsx`: カメラ+画像アップロード対応コンポーネント
- `src/pages/tools/qr-reader.astro`: Astro ページラッパー
- `src/data/tools.ts`: `qr-reader` エントリ追加(category: convert)
- ユニットテスト 15件(URL/テキスト判別の境界値を網羅)
- E2E テスト(.claude/settings.json 修正後に実行予定)
- SPEC.md / README.md / docs/decisions.md 更新

HTTP/HTTPS URL のみ検出してフィッシング警告付きリンクを表示。
javascript: 等の危険スキームは text として扱い自動リンク化しない([051])。
既存の useQrCamera フック・jsQR ライブラリを再利用して実装量を最小化。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(e2e): qr-reader の Playwright strict mode 違反を修正

- 'カメラ' ボタンの locator に exact: true を追加('カメラを起動' との多重マッチを解消)
- 'example.com' テキストの locator に exact: true を追加(URL 全体を含む <pre> との多重マッチを解消)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(qr-reader): コピーボタンと再スキャンボタンの高さ・スタイルを統一

- rescanButtonStyle を CopyButton と同じ視覚パラメータに合わせる
  (lineHeight 1 / padding 0.5rem 0.75rem / borderRadius 0.25rem / fontWeight 700)
- 両ボタンを flex flex-wrap items-center gap-2 でラップし
  横並びレイアウトを明示的に制御

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(qr-reader): ToolIcon.astro に qr-reader SVGアイコンを追加

QRの3つのファインダーパターン + データモジュール2点 + 水平スキャン線で構成。
qr-code(データモジュール4点)と造形を共有しつつ、スキャン線で「読取」を表現。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(qr-reader): レビュー指摘を修正(必須2件 + 推奨5件)

必須対応:
- canvas.getContext('2d') の non-null assertion を削除し、null 時に
  エラーメッセージを表示するガードを追加(iOS Safari OOM 対策)
- 長辺 1600px 超の画像を jsQR 前にダウンスケールし、高解像度
  スマホ写真での OOM・フリーズを防止

推奨対応:
- input[type=file] を display:none から visually-hidden に変更し
  キーボード・スクリーンリーダーから操作可能にする(A11y)
- モード切替時に decoded / decodeError / cameraError をクリアし
  古い結果との並存を防止
- useEffect 依存配列の参照を const { stopCamera } = camera で
  分割代入し、exhaustive-deps 警告を回避
- E2E: not.toBeVisible() → toHaveCount(0) に変更(DOM から
  削除されることの検証+auto-retry の安定化)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(qr-reader): e.target.value クリアの意図コメントを追加

同名ファイルの再選択を可能にするパターンであることを明示し、
onload 完了前にクリアする理由(File は file 変数で参照済み)を説明。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: 設定ファイル相互変換ツール (config-converter) を追加 (#133)

* feat: config-converter ユーティリティ層とユニットテストを実装

JSON/YAML/TOML/dotenv の相互変換・整形ユーティリティと
JSON Schema バリデーション(ajv draft-04/7対応)を追加。
TDD でテストを先行作成し、全 224 テストがパスすることを確認済み。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: config-converter 依存パッケージ追加とユーティリティ層の品質改善

- yaml, smol-toml, ajv, ajv-formats を package.json に明示追加
- toml.ts: stringifyToml に入力型ガードを追加
- dotenv.ts: quoteValue ヘルパで " を含む値のエスケープを修正
- yaml.ts: formatYaml にエラーハンドリングを追加
- schema-validator.ts: 非オブジェクトスキーマの入力ガードを追加
- テスト追加: schema-validator.test.ts 新規作成 (4件)、toml/dotenv テスト追加

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: ConfigConverter React コンポーネントを実装

From/To ToggleGroup・InputField/OutputField・警告バナー・
JSON Schema 検証パネル(dynamic import)・DownloadButton を含む

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: config-converter をツール一覧・Astro ページ・アイコンに追加

- src/data/tools.ts に Tool エントリ追加 (category: convert)
- src/pages/tools/config-converter.astro を作成
- src/components/ui/ToolIcon.astro に {} アイコンを追加

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(e2e): config-converter の E2E テストを追加

6ケース: YAML→JSON変換、JSON→YAML変換、ネスト拒否エラー、
クリア動作、JSON Schema 検証成功・失敗

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: config-converter のドキュメントを更新

- README.md: 変換・解析テーブルに設定ファイル相互変換を追加
- SPEC.md: ライブラリテーブル・ツール一覧・ディレクトリ構成・実装チェックリストを更新
- docs/decisions.md: [051] yaml/smol-toml/ajv 選定理由・HCL後送り判断を記録

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: develop の変更をマージ(Cloudflare Analytics 決定 [051]、.gitignore 更新)

- docs/decisions.md: develop で追加された [051] Cloudflare Web Analytics 却下エントリを挿入し、
  当ブランチの config-converter ライブラリ選定エントリを [052] に繰り下げ
- .gitignore: develop で追加された `.claude/worktrees/` 除外ルールを追加

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(e2e): strict mode violation を修正(exact: true、パス文字列を使用)

- getByLabel('JSON') → getByLabel('JSON', { exact: true }): from=to=JSON 時に
  入力ラベル「JSON (整形)」と出力ラベル「JSON」の2要素にマッチする問題を解消
- getByRole('button', { name: '検証する' }) → { exact: true }: 「▶ JSON Schema で検証する」
  ボタンにも部分マッチしていた問題を解消
- getByText(/age/) → getByText('/age'): 正規表現がテキストエリア内の "age" を含む
  全要素にマッチしていた問題を解消(パスは "/age" とスラッシュ付きなので区別可能)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: レビュー指摘対応(Critical 1-3、推奨 4-5-7、提案 10)

[Critical 1] ajv-draft-04 を dependencies に明示登録
- package.json に "ajv-draft-04": "^1.0.0" を追加
- dev tree の transitive dep に依存していたためクリーンインストール時にビルド失敗の恐れがあった
- docs/decisions.md [052] にも理由を追記

[Critical 2] handleValidate: convert() の .output を参照していなかったバグを修正
- data = convert(output, to, 'json') → data = JSON.parse(convert(output, to, 'json').output)
- to=yaml/toml/dotenv 時にスキーマ検証対象が ConvertResult オブジェクトになっていた
- to=yaml でスキーマ検証が動作することを確認する E2E テストを追加

[Critical 3] .env パーサーがインラインコメントを無視していた問題を修正
- KEY=value # comment → value として取り込むよう \s+# パターンで除去
- クォート内の # は除去しない("value # not a comment" → そのまま保持)

[推奨 4] convert() の二重呼び出しを解消
- useRef<string[]> で warnings をキャプチャし、別 useEffect での再 convert() を廃止

[推奨 5] E2E テストの waitForTimeout(500) を削除
- expect(...).toBeVisible() の auto-retry に委ねる(7件全パス確認)

[推奨 7] formatLabel ヘルパー関数を削除
- FORMAT_LABELS[f] を直接参照するよう変更

[提案 10] ajv 起因の英語エラーメッセージを日本語に統一
- 日本語文字を含まないエラーメッセージは "JSON Schema の解析に失敗しました" で上書き

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 再レビュー指摘対応(推奨 A・B・C、提案 D)

[推奨 A] parseDotenv インラインコメントのユニットテスト追加
- クォートなし値の # 除去、クォート内保持、URL フラグメント保持、
  クォート付き値+末尾コメントの 5 ケースを dotenv.test.ts に追加
- tests: 231 → 236 件

[推奨 B] クォート付き値 + 末尾コメント混在ケースを修正
- KEY="hello" # comment → "hello"(クォート残存)だったバグを修正
- 正規表現で /^"((?:[^"\\]|\\.)*)"\\s*(?:#.*)?$/ により
  クォート内容抽出と末尾コメント除去を一括処理
- ループ変数 raw とのシャドウイングを避けるため内部変数を rawValue に改名

[推奨 C] package.json dependencies をアルファベット順に整列
- ajv / ajv-draft-04 / ajv-formats / smol-toml を正しい位置に移動
- lockfile との整合性も改善

[提案 D] フォーマット切替時に validationResult をクリア
- handleFromChange / handleToChange に setValidationResult(null) を追加
- 切替後の出力に対して古い検証結果が残る誤認を防止

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(config-converter): 出力変更時に validationResult をクリアする

入力を編集して output が変わっても古いスキーマ検証結果(成功/失敗)が
残り続ける問題を修正。output/error を監視する既存 useEffect に
setValidationResult(null) を追加し、出力変化と同時に検証結果を無効化する。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: サブエージェントが PR レビューコメントを投稿できる権限を追加 (#142)

サブエージェントには対話 UI が無いため、未承認の tool 呼び出しは即拒否される。
PR レビュー作業を並列化する際にレビュー本文の投稿および TMPDIR への一時ファイル
書き出しが拒否されていた問題を解消する。

- Bash(gh pr review*) / Bash(gh pr comment*) を allow に追加
  - PR メタデータを変更する gh pr edit/create/merge/close は ask に残置
- Write(/tmp/claude*/**) / Write(/private/tmp/claude*/**) を allow に追加
  - サンドボックス TMPDIR 配下への一時ファイル書き出しを許可

* docs: PR ベースは必ず develop と CLAUDE.md 最重要ルールに昇格 (#143)

`docs/shared-agent-rules.md §6.3` に既に明記されているが、Claude Code の
system prompt が毎セッション "Main branch (you will usually use this for PRs): main"
と表示してくるため、shared-agent-rules.md を読まずに main 向け PR を作る事故が
繰り返し発生していた。

CLAUDE.md は毎セッション読み込まれるため、最重要ルールの 1 行に昇格させて
即時可視化する。

* fix(qr-reader): 非HTTPS環境で専用エラーメッセージを表示 (#135)

navigator.mediaDevices が undefined の場合に getUserMedia を呼ぶと
TypeError となり「カメラの起動に失敗しました」という汎用メッセージ
が出て原因が特定できなかった。startCamera 冒頭で早期 guard を入れ、
HTTPS または localhost が必要である旨を明示する。

* fix(config-converter): parseDotenv でエスケープシーケンスを解除 (#140)

stringifyDotenv は値の \\ と \" をエスケープして書き出すが、
parseDotenv 側でアンエスケープしておらず、ラウンドトリップが
壊れていた。

unescapeDotenv ヘルパーを追加し、DQ/SQ ブランチで適用。単発走査で
クォート種・バックスラッシュのみアンエスケープするため、
\\\" のような連鎖でも順序問題なく安全に解除できる。

テスト 5 件追加(DQ 内 \"・\\・連鎖、SQ 内 \'、ラウンドトリップ)。

* test(qr-reader): HTTPS 必須メッセージの E2E テストを追加 (#135)

レビュー指摘(feedback_e2e_tests)対応。Playwright の addInitScript で
navigator.mediaDevices を undefined にし、カメラ起動時に新しいエラー
メッセージが表示されることを検証する E2E ケースを追加。

* docs(decisions): .env パーサに expand オプション追加余地を明記

レビューでの指摘を受け、ダブルクォート内のエスケープ方針と
将来 `expand` オプション(POSIX dotenv 相当の `\n` 改行展開)を
追加できる旨を 052 章 .env セクションに 1 行追記。

* docs(rules): 先送り時の issue 化を 6.4 章として追加

「別 PR で対応」「後で追記」と口頭で先送りすると形骸化して
実施されないことがレビューで指摘されたため、共通規約に
「先送り時は必ず issue を起票し PR に番号を貼る」ルールを追加。

* test(qr-reader): HTTPS guard テストの再ナビゲート意図をコメント明記

レビュー任意指摘 (#144) を受け、beforeEach で goto 済みでも
addInitScript は次回ロードから反映されるため再 goto が必要、
という意図を 1 行コメントで補足。挙動変更なし。

* ci: developブランチへのプッシュ時もカバレッジバッジを更新する (#146)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(config-converter): 変換先フォーマット変更時に入力テキストを保持する (#147)

* fix(config-converter): 変換先変更時に入力テキストを保持する

変換先のみ変更した場合、handleToChange から reset() の呼び出しを削除し
入力テキストを保持したまま出力のみ再計算するよう修正。
変換元変更時(handleFromChange)は従来どおり入力をクリアする。

Closes #141

* test(config-converter): E2E テストのナビゲーションパターンを修正

beforeEach が既に /tools/config-converter へ遷移済みのため、
新規テスト内の page.goto('/') と waitForReactHydration を削除。

* test(config-converter): E2E テストのロケータとアサーションをプロジェクト規約に準拠させる

- waitForFunction + querySelectorAll を expect().toHaveValue() に置換
- locator('textarea[readonly]') を getByLabel() に置換
- toContainText を toHaveValue に置換(textarea は value を検証する必要があるため)

docs/ui-conventions.md 3.2 章のロケータ規約に準拠。

* feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加 (#148)

スキーマ textarea に Cmd+Enter (Mac) / Ctrl+Enter (Win/Linux) で検証を実行するショートカットを追加。InputField に optional な onKeyDown prop を追加して forward する形で実装。isComposing ガード・aria-keyshortcuts・kbd 要素によるアクセシビリティ対応も含む。

Closes #137

* ci: カバレッジバッジ更新を Gist 経由に変更 (#151)

ブランチ保護ルールにより github-actions[bot] からの develop / main への
直接 push が拒否されるため、外部 Gist に JSON を書き込み shields.io の
endpoint バッジで表示する方式に切り替える。

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
fumtas1k added a commit that referenced this pull request Apr 30, 2026
* docs: Cloudflare Web Analytics 導入を却下した決定を [051] として記録

プロジェクトの「外部送信ゼロ・外部リソースゼロ・トラッキングなし」方針と
Cloudflare Web Analytics(beacon 自動注入)が直接矛盾するため、有効化しない判断とした。
その経緯と却下理由を decisions.md に [051] として追記する。



* docs: [051] サーバー側集計ログに関する誤記を修正

Cloudflare Pages のトラフィック集計は Web Analytics を有効化しないと
表示されない。beacon なしで見られるという誤った記述を削除し、
正確に「把握する手段が無い」と修正する。



* chore: dev server / Playwright をサンドボックス除外コマンドに追加

`.claude/settings.json` のサンドボックスは listen() を拒否するため、
`npm run dev` / `npm run test:e2e` 経由の Astro dev server が
EPERM で起動できなかった。`excludedCommands` に dev / preview /
e2e / playwright / astro 系コマンドを追加し、ローカル listen を
必要とするコマンドだけサンドボックス外で実行できるようにする。



* chore: .claude/worktrees/ を gitignore に追加

Claude Code の worktrees ディレクトリは既に `.worktrees/` を ignore
しているが、Agent Teams 機能で生成される `.claude/worktrees/` が
別パスのため取り残されていた。誤コミット防止のため追加する。



* feat(tools): QRリーダーツールを新設(qr-reader)

カメラまたは画像ファイルからQRコードを読み取り、テキストを表示するツールを追加。

- `src/utils/qr-reader.ts`: URL/テキスト判別純粋関数 `detectQrContent()`
- `src/components/tools/QrReader.tsx`: カメラ+画像アップロード対応コンポーネント
- `src/pages/tools/qr-reader.astro`: Astro ページラッパー
- `src/data/tools.ts`: `qr-reader` エントリ追加(category: convert)
- ユニットテスト 15件(URL/テキスト判別の境界値を網羅)
- E2E テスト(.claude/settings.json 修正後に実行予定)
- SPEC.md / README.md / docs/decisions.md 更新

HTTP/HTTPS URL のみ検出してフィッシング警告付きリンクを表示。
javascript: 等の危険スキームは text として扱い自動リンク化しない([051])。
既存の useQrCamera フック・jsQR ライブラリを再利用して実装量を最小化。



* test(e2e): qr-reader の Playwright strict mode 違反を修正

- 'カメラ' ボタンの locator に exact: true を追加('カメラを起動' との多重マッチを解消)
- 'example.com' テキストの locator に exact: true を追加(URL 全体を含む <pre> との多重マッチを解消)



* fix(qr-reader): コピーボタンと再スキャンボタンの高さ・スタイルを統一

- rescanButtonStyle を CopyButton と同じ視覚パラメータに合わせる
  (lineHeight 1 / padding 0.5rem 0.75rem / borderRadius 0.25rem / fontWeight 700)
- 両ボタンを flex flex-wrap items-center gap-2 でラップし
  横並びレイアウトを明示的に制御



* feat(qr-reader): ToolIcon.astro に qr-reader SVGアイコンを追加

QRの3つのファインダーパターン + データモジュール2点 + 水平スキャン線で構成。
qr-code(データモジュール4点)と造形を共有しつつ、スキャン線で「読取」を表現。



* fix(qr-reader): レビュー指摘を修正(必須2件 + 推奨5件)

必須対応:
- canvas.getContext('2d') の non-null assertion を削除し、null 時に
  エラーメッセージを表示するガードを追加(iOS Safari OOM 対策)
- 長辺 1600px 超の画像を jsQR 前にダウンスケールし、高解像度
  スマホ写真での OOM・フリーズを防止

推奨対応:
- input[type=file] を display:none から visually-hidden に変更し
  キーボード・スクリーンリーダーから操作可能にする(A11y)
- モード切替時に decoded / decodeError / cameraError をクリアし
  古い結果との並存を防止
- useEffect 依存配列の参照を const { stopCamera } = camera で
  分割代入し、exhaustive-deps 警告を回避
- E2E: not.toBeVisible() → toHaveCount(0) に変更(DOM から
  削除されることの検証+auto-retry の安定化)



* docs(qr-reader): e.target.value クリアの意図コメントを追加

同名ファイルの再選択を可能にするパターンであることを明示し、
onload 完了前にクリアする理由(File は file 変数で参照済み)を説明。



* feat: 設定ファイル相互変換ツール (config-converter) を追加 (#133)

* feat: config-converter ユーティリティ層とユニットテストを実装

JSON/YAML/TOML/dotenv の相互変換・整形ユーティリティと
JSON Schema バリデーション(ajv draft-04/7対応)を追加。
TDD でテストを先行作成し、全 224 テストがパスすることを確認済み。



* feat: config-converter 依存パッケージ追加とユーティリティ層の品質改善

- yaml, smol-toml, ajv, ajv-formats を package.json に明示追加
- toml.ts: stringifyToml に入力型ガードを追加
- dotenv.ts: quoteValue ヘルパで " を含む値のエスケープを修正
- yaml.ts: formatYaml にエラーハンドリングを追加
- schema-validator.ts: 非オブジェクトスキーマの入力ガードを追加
- テスト追加: schema-validator.test.ts 新規作成 (4件)、toml/dotenv テスト追加



* feat: ConfigConverter React コンポーネントを実装

From/To ToggleGroup・InputField/OutputField・警告バナー・
JSON Schema 検証パネル(dynamic import)・DownloadButton を含む



* feat: config-converter をツール一覧・Astro ページ・アイコンに追加

- src/data/tools.ts に Tool エントリ追加 (category: convert)
- src/pages/tools/config-converter.astro を作成
- src/components/ui/ToolIcon.astro に {} アイコンを追加



* test(e2e): config-converter の E2E テストを追加

6ケース: YAML→JSON変換、JSON→YAML変換、ネスト拒否エラー、
クリア動作、JSON Schema 検証成功・失敗



* docs: config-converter のドキュメントを更新

- README.md: 変換・解析テーブルに設定ファイル相互変換を追加
- SPEC.md: ライブラリテーブル・ツール一覧・ディレクトリ構成・実装チェックリストを更新
- docs/decisions.md: [051] yaml/smol-toml/ajv 選定理由・HCL後送り判断を記録



* chore: develop の変更をマージ(Cloudflare Analytics 決定 [051]、.gitignore 更新)

- docs/decisions.md: develop で追加された [051] Cloudflare Web Analytics 却下エントリを挿入し、
  当ブランチの config-converter ライブラリ選定エントリを [052] に繰り下げ
- .gitignore: develop で追加された `.claude/worktrees/` 除外ルールを追加



* fix(e2e): strict mode violation を修正(exact: true、パス文字列を使用)

- getByLabel('JSON') → getByLabel('JSON', { exact: true }): from=to=JSON 時に
  入力ラベル「JSON (整形)」と出力ラベル「JSON」の2要素にマッチする問題を解消
- getByRole('button', { name: '検証する' }) → { exact: true }: 「▶ JSON Schema で検証する」
  ボタンにも部分マッチしていた問題を解消
- getByText(/age/) → getByText('/age'): 正規表現がテキストエリア内の "age" を含む
  全要素にマッチしていた問題を解消(パスは "/age" とスラッシュ付きなので区別可能)



* fix: レビュー指摘対応(Critical 1-3、推奨 4-5-7、提案 10)

[Critical 1] ajv-draft-04 を dependencies に明示登録
- package.json に "ajv-draft-04": "^1.0.0" を追加
- dev tree の transitive dep に依存していたためクリーンインストール時にビルド失敗の恐れがあった
- docs/decisions.md [052] にも理由を追記

[Critical 2] handleValidate: convert() の .output を参照していなかったバグを修正
- data = convert(output, to, 'json') → data = JSON.parse(convert(output, to, 'json').output)
- to=yaml/toml/dotenv 時にスキーマ検証対象が ConvertResult オブジェクトになっていた
- to=yaml でスキーマ検証が動作することを確認する E2E テストを追加

[Critical 3] .env パーサーがインラインコメントを無視していた問題を修正
- KEY=value # comment → value として取り込むよう \s+# パターンで除去
- クォート内の # は除去しない("value # not a comment" → そのまま保持)

[推奨 4] convert() の二重呼び出しを解消
- useRef<string[]> で warnings をキャプチャし、別 useEffect での再 convert() を廃止

[推奨 5] E2E テストの waitForTimeout(500) を削除
- expect(...).toBeVisible() の auto-retry に委ねる(7件全パス確認)

[推奨 7] formatLabel ヘルパー関数を削除
- FORMAT_LABELS[f] を直接参照するよう変更

[提案 10] ajv 起因の英語エラーメッセージを日本語に統一
- 日本語文字を含まないエラーメッセージは "JSON Schema の解析に失敗しました" で上書き



* fix: 再レビュー指摘対応(推奨 A・B・C、提案 D)

[推奨 A] parseDotenv インラインコメントのユニットテスト追加
- クォートなし値の # 除去、クォート内保持、URL フラグメント保持、
  クォート付き値+末尾コメントの 5 ケースを dotenv.test.ts に追加
- tests: 231 → 236 件

[推奨 B] クォート付き値 + 末尾コメント混在ケースを修正
- KEY="hello" # comment → "hello"(クォート残存)だったバグを修正
- 正規表現で /^"((?:[^"\\]|\\.)*)"\\s*(?:#.*)?$/ により
  クォート内容抽出と末尾コメント除去を一括処理
- ループ変数 raw とのシャドウイングを避けるため内部変数を rawValue に改名

[推奨 C] package.json dependencies をアルファベット順に整列
- ajv / ajv-draft-04 / ajv-formats / smol-toml を正しい位置に移動
- lockfile との整合性も改善

[提案 D] フォーマット切替時に validationResult をクリア
- handleFromChange / handleToChange に setValidationResult(null) を追加
- 切替後の出力に対して古い検証結果が残る誤認を防止



* fix(config-converter): 出力変更時に validationResult をクリアする

入力を編集して output が変わっても古いスキーマ検証結果(成功/失敗)が
残り続ける問題を修正。output/error を監視する既存 useEffect に
setValidationResult(null) を追加し、出力変化と同時に検証結果を無効化する。



---------



* chore: サブエージェントが PR レビューコメントを投稿できる権限を追加 (#142)

サブエージェントには対話 UI が無いため、未承認の tool 呼び出しは即拒否される。
PR レビュー作業を並列化する際にレビュー本文の投稿および TMPDIR への一時ファイル
書き出しが拒否されていた問題を解消する。

- Bash(gh pr review*) / Bash(gh pr comment*) を allow に追加
  - PR メタデータを変更する gh pr edit/create/merge/close は ask に残置
- Write(/tmp/claude*/**) / Write(/private/tmp/claude*/**) を allow に追加
  - サンドボックス TMPDIR 配下への一時ファイル書き出しを許可

* docs: PR ベースは必ず develop と CLAUDE.md 最重要ルールに昇格 (#143)

`docs/shared-agent-rules.md §6.3` に既に明記されているが、Claude Code の
system prompt が毎セッション "Main branch (you will usually use this for PRs): main"
と表示してくるため、shared-agent-rules.md を読まずに main 向け PR を作る事故が
繰り返し発生していた。

CLAUDE.md は毎セッション読み込まれるため、最重要ルールの 1 行に昇格させて
即時可視化する。

* fix(qr-reader): 非HTTPS環境で専用エラーメッセージを表示 (#135)

navigator.mediaDevices が undefined の場合に getUserMedia を呼ぶと
TypeError となり「カメラの起動に失敗しました」という汎用メッセージ
が出て原因が特定できなかった。startCamera 冒頭で早期 guard を入れ、
HTTPS または localhost が必要である旨を明示する。

* fix(config-converter): parseDotenv でエスケープシーケンスを解除 (#140)

stringifyDotenv は値の \\ と \" をエスケープして書き出すが、
parseDotenv 側でアンエスケープしておらず、ラウンドトリップが
壊れていた。

unescapeDotenv ヘルパーを追加し、DQ/SQ ブランチで適用。単発走査で
クォート種・バックスラッシュのみアンエスケープするため、
\\\" のような連鎖でも順序問題なく安全に解除できる。

テスト 5 件追加(DQ 内 \"・\\・連鎖、SQ 内 \'、ラウンドトリップ)。

* test(qr-reader): HTTPS 必須メッセージの E2E テストを追加 (#135)

レビュー指摘(feedback_e2e_tests)対応。Playwright の addInitScript で
navigator.mediaDevices を undefined にし、カメラ起動時に新しいエラー
メッセージが表示されることを検証する E2E ケースを追加。

* docs(decisions): .env パーサに expand オプション追加余地を明記

レビューでの指摘を受け、ダブルクォート内のエスケープ方針と
将来 `expand` オプション(POSIX dotenv 相当の `\n` 改行展開)を
追加できる旨を 052 章 .env セクションに 1 行追記。

* docs(rules): 先送り時の issue 化を 6.4 章として追加

「別 PR で対応」「後で追記」と口頭で先送りすると形骸化して
実施されないことがレビューで指摘されたため、共通規約に
「先送り時は必ず issue を起票し PR に番号を貼る」ルールを追加。

* test(qr-reader): HTTPS guard テストの再ナビゲート意図をコメント明記

レビュー任意指摘 (#144) を受け、beforeEach で goto 済みでも
addInitScript は次回ロードから反映されるため再 goto が必要、
という意図を 1 行コメントで補足。挙動変更なし。

* ci: developブランチへのプッシュ時もカバレッジバッジを更新する (#146)



* fix(config-converter): 変換先フォーマット変更時に入力テキストを保持する (#147)

* fix(config-converter): 変換先変更時に入力テキストを保持する

変換先のみ変更した場合、handleToChange から reset() の呼び出しを削除し
入力テキストを保持したまま出力のみ再計算するよう修正。
変換元変更時(handleFromChange)は従来どおり入力をクリアする。

Closes #141

* test(config-converter): E2E テストのナビゲーションパターンを修正

beforeEach が既に /tools/config-converter へ遷移済みのため、
新規テスト内の page.goto('/') と waitForReactHydration を削除。

* test(config-converter): E2E テストのロケータとアサーションをプロジェクト規約に準拠させる

- waitForFunction + querySelectorAll を expect().toHaveValue() に置換
- locator('textarea[readonly]') を getByLabel() に置換
- toContainText を toHaveValue に置換(textarea は value を検証する必要があるため)

docs/ui-conventions.md 3.2 章のロケータ規約に準拠。

* feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加 (#148)

スキーマ textarea に Cmd+Enter (Mac) / Ctrl+Enter (Win/Linux) で検証を実行するショートカットを追加。InputField に optional な onKeyDown prop を追加して forward する形で実装。isComposing ガード・aria-keyshortcuts・kbd 要素によるアクセシビリティ対応も含む。

Closes #137

* ci: カバレッジバッジ更新を Gist 経由に変更 (#151)

ブランチ保護ルールにより github-actions[bot] からの develop / main への
直接 push が拒否されるため、外部 Gist に JSON を書き込み shields.io の
endpoint バッジで表示する方式に切り替える。



---------

Co-authored-by: takasefumiyuki <71715982+fumtas1k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant