create-einja-app 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +32 -16
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/.claude/hooks/einja/plan-mode-skill-loader.sh +23 -0
- package/templates/default/.claude/settings.json +15 -1
- package/templates/default/.env.personal.example +6 -2
- package/templates/default/.envrc +5 -0
- package/templates/default/.github/workflows/deploy-pr-preview.yml +23 -24
- package/templates/default/.github/workflows/deploy-stable-branches.yml +55 -49
- package/templates/default/.mcp.json +2 -12
- package/templates/default/.serena/project.yml +7 -0
- package/templates/default/CLAUDE.md +28 -4
- package/templates/default/README.md +2 -2
- package/templates/default/apps/admin/package.json +1 -1
- package/templates/default/apps/admin/tsconfig.json +2 -1
- package/templates/default/apps/web/package.json +1 -1
- package/templates/default/apps/web/tsconfig.json +2 -1
- package/templates/default/docs/plans/.gitkeep +0 -0
- package/templates/default/docs/plans/ancient-greeting-flamingo-agent-a87e67c.md +221 -0
- package/templates/default/docs/plans/ancient-greeting-flamingo-agent-ab73a1c.md +107 -0
- package/templates/default/docs/plans/ancient-greeting-flamingo.md +120 -0
- package/templates/default/docs/plans/bright-stargazing-dawn.md +87 -0
- package/templates/default/docs/plans/calm-stirring-bonbon.md +196 -0
- package/templates/default/docs/plans/calm-watching-widget.md +111 -0
- package/templates/default/docs/plans/cheerful-wiggling-ullman.md +164 -0
- package/templates/default/docs/plans/compiled-humming-cherny.md +94 -0
- package/templates/default/docs/plans/dapper-launching-lynx.md +81 -0
- package/templates/default/docs/plans/effervescent-munching-kite-agent-ac08baf.md +672 -0
- package/templates/default/docs/plans/effervescent-munching-kite-agent-aecc373.md +442 -0
- package/templates/default/docs/plans/effervescent-munching-kite.md +263 -0
- package/templates/default/docs/plans/fix-orphan-cleaner-review.md +25 -0
- package/templates/default/docs/plans/fix-sync-template-variables.md +162 -0
- package/templates/default/docs/plans/glimmering-giggling-sedgewick.md +126 -0
- package/templates/default/docs/plans/glittery-swimming-bachman.md +78 -0
- package/templates/default/docs/plans/happy-watching-toast.md +56 -0
- package/templates/default/docs/plans/harmonic-strolling-nebula.md +210 -0
- package/templates/default/docs/plans/import-alias-refactor.md +75 -0
- package/templates/default/docs/plans/lazy-percolating-sloth-agent-abda679.md +346 -0
- package/templates/default/docs/plans/lazy-percolating-sloth.md +151 -0
- package/templates/default/docs/plans/linked-greeting-llama-agent-a7a6e5b.md +345 -0
- package/templates/default/docs/plans/linked-greeting-llama.md +467 -0
- package/templates/default/docs/plans/lovely-bubbling-rose.md +80 -0
- package/templates/default/docs/plans/optimized-watching-sprout.md +149 -0
- package/templates/default/docs/plans/peaceful-beaming-toast-agent-a292da6.md +288 -0
- package/templates/default/docs/plans/peaceful-beaming-toast-agent-a819699.md +366 -0
- package/templates/default/docs/plans/peaceful-beaming-toast-agent-ac11de2.md +474 -0
- package/templates/default/docs/plans/peaceful-beaming-toast.md +345 -0
- package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6194c.md +300 -0
- package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6900e.md +444 -0
- package/templates/default/docs/plans/purrfect-spinning-hickey.md +361 -0
- package/templates/default/docs/plans/recursive-kindling-lemon-agent-a42199e.md +186 -0
- package/templates/default/docs/plans/recursive-kindling-lemon.md +36 -0
- package/templates/default/docs/plans/seed-migration-tests.md +47 -0
- package/templates/default/docs/plans/sprightly-leaping-manatee.md +224 -0
- package/templates/default/docs/plans/stateful-wishing-lerdorf.md +161 -0
- package/templates/default/docs/plans/streamed-purring-wreath.md +40 -0
- package/templates/default/docs/plans/synthetic-percolating-pearl.md +101 -0
- package/templates/default/docs/plans/todo-fix-sync-template-variables.md +21 -0
- package/templates/default/docs/plans/todo-phase4-marker-update.md +39 -0
- package/templates/default/docs/plans/todo-skill-creator-sync.md +23 -0
- package/templates/default/docs/plans/typed-snuggling-parnas-agent-a6f6391.md +476 -0
- package/templates/default/docs/plans/typed-snuggling-parnas-agent-adb678b.md +144 -0
- package/templates/default/docs/plans/typed-snuggling-parnas.md +84 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a30aa4f.md +534 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a57a278.md +508 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a90b809.md +421 -0
- package/templates/default/docs/plans/warm-hopping-lighthouse.md +199 -0
- package/templates/default/docs/verification-test.md +2 -0
- package/templates/default/gitignore +4 -0
- package/templates/default/package.json +2 -2
- package/templates/default/packages/admin-ui/package.json +1 -1
- package/templates/default/packages/server-core/tsconfig.json +6 -1
- package/templates/default/pnpm-lock.yaml +276 -57
- package/templates/default/scripts/ensure-serena.sh +75 -0
- package/templates/default/scripts/lib/worktree-config.ts +64 -0
- package/templates/default/scripts/stop-serena.sh +25 -0
- package/templates/default/scripts/worktree/dev.ts +2 -2
- /package/templates/default/scripts/{cli-template-update.ts → _cli-template-update.ts} +0 -0
- /package/templates/default/scripts/{template-update.ts → _template-update.ts} +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Codexレビュー指摘修正: orphan-cleaner
|
|
2
|
+
|
|
3
|
+
## 修正対象: High 2件
|
|
4
|
+
|
|
5
|
+
### Fix 1: パストラバーサル脆弱性
|
|
6
|
+
**箇所**: `packages/cli/src/commands/sync.ts` 孤児削除処理部分
|
|
7
|
+
**修正**: 孤児ファイル削除前にパス検証関数を追加
|
|
8
|
+
- `path.normalize` で正規化
|
|
9
|
+
- `..` を含むパスを拒否
|
|
10
|
+
- 絶対パスを拒否
|
|
11
|
+
- 解決後パスがプロジェクトルート配下か検証
|
|
12
|
+
- OrphanCleaner.detectOrphans() 内でも検証(検出段階でフィルタ)
|
|
13
|
+
|
|
14
|
+
### Fix 2: 変更ファイル0件時に孤児削除に到達しない
|
|
15
|
+
**箇所**: `packages/cli/src/commands/sync.ts:161-163`
|
|
16
|
+
**修正**: 早期return条件に `orphanReport.hasOrphans` を追加
|
|
17
|
+
- 孤児検出を早期returnの前に移動
|
|
18
|
+
- `filesToProcess.length === 0 && !orphanReport.hasOrphans` の場合のみreturn
|
|
19
|
+
- 変更なし+孤児ありの場合はレポート表示/削除処理へ進む
|
|
20
|
+
|
|
21
|
+
## 実装ステップ
|
|
22
|
+
1. orphan-cleaner.ts にパス検証を追加(detectOrphans内)
|
|
23
|
+
2. sync.ts の早期return修正 + 削除処理にもパス検証ガード追加
|
|
24
|
+
3. orphan-cleaner.test.ts にパストラバーサルテスト追加
|
|
25
|
+
4. typecheck + test 実行
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# 修正計画: create-einja-app sync コマンドの設計ギャップ修正
|
|
2
|
+
|
|
3
|
+
## 問題一覧
|
|
4
|
+
|
|
5
|
+
### 致命的 (Critical)
|
|
6
|
+
|
|
7
|
+
| # | 問題 | 影響 |
|
|
8
|
+
|---|------|------|
|
|
9
|
+
| C-1 | sync にテンプレート変数置換がない | `@repo/`, `{{packageName}}` が未置換のままコピーされる |
|
|
10
|
+
| C-2 | 環境ファイルの暗号化キー上書き | `.env.keys` 以外の `.env.*` が上書きされ秘密鍵が破壊 |
|
|
11
|
+
| C-3 | Prismaスキーマの無条件上書き | apps/packages sync時にユーザーのDBモデルが消失 |
|
|
12
|
+
|
|
13
|
+
### 重大 (High)
|
|
14
|
+
|
|
15
|
+
| # | 問題 | 影響 |
|
|
16
|
+
|---|------|------|
|
|
17
|
+
| H-1 | package.json の scripts 完全上書き | ユーザーカスタムスクリプトが消失 |
|
|
18
|
+
| H-2 | tsconfig.json の extends/paths 上書き | TypeScriptビルド設定が破壊 |
|
|
19
|
+
| H-3 | dry-run がファイルリスト表示のみ | マージ結果や変数置換結果が確認不可 |
|
|
20
|
+
| H-4 | バックアップが既存ファイルのみ | 新規追加ファイルがロールバックで残る |
|
|
21
|
+
| H-5 | GitHub Actions YAML の無条件同期 | カスタマイズしたCIパイプラインが破壊 |
|
|
22
|
+
|
|
23
|
+
### 中程度 (Medium)
|
|
24
|
+
|
|
25
|
+
| # | 問題 | 影響 |
|
|
26
|
+
|---|------|------|
|
|
27
|
+
| M-1 | dev-cli / create-einja-app の管轄重複 | ユーザーが混乱 |
|
|
28
|
+
| M-2 | マーカーの入れ子エラー処理不足 | マーカー破損時に警告なし |
|
|
29
|
+
| M-3 | 冪等性の未保証 | sync 2回実行で結果が変わる可能性 |
|
|
30
|
+
| M-4 | 個別ファイル保護の仕組みがない | conflictStrategy でしか保護できない |
|
|
31
|
+
| M-5 | `.env.personal` の定義に `.env.keys` があるが実装で片方のみチェック | 保護漏れ |
|
|
32
|
+
|
|
33
|
+
## 修正方針
|
|
34
|
+
|
|
35
|
+
### Phase 1: 致命的問題の修正 (C-1, C-2, C-3)
|
|
36
|
+
|
|
37
|
+
#### 1-1. ユーザーリポジトリから packageScope/projectName を検出
|
|
38
|
+
|
|
39
|
+
sync は既存リポジトリに対して実行されるため、ユーザーの設定を自動検出する必要がある。
|
|
40
|
+
|
|
41
|
+
**取得元の優先順位**:
|
|
42
|
+
1. `.einja.json` (または `.einja-sync.json`) にメタデータとして保存されている場合
|
|
43
|
+
2. ルート `package.json` の `name` フィールド(projectName)
|
|
44
|
+
3. `apps/` or `packages/` 配下の `package.json` から `name` のスコープ部分を抽出
|
|
45
|
+
4. 上記で取得できない場合は対話式プロンプトで入力
|
|
46
|
+
|
|
47
|
+
**新規ファイル**: `src/utils/project-detector.ts`
|
|
48
|
+
|
|
49
|
+
#### 1-2. sync プロセスにテンプレート変数置換を統合
|
|
50
|
+
|
|
51
|
+
**方式**: テンプレートファイル読み込み後、マージ処理の**前**に `replacePlaceholders()` を適用
|
|
52
|
+
|
|
53
|
+
- `template.ts` の `replacePlaceholders()` を export して共用化
|
|
54
|
+
- `merger.ts` の `mergeAndWriteFile()` に templateVariables オプションを追加
|
|
55
|
+
- `sync.ts` で検出した変数情報を `mergeAndWriteFile` に渡す
|
|
56
|
+
|
|
57
|
+
**重要**: 置換はマージ前に行う。マージ後に行うと既存ファイルのユーザーカスタマイズまで誤って置換してしまう。
|
|
58
|
+
|
|
59
|
+
#### 1-3. 置換漏れ検証
|
|
60
|
+
|
|
61
|
+
sync 完了後に `@repo/` や `{{packageName}}` がターゲットファイルに残っていないか検証。
|
|
62
|
+
|
|
63
|
+
**新規ファイル**: `src/utils/placeholder-validator.ts`
|
|
64
|
+
|
|
65
|
+
#### 1-4. 環境ファイルの保護強化
|
|
66
|
+
|
|
67
|
+
`ENV_FILE_PROTECTION` を拡張。dotenvx 暗号化済み値を含むファイルを保護対象に追加。
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const ENV_FILE_PROTECTION = {
|
|
71
|
+
protected: [
|
|
72
|
+
".env.keys", // 既存
|
|
73
|
+
".env.personal", // 既存
|
|
74
|
+
".env.develop", // 追加
|
|
75
|
+
".env.local", // 追加
|
|
76
|
+
".env.production", // 追加
|
|
77
|
+
".env.staging", // 追加
|
|
78
|
+
".env.preview", // 追加
|
|
79
|
+
],
|
|
80
|
+
allowed: [ // 同期許可
|
|
81
|
+
".env.example",
|
|
82
|
+
".env.personal.example",
|
|
83
|
+
".envrc",
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### 1-5. Prismaスキーマ等の保護
|
|
89
|
+
|
|
90
|
+
sync 対象から除外すべきファイルパターンを追加:
|
|
91
|
+
- `**/prisma/schema.prisma` — DBモデルはユーザー固有
|
|
92
|
+
- `**/prisma/migrations/**` — マイグレーション履歴
|
|
93
|
+
- `pnpm-lock.yaml` — lockfile は sync すべきでない
|
|
94
|
+
|
|
95
|
+
### Phase 2: 重大問題の修正 (H-1 ~ H-5)
|
|
96
|
+
|
|
97
|
+
#### 2-1. package.json scripts の安全なマージ (H-1)
|
|
98
|
+
|
|
99
|
+
現状: `{...existing, ...template}` でテンプレートが後勝ち。
|
|
100
|
+
|
|
101
|
+
**方式**: scripts マージ時に、ユーザーが追加したキー(テンプレートに存在しないキー)を保持。テンプレート既存キーの上書き時は既にカスタマイズされている場合は警告。
|
|
102
|
+
|
|
103
|
+
#### 2-2. `--all` 時のデフォルトスコープ制限 (H-1)
|
|
104
|
+
|
|
105
|
+
`--all` 使用時も `packageJsonSections` のデフォルトを `["scripts", "engines"]` に限定。dependency 系は明示フラグ必要。
|
|
106
|
+
|
|
107
|
+
#### 2-3. dry-run の改善 (H-3)
|
|
108
|
+
|
|
109
|
+
dry-run 時にもマージ処理をシミュレーション実行し、実際のdiff(変更前後)を表示する。
|
|
110
|
+
|
|
111
|
+
#### 2-4. バックアップの完全性 (H-4)
|
|
112
|
+
|
|
113
|
+
sync 完了時に「追加されたファイル」のリストも記録し、ロールバック時に削除する。
|
|
114
|
+
|
|
115
|
+
#### 2-5. GitHub Actions のマーカーベース保護 (H-5)
|
|
116
|
+
|
|
117
|
+
`.github/workflows/*.yml` にマーカーベースマージを適用。managed セクション外のユーザーカスタマイズを保持。
|
|
118
|
+
|
|
119
|
+
### Phase 3: テスト
|
|
120
|
+
|
|
121
|
+
| テストファイル | 内容 |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `tests/unit/utils/project-detector.test.ts` | **新規**: packageScope 自動検出 |
|
|
124
|
+
| `tests/unit/utils/placeholder-validator.test.ts` | **新規**: 置換漏れ検出 |
|
|
125
|
+
| `tests/unit/utils/merger.test.ts` | **追加**: templateVariables 付きマージ |
|
|
126
|
+
| `tests/integration/sync-variables.test.ts` | **新規**: sync E2E(変数置換検証) |
|
|
127
|
+
|
|
128
|
+
## 実装順序
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Phase 1-1: project-detector 作成
|
|
132
|
+
↓
|
|
133
|
+
Phase 1-2: replacePlaceholders 共用化 + merger 統合
|
|
134
|
+
↓
|
|
135
|
+
Phase 1-3: placeholder-validator 作成
|
|
136
|
+
↓ (並行可)
|
|
137
|
+
Phase 1-4: env 保護強化 | Phase 1-5: Prisma/lockfile 除外
|
|
138
|
+
↓
|
|
139
|
+
Phase 2 (並行可): scripts安全マージ | dry-run改善 | バックアップ完全性
|
|
140
|
+
↓
|
|
141
|
+
Phase 3: テスト作成
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 変更ファイルサマリ
|
|
145
|
+
|
|
146
|
+
| ファイル | 操作 | 内容 |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| `src/utils/project-detector.ts` | **新規** | packageScope/projectName 検出 |
|
|
149
|
+
| `src/utils/placeholder-validator.ts` | **新規** | 置換漏れ検出 |
|
|
150
|
+
| `src/generators/template.ts` | 変更 | `replacePlaceholders()` を export |
|
|
151
|
+
| `src/utils/merger.ts` | 変更 | templateVariables オプション追加 |
|
|
152
|
+
| `src/commands/sync.ts` | 変更 | 変数検出・置換・検証ステップ追加 |
|
|
153
|
+
| `src/generators/sync.ts` | 変更 | env保護リスト拡張、Prisma除外パターン追加 |
|
|
154
|
+
| `src/types/index.ts` | 変更 | SyncMetadata に packageScope/projectName 追加 |
|
|
155
|
+
|
|
156
|
+
## リスク
|
|
157
|
+
|
|
158
|
+
| リスク | 軽減策 |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `pnpm-lock.yaml` 内の `@repo/` 置換で lockfile 破壊 | sync 対象から除外 |
|
|
161
|
+
| マージ前置換でマージ精度低下 | マーカーベースマージはテキスト比較なので影響なし |
|
|
162
|
+
| 既存ユーザーの設定ファイルに packageScope がない | 自動検出 + 対話確認でフォールバック |
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Plan: Plan mode進入時にeinja-skill-firstを自動リマインドするhook
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
現在、`einja-skill-first` SkillはCLAUDE.mdの指示に基づいて親エージェントが手動で呼び出す運用になっている。しかし強制力がなく、忘れる可能性がある。
|
|
6
|
+
|
|
7
|
+
`UserPromptSubmit` hookを使い、Plan mode中のプロンプト送信時に**軽量リマインダー**を自動注入することで、確実にeinja-skill-first評価が実行される仕組みを作る。
|
|
8
|
+
|
|
9
|
+
**なぜ `UserPromptSubmit` か:**
|
|
10
|
+
- `PreToolUse(EnterPlanMode)` はLLMが呼ぶ場合のみ発火し、**Shift+Tab**での手動Plan mode進入をカバーできない
|
|
11
|
+
- `UserPromptSubmit` は `permission_mode` フィールドを受け取れるため、Plan mode中の全プロンプト送信を確実に検出可能
|
|
12
|
+
|
|
13
|
+
**なぜ軽量リマインダー方式か:**
|
|
14
|
+
- SKILL.md全文(240行)を注入するとコンテキストを圧迫する
|
|
15
|
+
- 短い指示(2-3行)なら毎回注入しても低コスト → **状態管理(フラグファイル等)が不要**
|
|
16
|
+
- LLMが自分でSKILL.mdを読みに行くので、Compaction後も再読み込み可能
|
|
17
|
+
|
|
18
|
+
## 実装内容
|
|
19
|
+
|
|
20
|
+
### 1. hookスクリプト作成
|
|
21
|
+
|
|
22
|
+
**ファイル**: `.claude/hooks/einja/plan-mode-skill-loader.sh`
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
#!/bin/bash
|
|
26
|
+
# plan-mode-skill-loader.sh - Plan mode中にeinja-skill-firstのリマインダーを注入
|
|
27
|
+
#
|
|
28
|
+
# UserPromptSubmit hookとして設定
|
|
29
|
+
# permission_mode == "plan" の場合に、軽量リマインダーをadditionalContextとして注入
|
|
30
|
+
# 毎回注入しても2-3行なのでコスト無視可能。状態管理不要。
|
|
31
|
+
|
|
32
|
+
set -uo pipefail
|
|
33
|
+
|
|
34
|
+
input=$(cat)
|
|
35
|
+
|
|
36
|
+
# permission_modeを取得
|
|
37
|
+
permission_mode=$(echo "$input" | jq -r '.permission_mode // empty')
|
|
38
|
+
|
|
39
|
+
# Plan mode以外はスキップ
|
|
40
|
+
if [[ "$permission_mode" != "plan" ]]; then
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# 軽量リマインダーを注入
|
|
45
|
+
jq -n '{
|
|
46
|
+
"additionalContext": "【Plan mode自動リマインダー】計画作成前にeinja-skill-firstの評価を実施してください。.claude/skills/einja-skill-first/SKILL.mdを参照し、スキップ基準に該当しない場合はSkill作成の必要性を評価してください。スキップ基準(単発の小規模修正、既存キーワードトリガー一致、具体的かつ限定的な作業指示、1回限りの作業)に該当する場合は省略可。"
|
|
47
|
+
}'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. settings.json にhookを追加
|
|
51
|
+
|
|
52
|
+
**ファイル**: `.claude/settings.json`
|
|
53
|
+
|
|
54
|
+
`hooks` セクションに `UserPromptSubmit` を追加:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
"UserPromptSubmit": [
|
|
58
|
+
{
|
|
59
|
+
"hooks": [
|
|
60
|
+
{
|
|
61
|
+
"type": "command",
|
|
62
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/einja/plan-mode-skill-loader.sh",
|
|
63
|
+
"timeout": 5000
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
※ `UserPromptSubmit` はmatcherをサポートしない(全プロンプトで発火)。スクリプト内で `permission_mode` をチェックして制御する。
|
|
71
|
+
|
|
72
|
+
### 3. CLAUDE.md のskill-first関連記述を更新
|
|
73
|
+
|
|
74
|
+
**ファイル**: `CLAUDE.md`
|
|
75
|
+
|
|
76
|
+
現在の必須フロー Step3 の記述を更新:
|
|
77
|
+
|
|
78
|
+
**変更前:**
|
|
79
|
+
```
|
|
80
|
+
3. `einja-skill-first` で「Skill を先に作るべきか」を自動評価する
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**変更後:**
|
|
84
|
+
```
|
|
85
|
+
3. `einja-skill-first` で「Skill を先に作るべきか」を評価する
|
|
86
|
+
- Plan mode中は `UserPromptSubmit` hookにより自動でリマインダーが注入される
|
|
87
|
+
- `.claude/skills/einja-skill-first/SKILL.md` を読み込んで評価を実施する
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
手動キーワードトリガー(「Skill作るべき?」等)はそのまま維持。
|
|
91
|
+
|
|
92
|
+
## 変更対象ファイル
|
|
93
|
+
|
|
94
|
+
| ファイル | 操作 |
|
|
95
|
+
|---------|------|
|
|
96
|
+
| `.claude/hooks/einja/plan-mode-skill-loader.sh` | **新規作成** |
|
|
97
|
+
| `.claude/settings.json` | **編集** - `UserPromptSubmit` hook追加 |
|
|
98
|
+
| `CLAUDE.md` | **編集** - skill-first記述を更新(任意) |
|
|
99
|
+
|
|
100
|
+
## 検証方法
|
|
101
|
+
|
|
102
|
+
1. **スクリプト単体テスト**:
|
|
103
|
+
```bash
|
|
104
|
+
# Plan modeの場合 → additionalContext出力
|
|
105
|
+
echo '{"permission_mode":"plan","session_id":"test123"}' | \
|
|
106
|
+
CLAUDE_PROJECT_DIR="$(pwd)" \
|
|
107
|
+
bash .claude/hooks/einja/plan-mode-skill-loader.sh
|
|
108
|
+
|
|
109
|
+
# 通常modeの場合 → 何も出力しない
|
|
110
|
+
echo '{"permission_mode":"default","session_id":"test123"}' | \
|
|
111
|
+
CLAUDE_PROJECT_DIR="$(pwd)" \
|
|
112
|
+
bash .claude/hooks/einja/plan-mode-skill-loader.sh
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
2. **実動作テスト**:
|
|
116
|
+
- Shift+TabでPlan modeに入りプロンプトを送信
|
|
117
|
+
- リマインダーが注入され、skill-first評価が促されることを確認
|
|
118
|
+
|
|
119
|
+
3. **通常mode非発火テスト**:
|
|
120
|
+
- Plan mode以外でプロンプト送信した際に何も注入されないことを確認
|
|
121
|
+
|
|
122
|
+
## 注意事項
|
|
123
|
+
|
|
124
|
+
- `.claude/` 配下の変更は `presets/default/` にビルド時に自動コピーされる
|
|
125
|
+
- `additionalContext` はCompaction対象だが、毎回再注入されるため消失しても問題ない
|
|
126
|
+
- hookのタイムアウトは5秒(jqの実行のみなので十分余裕あり)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# packages層の相対パスインポート禁止を強制する
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
import-conventions.md で「`../` や `./` を使用しない」と規定されているが、packages層では強制手段がなく、29ファイルで `../../` 形式の相対パスが使われている。TSConfig paths エイリアス(`@/`)を追加し、既存の相対パスインポートを書き換える。
|
|
6
|
+
|
|
7
|
+
## 対象ファイル数
|
|
8
|
+
|
|
9
|
+
| パッケージ | 違反ファイル数 | 備考 |
|
|
10
|
+
|-----------|-------------|------|
|
|
11
|
+
| packages/server-core | 8件 | vitest.config.ts に `@/` alias 定義済み |
|
|
12
|
+
| packages/cli | 13件 | vitest alias 追加必要 |
|
|
13
|
+
| packages/create-einja-app | 8件 | テストファイル中心 |
|
|
14
|
+
|
|
15
|
+
## 実施内容
|
|
16
|
+
|
|
17
|
+
### Step 1: TSConfig paths 追加(3ファイル)
|
|
18
|
+
|
|
19
|
+
各パッケージの `tsconfig.json` に `paths` を追加。IDEの補完・型チェックで `@/` が使えるようになる。
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
"compilerOptions": {
|
|
23
|
+
"paths": {
|
|
24
|
+
"@/*": ["./src/*"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
対象:
|
|
30
|
+
- `packages/server-core/tsconfig.json`
|
|
31
|
+
- `packages/cli/tsconfig.json`
|
|
32
|
+
- `packages/create-einja-app/tsconfig.json`(テスト用に `tests/` へのalias も検討)
|
|
33
|
+
|
|
34
|
+
### Step 2: vitest.config.ts の alias 追加(2ファイル)
|
|
35
|
+
|
|
36
|
+
server-core は既に定義済み。cli と create-einja-app に追加。
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
resolve: {
|
|
40
|
+
alias: {
|
|
41
|
+
"@": path.resolve(__dirname, "./src"),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
対象:
|
|
47
|
+
- `packages/cli/vitest.config.ts`(存在確認要)
|
|
48
|
+
- `packages/create-einja-app/vitest.config.ts`(存在確認要)
|
|
49
|
+
|
|
50
|
+
### Step 3: 相対パスインポートを `@/` に書き換え(29ファイル)
|
|
51
|
+
|
|
52
|
+
例:
|
|
53
|
+
```typescript
|
|
54
|
+
// Before
|
|
55
|
+
import type { User } from "../../../domain/entities/User";
|
|
56
|
+
import { type Result, failure, success } from "../../../core/result";
|
|
57
|
+
|
|
58
|
+
// After
|
|
59
|
+
import type { User } from "@/domain/entities/User";
|
|
60
|
+
import { type Result, failure, success } from "@/core/result";
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Step 4: ドキュメント更新(1ファイル)
|
|
64
|
+
|
|
65
|
+
`.claude/skills/einja-coding-standards/references/import-conventions.md` の禁止事項セクションを更新:
|
|
66
|
+
- packages層でも `@/` エイリアスを使用する旨を明記
|
|
67
|
+
- 各パッケージの `@/` が何を指すか記載
|
|
68
|
+
|
|
69
|
+
### Step 5: 検証
|
|
70
|
+
|
|
71
|
+
- `pnpm typecheck` — 型チェック通過
|
|
72
|
+
- `pnpm test` — テスト通過(server-core, cli, create-einja-app)
|
|
73
|
+
- `pnpm build` — ビルド通過
|
|
74
|
+
- `grep` で `../../` が残っていないことを確認
|
|
75
|
+
|
|
76
|
+
## Lint強制について
|
|
77
|
+
|
|
78
|
+
Biomeの `noRestrictedImports` は完全一致のみでパターンマッチ不可。自動検出は不可のため、コードレビュー + ドキュメントで担保する。
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# CLAUDE.md ベストプラクティス統合
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
外部CLAUDE.mdベストプラクティスとの差分分析を行い、Codex分析・Planレビューの結果を踏まえて、取り入れるべき4つの改善を特定した。
|
|
6
|
+
|
|
7
|
+
## 対象ファイル
|
|
8
|
+
|
|
9
|
+
- `CLAUDE.md`
|
|
10
|
+
|
|
11
|
+
## 変更内容
|
|
12
|
+
|
|
13
|
+
### 変更1: 基本原則に「設計の見直し」追加(L11の後)
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
5. **設計の見直し**: 複雑な変更では実装前に「より良い設計はないか」を検討する。ただし単純な修正には不要
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 変更2: ブロッカー対応テーブルに汎用ルール追加(L68の後)
|
|
20
|
+
|
|
21
|
+
```markdown
|
|
22
|
+
| 想定外の事態全般 | **即座に停止**。無理に推し進めず再計画 |
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 変更3: 完了判定の必須チェックに差分確認追加(L172の後)
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
- [ ] `git diff` で意図しない変更が混入していないことを確認(`git diff --stat` で変更ファイル一覧を確認)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 変更4: 報告ルールセクションに進捗報告追加(L142-149)
|
|
32
|
+
|
|
33
|
+
セクション名を変更し、進捗報告ルールを追加:
|
|
34
|
+
|
|
35
|
+
**Before:**
|
|
36
|
+
```markdown
|
|
37
|
+
## サブエージェント結果報告のルール
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**After:**
|
|
41
|
+
```markdown
|
|
42
|
+
## 報告ルール
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
L149の後に追加:
|
|
46
|
+
```markdown
|
|
47
|
+
### 進捗報告の原則
|
|
48
|
+
- 複数ステップのタスクでは、各ステップ完了時にユーザーへ進捗を報告する
|
|
49
|
+
- 完了した作業と次のステップを簡潔に示す
|
|
50
|
+
- 問題が発生した場合は即座に共有する
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 検証方法
|
|
54
|
+
|
|
55
|
+
- CLAUDE.mdの各変更箇所をReadで確認
|
|
56
|
+
- 既存ルールとの矛盾がないことを目視確認
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Vercel環境変数競合解消 + デプロイジョブ動的マトリクス化
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
参照コミット:
|
|
6
|
+
1. https://github.com/drlovekoushiki/drlove_demo_app/commit/565dd31 (env競合解消)
|
|
7
|
+
2. https://github.com/drlovekoushiki/drlove_demo_app/commit/1ad3045 (変更検知フィルタ追加)
|
|
8
|
+
3. https://github.com/drlovekoushiki/drlove_demo_app/commit/2cf8bbf (動的マトリクス化)
|
|
9
|
+
4. https://github.com/drlovekoushiki/drlove_demo_app/commit/d12f7c4 (エッジケース対応)
|
|
10
|
+
|
|
11
|
+
**問題1**: PR環境で`vercel env add`が重複追加・並行PR競合を起こす
|
|
12
|
+
**問題2**: 変更がないアプリもデプロイジョブが走り、各ステップで`should_deploy`条件分岐が冗長
|
|
13
|
+
**問題3**: grep正規表現が数字含む変数名に非対応、空文字値の未注入、changesジョブ失敗時ガード不足
|
|
14
|
+
|
|
15
|
+
**解決方針**:
|
|
16
|
+
- PR環境: `vercel env add`を廃止し、`--env`フラグで実行時注入
|
|
17
|
+
- stable branches: mainブランチのみ`vercel env add`、他は`--env`注入
|
|
18
|
+
- 変更検知フィルタに`.github/workflows/**`を追加
|
|
19
|
+
- 動的マトリクスで変更のあるアプリのみジョブを生成
|
|
20
|
+
- エッジケース修正を最初から反映
|
|
21
|
+
|
|
22
|
+
## 変更対象ファイル
|
|
23
|
+
|
|
24
|
+
| ファイル | 変更内容 |
|
|
25
|
+
|---------|---------|
|
|
26
|
+
| `.github/workflows/deploy-pr-preview.yml` | env sync削除、deploy時`--env`注入 |
|
|
27
|
+
| `.github/workflows/deploy-stable-branches.yml` | 動的マトリクス化、env syncをmain限定、deploy時`--env`注入、フィルタ追加 |
|
|
28
|
+
| `docs/einja/steering/infrastructure/deployment.md` | フロー図・説明を新方式に更新 |
|
|
29
|
+
| `docs/einja/instructions/vercel-cli-reference.md` | 環境変数同期セクションを新方式に更新 |
|
|
30
|
+
| `.claude/skills/einja-infra-maintenance/SKILL.md` | env sync説明をmain限定+`--env`注入に更新 |
|
|
31
|
+
|
|
32
|
+
## 詳細変更計画
|
|
33
|
+
|
|
34
|
+
### 1. deploy-pr-preview.yml
|
|
35
|
+
|
|
36
|
+
#### 1a. 「Sync environment variables to Vercel」ステップを削除(L264-282)
|
|
37
|
+
|
|
38
|
+
現在のL264-282にある`vercel env rm`/`vercel env add`ループを完全削除。
|
|
39
|
+
|
|
40
|
+
#### 1b. 「Deploy to Vercel」ステップを変更(L294-300)
|
|
41
|
+
|
|
42
|
+
**変更後:**
|
|
43
|
+
```yaml
|
|
44
|
+
- name: Deploy to Vercel
|
|
45
|
+
id: deploy
|
|
46
|
+
run: |
|
|
47
|
+
npx dotenvx run -f $GITHUB_WORKSPACE/.env.preview -- bash -c '
|
|
48
|
+
declare -a ENV_FLAGS=()
|
|
49
|
+
while IFS= read -r key; do
|
|
50
|
+
case "$key" in
|
|
51
|
+
NEON_*|DOTENV_PUBLIC_KEY_*) continue ;;
|
|
52
|
+
esac
|
|
53
|
+
value="${!key}"
|
|
54
|
+
if [ -n "$value" ]; then
|
|
55
|
+
echo "::add-mask::${value}"
|
|
56
|
+
fi
|
|
57
|
+
ENV_FLAGS+=("--env" "${key}=${value}")
|
|
58
|
+
done < <(grep -E "^[A-Z_][A-Z0-9_]*=\"?encrypted:" "$GITHUB_WORKSPACE/.env.preview" | cut -d= -f1)
|
|
59
|
+
|
|
60
|
+
# DATABASE_URLはNeon APIから取得した値を注入(.env.previewには含まれない)
|
|
61
|
+
ENV_FLAGS+=("--env" "DATABASE_URL=${DATABASE_URL}")
|
|
62
|
+
|
|
63
|
+
echo "Deploying with ${#ENV_FLAGS[@]} runtime env vars..."
|
|
64
|
+
DEPLOY_URL=$(vercel deploy --prebuilt "${ENV_FLAGS[@]}" --token="$VERCEL_TOKEN")
|
|
65
|
+
echo "url=$DEPLOY_URL" >> "$GITHUB_OUTPUT"
|
|
66
|
+
'
|
|
67
|
+
env:
|
|
68
|
+
DOTENV_PRIVATE_KEY_PREVIEW: ${{ secrets.DOTENV_PRIVATE_KEY_PREVIEW }}
|
|
69
|
+
DATABASE_URL: ${{ steps.db-urls.outputs.db_url_pooled }}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**反映済みエッジケース修正(コミット#4):**
|
|
73
|
+
- grep正規表現: `[A-Z_][A-Z0-9_]*`(数字含む変数名対応)
|
|
74
|
+
- `ENV_FLAGS+=`を`if`の外に配置(空文字値も`--env`注入)
|
|
75
|
+
|
|
76
|
+
### 2. deploy-stable-branches.yml
|
|
77
|
+
|
|
78
|
+
#### 2a. changesジョブ: フィルタ追加 + 動的マトリクス構築
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
outputs:
|
|
82
|
+
web: ${{ steps.filter.outputs.web }}
|
|
83
|
+
admin: ${{ steps.filter.outputs.admin }}
|
|
84
|
+
deploy_matrix: ${{ steps.matrix.outputs.matrix }} # 追加
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
web/admin両方のフィルタに`.github/workflows/**`追加。新規ステップ追加:
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
- name: Build deploy matrix
|
|
91
|
+
id: matrix
|
|
92
|
+
run: |
|
|
93
|
+
MATRIX='[]'
|
|
94
|
+
if [ "${{ steps.filter.outputs.web }}" = "true" ]; then
|
|
95
|
+
MATRIX=$(echo "$MATRIX" | jq -c '. + [{"app": "web"}]')
|
|
96
|
+
fi
|
|
97
|
+
if [ "${{ steps.filter.outputs.admin }}" = "true" ]; then
|
|
98
|
+
MATRIX=$(echo "$MATRIX" | jq -c '. + [{"app": "admin"}]')
|
|
99
|
+
fi
|
|
100
|
+
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### 2b. deployジョブ: 動的マトリクスに変更 + should_deploy全削除
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
deploy:
|
|
107
|
+
needs: [ci, migrate, changes]
|
|
108
|
+
if: always() && needs.ci.result == 'success' && (needs.migrate.result == 'success' || needs.migrate.result == 'skipped') && needs.changes.result == 'success' && needs.changes.outputs.deploy_matrix != '[]'
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
strategy:
|
|
111
|
+
fail-fast: false
|
|
112
|
+
matrix:
|
|
113
|
+
include: ${{ fromJSON(needs.changes.outputs.deploy_matrix) }}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- 「Check if deploy is needed」ステップを完全削除
|
|
117
|
+
- 全ステップから`if: steps.check.outputs.should_deploy == 'true'`を削除
|
|
118
|
+
- `Extract alias domain`と`Set Vercel alias`は`if: env.DEPLOY_RUN_ALIAS == 'true'`のみに
|
|
119
|
+
|
|
120
|
+
#### 2c. env sync関連をmainブランチ限定に + grep修正
|
|
121
|
+
|
|
122
|
+
if条件を変更し、grep正規表現も数字含む変数名に対応:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
- name: Sync environment variables to Vercel
|
|
126
|
+
if: github.ref_name == 'main'
|
|
127
|
+
run: |
|
|
128
|
+
npx dotenvx run -f ${{ env.DEPLOY_DOTENV_FILE }} -- bash -c '
|
|
129
|
+
grep -E "^[A-Z_][A-Z0-9_]*=\"?encrypted:" ${{ env.DEPLOY_DOTENV_FILE }} | cut -d= -f1 | while read -r key; do
|
|
130
|
+
case "$key" in
|
|
131
|
+
NEON_*) continue ;;
|
|
132
|
+
esac
|
|
133
|
+
value="${!key}"
|
|
134
|
+
if [ -n "$value" ]; then
|
|
135
|
+
echo "Syncing $key to Vercel (${{ env.DEPLOY_VERCEL_ENV }})..."
|
|
136
|
+
echo "$value" | vercel env rm "$key" ${{ env.DEPLOY_VERCEL_ENV }} --yes --token=$VERCEL_TOKEN 2>/dev/null || true
|
|
137
|
+
echo "$value" | vercel env add "$key" ${{ env.DEPLOY_VERCEL_ENV }} --token=$VERCEL_TOKEN
|
|
138
|
+
fi
|
|
139
|
+
done
|
|
140
|
+
'
|
|
141
|
+
env:
|
|
142
|
+
DOTENV_PRIVATE_KEY_PRODUCTION: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
|
|
143
|
+
DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
|
|
144
|
+
DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
- name: Re-pull Vercel Environment after sync
|
|
149
|
+
if: github.ref_name == 'main'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### 2d. 「Deploy to Vercel」ステップを`--env`注入に変更
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
- name: Deploy to Vercel
|
|
156
|
+
id: deploy
|
|
157
|
+
run: |
|
|
158
|
+
npx dotenvx run -f $DEPLOY_DOTENV_FILE -- bash -c '
|
|
159
|
+
declare -a ENV_FLAGS=()
|
|
160
|
+
while IFS= read -r key; do
|
|
161
|
+
case "$key" in
|
|
162
|
+
NEON_*|VERCEL_ALIAS_DOMAIN_*|DOTENV_PUBLIC_KEY_*) continue ;;
|
|
163
|
+
esac
|
|
164
|
+
value="${!key}"
|
|
165
|
+
if [ -n "$value" ]; then
|
|
166
|
+
echo "::add-mask::${value}"
|
|
167
|
+
fi
|
|
168
|
+
ENV_FLAGS+=("--env" "${key}=${value}")
|
|
169
|
+
done < <(grep -E "^[A-Z_][A-Z0-9_]*=\"?encrypted:" "$DEPLOY_DOTENV_FILE" | cut -d= -f1)
|
|
170
|
+
|
|
171
|
+
echo "Deploying with ${#ENV_FLAGS[@]} runtime env vars..."
|
|
172
|
+
DEPLOY_URL=$(vercel deploy --prebuilt $DEPLOY_PROD_FLAG "${ENV_FLAGS[@]}" --token="$VERCEL_TOKEN")
|
|
173
|
+
echo "url=$DEPLOY_URL" >> "$GITHUB_OUTPUT"
|
|
174
|
+
'
|
|
175
|
+
env:
|
|
176
|
+
DOTENV_PRIVATE_KEY_PRODUCTION: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
|
|
177
|
+
DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
|
|
178
|
+
DOTENV_PRIVATE_KEY_DEVELOP: ${{ secrets.DOTENV_PRIVATE_KEY_DEVELOP }}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**注意**: このプロジェクトでは`DEPLOY_VERCEL_TARGET_FLAG`ではなく`DEPLOY_PROD_FLAG`を使用。
|
|
182
|
+
|
|
183
|
+
### 3. docs/einja/steering/infrastructure/deployment.md
|
|
184
|
+
|
|
185
|
+
更新箇所:
|
|
186
|
+
- PRプレビューフローのmermaid図: `環境変数同期` → `vercel deploy --env(実行時注入)`
|
|
187
|
+
- Stableブランチフローのmermaid図: mainのみ環境変数同期、他は`--env`注入
|
|
188
|
+
- フローチャート: 動的マトリクスで変更アプリのみデプロイ
|
|
189
|
+
- 同時PR運用時の注意: 全encrypted環境変数`--env`注入の説明に更新
|
|
190
|
+
- Vercel環境変数の自動同期: mainブランチ限定を明記
|
|
191
|
+
- ワークフロー一覧テーブル: 動的マトリクス、`--env`注入の説明追加
|
|
192
|
+
|
|
193
|
+
### 4. docs/einja/instructions/vercel-cli-reference.md
|
|
194
|
+
|
|
195
|
+
更新箇所:
|
|
196
|
+
- 環境変数同期自動化セクション: mainのみ`vercel env add`、他は`--env`注入
|
|
197
|
+
- CI/CD非対話パターン表: `vercel deploy --env`の使い方追記
|
|
198
|
+
|
|
199
|
+
### 5. .claude/skills/einja-infra-maintenance/SKILL.md
|
|
200
|
+
|
|
201
|
+
更新箇所:
|
|
202
|
+
- 環境変数同期の説明: mainのみ`vercel env add`、他は`--env`注入
|
|
203
|
+
- ワークフロー一覧テーブル: 動的マトリクス、`--env`注入の説明追加
|
|
204
|
+
- 競合回避ルール: env syncがmain限定であることを明記
|
|
205
|
+
|
|
206
|
+
## 検証方法
|
|
207
|
+
|
|
208
|
+
1. YAML構文チェック(両ファイル)
|
|
209
|
+
2. `git diff --stat`で変更が5ファイルのみであることを確認
|
|
210
|
+
3. PR作成後にActionsでの動作確認(推奨)
|