@einja/dev-cli 0.1.40 → 0.1.44

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 (207) hide show
  1. package/README.md +89 -1
  2. package/dist/cli.js +1 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +71 -1
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/commands/list.js.map +1 -1
  8. package/dist/commands/sync.d.ts.map +1 -1
  9. package/dist/commands/sync.js +187 -13
  10. package/dist/commands/sync.js.map +1 -1
  11. package/dist/commands/task-loop/lib/github-client.test.js.map +1 -1
  12. package/dist/commands/task-loop/lib/vibe-kanban-rest-client.js +2 -2
  13. package/dist/commands/task-loop/lib/vibe-kanban-rest-client.js.map +1 -1
  14. package/dist/lib/dependency-checker.d.ts.map +1 -1
  15. package/dist/lib/merger.d.ts +12 -0
  16. package/dist/lib/merger.d.ts.map +1 -1
  17. package/dist/lib/merger.js +28 -0
  18. package/dist/lib/merger.js.map +1 -1
  19. package/dist/lib/preset-update/cli-repo-detector.d.ts.map +1 -1
  20. package/dist/lib/preset-update/file-copier.d.ts.map +1 -1
  21. package/dist/lib/preset-update/file-copier.js +3 -3
  22. package/dist/lib/preset-update/file-copier.js.map +1 -1
  23. package/dist/lib/preset-update/preset-finder.d.ts.map +1 -1
  24. package/dist/lib/preset.d.ts.map +1 -1
  25. package/dist/lib/sync/category-validator.d.ts +1 -1
  26. package/dist/lib/sync/category-validator.d.ts.map +1 -1
  27. package/dist/lib/sync/category-validator.js +2 -1
  28. package/dist/lib/sync/category-validator.js.map +1 -1
  29. package/dist/lib/sync/category-validator.test.js +3 -1
  30. package/dist/lib/sync/category-validator.test.js.map +1 -1
  31. package/dist/lib/sync/conflict-reporter.d.ts.map +1 -1
  32. package/dist/lib/sync/diff-engine.d.ts.map +1 -1
  33. package/dist/lib/sync/file-filter.d.ts.map +1 -1
  34. package/dist/lib/sync/file-filter.js +1 -0
  35. package/dist/lib/sync/file-filter.js.map +1 -1
  36. package/dist/lib/sync/integration.test.js +255 -69
  37. package/dist/lib/sync/integration.test.js.map +1 -1
  38. package/dist/lib/sync/json-processor.d.ts +4 -4
  39. package/dist/lib/sync/json-processor.d.ts.map +1 -1
  40. package/dist/lib/sync/json-processor.js +11 -11
  41. package/dist/lib/sync/json-processor.js.map +1 -1
  42. package/dist/lib/sync/marker-processor.d.ts +60 -8
  43. package/dist/lib/sync/marker-processor.d.ts.map +1 -1
  44. package/dist/lib/sync/marker-processor.js +117 -26
  45. package/dist/lib/sync/marker-processor.js.map +1 -1
  46. package/dist/lib/sync/marker-processor.test.js +261 -40
  47. package/dist/lib/sync/marker-processor.test.js.map +1 -1
  48. package/dist/lib/sync/metadata-manager.d.ts +4 -0
  49. package/dist/lib/sync/metadata-manager.d.ts.map +1 -1
  50. package/dist/lib/sync/metadata-manager.js +15 -0
  51. package/dist/lib/sync/metadata-manager.js.map +1 -1
  52. package/dist/lib/sync/metadata-manager.test.js +69 -0
  53. package/dist/lib/sync/metadata-manager.test.js.map +1 -1
  54. package/dist/lib/sync/orphan-cleaner.d.ts +29 -0
  55. package/dist/lib/sync/orphan-cleaner.d.ts.map +1 -0
  56. package/dist/lib/sync/orphan-cleaner.js +80 -0
  57. package/dist/lib/sync/orphan-cleaner.js.map +1 -0
  58. package/dist/lib/sync/orphan-cleaner.test.d.ts +2 -0
  59. package/dist/lib/sync/orphan-cleaner.test.d.ts.map +1 -0
  60. package/dist/lib/sync/orphan-cleaner.test.js +169 -0
  61. package/dist/lib/sync/orphan-cleaner.test.js.map +1 -0
  62. package/dist/lib/sync/project-private-synchronizer.d.ts +52 -0
  63. package/dist/lib/sync/project-private-synchronizer.d.ts.map +1 -0
  64. package/dist/lib/sync/project-private-synchronizer.js +110 -0
  65. package/dist/lib/sync/project-private-synchronizer.js.map +1 -0
  66. package/dist/lib/sync/project-private-synchronizer.test.d.ts +2 -0
  67. package/dist/lib/sync/project-private-synchronizer.test.d.ts.map +1 -0
  68. package/dist/lib/sync/project-private-synchronizer.test.js +348 -0
  69. package/dist/lib/sync/project-private-synchronizer.test.js.map +1 -0
  70. package/dist/types/index.d.ts +1 -0
  71. package/dist/types/index.d.ts.map +1 -1
  72. package/dist/types/sync.d.ts +36 -6
  73. package/dist/types/sync.d.ts.map +1 -1
  74. package/dist/types/sync.js +2 -2
  75. package/dist/types/sync.js.map +1 -1
  76. package/package.json +5 -4
  77. package/presets/default/.claude/agents/einja/Explore.md +140 -0
  78. package/presets/default/.claude/agents/einja/backend-architect.md +21 -1
  79. package/presets/default/.claude/agents/einja/codex-agent.md +5 -1
  80. package/presets/default/.claude/agents/einja/design-engineer.md +5 -1
  81. package/presets/default/.claude/agents/einja/docs/docs-updater.md +7 -93
  82. package/presets/default/.claude/agents/einja/frontend-architect.md +21 -1
  83. package/presets/default/.claude/agents/einja/frontend-coder.md +5 -1
  84. package/presets/default/.claude/agents/einja/{specs/spec-design-generator.md → issue-specs/design-generator.md} +16 -8
  85. package/presets/default/.claude/agents/einja/{specs/spec-qa-generator.md → issue-specs/qa-generator.md} +10 -4
  86. package/presets/default/.claude/agents/einja/{specs/spec-requirements-generator.md → issue-specs/requirements-generator.md} +9 -6
  87. package/presets/default/.claude/agents/einja/{specs/spec-tasks-generator.md → issue-specs/tasks-generator.md} +19 -16
  88. package/presets/default/.claude/agents/einja/{specs/spec-tasks-validator.md → issue-specs/tasks-validator.md} +13 -9
  89. package/presets/default/.claude/agents/einja/issue-specs/ui-design-generator.md +114 -0
  90. package/presets/default/.claude/agents/einja/task/task-executer.md +64 -116
  91. package/presets/default/.claude/agents/einja/task/task-modification-analyzer.md +6 -2
  92. package/presets/default/.claude/agents/einja/task/task-qa.md +7 -3
  93. package/presets/default/.claude/agents/einja/task/task-reviewer.md +17 -1
  94. package/presets/default/.claude/commands/einja/einja-sync.md +124 -45
  95. package/presets/default/.claude/commands/einja/frontend-implement.md +3 -1
  96. package/presets/default/.claude/commands/einja/issue-exec.md +413 -0
  97. package/presets/default/.claude/commands/einja/start-dev.md +4 -0
  98. package/presets/default/.claude/commands/einja/sync-cursor-commands.md +10 -6
  99. package/presets/default/.claude/commands/einja/{update-docs-by-task-specs.md → update-docs-by-issue-specs.md} +61 -57
  100. package/presets/default/.claude/hooks/einja/plan-mode-skill-loader.sh +27 -0
  101. package/presets/default/.claude/settings.json +29 -5
  102. package/presets/default/.claude/skills/{einja-general-context-loader → _einja-general-context-loader}/SKILL.md +6 -2
  103. package/presets/default/.claude/skills/{einja-output-format → _einja-output-format}/SKILL.md +5 -1
  104. package/presets/default/.claude/skills/_einja-project-overview/SKILL.md +29 -0
  105. package/presets/default/.claude/skills/{einja-spec-context-loader → _einja-spec-context-loader}/SKILL.md +9 -5
  106. package/presets/default/.claude/skills/einja-coding-standards/references/testing-strategy.md +899 -0
  107. package/presets/default/.claude/skills/einja-conflict-resolver/SKILL.md +5 -1
  108. package/presets/default/.claude/skills/einja-create-pr/SKILL.md +138 -0
  109. package/presets/default/.claude/skills/einja-infra-maintenance/SKILL.md +779 -0
  110. package/presets/default/.claude/{commands/einja/spec-create.md → skills/einja-issue-spec-create/SKILL.md} +60 -23
  111. package/presets/default/.claude/skills/einja-issue-spec-generator/SKILL.md +105 -0
  112. package/presets/default/.claude/skills/einja-issue-spec-generator/references/format-rules.md +35 -0
  113. package/presets/default/.claude/skills/einja-issue-spec-validator/SKILL.md +130 -0
  114. package/presets/default/.claude/skills/einja-issue-spec-validator/references/validation-rules.md +52 -0
  115. package/presets/default/.claude/skills/einja-npm-release/SKILL.md +242 -0
  116. package/presets/default/.claude/skills/einja-skill-creator/SKILL.md +311 -263
  117. package/presets/default/.claude/skills/einja-skill-creator/agents/analyzer.md +274 -0
  118. package/presets/default/.claude/skills/einja-skill-creator/agents/comparator.md +202 -0
  119. package/presets/default/.claude/skills/einja-skill-creator/agents/grader.md +195 -0
  120. package/presets/default/.claude/skills/einja-skill-creator/assets/eval_review.html +146 -0
  121. package/presets/default/.claude/skills/einja-skill-creator/eval-viewer/generate_review.py +471 -0
  122. package/presets/default/.claude/skills/einja-skill-creator/eval-viewer/viewer.html +1325 -0
  123. package/presets/default/.claude/skills/einja-skill-creator/references/schemas.md +430 -0
  124. package/presets/default/.claude/skills/einja-skill-creator/scripts/aggregate_benchmark.py +401 -0
  125. package/presets/default/.claude/skills/einja-skill-creator/scripts/compare_runs.py +154 -0
  126. package/presets/default/.claude/skills/einja-skill-creator/scripts/generate_report.py +272 -0
  127. package/presets/default/.claude/skills/einja-skill-creator/scripts/improve_description.py +247 -0
  128. package/presets/default/.claude/skills/einja-skill-creator/scripts/init_skill.py +13 -19
  129. package/presets/default/.claude/skills/einja-skill-creator/scripts/package_skill.py +36 -7
  130. package/presets/default/.claude/skills/einja-skill-creator/scripts/run_eval.py +310 -0
  131. package/presets/default/.claude/skills/einja-skill-creator/scripts/run_loop.py +375 -0
  132. package/presets/default/.claude/skills/einja-skill-creator/scripts/utils.py +48 -0
  133. package/presets/default/.claude/skills/einja-skill-first/SKILL.md +265 -0
  134. package/presets/default/.claude/skills/einja-subagent-question-protocol/SKILL.md +98 -0
  135. package/presets/default/.claude/skills/einja-task-commit/SKILL.md +11 -7
  136. package/presets/default/.claude/{commands/einja/task-exec.md → skills/einja-task-exec/SKILL.md} +106 -89
  137. package/presets/default/.claude/skills/einja-task-qa/SKILL.md +8 -4
  138. package/presets/default/.claude/skills/einja-task-qa/references/troubleshooting.md +1 -1
  139. package/presets/default/.claude/skills/einja-task-qa/references/usage-patterns.md +2 -2
  140. package/presets/default/.claude/skills/einja-team-exec/SKILL.md +165 -0
  141. package/presets/default/.envrc +5 -0
  142. package/presets/default/.mcp.json +2 -12
  143. package/presets/default/CLAUDE.md.template +45 -8
  144. package/presets/default/docs/einja/example/specs/issues/issue999-example-task/tasks.md +1 -1
  145. package/presets/default/docs/einja/instructions/deployment-setup.md +4 -9
  146. package/presets/default/docs/einja/instructions/environment-setup.md +3 -8
  147. package/presets/default/docs/einja/instructions/issue-exec-workflow.md +276 -0
  148. package/presets/default/docs/einja/instructions/local-server-environment-and-worktree.md +71 -9
  149. package/presets/default/docs/einja/instructions/neon-cli-reference.md +3 -8
  150. package/presets/default/docs/einja/instructions/setup-flow.md +279 -0
  151. package/presets/default/docs/einja/instructions/task-execute.md +63 -68
  152. package/presets/default/docs/einja/instructions/vercel-cli-reference.md +17 -10
  153. package/presets/default/docs/einja/steering/README.md +11 -11
  154. package/presets/default/docs/einja/steering/acceptance-criteria-and-qa-guide.md +4 -9
  155. package/presets/default/docs/einja/steering/architecture.md +3 -8
  156. package/presets/default/docs/einja/steering/branch-strategy.md +63 -70
  157. package/presets/default/docs/einja/steering/commit-rules.md +3 -8
  158. package/presets/default/docs/einja/steering/db-schema-design.md +3 -8
  159. package/presets/default/docs/einja/steering/development/api-development.md +3 -8
  160. package/presets/default/docs/einja/steering/development/backend-architecture.md +3 -8
  161. package/presets/default/docs/einja/steering/development/coding-standards.md +723 -0
  162. package/presets/default/docs/einja/steering/development/component-design.md +502 -0
  163. package/presets/default/docs/einja/steering/development/database-guidelines.md +2 -2
  164. package/presets/default/docs/einja/steering/development/frontend-development.md +3 -8
  165. package/presets/default/docs/einja/steering/development/playwright-guidelines.md +59 -0
  166. package/presets/default/docs/einja/steering/development/review-guidelines.md +3 -8
  167. package/presets/default/docs/einja/steering/development/testing-strategy.md +3 -8
  168. package/presets/default/docs/einja/steering/development-workflow.md +155 -140
  169. package/presets/default/docs/einja/steering/infrastructure/deployment.md +156 -55
  170. package/presets/default/docs/einja/steering/infrastructure/environment-variables.md +4 -8
  171. package/presets/default/docs/einja/steering/product.md +3 -8
  172. package/presets/default/docs/einja/steering/task-management.md +22 -110
  173. package/presets/default/scripts/ensure-serena.sh +75 -0
  174. package/presets/default/scripts/env-rotate-secrets.ts +396 -0
  175. package/presets/default/scripts/env-show.ts +130 -0
  176. package/presets/default/scripts/env.ts +479 -0
  177. package/presets/default/scripts/init-github.ts +363 -0
  178. package/presets/default/scripts/init.sh +98 -0
  179. package/presets/default/scripts/lib/env-common.ts +108 -0
  180. package/presets/default/scripts/lib/worktree-config.ts +64 -0
  181. package/presets/default/scripts/setup-dev.ts +655 -0
  182. package/presets/default/scripts/stop-serena.sh +25 -0
  183. package/presets/default/scripts/worktree/dev.ts +872 -0
  184. package/dist/lib/sync/seed-synchronizer.d.ts +0 -27
  185. package/dist/lib/sync/seed-synchronizer.d.ts.map +0 -1
  186. package/dist/lib/sync/seed-synchronizer.js +0 -72
  187. package/dist/lib/sync/seed-synchronizer.js.map +0 -1
  188. package/dist/lib/sync/seed-synchronizer.test.d.ts +0 -2
  189. package/dist/lib/sync/seed-synchronizer.test.d.ts.map +0 -1
  190. package/dist/lib/sync/seed-synchronizer.test.js +0 -147
  191. package/dist/lib/sync/seed-synchronizer.test.js.map +0 -1
  192. package/presets/default/.claude/agents/einja/git/conflict-resolver.md +0 -148
  193. package/presets/default/.claude/hooks/einja/validate-git-commit.sh +0 -239
  194. package/presets/default/.claude/skills/einja-api-development/SKILL.md +0 -14
  195. package/presets/default/.claude/skills/einja-backend-architecture/SKILL.md +0 -18
  196. package/presets/default/.claude/skills/einja-coding-standards/SKILL.md +0 -132
  197. package/presets/default/.claude/skills/einja-coding-standards/references/import-conventions.md +0 -69
  198. package/presets/default/.claude/skills/einja-coding-standards/references/naming-conventions.md +0 -107
  199. package/presets/default/.claude/skills/einja-coding-standards/references/prohibited-patterns.md +0 -169
  200. package/presets/default/.claude/skills/einja-coding-standards/references/typescript-rules.md +0 -247
  201. package/presets/default/.claude/skills/einja-component-design/SKILL.md +0 -109
  202. package/presets/default/.claude/skills/einja-component-design/references/directory-structure.md +0 -117
  203. package/presets/default/.claude/skills/einja-component-design/references/props-patterns.md +0 -159
  204. package/presets/default/.claude/skills/einja-component-design/references/styling-guide.md +0 -122
  205. package/presets/default/.claude/skills/einja-frontend-development/SKILL.md +0 -14
  206. package/presets/default/.claude/skills/einja-project-overview/SKILL.md +0 -35
  207. package/presets/default/docs/einja/instructions/task-vibe-kanban-loop.md +0 -565
