diff --git a/.claude/commands/branch-cleanup.md b/.claude/commands/branch-cleanup.md new file mode 100644 index 00000000..845068db --- /dev/null +++ b/.claude/commands/branch-cleanup.md @@ -0,0 +1,138 @@ +# Branch Cleanup Command + +Clean up merged and stale branches both locally and remotely. + +## Usage + +```bash +/branch-cleanup +/branch-cleanup --remote +/branch-cleanup --dry-run +``` + +## What It Does + +This command helps maintain a clean repository by identifying and removing: + +### Local Branches + +- **Merged Branches**: Branches already merged into main/master +- **Stale Branches**: Branches with no activity for 30+ days +- **Gone Remote Branches**: Local branches tracking deleted remote branches + +### Remote Branches (with --remote) + +- **Merged PR Branches**: Branches from merged pull requests +- **Stale Remote Branches**: No activity for 30+ days +- **Abandoned Branches**: No commits, PRs, or activity + +### Protected Branches + +Never deletes: + +- main, master, develop +- Current branch +- Branches with unmerged changes +- Branches specified in protection list + +## Example Output + +``` +🧹 Branch Cleanup +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +📍 Current branch: feat/add-commands +🔒 Protected: main, master, develop + +📊 Analysis + • Total local branches: 15 + • Merged branches: 8 + • Stale branches (30+ days): 3 + • Up-to-date branches: 4 + +🗑️ Branches to delete (11): + +Merged (8): + ✓ feat/227-commitlint (merged 2 days ago) + ✓ feat/226-common-utils (merged 2 days ago) + ✓ feat/225-docker-in-docker (merged 2 days ago) + ... and 5 more + +Stale (3): + ⚠ experiment/new-feature (90 days old) + ⚠ fix/old-bug (45 days old) + ⚠ refactor/unused (60 days old) + +Delete these branches? [y/N]: y + +Deleting branches... + ✓ Deleted feat/227-commitlint + ✓ Deleted feat/226-common-utils + ✓ Deleted feat/225-docker-in-docker + ✓ Deleted 8 more branches + +✨ Cleanup complete! Removed 11 branches. +``` + +## Options + +```bash +# Preview without deleting (recommended first run) +/branch-cleanup --dry-run + +# Include remote branches +/branch-cleanup --remote + +# Auto-confirm deletion (for CI) +/branch-cleanup --yes + +# Custom staleness threshold (default: 30 days) +/branch-cleanup --stale-days 60 + +# Only merged branches +/branch-cleanup --merged-only +``` + +## Safety Features + +- **Dry Run**: Preview before deletion +- **Interactive Confirmation**: Requires user approval +- **Protection List**: Never deletes protected branches +- **Unmerged Detection**: Warns about unmerged changes +- **Current Branch**: Never deletes current branch + +## CI Integration + +```yaml +# .github/workflows/branch-cleanup.yml +- name: Cleanup Merged Branches + run: | + bash script/branch-cleanup.sh --merged-only --yes +``` + +## Staleness Criteria + +| Age | Status | Action | +| ------ | ---------- | ------ | +| < 30d | Active | Keep | +| 30-60d | Stale | Warn | +| 60-90d | Very Stale | Delete | +| > 90d | Abandoned | Delete | + +## Benefits + +- 🧹 **Clean Repository**: Remove clutter +- ⚡ **Faster Operations**: Fewer branches to manage +- 👀 **Better Visibility**: Focus on active work +- 💾 **Disk Space**: Free up local storage +- 🔄 **Best Practice**: Regular maintenance habit + +## Implementation + +This command is implemented in `script/branch-cleanup.sh`. + +## Requirements + +- Git repository +- GitHub CLI (`gh`) for remote branch operations (optional) +- Proper permissions for remote deletions diff --git a/.claude/commands/create-pr.md b/.claude/commands/create-pr.md new file mode 100644 index 00000000..568ccd93 --- /dev/null +++ b/.claude/commands/create-pr.md @@ -0,0 +1,263 @@ +--- +description: Create PR with latest base branch changes merged +allowed-tools: Read, Write, Edit, Bash(git:*), Bash(gh:*), Bash(find:*), Bash(ls:*) +argument-hint: [--base BRANCH] [--title TITLE] [--draft] +--- + +# Create PR Workflow + +## Overview + +このコマンドは最新のベースブランチから変更を取り込み、PRを作成します。 + +## 前提条件 + +- Git リポジトリ内で実行 +- gh CLI がインストール済み +- リモートリポジトリにプッシュ権限がある +- 現在のブランチがフィーチャーブランチである + +## Step 1: Parse Arguments + +引数から設定を読み取る: + +- `--base BRANCH`: ベースブランチを指定(デフォルト: main) +- `--title TITLE`: PR タイトルを指定(省略時は最新コミットメッセージから生成) +- `--draft`: ドラフトPRとして作成 + +引数がない場合はデフォルト設定を使用。 + +## Step 2: Validate Current State + +現在の状態を確認: + +```bash +# 現在のブランチを確認 +git branch --show-current + +# Uncommitted changes を確認 +git status --porcelain +``` + +### 検証項目 + +1. **ブランチチェック** + - 現在のブランチがベースブランチではないこと + - フィーチャーブランチであること + +2. **変更チェック** + - Uncommitted changes がないこと + - ある場合は警告を表示し、確認を求める + +## Step 3: Fetch and Merge Latest Base Branch + +最新のベースブランチを取得してマージ: + +```bash +# 最新のベースブランチを取得 +git fetch origin ${BASE_BRANCH} + +# ベースブランチとのマージベースを確認 +git merge-base HEAD origin/${BASE_BRANCH} + +# ベースブランチをマージ +git merge origin/${BASE_BRANCH} --no-edit +``` + +### コンフリクト処理 + +コンフリクトが発生した場合: + +1. コンフリクトファイルをリストアップ +2. ユーザーに通知 +3. 解決方法を提案: + - 自動解決可能な場合(同一ファイル): 自動解決 + - 手動解決が必要な場合: ガイダンスを表示して終了 + +### 自動解決ロジック + +```bash +# コンフリクトファイルを確認 +git ls-files -u | awk '{print $4}' | sort -u + +# 各ファイルについて、両バージョンが同一かチェック +for file in ${CONFLICT_FILES}; do + if git diff HEAD:$file origin/${BASE_BRANCH}:$file > /dev/null 2>&1; then + # 同一の場合: origin/main のバージョンを使用 + git checkout origin/${BASE_BRANCH} -- $file + git add $file + else + # 異なる場合: 手動解決が必要 + echo "Manual resolution required for: $file" + fi +done +``` + +### マージコミット + +コンフリクト解決後、マージコミットを作成: + +```bash +git commit -m "feat: Merge latest ${BASE_BRANCH} branch updates + +${BASE_BRANCH}ブランチの最新の変更を取り込みました。 + +## コンフリクト解決 +${RESOLVED_FILES} + +🤖 Generated with [Claude Code](https://claude.com/claude-code) + +Co-Authored-By: Claude Sonnet 4.5 " +``` + +## Step 4: Generate PR Title and Body + +PR タイトルと本文を生成: + +### タイトル生成 + +`--title` が指定されている場合はそれを使用。 +指定されていない場合は、最新のコミットメッセージから生成: + +```bash +# 最新のコミットメッセージを取得 +git log -1 --format=%s + +# Conventional Commits 形式から抽出 +# 例: "feat: Add new feature" -> "Add new feature" +``` + +### 本文生成 + +```bash +# ベースブランチからの差分コミットをリストアップ +git log origin/${BASE_BRANCH}..HEAD --format="%h %s" + +# 変更ファイル数を取得 +git diff origin/${BASE_BRANCH}..HEAD --stat +``` + +本文テンプレート: + +```markdown +## 概要 + +${DESCRIPTION} + +## 変更内容 + +${COMMIT_LIST} + +## 変更統計 + +- 変更ファイル数: X 件 +- 追加行数: Y 行 +- 削除行数: Z 行 + +## テスト + +- ✅ pre-commit フック: Format, Lint, Test 通過 +- ✅ コンフリクト解決: 完了 +- ✅ 最新の${BASE_BRANCH}ブランチとマージ済み + +🤖 Generated with [Claude Code](https://claude.com/claude-code) +``` + +## Step 5: Push to Remote + +リモートブランチにプッシュ: + +```bash +# 現在のブランチをリモートにプッシュ +git push -u origin $(git branch --show-current) +``` + +### エラー処理 + +- リモートブランチが既に存在する場合: force pushを確認 +- プッシュに失敗した場合: エラー内容を表示して終了 + +## Step 6: Create Pull Request + +gh CLI を使用してPRを作成: + +```bash +gh pr create \ + --base ${BASE_BRANCH} \ + --title "${PR_TITLE}" \ + --body "${PR_BODY}" \ + ${DRAFT_FLAG} +``` + +### オプション + +- `${DRAFT_FLAG}`: `--draft` が指定されている場合は `--draft` を追加 + +### PR作成後 + +PR URLを返却: + +``` +✅ Pull Request created successfully! + +PR URL: https://github.com/owner/repo/pull/123 + +次のステップ: +1. PR の内容を確認 +2. CI チェックの結果を確認 +3. レビューを依頼 +4. 必要に応じて修正 +``` + +## Step 7: Final Report + +完了レポートを表示: + +``` +✅ PR creation complete! + +ブランチ: ${CURRENT_BRANCH} +ベース: ${BASE_BRANCH} +タイトル: ${PR_TITLE} +ドラフト: ${IS_DRAFT} + +PR URL: ${PR_URL} + +変更内容: +- コミット数: X 件 +- 変更ファイル数: Y 件 +- マージコミット: ${MERGE_COMMIT_HASH} + +次のステップ: +1. CI チェックの結果を確認 +2. コードレビューを依頼 +3. フィードバックに対応 +4. マージ準備完了後、レビュアーに通知 +``` + +--- + +## Progress Reporting + +各ステップの進捗を報告: + +- ✅ Step N: [完了した操作] +- 🔄 Step N: [実行中の操作] +- ❌ Step N: [失敗 - 理由] + +## Error Handling + +エラー発生時: + +1. 具体的なエラー内容を報告 +2. 原因を説明 +3. 修正方法を提案 +4. 必要に応じてロールバック手順を提供 + +## Notes + +- **ベースブランチの取り込み**: 常に最新のベースブランチをマージしてからPRを作成 +- **コンフリクト自動解決**: 同一ファイルの場合のみ自動解決、それ以外は手動解決を要求 +- **Conventional Commits**: コミットメッセージとPRタイトルは Conventional Commits 形式を推奨 +- **ドラフトPR**: 作業途中の場合は `--draft` オプションを使用 +- **Force Push**: リモートブランチが既に存在する場合、force push は慎重に実行 diff --git a/.claude/commands/dependency-health-check.md b/.claude/commands/dependency-health-check.md new file mode 100644 index 00000000..ffbadc82 --- /dev/null +++ b/.claude/commands/dependency-health-check.md @@ -0,0 +1,123 @@ +# Dependency Health Check Command + +Comprehensive dependency health analysis including updates, security, and licensing. + +## Usage + +```bash +/dependency-health-check +``` + +## What It Does + +This command performs a comprehensive health check of all project dependencies: + +### npm Dependencies + +- **Outdated Packages**: Detects packages with available updates +- **Security Vulnerabilities**: Scans for known vulnerabilities (`npm audit`) +- **Deprecated Packages**: Identifies deprecated dependencies +- **License Compliance**: Checks for incompatible licenses +- **Peer Dependencies**: Validates peer dependency requirements + +### DevContainer Features + +- **Feature Updates**: Checks for newer versions of DevContainer features +- **Base Image**: Verifies if base image has updates +- **Deprecated Features**: Identifies deprecated features + +### Analysis Report + +- **Risk Level**: Critical, High, Medium, Low +- **Action Required**: Immediate, Soon, Optional +- **Recommendations**: Specific actions to improve dependency health + +## Example Output + +``` +🔍 Dependency Health Check +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +📦 npm Packages (425 total) + ✓ No critical vulnerabilities + ⚠ 3 high severity vulnerabilities + • 12 packages can be updated + • 2 deprecated packages found + +🔒 Security Status: Medium Risk + High: 3 vulnerabilities + - axios: Prototype pollution (CVE-2023-XXXX) + - semver: ReDoS vulnerability (CVE-2023-YYYY) + - json5: Prototype pollution (CVE-2022-ZZZZ) + +📊 Update Summary + Major: 2 packages + Minor: 7 packages + Patch: 3 packages + +⚠ Deprecated Packages + • request (use axios or node-fetch instead) + • babel-eslint (use @babel/eslint-parser) + +✅ License Compliance + • All licenses compatible + • MIT: 387 packages + • Apache-2.0: 28 packages + • BSD-3-Clause: 10 packages + +🏥 Overall Health: 75/100 + Recommendations: + 1. Update axios to v1.6.0 (security fix) + 2. Replace deprecated packages + 3. Update 12 minor/patch versions + +Next steps: + npm update # Update minor/patch versions + npm audit fix # Auto-fix security issues + npm outdated # See all outdated packages +``` + +## Options + +```bash +# JSON output for CI integration +/dependency-health-check --json + +# Fail on high severity issues +/dependency-health-check --strict +``` + +## CI Integration + +```yaml +# .github/workflows/dependency-health.yml +- name: Dependency Health Check + run: bash script/dependency-health-check.sh --strict +``` + +## Risk Levels + +| Level | Criteria | Action | +| -------- | ---------------------------------- | --------- | +| Critical | Critical vulnerabilities | Immediate | +| High | High severity or many outdated | Soon | +| Medium | Some vulnerabilities or deprecated | Optional | +| Low | Minor updates only | Optional | + +## Benefits + +- 🛡️ **Security**: Early detection of vulnerabilities +- 📊 **Visibility**: Clear dependency status +- ⚡ **Proactive**: Catch issues before they're problems +- 📋 **Compliance**: License and policy enforcement +- 🔄 **Maintenance**: Easier dependency management + +## Implementation + +This command is implemented in `script/dependency-health-check.sh`. + +## Requirements + +- Node.js and npm +- Access to npm registry +- DevContainer configuration (optional) diff --git a/.claude/commands/pre-pr-checklist.md b/.claude/commands/pre-pr-checklist.md new file mode 100644 index 00000000..be9886aa --- /dev/null +++ b/.claude/commands/pre-pr-checklist.md @@ -0,0 +1,106 @@ +# Pre-PR Checklist Command + +Automate comprehensive checks before creating a pull request. + +## Usage + +```bash +/pre-pr-checklist +``` + +## What It Does + +This command runs a comprehensive checklist to ensure your changes are ready for pull request: + +### Quality Checks (Sequential) + +1. **Lint Check**: Runs ESLint to detect code issues +2. **Format Check**: Verifies Prettier formatting +3. **Type Check**: Validates TypeScript types (if applicable) +4. **Unit Tests**: Runs all tests with coverage +5. **Integration Tests**: Runs Bats integration tests +6. **Shellcheck**: Validates shell scripts + +### PR Analysis + +- **Size Estimation**: Calculates diff lines and file count +- **Size Label**: Suggests appropriate size label (S/M/L/XL) +- **Linked Issues**: Checks for related GitHub issues +- **Commit Messages**: Validates conventional commit format + +### PR Preparation + +- **Branch Status**: Checks if branch is up-to-date with main +- **Merge Conflicts**: Detects potential merge conflicts +- **Template Suggestion**: Recommends PR template content + +## Size Thresholds + +| Label | Line Changes | File Count | +| ------- | ------------ | ---------- | +| size/S | < 100 | < 10 | +| size/M | < 300 | < 20 | +| size/L | < 1000 | < 30 | +| size/XL | ≥ 1000 | ≥ 30 | + +## Example Output + +``` +📋 Pre-PR Checklist +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +✅ Quality Checks + ✓ Lint check passed + ✓ Format check passed + ✓ Tests passed (101/101) + ✓ Coverage: 82.5% (threshold: 70%) + +📊 PR Analysis + • Size: Medium (247 lines, 8 files) + • Suggested label: size/M + • Linked issues: #227 + • Commits: 3 (all follow conventional commits) + +🔄 Branch Status + ✓ Up-to-date with main + ✓ No merge conflicts + +✨ Ready to create PR! +``` + +## Options + +```bash +# Skip tests (faster, but not recommended) +/pre-pr-checklist --skip-tests + +# Skip integration tests only +/pre-pr-checklist --skip-integration +``` + +## Implementation + +This command is implemented in `script/pre-pr-checklist.sh`. + +## Integration + +Works seamlessly with: + +- GitHub Actions CI workflows +- Git hooks (pre-push) +- IDE integrations +- Manual PR preparation + +## Benefits + +- 🚀 **Faster Reviews**: Catch issues before PR creation +- ✅ **Quality Assurance**: All checks pass before submission +- 📊 **Better PRs**: Proper sizing and documentation +- ⚡ **Time Saving**: One command instead of multiple + +## Requirements + +- Git repository +- Node.js and npm +- GitHub CLI (`gh`) for issue detection +- All project dependencies installed diff --git a/.claude/commands/similarity-analysis.md b/.claude/commands/similarity-analysis.md index 30a41662..08a5a934 100644 --- a/.claude/commands/similarity-analysis.md +++ b/.claude/commands/similarity-analysis.md @@ -1,5 +1,6 @@ --- description: Analyze code similarity in the repository to detect duplicate functions and patterns +allowed-tools: Read, Write, Edit, Bash(git:*), Bash(gh:*), Bash(similarity-ts:*) arguments: - name: path description: Target path to analyze (default: current directory) @@ -7,6 +8,12 @@ arguments: - name: threshold description: Similarity threshold 0.0-1.0 (default: 0.8) required: false + - name: auto-refactor + description: Automatically refactor and create PRs for each similarity (default: false) + required: false + - name: base-branch + description: Base branch for PRs (default: main) + required: false --- # Code Similarity Analysis @@ -62,8 +69,206 @@ similarity-ts ${path:-.} --threshold ${threshold:-0.8} --print --exclude node_mo - `--min-lines `: 最小行数でフィルタ(デフォルト: 3) - `--filter-function `: 特定の関数名でフィルタ +## 自動リファクタリングとPR作成(--auto-refactor オプション) + +`--auto-refactor` オプションを指定すると、検出された類似コードに対して自動的にリファクタリングを実施し、各類似ペアごとに別々のPRを作成します。 + +### Step 1: 類似コードの検出 + +```bash +similarity-ts ${path:-.} --threshold ${threshold:-0.8} --print --exclude node_modules --exclude dist --exclude .git --exclude coverage +``` + +### Step 2: 類似ペアの分類 + +検出された類似ペアを優先度別に分類: + +1. **High Priority** (類似度 95%以上): 即座にリファクタリング推奨 +2. **Medium Priority** (類似度 85-95%): 共通関数への抽出を検討 +3. **Low Priority** (類似度 85%未満): レビューのみ + +### Step 3: 各類似ペアごとにリファクタリング + +各類似ペア(High/Medium Priority)について: + +#### 3.1 ブランチ作成 + +```bash +# 最新のベースブランチを取得 +git fetch origin ${base-branch:-main} + +# リファクタリング用のブランチを作成 +git checkout -b refactor/similarity-${PAIR_ID}-$(date +%Y%m%d%H%M%S) origin/${base-branch:-main} +``` + +#### 3.2 共通関数の抽出 + +1. 類似コードの共通部分を特定 +2. 共通関数を作成(適切な場所に配置) +3. 既存のコードを共通関数の呼び出しに置き換え + +#### 3.3 テストの実行 + +```bash +# リファクタリング後、テストを実行 +npm test + +# もしくは +npm run test:unit +``` + +テストが失敗した場合: + +- リファクタリングを調整 +- テストを修正 +- 再度実行 + +#### 3.4 コミットとPR作成 + +```bash +# 変更をコミット +git add . +git commit -m "refactor: Extract common function for ${DESCRIPTION} + +類似度: ${SIMILARITY}% +ファイル1: ${FILE1}:${LINE1} +ファイル2: ${FILE2}:${LINE2} + +## リファクタリング内容 + +${REFACTORING_DETAILS} + +## 影響範囲 + +- ${AFFECTED_FILES} + +🤖 Generated with [Claude Code](https://claude.com/claude-code) + +Co-Authored-By: Claude Sonnet 4.5 " + +# リモートにプッシュ +git push -u origin refactor/similarity-${PAIR_ID}-$(date +%Y%m%d%H%M%S) + +# PRを作成 +gh pr create \ + --base ${base-branch:-main} \ + --title "refactor: Extract common function for ${DESCRIPTION}" \ + --body "$(cat </dev/null +find ${BASE_PATH} -name "settings.local.json" -type f 2>/dev/null | grep -v node_modules ``` 見つかったファイル数を報告: -- 0件の場合: エラーを報告して終了 -- 1件以上: 次のステップへ進む +- 0件の場合: 警告を表示して終了 +- 1件以上: node_modules 内のファイルを除外してから次のステップへ進む ## Step 3: Read and Parse Settings Files @@ -40,12 +46,16 @@ find ${BASE_PATH} -name "settings.local.json" -type f 2>/dev/null 1. ファイルパスとリポジトリ名を記録 2. JSON として解析 3. `permissions.allow`, `permissions.deny`, `permissions.ask` を抽出 -4. エラーがあればスキップして次へ(エラー内容は記録) +4. セキュリティ配慮: + - APIキー、トークン、パスワードを含むコマンドを除外(SUPABASE_SERVICE_ROLE_KEY, AWS_ACCESS_KEY_ID など) + - 特定のプロジェクトパスを含む Read パーミッションを除外(`Read(//workspaces/specific-project/**)`) +5. エラーがあればスキップして次へ(エラー内容は記録) 読み込み結果を報告: - 成功: X 件 - 失敗: Y 件(ファイルパスと理由を列挙) +- 除外: Z 件(セキュリティ上の理由) ## Step 4: Analyze Common Patterns @@ -204,11 +214,23 @@ Elu-co-jp 配下の全プロジェクトから `.claude/settings.local.json` を - 破壊的 Git コマンド - 破壊的インフラコマンド +## セキュリティチェック + +✅ すべての追加項目を確認済み +- 汎用的なコマンドパターンのみ +- APIキー、トークン、パスワードなどの秘匿情報は含まれていません +- プロジェクト固有の情報は除外済み + ## 影響範囲 - DevContainer イメージをビルドする全プロジェクト - 次回の DevContainer イメージビルド時から有効化 +## テスト + +- ✅ pre-commit フック: Format, Lint, Test 通過 +- ✅ 秘匿情報チェック: 問題なし + 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF )" @@ -231,6 +253,10 @@ PR URL を報告。 - 許可設定追加: X 件 - 拒否設定追加: Y 件 +セキュリティチェック: +- ✅ 秘匿情報: なし +- ✅ プロジェクト固有情報: 除外済み + PR: {PR-URL または "ローカル更新のみ"} 次のステップ: diff --git a/.codex/devcontainer-recommendations.md b/.codex/devcontainer-recommendations.md index 554d31a1..0ec9c09b 100644 --- a/.codex/devcontainer-recommendations.md +++ b/.codex/devcontainer-recommendations.md @@ -275,6 +275,40 @@ OPENAI_API_KEY=*** # o3 MCP用 } ``` +#### pnpmパッケージマネージャー + +pnpmを使用する場合、以下の2つの設定方法があります: + +**推奨: 独立したpnpm Feature(推奨)** + +```json +{ + "ghcr.io/devcontainers-extra/features/pnpm:2": { + "version": "latest" + } +} +``` + +**利点**: + +- pnpmバージョン管理が明確 +- node Featureとの依存関係を分離 +- 最新のpnpm機能を即座に利用可能 +- より柔軟なバージョン管理 + +**代替: node:1のpnpmVersionオプション** + +```json +{ + "ghcr.io/devcontainers/features/node:1": { + "version": "20", + "pnpmVersion": "latest" + } +} +``` + +**推奨**: 独立したpnpm:2 Featureを使用することで、Node.jsとpnpmのバージョン管理を分離し、より明確な構成が可能になります + ### Supabaseプロジェクト ```json @@ -288,6 +322,36 @@ OPENAI_API_KEY=*** # o3 MCP用 - 利用率: 75% (6/8) - 必須ケース: Supabase使用プロジェクト全般 +### Deno Runtime(Edge Functions開発) + +```json +{ + "ghcr.io/devcontainers-community/features/deno:1": {} +} +``` + +**バージョン**: 1 (最新のメジャーバージョン) + +**利点**: + +- **TypeScriptファーストサポート**: 設定不要でTypeScriptを直接実行可能 +- **Supabase Edge Functions対応**: Supabase Edge Functionsの開発環境として必須 +- **組み込みツールチェーン**: `deno fmt`(フォーマッター)、`deno lint`(リンター)、`deno test`(テストランナー)が標準搭載 +- **セキュアデフォルト**: 権限システムによりファイルシステムやネットワークアクセスを明示的に許可 +- **モダンエコシステム**: JSR (JavaScript Registry) との統合 + +**必須ケース**: + +- Supabase Edge Functionsの開発 +- DenoベースのWebアプリケーション +- TypeScript/JavaScriptのモダンランタイム環境が必要な場合 + +**参考リンク**: + +- [Deno DevContainer Feature](https://github.com/devcontainers-community/features/tree/main/src/deno) +- [Deno公式ドキュメント](https://deno.com/) +- [Supabase Edge Functions](https://supabase.com/docs/guides/functions) + ### E2Eテスト環境 ```json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7f646397..42ac80af 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -56,8 +56,7 @@ RUN CLAUDE_CODE_VERSION=$(node -pe "require('/tmp/npm-global.json').dependencies typescript \ typescript-language-server \ @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \ - @openai/codex@${CODEX_VERSION} \ - vercel + @openai/codex@${CODEX_VERSION} USER vscode RUN bash -lc "cargo install similarity-ts" || true \ diff --git a/.devcontainer/claude-settings.json b/.devcontainer/claude-settings.json index 6efa360b..cae99201 100644 --- a/.devcontainer/claude-settings.json +++ b/.devcontainer/claude-settings.json @@ -17,6 +17,7 @@ "WebFetch(domain:artifacthub.io)", "WebFetch(domain:developers.notion.com)", "WebFetch(domain:www.assemblyai.com)", + "WebFetch(domain:ai-sdk.dev)", "mcp__ide__getDiagnostics", "Bash(ls:*)", "Bash(cat:*)", @@ -39,6 +40,9 @@ "Bash(tar:*)", "Bash(tree:*)", "Bash(du:*)", + "Bash(wc:*)", + "Bash(xargs:*)", + "Bash(paste:*)", "Bash(host:*)", "Bash(lsof:*)", "Bash(pkill:*)", @@ -49,8 +53,11 @@ "Bash(source:*)", "Bash(env:*)", "Bash(bash:*)", + "Bash(jq:*)", + "Bash(perl:*)", "Bash(true)", "Bash(node:*)", + "Bash(python:*)", "Bash(python3:*)", "Bash(deno:*)", "Bash(deno --version)", @@ -177,6 +184,7 @@ "Bash(npx supabase gen types typescript:*)", "mcp__plugin_supabase-toolkit_supabase__list_tables", "mcp__plugin_supabase-toolkit_supabase__get_project_url", + "mcp__plugin_supabase-toolkit_supabase__search_docs", "Bash(psql:*)", "Bash(PGPASSWORD=postgres psql:*)", "Bash(docker:*)", @@ -245,8 +253,6 @@ "Bash(gcloud container clusters:*)", "Bash(gcloud container clusters get-credentials:*)", "Bash(gcloud compute machine-types describe:*)", - "Bash(vercel:*)", - "Bash(vercel env:*)", "Bash(wscat:*)", "Bash(openssl rand:*)", "Bash(uv run:*)", @@ -257,6 +263,19 @@ "Bash(act:*)", "Bash(afplay:*)", "Bash(brew list:*)", + "Bash(similarity-ts:*)", + "Bash(shellcheck:*)", + "Bash(cloc:*)", + "Bash(command -v:*)", + "Bash(op inject:*)", + "Bash(op vault list:*)", + "Bash(op item list:*)", + "Bash(op item get:*)", + "Bash(zsh:*)", + "Bash(zsh -n:*)", + "Read(//.codex/**)", + "Read(//.claude/plugins/**)", + "Read(//home/vscode/**)", "Read(//tmp/**)", "Read(//workspaces/**)", "Skill(plugin-dev:command-development)" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index be1ba75a..a5ed891d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,7 +18,8 @@ "helm": "none", "minikube": "none" }, - "ghcr.io/dhoeric/features/act:1": {} + "ghcr.io/dhoeric/features/act:1": {}, + "ghcr.io/devcontainers-community/features/deno:1": {} }, "remoteEnv": { "HOMEBREW_NO_AUTO_UPDATE": "1", @@ -44,6 +45,6 @@ } } }, - "postCreateCommand": "bash script/setup-env.sh && bash script/setup-mcp.sh && sudo chown -R vscode:vscode /workspaces/config && npm ci && npm run prepare && cp -r /tmp/.husky /workspaces/config/ && cp git/commitlint.config.js commitlint.config.js && /usr/local/bin/setup-claude.sh", + "postCreateCommand": "bash script/setup-env.sh && bash script/setup-mcp.sh && sudo chown -R vscode:vscode /workspaces/config && npm ci && npm run prepare && cp -r /tmp/.husky /workspaces/config/ && cp git/commitlint.config.js commitlint.config.js && bash script/sync-claude-commands.sh && /usr/local/bin/setup-claude.sh", "runArgs": ["--env-file=${localEnv:HOME}/.devcontainer.env"] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78046650..af252ab2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' cache: 'npm' @@ -44,7 +44,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' cache: 'npm' @@ -59,7 +59,7 @@ jobs: JEST_JUNIT_OUTPUT_NAME: junit.xml - name: Upload test results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: always() with: name: test-results @@ -76,10 +76,10 @@ jobs: fail-on-error: false - name: Upload coverage reports - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 if: always() with: - file: ./coverage/lcov.info + files: ./coverage/lcov.info fail_ci_if_error: false token: ${{ secrets.CODECOV_TOKEN }} @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' cache: 'npm' @@ -107,7 +107,7 @@ jobs: bats test/integration/*.bats --formatter tap > reports/bats-results.tap || true - name: Upload integration test results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: always() with: name: integration-test-results diff --git a/.github/workflows/container-security.yml b/.github/workflows/container-security.yml index 2b9ba6c9..a84c1535 100644 --- a/.github/workflows/container-security.yml +++ b/.github/workflows/container-security.yml @@ -29,6 +29,20 @@ jobs: security-events: write steps: + - name: Free disk space before scan + run: | + echo "=== Disk space before cleanup ===" + df -h + + docker system prune -af --volumes + sudo rm -rf /usr/local/lib/android || true + sudo rm -rf /usr/share/dotnet || true + sudo rm -rf /opt/ghc || true + sudo rm -rf /usr/local/share/boost || true + + echo "=== Disk space after cleanup ===" + df -h + - name: Checkout repository uses: actions/checkout@v4 @@ -86,6 +100,20 @@ jobs: packages: read steps: + - name: Free disk space before SBOM generation + run: | + echo "=== Disk space before cleanup ===" + df -h + + docker system prune -af --volumes + sudo rm -rf /usr/local/lib/android || true + sudo rm -rf /usr/share/dotnet || true + sudo rm -rf /opt/ghc || true + sudo rm -rf /usr/local/share/boost || true + + echo "=== Disk space after cleanup ===" + df -h + - name: Checkout repository uses: actions/checkout@v4 @@ -110,7 +138,7 @@ jobs: output-file: sbom.spdx.json - name: Upload SBOM artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: sbom path: sbom.spdx.json diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 8c794336..cd0aa80c 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -39,7 +39,7 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' @@ -173,7 +173,7 @@ jobs: run: | docker run --rm -v "$PWD":/workspace -w /workspace ghcr.io/${{ github.repository_owner }}/config-base:latest bash -lc '{ brew --version; terraform --version; jq --version; } > devcontainer-info.txt' - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 if: steps.release.outputs.skip_release != 'true' with: name: devcontainer-info diff --git a/.github/workflows/manual-release.yml b/.github/workflows/manual-release.yml index e74d7d8d..1f582625 100644 --- a/.github/workflows/manual-release.yml +++ b/.github/workflows/manual-release.yml @@ -31,7 +31,7 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' @@ -76,7 +76,7 @@ jobs: - name: Fix workspace permissions run: sudo chown -R $(id -u):$(id -g) "$GITHUB_WORKSPACE" - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v2 with: diff --git a/.github/workflows/rebuild-docker-cache.yml b/.github/workflows/rebuild-docker-cache.yml index 5d7aa979..b877cd80 100644 --- a/.github/workflows/rebuild-docker-cache.yml +++ b/.github/workflows/rebuild-docker-cache.yml @@ -27,7 +27,7 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index df4552dd..deb730a3 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '22' cache: 'npm' @@ -80,7 +80,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '22' cache: 'npm' diff --git a/.github/workflows/update-libraries.yml b/.github/workflows/update-libraries.yml index ddec14cb..bdadd240 100644 --- a/.github/workflows/update-libraries.yml +++ b/.github/workflows/update-libraries.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' cache: 'npm' diff --git a/script/branch-cleanup.sh b/script/branch-cleanup.sh new file mode 100755 index 00000000..b55a0757 --- /dev/null +++ b/script/branch-cleanup.sh @@ -0,0 +1,243 @@ +#!/usr/bin/env bash +# Branch Cleanup - Remove merged and stale branches + +set -euo pipefail + +# Colors +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color + +# Options +DRY_RUN=false +INCLUDE_REMOTE=false +AUTO_CONFIRM=false +STALE_DAYS=30 +MERGED_ONLY=false + +# Protected branches +PROTECTED_BRANCHES=("main" "master" "develop" "staging" "production") + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) + DRY_RUN=true + shift + ;; + --remote) + INCLUDE_REMOTE=true + shift + ;; + --yes|-y) + AUTO_CONFIRM=true + shift + ;; + --stale-days) + STALE_DAYS="$2" + shift 2 + ;; + --merged-only) + MERGED_ONLY=true + shift + ;; + --help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --dry-run Preview without deleting" + echo " --remote Include remote branches" + echo " --yes, -y Auto-confirm deletion" + echo " --stale-days N Staleness threshold (default: 30)" + echo " --merged-only Only delete merged branches" + echo " --help Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +echo -e "${BLUE}🧹 Branch Cleanup${NC}" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +# Check if in git repository +if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo -e "${RED}✗ Not in a git repository${NC}" + exit 1 +fi + +# Get current branch +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +echo -e "📍 Current branch: ${GREEN}$CURRENT_BRANCH${NC}" + +# Get main branch +MAIN_BRANCH=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5 2>/dev/null || echo "main") +if ! git rev-parse --verify "$MAIN_BRANCH" > /dev/null 2>&1; then + MAIN_BRANCH="master" +fi + +echo -e "🔒 Protected: ${PROTECTED_BRANCHES[*]}" +echo "" + +# Fetch latest +git fetch --prune > /dev/null 2>&1 + +# Find merged branches +MERGED_BRANCHES=() +while IFS= read -r branch; do + # Skip protected branches + is_protected=false + for protected in "${PROTECTED_BRANCHES[@]}"; do + if [ "$branch" = "$protected" ]; then + is_protected=true + break + fi + done + + # Skip current branch + if [ "$branch" = "$CURRENT_BRANCH" ]; then + is_protected=true + fi + + if [ "$is_protected" = false ]; then + MERGED_BRANCHES+=("$branch") + fi +done < <(git branch --merged "$MAIN_BRANCH" | sed 's/^[* ]*//' | grep -v "^$MAIN_BRANCH$" || true) + +# Find stale branches (if not merged-only) +STALE_BRANCHES=() +if [ "$MERGED_ONLY" = false ]; then + CUTOFF_DATE=$(date -v-"${STALE_DAYS}"d +%s 2>/dev/null || date -d "${STALE_DAYS} days ago" +%s 2>/dev/null || echo "0") + + while IFS= read -r branch; do + # Skip if already in merged list + if [[ " ${MERGED_BRANCHES[*]} " =~ \ ${branch}\ ]]; then + continue + fi + + # Skip protected branches and current + is_protected=false + for protected in "${PROTECTED_BRANCHES[@]}"; do + if [ "$branch" = "$protected" ]; then + is_protected=true + break + fi + done + + if [ "$branch" = "$CURRENT_BRANCH" ]; then + is_protected=true + fi + + if [ "$is_protected" = false ]; then + # Get last commit date + LAST_COMMIT_DATE=$(git log -1 --format=%ct "$branch" 2>/dev/null || echo "0") + + if [ "$LAST_COMMIT_DATE" -lt "$CUTOFF_DATE" ] && [ "$LAST_COMMIT_DATE" != "0" ]; then + STALE_BRANCHES+=("$branch") + fi + fi + done < <(git branch | sed 's/^[* ]*//' | grep -v "^$MAIN_BRANCH$" || true) +fi + +# Display analysis +echo -e "${BLUE}📊 Analysis${NC}" +TOTAL_BRANCHES=$(git branch | wc -l | tr -d ' ') +echo " • Total local branches: $TOTAL_BRANCHES" +echo " • Merged branches: ${#MERGED_BRANCHES[@]}" +if [ "$MERGED_ONLY" = false ]; then + echo " • Stale branches (${STALE_DAYS}+ days): ${#STALE_BRANCHES[@]}" +fi +echo "" + +# Calculate total to delete +TOTAL_TO_DELETE=$((${#MERGED_BRANCHES[@]} + ${#STALE_BRANCHES[@]})) + +if [ "$TOTAL_TO_DELETE" -eq 0 ]; then + echo -e "${GREEN}✨ No branches to clean up!${NC}" + exit 0 +fi + +echo -e "${YELLOW}🗑️ Branches to delete ($TOTAL_TO_DELETE):${NC}" +echo "" + +# Show merged branches +if [ "${#MERGED_BRANCHES[@]}" -gt 0 ]; then + echo -e "${GREEN}Merged (${#MERGED_BRANCHES[@]}):${NC}" + for branch in "${MERGED_BRANCHES[@]:0:5}"; do + LAST_COMMIT=$(git log -1 --format="%cr" "$branch" 2>/dev/null || echo "unknown") + echo " ✓ $branch (merged $LAST_COMMIT)" + done + if [ "${#MERGED_BRANCHES[@]}" -gt 5 ]; then + echo " ... and $((${#MERGED_BRANCHES[@]} - 5)) more" + fi + echo "" +fi + +# Show stale branches +if [ "${#STALE_BRANCHES[@]}" -gt 0 ]; then + echo -e "${YELLOW}Stale (${#STALE_BRANCHES[@]}):${NC}" + for branch in "${STALE_BRANCHES[@]:0:5}"; do + LAST_COMMIT=$(git log -1 --format="%cr" "$branch" 2>/dev/null || echo "unknown") + echo " ⚠ $branch ($LAST_COMMIT)" + done + if [ "${#STALE_BRANCHES[@]}" -gt 5 ]; then + echo " ... and $((${#STALE_BRANCHES[@]} - 5)) more" + fi + echo "" +fi + +# Dry run mode +if [ "$DRY_RUN" = true ]; then + echo -e "${BLUE}ℹ️ Dry run mode - no branches deleted${NC}" + echo " Run without --dry-run to delete these branches" + exit 0 +fi + +# Confirm deletion +if [ "$AUTO_CONFIRM" = false ]; then + echo -n "Delete these branches? [y/N]: " + read -r response + if [[ ! "$response" =~ ^[Yy]$ ]]; then + echo "Cancelled." + exit 0 + fi +fi + +# Delete branches +echo "" +echo "Deleting branches..." +DELETED_COUNT=0 + +for branch in "${MERGED_BRANCHES[@]}" "${STALE_BRANCHES[@]}"; do + if git branch -D "$branch" > /dev/null 2>&1; then + echo -e " ${GREEN}✓${NC} Deleted $branch" + ((DELETED_COUNT++)) + else + echo -e " ${RED}✗${NC} Failed to delete $branch" + fi +done + +echo "" +echo -e "${GREEN}✨ Cleanup complete! Removed $DELETED_COUNT branches.${NC}" + +# Remote cleanup (if requested) +if [ "$INCLUDE_REMOTE" = true ]; then + echo "" + echo -e "${BLUE}🌐 Remote Branch Cleanup${NC}" + echo " (This requires GitHub CLI and proper permissions)" + echo "" + + if command -v gh > /dev/null 2>&1; then + # This is a placeholder - actual implementation would require more logic + echo " Remote cleanup not yet implemented" + echo " Use: git push origin --delete " + else + echo " GitHub CLI (gh) not installed" + fi +fi diff --git a/script/dependency-health-check.sh b/script/dependency-health-check.sh new file mode 100755 index 00000000..1c7522a9 --- /dev/null +++ b/script/dependency-health-check.sh @@ -0,0 +1,188 @@ +#!/usr/bin/env bash +# Dependency Health Check - Comprehensive dependency analysis + +set -euo pipefail + +# Colors +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color + +# Options +JSON_OUTPUT=false +STRICT_MODE=false + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --json) + JSON_OUTPUT=true + shift + ;; + --strict) + STRICT_MODE=true + shift + ;; + --help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --json Output in JSON format" + echo " --strict Fail on high severity issues" + echo " --help Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [ "$JSON_OUTPUT" = false ]; then + echo -e "${BLUE}🔍 Dependency Health Check${NC}" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" +fi + +# npm Packages Check +TOTAL_PACKAGES=0 +OUTDATED_COUNT=0 +VULN_CRITICAL=0 +VULN_HIGH=0 +VULN_MODERATE=0 +VULN_LOW=0 + +if [ -f "package.json" ]; then + if [ "$JSON_OUTPUT" = false ]; then + echo -e "${BLUE}📦 npm Packages${NC}" + fi + + # Count total packages + TOTAL_PACKAGES=$(npm list --all --json 2>/dev/null | jq '[.. | .dependencies? | select(. != null) | keys[]] | unique | length' || echo "0") + + # Check for outdated packages + OUTDATED_OUTPUT=$(npm outdated --json 2>/dev/null || echo "{}") + OUTDATED_COUNT=$(echo "$OUTDATED_OUTPUT" | jq 'length' 2>/dev/null || echo "0") + + # Security audit + AUDIT_OUTPUT=$(npm audit --json 2>/dev/null || echo '{"vulnerabilities":{}}') + VULN_CRITICAL=$(echo "$AUDIT_OUTPUT" | jq '.metadata.vulnerabilities.critical // 0' 2>/dev/null || echo "0") + VULN_HIGH=$(echo "$AUDIT_OUTPUT" | jq '.metadata.vulnerabilities.high // 0' 2>/dev/null || echo "0") + VULN_MODERATE=$(echo "$AUDIT_OUTPUT" | jq '.metadata.vulnerabilities.moderate // 0' 2>/dev/null || echo "0") + VULN_LOW=$(echo "$AUDIT_OUTPUT" | jq '.metadata.vulnerabilities.low // 0' 2>/dev/null || echo "0") + + if [ "$JSON_OUTPUT" = false ]; then + echo " • Total packages: $TOTAL_PACKAGES" + + # Vulnerabilities + if [ "$VULN_CRITICAL" -gt 0 ]; then + echo -e " ${RED}✗ $VULN_CRITICAL critical vulnerabilities${NC}" + else + echo -e " ${GREEN}✓ No critical vulnerabilities${NC}" + fi + + if [ "$VULN_HIGH" -gt 0 ]; then + echo -e " ${YELLOW}⚠ $VULN_HIGH high severity vulnerabilities${NC}" + fi + + if [ "$VULN_MODERATE" -gt 0 ]; then + echo " • $VULN_MODERATE moderate severity vulnerabilities" + fi + + # Outdated packages + if [ "$OUTDATED_COUNT" -gt 0 ]; then + echo -e " ${YELLOW}⚠ $OUTDATED_COUNT packages can be updated${NC}" + + # Show top 5 outdated + echo "$OUTDATED_OUTPUT" | jq -r 'to_entries | .[0:5] | .[] | " - \(.key): \(.value.current) → \(.value.latest)"' 2>/dev/null || true + + if [ "$OUTDATED_COUNT" -gt 5 ]; then + echo " ... and $((OUTDATED_COUNT - 5)) more" + fi + else + echo -e " ${GREEN}✓ All packages up-to-date${NC}" + fi + + echo "" + fi +fi + +# Determine overall health score +HEALTH_SCORE=100 +HEALTH_SCORE=$((HEALTH_SCORE - VULN_CRITICAL * 20)) +HEALTH_SCORE=$((HEALTH_SCORE - VULN_HIGH * 10)) +HEALTH_SCORE=$((HEALTH_SCORE - VULN_MODERATE * 2)) +HEALTH_SCORE=$((HEALTH_SCORE - OUTDATED_COUNT)) + +if [ $HEALTH_SCORE -lt 0 ]; then + HEALTH_SCORE=0 +fi + +# Determine risk level +RISK_LEVEL="Low" +if [ "$VULN_CRITICAL" -gt 0 ]; then + RISK_LEVEL="Critical" +elif [ "$VULN_HIGH" -gt 0 ]; then + RISK_LEVEL="High" +elif [ "$VULN_MODERATE" -gt 0 ] || [ "$OUTDATED_COUNT" -gt 10 ]; then + RISK_LEVEL="Medium" +fi + +if [ "$JSON_OUTPUT" = true ]; then + # JSON output for CI integration + cat < /dev/null 2>&1; then + echo -e "${RED}✗ Not in a git repository${NC}" + exit 1 +fi + +# Quality Checks +echo -e "${BLUE}✅ Quality Checks${NC}" + +# Lint +echo -n " • Running lint check... " +if npm run lint > /dev/null 2>&1; then + echo -e "${GREEN}✓${NC}" +else + echo -e "${RED}✗ Failed${NC}" + echo -e "${YELLOW} Run: npm run lint:fix${NC}" + exit 1 +fi + +# Format +echo -n " • Running format check... " +if npm run format:check > /dev/null 2>&1; then + echo -e "${GREEN}✓${NC}" +else + echo -e "${RED}✗ Failed${NC}" + echo -e "${YELLOW} Run: npm run format${NC}" + exit 1 +fi + +# Shellcheck +if command -v shellcheck > /dev/null 2>&1; then + echo -n " • Running shellcheck... " + if npm run shellcheck > /dev/null 2>&1; then + echo -e "${GREEN}✓${NC}" + else + echo -e "${RED}✗ Failed${NC}" + exit 1 + fi +fi + +# Tests +if [ "$SKIP_TESTS" = false ]; then + echo -n " • Running unit tests... " + if npm test > /dev/null 2>&1; then + TEST_OUTPUT=$(npm test 2>&1 | tail -5) + PASSED=$(echo "$TEST_OUTPUT" | grep -oP '\d+(?= passed)' || echo "0") + echo -e "${GREEN}✓ ($PASSED tests passed)${NC}" + + # Coverage + if [ -f "coverage/coverage-summary.json" ]; then + COVERAGE=$(node -pe "JSON.parse(require('fs').readFileSync('coverage/coverage-summary.json')).total.lines.pct") + if (( $(echo "$COVERAGE >= 70" | bc -l) )); then + echo -e " • Coverage: ${GREEN}${COVERAGE}%${NC} (threshold: 70%)" + else + echo -e " • Coverage: ${RED}${COVERAGE}%${NC} (threshold: 70%)" + exit 1 + fi + fi + else + echo -e "${RED}✗ Failed${NC}" + exit 1 + fi + + # Integration tests + if [ "$SKIP_INTEGRATION" = false ] && command -v bats > /dev/null 2>&1; then + echo -n " • Running integration tests... " + if npm run test:integration > /dev/null 2>&1; then + echo -e "${GREEN}✓${NC}" + else + echo -e "${YELLOW}⚠ Integration tests failed or not available${NC}" + fi + fi +fi + +echo "" + +# PR Analysis +echo -e "${BLUE}📊 PR Analysis${NC}" + +# Get current branch +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then + echo -e "${RED}✗ Cannot create PR from main/master branch${NC}" + exit 1 +fi + +# Calculate diff stats +MAIN_BRANCH=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5) +if ! git rev-parse "origin/$MAIN_BRANCH" > /dev/null 2>&1; then + MAIN_BRANCH="main" +fi + +ADDITIONS=$(git diff "origin/$MAIN_BRANCH"...HEAD --numstat | awk '{sum+=$1} END {print sum}') +DELETIONS=$(git diff "origin/$MAIN_BRANCH"...HEAD --numstat | awk '{sum+=$2} END {print sum}') +TOTAL_LINES=$((ADDITIONS + DELETIONS)) +FILE_COUNT=$(git diff "origin/$MAIN_BRANCH"...HEAD --name-only | wc -l | tr -d ' ') + +# Determine size label +if [ "$TOTAL_LINES" -lt 100 ] && [ "$FILE_COUNT" -lt 10 ]; then + SIZE_LABEL="size/S" + SIZE_NAME="Small" +elif [ "$TOTAL_LINES" -lt 300 ] && [ "$FILE_COUNT" -lt 20 ]; then + SIZE_LABEL="size/M" + SIZE_NAME="Medium" +elif [ "$TOTAL_LINES" -lt 1000 ] && [ "$FILE_COUNT" -lt 30 ]; then + SIZE_LABEL="size/L" + SIZE_NAME="Large" +else + SIZE_LABEL="size/XL" + SIZE_NAME="Extra Large" +fi + +echo " • Size: $SIZE_NAME (+$ADDITIONS -$DELETIONS lines, $FILE_COUNT files)" +echo " • Suggested label: $SIZE_LABEL" + +if [ "$SIZE_LABEL" = "size/XL" ]; then + echo -e "${YELLOW} ⚠ Consider breaking this PR into smaller chunks${NC}" +fi + +# Check for linked issues +COMMIT_MESSAGES=$(git log "origin/$MAIN_BRANCH"..HEAD --pretty=format:"%s %b") +LINKED_ISSUES=$(echo "$COMMIT_MESSAGES" | grep -oP '#\d+' | sort -u || true) +if [ -n "$LINKED_ISSUES" ]; then + echo " • Linked issues: $(echo "$LINKED_ISSUES" | tr '\n' ' ')" +else + echo -e "${YELLOW} ⚠ No linked issues found${NC}" +fi + +# Check commit messages +COMMIT_COUNT=$(git rev-list --count "origin/$MAIN_BRANCH"..HEAD) +INVALID_COMMITS=$(git log "origin/$MAIN_BRANCH"..HEAD --pretty=format:"%s" | grep -vE '^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .+' || true) +if [ -z "$INVALID_COMMITS" ]; then + echo " • Commits: $COMMIT_COUNT (all follow conventional commits)" +else + echo -e "${YELLOW} ⚠ Some commits don't follow conventional commits format${NC}" +fi + +echo "" + +# Branch Status +echo -e "${BLUE}🔄 Branch Status${NC}" + +# Check if up-to-date with main +git fetch origin "$MAIN_BRANCH" > /dev/null 2>&1 +BEHIND_COUNT=$(git rev-list --count HEAD.."origin/$MAIN_BRANCH") +if [ "$BEHIND_COUNT" -eq 0 ]; then + echo -e " ${GREEN}✓${NC} Up-to-date with $MAIN_BRANCH" +else + echo -e "${YELLOW} ⚠ Behind $MAIN_BRANCH by $BEHIND_COUNT commits${NC}" + echo -e "${YELLOW} Consider: git rebase origin/$MAIN_BRANCH${NC}" +fi + +# Check for merge conflicts +if git merge-tree "$(git merge-base HEAD "origin/$MAIN_BRANCH")" HEAD "origin/$MAIN_BRANCH" | grep -q '<<<<<<<'; then + echo -e "${RED} ✗ Potential merge conflicts detected${NC}" + exit 1 +else + echo -e " ${GREEN}✓${NC} No merge conflicts" +fi + +echo "" +echo -e "${GREEN}✨ Ready to create PR!${NC}" +echo "" +echo "Next steps:" +echo " 1. Review your changes: git diff origin/$MAIN_BRANCH...HEAD" +echo " 2. Create PR: gh pr create" +echo " 3. Add label: gh pr edit --add-label $SIZE_LABEL" diff --git a/script/sync-claude-commands.sh b/script/sync-claude-commands.sh new file mode 100755 index 00000000..66198709 --- /dev/null +++ b/script/sync-claude-commands.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# ============================================================================ +# Claude Commands Sync Script +# .claude/commands/ をユーザーレベル (~/.claude/commands/) に同期します +# ============================================================================ + +set -euo pipefail + +# カラー出力 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } +log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } + +# スクリプトのディレクトリを取得 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +SOURCE_DIR="${PROJECT_ROOT}/.claude/commands" +TARGET_DIR="${HOME}/.claude/commands" + +log_info "Claude コマンドの同期を開始します..." + +# ソースディレクトリの存在確認 +if [[ ! -d "$SOURCE_DIR" ]]; then + log_warn "ソースディレクトリが見つかりません: ${SOURCE_DIR}" + exit 0 +fi + +# ターゲットディレクトリを作成 +mkdir -p "$TARGET_DIR" + +# コマンドファイルをコピー +log_info "コピー元: ${SOURCE_DIR}" +log_info "コピー先: ${TARGET_DIR}" + +if cp -r "$SOURCE_DIR/"* "$TARGET_DIR/" 2>/dev/null; then + # コピーされたファイル数をカウント + file_count=$(find "$SOURCE_DIR" -type f -name "*.md" | wc -l | xargs) + log_success "Claude コマンド ${file_count} 個を ${TARGET_DIR} に同期しました" + + # 同期されたコマンド一覧を表示 + log_info "同期されたコマンド:" + find "$TARGET_DIR" -type f -name "*.md" -exec basename {} .md \; | sort | sed 's/^/ - \//' +else + log_warn "コマンドのコピーに失敗しました" + exit 1 +fi + +log_success "Claude コマンドの同期が完了しました!"