From 2bbc5785a470abd651b48623c67ef9b47c6d7102 Mon Sep 17 00:00:00 2001 From: r-ikeda Date: Wed, 6 May 2026 10:48:26 +0900 Subject: [PATCH] =?UTF-8?q?ClaudeCode=E3=81=AE=E3=82=BB=E3=83=83=E3=83=88?= =?UTF-8?q?=E3=82=A2=E3=83=83=E3=83=97=E3=82=92=E3=82=B3=E3=83=9F=E3=83=83?= =?UTF-8?q?=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/agents/frontend-reviewer.md | 78 ++++++++++++ .claude/commands/branch-from-develop.md | 45 +++++++ .claude/commands/deploy-check.md | 67 +++++++++++ .claude/commands/pre-commit.md | 41 +++++++ .claude/commands/react-doctor.md | 112 +++++++++++++++++ .claude/hooks/block-no-verify.sh | 16 +++ .claude/hooks/config-protection.sh | 21 ++++ .claude/hooks/console-warn.sh | 28 +++++ .claude/hooks/post-edit-lint-fix.sh | 31 +++++ .claude/hooks/post-edit-typecheck.sh | 28 +++++ .claude/hooks/pre-edit-guard.sh | 28 +++++ .claude/hooks/react-doctor-reminder.sh | 25 ++++ .claude/hooks/secret-detection.sh | 48 ++++++++ .claude/hooks/stop-react-doctor.sh | 25 ++++ .claude/rules/git-workflow.md | 48 ++++++++ .claude/rules/typescript-nextjs.md | 68 +++++++++++ .claude/settings.json | 152 ++++++++++++++++++++++++ .gitignore | 3 + CLAUDE.md | 10 ++ app/CLAUDE.md | 46 +++++++ package.json | 7 +- react-doctor.config.json | 11 ++ 22 files changed, 935 insertions(+), 3 deletions(-) create mode 100644 .claude/agents/frontend-reviewer.md create mode 100644 .claude/commands/branch-from-develop.md create mode 100644 .claude/commands/deploy-check.md create mode 100644 .claude/commands/pre-commit.md create mode 100644 .claude/commands/react-doctor.md create mode 100755 .claude/hooks/block-no-verify.sh create mode 100755 .claude/hooks/config-protection.sh create mode 100755 .claude/hooks/console-warn.sh create mode 100755 .claude/hooks/post-edit-lint-fix.sh create mode 100755 .claude/hooks/post-edit-typecheck.sh create mode 100755 .claude/hooks/pre-edit-guard.sh create mode 100755 .claude/hooks/react-doctor-reminder.sh create mode 100755 .claude/hooks/secret-detection.sh create mode 100755 .claude/hooks/stop-react-doctor.sh create mode 100644 .claude/rules/git-workflow.md create mode 100644 .claude/rules/typescript-nextjs.md create mode 100644 .claude/settings.json create mode 100644 app/CLAUDE.md create mode 100644 react-doctor.config.json diff --git a/.claude/agents/frontend-reviewer.md b/.claude/agents/frontend-reviewer.md new file mode 100644 index 0000000..6b1ab09 --- /dev/null +++ b/.claude/agents/frontend-reviewer.md @@ -0,0 +1,78 @@ +--- +name: frontend-reviewer +description: Next.js 15 App Router / TypeScript / Tailwind v4 コードのレビューを行う。コミット前のセルフレビューや PR レビュー前のチェックに使う。Read-only でコードを読み、診断結果を返す。 +tools: Read, Grep, Glob, Bash +model: inherit +--- + +あなたは Next.js 15 (App Router) / React 19 / TypeScript / Tailwind CSS v4 のシニアレビュアーです。 +ic-gr-website は AWS S3 + CloudFront による静的配信 (`output: 'export'`, `trailingSlash: true`) です。 + +## レビューの観点 + +### 1. Next.js / React + +- **Server vs Client Component**: 不要な `"use client"` が付いていないか / 必要な場所に付いているか +- **不要な `useEffect`**: 派生ステート計算・初期値計算は `useMemo` や直接計算で済む場合がある +- **`next/image`**: `unoptimized: true` 環境では `width`/`height` 必須、最適化前提のオプションを使っていないか +- **動的ルート**: `generateStaticParams` で全パスが列挙されているか(static export 制約) +- **Route Handlers (`app/api/`)**: static export では使えないため、追加されていたら警告 + +### 2. TypeScript + +- `any` の混入が無いか +- `interface` ではなく `type` を使っているか +- props 型が明示されているか +- 自動生成型に頼るべき箇所で手書き型を作っていないか + +### 3. アクセシビリティ + +- `` / `` の `alt` 属性 +- ボタン・リンクのラベル / `aria-label` +- ヘッダ階層 (`h1` → `h2` → `h3`) の整合 +- フォーム要素のラベル関連付け + +### 4. Tailwind v4 + +- `app/globals.css` の CSS 変数 / `@theme` を逸脱した直書きカラーになっていないか +- 過剰なクラス重複 / 矛盾するユーティリティ +- レスポンシブ (`sm:` `md:` `lg:`) の妥当性 + +### 5. 静的配信整合 + +- `output: 'export'` で動かないコードが混入していないか +- 新規ルートが `out//index.html` として生成されるか +- サブパス直リンクが CloudFront Function (`infra/cloudfront/url-rewrite.js`) で扱える形か + +### 6. パフォーマンス・セキュリティ + +- 巨大な client bundle 化(`"use client"` で大量のサーバ専用ロジックを巻き込んでいないか) +- 外部 URL のハードコード(環境変数化検討) +- `dangerouslySetInnerHTML` の使用箇所 + +## 出力フォーマット + +``` +## レビュー結果 + +### 🔴 高優先(セキュリティ / 動作不良 / a11y) +- ファイルパス:行番号 — 内容と修正案 + +### 🟡 中優先(保守性 / パフォーマンス) +- 同上 + +### 🟢 低優先(スタイル / 命名 / 微調整) +- 同上 + +### ✅ 良かった点 +- ... + +### 提案 +- React Doctor を回したか / `/react-doctor` を実行することを推奨 +``` + +## 注意 + +- ファイルは編集せず、指摘のみ行う +- 修正が必要な場合は呼び出し元 (Claude main) に戻して、main 側で修正実行する +- `.claude/rules/typescript-nextjs.md` の規約と矛盾する指摘はしない diff --git a/.claude/commands/branch-from-develop.md b/.claude/commands/branch-from-develop.md new file mode 100644 index 0000000..ac8ac0c --- /dev/null +++ b/.claude/commands/branch-from-develop.md @@ -0,0 +1,45 @@ +--- +name: branch-from-develop +description: develop から最新を取得して feature/ ブランチを切る +argument-hint: +--- + +`develop` の最新から `feature/$ARGUMENTS` ブランチを切ってください。 + +## 手順 + +### 1. 引数の検証 + +`$ARGUMENTS` が空の場合、ユーザーにトピック名(例: `add-contact-form`, `fix-mobile-header`)を確認する。 +`feature/` プレフィックスが既に含まれていれば取り除く(重複防止)。 + +### 2. develop の最新化 + +```bash +git checkout develop +git pull --ff-only origin develop +``` + +未コミットの変更があれば「stash するか確認してから」進める。 + +### 3. ブランチ作成 + +```bash +git checkout -b feature/ +``` + +### 4. 報告 + +``` +作業ブランチを作成しました: +- 元ブランチ: develop (commit: <短縮 SHA>) +- 新ブランチ: feature/ +``` + +## 命名規則 + +- `feature/` — 新機能・改善 +- `fix/` — バグ修正 +- `chore/` — 設定・ドキュメント等 + +`` は kebab-case で簡潔に(例: `add-favicon`, `fix-overview-typo`)。 diff --git a/.claude/commands/deploy-check.md b/.claude/commands/deploy-check.md new file mode 100644 index 0000000..6e81de0 --- /dev/null +++ b/.claude/commands/deploy-check.md @@ -0,0 +1,67 @@ +--- +name: deploy-check +description: prod へ push する前のデプロイ前チェック(CI 相当 + サブパスリライト確認) +--- + +`prod` ブランチへ push して本番デプロイをトリガする前の最終チェックを行ってください。 + +## 前提 + +- `prod` への push で GitHub Actions (`deploy.yml`) が S3 sync + CloudFront invalidation を実行する +- `develop` で動作確認済みであることが前提 +- ステージング環境は無い + +## 手順 + +### 1. ブランチ状態の確認 + +```bash +git status +git branch --show-current +``` + +`develop` または `feature/*` で作業中であること、未コミット変更が無いことを確認。 + +### 2. ローカルで CI 相当を実行 + +```bash +npm ci +npm run lint +npm run typecheck +npm run build +``` + +`out/` が生成され、`out/index.html`, `out/company/index.html` などサブパスの index が出力されていることを確認: + +```bash +ls out/ +ls out/company out/overview out/privacy +``` + +### 3. CloudFront Function の整合確認 + +`infra/cloudfront/url-rewrite.js` を読み、サブパス → `index.html` のリライトロジックに変更が無いか確認。 +変更がある場合は **コードと AWS 上の関数 (`ic-gr-url-rewrite`) の両方** を更新する必要があるため、ユーザーに警告。 + +### 4. デプロイ手順の提示 + +チェックが通ったら、以下を提示してユーザーの確認を取ってから実行: + +```bash +git checkout prod +git merge --ff-only develop # または PR develop → prod +git push origin prod # ← これがデプロイトリガ +``` + +push 後に GitHub Actions の進行を確認: + +```bash +gh run list --branch prod --limit 3 +gh run watch +``` + +## 注意 + +- `prod` への直接 push はワークフロートリガになり、本番が即座に書き換わる +- 緊急時のロールバックは前のコミットへ revert → push(同じパイプラインで戻る) +- CloudFront Function の変更はリポジトリだけでは反映されない(AWS 側のデプロイが別途必要)。`infra/cloudfront/README.md` を参照 diff --git a/.claude/commands/pre-commit.md b/.claude/commands/pre-commit.md new file mode 100644 index 0000000..a910483 --- /dev/null +++ b/.claude/commands/pre-commit.md @@ -0,0 +1,41 @@ +--- +name: pre-commit +description: コミット前の包括的チェック(lint, typecheck, build, react-doctor)を実行 +--- + +コミット前の包括的なチェックを実行してください。 + +## 1. 静的解析・型チェック + +```bash +npm run lint +npm run typecheck +``` + +エラーがある場合は修正してから次に進む。設定(`eslint.config.*` / `tsconfig.json`)を緩めて回避するのは禁止。 + +## 2. ビルド確認 + +```bash +npm run build +``` + +`out/` への static export まで成功することを確認。 + +## 3. React Doctor 診断 + +`/react-doctor diff` を実行し、`develop` との差分に対するスコアと指摘を確認する。 +高優先度(セキュリティ・a11y)の指摘があれば修正する。 + +## 4. 結果サマリー + +全チェックの結果を一覧(OK / NG)で報告してください: + +``` +- lint: OK/NG +- typecheck: OK/NG +- build: OK/NG +- react-doctor: スコア XX/100, 高優先指摘 N 件 +``` + +問題がある場合は修正方法を提案してください。 diff --git a/.claude/commands/react-doctor.md b/.claude/commands/react-doctor.md new file mode 100644 index 0000000..d3da8aa --- /dev/null +++ b/.claude/commands/react-doctor.md @@ -0,0 +1,112 @@ +--- +name: react-doctor +description: React Doctor でフロントエンドを診断し、検出された問題を修正する(スコア、セキュリティ、パフォーマンス、a11y、アーキテクチャ) +argument-hint: +--- + +React Doctor でフロントエンドのコード品質を診断し、検出された問題を修正してください: $ARGUMENTS + +## React Doctor とは + +Aiden Bai(Million.js / React Scan 作者)による React コードベース診断 CLI。プロジェクトをスキャンしてセキュリティ・パフォーマンス・正確性・アクセシビリティ・アーキテクチャの問題を 47 以上のルールで検出し、0〜100 のスコアで健全性を可視化する。 + +主な検出対象: +- 不要な `useEffect`(派生ステート計算・イベントハンドラで代替可能な処理) +- アクセシビリティ問題(`alt` 属性欠落、ラベルなしフォーム要素) +- Prop drilling(Context / Composition で置き換えるべき多段 props 渡し) +- デッドコード(未使用 export / 未参照ファイル) +- 古い / 非推奨パターン、セキュリティ上のアンチパターン + +## 対象の判定 + +- 引数なし → リポジトリ全体(`app/`, `components/`)をスキャン +- 引数が `diff` → `develop` ブランチとの差分のみスキャン +- 引数が `score` → スコアのみ出力(修正フェーズに進まない) +- 引数がパス → 当該パス配下をスキャン + +設定は `react-doctor.config.json` を参照(`.next/`, `out/`, `node_modules/` は除外済み)。 + +## 手順 + +### 1. 診断実行 + +リポジトリルートで React Doctor を実行する。 + +```bash +# 詳細表示(問題のあるファイルと行番号) +npx --yes react-doctor@latest . --verbose + +# 差分スキャン(引数: diff) +npx --yes react-doctor@latest . --diff develop --verbose + +# スコアのみ(引数: score) +npx --yes react-doctor@latest . --score +``` + +初回実行時は `npx` によるパッケージ取得に時間がかかる。 + +### 2. 結果の読み取り + +- 総合スコア(例: `Score: 87/100`) +- 検出されたルール違反のカテゴリ別件数 +- ファイルパス・行番号付きの個別指摘 + +### 3. 除外対象の判定 + +以下のファイルは指摘があっても**編集しない**: +- `.next/`, `out/`, `node_modules/`(ビルド成果物・依存) +- `next-env.d.ts`(Next.js 自動生成) + +### 4. 修正方針の提示 + +検出件数が多い場合は、ユーザーに修正スコープを確認する前に以下を整理して提示: + +- **高優先**: セキュリティ、a11y 違反、明確なバグ +- **中優先**: 不要な `useEffect`、prop drilling、パフォーマンス +- **低優先**: デッドコード、命名、スタイル + +### 5. 修正の実行 + +優先度順に 1 件ずつ修正。各修正後に以下を遵守: + +- `.claude/rules/typescript-nextjs.md` のコーディング規約を守る +- `any` は導入しない。やむを得ない場合は `unknown` + 型ガード +- Server Components をデフォルトに。`"use client"` は必要時のみ + +### 6. 検証 + +修正後に以下を実行し、スコアが改善し既存機能が壊れていないことを確認: + +```bash +npx --yes react-doctor@latest . --score +npm run lint +npm run typecheck +npm run build +``` + +### 7. 結果報告 + +``` +## React Doctor 診断結果 + +### スコア +- 修正前: XX/100 +- 修正後: YY/100 + +### 修正した問題 +- [カテゴリ] 内容: ファイルパス:行番号 + +### 未対応(理由付き) +- [カテゴリ] 内容: 理由 + +### ビルド・型チェック +- lint: OK/NG +- typecheck: OK/NG +- build: OK/NG +``` + +## 注意事項 + +- `npx --yes react-doctor@latest` は毎回最新を取得する。オフライン環境ではユーザーに確認する +- 大規模リファクタ(prop drilling → Context 化など)はユーザー確認を取ってから実行 +- スコア改善のためだけに本質を逸脱する変更(意味のある `useEffect` を無理に除去する等)は避ける diff --git a/.claude/hooks/block-no-verify.sh b/.claude/hooks/block-no-verify.sh new file mode 100755 index 0000000..82beeee --- /dev/null +++ b/.claude/hooks/block-no-verify.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# PreToolUse hook: --no-verify フラグの使用をブロック +set -euo pipefail + +COMMAND=$(jq -r '.tool_input.command // empty') + +if [ -z "$COMMAND" ]; then + exit 0 +fi + +if echo "$COMMAND" | grep -qE '\-\-no-verify'; then + echo "BLOCKED: --no-verify フラグは禁止されています。pre-commit / CI のチェックをバイパスせず、原因を修正してください。" >&2 + exit 2 +fi + +exit 0 diff --git a/.claude/hooks/config-protection.sh b/.claude/hooks/config-protection.sh new file mode 100755 index 0000000..f05c637 --- /dev/null +++ b/.claude/hooks/config-protection.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# PreToolUse hook: プロジェクト設定ファイル編集を警告(ブロックはしない) +set -euo pipefail + +FILE=$(jq -r '.tool_input.file_path // empty') + +if [ -z "$FILE" ]; then + exit 0 +fi + +if echo "$FILE" | grep -qE '(next\.config\.(ts|mjs|js)|tsconfig\.json|postcss\.config\.(mjs|js)|eslint\.config\.(mjs|js)|\.eslintrc.*|package\.json)$'; then + BASENAME=$(basename "$FILE") + echo "WARNING: ${BASENAME} はプロジェクト設定ファイルです。リンター/ビルド設定を緩めてエラー回避するのではなく、コード側を修正してください。本当に設定変更が必要な場合のみ編集してください。" >&2 +fi + +# CLAUDE.md / .claude/* の編集も明示的に通知(隠れた挙動変更を防ぐ) +if echo "$FILE" | grep -qE '(^|/)(CLAUDE\.md|\.claude/(settings\.json|hooks/|commands/|agents/|rules/))'; then + echo "NOTE: Claude Code 設定ファイルを編集しています。フック・コマンド・ルール変更はチーム全体に影響します。" >&2 +fi + +exit 0 diff --git a/.claude/hooks/console-warn.sh b/.claude/hooks/console-warn.sh new file mode 100755 index 0000000..3066160 --- /dev/null +++ b/.claude/hooks/console-warn.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# PostToolUse hook: TS/TSX に console.log があれば警告 +set -euo pipefail + +FILE=$(jq -r '.tool_input.file_path // empty') + +if [ -z "$FILE" ]; then + exit 0 +fi + +if ! echo "$FILE" | grep -qE '\.(ts|tsx)$'; then + exit 0 +fi + +# テストファイル(将来追加されたとき用) +if echo "$FILE" | grep -qE '\.(test|spec)\.(ts|tsx)$'; then + exit 0 +fi + +[ -f "$FILE" ] || exit 0 + +if grep -qn 'console\.log' "$FILE"; then + LINES=$(grep -n 'console\.log' "$FILE" | head -5) + echo "WARNING: console.log が検出されました。デバッグ用であれば削除してください:" >&2 + echo "$LINES" >&2 +fi + +exit 0 diff --git a/.claude/hooks/post-edit-lint-fix.sh b/.claude/hooks/post-edit-lint-fix.sh new file mode 100755 index 0000000..123b7e8 --- /dev/null +++ b/.claude/hooks/post-edit-lint-fix.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# PostToolUse hook: TS/TSX 編集後に ESLint auto-fix を実行 +set -euo pipefail + +FILE=$(jq -r '.tool_input.file_path // empty') + +if [ -z "$FILE" ]; then + exit 0 +fi + +if ! echo "$FILE" | grep -qE '\.(ts|tsx|js|jsx|mjs)$'; then + exit 0 +fi + +# ビルド成果物・依存はスキップ +if echo "$FILE" | grep -qE '(^|/)(\.next/|out/|node_modules/)'; then + exit 0 +fi + +[ -f "$FILE" ] || exit 0 + +cd "${CLAUDE_PROJECT_DIR:-$(pwd)}" + +OUTPUT=$(npx --no-install eslint --fix "$FILE" 2>&1) || true + +if [ -n "$OUTPUT" ]; then + echo "ESLint:" >&2 + echo "$OUTPUT" | head -10 >&2 +fi + +exit 0 diff --git a/.claude/hooks/post-edit-typecheck.sh b/.claude/hooks/post-edit-typecheck.sh new file mode 100755 index 0000000..6476105 --- /dev/null +++ b/.claude/hooks/post-edit-typecheck.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# PostToolUse hook: TS/TSX 編集後に型チェックを実行(警告のみ・ブロックしない) +set -euo pipefail + +FILE=$(jq -r '.tool_input.file_path // empty') + +if [ -z "$FILE" ]; then + exit 0 +fi + +if ! echo "$FILE" | grep -qE '\.(ts|tsx)$'; then + exit 0 +fi + +if echo "$FILE" | grep -qE '(^|/)(\.next/|out/|node_modules/|next-env\.d\.ts$)'; then + exit 0 +fi + +cd "${CLAUDE_PROJECT_DIR:-$(pwd)}" + +OUTPUT=$(npx --no-install tsc --noEmit 2>&1) || true + +if echo "$OUTPUT" | grep -qE 'error TS'; then + echo "TypeScript 型エラーが検出されました:" >&2 + echo "$OUTPUT" | grep -E 'error TS' | head -10 >&2 +fi + +exit 0 diff --git a/.claude/hooks/pre-edit-guard.sh b/.claude/hooks/pre-edit-guard.sh new file mode 100755 index 0000000..75d1ea6 --- /dev/null +++ b/.claude/hooks/pre-edit-guard.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# PreToolUse hook: 自動生成・ビルド成果物の編集をブロック +set -euo pipefail + +FILE=$(jq -r '.tool_input.file_path // empty') + +if [ -z "$FILE" ]; then + exit 0 +fi + +# Next.js ビルド成果物・自動生成 +if echo "$FILE" | grep -qE '(^|/)(\.next/|out/|node_modules/)'; then + echo "BLOCKED: ${FILE} は Next.js のビルド成果物または依存です。直接編集せず、ソース側を修正して 'npm run build' で再生成してください。" >&2 + exit 2 +fi + +if echo "$FILE" | grep -qE '(^|/)next-env\.d\.ts$'; then + echo "BLOCKED: next-env.d.ts は Next.js が自動生成・更新します。直接編集しないでください。" >&2 + exit 2 +fi + +# package-lock.json は npm が管理 +if echo "$FILE" | grep -qE '(^|/)package-lock\.json$'; then + echo "BLOCKED: package-lock.json は npm が管理します。'npm install' / 'npm ci' で更新してください。" >&2 + exit 2 +fi + +exit 0 diff --git a/.claude/hooks/react-doctor-reminder.sh b/.claude/hooks/react-doctor-reminder.sh new file mode 100755 index 0000000..5e0461b --- /dev/null +++ b/.claude/hooks/react-doctor-reminder.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# PostToolUse hook: app/ または components/ 配下の TS/TSX が変更されたら React Doctor をリマインド +set -euo pipefail + +FILE=$(jq -r '.tool_input.file_path // .tool_response.filePath // empty' 2>/dev/null) + +if [ -z "$FILE" ]; then + exit 0 +fi + +# 対象は React コンポーネント領域のみ +if ! echo "$FILE" | grep -qE '(^|/)(app|components)/.+\.(ts|tsx)$'; then + exit 0 +fi + +# ビルド成果物は除外 +if echo "$FILE" | grep -qE '(^|/)(\.next/|out/|node_modules/)'; then + exit 0 +fi + +cat <<'EOF' +{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"[react-doctor reminder] React コンポーネント領域 (app/ または components/) のファイルが変更されました。一連の編集が完了したら /react-doctor を実行してフロントエンドの問題を診断してください。"}} +EOF + +exit 0 diff --git a/.claude/hooks/secret-detection.sh b/.claude/hooks/secret-detection.sh new file mode 100755 index 0000000..ccb8ae1 --- /dev/null +++ b/.claude/hooks/secret-detection.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# PreToolUse hook: git commit 前にステージングされたファイルからシークレットを検出 +set -euo pipefail + +COMMAND=$(jq -r '.tool_input.command // empty') + +if [ -z "$COMMAND" ]; then + exit 0 +fi + +if ! echo "$COMMAND" | grep -qE '^git commit'; then + exit 0 +fi + +echo "Secret detection: ステージングされたファイルをスキャン中..." >&2 + +STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null) || exit 0 + +if [ -z "$STAGED_FILES" ]; then + exit 0 +fi + +FOUND_SECRETS=false + +for FILE in $STAGED_FILES; do + case "$FILE" in + *.lock|package-lock.json|*.min.js|*.min.css|*.d.ts|out/*|.next/*) + continue + ;; + esac + + [ -f "$FILE" ] || continue + + MATCHES=$(grep -nEi '(password|passwd|secret|api_key|apikey|access_key|private_key|aws_access|aws_secret|token)\s*[:=]\s*["\x27][^\s"'\'']{8,}' "$FILE" 2>/dev/null) || true + + if [ -n "$MATCHES" ]; then + echo "WARNING: $FILE にシークレットの可能性がある文字列が検出されました:" >&2 + echo "$MATCHES" | head -5 >&2 + FOUND_SECRETS=true + fi +done + +if [ "$FOUND_SECRETS" = true ]; then + echo "" >&2 + echo "WARNING: シークレットがハードコードされている可能性があります。環境変数 / GitHub Actions Secrets / AWS Secrets Manager を使用してください。" >&2 +fi + +exit 0 diff --git a/.claude/hooks/stop-react-doctor.sh b/.claude/hooks/stop-react-doctor.sh new file mode 100755 index 0000000..b063824 --- /dev/null +++ b/.claude/hooks/stop-react-doctor.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Stop hook: app/ または components/ に未コミットの変更がある場合のみ React Doctor のスコアを表示 +set -u + +cd "${CLAUDE_PROJECT_DIR:-$(pwd)}" || exit 0 + +# 対象領域に変更があるかチェック +HAS_CHANGES=0 +{ + git diff --name-only HEAD 2>/dev/null + git ls-files --others --exclude-standard 2>/dev/null +} | grep -qE '^(app|components)/' && HAS_CHANGES=1 + +if [ "$HAS_CHANGES" -eq 0 ]; then + exit 0 +fi + +# React Doctor をスコアモードで実行(リポジトリルートが対象) +SCORE=$(npx --yes react-doctor@latest . --score 2>/dev/null | tail -1) + +if [[ "$SCORE" =~ ^[0-9]+$ ]]; then + printf '{"systemMessage": "React Doctor スコア: %s/100 (詳細は /react-doctor で確認)"}\n' "$SCORE" +fi + +exit 0 diff --git a/.claude/rules/git-workflow.md b/.claude/rules/git-workflow.md new file mode 100644 index 0000000..dca92cc --- /dev/null +++ b/.claude/rules/git-workflow.md @@ -0,0 +1,48 @@ +# Git ワークフロー + +## ブランチ運用 + +``` +feature/ ──PR──▶ develop ──PR/FF──▶ prod ──push トリガで本番デプロイ──▶ AWS +``` + +- **`develop`** がデフォルトブランチ。機能ブランチからの PR を集約 +- **`prod`** へ push されると GitHub Actions (`deploy.yml`) が走り本番反映 +- ステージング環境は持たない(`develop` で動作確認 → `prod` へ進める) + +旧 `main` / `gh-pages` ブランチは過去の遺物。触らない。 + +## ブランチ命名 + +- `feature/` — 新機能・改善 +- `fix/` — バグ修正 +- `chore/` — 設定・ドキュメント +- `` は kebab-case で簡潔に(例: `feature/add-contact-form`, `fix/mobile-header`) + +ブランチを切るときは `/branch-from-develop ` slash command を使うと自動化される。 + +## コミットメッセージ + +- 1 行目は日本語で簡潔に(`add: 〜`, `fix: 〜`, `update: 〜`, `chore: 〜`) +- `--no-verify` は禁止(`block-no-verify.sh` フックでブロック) + +## PR + +- ベースは `develop` +- マージ方式は squash merge を推奨 +- `prod` への merge は **`develop` → `prod`** に限定(feature ブランチから直接 prod に出さない) + +## prod への進め方 + +1. `develop` で `/pre-commit` 相当のチェックを通す +2. `/deploy-check` を実行して `out/` 出力とサブパスを確認 +3. `git checkout prod && git merge --ff-only develop` または PR `develop → prod` +4. `git push origin prod` → デプロイ自動実行 +5. `gh run watch` で進行確認 + +## やってはいけないこと + +- `git push --force` / `--force-with-lease`(フックで deny 済み) +- `git reset --hard`(フックで deny 済み) +- `develop` / `prod` への直接コミット(必ず feature ブランチ経由) +- `--no-verify` でフックバイパス diff --git a/.claude/rules/typescript-nextjs.md b/.claude/rules/typescript-nextjs.md new file mode 100644 index 0000000..165be83 --- /dev/null +++ b/.claude/rules/typescript-nextjs.md @@ -0,0 +1,68 @@ +# TypeScript / Next.js コーディング規約 + +ic-gr-website (Next.js 15 App Router + React 19 + TypeScript + Tailwind v4) の規約。 + +## 型システム + +- `type` を使用(`interface` は使わない) +- `any` 禁止。やむを得ない場合は `unknown` + 型ガードで絞り込む +- `import type` で型と値を明示的に分離 +- props には明示的な型を付ける + +## Server / Client コンポーネント + +- **Server Components をデフォルト**にし、必要なときだけ `"use client"` を付ける +- 以下のときに client component が必要: + - `useState` / `useEffect` / `useReducer` などのフック + - `onClick` / `onChange` などのイベントハンドラ + - `usePathname` / `useRouter` などのクライアント API + - ブラウザ専用 API(`window`, `document`, `localStorage` 等) +- 既存の `"use client"` 例: `app/components/Header.tsx`, `app/components/ScrollToTop.tsx` + +## エクスポート + +- Named export を原則使用 +- 例外(Next.js が default export を要求): + - `app/**/page.tsx`, `app/**/layout.tsx`, `app/**/error.tsx`, `app/**/not-found.tsx` + - `app/sitemap.ts`, `app/robots.ts` + - `next.config.ts`, `postcss.config.mjs` + +## 関数スタイル + +- トップレベルの関数は関数宣言 (`function foo() {}`) を優先 +- コールバックは arrow function + +## ファイル命名 + +- コンポーネント: PascalCase(`Header.tsx`, `Footer.tsx`) +- フック / ユーティリティ: kebab-case(`use-something.ts`) +- App Router の特殊ファイル: 固定(`page.tsx`, `layout.tsx`, ...) + +## パス・インポート + +- 深い相対パス(`../../*`)を避け、`@/*` パスエイリアスがあればそちらを使う(neko では tsconfig で別途設定要) +- 同じディレクトリ内の参照は相対パスで OK + +## 静的エクスポート (`output: 'export'`) の制約 + +- `next/image` は `unoptimized: true` 設定済み(`next.config.ts`)。動的最適化は使えない +- `app/api/*` のような Route Handlers は使えない +- `revalidate` / ISR は使えない +- 動的ルート (`[id]`) を使う場合は `generateStaticParams` で全パスを列挙する +- 環境変数は `NEXT_PUBLIC_*` のみクライアントに渡る(ビルド時に埋め込まれる) + +## サブパス挙動 + +- `next.config.ts` で `trailingSlash: true` を設定済み → `/company` ではなく `/company/` で配信 +- 直リンク・リロード対応は CloudFront Function (`infra/cloudfront/url-rewrite.js`) が viewer-request でリライト +- 新しいページを追加した場合、ローカルビルド (`npm run build`) で `out//index.html` が出力されることを確認 + +## Tailwind CSS v4 + +- v4 は CSS first-class(`@tailwindcss/postcss` 経由)。`tailwind.config.*` は持たず、`app/globals.css` で `@theme` と CSS 変数を直接定義 +- ブランドカラー・フォントは `app/globals.css` の CSS 変数を使う + +## フォーム・バリデーション(必要時) + +- 現状フォーム実装は無い(問い合わせは外部 Google Form) +- 必要になったら `react-hook-form` + `zod` を導入する想定 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..17a029c --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,152 @@ +{ + "permissions": { + "allow": [ + "Read", + "Edit", + "Write", + "Glob", + "Grep", + "Bash(npm install)", + "Bash(npm ci)", + "Bash(npm run dev)", + "Bash(npm run build)", + "Bash(npm run lint)", + "Bash(npm run lint -- --fix)", + "Bash(npm run typecheck)", + "Bash(npm run start)", + "Bash(npx eslint:*)", + "Bash(npx tsc:*)", + "Bash(npx react-doctor:*)", + "Bash(node:*)", + "Bash(git status)", + "Bash(git status -s)", + "Bash(git diff:*)", + "Bash(git log:*)", + "Bash(git show:*)", + "Bash(git branch:*)", + "Bash(git ls-files:*)", + "Bash(git rev-parse:*)", + "Bash(git fetch:*)", + "Bash(git checkout:*)", + "Bash(git switch:*)", + "Bash(git pull --ff-only:*)", + "Bash(git add:*)", + "Bash(git restore --staged:*)", + "Bash(git commit:*)", + "Bash(git push)", + "Bash(git push origin:*)", + "Bash(gh pr:*)", + "Bash(gh issue:*)", + "Bash(gh repo view:*)", + "Bash(gh run list:*)", + "Bash(gh run view:*)", + "Bash(ls:*)", + "Bash(pwd)", + "Bash(which:*)", + "Bash(mkdir -p:*)", + "Bash(chmod +x:*)" + ], + "deny": [ + "Bash(rm -rf:*)", + "Bash(rm -r:*)", + "Bash(git push --force:*)", + "Bash(git push -f:*)", + "Bash(git push --force-with-lease:*)", + "Bash(git reset --hard:*)", + "Bash(git clean -fd:*)", + "Bash(git clean -fdx:*)", + "Bash(npm publish:*)", + "Bash(aws:* s3 rb:*)", + "Bash(aws:* s3 rm:* --recursive:*)", + "Bash(aws:* cloudfront delete-distribution:*)" + ] + }, + "hooks": { + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-edit-guard.sh", + "timeout": 5, + "statusMessage": "自動生成・ビルド成果物チェック中..." + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/config-protection.sh", + "timeout": 5, + "statusMessage": "設定ファイル保護チェック中..." + } + ] + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-no-verify.sh", + "timeout": 5, + "statusMessage": "--no-verify チェック中..." + } + ] + }, + { + "matcher": "Bash(git commit*)", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/secret-detection.sh", + "timeout": 15, + "statusMessage": "シークレット検出中..." + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-edit-lint-fix.sh", + "timeout": 30, + "statusMessage": "ESLint auto-fix..." + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-edit-typecheck.sh", + "timeout": 60, + "statusMessage": "TypeScript 型チェック中..." + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/console-warn.sh", + "timeout": 5, + "statusMessage": "console.log チェック中..." + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/react-doctor-reminder.sh", + "timeout": 5, + "statusMessage": "React Doctor リマインド中..." + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-react-doctor.sh", + "timeout": 90, + "async": true, + "statusMessage": "React Doctor スコア計測中..." + } + ] + } + ] + }, + "language": "japanese" +} diff --git a/.gitignore b/.gitignore index a4ef5a1..2d5ada7 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ next-env.d.ts npm-debug.log* yarn-debug.log* yarn-error.log* + +# Claude Code(個人 permissions / 環境固有 — チーム共有は .claude/settings.json) +.claude/settings.local.json diff --git a/CLAUDE.md b/CLAUDE.md index ca80d9f..a3f8238 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,8 +12,18 @@ npm install npm run dev # http://localhost:3000 npm run build # next build (static export → out/) npm run lint +npm run typecheck # tsc --noEmit ``` +## Claude Code 連携 + +- コーディング規約: `.claude/rules/typescript-nextjs.md` +- ブランチ運用: `.claude/rules/git-workflow.md` +- スラッシュコマンド: `/react-doctor`, `/pre-commit`, `/deploy-check`, `/branch-from-develop` +- レビュー sub-agent: `frontend-reviewer` +- 編集後は ESLint auto-fix / 型チェック / React Doctor リマインドが自動で走る(`.claude/hooks/`) +- 終了時に `app/` または `components/` に変更があれば React Doctor のスコアが表示される + ## デプロイ `prod` ブランチへの push で GitHub Actions が自動デプロイする(手動操作は不要)。 diff --git a/app/CLAUDE.md b/app/CLAUDE.md new file mode 100644 index 0000000..0c4d961 --- /dev/null +++ b/app/CLAUDE.md @@ -0,0 +1,46 @@ +# app/ — Next.js App Router + +## 概要 + +Next.js 15 App Router によるルート定義。`output: 'export'` で静的書き出し(`out/`)。 + +## ルート構成 + +``` +app/ +├── layout.tsx # ルートレイアウト(Header + Footer + ScrollToTop + metadata) +├── page.tsx # / +├── overview/page.tsx # /overview +├── company/page.tsx # /company +├── privacy/page.tsx # /privacy +├── sitemap.ts # ビルド時に sitemap.xml を生成 +├── robots.ts # ビルド時に robots.txt を生成 +├── globals.css # Tailwind v4 + CSS 変数(ブランドカラー / フォント) +└── components/ + ├── Header.tsx # 'use client'(ハンバーガー開閉) + ├── Footer.tsx + ├── ScrollToTop.tsx # 'use client'(usePathname でスクロールリセット) + ├── SectionComponent1.tsx + ├── Backup.tsx + └── Consult.tsx +``` + +## 規約(詳細は `.claude/rules/typescript-nextjs.md`) + +- Server Component をデフォルトに、`"use client"` は **本当に必要なときのみ** +- 各 `page.tsx` / `layout.tsx` は default export(Next.js が要求) +- それ以外は named export + +## 新規ページ追加時のチェックリスト + +1. `app//page.tsx` を作る(Server Component が原則) +2. `npm run build` を実行し `out//index.html` が出力されることを確認 +3. ヘッダ・フッタからの遷移リンク (`Header.tsx` / `Footer.tsx`) を更新 +4. `app/sitemap.ts` に新ルートを追加 +5. CloudFront Function (`infra/cloudfront/url-rewrite.js`) は拡張子無し URL を `index.html` にリライトするので、新規ルートにも自動で効く(個別の AWS 操作は不要) +6. `/react-doctor` でレビュー + +## 自動編集禁止 + +- `next-env.d.ts`(自動生成) +- `.next/`, `out/`(ビルド成果物) diff --git a/package.json b/package.json index f3c6c7d..71d233d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ic-gr-website", - "version": "1.0.0", + "version": "2.0.1", "private": true, "engines": { "node": ">=22.0.0" @@ -9,7 +9,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "typecheck": "tsc --noEmit" }, "dependencies": { "next": "^15.1.0", @@ -26,4 +27,4 @@ "tailwindcss": "^4.0.0", "typescript": "^5.7.0" } -} +} \ No newline at end of file diff --git a/react-doctor.config.json b/react-doctor.config.json new file mode 100644 index 0000000..c2cf411 --- /dev/null +++ b/react-doctor.config.json @@ -0,0 +1,11 @@ +{ + "ignore": { + "files": [ + ".next/**", + "out/**", + "node_modules/**", + "next-env.d.ts", + "infra/**" + ] + } +}