@@ -0,0 +1,363 @@
1
+ import { execSync, spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import * as p from "@clack/prompts";
5
+
6
+ // ANSI color codes
7
+ const colors = {
8
+ blue: (text: string) => `\x1b[34m${text}\x1b[0m`,
9
+ green: (text: string) => `\x1b[32m${text}\x1b[0m`,
10
+ yellow: (text: string) => `\x1b[33m${text}\x1b[0m`,
11
+ gray: (text: string) => `\x1b[90m${text}\x1b[0m`,
12
+ red: (text: string) => `\x1b[31m${text}\x1b[0m`,
13
+ cyan: (text: string) => `\x1b[36m${text}\x1b[0m`,
14
+ bold: (text: string) => `\x1b[1m${text}\x1b[0m`,
15
+ };
16
+
17
+ function commandExists(cmd: string): boolean {
18
+ try {
19
+ const checkCmd =
20
+ process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`;
21
+ execSync(checkCmd, { stdio: "ignore", shell: true });
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ function succeed(message: string): void {
29
+ console.log(`${colors.green("✓")} ${message}`);
30
+ }
31
+
32
+ function warn(message: string): void {
33
+ console.log(`${colors.yellow("⚠")} ${message}`);
34
+ }
35
+
36
+ function fail(message: string): void {
37
+ console.log(`${colors.red("✗")} ${message}`);
38
+ }
39
+
40
+ function step(num: number, message: string): void {
41
+ console.log(`\n${colors.blue(`Step ${num}:`)} ${message}`);
42
+ }
43
+
44
+ /**
45
+ * git remote URLからorg/repoを解析する
46
+ */
47
+ function parseRemoteUrl(url: string): { org: string; repo: string } | null {
48
+ // SSH形式: git@github.com:org/repo.git
49
+ const sshMatch = url.match(/git@github\.com:([^/]+)\/([^/.]+)(\.git)?/);
50
+ if (sshMatch) {
51
+ return { org: sshMatch[1], repo: sshMatch[2] };
52
+ }
53
+ // HTTPS形式: https://github.com/org/repo.git
54
+ const httpsMatch = url.match(
55
+ /https:\/\/github\.com\/([^/]+)\/([^/.]+)(\.git)?/,
56
+ );
57
+ if (httpsMatch) {
58
+ return { org: httpsMatch[1], repo: httpsMatch[2] };
59
+ }
60
+ return null;
61
+ }
62
+
63
+ /**
64
+ * .env.keysファイルからDOTENV_PRIVATE_KEY_*のエントリを読み取る
65
+ */
66
+ function parseEnvKeys(
67
+ filePath: string,
68
+ ): Array<{ name: string; value: string }> {
69
+ const content = fs.readFileSync(filePath, "utf-8");
70
+ const entries: Array<{ name: string; value: string }> = [];
71
+ for (const line of content.split("\n")) {
72
+ const match = line.match(
73
+ /^(DOTENV_PRIVATE_KEY_\w+)=["']?([^"'\n]+)["']?/,
74
+ );
75
+ if (match) {
76
+ entries.push({ name: match[1], value: match[2] });
77
+ }
78
+ }
79
+ return entries;
80
+ }
81
+
82
+ async function main(): Promise<void> {
83
+ const cwd = process.cwd();
84
+
85
+ p.intro(colors.blue("GitHub リポジトリセットアップ"));
86
+
87
+ // Step 1: gh CLIの確認
88
+ step(1, "GitHub CLI (gh) の確認...");
89
+
90
+ if (!commandExists("gh")) {
91
+ fail("GitHub CLI (gh) がインストールされていません");
92
+ console.log(colors.yellow(" インストール方法:"));
93
+ console.log(colors.cyan(" brew install gh"));
94
+ console.log(
95
+ colors.gray(" 詳細: https://cli.github.com/manual/installation"),
96
+ );
97
+ process.exit(1);
98
+ }
99
+ succeed("GitHub CLI (gh) がインストールされています");
100
+
101
+ // Step 2: gh認証の確認
102
+ step(2, "GitHub認証の確認...");
103
+
104
+ try {
105
+ execSync("gh auth status", { stdio: "pipe" });
106
+ succeed("GitHub認証済みです");
107
+ } catch {
108
+ fail("GitHubに認証されていません");
109
+ console.log(colors.yellow(" 以下のコマンドでログインしてください:"));
110
+ console.log(colors.cyan(" gh auth login"));
111
+ process.exit(1);
112
+ }
113
+
114
+ // Step 3: リモートリポジトリの確認
115
+ step(3, "リモートリポジトリの確認...");
116
+
117
+ let org = "";
118
+ let repo = "";
119
+ let remoteUrl = "";
120
+
121
+ try {
122
+ remoteUrl = execSync("git remote get-url origin", {
123
+ encoding: "utf-8",
124
+ stdio: ["pipe", "pipe", "pipe"],
125
+ }).trim();
126
+ } catch {
127
+ remoteUrl = "";
128
+ }
129
+
130
+ if (!remoteUrl) {
131
+ // Step 4: リモートがない場合、新規作成
132
+ step(4, "新規リポジトリの作成...");
133
+
134
+ const dirName = path.basename(cwd);
135
+
136
+ const orgInput = await p.text({
137
+ message: "GitHub組織名(またはユーザー名)を入力してください",
138
+ placeholder: "einja-inc",
139
+ initialValue: "einja-inc",
140
+ });
141
+ if (p.isCancel(orgInput)) {
142
+ p.cancel("キャンセルしました");
143
+ process.exit(0);
144
+ }
145
+ org = orgInput;
146
+
147
+ const repoInput = await p.text({
148
+ message: "リポジトリ名を入力してください",
149
+ placeholder: dirName,
150
+ defaultValue: dirName,
151
+ });
152
+ if (p.isCancel(repoInput)) {
153
+ p.cancel("キャンセルしました");
154
+ process.exit(0);
155
+ }
156
+ repo = repoInput;
157
+
158
+ const visibility = await p.select({
159
+ message: "リポジトリの公開設定を選択してください",
160
+ options: [
161
+ { value: "private", label: "Private(非公開)", hint: "推奨" },
162
+ { value: "public", label: "Public(公開)" },
163
+ ],
164
+ initialValue: "private",
165
+ });
166
+ if (p.isCancel(visibility)) {
167
+ p.cancel("キャンセルしました");
168
+ process.exit(0);
169
+ }
170
+
171
+ const visibilityFlag =
172
+ visibility === "public" ? "--public" : "--private";
173
+
174
+ try {
175
+ execSync(
176
+ `gh repo create ${org}/${repo} ${visibilityFlag} --source=. --remote=origin`,
177
+ { stdio: "inherit", cwd },
178
+ );
179
+ succeed(`リポジトリ ${org}/${repo} を作成しました`);
180
+ } catch (error) {
181
+ fail("リポジトリの作成に失敗しました");
182
+ console.log(colors.red(` ${error}`));
183
+ process.exit(1);
184
+ }
185
+ } else {
186
+ // リモートが存在する場合、org/repoを解析
187
+ const parsed = parseRemoteUrl(remoteUrl);
188
+ if (parsed) {
189
+ org = parsed.org;
190
+ repo = parsed.repo;
191
+ succeed(`リモートリポジトリ: ${org}/${repo}`);
192
+ } else {
193
+ warn(`リモートURLの解析に失敗しました: ${remoteUrl}`);
194
+ org = "";
195
+ repo = "";
196
+ }
197
+ }
198
+
199
+ // Step 5: mainブランチのプッシュ
200
+ step(5, "mainブランチのプッシュ...");
201
+
202
+ try {
203
+ const trackingBranch = execSync(
204
+ "git rev-parse --abbrev-ref --symbolic-full-name @{u}",
205
+ {
206
+ encoding: "utf-8",
207
+ stdio: ["pipe", "pipe", "pipe"],
208
+ },
209
+ ).trim();
210
+ succeed(`リモート追跡ブランチ: ${trackingBranch}`);
211
+ } catch {
212
+ // 追跡ブランチがない場合はプッシュ
213
+ try {
214
+ execSync("git push -u origin main", { stdio: "inherit", cwd });
215
+ succeed("mainブランチをプッシュしました");
216
+ } catch (error) {
217
+ warn("mainブランチのプッシュに失敗しました");
218
+ console.log(colors.gray(` ${error}`));
219
+ }
220
+ }
221
+
222
+ // Step 6: ブランチ保護ルールの設定
223
+ step(6, "ブランチ保護ルールの設定...");
224
+
225
+ if (org && repo) {
226
+ try {
227
+ const protectionPayload = JSON.stringify({
228
+ required_status_checks: {
229
+ strict: true,
230
+ contexts: [],
231
+ },
232
+ enforce_admins: false,
233
+ required_pull_request_reviews: {
234
+ dismiss_stale_reviews: true,
235
+ require_code_owner_reviews: false,
236
+ required_approving_review_count: 1,
237
+ },
238
+ restrictions: null,
239
+ allow_force_pushes: false,
240
+ allow_deletions: false,
241
+ });
242
+
243
+ execSync(
244
+ `gh api repos/${org}/${repo}/branches/main/protection -X PUT --input - <<'EOF'\n${protectionPayload}\nEOF`,
245
+ {
246
+ stdio: "pipe",
247
+ cwd,
248
+ shell: true,
249
+ },
250
+ );
251
+ succeed("ブランチ保護ルールを設定しました");
252
+ console.log(colors.gray(" - PRレビュー必須(1名以上)"));
253
+ console.log(colors.gray(" - ステータスチェック必須"));
254
+ console.log(colors.gray(" - 古いレビューの自動却下"));
255
+ console.log(colors.gray(" - フォースプッシュ禁止"));
256
+ } catch (error) {
257
+ warn("ブランチ保護ルールの設定に失敗しました");
258
+ console.log(
259
+ colors.gray(
260
+ " リポジトリの権限を確認してください(Freeプランでは制限あり)",
261
+ ),
262
+ );
263
+ }
264
+ } else {
265
+ warn("org/repoが不明のため、ブランチ保護ルールをスキップしました");
266
+ }
267
+
268
+ // Step 7: GitHub Secretsの設定
269
+ step(7, "GitHub Secretsの設定(.env.keys)...");
270
+
271
+ const envKeysPath = path.join(cwd, ".env.keys");
272
+
273
+ if (fs.existsSync(envKeysPath)) {
274
+ const keys = parseEnvKeys(envKeysPath);
275
+ if (keys.length > 0) {
276
+ let successCount = 0;
277
+ let failCount = 0;
278
+
279
+ for (const { name, value } of keys) {
280
+ try {
281
+ const result = spawnSync("gh", ["secret", "set", name, "--body", value], {
282
+ stdio: "pipe",
283
+ cwd,
284
+ });
285
+ if (result.status !== 0) {
286
+ throw new Error(result.stderr?.toString() || "gh secret set failed");
287
+ }
288
+ successCount++;
289
+ } catch {
290
+ failCount++;
291
+ warn(` Secret ${name} の設定に失敗しました`);
292
+ }
293
+ }
294
+
295
+ if (successCount > 0) {
296
+ succeed(`${successCount}件のSecretを設定しました`);
297
+ }
298
+ if (failCount > 0) {
299
+ warn(`${failCount}件のSecretの設定に失敗しました`);
300
+ }
301
+ } else {
302
+ warn(
303
+ ".env.keys にDOTENV_PRIVATE_KEY_*エントリが見つかりませんでした",
304
+ );
305
+ }
306
+ } else {
307
+ warn(".env.keys が見つかりません(スキップ)");
308
+ console.log(
309
+ colors.gray(
310
+ " 後で .env.keys を配置して再実行してください",
311
+ ),
312
+ );
313
+ }
314
+
315
+ // Step 8: GitHub Environmentsの作成
316
+ step(8, "GitHub Environmentsの作成...");
317
+
318
+ if (org && repo) {
319
+ const environments = ["production", "preview"];
320
+
321
+ for (const envName of environments) {
322
+ try {
323
+ execSync(
324
+ `gh api repos/${org}/${repo}/environments/${envName} -X PUT --input - <<'EOF'\n{}\nEOF`,
325
+ {
326
+ stdio: "pipe",
327
+ cwd,
328
+ shell: true,
329
+ },
330
+ );
331
+ succeed(`Environment "${envName}" を作成しました`);
332
+ } catch {
333
+ warn(`Environment "${envName}" の作成に失敗しました`);
334
+ }
335
+ }
336
+ } else {
337
+ warn("org/repoが不明のため、Environmentsの作成をスキップしました");
338
+ }
339
+
340
+ // 完了サマリー
341
+ console.log(colors.green("\n=========================================="));
342
+ console.log(colors.green("✅ GitHubリポジトリのセットアップが完了しました!"));
343
+ console.log(colors.green("==========================================\n"));
344
+
345
+ if (org && repo) {
346
+ console.log(`リポジトリ: ${colors.cyan(`https://github.com/${org}/${repo}`)}`);
347
+ console.log("");
348
+ }
349
+
350
+ console.log("セットアップ内容:");
351
+ console.log(colors.gray(" - リモートリポジトリの設定"));
352
+ console.log(colors.gray(" - mainブランチの保護ルール"));
353
+ console.log(colors.gray(" - GitHub Secrets(.env.keys)"));
354
+ console.log(colors.gray(" - GitHub Environments(production, preview)"));
355
+ console.log("");
356
+
357
+ p.outro(colors.green("セットアップが完了しました"));
358
+ }
359
+
360
+ main().catch((error: unknown) => {
361
+ console.error(colors.red("エラーが発生しました:"), error);
362
+ process.exit(1);
363
+ });
@@ -0,0 +1,98 @@
1
+ #!/bin/bash
2
+ #
3
+ # init.sh - 初回セットアップ(Volta/Node/pnpm導入)
4
+ #
5
+ # 使い方:
6
+ # ./scripts/init.sh
7
+ #
8
+ # ※ 初回のみ実行。2回目以降は不要です。
9
+ #
10
+
11
+ set -e
12
+
13
+ # 色定義
14
+ BLUE='\033[34m'
15
+ GREEN='\033[32m'
16
+ YELLOW='\033[33m'
17
+ GRAY='\033[90m'
18
+ NC='\033[0m'
19
+
20
+ log_success() { echo -e "${GREEN}✓${NC} $1"; }
21
+ log_warn() { echo -e "${YELLOW}⚠${NC} $1"; }
22
+ log_step() { echo -e "\n${BLUE}Step $1:${NC} $2"; }
23
+
24
+ # スクリプトのディレクトリに移動
25
+ cd "$(dirname "$0")/.."
26
+
27
+ echo -e "${BLUE}"
28
+ echo "=========================================="
29
+ echo " 初回セットアップ"
30
+ echo "=========================================="
31
+ echo -e "${NC}"
32
+
33
+ # Step 1: Voltaの確認とインストール
34
+ log_step 1 "Voltaのインストール..."
35
+
36
+ if ! command -v volta &> /dev/null; then
37
+ curl -fsSL https://get.volta.sh | bash -s -- --skip-setup
38
+ export VOLTA_HOME="$HOME/.volta"
39
+ export PATH="$VOLTA_HOME/bin:$PATH"
40
+ log_success "Voltaをインストールしました"
41
+ else
42
+ log_success "Voltaは既にインストール済み"
43
+ fi
44
+
45
+ # Volta PATHを設定
46
+ export VOLTA_HOME="$HOME/.volta"
47
+ export PATH="$VOLTA_HOME/bin:$PATH"
48
+ export VOLTA_FEATURE_PNPM=1
49
+
50
+ # Step 2: シェル設定
51
+ log_step 2 "シェル設定..."
52
+
53
+ SHELL_NAME=$(basename "$SHELL")
54
+ case "$SHELL_NAME" in
55
+ zsh) RC_FILE="$HOME/.zshrc" ;;
56
+ bash) RC_FILE="$HOME/.bashrc" ;;
57
+ *) RC_FILE="" ;;
58
+ esac
59
+
60
+ if [ -n "$RC_FILE" ] && ! grep -q "VOLTA_FEATURE_PNPM" "$RC_FILE" 2>/dev/null; then
61
+ echo -e "\n# Volta - pnpm support\nexport VOLTA_FEATURE_PNPM=1" >> "$RC_FILE"
62
+ log_success "シェル設定を追加しました"
63
+ else
64
+ log_success "シェル設定は既に完了"
65
+ fi
66
+
67
+ # Step 3: Node.js/pnpmインストール
68
+ log_step 3 "Node.js/pnpmのインストール..."
69
+
70
+ NODE_VERSION=$(grep -o '"node": *"[^"]*"' package.json | grep -o '[0-9.]*')
71
+ PNPM_VERSION=$(grep -o '"pnpm": *"[^"]*"' package.json | grep -o '[0-9.]*')
72
+
73
+ volta install node@"$NODE_VERSION"
74
+ volta install pnpm@"$PNPM_VERSION"
75
+ log_success "Node.js $NODE_VERSION, pnpm $PNPM_VERSION をインストールしました"
76
+
77
+ # Step 4: direnv allow(direnvが利用可能な場合)
78
+ log_step 4 "direnv設定..."
79
+
80
+ if command -v direnv &> /dev/null; then
81
+ direnv allow
82
+ log_success "direnv allow を実行しました"
83
+ else
84
+ log_warn "direnvが見つかりません(スキップ)"
85
+ echo -e " ${GRAY}direnvインストール後に 'direnv allow' を実行してください${NC}"
86
+ fi
87
+
88
+ # 完了
89
+ echo ""
90
+ echo -e "${GREEN}=========================================="
91
+ echo -e "✅ 初回セットアップ完了!"
92
+ echo -e "==========================================${NC}"
93
+ echo ""
94
+ echo "次のステップ:"
95
+ echo -e " 1. ターミナルを再起動: ${BLUE}exec \$SHELL${NC}"
96
+ echo -e " 2. 依存関係インストール: ${BLUE}pnpm install${NC}"
97
+ echo -e " 3. 環境セットアップ: ${BLUE}pnpm dev:setup${NC}"
98
+ echo ""
@@ -0,0 +1,108 @@
1
+ /**
2
+ * 環境変数管理の共通処理
3
+ *
4
+ * scripts/env.ts と scripts/env-rotate-secrets.ts で共有される
5
+ * 共通のユーティリティ関数と型定義を提供します。
6
+ */
7
+
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+
11
+ const cwd = process.cwd();
12
+
13
+ /** 環境変数ファイルのパス */
14
+ export const ENV_KEYS_PATH = path.join(cwd, ".env.keys");
15
+
16
+ /**
17
+ * 環境設定の定義
18
+ */
19
+ export interface EnvironmentConfig {
20
+ name: string;
21
+ file: string;
22
+ privateKeyEnv: string;
23
+ description: string;
24
+ }
25
+
26
+ /**
27
+ * サポートされる環境の定義
28
+ */
29
+ export const ENVIRONMENTS: EnvironmentConfig[] = [
30
+ {
31
+ name: "local",
32
+ file: ".env.local",
33
+ privateKeyEnv: "DOTENV_PRIVATE_KEY_LOCAL",
34
+ description: "ローカル開発環境",
35
+ },
36
+ {
37
+ name: "develop",
38
+ file: ".env.develop",
39
+ privateKeyEnv: "DOTENV_PRIVATE_KEY_DEVELOP",
40
+ description: "開発環境",
41
+ },
42
+ {
43
+ name: "preview",
44
+ file: ".env.preview",
45
+ privateKeyEnv: "DOTENV_PRIVATE_KEY_PREVIEW",
46
+ description: "プレビュー環境",
47
+ },
48
+ {
49
+ name: "production",
50
+ file: ".env.production",
51
+ privateKeyEnv: "DOTENV_PRIVATE_KEY_PRODUCTION",
52
+ description: "本番環境",
53
+ },
54
+ {
55
+ name: "ci",
56
+ file: ".env.ci",
57
+ privateKeyEnv: "DOTENV_PRIVATE_KEY_CI",
58
+ description: "CI環境",
59
+ },
60
+ ];
61
+
62
+ /**
63
+ * 環境変数ファイルを読み込んでパース
64
+ *
65
+ * @param filePath - パースする環境変数ファイルのパス
66
+ * @returns 環境変数のキーバリューペア
67
+ */
68
+ export function parseEnvFile(filePath: string): Record<string, string> {
69
+ if (!fs.existsSync(filePath)) {
70
+ return {};
71
+ }
72
+ const content = fs.readFileSync(filePath, "utf-8");
73
+ const result: Record<string, string> = {};
74
+
75
+ for (const line of content.split("\n")) {
76
+ const trimmed = line.trim();
77
+ if (!trimmed || trimmed.startsWith("#")) continue;
78
+
79
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
80
+ if (match) {
81
+ const key = match[1].trim();
82
+ let value = match[2].trim();
83
+ // クォートを除去
84
+ if (
85
+ (value.startsWith('"') && value.endsWith('"')) ||
86
+ (value.startsWith("'") && value.endsWith("'"))
87
+ ) {
88
+ value = value.slice(1, -1);
89
+ }
90
+ result[key] = value;
91
+ }
92
+ }
93
+ return result;
94
+ }
95
+
96
+ /**
97
+ * .env.keysから指定された環境の秘密鍵を取得
98
+ *
99
+ * @param privateKeyEnv - 秘密鍵の環境変数名(例: DOTENV_PRIVATE_KEY_LOCAL)
100
+ * @returns 秘密鍵の値。見つからない場合はnull
101
+ */
102
+ export function getPrivateKey(privateKeyEnv: string): string | null {
103
+ if (!fs.existsSync(ENV_KEYS_PATH)) {
104
+ return null;
105
+ }
106
+ const keys = parseEnvFile(ENV_KEYS_PATH);
107
+ return keys[privateKeyEnv] || null;
108
+ }
@@ -0,0 +1,64 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ export interface AppConfig {
5
+ id: string;
6
+ portRangeStart: number;
7
+ rangeSize: number;
8
+ }
9
+
10
+ export interface PostgresConfig {
11
+ port: number;
12
+ containerName: string;
13
+ }
14
+
15
+ export interface WorktreeConfig {
16
+ schemaVersion: number;
17
+ postgres: PostgresConfig;
18
+ apps: AppConfig[];
19
+ }
20
+
21
+ const defaultWorktreeConfig: WorktreeConfig = {
22
+ schemaVersion: 1,
23
+ postgres: { port: 25432, containerName: "einja-management-postgres" },
24
+ apps: [{ id: "web", portRangeStart: 3000, rangeSize: 1000 }],
25
+ };
26
+
27
+ function findProjectRoot(startDir: string = process.cwd()): string | null {
28
+ let currentDir = startDir;
29
+ while (currentDir !== path.dirname(currentDir)) {
30
+ if (fs.existsSync(path.join(currentDir, "package.json"))) {
31
+ return currentDir;
32
+ }
33
+ currentDir = path.dirname(currentDir);
34
+ }
35
+ return null;
36
+ }
37
+
38
+ export function loadWorktreeConfig(projectRoot?: string): WorktreeConfig {
39
+ const root = projectRoot ?? findProjectRoot();
40
+ if (!root) return defaultWorktreeConfig;
41
+
42
+ const configPath = path.join(root, "worktree.config.json");
43
+ if (!fs.existsSync(configPath)) return defaultWorktreeConfig;
44
+
45
+ try {
46
+ const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
47
+ return {
48
+ schemaVersion: raw.schemaVersion ?? 1,
49
+ postgres: {
50
+ port: typeof raw.postgres?.port === "number" ? raw.postgres.port : 25432,
51
+ containerName: typeof raw.postgres?.containerName === "string"
52
+ ? raw.postgres.containerName : "einja-management-postgres",
53
+ },
54
+ apps: Array.isArray(raw.apps)
55
+ ? raw.apps.filter((a: unknown) =>
56
+ typeof a === "object" && a !== null && "id" in a && "portRangeStart" in a
57
+ )
58
+ : defaultWorktreeConfig.apps,
59
+ };
60
+ } catch {
61
+ console.warn("worktree.config.json の読み込みに失敗。デフォルト設定を使用します。");
62
+ return defaultWorktreeConfig;
63
+ }
64
+ }