create-einja-app 0.3.1 → 0.3.3

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 (120) hide show
  1. package/README.md +34 -1
  2. package/dist/cli.js +92 -80
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/default/.changeset/config.json +11 -0
  6. package/templates/default/.claude/hooks/einja/plan-mode-skill-loader.sh +27 -0
  7. package/templates/default/.claude/settings.json +29 -1
  8. package/templates/default/.claude/skills/cli-package-specs/SKILL.md +247 -0
  9. package/templates/default/.einja-sync.json +1 -1
  10. package/templates/default/.env.personal.example +6 -2
  11. package/templates/default/.envrc +5 -0
  12. package/templates/default/.github/release.yml +10 -0
  13. package/templates/default/.github/workflows/changeset-status.yml +60 -0
  14. package/templates/default/.github/workflows/deploy-pr-preview.yml +23 -24
  15. package/templates/default/.github/workflows/deploy-stable-branches.yml +336 -100
  16. package/templates/default/.mcp.json +2 -12
  17. package/templates/default/.serena/project.yml +7 -0
  18. package/templates/default/CLAUDE.md +61 -10
  19. package/templates/default/README.md +22 -10
  20. package/templates/default/apps/admin/package.json +1 -1
  21. package/templates/default/apps/admin/tsconfig.json +2 -1
  22. package/templates/default/apps/web/package.json +1 -1
  23. package/templates/default/apps/web/tsconfig.json +2 -1
  24. package/templates/default/docs/plans/.gitkeep +0 -0
  25. package/templates/default/docs/plans/agile-munching-knuth.md +161 -0
  26. package/templates/default/docs/plans/agile-riding-nova.md +158 -0
  27. package/templates/default/docs/plans/agile-wibbling-dusk.md +91 -0
  28. package/templates/default/docs/plans/ancient-greeting-flamingo-agent-a87e67c.md +221 -0
  29. package/templates/default/docs/plans/ancient-greeting-flamingo-agent-ab73a1c.md +107 -0
  30. package/templates/default/docs/plans/ancient-greeting-flamingo.md +120 -0
  31. package/templates/default/docs/plans/ancient-watching-otter.md +152 -0
  32. package/templates/default/docs/plans/bright-sauteeing-bumblebee.md +30 -0
  33. package/templates/default/docs/plans/bright-stargazing-dawn.md +87 -0
  34. package/templates/default/docs/plans/calm-stirring-bonbon.md +196 -0
  35. package/templates/default/docs/plans/calm-watching-widget.md +111 -0
  36. package/templates/default/docs/plans/cheerful-wiggling-ullman.md +164 -0
  37. package/templates/default/docs/plans/compiled-humming-cherny.md +94 -0
  38. package/templates/default/docs/plans/composed-doodling-mountain.md +362 -0
  39. package/templates/default/docs/plans/dapper-launching-lynx.md +81 -0
  40. package/templates/default/docs/plans/dazzling-foraging-cascade.md +32 -0
  41. package/templates/default/docs/plans/effervescent-munching-kite-agent-ac08baf.md +672 -0
  42. package/templates/default/docs/plans/effervescent-munching-kite-agent-aecc373.md +442 -0
  43. package/templates/default/docs/plans/effervescent-munching-kite.md +263 -0
  44. package/templates/default/docs/plans/enchanted-wiggling-ember-agent-a5befd57d0ca4c7c7.md +177 -0
  45. package/templates/default/docs/plans/enchanted-wiggling-ember.md +170 -0
  46. package/templates/default/docs/plans/federated-questing-kahan.md +47 -0
  47. package/templates/default/docs/plans/fix-orphan-cleaner-review.md +25 -0
  48. package/templates/default/docs/plans/fix-sync-template-variables.md +162 -0
  49. package/templates/default/docs/plans/flickering-pondering-hearth.md +26 -0
  50. package/templates/default/docs/plans/fluttering-snuggling-sprout.md +172 -0
  51. package/templates/default/docs/plans/generic-sleeping-snowglobe-agent-a41d8da.md +179 -0
  52. package/templates/default/docs/plans/generic-sleeping-snowglobe.md +108 -0
  53. package/templates/default/docs/plans/generic-snuggling-pudding.md +57 -0
  54. package/templates/default/docs/plans/glimmering-giggling-sedgewick.md +126 -0
  55. package/templates/default/docs/plans/glittery-swimming-bachman.md +78 -0
  56. package/templates/default/docs/plans/happy-watching-toast.md +56 -0
  57. package/templates/default/docs/plans/harmonic-strolling-nebula.md +210 -0
  58. package/templates/default/docs/plans/idempotent-wiggling-cherny.md +122 -0
  59. package/templates/default/docs/plans/import-alias-refactor.md +75 -0
  60. package/templates/default/docs/plans/lazy-percolating-sloth-agent-abda679.md +346 -0
  61. package/templates/default/docs/plans/lazy-percolating-sloth.md +151 -0
  62. package/templates/default/docs/plans/linked-greeting-llama-agent-a7a6e5b.md +345 -0
  63. package/templates/default/docs/plans/linked-greeting-llama.md +467 -0
  64. package/templates/default/docs/plans/lovely-bubbling-rose.md +80 -0
  65. package/templates/default/docs/plans/optimized-watching-sprout.md +149 -0
  66. package/templates/default/docs/plans/peaceful-beaming-toast-agent-a292da6.md +288 -0
  67. package/templates/default/docs/plans/peaceful-beaming-toast-agent-a819699.md +366 -0
  68. package/templates/default/docs/plans/peaceful-beaming-toast-agent-ac11de2.md +474 -0
  69. package/templates/default/docs/plans/peaceful-beaming-toast.md +345 -0
  70. package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6194c.md +300 -0
  71. package/templates/default/docs/plans/purrfect-spinning-hickey-agent-ae6900e.md +444 -0
  72. package/templates/default/docs/plans/purrfect-spinning-hickey.md +361 -0
  73. package/templates/default/docs/plans/recursive-fluttering-mitten.md +176 -0
  74. package/templates/default/docs/plans/recursive-kindling-lemon-agent-a42199e.md +186 -0
  75. package/templates/default/docs/plans/recursive-kindling-lemon.md +36 -0
  76. package/templates/default/docs/plans/seed-migration-tests.md +47 -0
  77. package/templates/default/docs/plans/sprightly-leaping-manatee.md +224 -0
  78. package/templates/default/docs/plans/stateful-wishing-lerdorf.md +161 -0
  79. package/templates/default/docs/plans/streamed-purring-wreath.md +40 -0
  80. package/templates/default/docs/plans/synthetic-percolating-pearl.md +101 -0
  81. package/templates/default/docs/plans/todo-create-einja-app-ux-fix.md +16 -0
  82. package/templates/default/docs/plans/todo-direnv-hang-fix.md +12 -0
  83. package/templates/default/docs/plans/todo-fix-sync-template-variables.md +21 -0
  84. package/templates/default/docs/plans/todo-github-actions-release-workflow.md +34 -0
  85. package/templates/default/docs/plans/todo-issue-spec-rename.md +24 -0
  86. package/templates/default/docs/plans/todo-phase4-marker-update.md +39 -0
  87. package/templates/default/docs/plans/todo-skill-creator-sync.md +23 -0
  88. package/templates/default/docs/plans/todo-skill-creator-upgrade.md +18 -0
  89. package/templates/default/docs/plans/typed-snuggling-parnas-agent-a6f6391.md +476 -0
  90. package/templates/default/docs/plans/typed-snuggling-parnas-agent-adb678b.md +144 -0
  91. package/templates/default/docs/plans/typed-snuggling-parnas.md +84 -0
  92. package/templates/default/docs/plans/velvety-chasing-spark.md +28 -0
  93. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a30aa4f.md +534 -0
  94. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a57a278.md +508 -0
  95. package/templates/default/docs/plans/warm-hopping-lighthouse-agent-a90b809.md +421 -0
  96. package/templates/default/docs/plans/warm-hopping-lighthouse.md +199 -0
  97. package/templates/default/docs/plans/wondrous-strolling-crystal-agent-a0615fc.md +215 -0
  98. package/templates/default/docs/plans/wondrous-strolling-crystal.md +182 -0
  99. package/templates/default/docs/plans/zesty-roaming-steele.md +74 -0
  100. package/templates/default/docs/verification-test.md +2 -0
  101. package/templates/default/gitignore +9 -1
  102. package/templates/default/package.json +6 -2
  103. package/templates/default/packages/admin-ui/package.json +1 -1
  104. package/templates/default/packages/server-core/tsconfig.json +6 -1
  105. package/templates/default/pnpm-lock.yaml +823 -57
  106. package/templates/default/scripts/ensure-serena.sh +75 -0
  107. package/templates/default/scripts/env-rotate-secrets.ts +66 -6
  108. package/templates/default/scripts/init-github.ts +363 -0
  109. package/templates/default/scripts/init.sh +11 -5
  110. package/templates/default/scripts/lib/worktree-config.ts +64 -0
  111. package/templates/default/scripts/setup-dev.ts +16 -1
  112. package/templates/default/scripts/stop-serena.sh +25 -0
  113. package/templates/default/scripts/worktree/dev.ts +2 -2
  114. package/templates/default/.claude/skills/create-einja-app-release/SKILL.md +0 -186
  115. package/templates/default/.claude/skills/dev-cli-release/SKILL.md +0 -173
  116. package/templates/default/.cursor/commands/spec-create.md +0 -227
  117. package/templates/default/.cursor/commands/task-exec.md +0 -287
  118. package/templates/default/.cursor/commands/update-docs-by-task-specs.md +0 -448
  119. /package/templates/default/scripts/{cli-template-update.ts → _cli-template-update.ts} +0 -0
  120. /package/templates/default/scripts/{template-update.ts → _template-update.ts} +0 -0
