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.
Files changed (80) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +32 -16
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/default/.claude/hooks/einja/plan-mode-skill-loader.sh +23 -0
  6. package/templates/default/.claude/settings.json +15 -1
  7. package/templates/default/.env.personal.example +6 -2
  8. package/templates/default/.envrc +5 -0
  9. package/templates/default/.github/workflows/deploy-pr-preview.yml +23 -24
  10. package/templates/default/.github/workflows/deploy-stable-branches.yml +55 -49
  11. package/templates/default/.mcp.json +2 -12
  12. package/templates/default/.serena/project.yml +7 -0
  13. package/templates/default/CLAUDE.md +28 -4
  14. package/templates/default/README.md +2 -2
  15. package/templates/default/apps/admin/package.json +1 -1
  16. package/templates/default/apps/admin/tsconfig.json +2 -1
  17. package/templates/default/apps/web/package.json +1 -1
  18. package/templates/default/apps/web/tsconfig.json +2 -1
  19. package/templates/default/docs/plans/.gitkeep +0 -0
  20. package/templates/default/docs/plans/ancient-greeting-flamingo-agent-a87e67c.md +221 -0
  21. package/templates/default/docs/plans/ancient-greeting-flamingo-agent-ab73a1c.md +107 -0
  22. package/templates/default/docs/plans/ancient-greeting-flamingo.md +120 -0
  23. package/templates/default/docs/plans/bright-stargazing-dawn.md +87 -0
  24. package/templates/default/docs/plans/calm-stirring-bonbon.md +196 -0
  25. package/templates/default/docs/plans/calm-watching-widget.md +111 -0
  26. package/templates/default/docs/plans/cheerful-wiggling-ullman.md +164 -0
  27. package/templates/default/docs/plans/compiled-humming-cherny.md +94 -0
  28. package/templates/default/docs/plans/dapper-launching-lynx.md +81 -0
  29. package/templates/default/docs/plans/effervescent-munching-kite-agent-ac08baf.md +672 -0
  30. package/templates/default/docs/plans/effervescent-munching-kite-agent-aecc373.md +442 -0
  31. package/templates/default/docs/plans/effervescent-munching-kite.md +263 -0
  32. package/templates/default/docs/plans/fix-orphan-cleaner-review.md +25 -0
  33. package/templates/default/docs/plans/fix-sync-template-variables.md +162 -0
  34. package/templates/default/docs/plans/glimmering-giggling-sedgewick.md +126 -0
  35. package/templates/default/docs/plans/glittery-swimming-bachman.md +78 -0
  36. package/templates/default/docs/plans/happy-watching-toast.md +56 -0
  37. package/templates/default/docs/plans/harmonic-strolling-nebula.md +210 -0
  38. package/templates/default/docs/plans/import-alias-refactor.md +75 -0
  39. package/templates/default/docs/plans/lazy-percolating-sloth-agent-abda679.md +346 -0
  40. package/templates/default/docs/plans/lazy-percolating-sloth.md +151 -0
  41. package/templates/default/docs/plans/linked-greeting-llama-agent-a7a6e5b.md +345 -0
  42. package/templates/default/docs/plans/linked-greeting-llama.md +467 -0
  43. package/templates/default/docs/plans/lovely-bubbling-rose.md +80 -0
  44. package/templates/default/docs/plans/optimized-watching-sprout.md +149 -0
  45. package/templates/default/docs/plans/peaceful-beaming-toast-agent-a292da6.md +288 -0
  46. package/templates/default/docs/plans/peaceful-beaming-toast-agent-a819699.md +366 -0
  47. package/templates/default/docs/plans/peaceful-beaming-toast-agent-ac11de2.md +474 -0
  48. package/templates/default/docs/plans/peaceful-beaming-toast.md +345 -0
  49. package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6194c.md +300 -0
  50. package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6900e.md +444 -0
  51. package/templates/default/docs/plans/purrfect-spinning-hickey.md +361 -0
  52. package/templates/default/docs/plans/recursive-kindling-lemon-agent-a42199e.md +186 -0
  53. package/templates/default/docs/plans/recursive-kindling-lemon.md +36 -0
  54. package/templates/default/docs/plans/seed-migration-tests.md +47 -0
  55. package/templates/default/docs/plans/sprightly-leaping-manatee.md +224 -0
  56. package/templates/default/docs/plans/stateful-wishing-lerdorf.md +161 -0
  57. package/templates/default/docs/plans/streamed-purring-wreath.md +40 -0
  58. package/templates/default/docs/plans/synthetic-percolating-pearl.md +101 -0
  59. package/templates/default/docs/plans/todo-fix-sync-template-variables.md +21 -0
  60. package/templates/default/docs/plans/todo-phase4-marker-update.md +39 -0
  61. package/templates/default/docs/plans/todo-skill-creator-sync.md +23 -0
  62. package/templates/default/docs/plans/typed-snuggling-parnas-agent-a6f6391.md +476 -0
  63. package/templates/default/docs/plans/typed-snuggling-parnas-agent-adb678b.md +144 -0
  64. package/templates/default/docs/plans/typed-snuggling-parnas.md +84 -0
  65. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a30aa4f.md +534 -0
  66. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a57a278.md +508 -0
  67. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a90b809.md +421 -0
  68. package/templates/default/docs/plans/warm-hopping-lighthouse.md +199 -0
  69. package/templates/default/docs/verification-test.md +2 -0
  70. package/templates/default/gitignore +4 -0
  71. package/templates/default/package.json +2 -2
  72. package/templates/default/packages/admin-ui/package.json +1 -1
  73. package/templates/default/packages/server-core/tsconfig.json +6 -1
  74. package/templates/default/pnpm-lock.yaml +276 -57
  75. package/templates/default/scripts/ensure-serena.sh +75 -0
  76. package/templates/default/scripts/lib/worktree-config.ts +64 -0
  77. package/templates/default/scripts/stop-serena.sh +25 -0
  78. package/templates/default/scripts/worktree/dev.ts +2 -2
  79. /package/templates/default/scripts/{cli-template-update.ts → _cli-template-update.ts} +0 -0
  80. /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での動作確認(推奨)