@sk8metal/michi-cli 0.0.1 → 0.0.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 (57) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +60 -24
  3. package/dist/scripts/__tests__/validate-phase.test.d.ts +5 -0
  4. package/dist/scripts/__tests__/validate-phase.test.d.ts.map +1 -0
  5. package/dist/scripts/__tests__/validate-phase.test.js +162 -0
  6. package/dist/scripts/__tests__/validate-phase.test.js.map +1 -0
  7. package/dist/scripts/utils/__tests__/config-validator.test.d.ts +5 -0
  8. package/dist/scripts/utils/__tests__/config-validator.test.d.ts.map +1 -0
  9. package/dist/scripts/utils/__tests__/config-validator.test.js +247 -0
  10. package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -0
  11. package/dist/scripts/utils/__tests__/feature-name-validator.test.d.ts +5 -0
  12. package/dist/scripts/utils/__tests__/feature-name-validator.test.d.ts.map +1 -0
  13. package/dist/scripts/utils/__tests__/feature-name-validator.test.js +106 -0
  14. package/dist/scripts/utils/__tests__/feature-name-validator.test.js.map +1 -0
  15. package/dist/scripts/utils/config-loader.js +1 -1
  16. package/dist/scripts/utils/config-loader.js.map +1 -1
  17. package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
  18. package/dist/scripts/utils/confluence-hierarchy.js +2 -1
  19. package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
  20. package/dist/src/__tests__/cli.test.d.ts +5 -0
  21. package/dist/src/__tests__/cli.test.d.ts.map +1 -0
  22. package/dist/src/__tests__/cli.test.js +58 -0
  23. package/dist/src/__tests__/cli.test.js.map +1 -0
  24. package/dist/src/cli.js +0 -0
  25. package/dist/vitest.config.d.ts +3 -0
  26. package/dist/vitest.config.d.ts.map +1 -0
  27. package/dist/vitest.config.js +29 -0
  28. package/dist/vitest.config.js.map +1 -0
  29. package/docs/setup.md +1 -1
  30. package/package.json +8 -4
  31. package/scripts/__tests__/README.md +101 -0
  32. package/scripts/__tests__/validate-phase.test.ts +185 -0
  33. package/scripts/config/config-schema.ts +130 -0
  34. package/scripts/config/default-config.json +57 -0
  35. package/scripts/config-interactive.ts +494 -0
  36. package/scripts/confluence-sync.ts +503 -0
  37. package/scripts/create-project.ts +293 -0
  38. package/scripts/jira-sync.ts +644 -0
  39. package/scripts/list-projects.ts +85 -0
  40. package/scripts/markdown-to-confluence.ts +161 -0
  41. package/scripts/multi-project-estimate.ts +255 -0
  42. package/scripts/phase-runner.ts +303 -0
  43. package/scripts/pr-automation.ts +67 -0
  44. package/scripts/pre-flight-check.ts +285 -0
  45. package/scripts/resource-dashboard.ts +124 -0
  46. package/scripts/setup-env.sh +52 -0
  47. package/scripts/setup-existing-project.ts +381 -0
  48. package/scripts/setup-existing.sh +145 -0
  49. package/scripts/utils/__tests__/config-validator.test.ts +302 -0
  50. package/scripts/utils/__tests__/feature-name-validator.test.ts +129 -0
  51. package/scripts/utils/config-loader.ts +326 -0
  52. package/scripts/utils/config-validator.ts +347 -0
  53. package/scripts/utils/confluence-hierarchy.ts +855 -0
  54. package/scripts/utils/feature-name-validator.ts +135 -0
  55. package/scripts/utils/project-meta.ts +69 -0
  56. package/scripts/validate-phase.ts +279 -0
  57. package/scripts/workflow-orchestrator.ts +178 -0
