@sk8metal/michi-cli 0.3.0 → 0.4.0

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 (157) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/scripts/config-global.d.ts +10 -0
  3. package/dist/scripts/config-global.d.ts.map +1 -0
  4. package/dist/scripts/config-global.js +111 -0
  5. package/dist/scripts/config-global.js.map +1 -0
  6. package/dist/scripts/confluence-sync.d.ts +22 -4
  7. package/dist/scripts/confluence-sync.d.ts.map +1 -1
  8. package/dist/scripts/confluence-sync.js +22 -12
  9. package/dist/scripts/confluence-sync.js.map +1 -1
  10. package/dist/scripts/jira-sync.d.ts.map +1 -1
  11. package/dist/scripts/jira-sync.js +201 -167
  12. package/dist/scripts/jira-sync.js.map +1 -1
  13. package/dist/scripts/list-projects.js.map +1 -1
  14. package/dist/scripts/multi-project-estimate.js.map +1 -1
  15. package/dist/scripts/phase-runner.d.ts +1 -1
  16. package/dist/scripts/phase-runner.d.ts.map +1 -1
  17. package/dist/scripts/phase-runner.js +295 -522
  18. package/dist/scripts/phase-runner.js.map +1 -1
  19. package/dist/scripts/pre-flight-check.d.ts.map +1 -1
  20. package/dist/scripts/pre-flight-check.js +10 -6
  21. package/dist/scripts/pre-flight-check.js.map +1 -1
  22. package/dist/scripts/resource-dashboard.js.map +1 -1
  23. package/dist/scripts/spec-impl-workflow.js +1 -1
  24. package/dist/scripts/spec-impl-workflow.js.map +1 -1
  25. package/dist/scripts/template/renderer.d.ts +1 -1
  26. package/dist/scripts/template/renderer.d.ts.map +1 -1
  27. package/dist/scripts/test-interactive.d.ts.map +1 -1
  28. package/dist/scripts/test-interactive.js +0 -15
  29. package/dist/scripts/test-interactive.js.map +1 -1
  30. package/dist/scripts/test-new-features.js +6 -3
  31. package/dist/scripts/test-new-features.js.map +1 -1
  32. package/dist/scripts/test-spec-generator.d.ts.map +1 -1
  33. package/dist/scripts/test-spec-generator.js +1 -2
  34. package/dist/scripts/test-spec-generator.js.map +1 -1
  35. package/dist/scripts/utils/config-loader.d.ts +7 -2
  36. package/dist/scripts/utils/config-loader.d.ts.map +1 -1
  37. package/dist/scripts/utils/config-loader.js +79 -8
  38. package/dist/scripts/utils/config-loader.js.map +1 -1
  39. package/dist/scripts/utils/config-sections.d.ts +54 -0
  40. package/dist/scripts/utils/config-sections.d.ts.map +1 -0
  41. package/dist/scripts/utils/config-sections.js +178 -0
  42. package/dist/scripts/utils/config-sections.js.map +1 -0
  43. package/dist/scripts/utils/config-validator.d.ts +4 -0
  44. package/dist/scripts/utils/config-validator.d.ts.map +1 -1
  45. package/dist/scripts/utils/config-validator.js +57 -1
  46. package/dist/scripts/utils/config-validator.js.map +1 -1
  47. package/dist/scripts/utils/confluence-approval.d.ts.map +1 -1
  48. package/dist/scripts/utils/confluence-approval.js +5 -3
  49. package/dist/scripts/utils/confluence-approval.js.map +1 -1
  50. package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
  51. package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
  52. package/dist/scripts/utils/interactive-helpers.d.ts +32 -0
  53. package/dist/scripts/utils/interactive-helpers.d.ts.map +1 -0
  54. package/dist/scripts/utils/interactive-helpers.js +92 -0
  55. package/dist/scripts/utils/interactive-helpers.js.map +1 -0
  56. package/dist/scripts/utils/jira-issue-type-fetcher.d.ts.map +1 -1
  57. package/dist/scripts/utils/jira-issue-type-fetcher.js +27 -18
  58. package/dist/scripts/utils/jira-issue-type-fetcher.js.map +1 -1
  59. package/dist/scripts/utils/release-notes-generator.d.ts.map +1 -1
  60. package/dist/scripts/utils/release-notes-generator.js +2 -1
  61. package/dist/scripts/utils/release-notes-generator.js.map +1 -1
  62. package/dist/scripts/utils/spec-updater.d.ts +19 -0
  63. package/dist/scripts/utils/spec-updater.d.ts.map +1 -1
  64. package/dist/scripts/utils/spec-updater.js.map +1 -1
  65. package/dist/scripts/utils/tasks-converter.d.ts.map +1 -1
  66. package/dist/scripts/utils/tasks-converter.js +2 -2
  67. package/dist/scripts/utils/tasks-converter.js.map +1 -1
  68. package/dist/scripts/utils/tasks-format-validator.d.ts.map +1 -1
  69. package/dist/scripts/utils/tasks-format-validator.js +0 -12
  70. package/dist/scripts/utils/tasks-format-validator.js.map +1 -1
  71. package/dist/scripts/utils/test-runner.d.ts.map +1 -1
  72. package/dist/scripts/utils/test-runner.js +3 -2
  73. package/dist/scripts/utils/test-runner.js.map +1 -1
  74. package/dist/scripts/validate-phase.d.ts +1 -1
  75. package/dist/scripts/validate-phase.d.ts.map +1 -1
  76. package/dist/scripts/validate-phase.js +12 -62
  77. package/dist/scripts/validate-phase.js.map +1 -1
  78. package/dist/scripts/workflow-orchestrator.d.ts.map +1 -1
  79. package/dist/scripts/workflow-orchestrator.js +11 -16
  80. package/dist/scripts/workflow-orchestrator.js.map +1 -1
  81. package/dist/src/__tests__/integration/setup/init.test.d.ts +5 -0
  82. package/dist/src/__tests__/integration/setup/init.test.d.ts.map +1 -0
  83. package/dist/src/__tests__/integration/setup/init.test.js +352 -0
  84. package/dist/src/__tests__/integration/setup/init.test.js.map +1 -0
  85. package/dist/src/cli.d.ts.map +1 -1
  86. package/dist/src/cli.js +28 -20
  87. package/dist/src/cli.js.map +1 -1
  88. package/dist/src/commands/init.d.ts +28 -0
  89. package/dist/src/commands/init.d.ts.map +1 -0
  90. package/dist/src/commands/init.js +490 -0
  91. package/dist/src/commands/init.js.map +1 -0
  92. package/docs/user-guide/getting-started/setup.md +31 -3
  93. package/docs/user-guide/guides/customization.md +64 -11
  94. package/docs/user-guide/guides/workflow.md +35 -21
  95. package/docs/user-guide/reference/config.md +30 -5
  96. package/docs/user-guide/reference/quick-reference.md +68 -74
  97. package/docs/user-guide/testing/test-planning-flow.md +4 -0
  98. package/package.json +2 -4
  99. package/scripts/config-global.ts +160 -0
  100. package/scripts/confluence-sync.ts +91 -27
  101. package/scripts/jira-sync.ts +284 -218
  102. package/scripts/list-projects.ts +2 -2
  103. package/scripts/multi-project-estimate.ts +3 -3
  104. package/scripts/phase-runner.ts +391 -594
  105. package/scripts/pre-flight-check.ts +20 -9
  106. package/scripts/pre-publish-check.sh +3 -34
  107. package/scripts/resource-dashboard.ts +4 -4
  108. package/scripts/spec-impl-workflow.ts +1 -1
  109. package/scripts/template/renderer.ts +1 -1
  110. package/scripts/test-interactive.ts +0 -19
  111. package/scripts/test-new-features.ts +10 -7
  112. package/scripts/test-npm-package.sh +3 -34
  113. package/scripts/test-spec-generator.ts +3 -7
  114. package/scripts/utils/config-loader.ts +107 -26
  115. package/scripts/utils/config-sections.ts +316 -0
  116. package/scripts/utils/config-validator.ts +66 -1
  117. package/scripts/utils/confluence-approval.ts +8 -6
  118. package/scripts/utils/confluence-hierarchy.ts +27 -27
  119. package/scripts/utils/interactive-helpers.ts +135 -0
  120. package/scripts/utils/jira-issue-type-fetcher.ts +29 -21
  121. package/scripts/utils/release-notes-generator.ts +3 -2
  122. package/scripts/utils/spec-updater.ts +37 -15
  123. package/scripts/utils/tasks-converter.ts +4 -6
  124. package/scripts/utils/tasks-format-validator.ts +0 -13
  125. package/scripts/utils/test-runner.ts +4 -3
  126. package/scripts/validate-phase.ts +21 -80
  127. package/scripts/workflow-orchestrator.ts +16 -25
  128. package/templates/claude/commands/kiro/kiro-spec-impl.md +4 -0
  129. package/templates/claude/commands/kiro/kiro-spec-tasks.md +3 -1
  130. package/templates/claude/commands/michi/confluence-sync.md +8 -2
  131. package/templates/claude/commands/michi/design-review.md +4 -0
  132. package/templates/claude/commands/michi/e2e-plan.md +4 -0
  133. package/templates/claude/commands/michi/license-check.md +4 -0
  134. package/templates/claude/commands/michi/pr-resolve.md +4 -0
  135. package/templates/claude/commands/michi/project-switch.md +8 -2
  136. package/templates/claude/commands/michi/spec-design.md +78 -0
  137. package/templates/claude/commands/michi/spec-impl.md +716 -0
  138. package/templates/claude/commands/michi/test-planning.md +174 -0
  139. package/templates/claude/commands/michi/validate-design.md +58 -0
  140. package/templates/claude/commands/michi/version-audit.md +4 -0
  141. package/templates/michi/cc-sdd-overrides/README.md +8 -0
  142. package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +53 -0
  143. package/dist/scripts/config-interactive.d.ts +0 -10
  144. package/dist/scripts/config-interactive.d.ts.map +0 -1
  145. package/dist/scripts/config-interactive.js +0 -372
  146. package/dist/scripts/config-interactive.js.map +0 -1
  147. package/dist/scripts/setup-existing-project.d.ts +0 -15
  148. package/dist/scripts/setup-existing-project.d.ts.map +0 -1
  149. package/dist/scripts/setup-existing-project.js +0 -455
  150. package/dist/scripts/setup-existing-project.js.map +0 -1
  151. package/dist/scripts/setup-interactive.d.ts +0 -10
  152. package/dist/scripts/setup-interactive.d.ts.map +0 -1
  153. package/dist/scripts/setup-interactive.js +0 -413
  154. package/dist/scripts/setup-interactive.js.map +0 -1
  155. package/scripts/config-interactive.ts +0 -550
  156. package/scripts/setup-existing-project.ts +0 -585
  157. package/scripts/setup-interactive.ts +0 -565