@@ -0,0 +1,164 @@
1
+ # einja sync: 孤児ファイル検出・削除機能
2
+
3
+ ## Context
4
+
5
+ `einja sync` はテンプレートからプロジェクトへファイルを同期するが、テンプレート側でファイルが削除されても利用者側のファイルはそのまま残る。`.einja-sync.json` に過去の同期記録があるため、「メタデータにあるがテンプレートにないファイル」=孤児として検出可能。
6
+
7
+ ## 方針
8
+
9
+ - `sync` 実行時に常に孤児を検出・警告表示
10
+ - `--clean` フラグで実際に削除(確認プロンプト + バックアップ付き)
11
+ - 既存のUXパターン(dry-run, JSON出力, --yes, --no-backup)に完全準拠
12
+
13
+ ## 変更対象ファイル
14
+
15
+ | ファイル | 変更内容 |
16
+ |---------|---------|
17
+ | `packages/cli/src/types/index.ts` | `SyncOptions` に `clean?: boolean` 追加 |
18
+ | `packages/cli/src/types/sync.ts` | `OrphanFile`, `OrphanReport` 型追加、`JsonOutput.summary` に `orphansDetected?`, `orphansDeleted?` 追加、`JsonOutput.orphans?` 追加 |
19
+ | `packages/cli/src/lib/sync/orphan-cleaner.ts` | **新規**: 孤児検出・レポート生成クラス |
20
+ | `packages/cli/src/lib/sync/metadata-manager.ts` | `removeFiles()` メソッド追加 |
21
+ | `packages/cli/src/commands/sync.ts` | 孤児検出→警告表示→`--clean`時の削除処理を統合 |
22
+ | `packages/cli/src/cli.ts` | `--clean` オプション追加 |
23
+ | `packages/cli/src/lib/sync/orphan-cleaner.test.ts` | **新規**: OrphanCleanerのユニットテスト |
24
+ | `packages/cli/src/lib/sync/metadata-manager.test.ts` | `removeFiles` テスト追加 |
25
+
26
+ ## 実装ステップ
27
+
28
+ ### Step 1: 型定義追加
29
+
30
+ **`types/index.ts`** — `SyncOptions` に追加:
31
+ ```typescript
32
+ clean?: boolean;
33
+ ```
34
+
35
+ **`types/sync.ts`** — 型追加:
36
+ ```typescript
37
+ export interface OrphanFile {
38
+ path: string;
39
+ category: string | null;
40
+ exists: boolean; // ディスク上に実在するか
41
+ }
42
+
43
+ export interface OrphanReport {
44
+ hasOrphans: boolean;
45
+ orphans: OrphanFile[];
46
+ total: number;
47
+ existingCount: number; // ディスク上に実在する数
48
+ }
49
+ ```
50
+
51
+ `JsonOutput.summary` に `orphansDetected?`, `orphansDeleted?` を追加。`JsonOutput` に `orphans?: OrphanFile[]` を追加。
52
+
53
+ ### Step 2: OrphanCleaner クラス(新規)
54
+
55
+ **`lib/sync/orphan-cleaner.ts`** — 既存の `ConflictReporter` と同じ設計パターン:
56
+
57
+ ```typescript
58
+ export class OrphanCleaner {
59
+ constructor(private projectRoot: string, private fileFilter: FileFilter) {}
60
+
61
+ async detectOrphans(
62
+ metadata: SyncMetadata,
63
+ currentTemplateFiles: string[],
64
+ categories?: string[]
65
+ ): Promise<OrphanFile[]>
66
+ // metadata.files のキー vs currentTemplateFiles を比較
67
+ // カテゴリフィルタ: fileFilter.getCategoryFromPath() で判定
68
+ // fs.pathExists でディスク実在チェック
69
+
70
+ createReport(orphans: OrphanFile[]): OrphanReport
71
+
72
+ formatReport(report: OrphanReport): string
73
+ // 例: " 🗑️ .claude/skills/einja-old/SKILL.md (存在)"
74
+
75
+ formatHelpMessage(): string
76
+ // "💡 削除するには --clean オプションを使用してください"
77
+ }
78
+ ```
79
+
80
+ ### Step 3: MetadataManager 拡張
81
+
82
+ **`lib/sync/metadata-manager.ts`** に追加:
83
+ ```typescript
84
+ removeFiles(metadata: SyncMetadata, filePaths: string[]): SyncMetadata
85
+ // metadata.files から指定パスのエントリを削除して返す
86
+ ```
87
+
88
+ ### Step 4: CLI オプション追加
89
+
90
+ **`cli.ts`** の sync コマンドに追加:
91
+ ```typescript
92
+ .option("--clean", "テンプレートから削除されたファイル(孤児)を削除")
93
+ ```
94
+
95
+ ### Step 5: sync.ts への統合
96
+
97
+ 既存フローへの挿入位置:
98
+
99
+ ```
100
+ 1. メタデータ読み込み
101
+ 2. 同期対象スキャン (targets)
102
+ 3. 差分計算
103
+ 4. ★ 孤児検出(常に実行)
104
+ 5. dry-run モード
105
+ - 既存の差分表示
106
+ - ★ 孤児レポート表示
107
+ 6. 確認プロンプト
108
+ 7. バックアップ作成
109
+ 8. ファイルマージ処理
110
+ 9. ★ 孤児削除処理(--clean 時のみ)
111
+ - 確認プロンプト(デフォルトNo、--yes でスキップ)
112
+ - バックアップ作成
113
+ - ファイル削除
114
+ - メタデータから削除
115
+ 10. メタデータ保存
116
+ 11. 結果出力
117
+ - ★ 孤児レポート(--clean 無しでも警告表示)
118
+ - ★ JSON出力に orphans フィールド追加
119
+ 12. 依存関係チェック
120
+ ```
121
+
122
+ ### Step 6: テスト
123
+
124
+ **orphan-cleaner.test.ts(新規):**
125
+ - メタデータにあるがテンプレートにないファイルを検出
126
+ - カテゴリフィルタが正しく適用される
127
+ - ディスク実在チェックが正しい
128
+ - レポートフォーマットが正しい
129
+
130
+ **metadata-manager.test.ts(追加):**
131
+ - `removeFiles` で複数ファイルが正しく削除される
132
+
133
+ ## UX例
134
+
135
+ ### 通常 sync(孤児あり、--clean なし)
136
+ ```
137
+ ✅ 同期完了!
138
+ - 成功: 3ファイル
139
+ - スキップ: 12ファイル
140
+
141
+ ⚠️ 孤児ファイルが検出されました:
142
+ 🗑️ .claude/skills/einja-old/SKILL.md
143
+ 🗑️ docs/einja/deprecated.md
144
+
145
+ 💡 削除するには --clean オプションを使用してください
146
+ ```
147
+
148
+ ### `--clean` 実行時
149
+ ```
150
+ ⚠️ 以下の孤児ファイルを削除します:
151
+ 🗑️ .claude/skills/einja-old/SKILL.md
152
+ 🗑️ docs/einja/deprecated.md
153
+
154
+ ? 2ファイルを削除します。続行しますか? (y/N) y
155
+
156
+ - 孤児削除: 2ファイル
157
+ ```
158
+
159
+ ## 検証方法
160
+
161
+ 1. `pnpm -F @einja/dev-cli test` — 全テスト通過
162
+ 2. `pnpm -F @einja/dev-cli typecheck` — 型チェック通過
163
+ 3. `pnpm -F @einja/dev-cli lint` — lint通過
164
+ 4. 手動テスト: `.einja-sync.json` に存在しないファイルエントリを追加 → `einja sync --dry-run` で孤児検出 → `einja sync --clean --yes` で削除確認
@@ -0,0 +1,94 @@
1
+ # Plan: project-privateセクションの簡素化
2
+
3
+ ## Context
4
+
5
+ project-privateセクションの冗長な形式を簡素化する。現在の形式:
6
+
7
+ ```markdown
8
+ ---
9
+
10
+ <!-- @einja:project-private:start id="xxx" -->
11
+ ## プロジェクト固有の設定
12
+
13
+ <!-- このセクションはプロジェクト固有の内容を追記する場所です -->
14
+ <!-- einja syncで上書きされません -->
15
+ <!-- @einja:project-private:end -->
16
+ ```
17
+
18
+ 新しい形式:
19
+
20
+ ```markdown
21
+ <!-- @einja:project-private:start id="xxx" -->
22
+ <!-- プロジェクト固有の情報を記入 -->
23
+ <!-- @einja:project-private:end -->
24
+ ```
25
+
26
+ 変更点:
27
+ 1. 直前の `---` 罫線を削除
28
+ 2. `## プロジェクト固有の設定` 見出しを削除
29
+ 3. 空行 + 2行コメントを1行コメントに統合
30
+
31
+ ## 互換性
32
+
33
+ CLIの `marker-processor.ts` はstart/endタグのみでパースするため、中身のコメント変更は完全に互換。
34
+
35
+ ## 対象ファイル
36
+
37
+ ### 一括置換対象(63ファイル)
38
+
39
+ | カテゴリ | ファイル数 |
40
+ |---------|-----------|
41
+ | `.claude/agents/einja/` | 16 |
42
+ | `.claude/commands/einja/` | 7 |
43
+ | `.claude/skills/einja-*/SKILL.md` | 13 |
44
+ | `docs/einja/steering/` | 20 |
45
+ | `docs/einja/instructions/` | 7 |
46
+
47
+ ### 対象外
48
+
49
+ | カテゴリ | 理由 |
50
+ |---------|------|
51
+ | `CLAUDE.md` | 修正済み |
52
+ | `docs/einja/memory/` | ファイル全体がproject-private、見出しは意味あるコンテンツ |
53
+ | `packages/cli/` | ソースコード・テスト(文字列リテラル・テストデータ) |
54
+ | `packages/create-einja-app/` | 同上 |
55
+ | `docs/plans/` | 過去のplan(参照用) |
56
+ | `.claude/skills/einja-skill-creator/scripts/init_skill.py` | 説明用コードブロック内(短い形式で既にOK) |
57
+ | `.claude/skills/einja-skill-creator/SKILL.md` | 説明用コードブロック + 自身のproject-private |
58
+ | `.claude/skills/einja-coding-standards/references/testing-strategy.md` | 確認が必要 |
59
+
60
+ ## 実装手順
61
+
62
+ ### Step 1: sedで一括置換
63
+
64
+ 2つのパターンに対応:
65
+
66
+ **パターンA**: `---` + 空行 + `---` + 空行 + project-private(1ファイル: `frontend-implement.md`)
67
+ **パターンB**: `---` + 空行 + project-private(約62ファイル)
68
+
69
+ 両パターンをカバーするPerl正規表現で一括処理:
70
+
71
+ ```bash
72
+ # .claude/ と docs/einja/ 配下の .md ファイルを対象に、
73
+ # "---\n\n" (1つまたは2つ) + 冗長なproject-private を簡素な形式に置換
74
+ find .claude/agents .claude/commands .claude/skills docs/einja/steering docs/einja/instructions \
75
+ -name '*.md' -exec perl -0777 -pi -e '
76
+ s/\n---\n(\n---\n)?\n<!-- @einja:project-private:start id="([^"]+)" -->\n## プロジェクト固有の設定\n\n<!-- このセクションはプロジェクト固有の内容を追記する場所です -->\n<!-- einja syncで上書きされません -->\n<!-- @einja:project-private:end -->/\n<!-- \@einja:project-private:start id="$2" -->\n<!-- プロジェクト固有の情報を記入 -->\n<!-- \@einja:project-private:end -->/g' {} +
77
+ ```
78
+
79
+ ### Step 2: SKILL.md内のドキュメント例も更新
80
+
81
+ `einja-skill-creator/SKILL.md` 内の説明用コードブロック部分は手動で確認・更新。
82
+
83
+ ### Step 3: 検証
84
+
85
+ ```bash
86
+ # 旧形式が残っていないことを確認
87
+ grep -r "## プロジェクト固有の設定" .claude/ docs/einja/steering/ docs/einja/instructions/ --include="*.md"
88
+
89
+ # 新形式の件数を確認(63ファイル程度)
90
+ grep -r "<!-- プロジェクト固有の情報を記入 -->" .claude/ docs/einja/ --include="*.md" | wc -l
91
+
92
+ # テスト実行(marker-processor関連)
93
+ cd packages/cli && pnpm test -- marker-processor
94
+ ```
@@ -0,0 +1,362 @@
1
+ # GitHub Actions リリースワークフロー + 承認フロー設計
2
+
3
+ ## Context
4
+
5
+ 現在、main/staging/developへのpushで自動Vercelデプロイが走るが、GitHub Release作成・承認フローがない。リリースの可視性と本番デプロイの安全性を向上させるため、以下を導入する:
6
+
7
+ - staging マージ → GitHub **PreRelease** 自動作成
8
+ - main マージ → GitHub **Release** 自動作成
9
+ - production デプロイに **承認ゲート**(Environment Protection)
10
+
11
+ ## 設計方針
12
+
13
+ | 項目 | 決定 |
14
+ |------|------|
15
+ | リリース順序 | PRマージ → 自動リリース作成 |
16
+ | 昇格フロー | staging → main 直接マージPR |
17
+ | バージョニング | changesets(Turborepo公式推奨) |
18
+ | 承認フロー | GitHub Environments + Required Reviewer 1名(productionのみ) |
19
+
20
+ ## 全体フロー
21
+
22
+ ```
23
+ feature/* → staging PR(changeset含む)
24
+ ↓ マージ
25
+ [staging] → CI + Migrate + Deploy(承認不要)
26
+ → PreRelease自動作成(v0.2.0-rc.42)
27
+ ※ changeset未消費(タグ作成のみ)
28
+
29
+ staging → main PR(昇格)
30
+ ↓ マージ
31
+ [main] → CI + ⚠️承認待ち → Migrate + Deploy
32
+ → changeset version(バージョン確定)→ Release自動作成(v0.2.0)
33
+ ```
34
+
35
+ ## Codexレビュー反映事項
36
+
37
+ | # | 問題 | 修正内容 |
38
+ |---|------|---------|
39
+ | C-1 | stagingでchangeset消費すると main で残らない | `changeset version` はmainのみ。stagingはRCタグ作成のみ(未消費) |
40
+ | C-2 | `workflow_run` はdefault branch制約がある | release処理を `deploy-stable-branches.yml` 内ジョブに統合(独立ワークフロー廃止) |
41
+ | C-3 | `--previous-tag` は存在しないCLIオプション | `--notes-start-tag` に修正 |
42
+ | C-4 | 承認前にDBマイグレーションが走る | migrate + deploy 両方にproduction environment適用 |
43
+ | H-1 | `ci`ジョブのみの無限ループ防止は不十分 | `GITHUB_TOKEN` はデフォルトでワークフロー再起動しない特性を活用。ワークフロー全体ガード追加 |
44
+ | H-2 | `permissions: contents: write` 未定義 | 明示追加 |
45
+ | H-3 | `environment: ''` の動作が不確実 | ジョブ分割で解消(develop/staging/production別ジョブ) |
46
+ | H-4 | ブランチ保護との衝突リスク | version bumpはmainのみ。GITHUB_TOKEN使用(bot名義でprotection bypass可能な設定前提) |
47
+
48
+ ## 実装ステップ
49
+
50
+ ### Phase 1: changesets基盤導入
51
+
52
+ **変更ファイル:**
53
+ - `package.json` — `@changesets/cli`, `@changesets/changelog-github` をdevDependenciesに追加。scriptsに `"changeset": "changeset"` 追加
54
+ - `.changeset/config.json` — 新規作成
55
+
56
+ ```json
57
+ {
58
+ "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
59
+ "changelog": ["@changesets/changelog-github", { "repo": "einja-inc/einja-management-template" }],
60
+ "commit": false,
61
+ "fixed": [],
62
+ "linked": [],
63
+ "access": "restricted",
64
+ "baseBranch": "main",
65
+ "updateInternalDependencies": "patch",
66
+ "ignore": ["@einja/dev-cli", "create-einja-app"]
67
+ }
68
+ ```
69
+
70
+ **リスク:** なし。既存フローに影響しない
71
+
72
+ ### Phase 2: GitHub Environments設定(手動・GitHub UI)
73
+
74
+ | Environment | Required Reviewers | Wait Timer | Deployment Branches |
75
+ |------------|-------------------|------------|-------------------|
76
+ | `staging` | なし | なし | `staging`のみ |
77
+ | `production` | 1名 | なし | `main`のみ |
78
+
79
+ **リスク:** なし。ワークフローに`environment`参照がないため、設定だけでは動作変更なし
80
+
81
+ ### Phase 3: `deploy-stable-branches.yml` 大規模改修
82
+
83
+ **変更概要:** デプロイジョブをブランチ別に分割 + リリース作成ジョブを統合
84
+
85
+ **改修前の構造:**
86
+ ```
87
+ ci → changes → migrate → deploy(全ブランチ共通)
88
+ ```
89
+
90
+ **改修後の構造:**
91
+ ```
92
+ ci → changes → (ブランチ別分岐)
93
+ ├─ [develop] → deploy-develop(環境なし)
94
+ ├─ [staging] → deploy-staging(staging環境)→ release-staging(PreRelease作成)
95
+ └─ [main] → migrate-production(production環境・承認待ち)→ deploy-production → release-production(Release作成)
96
+ ```
97
+
98
+ **主要な変更点:**
99
+
100
+ #### 3-1. ワークフロー全体ガード(無限ループ防止)
101
+ ```yaml
102
+ on:
103
+ push:
104
+ branches: [main, develop, staging]
105
+
106
+ # GITHUB_TOKENで作成されたイベントはデフォルトでワークフロー再起動しない
107
+ # 念のため明示的にbotコミットを除外
108
+ jobs:
109
+ ci:
110
+ # github-actions[bot] のバージョンバンプコミットはスキップ
111
+ if: "!contains(github.event.head_commit.message, 'chore: release v')"
112
+ ```
113
+
114
+ #### 3-2. デプロイジョブをブランチ別に分割
115
+ ```yaml
116
+ # develop: 環境なし、承認なし
117
+ deploy-develop:
118
+ needs: [ci, changes]
119
+ if: github.ref_name == 'develop' && needs.changes.outputs.deploy_matrix != '[]'
120
+ runs-on: ubuntu-latest
121
+ strategy: ...
122
+ steps: ... # 既存のdeployステップをそのまま
123
+
124
+ # staging: staging環境、承認なし
125
+ deploy-staging:
126
+ needs: [ci, migrate, changes]
127
+ if: github.ref_name == 'staging' && ...
128
+ environment: staging
129
+ runs-on: ubuntu-latest
130
+ strategy: ...
131
+ steps: ... # 既存のdeployステップをそのまま
132
+
133
+ # main: production環境、1名承認 → migrate → deploy
134
+ migrate-production:
135
+ needs: ci
136
+ if: github.ref_name == 'main'
137
+ environment: production # ← ここで承認待ち(migrate前にブロック)
138
+ runs-on: ubuntu-latest
139
+ steps: ... # 既存のmigrateステップ
140
+
141
+ deploy-production:
142
+ needs: [migrate-production, changes]
143
+ if: github.ref_name == 'main' && needs.changes.outputs.deploy_matrix != '[]'
144
+ runs-on: ubuntu-latest
145
+ strategy: ...
146
+ steps: ... # 既存のdeployステップ
147
+ ```
148
+
149
+ #### 3-3. リリース作成ジョブを統合(release-app.yml廃止)
150
+ ```yaml
151
+ permissions:
152
+ contents: write # タグ作成・Release作成に必要
153
+
154
+ # staging: PreRelease作成(changeset未消費)
155
+ release-staging:
156
+ needs: deploy-staging
157
+ if: github.ref_name == 'staging'
158
+ runs-on: ubuntu-latest
159
+ steps:
160
+ - uses: actions/checkout@v4
161
+ with:
162
+ fetch-depth: 0
163
+ - name: Check for changesets
164
+ id: check
165
+ run: |
166
+ COUNT=$(ls .changeset/*.md 2>/dev/null | grep -cv README.md || echo 0)
167
+ echo "has_changesets=$([[ $COUNT -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT
168
+ - name: Create PreRelease tag
169
+ if: steps.check.outputs.has_changesets == 'true'
170
+ id: tag
171
+ run: |
172
+ VERSION=$(node -p "require('./package.json').version")
173
+ TAG="v${VERSION}-rc.${{ github.run_number }}"
174
+ git tag "$TAG"
175
+ git push origin "$TAG"
176
+ echo "tag=$TAG" >> $GITHUB_OUTPUT
177
+ - name: Create GitHub PreRelease
178
+ if: steps.tag.outputs.tag
179
+ run: |
180
+ PREV=$(git tag --list 'v*-rc.*' --sort=-v:refname | grep -v "${{ steps.tag.outputs.tag }}" | head -1)
181
+ OPTS="--prerelease --target staging --generate-notes"
182
+ [[ -n "$PREV" ]] && OPTS="$OPTS --notes-start-tag $PREV"
183
+ gh release create "${{ steps.tag.outputs.tag }}" --title "Pre-release ${{ steps.tag.outputs.tag }}" $OPTS
184
+ env:
185
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
186
+
187
+ # main: changeset消費 + Release作成
188
+ release-production:
189
+ needs: deploy-production
190
+ if: github.ref_name == 'main'
191
+ runs-on: ubuntu-latest
192
+ steps:
193
+ - uses: actions/checkout@v4
194
+ with:
195
+ fetch-depth: 0
196
+ - name: Setup
197
+ uses: ./.github/actions/setup
198
+ - name: Check for changesets
199
+ id: check
200
+ run: |
201
+ COUNT=$(ls .changeset/*.md 2>/dev/null | grep -cv README.md || echo 0)
202
+ echo "has_changesets=$([[ $COUNT -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT
203
+ - name: Version packages
204
+ if: steps.check.outputs.has_changesets == 'true'
205
+ run: npx changeset version
206
+ - name: Commit version bump and create Release
207
+ if: steps.check.outputs.has_changesets == 'true'
208
+ id: release
209
+ run: |
210
+ VERSION=$(node -p "require('./package.json').version")
211
+ TAG="v${VERSION}"
212
+ git config user.name "github-actions[bot]"
213
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
214
+ git add -A
215
+ git commit -m "chore: release ${TAG}" || echo "No changes"
216
+ git tag "$TAG"
217
+ git push origin main --follow-tags
218
+ PREV=$(git tag --list 'v*' --sort=-v:refname | grep -v 'rc' | grep -v "$TAG" | head -1)
219
+ OPTS="--target main --generate-notes"
220
+ [[ -n "$PREV" ]] && OPTS="$OPTS --notes-start-tag $PREV"
221
+ gh release create "$TAG" --title "Release ${TAG}" $OPTS
222
+ env:
223
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
224
+ ```
225
+
226
+ **無限ループ防止の多重防御:**
227
+ 1. `GITHUB_TOKEN` で作成されたpushイベントはデフォルトでワークフローを再トリガーしない
228
+ 2. コミットメッセージ `chore: release v` でのフィルタリング
229
+ 3. バージョンバンプコミットは `github-actions[bot]` 名義
230
+
231
+ **リスク:** 高。ジョブ分割により既存ワークフローの構造が大きく変わる。段階的にテストが必要
232
+
233
+ ### Phase 4: `.github/release.yml` リリースノート設定
234
+
235
+ ```yaml
236
+ changelog:
237
+ exclude:
238
+ labels: ["dependencies", "skip-changelog"]
239
+ categories:
240
+ - title: "New Features"
241
+ labels: ["enhancement", "feature"]
242
+ - title: "Bug Fixes"
243
+ labels: ["bug", "fix"]
244
+ - title: "Other Changes"
245
+ labels: ["*"]
246
+ ```
247
+
248
+ ### Phase 5: `changeset-status.yml` 新規作成(任意)
249
+
250
+ PR上にchangesetの有無を表示するワークフロー。開発体験向上目的。
251
+
252
+ ### Phase 6: einja-create-pr Skill新規作成
253
+
254
+ **新規ファイル:** `.claude/skills/einja-create-pr/SKILL.md`
255
+
256
+ **責務:** PR作成時にchangeset自動生成 + ラベル付与 + PR作成を一括実行
257
+
258
+ **処理フロー(5ステップ):**
259
+
260
+ 1. **差分分析**: `git log --format="%s%n%b" origin/{base}..HEAD` + `git diff --name-only origin/{base}..HEAD`
261
+ 2. **changeset生成判定**:
262
+ - スキップ条件: staging→main昇格PR、apps/配下に変更なし(docs/CI/設定のみ)、既にchangesetファイルがある
263
+ - 変更種別推定: コミットメッセージプレフィックスの最大値(feat+fix→minor, feat!→major)
264
+ - パッケージ判定: ファイルパスから(apps/web/** → @repo/web等)
265
+ 3. **changeset生成**: `.changeset/{ランダム名}.md` を作成 → コミット
266
+ 4. **ラベル判定**: PRタイトルプレフィックスから単一ラベル選択(enhancement/bug/maintenance)
267
+ 5. **PR作成**: `gh pr create --title ... --body ... --label ...`
268
+
269
+ **動作モード:**
270
+ | モード | トリガー | changeset確認 | ラベル確認 |
271
+ |--------|---------|--------------|-----------|
272
+ | 自動 | task-exec/issue-exec経由 | 推定値で自動決定 | 自動 |
273
+ | 対話 | 手動 `/einja-create-pr` | AskUserQuestionで確認 | AskUserQuestionで確認 |
274
+
275
+ **既存ワークフローへの統合:**
276
+ - `issue-exec.md` のManagerのPR作成部分を `/einja-create-pr` 呼び出しに変更
277
+ - `task-exec.md` は変更不要(現状PR作成は含まない)
278
+
279
+ **ラベル判定ルール(単一ラベル、優先度順):**
280
+ ```
281
+ feat!/BREAKING → breaking-change
282
+ feat: → enhancement
283
+ fix: → bug
284
+ その他 → maintenance
285
+ ```
286
+
287
+ ### Phase 7: 既存ドキュメントへの追記
288
+
289
+ **`docs/einja/steering/infrastructure/deployment.md`** — 以下のセクションを追記:
290
+ - 「8. リリース管理」: GitHub Release/PreRelease自動作成フロー
291
+ - 「9. バージョニング戦略」: changesets採番ルール、タグ形式
292
+ - 「10. 承認フロー」: GitHub Environments設定、production承認ゲート
293
+ - ワークフロー一覧テーブルに release-staging/release-production ジョブを追加
294
+
295
+ **`docs/einja/steering/development-workflow.md`** — 以下を追記:
296
+ - changesetの運用フロー(PRにchangeset含める手順)
297
+ - 「Phase A: 仕様書作成」→「Phase B: タスク実行」→「Phase C: リリース」の流れ
298
+
299
+ ## バージョニング採番ルール
300
+
301
+ ### セマンティックバージョニング(changeset指定)
302
+
303
+ | 変更種別 | changeset指定 | バージョン変更例 | 使用シーン |
304
+ |---------|--------------|----------------|----------|
305
+ | 破壊的変更 | `major` | `0.1.0` → `1.0.0` | API仕様変更、DB破壊的マイグレーション |
306
+ | 新機能追加 | `minor` | `0.1.0` → `0.2.0` | 新画面、新API追加 |
307
+ | バグ修正 | `patch` | `0.1.0` → `0.1.1` | 不具合修正、パフォーマンス改善 |
308
+
309
+ ### タグ形式
310
+
311
+ | 環境 | タグ形式 | 例 | GitHub Release種別 |
312
+ |------|---------|-----|------------------|
313
+ | staging | `v{version}-rc.{run_number}` | `v0.2.0-rc.42` | PreRelease |
314
+ | production | `v{version}` | `v0.2.0` | Release |
315
+
316
+ ### changeset消費タイミング
317
+
318
+ | ブランチ | changeset消費 | バージョンバンプ | タグ形式 |
319
+ |---------|-------------|----------------|---------|
320
+ | staging | **消費しない** | なし(package.json据え置き) | `v{current}-rc.{run_number}` |
321
+ | main | `changeset version` で消費 | package.json更新 | `v{new_version}` |
322
+
323
+ ## 変更ファイル一覧
324
+
325
+ | ファイル | 操作 | Phase |
326
+ |---------|------|-------|
327
+ | `package.json` | 改修(devDeps + scripts追加) | 1 |
328
+ | `.changeset/config.json` | 新規 | 1 |
329
+ | `.github/workflows/deploy-stable-branches.yml` | 大規模改修(ジョブ分割 + リリース統合) | 3 |
330
+ | `.github/release.yml` | 新規 | 4 |
331
+ | `.github/workflows/changeset-status.yml` | 新規(任意) | 5 |
332
+ | `.claude/skills/einja-create-pr/SKILL.md` | 新規(changeset生成 + ラベル付与 + PR作成) | 6 |
333
+ | `.claude/commands/einja/issue-exec.md` | 改修(PR作成部分をeinja-create-pr呼び出しに変更) | 6 |
334
+ | `docs/einja/steering/infrastructure/deployment.md` | 追記(リリース管理・バージョニング・承認フロー) | 7 |
335
+ | `docs/einja/steering/development-workflow.md` | 追記(changeset運用フロー) | 7 |
336
+
337
+ **変更しないファイル:**
338
+ - `release-cli.yml` / `release-create-einja-app.yml`(NPMリリースは独立運用を維持)
339
+ - `deploy-pr-preview.yml` / `cleanup-*.yml` / `claude.yml`
340
+ - Composite Actions(`setup`, `ci`, `migrate`, `neon-export-env`)
341
+
342
+ **廃止:**
343
+ - `release-app.yml` は作成しない(deploy-stable-branches.yml内に統合)
344
+
345
+ ## NPMリリースとの棲み分け
346
+
347
+ | タグパターン | 用途 | 生成元 |
348
+ |-------------|------|--------|
349
+ | `v1.2.0` | アプリ Stable Release | deploy-stable-branches.yml内 release-production ジョブ |
350
+ | `v1.2.0-rc.42` | アプリ PreRelease | deploy-stable-branches.yml内 release-staging ジョブ |
351
+ | `cli-v0.1.41` | @einja/dev-cli | 手動タグ(既存運用) |
352
+ | `create-einja-app-v0.3.2` | create-einja-app | 手動タグ(既存運用) |
353
+
354
+ ## 検証方法
355
+
356
+ 1. **Phase 1検証**: `pnpm changeset` で対話UIが起動し、`.changeset/` にmdファイルが生成されること
357
+ 2. **Phase 3検証**:
358
+ - developブランチにpush → deploy-developが承認なしで実行(従来通り)
359
+ - stagingブランチにpush → deploy-stagingが承認なしで実行 → release-stagingでPreRelease作成
360
+ - mainブランチにpush → migrate-productionがGitHub UIで承認待ち → 承認後migrate+deploy → release-productionでRelease作成
361
+ - バージョンバンプコミットでワークフローが再トリガーされない(無限ループなし)ことを確認
362
+ 3. **Phase 6検証**: `/einja-create-pr` でchangeset生成 + ラベル付与 + PR作成が一括実行されること