@@ -0,0 +1,303 @@
1
+ /**
2
+ * フェーズランナー
3
+ * 各フェーズを実行し、Confluence/JIRA作成を確実に実行
4
+ */
5
+
6
+ import { existsSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { syncToConfluence } from './confluence-sync.js';
9
+ import { syncTasksToJIRA } from './jira-sync.js';
10
+ import { validatePhase } from './validate-phase.js';
11
+ import { runPreFlightCheck } from './pre-flight-check.js';
12
+ import { validateFeatureNameOrThrow } from './utils/feature-name-validator.js';
13
+
14
+ type Phase = 'requirements' | 'design' | 'tasks';
15
+
16
+ interface PhaseRunResult {
17
+ phase: Phase;
18
+ success: boolean;
19
+ confluenceCreated: boolean;
20
+ jiraCreated: boolean;
21
+ validationPassed: boolean;
22
+ errors: string[];
23
+ }
24
+
25
+ /**
26
+ * 要件定義フェーズを実行
27
+ */
28
+ async function runRequirementsPhase(feature: string): Promise<PhaseRunResult> {
29
+ console.log('\n📋 Phase: Requirements(要件定義)');
30
+ console.log('='.repeat(60));
31
+
32
+ const errors: string[] = [];
33
+ let confluenceCreated = false;
34
+ let confluenceUrl: string | null = null;
35
+
36
+ // Step 1: requirements.md存在確認
37
+ const requirementsPath = join(process.cwd(), '.kiro', 'specs', feature, 'requirements.md');
38
+ if (!existsSync(requirementsPath)) {
39
+ errors.push('requirements.mdが存在しません。先に/kiro:spec-requirements を実行してください');
40
+ return {
41
+ phase: 'requirements',
42
+ success: false,
43
+ confluenceCreated: false,
44
+ jiraCreated: false,
45
+ validationPassed: false,
46
+ errors
47
+ };
48
+ }
49
+
50
+ console.log('✅ requirements.md 存在確認');
51
+
52
+ // Step 2: Confluenceページ作成
53
+ console.log('\n📤 Confluenceページ作成中...');
54
+ try {
55
+ confluenceUrl = await syncToConfluence(feature, 'requirements');
56
+ confluenceCreated = true;
57
+ console.log('✅ Confluenceページ作成成功');
58
+ } catch (error: any) {
59
+ errors.push(`Confluenceページ作成失敗: ${error.message}`);
60
+ console.error('❌ Confluenceページ作成失敗:', error.message);
61
+ }
62
+
63
+ // Step 3: バリデーション
64
+ console.log('\n🔍 バリデーション実行中...');
65
+ const validation = validatePhase(feature, 'requirements');
66
+ errors.push(...validation.errors);
67
+
68
+ // Step 4: 結果サマリー
69
+ console.log('\n' + '='.repeat(60));
70
+ console.log('📊 要件定義フェーズ完了チェック:');
71
+ console.log(' ✅ requirements.md: 作成済み');
72
+ console.log(` ${confluenceCreated ? '✅' : '❌'} Confluenceページ: ${confluenceCreated ? '作成済み' : '未作成'}`);
73
+ console.log(` ${validation.valid ? '✅' : '❌'} バリデーション: ${validation.valid ? '成功' : '失敗'}`);
74
+
75
+ if (validation.valid && confluenceCreated) {
76
+ console.log('\n🎉 要件定義フェーズが完了しました!');
77
+ console.log('📢 PMや部長にConfluenceでレビューを依頼してください');
78
+ if (confluenceUrl) {
79
+ console.log(`📄 Confluence: ${confluenceUrl}`);
80
+ } else {
81
+ const baseUrl = process.env.ATLASSIAN_URL || 'https://your-site.atlassian.net';
82
+ console.log(`📄 Confluence: ${baseUrl}/wiki/spaces/(URLは上記のログを参照)`);
83
+ }
84
+ }
85
+
86
+ return {
87
+ phase: 'requirements',
88
+ success: validation.valid && confluenceCreated,
89
+ confluenceCreated,
90
+ jiraCreated: false,
91
+ validationPassed: validation.valid,
92
+ errors
93
+ };
94
+ }
95
+
96
+ /**
97
+ * 設計フェーズを実行
98
+ */
99
+ async function runDesignPhase(feature: string): Promise<PhaseRunResult> {
100
+ console.log('\n🏗️ Phase: Design(設計)');
101
+ console.log('='.repeat(60));
102
+
103
+ const errors: string[] = [];
104
+ let confluenceCreated = false;
105
+ let confluenceUrl: string | null = null;
106
+
107
+ // Step 1: design.md存在確認
108
+ const designPath = join(process.cwd(), '.kiro', 'specs', feature, 'design.md');
109
+ if (!existsSync(designPath)) {
110
+ errors.push('design.mdが存在しません。先に/kiro:spec-design を実行してください');
111
+ return {
112
+ phase: 'design',
113
+ success: false,
114
+ confluenceCreated: false,
115
+ jiraCreated: false,
116
+ validationPassed: false,
117
+ errors
118
+ };
119
+ }
120
+
121
+ console.log('✅ design.md 存在確認');
122
+
123
+ // Step 2: Confluenceページ作成
124
+ console.log('\n📤 Confluenceページ作成中...');
125
+ try {
126
+ confluenceUrl = await syncToConfluence(feature, 'design');
127
+ confluenceCreated = true;
128
+ console.log('✅ Confluenceページ作成成功');
129
+ } catch (error: any) {
130
+ errors.push(`Confluenceページ作成失敗: ${error.message}`);
131
+ console.error('❌ Confluenceページ作成失敗:', error.message);
132
+ }
133
+
134
+ // Step 3: バリデーション
135
+ console.log('\n🔍 バリデーション実行中...');
136
+ const validation = validatePhase(feature, 'design');
137
+ errors.push(...validation.errors);
138
+
139
+ // Step 4: 結果サマリー
140
+ console.log('\n' + '='.repeat(60));
141
+ console.log('📊 設計フェーズ完了チェック:');
142
+ console.log(' ✅ design.md: 作成済み');
143
+ console.log(` ${confluenceCreated ? '✅' : '❌'} Confluenceページ: ${confluenceCreated ? '作成済み' : '未作成'}`);
144
+ console.log(` ${validation.valid ? '✅' : '❌'} バリデーション: ${validation.valid ? '成功' : '失敗'}`);
145
+
146
+ if (validation.valid && confluenceCreated) {
147
+ console.log('\n🎉 設計フェーズが完了しました!');
148
+ console.log('📢 PMや部長にConfluenceでレビューを依頼してください');
149
+ if (confluenceUrl) {
150
+ console.log(`📄 Confluence: ${confluenceUrl}`);
151
+ } else {
152
+ const baseUrl = process.env.ATLASSIAN_URL || 'https://your-site.atlassian.net';
153
+ console.log(`📄 Confluence: ${baseUrl}/wiki/spaces/(URLは上記のログを参照)`);
154
+ }
155
+ }
156
+
157
+ return {
158
+ phase: 'design',
159
+ success: validation.valid && confluenceCreated,
160
+ confluenceCreated,
161
+ jiraCreated: false,
162
+ validationPassed: validation.valid,
163
+ errors
164
+ };
165
+ }
166
+
167
+ /**
168
+ * タスク分割フェーズを実行
169
+ */
170
+ async function runTasksPhase(feature: string): Promise<PhaseRunResult> {
171
+ console.log('\n📝 Phase: Tasks(タスク分割)');
172
+ console.log('='.repeat(60));
173
+
174
+ const errors: string[] = [];
175
+ let jiraCreated = false;
176
+
177
+ // Step 0: プリフライトチェック
178
+ console.log('\n🔍 プリフライトチェック...');
179
+ const preFlightResult = await runPreFlightCheck('jira');
180
+
181
+ if (!preFlightResult.valid) {
182
+ console.log('\n❌ プリフライトチェック失敗:');
183
+ preFlightResult.errors.forEach(e => console.log(` ${e}`));
184
+ return {
185
+ phase: 'tasks',
186
+ success: false,
187
+ confluenceCreated: false,
188
+ jiraCreated: false,
189
+ validationPassed: false,
190
+ errors: preFlightResult.errors
191
+ };
192
+ }
193
+
194
+ console.log('✅ プリフライトチェック成功');
195
+
196
+ // Step 1: tasks.md存在確認
197
+ const tasksPath = join(process.cwd(), '.kiro', 'specs', feature, 'tasks.md');
198
+ if (!existsSync(tasksPath)) {
199
+ errors.push('tasks.mdが存在しません。先に/kiro:spec-tasks を実行してください');
200
+ return {
201
+ phase: 'tasks',
202
+ success: false,
203
+ confluenceCreated: false,
204
+ jiraCreated: false,
205
+ validationPassed: false,
206
+ errors
207
+ };
208
+ }
209
+
210
+ console.log('✅ tasks.md 存在確認');
211
+
212
+ // Step 2: JIRA Epic/Story作成
213
+ console.log('\n📤 JIRA Epic/Story作成中...');
214
+ try {
215
+ await syncTasksToJIRA(feature);
216
+ jiraCreated = true;
217
+ console.log('✅ JIRA Epic/Story作成成功');
218
+ } catch (error: any) {
219
+ errors.push(`JIRA作成失敗: ${error.message}`);
220
+ console.error('❌ JIRA作成失敗:', error.message);
221
+ }
222
+
223
+ // Step 3: バリデーション
224
+ console.log('\n🔍 バリデーション実行中...');
225
+ const validation = validatePhase(feature, 'tasks');
226
+ errors.push(...validation.errors);
227
+
228
+ // Step 4: 結果サマリー
229
+ console.log('\n' + '='.repeat(60));
230
+ console.log('📊 タスク分割フェーズ完了チェック:');
231
+ console.log(' ✅ tasks.md: 作成済み');
232
+ console.log(` ${jiraCreated ? '✅' : '❌'} JIRA Epic/Story: ${jiraCreated ? '作成済み' : '未作成'}`);
233
+ console.log(` ${validation.valid ? '✅' : '❌'} バリデーション: ${validation.valid ? '成功' : '失敗'}`);
234
+
235
+ if (validation.valid && jiraCreated) {
236
+ console.log('\n🎉 タスク分割フェーズが完了しました!');
237
+ console.log('📢 開発チームに実装開始を通知してください');
238
+ console.log('🚀 次のステップ: /kiro:spec-impl <feature>');
239
+ }
240
+
241
+ return {
242
+ phase: 'tasks',
243
+ success: validation.valid && jiraCreated,
244
+ confluenceCreated: false,
245
+ jiraCreated,
246
+ validationPassed: validation.valid,
247
+ errors
248
+ };
249
+ }
250
+
251
+ /**
252
+ * フェーズを実行
253
+ */
254
+ export async function runPhase(feature: string, phase: Phase): Promise<PhaseRunResult> {
255
+ // feature名のバリデーション(必須)
256
+ validateFeatureNameOrThrow(feature);
257
+
258
+ switch (phase) {
259
+ case 'requirements':
260
+ return await runRequirementsPhase(feature);
261
+ case 'design':
262
+ return await runDesignPhase(feature);
263
+ case 'tasks':
264
+ return await runTasksPhase(feature);
265
+ default:
266
+ throw new Error(`Unknown phase: ${phase}`);
267
+ }
268
+ }
269
+
270
+ // CLI実行
271
+ if (import.meta.url === `file://${process.argv[1]}`) {
272
+ const args = process.argv.slice(2);
273
+
274
+ if (args.length < 2) {
275
+ console.error('Usage: npm run phase:run <feature> <phase>');
276
+ console.error('Example: npm run phase:run calculator-app requirements');
277
+ console.error('Phases: requirements, design, tasks');
278
+ process.exit(1);
279
+ }
280
+
281
+ const [feature, phase] = args;
282
+
283
+ if (!['requirements', 'design', 'tasks'].includes(phase)) {
284
+ console.error('Invalid phase. Must be: requirements, design, or tasks');
285
+ process.exit(1);
286
+ }
287
+
288
+ runPhase(feature, phase as Phase)
289
+ .then((result) => {
290
+ if (result.success) {
291
+ console.log('\n✅ フェーズ完了');
292
+ process.exit(0);
293
+ } else {
294
+ console.log('\n❌ フェーズ未完了(エラーを確認してください)');
295
+ process.exit(1);
296
+ }
297
+ })
298
+ .catch((error) => {
299
+ console.error(`\n❌ フェーズ実行エラー: ${error.message}`);
300
+ process.exit(1);
301
+ });
302
+ }
303
+
@@ -0,0 +1,67 @@
1
+ /**
2
+ * GitHub PR自動化スクリプト
3
+ */
4
+
5
+ import { Octokit } from '@octokit/rest';
6
+ import { config } from 'dotenv';
7
+ import { loadProjectMeta } from './utils/project-meta.js';
8
+
9
+ config();
10
+
11
+ interface PROptions {
12
+ branch: string;
13
+ title: string;
14
+ body: string;
15
+ base?: string;
16
+ }
17
+
18
+ async function createPR(options: PROptions): Promise<void> {
19
+ const token = process.env.GITHUB_TOKEN;
20
+ const repo = process.env.GITHUB_REPO;
21
+
22
+ if (!token || !repo) {
23
+ throw new Error('Missing GitHub credentials');
24
+ }
25
+
26
+ const [owner, repoName] = repo.split('/');
27
+ const octokit = new Octokit({ auth: token });
28
+
29
+ const { branch, title, body, base = 'main' } = options;
30
+
31
+ console.log(`Creating PR: ${title}`);
32
+
33
+ const pr = await octokit.pulls.create({
34
+ owner,
35
+ repo: repoName,
36
+ title,
37
+ body,
38
+ head: branch,
39
+ base
40
+ });
41
+
42
+ console.log(`✅ PR created: ${pr.data.html_url}`);
43
+ }
44
+
45
+ // CLI実行
46
+ if (import.meta.url === `file://${process.argv[1]}`) {
47
+ const args = process.argv.slice(2);
48
+
49
+ if (args.length === 0) {
50
+ console.error('Usage: npm run github:create-pr <branch> [title]');
51
+ process.exit(1);
52
+ }
53
+
54
+ const branch = args[0];
55
+ const title = args[1] || `feat: ${branch}`;
56
+ const body = 'Auto-generated PR';
57
+
58
+ createPR({ branch, title, body })
59
+ .then(() => process.exit(0))
60
+ .catch((error) => {
61
+ console.error('❌ PR creation failed:', error.message);
62
+ process.exit(1);
63
+ });
64
+ }
65
+
66
+ export { createPR };
67
+
@@ -0,0 +1,285 @@
1
+ /**
2
+ * プリフライトチェック
3
+ * スクリプト実行前に必要な設定が揃っているか確認
4
+ */
5
+
6
+ import { existsSync, readFileSync } from 'fs';
7
+ import { join } from 'path';
8
+ import axios from 'axios';
9
+ import { config } from 'dotenv';
10
+
11
+ config();
12
+
13
+ interface PreFlightResult {
14
+ valid: boolean;
15
+ errors: string[];
16
+ warnings: string[];
17
+ }
18
+
19
+ /**
20
+ * .env設定をチェック
21
+ */
22
+ function checkEnvConfig(): { errors: string[], warnings: string[] } {
23
+ const errors: string[] = [];
24
+ const warnings: string[] = [];
25
+
26
+ // .envファイル存在チェック
27
+ if (!existsSync('.env')) {
28
+ errors.push('❌ .envファイルが存在しません');
29
+ errors.push(' → テンプレートからコピー: cp .env.example .env');
30
+ errors.push(' → 編集: vim .env(認証情報を設定)');
31
+ errors.push(' → API Token取得: https://id.atlassian.com/manage-profile/security/api-tokens');
32
+ return { errors, warnings };
33
+ }
34
+
35
+ // 必須環境変数チェック
36
+ const required = [
37
+ 'ATLASSIAN_URL',
38
+ 'ATLASSIAN_EMAIL',
39
+ 'ATLASSIAN_API_TOKEN'
40
+ ];
41
+
42
+ const missing = required.filter(key => !process.env[key]);
43
+
44
+ if (missing.length > 0) {
45
+ errors.push(`❌ .envに必須項目が設定されていません: ${missing.join(', ')}`);
46
+ errors.push(' → 編集: vim .env');
47
+ errors.push(' → API Token取得: https://id.atlassian.com/manage-profile/security/api-tokens');
48
+ errors.push('');
49
+ errors.push(' 必須項目:');
50
+ errors.push(' ATLASSIAN_URL=https://your-site.atlassian.net');
51
+ errors.push(' ATLASSIAN_EMAIL=your-email@example.com');
52
+ errors.push(' ATLASSIAN_API_TOKEN=your-api-token');
53
+ }
54
+
55
+ // オプション環境変数の警告
56
+ if (!process.env.CONFLUENCE_PRD_SPACE) {
57
+ warnings.push('⚠️ CONFLUENCE_PRD_SPACEが未設定(デフォルト: PRD)');
58
+ warnings.push(` → スペース一覧: ${process.env.ATLASSIAN_URL}/wiki/spaces`);
59
+ }
60
+
61
+ return { errors, warnings };
62
+ }
63
+
64
+ /**
65
+ * project.jsonをチェック
66
+ */
67
+ function checkProjectJson(): { errors: string[], warnings: string[] } {
68
+ const errors: string[] = [];
69
+ const warnings: string[] = [];
70
+
71
+ const projectJsonPath = join(process.cwd(), '.kiro', 'project.json');
72
+
73
+ if (!existsSync(projectJsonPath)) {
74
+ errors.push('❌ .kiro/project.json が存在しません');
75
+ errors.push(' → このディレクトリはMichiプロジェクトではありません');
76
+ errors.push(' → セットアップ: npm run setup-existing(michi-practice1の場合)');
77
+ errors.push(' → または、Michiプロジェクトのディレクトリに移動してください');
78
+ return { errors, warnings };
79
+ }
80
+
81
+ let projectMeta: any;
82
+ try {
83
+ projectMeta = JSON.parse(readFileSync(projectJsonPath, 'utf-8'));
84
+ } catch (error) {
85
+ errors.push('❌ project.json のパースに失敗しました');
86
+ return { errors, warnings };
87
+ }
88
+
89
+ // 必須フィールドチェック
90
+ const required = ['projectId', 'projectName', 'jiraProjectKey'];
91
+ const missing = required.filter(key => !projectMeta[key]);
92
+
93
+ if (missing.length > 0) {
94
+ errors.push(`❌ project.jsonに必須項目がありません: ${missing.join(', ')}`);
95
+ }
96
+
97
+ return { errors, warnings };
98
+ }
99
+
100
+ /**
101
+ * Confluenceスペース存在チェック(API呼び出し)
102
+ */
103
+ async function checkConfluenceSpace(spaceKey: string): Promise<{ errors: string[], warnings: string[] }> {
104
+ const errors: string[] = [];
105
+ const warnings: string[] = [];
106
+
107
+ const url = process.env.ATLASSIAN_URL;
108
+ const email = process.env.ATLASSIAN_EMAIL;
109
+ const apiToken = process.env.ATLASSIAN_API_TOKEN;
110
+
111
+ if (!url || !email || !apiToken) {
112
+ errors.push('❌ .env設定が不完全なため、Confluenceスペースチェックをスキップ');
113
+ return { errors, warnings };
114
+ }
115
+
116
+ try {
117
+ const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');
118
+ const response = await axios.get(`${url}/wiki/rest/api/space/${spaceKey}`, {
119
+ headers: {
120
+ 'Authorization': `Basic ${auth}`,
121
+ 'Content-Type': 'application/json'
122
+ }
123
+ });
124
+
125
+ if (response.data) {
126
+ console.log(` ✅ Confluenceスペース確認: ${spaceKey} (${response.data.name})`);
127
+ }
128
+ } catch (error: any) {
129
+ if (error.response?.status === 404) {
130
+ errors.push(`❌ Confluenceスペースが存在しません: ${spaceKey}`);
131
+ errors.push(` → Confluenceで新しいスペースを作成: ${url}/wiki/spaces`);
132
+ errors.push(' → または、.envのCONFLUENCE_PRD_SPACEを修正してください');
133
+ } else if (error.response?.status === 401) {
134
+ errors.push('❌ Confluence認証エラー(.envの認証情報を確認)');
135
+ errors.push(` → API Token管理: ${url.replace('atlassian.net', 'atlassian.net/manage/profile/security/api-tokens')}`);
136
+ } else {
137
+ warnings.push(`⚠️ Confluenceスペースチェック失敗: ${error.message}`);
138
+ }
139
+ }
140
+
141
+ return { errors, warnings };
142
+ }
143
+
144
+ /**
145
+ * JIRAプロジェクト存在チェック(API呼び出し)
146
+ */
147
+ async function checkJiraProject(projectKey: string): Promise<{ errors: string[], warnings: string[] }> {
148
+ const errors: string[] = [];
149
+ const warnings: string[] = [];
150
+
151
+ const url = process.env.ATLASSIAN_URL;
152
+ const email = process.env.ATLASSIAN_EMAIL;
153
+ const apiToken = process.env.ATLASSIAN_API_TOKEN;
154
+
155
+ if (!url || !email || !apiToken) {
156
+ errors.push('❌ .env設定が不完全なため、JIRAプロジェクトチェックをスキップ');
157
+ return { errors, warnings };
158
+ }
159
+
160
+ try {
161
+ const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');
162
+ const response = await axios.get(`${url}/rest/api/3/project/${projectKey}`, {
163
+ headers: {
164
+ 'Authorization': `Basic ${auth}`,
165
+ 'Content-Type': 'application/json'
166
+ }
167
+ });
168
+
169
+ if (response.data) {
170
+ console.log(` ✅ JIRAプロジェクト確認: ${projectKey} (${response.data.name})`);
171
+ }
172
+ } catch (error: any) {
173
+ if (error.response?.status === 404) {
174
+ errors.push(`❌ JIRAプロジェクトが存在しません: ${projectKey}`);
175
+ errors.push(` → JIRAプロジェクト作成: ${url}/jira/projects/create`);
176
+ errors.push(` → プロジェクト一覧: ${url}/jira/settings/projects`);
177
+ errors.push(' → または、.kiro/project.jsonのjiraProjectKeyを修正してください');
178
+ errors.push(` 現在の設定: "${projectKey}" → 実際に存在するキーに変更`);
179
+ } else if (error.response?.status === 401) {
180
+ errors.push('❌ JIRA認証エラー(.envの認証情報を確認)');
181
+ errors.push(` → API Token管理: https://id.atlassian.com/manage-profile/security/api-tokens`);
182
+ } else {
183
+ warnings.push(`⚠️ JIRAプロジェクトチェック失敗: ${error.message}`);
184
+ }
185
+ }
186
+
187
+ return { errors, warnings };
188
+ }
189
+
190
+ /**
191
+ * プリフライトチェック実行
192
+ */
193
+ export async function runPreFlightCheck(phase: 'confluence' | 'jira' | 'all'): Promise<PreFlightResult> {
194
+ console.log('\n🔍 プリフライトチェック実行中...\n');
195
+
196
+ const allErrors: string[] = [];
197
+ const allWarnings: string[] = [];
198
+
199
+ // 1. .env設定チェック
200
+ console.log('📋 Step 1: .env設定チェック');
201
+ const envCheck = checkEnvConfig();
202
+ allErrors.push(...envCheck.errors);
203
+ allWarnings.push(...envCheck.warnings);
204
+
205
+ if (envCheck.errors.length === 0) {
206
+ console.log(' ✅ .env設定OK');
207
+ }
208
+
209
+ // 2. project.jsonチェック
210
+ console.log('\n📋 Step 2: project.json設定チェック');
211
+ const projectCheck = checkProjectJson();
212
+ allErrors.push(...projectCheck.errors);
213
+ allWarnings.push(...projectCheck.warnings);
214
+
215
+ if (projectCheck.errors.length === 0) {
216
+ console.log(' ✅ project.json設定OK');
217
+ }
218
+
219
+ // .envまたはproject.jsonエラーがあれば、ここで中断
220
+ if (allErrors.length > 0) {
221
+ return { valid: false, errors: allErrors, warnings: allWarnings };
222
+ }
223
+
224
+ // 3. Confluenceスペースチェック(API呼び出し)
225
+ if (phase === 'confluence' || phase === 'all') {
226
+ console.log('\n📋 Step 3: Confluenceスペース存在チェック');
227
+ const spaceKey = process.env.CONFLUENCE_PRD_SPACE || 'PRD';
228
+ const spaceCheck = await checkConfluenceSpace(spaceKey);
229
+ allErrors.push(...spaceCheck.errors);
230
+ allWarnings.push(...spaceCheck.warnings);
231
+ }
232
+
233
+ // 4. JIRAプロジェクトチェック(API呼び出し)
234
+ if (phase === 'jira' || phase === 'all') {
235
+ console.log('\n📋 Step 4: JIRAプロジェクト存在チェック');
236
+ const projectJsonPath = join(process.cwd(), '.kiro', 'project.json');
237
+ const projectMeta = JSON.parse(readFileSync(projectJsonPath, 'utf-8'));
238
+ const jiraCheck = await checkJiraProject(projectMeta.jiraProjectKey);
239
+ allErrors.push(...jiraCheck.errors);
240
+ allWarnings.push(...jiraCheck.warnings);
241
+ }
242
+
243
+ return {
244
+ valid: allErrors.length === 0,
245
+ errors: allErrors,
246
+ warnings: allWarnings
247
+ };
248
+ }
249
+
250
+ // CLI実行
251
+ if (import.meta.url === `file://${process.argv[1]}`) {
252
+ const args = process.argv.slice(2);
253
+ const phase = (args[0] as 'confluence' | 'jira' | 'all') || 'all';
254
+
255
+ if (!['confluence', 'jira', 'all'].includes(phase)) {
256
+ console.error('Usage: npm run preflight [confluence|jira|all]');
257
+ process.exit(1);
258
+ }
259
+
260
+ runPreFlightCheck(phase)
261
+ .then((result) => {
262
+ console.log('\n' + '='.repeat(60));
263
+
264
+ if (result.warnings.length > 0) {
265
+ console.log('\n⚠️ 警告:');
266
+ result.warnings.forEach(w => console.log(` ${w}`));
267
+ }
268
+
269
+ if (result.errors.length > 0) {
270
+ console.log('\n❌ エラー:');
271
+ result.errors.forEach(e => console.log(` ${e}`));
272
+ console.log('\n❌ プリフライトチェック失敗');
273
+ process.exit(1);
274
+ } else {
275
+ console.log('\n✅ プリフライトチェック成功');
276
+ console.log(' すべての設定が正しく構成されています');
277
+ process.exit(0);
278
+ }
279
+ })
280
+ .catch((error) => {
281
+ console.error(`\n❌ チェックエラー: ${error.message}`);
282
+ process.exit(1);
283
+ });
284
+ }
285
+