@@ -1,565 +0,0 @@
1
- /**
2
- * 対話式設定ツール
3
- * project.jsonと.envを対話的に作成・更新
4
- */
5
-
6
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
7
- import { join, basename } from 'path';
8
- import * as readline from 'readline';
9
- import { findCurrentProject, findAllProjects, selectProject, findRepositoryRoot } from './utils/project-finder.js';
10
- import type { ProjectMetadata } from './utils/project-meta.js';
11
-
12
- /**
13
- * プロジェクトIDのバリデーション
14
- * パストラバーサル攻撃を防ぐため、安全な文字のみを許可
15
- */
16
- function validateProjectId(projectId: string): boolean {
17
- // 空文字、空白のみを拒否
18
- if (!projectId.trim() || /^\s+$/.test(projectId)) {
19
- return false;
20
- }
21
- // パストラバーサル文字を拒否
22
- if (projectId.includes('..') || projectId.includes('/') || projectId.includes('\\')) {
23
- return false;
24
- }
25
- // 許可する文字のみ(英数字、ハイフン、アンダースコア)
26
- return /^[A-Za-z0-9_-]+$/.test(projectId);
27
- }
28
-
29
- /**
30
- * readlineインターフェースを作成
31
- */
32
- function createInterface(): readline.Interface {
33
- return readline.createInterface({
34
- input: process.stdin,
35
- output: process.stdout
36
- });
37
- }
38
-
39
- /**
40
- * 質問を表示して回答を取得
41
- */
42
- function question(rl: readline.Interface, query: string): Promise<string> {
43
- return new Promise(resolve => {
44
- rl.question(query, answer => {
45
- resolve(answer.trim());
46
- });
47
- });
48
- }
49
-
50
- /**
51
- * Yes/No質問
52
- */
53
- async function confirm(rl: readline.Interface, prompt: string, defaultValue: boolean = true): Promise<boolean> {
54
- const defaultText = defaultValue ? '[Y/n]' : '[y/N]';
55
- const answer = await question(rl, `${prompt} ${defaultText}: `);
56
-
57
- if (!answer) {
58
- return defaultValue;
59
- }
60
-
61
- return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
62
- }
63
-
64
- /**
65
- * プロジェクトメタデータを対話的に取得
66
- */
67
- async function getProjectMetadata(
68
- rl: readline.Interface,
69
- existingMeta: Partial<ProjectMetadata> | null,
70
- projectPath: string
71
- ): Promise<ProjectMetadata> {
72
- console.log('\n📦 プロジェクトメタデータ設定');
73
- console.log('='.repeat(60));
74
-
75
- // projectId
76
- const projectIdDefault = existingMeta?.projectId || basename(projectPath);
77
- const projectId = await question(
78
- rl,
79
- `プロジェクトID [${projectIdDefault}]: `
80
- ) || projectIdDefault;
81
-
82
- // バリデーション
83
- if (!validateProjectId(projectId)) {
84
- throw new Error('無効なプロジェクトIDです。英数字、ハイフン、アンダースコアのみ使用できます。');
85
- }
86
-
87
- // projectName
88
- const projectName = await question(
89
- rl,
90
- `プロジェクト名${existingMeta?.projectName ? ` [${existingMeta.projectName}]` : ''}: `
91
- ) || existingMeta?.projectName || '';
92
-
93
- if (!projectName) {
94
- throw new Error('プロジェクト名は必須です');
95
- }
96
-
97
- // jiraProjectKey
98
- const jiraProjectKey = await question(
99
- rl,
100
- `JIRAプロジェクトキー${existingMeta?.jiraProjectKey ? ` [${existingMeta.jiraProjectKey}]` : ''}: `
101
- ) || existingMeta?.jiraProjectKey || '';
102
-
103
- if (!jiraProjectKey) {
104
- throw new Error('JIRAプロジェクトキーは必須です');
105
- }
106
-
107
- // confluenceLabels
108
- const existingLabels = existingMeta?.confluenceLabels || [];
109
- const labelsInput = await question(
110
- rl,
111
- `Confluenceラベル(カンマ区切り)${existingLabels.length > 0 ? ` [${existingLabels.join(', ')}]` : ''}: `
112
- );
113
-
114
- const confluenceLabels = labelsInput
115
- ? labelsInput.split(',').map(s => s.trim()).filter(s => s.length > 0)
116
- : existingLabels;
117
-
118
- // プロジェクトラベルが含まれていない場合は自動追加
119
- const projectLabel = `project:${projectId}`;
120
- if (!confluenceLabels.includes(projectLabel)) {
121
- confluenceLabels.unshift(projectLabel);
122
- }
123
-
124
- // status
125
- const statusChoices = [
126
- { value: 'active', label: 'active(開発中)' },
127
- { value: 'inactive', label: 'inactive(休止中)' },
128
- { value: 'completed', label: 'completed(完了)' }
129
- ];
130
-
131
- console.log('\nステータスを選択してください:');
132
- statusChoices.forEach((choice, index) => {
133
- const defaultMark = existingMeta?.status === choice.value ? ' (現在の値)' : '';
134
- console.log(` ${index + 1}. ${choice.label}${defaultMark}`);
135
- });
136
-
137
- const statusAnswer = await question(rl, `選択 [${existingMeta?.status || 'active'}]: `);
138
- const statusIndex = statusAnswer ? parseInt(statusAnswer, 10) - 1 : -1;
139
- const status = (statusIndex >= 0 && statusIndex < statusChoices.length)
140
- ? statusChoices[statusIndex].value
141
- : (existingMeta?.status || 'active');
142
-
143
- // team
144
- const existingTeam = existingMeta?.team || [];
145
- const teamInput = await question(
146
- rl,
147
- `チームメンバー(カンマ区切り、@プレフィックス付き)${existingTeam.length > 0 ? ` [${existingTeam.join(', ')}]` : ''}: `
148
- );
149
- const team = teamInput
150
- ? teamInput.split(',').map(s => s.trim()).filter(s => s.length > 0)
151
- : existingTeam;
152
-
153
- // stakeholders
154
- const existingStakeholders = existingMeta?.stakeholders || [];
155
- const stakeholdersInput = await question(
156
- rl,
157
- `ステークホルダー(カンマ区切り、@プレフィックス付き)${existingStakeholders.length > 0 ? ` [${existingStakeholders.join(', ')}]` : ''}: `
158
- );
159
- const stakeholders = stakeholdersInput
160
- ? stakeholdersInput.split(',').map(s => s.trim()).filter(s => s.length > 0)
161
- : existingStakeholders;
162
-
163
- // repository
164
- const repository = await question(
165
- rl,
166
- `リポジトリURL${existingMeta?.repository ? ` [${existingMeta.repository}]` : ''}: `
167
- ) || existingMeta?.repository || '';
168
-
169
- // description
170
- const description = await question(
171
- rl,
172
- `説明${existingMeta?.description ? ` [${existingMeta.description}]` : ''}: `
173
- ) || existingMeta?.description || '';
174
-
175
- return {
176
- projectId,
177
- projectName,
178
- jiraProjectKey,
179
- confluenceLabels,
180
- status: status as ProjectMetadata['status'],
181
- team,
182
- stakeholders,
183
- repository,
184
- description: description || undefined
185
- };
186
- }
187
-
188
- /**
189
- * 環境変数を対話的に取得
190
- */
191
- async function getEnvConfig(
192
- rl: readline.Interface,
193
- existingEnv: Record<string, string> | null
194
- ): Promise<Record<string, string>> {
195
- console.log('\n🔐 環境変数設定');
196
- console.log('='.repeat(60));
197
-
198
- const env: Record<string, string> = {};
199
-
200
- // Atlassian設定
201
- console.log('\n📋 Atlassian設定');
202
- env.ATLASSIAN_URL = await question(
203
- rl,
204
- `Atlassian URL${existingEnv?.ATLASSIAN_URL ? ` [${existingEnv.ATLASSIAN_URL}]` : ''}: `
205
- ) || existingEnv?.ATLASSIAN_URL || '';
206
-
207
- env.ATLASSIAN_EMAIL = await question(
208
- rl,
209
- `Atlassian Email${existingEnv?.ATLASSIAN_EMAIL ? ` [${existingEnv.ATLASSIAN_EMAIL}]` : ''}: `
210
- ) || existingEnv?.ATLASSIAN_EMAIL || '';
211
-
212
- env.ATLASSIAN_API_TOKEN = await question(
213
- rl,
214
- `Atlassian API Token${existingEnv?.ATLASSIAN_API_TOKEN ? ' [***]' : ''}: `
215
- ) || existingEnv?.ATLASSIAN_API_TOKEN || '';
216
-
217
- // GitHub設定
218
- console.log('\n🐙 GitHub設定');
219
- env.GITHUB_ORG = await question(
220
- rl,
221
- `GitHub組織名${existingEnv?.GITHUB_ORG ? ` [${existingEnv.GITHUB_ORG}]` : ''}: `
222
- ) || existingEnv?.GITHUB_ORG || '';
223
-
224
- env.GITHUB_TOKEN = await question(
225
- rl,
226
- `GitHub Token${existingEnv?.GITHUB_TOKEN ? ' [***]' : ''}: `
227
- ) || existingEnv?.GITHUB_TOKEN || '';
228
-
229
- env.GITHUB_REPO = await question(
230
- rl,
231
- `GitHubリポジトリ(org/repo形式)${existingEnv?.GITHUB_REPO ? ` [${existingEnv.GITHUB_REPO}]` : ''}: `
232
- ) || existingEnv?.GITHUB_REPO || '';
233
-
234
- // Confluence設定
235
- console.log('\n📄 Confluence設定');
236
- env.CONFLUENCE_PRD_SPACE = await question(
237
- rl,
238
- `PRDスペースキー${existingEnv?.CONFLUENCE_PRD_SPACE ? ` [${existingEnv.CONFLUENCE_PRD_SPACE}]` : ''}: `
239
- ) || existingEnv?.CONFLUENCE_PRD_SPACE || '';
240
-
241
- env.CONFLUENCE_QA_SPACE = await question(
242
- rl,
243
- `QAスペースキー${existingEnv?.CONFLUENCE_QA_SPACE ? ` [${existingEnv.CONFLUENCE_QA_SPACE}]` : ''}: `
244
- ) || existingEnv?.CONFLUENCE_QA_SPACE || '';
245
-
246
- env.CONFLUENCE_RELEASE_SPACE = await question(
247
- rl,
248
- `RELEASEスペースキー${existingEnv?.CONFLUENCE_RELEASE_SPACE ? ` [${existingEnv.CONFLUENCE_RELEASE_SPACE}]` : ''}: `
249
- ) || existingEnv?.CONFLUENCE_RELEASE_SPACE || '';
250
-
251
- // JIRA設定
252
- console.log('\n📋 JIRA設定');
253
- env.JIRA_PROJECT_KEYS = await question(
254
- rl,
255
- `JIRAプロジェクトキー(カンマ区切り)${existingEnv?.JIRA_PROJECT_KEYS ? ` [${existingEnv.JIRA_PROJECT_KEYS}]` : ''}: `
256
- ) || existingEnv?.JIRA_PROJECT_KEYS || '';
257
-
258
- env.JIRA_ISSUE_TYPE_STORY = await question(
259
- rl,
260
- `JIRA Story Issue Type ID${existingEnv?.JIRA_ISSUE_TYPE_STORY ? ` [${existingEnv.JIRA_ISSUE_TYPE_STORY}]` : ''}: `
261
- ) || existingEnv?.JIRA_ISSUE_TYPE_STORY || '';
262
-
263
- env.JIRA_ISSUE_TYPE_SUBTASK = await question(
264
- rl,
265
- `JIRA Subtask Issue Type ID${existingEnv?.JIRA_ISSUE_TYPE_SUBTASK ? ` [${existingEnv.JIRA_ISSUE_TYPE_SUBTASK}]` : ''}: `
266
- ) || existingEnv?.JIRA_ISSUE_TYPE_SUBTASK || '';
267
-
268
- // Slack設定(オプション)
269
- const configureSlack = await confirm(rl, '\nSlack通知を設定しますか?', false);
270
- if (configureSlack) {
271
- env.SLACK_WEBHOOK_URL = await question(
272
- rl,
273
- `Slack Webhook URL${existingEnv?.SLACK_WEBHOOK_URL ? ' [***]' : ''}: `
274
- ) || existingEnv?.SLACK_WEBHOOK_URL || '';
275
- }
276
-
277
- return env;
278
- }
279
-
280
- /**
281
- * .envファイルを読み込む
282
- */
283
- function loadEnvFile(envPath: string): Record<string, string> {
284
- const env: Record<string, string> = {};
285
-
286
- if (!existsSync(envPath)) {
287
- return env;
288
- }
289
-
290
- try {
291
- const content = readFileSync(envPath, 'utf-8');
292
- const lines = content.split('\n');
293
-
294
- for (const line of lines) {
295
- const trimmed = line.trim();
296
- // コメント行と空行をスキップ
297
- if (!trimmed || trimmed.startsWith('#')) {
298
- continue;
299
- }
300
-
301
- const match = trimmed.match(/^([^=]+)=(.*)$/);
302
- if (match) {
303
- const key = match[1].trim();
304
- const value = match[2].trim();
305
- env[key] = value;
306
- }
307
- }
308
- } catch (error) {
309
- // 読み込みエラーを警告として表示
310
- console.warn(`⚠️ .envファイルの読み込みに失敗しました: ${error instanceof Error ? error.message : 'Unknown error'}`);
311
- }
312
-
313
- return env;
314
- }
315
-
316
- /**
317
- * .envファイルを保存
318
- */
319
- function saveEnvFile(envPath: string, env: Record<string, string>): void {
320
- const lines: string[] = [];
321
-
322
- // Atlassian設定
323
- lines.push('# Atlassian設定');
324
- lines.push(`ATLASSIAN_URL=${env.ATLASSIAN_URL || ''}`);
325
- lines.push(`ATLASSIAN_EMAIL=${env.ATLASSIAN_EMAIL || ''}`);
326
- lines.push(`ATLASSIAN_API_TOKEN=${env.ATLASSIAN_API_TOKEN || ''}`);
327
- lines.push('');
328
-
329
- // GitHub設定
330
- lines.push('# GitHub設定');
331
- lines.push(`GITHUB_ORG=${env.GITHUB_ORG || ''}`);
332
- lines.push(`GITHUB_TOKEN=${env.GITHUB_TOKEN || ''}`);
333
- lines.push(`GITHUB_REPO=${env.GITHUB_REPO || ''}`);
334
- lines.push('');
335
-
336
- // Confluence設定
337
- lines.push('# Confluence設定');
338
- lines.push(`CONFLUENCE_PRD_SPACE=${env.CONFLUENCE_PRD_SPACE || ''}`);
339
- lines.push(`CONFLUENCE_QA_SPACE=${env.CONFLUENCE_QA_SPACE || ''}`);
340
- lines.push(`CONFLUENCE_RELEASE_SPACE=${env.CONFLUENCE_RELEASE_SPACE || ''}`);
341
- lines.push('');
342
-
343
- // JIRA設定
344
- lines.push('# JIRA設定');
345
- lines.push(`JIRA_PROJECT_KEYS=${env.JIRA_PROJECT_KEYS || ''}`);
346
- lines.push('');
347
- lines.push('# JIRA Issue Type IDs(JIRAインスタンス固有の値 - 必須)');
348
- lines.push(`JIRA_ISSUE_TYPE_STORY=${env.JIRA_ISSUE_TYPE_STORY || ''}`);
349
- lines.push(`JIRA_ISSUE_TYPE_SUBTASK=${env.JIRA_ISSUE_TYPE_SUBTASK || ''}`);
350
- lines.push('');
351
-
352
- // Slack設定(オプション)
353
- if (env.SLACK_WEBHOOK_URL) {
354
- lines.push('# Slack通知(オプション)');
355
- lines.push(`SLACK_WEBHOOK_URL=${env.SLACK_WEBHOOK_URL}`);
356
- lines.push('');
357
- }
358
-
359
- writeFileSync(envPath, lines.join('\n'), 'utf-8');
360
- }
361
-
362
- /**
363
- * メイン処理
364
- */
365
- async function main(): Promise<number> {
366
- const rl = createInterface();
367
-
368
- try {
369
- console.log('🎨 Michi プロジェクト設定ツール');
370
- console.log('='.repeat(60));
371
- console.log('このツールで project.json と .env を対話的に設定できます。\n');
372
-
373
- // リポジトリルートを検出
374
- const repoRoot = findRepositoryRoot();
375
- const projectsDir = join(repoRoot, 'projects');
376
-
377
- // プロジェクト検出
378
- findCurrentProject();
379
- const allProjects = findAllProjects();
380
-
381
- let projectPath: string;
382
- let existingMeta: Partial<ProjectMetadata> | null = null;
383
- let isNewProject = false;
384
-
385
- if (allProjects.length === 0) {
386
- // プロジェクトが見つからない場合、新規プロジェクトを作成
387
- // プロジェクトIDを取得(ディレクトリ名から推測、または対話的に入力)
388
- const defaultProjectId = basename(repoRoot);
389
- let projectId: string;
390
- let isValid = false;
391
-
392
- while (!isValid) {
393
- const projectIdInput = await question(
394
- rl,
395
- `新規プロジェクトID [${defaultProjectId}]: `
396
- );
397
- projectId = projectIdInput.trim() || defaultProjectId;
398
-
399
- if (validateProjectId(projectId)) {
400
- isValid = true;
401
- } else {
402
- console.log('❌ 無効なプロジェクトIDです。英数字、ハイフン、アンダースコアのみ使用できます。');
403
- }
404
- }
405
-
406
- // projects/{project-id}/配下に作成
407
- projectPath = join(projectsDir, projectId!);
408
- isNewProject = true;
409
-
410
- console.log(`\n📁 新規プロジェクトを作成します: ${projectPath}\n`);
411
- } else if (allProjects.length === 1) {
412
- // 1つのプロジェクトのみ
413
- projectPath = allProjects[0].path;
414
- console.log(`📦 プロジェクト: ${allProjects[0].projectName} (${allProjects[0].projectId})`);
415
- console.log(`📁 パス: ${projectPath}\n`);
416
-
417
- // 既存のproject.jsonを読み込む
418
- const projectJsonPath = join(projectPath, '.kiro', 'project.json');
419
- if (existsSync(projectJsonPath)) {
420
- try {
421
- const content = readFileSync(projectJsonPath, 'utf-8');
422
- existingMeta = JSON.parse(content);
423
- } catch {
424
- // 読み込みエラーは無視
425
- }
426
- }
427
- } else {
428
- // 複数プロジェクトが見つかった場合、選択
429
- const selected = await selectProject(allProjects, question.bind(null, rl));
430
- if (!selected) {
431
- console.log('プロジェクトが選択されませんでした。');
432
- return 0;
433
- }
434
-
435
- projectPath = selected.path;
436
- console.log(`\n📦 選択されたプロジェクト: ${selected.projectName} (${selected.projectId})`);
437
- console.log(`📁 パス: ${projectPath}\n`);
438
-
439
- // 既存のproject.jsonを読み込む
440
- const projectJsonPath = join(projectPath, '.kiro', 'project.json');
441
- if (existsSync(projectJsonPath)) {
442
- try {
443
- const content = readFileSync(projectJsonPath, 'utf-8');
444
- existingMeta = JSON.parse(content);
445
- } catch {
446
- // 読み込みエラーは無視
447
- }
448
- }
449
- }
450
-
451
- // 設定する項目を選択
452
- const configureProject = await confirm(rl, 'project.jsonを設定しますか?', true);
453
- const configureEnv = await confirm(rl, '.envを設定しますか?', true);
454
-
455
- if (!configureProject && !configureEnv) {
456
- console.log('設定する項目がありません。');
457
- return 0;
458
- }
459
-
460
- let projectMeta: ProjectMetadata | null = null;
461
- let envConfig: Record<string, string> | null = null;
462
-
463
- // project.json設定
464
- if (configureProject) {
465
- projectMeta = await getProjectMetadata(rl, existingMeta, projectPath);
466
- }
467
-
468
- // .env設定
469
- if (configureEnv) {
470
- const envPath = join(projectPath, '.env');
471
- const existingEnv = loadEnvFile(envPath);
472
- envConfig = await getEnvConfig(rl, existingEnv);
473
- }
474
-
475
- // 確認
476
- console.log('\n📋 設定内容の確認');
477
- console.log('='.repeat(60));
478
-
479
- if (projectMeta) {
480
- console.log('\n📦 project.json:');
481
- console.log(JSON.stringify(projectMeta, null, 2));
482
- }
483
-
484
- if (envConfig) {
485
- console.log('\n🔐 .env:');
486
- // 機密情報をマスク
487
- const maskedEnv = { ...envConfig };
488
- if (maskedEnv.ATLASSIAN_API_TOKEN) {
489
- maskedEnv.ATLASSIAN_API_TOKEN = '***';
490
- }
491
- if (maskedEnv.GITHUB_TOKEN) {
492
- maskedEnv.GITHUB_TOKEN = '***';
493
- }
494
- if (maskedEnv.SLACK_WEBHOOK_URL) {
495
- maskedEnv.SLACK_WEBHOOK_URL = '***';
496
- }
497
- Object.entries(maskedEnv).forEach(([key, value]) => {
498
- if (value) {
499
- console.log(`${key}=${value}`);
500
- }
501
- });
502
- }
503
-
504
- console.log('');
505
- const confirmSave = await confirm(rl, 'この設定を保存しますか?', true);
506
-
507
- if (!confirmSave) {
508
- console.log('保存をキャンセルしました。');
509
- return 0;
510
- }
511
-
512
- // 保存
513
- if (projectMeta) {
514
- // 新規プロジェクトの場合はディレクトリを作成
515
- if (isNewProject) {
516
- if (!existsSync(projectsDir)) {
517
- mkdirSync(projectsDir, { recursive: true });
518
- }
519
- if (!existsSync(projectPath)) {
520
- mkdirSync(projectPath, { recursive: true });
521
- }
522
- }
523
-
524
- const projectJsonPath = join(projectPath, '.kiro', 'project.json');
525
- const projectJsonDir = join(projectPath, '.kiro');
526
-
527
- if (!existsSync(projectJsonDir)) {
528
- mkdirSync(projectJsonDir, { recursive: true });
529
- }
530
-
531
- writeFileSync(projectJsonPath, JSON.stringify(projectMeta, null, 2) + '\n', 'utf-8');
532
- console.log(`\n✅ project.jsonを保存しました: ${projectJsonPath}`);
533
- }
534
-
535
- if (envConfig) {
536
- const envPath = join(projectPath, '.env');
537
- saveEnvFile(envPath, envConfig);
538
- console.log(`✅ .envを保存しました: ${envPath}`);
539
- }
540
-
541
- console.log('\n🎉 設定が完了しました!');
542
- return 0;
543
-
544
- } catch (error) {
545
- console.error('❌ エラーが発生しました:', error instanceof Error ? error.message : error);
546
- return 1;
547
- } finally {
548
- rl.close();
549
- }
550
- }
551
-
552
- // CLI実行
553
- if (import.meta.url === `file://${process.argv[1]}`) {
554
- main()
555
- .then(exitCode => {
556
- process.exit(exitCode);
557
- })
558
- .catch(error => {
559
- console.error('❌ 予期しないエラー:', error);
560
- process.exit(1);
561
- });
562
- }
563
-
564
- export { main as setupInteractive };
565
-