@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
@@ -3,7 +3,7 @@
3
3
  * 各フェーズを実行し、Confluence/JIRA作成を確実に実行
4
4
  */
5
5
 
6
- import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs';
6
+ import { existsSync, writeFileSync, readFileSync } from 'fs';
7
7
  import { join, relative } from 'path';
8
8
  import { syncToConfluence } from './confluence-sync.js';
9
9
  import { syncTasksToJIRA } from './jira-sync.js';
@@ -17,8 +17,6 @@ import inquirer from 'inquirer';
17
17
  type Phase =
18
18
  | 'requirements'
19
19
  | 'design'
20
- | 'test-type-selection'
21
- | 'test-spec'
22
20
  | 'tasks'
23
21
  | 'environment-setup'
24
22
  | 'phase-a'
@@ -74,9 +72,10 @@ async function runRequirementsPhase(feature: string): Promise<PhaseRunResult> {
74
72
  confluenceUrl = await syncToConfluence(feature, 'requirements');
75
73
  confluenceCreated = true;
76
74
  console.log('✅ Confluenceページ作成成功');
77
- } catch (error: any) {
78
- errors.push(`Confluenceページ作成失敗: ${error.message}`);
79
- console.error('❌ Confluenceページ作成失敗:', error.message);
75
+ } catch (error: unknown) {
76
+ const message = error instanceof Error ? error.message : String(error);
77
+ errors.push(`Confluenceページ作成失敗: ${message}`);
78
+ console.error('❌ Confluenceページ作成失敗:', message);
80
79
  }
81
80
 
82
81
  // Step 3: バリデーション
@@ -160,9 +159,10 @@ async function runDesignPhase(feature: string): Promise<PhaseRunResult> {
160
159
  confluenceUrl = await syncToConfluence(feature, 'design');
161
160
  confluenceCreated = true;
162
161
  console.log('✅ Confluenceページ作成成功');
163
- } catch (error: any) {
164
- errors.push(`Confluenceページ作成失敗: ${error.message}`);
165
- console.error('❌ Confluenceページ作成失敗:', error.message);
162
+ } catch (error: unknown) {
163
+ const message = error instanceof Error ? error.message : String(error);
164
+ errors.push(`Confluenceページ作成失敗: ${message}`);
165
+ console.error('❌ Confluenceページ作成失敗:', message);
166
166
  }
167
167
 
168
168
  // Step 3: バリデーション
@@ -206,164 +206,157 @@ async function runDesignPhase(feature: string): Promise<PhaseRunResult> {
206
206
  }
207
207
 
208
208
  /**
209
- * タスク分割フェーズを実行
209
+ * タスク分割フェーズの前提条件をチェック
210
210
  */
211
- async function runTasksPhase(feature: string): Promise<PhaseRunResult> {
212
- console.log('\n📝 Phase: Tasks(タスク分割)');
213
- console.log('='.repeat(60));
214
-
211
+ async function checkTasksPrerequisites(
212
+ feature: string
213
+ ): Promise<{ valid: boolean; errors: string[] }> {
215
214
  const errors: string[] = [];
216
- let jiraCreated = false;
217
215
 
218
- // Step 0: プリフライトチェック
216
+ // プリフライトチェック
219
217
  console.log('\n🔍 プリフライトチェック...');
220
218
  const preFlightResult = await runPreFlightCheck('jira');
221
219
 
222
220
  if (!preFlightResult.valid) {
223
221
  console.log('\n❌ プリフライトチェック失敗:');
224
222
  preFlightResult.errors.forEach((e) => console.log(` ${e}`));
225
- return {
226
- phase: 'tasks',
227
- success: false,
228
- confluenceCreated: false,
229
- jiraCreated: false,
230
- validationPassed: false,
231
- errors: preFlightResult.errors,
232
- };
223
+ return { valid: false, errors: preFlightResult.errors };
233
224
  }
234
225
 
235
226
  console.log('✅ プリフライトチェック成功');
236
227
 
237
- // Step 1: tasks.md存在確認
228
+ // tasks.md存在確認
238
229
  const tasksPath = join(process.cwd(), '.kiro', 'specs', feature, 'tasks.md');
239
230
  if (!existsSync(tasksPath)) {
240
231
  errors.push(
241
232
  'tasks.mdが存在しません。先に/kiro:spec-tasks を実行してください',
242
233
  );
243
- return {
244
- phase: 'tasks',
245
- success: false,
246
- confluenceCreated: false,
247
- jiraCreated: false,
248
- validationPassed: false,
249
- errors,
250
- };
234
+ return { valid: false, errors };
251
235
  }
252
236
 
253
237
  console.log('✅ tasks.md 存在確認');
238
+ return { valid: true, errors: [] };
239
+ }
240
+
241
+ /**
242
+ * AI-DLC形式を検出して変換を提案
243
+ */
244
+ async function detectAndConvertAIDLCFormat(
245
+ feature: string,
246
+ tasksPath: string
247
+ ): Promise<{ success: boolean; errors: string[] }> {
248
+ const errors: string[] = [];
254
249
 
255
- // Step 1.5: AI-DLC形式検出と変換提案
256
250
  console.log('\n🔍 tasks.mdフォーマット検証中...');
257
251
  const { isAIDLCFormat } = await import('./utils/aidlc-parser.js');
258
252
  const tasksContent = readFileSync(tasksPath, 'utf-8');
259
253
 
260
- if (isAIDLCFormat(tasksContent)) {
261
- console.log('\n⚠️ AI-DLC形式が検出されました');
262
- console.log(' tasks.mdはMichiワークフロー形式ではなくAI-DLC形式です。');
263
- console.log('');
264
- console.log('🔄 変換オプション:');
265
- console.log(` michi tasks:convert ${feature} --dry-run # プレビュー`);
254
+ if (!isAIDLCFormat(tasksContent)) {
255
+ return { success: true, errors: [] };
256
+ }
257
+
258
+ console.log('\n⚠️ AI-DLC形式が検出されました');
259
+ console.log(' tasks.mdはMichiワークフロー形式ではなくAI-DLC形式です。');
260
+ console.log('');
261
+ console.log('🔄 変換オプション:');
262
+ console.log(` michi tasks:convert ${feature} --dry-run # プレビュー`);
263
+ console.log(
264
+ ` michi tasks:convert ${feature} --backup # バックアップ付きで変換`,
265
+ );
266
+ console.log(` michi tasks:convert ${feature} # 直接変換`);
267
+ console.log('');
268
+
269
+ // 対話的に変換を提案
270
+ const shouldConvert = await inquirer.prompt([
271
+ {
272
+ type: 'confirm',
273
+ name: 'convert',
274
+ message: 'AI-DLC形式をMichiワークフロー形式に変換しますか?',
275
+ default: true,
276
+ },
277
+ ]);
278
+
279
+ if (!shouldConvert.convert) {
280
+ console.log('\n⏭️ 変換をスキップしました');
266
281
  console.log(
267
- ` michi tasks:convert ${feature} --backup # バックアップ付きで変換`,
282
+ ' AI-DLC形式のままではJIRA連携が正常に動作しない可能性があります。',
283
+ );
284
+ errors.push(
285
+ 'tasks.mdがAI-DLC形式のため、フォーマット検証をスキップしました',
268
286
  );
269
- console.log(` michi tasks:convert ${feature} # 直接変換`);
270
- console.log('');
271
-
272
- // 対話的に変換を提案
273
- const shouldConvert = await inquirer.prompt([
274
- {
275
- type: 'confirm',
276
- name: 'convert',
277
- message: 'AI-DLC形式をMichiワークフロー形式に変換しますか?',
278
- default: true,
279
- },
280
- ]);
281
-
282
- if (shouldConvert.convert) {
283
- console.log('\n🔄 AI-DLC形式をMichiワークフロー形式に変換中...');
284
- const { convertTasksFile } = await import('./utils/tasks-converter.js');
285
- const result = convertTasksFile(tasksPath, undefined, {
286
- backup: true,
287
- language: 'ja',
288
- projectName: feature,
289
- });
287
+ return { success: false, errors };
288
+ }
290
289
 
291
- if (result.success) {
292
- console.log('✅ 変換成功!');
293
- console.log(` 元ファイル: ${result.backupPath}`);
294
- console.log(` 変換後: ${tasksPath}`);
295
- console.log(
296
- ` 統計: ${result.stats.originalTasks}タスク → ${result.stats.convertedStories}ストーリー`,
297
- );
298
- } else {
299
- errors.push('AI-DLC形式の変換に失敗しました');
300
- result.warnings.forEach((w) => errors.push(w));
301
- console.error('❌ 変換失敗');
302
- return {
303
- phase: 'tasks',
304
- success: false,
305
- confluenceCreated: false,
306
- jiraCreated: false,
307
- validationPassed: false,
308
- errors,
309
- };
310
- }
311
- } else {
312
- console.log('\n⏭️ 変換をスキップしました');
313
- console.log(
314
- ' AI-DLC形式のままではJIRA連携が正常に動作しない可能性があります。',
315
- );
316
- errors.push(
317
- 'tasks.mdがAI-DLC形式のため、フォーマット検証をスキップしました',
318
- );
319
- return {
320
- phase: 'tasks',
321
- success: false,
322
- confluenceCreated: false,
323
- jiraCreated: false,
324
- validationPassed: false,
325
- errors,
326
- };
327
- }
290
+ console.log('\n🔄 AI-DLC形式をMichiワークフロー形式に変換中...');
291
+ const { convertTasksFile } = await import('./utils/tasks-converter.js');
292
+ const result = convertTasksFile(tasksPath, undefined, {
293
+ backup: true,
294
+ language: 'ja',
295
+ projectName: feature,
296
+ });
297
+
298
+ if (result.success) {
299
+ console.log('✅ 変換成功!');
300
+ console.log(` 元ファイル: ${result.backupPath}`);
301
+ console.log(` 変換後: ${tasksPath}`);
302
+ console.log(
303
+ ` 統計: ${result.stats.originalTasks}タスク → ${result.stats.convertedStories}ストーリー`,
304
+ );
305
+ return { success: true, errors: [] };
328
306
  }
329
307
 
330
- // Step 1.6: tasks.mdフォーマット検証
308
+ errors.push('AI-DLC形式の変換に失敗しました');
309
+ result.warnings.forEach((w) => errors.push(w));
310
+ console.error('❌ 変換失敗');
311
+ return { success: false, errors };
312
+ }
313
+
314
+ /**
315
+ * tasks.mdのフォーマット検証とJIRA同期
316
+ */
317
+ async function validateTasksFormatAndSyncJIRA(
318
+ feature: string,
319
+ tasksPath: string
320
+ ): Promise<{ jiraCreated: boolean; errors: string[] }> {
321
+ const errors: string[] = [];
322
+ let jiraCreated = false;
323
+
324
+ // フォーマット検証
331
325
  const { validateTasksFormat } = await import(
332
326
  './utils/tasks-format-validator.js'
333
327
  );
334
328
  try {
335
329
  validateTasksFormat(tasksPath);
336
330
  console.log('✅ tasks.mdフォーマット検証成功');
337
- } catch (error: any) {
338
- errors.push(`フォーマット検証失敗: ${error.message}`);
339
- console.error('❌ フォーマット検証失敗:', error.message);
340
- return {
341
- phase: 'tasks',
342
- success: false,
343
- confluenceCreated: false,
344
- jiraCreated: false,
345
- validationPassed: false,
346
- errors,
347
- };
331
+ } catch (error: unknown) {
332
+ const message = error instanceof Error ? error.message : String(error);
333
+ errors.push(`フォーマット検証失敗: ${message}`);
334
+ console.error('❌ フォーマット検証失敗:', message);
335
+ return { jiraCreated: false, errors };
348
336
  }
349
337
 
350
- // Step 2: JIRA Epic/Story作成
338
+ // JIRA Epic/Story作成
351
339
  console.log('\n📤 JIRA Epic/Story作成中...');
352
340
  try {
353
341
  await syncTasksToJIRA(feature);
354
342
  jiraCreated = true;
355
343
  console.log('✅ JIRA Epic/Story作成成功');
356
- } catch (error: any) {
357
- errors.push(`JIRA作成失敗: ${error.message}`);
358
- console.error('❌ JIRA作成失敗:', error.message);
344
+ } catch (error: unknown) {
345
+ const message = error instanceof Error ? error.message : String(error);
346
+ errors.push(`JIRA作成失敗: ${message}`);
347
+ console.error('❌ JIRA作成失敗:', message);
359
348
  }
360
349
 
361
- // Step 3: バリデーション
362
- console.log('\n🔍 バリデーション実行中...');
363
- const validation = validatePhase(feature, 'tasks');
364
- errors.push(...validation.errors);
350
+ return { jiraCreated, errors };
351
+ }
365
352
 
366
- // Step 4: 結果サマリー
353
+ /**
354
+ * タスク分割フェーズのサマリーを表示
355
+ */
356
+ function displayTasksPhaseSummary(
357
+ jiraCreated: boolean,
358
+ validation: { valid: boolean; errors: string[] }
359
+ ): void {
367
360
  console.log('\n' + '='.repeat(60));
368
361
  console.log('📊 タスク分割フェーズ完了チェック:');
369
362
  console.log(' ✅ tasks.md: 作成済み');
@@ -379,293 +372,38 @@ async function runTasksPhase(feature: string): Promise<PhaseRunResult> {
379
372
  console.log('📢 開発チームに実装開始を通知してください');
380
373
  console.log('🚀 次のステップ: /kiro:spec-impl <feature>');
381
374
  }
382
-
383
- return {
384
- phase: 'tasks',
385
- success: validation.valid && jiraCreated,
386
- confluenceCreated: false,
387
- jiraCreated,
388
- validationPassed: validation.valid,
389
- errors,
390
- };
391
- }
392
-
393
- /**
394
- * テストタイプ選択フェーズを実行(Phase 0.3)
395
- * 対話的にテストタイプを選択
396
- */
397
- async function runTestTypeSelectionPhase(
398
- feature: string,
399
- ): Promise<PhaseRunResult> {
400
- console.log('\n🧪 Phase 0.3: Test Type Selection(テストタイプ選択)');
401
- console.log('='.repeat(60));
402
-
403
- const errors: string[] = [];
404
- const specDir = join(process.cwd(), '.kiro', 'specs', feature);
405
- const selectionPath = join(specDir, 'test-type-selection.json');
406
-
407
- // 既存の選択を読み込む(存在する場合)
408
- let existingSelection: any = null;
409
- if (existsSync(selectionPath)) {
410
- try {
411
- existingSelection = JSON.parse(readFileSync(selectionPath, 'utf-8'));
412
- console.log('\n📋 既存の選択が見つかりました:');
413
- console.log(
414
- ` 選択済みテストタイプ: ${existingSelection.selectedTypes?.join(', ') || 'なし'}`,
415
- );
416
- } catch {
417
- console.warn('⚠️ 既存の選択ファイルの読み込みに失敗しました');
418
- }
419
- }
420
-
421
- console.log('\n📚 プロジェクト要件に応じてテストタイプを選択してください\n');
422
-
423
- // 対話的な質問
424
- const answers = await inquirer.prompt([
425
- {
426
- type: 'checkbox',
427
- name: 'testTypes',
428
- message:
429
- '実施するテストタイプを選択してください(スペースキーで選択/解除、Enterで確定):',
430
- choices: [
431
- {
432
- name: '単体テスト (Unit Tests) - 必須 [Phase A]',
433
- value: 'unit',
434
- checked: true, // 必須のためデフォルトで選択
435
- disabled: true, // 必須のため変更不可
436
- },
437
- {
438
- name: 'Lint実行 - 必須 [Phase A]',
439
- value: 'lint',
440
- checked: true,
441
- disabled: true,
442
- },
443
- {
444
- name: 'ビルド実行 - 必須 [Phase A]',
445
- value: 'build',
446
- checked: true,
447
- disabled: true,
448
- },
449
- new inquirer.Separator('--- 推奨テスト ---'),
450
- {
451
- name: '統合テスト (Integration Tests) - 推奨 [Phase 3/B]',
452
- value: 'integration',
453
- checked:
454
- existingSelection?.selectedTypes?.includes('integration') || false,
455
- },
456
- {
457
- name: 'E2Eテスト (End-to-End Tests) - 推奨 [Phase 3/B]',
458
- value: 'e2e',
459
- checked: existingSelection?.selectedTypes?.includes('e2e') || false,
460
- },
461
- new inquirer.Separator('--- 任意テスト ---'),
462
- {
463
- name: '性能テスト (Performance Tests) - 任意 [Phase B]',
464
- value: 'performance',
465
- checked:
466
- existingSelection?.selectedTypes?.includes('performance') || false,
467
- },
468
- {
469
- name: 'セキュリティテスト (Security Tests) - 任意 [Phase B]',
470
- value: 'security',
471
- checked:
472
- existingSelection?.selectedTypes?.includes('security') || false,
473
- },
474
- ],
475
- validate: () => {
476
- // disabled項目は自動的に含まれるため、バリデーションは常に成功
477
- return true;
478
- },
479
- },
480
- {
481
- type: 'confirm',
482
- name: 'confirm',
483
- message: (answers: any) => {
484
- // 必須テストを自動的に追加
485
- const required = ['unit', 'lint', 'build'];
486
- const selected = [...new Set([...required, ...answers.testTypes])];
487
- const optional = selected.filter((t: string) => !required.includes(t));
488
- if (optional.length === 0) {
489
- return '必須テストのみが選択されています。この選択で進めますか?';
490
- }
491
- return `選択したテストタイプ: ${selected.join(', ')}\nこの選択で進めますか?`;
492
- },
493
- default: true,
494
- },
495
- ]);
496
-
497
- // 必須テストを自動的に追加(disabled項目はanswers.testTypesに含まれないため)
498
- const requiredTests = ['unit', 'lint', 'build'];
499
- answers.testTypes = [...new Set([...requiredTests, ...answers.testTypes])];
500
-
501
- if (!answers.confirm) {
502
- console.log('\n❌ 選択がキャンセルされました');
503
- return {
504
- phase: 'test-type-selection' as Phase,
505
- success: false,
506
- confluenceCreated: false,
507
- jiraCreated: false,
508
- validationPassed: false,
509
- errors: ['ユーザーが選択をキャンセルしました'],
510
- };
511
- }
512
-
513
- // 選択結果を保存
514
- const selection = {
515
- feature,
516
- selectedTypes: answers.testTypes,
517
- selectedAt: new Date().toISOString(),
518
- testTypes: {
519
- unit: {
520
- enabled: true,
521
- required: true,
522
- phase: 'A',
523
- description: '単体テスト',
524
- },
525
- lint: {
526
- enabled: true,
527
- required: true,
528
- phase: 'A',
529
- description: 'Lint実行',
530
- },
531
- build: {
532
- enabled: true,
533
- required: true,
534
- phase: 'A',
535
- description: 'ビルド実行',
536
- },
537
- integration: {
538
- enabled: answers.testTypes.includes('integration'),
539
- required: false,
540
- phase: 'B',
541
- description: '統合テスト',
542
- },
543
- e2e: {
544
- enabled: answers.testTypes.includes('e2e'),
545
- required: false,
546
- phase: 'B',
547
- description: 'E2Eテスト',
548
- },
549
- performance: {
550
- enabled: answers.testTypes.includes('performance'),
551
- required: false,
552
- phase: 'B',
553
- description: '性能テスト',
554
- },
555
- security: {
556
- enabled: answers.testTypes.includes('security'),
557
- required: false,
558
- phase: 'B',
559
- description: 'セキュリティテスト',
560
- },
561
- },
562
- };
563
-
564
- // ディレクトリが存在しない場合は作成
565
- if (!existsSync(specDir)) {
566
- mkdirSync(specDir, { recursive: true });
567
- }
568
-
569
- // 選択結果を保存
570
- writeFileSync(selectionPath, JSON.stringify(selection, null, 2), 'utf-8');
571
- console.log(`\n✅ テストタイプ選択を保存しました: ${selectionPath}`);
572
-
573
- // spec.jsonを更新
574
- try {
575
- const specPath = join(specDir, 'spec.json');
576
- if (existsSync(specPath)) {
577
- const spec = JSON.parse(readFileSync(specPath, 'utf-8'));
578
- spec.testTypeSelection = {
579
- completed: true,
580
- selectedTypes: answers.testTypes,
581
- selectedAt: selection.selectedAt,
582
- };
583
- spec.lastUpdated = new Date().toISOString();
584
- writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf-8');
585
- console.log('✅ spec.jsonを更新しました');
586
- }
587
- } catch (error: any) {
588
- errors.push(`spec.json更新失敗: ${error.message}`);
589
- console.warn(`⚠️ spec.json更新失敗: ${error.message}`);
590
- }
591
-
592
- // 選択結果のサマリーを表示
593
- console.log('\n' + '='.repeat(60));
594
- console.log('📊 選択結果サマリー:');
595
- console.log(` 必須テスト: ${['unit', 'lint', 'build'].join(', ')}`);
596
- const optional = answers.testTypes.filter(
597
- (t: string) => !['unit', 'lint', 'build'].includes(t),
598
- );
599
- if (optional.length > 0) {
600
- console.log(` 追加テスト: ${optional.join(', ')}`);
601
- } else {
602
- console.log(' 追加テスト: なし');
603
- }
604
-
605
- console.log('\n📖 次のステップ:');
606
- console.log(' 1. Phase 0.4: テスト仕様書作成へ進む');
607
- console.log(' michi phase:run ' + feature + ' test-spec');
608
- console.log(
609
- ' 2. 詳細ガイド: docs/user-guide/testing/test-planning-flow.md',
610
- );
611
-
612
- console.log('\n' + '='.repeat(60));
613
- console.log('✅ Phase 0.3: テストタイプ選択が完了しました');
614
-
615
- return {
616
- phase: 'test-type-selection' as Phase,
617
- success: true,
618
- confluenceCreated: false,
619
- jiraCreated: false,
620
- validationPassed: true,
621
- errors,
622
- };
623
375
  }
624
376
 
625
377
  /**
626
- * テスト仕様書作成フェーズを実行(Phase 0.4)
627
- * 自動生成: test-type-selectionから選択されたテストタイプの仕様書を生成
378
+ * タスク分割フェーズを実行
628
379
  */
629
- async function runTestSpecPhase(feature: string): Promise<PhaseRunResult> {
630
- console.log('\n📝 Phase 0.4: Test Specification(テスト仕様書作成)');
380
+ async function runTasksPhase(feature: string): Promise<PhaseRunResult> {
381
+ console.log('\n📝 Phase: Tasks(タスク分割)');
631
382
  console.log('='.repeat(60));
632
383
 
633
384
  const errors: string[] = [];
385
+ let jiraCreated = false;
386
+ const tasksPath = join(process.cwd(), '.kiro', 'specs', feature, 'tasks.md');
634
387
 
635
- // Step 1: テストタイプ選択の読み込み
636
- const selectionPath = join(
637
- process.cwd(),
638
- '.kiro',
639
- 'specs',
640
- feature,
641
- 'test-type-selection.json',
642
- );
643
- if (!existsSync(selectionPath)) {
644
- errors.push(
645
- 'test-type-selection.jsonが存在しません。先にtest-type-selectionフェーズを実行してください',
646
- );
388
+ // 前提条件チェック
389
+ const prereqCheck = await checkTasksPrerequisites(feature);
390
+ if (!prereqCheck.valid) {
647
391
  return {
648
- phase: 'test-spec' as Phase,
392
+ phase: 'tasks',
649
393
  success: false,
650
394
  confluenceCreated: false,
651
395
  jiraCreated: false,
652
396
  validationPassed: false,
653
- errors,
397
+ errors: prereqCheck.errors,
654
398
  };
655
399
  }
656
400
 
657
- let selection: { selectedTypes?: string[] };
658
- let testTypes: string[] = [];
659
-
660
- try {
661
- selection = JSON.parse(readFileSync(selectionPath, 'utf-8'));
662
- testTypes = selection.selectedTypes || [];
663
- } catch (error) {
664
- errors.push(
665
- `test-type-selection.jsonの読み込みまたはパースに失敗しました: ${error instanceof Error ? error.message : String(error)}`,
666
- );
401
+ // AI-DLC形式検出と変換
402
+ const convertResult = await detectAndConvertAIDLCFormat(feature, tasksPath);
403
+ if (!convertResult.success) {
404
+ errors.push(...convertResult.errors);
667
405
  return {
668
- phase: 'test-spec' as Phase,
406
+ phase: 'tasks',
669
407
  success: false,
670
408
  confluenceCreated: false,
671
409
  jiraCreated: false,
@@ -674,96 +412,36 @@ async function runTestSpecPhase(feature: string): Promise<PhaseRunResult> {
674
412
  };
675
413
  }
676
414
 
677
- console.log(`\n✅ 選択されたテストタイプ: ${testTypes.join(', ')}`);
678
-
679
- // Step 2: 各テストタイプのテスト仕様書を生成
680
- console.log('\n🤖 テスト仕様書を自動生成中...');
681
-
682
- const specDir = join(process.cwd(), '.kiro', 'specs', feature, 'test-specs');
683
- mkdirSync(specDir, { recursive: true });
684
-
685
- const { generateTestSpec } = await import('./test-spec-generator.js');
686
- const generatedSpecs: string[] = [];
687
-
688
- for (const testType of testTypes) {
689
- // lint/buildはテスト仕様書不要(CI設定で対応)
690
- if (testType === 'lint' || testType === 'build') {
691
- console.log(` ⏭️ ${testType}: スキップ(CI設定で対応)`);
692
- continue;
693
- }
694
-
695
- try {
696
- await generateTestSpec(feature, testType);
697
- console.log(` ✅ ${testType}テスト仕様書: ${testType}-test-spec.md`);
698
- generatedSpecs.push(testType);
699
- } catch (error: any) {
700
- errors.push(`${testType}テスト仕様書生成失敗: ${error.message}`);
701
- console.error(` ❌ ${testType}テスト仕様書生成失敗: ${error.message}`);
702
- }
703
- }
704
-
705
- // Step 3: spec.json更新
706
- const specPath = join(process.cwd(), '.kiro', 'specs', feature, 'spec.json');
707
- if (existsSync(specPath)) {
708
- try {
709
- const spec = JSON.parse(readFileSync(specPath, 'utf-8'));
710
- spec.testSpecification = {
711
- completed: true,
712
- generatedAt: new Date().toISOString(),
713
- testTypes: testTypes,
714
- generatedSpecs: generatedSpecs,
715
- };
716
- spec.lastUpdated = new Date().toISOString();
717
- writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf-8');
718
- console.log('\n✅ spec.jsonを更新しました');
719
- } catch (error: any) {
720
- errors.push(`spec.json更新失敗: ${error.message}`);
721
- console.warn(`⚠️ spec.json更新失敗: ${error.message}`);
722
- }
723
- }
724
-
725
- // Step 4: サマリー表示
726
- console.log('\n' + '='.repeat(60));
727
- console.log('📊 テスト仕様書作成完了:');
728
- console.log(` 生成されたファイル: ${generatedSpecs.length}件`);
729
- console.log(` 保存先: .kiro/specs/${feature}/test-specs/`);
415
+ // フォーマット検証とJIRA同期
416
+ const syncResult = await validateTasksFormatAndSyncJIRA(feature, tasksPath);
417
+ jiraCreated = syncResult.jiraCreated;
418
+ errors.push(...syncResult.errors);
730
419
 
731
- if (generatedSpecs.length > 0) {
732
- console.log('\n📄 生成されたファイル:');
733
- generatedSpecs.forEach((type) => {
734
- console.log(` - ${type}-test-spec.md`);
735
- });
736
- }
737
-
738
- console.log('\n📖 次のステップ:');
739
- console.log(' 1. Phase 0.5: タスク分割へ進む');
740
- console.log(` michi phase:run ${feature} tasks`);
420
+ // バリデーション
421
+ console.log('\n🔍 バリデーション実行中...');
422
+ const validation = validatePhase(feature, 'tasks');
423
+ errors.push(...validation.errors);
741
424
 
742
- console.log('\n' + '='.repeat(60));
743
- console.log('✅ Phase 0.4: テスト仕様書作成が完了しました');
425
+ // サマリー表示
426
+ displayTasksPhaseSummary(jiraCreated, validation);
744
427
 
745
428
  return {
746
- phase: 'test-spec' as Phase,
747
- success: errors.length === 0 && generatedSpecs.length > 0,
429
+ phase: 'tasks',
430
+ success: validation.valid && jiraCreated,
748
431
  confluenceCreated: false,
749
- jiraCreated: false,
750
- validationPassed: true,
432
+ jiraCreated,
433
+ validationPassed: validation.valid,
751
434
  errors,
752
435
  };
753
436
  }
754
437
 
755
- /**
756
- * 環境構築フェーズを実行(Phase 1)
757
- * 対話的に環境を構築し、必要な設定ファイルを生成
758
- */
759
- async function runEnvironmentSetupPhase(
760
- feature: string,
761
- ): Promise<PhaseRunResult> {
762
- console.log('\n⚙️ Phase 1: Environment Setup(環境構築)');
763
- console.log('='.repeat(60));
764
438
 
765
- const errors: string[] = [];
766
439
 
440
+
441
+ /**
442
+ * プロジェクト検出と言語/Docker要件を分析
443
+ */
444
+ async function detectAndAnalyzeProject(feature: string) {
767
445
  // Step 1: プロジェクト検出
768
446
  const { detectProject } = await import('./utils/project-detector.js');
769
447
  const detected = detectProject();
@@ -808,7 +486,17 @@ async function runEnvironmentSetupPhase(
808
486
  }
809
487
  }
810
488
 
811
- // Step 4: 対話的質問
489
+ return { detected, languageAnalysis, dockerAnalysis };
490
+ }
491
+
492
+ /**
493
+ * 環境構築の対話的質問を収集
494
+ */
495
+ async function collectEnvironmentAnswers(
496
+ detected: ReturnType<typeof import('./utils/project-detector.js').detectProject>,
497
+ languageAnalysis: ReturnType<typeof import('./utils/language-detector.js').analyzeLanguage>,
498
+ dockerAnalysis: ReturnType<typeof import('./utils/docker-requirement-detector.js').analyzeDockerRequirement>
499
+ ) {
812
500
  console.log('\n📚 環境構築を対話的に設定します\n');
813
501
 
814
502
  const languageMap: Record<string, string> = {
@@ -882,15 +570,26 @@ async function runEnvironmentSetupPhase(
882
570
  // Docker Composeの推奨サービスを保存
883
571
  answers.suggestedServices = dockerAnalysis.suggestedServices;
884
572
 
885
- // Step 3: 設定ファイル生成
573
+ return answers;
574
+ }
575
+
576
+ /**
577
+ * 環境構築の設定ファイルを生成
578
+ */
579
+ async function generateEnvironmentConfigs(
580
+ feature: string,
581
+ answers: { language: string; ciTool: string; needsDocker: boolean; suggestedServices?: string[] },
582
+ errors: string[]
583
+ ) {
886
584
  console.log('\n🤖 設定ファイルを生成中...');
887
585
 
888
586
  try {
889
587
  const { generateCIConfig } = await import('./utils/ci-generator.js');
890
588
  await generateCIConfig(feature, answers);
891
- } catch (error: any) {
892
- errors.push(`CI/CD設定生成失敗: ${error.message}`);
893
- console.error(`CI/CD設定生成失敗: ${error.message}`);
589
+ } catch (error: unknown) {
590
+ const message = error instanceof Error ? error.message : String(error);
591
+ errors.push(`CI/CD設定生成失敗: ${message}`);
592
+ console.error(` ❌ CI/CD設定生成失敗: ${message}`);
894
593
  }
895
594
 
896
595
  try {
@@ -898,9 +597,10 @@ async function runEnvironmentSetupPhase(
898
597
  './utils/test-config-generator.js'
899
598
  );
900
599
  await generateTestConfig(feature, answers);
901
- } catch (error: any) {
902
- errors.push(`テスト設定生成失敗: ${error.message}`);
903
- console.error(` ❌ テスト設定生成失敗: ${error.message}`);
600
+ } catch (error: unknown) {
601
+ const message = error instanceof Error ? error.message : String(error);
602
+ errors.push(`テスト設定生成失敗: ${message}`);
603
+ console.error(` ❌ テスト設定生成失敗: ${message}`);
904
604
  }
905
605
 
906
606
  if (answers.needsDocker) {
@@ -909,90 +609,120 @@ async function runEnvironmentSetupPhase(
909
609
  './utils/docker-generator.js'
910
610
  );
911
611
  await generateDockerCompose(feature, answers.suggestedServices || []);
912
- } catch (error: any) {
913
- errors.push(`Docker Compose生成失敗: ${error.message}`);
914
- console.error(`Docker Compose生成失敗: ${error.message}`);
612
+ } catch (error: unknown) {
613
+ const message = error instanceof Error ? error.message : String(error);
614
+ errors.push(`Docker Compose生成失敗: ${message}`);
615
+ console.error(` ❌ Docker Compose生成失敗: ${message}`);
915
616
  }
916
617
  }
618
+ }
917
619
 
918
- // Step 4: 依存関係インストール(オプション)
919
- if (answers.installDeps) {
920
- console.log('\n📦 依存関係をインストール中...');
620
+ /**
621
+ * 依存関係を自動インストール
622
+ */
623
+ async function installDependencies(
624
+ answers: { language: string; installDeps?: boolean },
625
+ detected: { packageManager?: string }
626
+ ) {
627
+ if (!answers.installDeps) {
628
+ return;
629
+ }
921
630
 
922
- const { execSync } = await import('child_process');
631
+ console.log('\n📦 依存関係をインストール中...');
923
632
 
924
- // 言語別のビルドファイル存在確認
925
- const buildFileChecks: Record<string, string> = {
926
- 'Node.js/TypeScript': 'package.json',
927
- Java: 'build.gradle',
928
- PHP: 'composer.json',
929
- Python: 'requirements.txt',
930
- Go: 'go.mod',
931
- Rust: 'Cargo.toml',
932
- };
633
+ const { execSync } = await import('child_process');
933
634
 
934
- const buildFile = buildFileChecks[answers.language];
935
- if (!buildFile || !existsSync(buildFile)) {
936
- console.log(
937
- ` ℹ️ ${buildFile || 'ビルドファイル'}が見つかりません(スキップ)`,
938
- );
939
- console.log(
940
- ' 💡 実際のプロジェクトでは、先にプロジェクトを初期化してください',
635
+ // 言語別のビルドファイル存在確認
636
+ const buildFileChecks: Record<string, string> = {
637
+ 'Node.js/TypeScript': 'package.json',
638
+ Java: 'build.gradle',
639
+ PHP: 'composer.json',
640
+ Python: 'requirements.txt',
641
+ Go: 'go.mod',
642
+ Rust: 'Cargo.toml',
643
+ };
644
+
645
+ const buildFile = buildFileChecks[answers.language];
646
+ if (!buildFile || !existsSync(buildFile)) {
647
+ console.log(
648
+ ` ℹ️ ${buildFile || 'ビルドファイル'}が見つかりません(スキップ)`,
649
+ );
650
+ console.log(
651
+ ' 💡 実際のプロジェクトでは、先にプロジェクトを初期化してください',
652
+ );
653
+ return;
654
+ }
655
+
656
+ const commands: Record<string, string> = {
657
+ 'Node.js/TypeScript':
658
+ detected.packageManager === 'pnpm'
659
+ ? 'pnpm install'
660
+ : detected.packageManager === 'yarn'
661
+ ? 'yarn install'
662
+ : 'npm install',
663
+ Java: existsSync('./gradlew')
664
+ ? './gradlew build --no-daemon'
665
+ : 'gradle build',
666
+ PHP: 'composer install',
667
+ Python: 'pip install -r requirements.txt',
668
+ Go: 'go mod download',
669
+ Rust: 'cargo fetch',
670
+ };
671
+
672
+ const command = commands[answers.language];
673
+ if (command) {
674
+ try {
675
+ execSync(command, { stdio: 'inherit', cwd: process.cwd() });
676
+ console.log(' ✅ 依存関係のインストール完了');
677
+ } catch (error: unknown) {
678
+ const message = error instanceof Error ? error.message : String(error);
679
+ console.warn(` ⚠️ 依存関係インストール失敗: ${message}`);
680
+ console.warn(
681
+ ' 💡 プロジェクト初期化後に手動でインストールしてください',
941
682
  );
942
- } else {
943
- const commands: Record<string, string> = {
944
- 'Node.js/TypeScript':
945
- detected.packageManager === 'pnpm'
946
- ? 'pnpm install'
947
- : detected.packageManager === 'yarn'
948
- ? 'yarn install'
949
- : 'npm install',
950
- Java: existsSync('./gradlew')
951
- ? './gradlew build --no-daemon'
952
- : 'gradle build',
953
- PHP: 'composer install',
954
- Python: 'pip install -r requirements.txt',
955
- Go: 'go mod download',
956
- Rust: 'cargo fetch',
957
- };
958
-
959
- const command = commands[answers.language];
960
- if (command) {
961
- try {
962
- execSync(command, { stdio: 'inherit', cwd: process.cwd() });
963
- console.log(' ✅ 依存関係のインストール完了');
964
- } catch (error: any) {
965
- console.warn(` ⚠️ 依存関係インストール失敗: ${error.message}`);
966
- console.warn(
967
- ' 💡 プロジェクト初期化後に手動でインストールしてください',
968
- );
969
- }
970
- }
971
683
  }
972
684
  }
685
+ }
973
686
 
974
- // Step 5: spec.json更新
687
+ /**
688
+ * spec.jsonを更新
689
+ */
690
+ async function updateEnvironmentSpecJson(
691
+ feature: string,
692
+ answers: { language: string; ciTool: string; needsDocker: boolean },
693
+ errors: string[]
694
+ ) {
975
695
  const specPath = join(process.cwd(), '.kiro', 'specs', feature, 'spec.json');
976
- if (existsSync(specPath)) {
977
- try {
978
- const spec = JSON.parse(readFileSync(specPath, 'utf-8'));
979
- spec.environmentSetup = {
980
- completed: true,
981
- language: answers.language,
982
- ciTool: answers.ciTool,
983
- dockerCompose: answers.needsDocker,
984
- completedAt: new Date().toISOString(),
985
- };
986
- spec.lastUpdated = new Date().toISOString();
987
- writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf-8');
988
- console.log('\n✅ spec.jsonを更新しました');
989
- } catch (error: any) {
990
- errors.push(`spec.json更新失敗: ${error.message}`);
991
- console.warn(`⚠️ spec.json更新失敗: ${error.message}`);
992
- }
696
+ if (!existsSync(specPath)) {
697
+ return;
993
698
  }
994
699
 
995
- // Step 6: サマリー表示
700
+ try {
701
+ const spec = JSON.parse(readFileSync(specPath, 'utf-8'));
702
+ spec.environmentSetup = {
703
+ completed: true,
704
+ language: answers.language,
705
+ ciTool: answers.ciTool,
706
+ dockerCompose: answers.needsDocker,
707
+ completedAt: new Date().toISOString(),
708
+ };
709
+ spec.lastUpdated = new Date().toISOString();
710
+ writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf-8');
711
+ console.log('\n✅ spec.jsonを更新しました');
712
+ } catch (error: unknown) {
713
+ const message = error instanceof Error ? error.message : String(error);
714
+ errors.push(`spec.json更新失敗: ${message}`);
715
+ console.warn(`⚠️ spec.json更新失敗: ${message}`);
716
+ }
717
+ }
718
+
719
+ /**
720
+ * 環境構築のサマリーを表示
721
+ */
722
+ function displayEnvironmentSummary(
723
+ feature: string,
724
+ answers: { language: string; ciTool: string; needsDocker: boolean }
725
+ ) {
996
726
  console.log('\n' + '='.repeat(60));
997
727
  console.log('📊 環境構築完了:');
998
728
  console.log(` 言語: ${answers.language}`);
@@ -1005,6 +735,37 @@ async function runEnvironmentSetupPhase(
1005
735
 
1006
736
  console.log('\n' + '='.repeat(60));
1007
737
  console.log('✅ Phase 1: 環境構築が完了しました');
738
+ }
739
+
740
+ /**
741
+ * 環境構築フェーズを実行(Phase 1)
742
+ * 対話的に環境を構築し、必要な設定ファイルを生成
743
+ */
744
+ async function runEnvironmentSetupPhase(
745
+ feature: string,
746
+ ): Promise<PhaseRunResult> {
747
+ console.log('\n⚙️ Phase 1: Environment Setup(環境構築)');
748
+ console.log('='.repeat(60));
749
+
750
+ const errors: string[] = [];
751
+
752
+ // プロジェクト検出と分析
753
+ const { detected, languageAnalysis, dockerAnalysis } = await detectAndAnalyzeProject(feature);
754
+
755
+ // 対話的質問の収集
756
+ const answers = await collectEnvironmentAnswers(detected, languageAnalysis, dockerAnalysis);
757
+
758
+ // 設定ファイル生成
759
+ await generateEnvironmentConfigs(feature, answers, errors);
760
+
761
+ // 依存関係インストール
762
+ await installDependencies(answers, detected);
763
+
764
+ // spec.json更新
765
+ await updateEnvironmentSpecJson(feature, answers, errors);
766
+
767
+ // サマリー表示
768
+ displayEnvironmentSummary(feature, answers);
1008
769
 
1009
770
  return {
1010
771
  phase: 'environment-setup' as Phase,
@@ -1066,17 +827,9 @@ async function runPhaseAPhase(feature: string): Promise<PhaseRunResult> {
1066
827
  }
1067
828
 
1068
829
  /**
1069
- * リリース準備テストフェーズを実行(Phase B
1070
- * テスト実行ファイルを自動生成し、手動テストチェックリストを表示
830
+ * Phase B対象のテストタイプを読み込み
1071
831
  */
1072
- async function runPhaseBPhase(feature: string): Promise<PhaseRunResult> {
1073
- console.log('\n🔍 Phase B: リリース準備テスト(Release Tests)');
1074
- console.log('='.repeat(60));
1075
-
1076
- const errors: string[] = [];
1077
- const generatedFiles: string[] = [];
1078
-
1079
- // Step 1: テストタイプ選択の読み込み
832
+ function loadPhaseBTestTypes(feature: string): string[] {
1080
833
  const selectionPath = join(
1081
834
  process.cwd(),
1082
835
  '.kiro',
@@ -1107,42 +860,71 @@ async function runPhaseBPhase(feature: string): Promise<PhaseRunResult> {
1107
860
  ];
1108
861
  }
1109
862
 
1110
- // Step 2: Phase B対象のテストタイプを抽出
863
+ // Phase B対象のテストタイプを抽出(Phase Aのテストを除外)
1111
864
  const phaseBTypes = selectedTypes.filter(
1112
865
  (t) => !['unit', 'lint', 'build'].includes(t),
1113
866
  );
1114
867
 
1115
868
  if (phaseBTypes.length > 0) {
1116
869
  console.log(`\n📝 Phase B対象テスト: ${phaseBTypes.join(', ')}`);
870
+ }
1117
871
 
1118
- // Step 3: テスト実行ファイルを生成
1119
- console.log('\n🤖 テスト実行ファイルを自動生成中...');
872
+ return phaseBTypes;
873
+ }
1120
874
 
1121
- const { generateTestExecution } = await import(
1122
- './test-execution-generator.js'
1123
- );
875
+ /**
876
+ * Phase Bのテスト実行ファイルを生成
877
+ */
878
+ async function generatePhaseBTestFiles(
879
+ feature: string,
880
+ phaseBTypes: string[]
881
+ ): Promise<{ generatedFiles: string[]; errors: string[] }> {
882
+ const generatedFiles: string[] = [];
883
+ const errors: string[] = [];
1124
884
 
1125
- for (const testType of phaseBTypes) {
1126
- try {
1127
- const result = await generateTestExecution(feature, testType);
1128
-
1129
- if (result.success) {
1130
- console.log(
1131
- ` ✅ ${result.testType}: ${result.files.length}ファイル生成`,
1132
- );
1133
- generatedFiles.push(...result.files);
1134
- } else {
1135
- console.error(` ❌ ${result.testType}: ${result.error}`);
1136
- errors.push(`${result.testType}テスト生成失敗: ${result.error}`);
1137
- }
1138
- } catch (error: any) {
1139
- errors.push(`${testType}テスト生成失敗: ${error.message}`);
1140
- console.error(`❌ ${testType}テスト生成失敗:`, error.message);
885
+ if (phaseBTypes.length === 0) {
886
+ return { generatedFiles, errors };
887
+ }
888
+
889
+ console.log('\n🤖 テスト実行ファイルを自動生成中...');
890
+
891
+ const { generateTestExecution } = await import(
892
+ './test-execution-generator.js'
893
+ );
894
+
895
+ for (const testType of phaseBTypes) {
896
+ try {
897
+ const result = await generateTestExecution(feature, testType);
898
+
899
+ if (result.success) {
900
+ console.log(
901
+ ` ✅ ${result.testType}: ${result.files.length}ファイル生成`,
902
+ );
903
+ generatedFiles.push(...result.files);
904
+ } else {
905
+ console.error(` ❌ ${result.testType}: ${result.error}`);
906
+ errors.push(`${result.testType}テスト生成失敗: ${result.error}`);
1141
907
  }
908
+ } catch (error: unknown) {
909
+ const message = error instanceof Error ? error.message : String(error);
910
+ errors.push(`${testType}テスト生成失敗: ${message}`);
911
+ console.error(`❌ ${testType}テスト生成失敗:`, message);
1142
912
  }
1143
913
  }
1144
914
 
1145
- // Step 4: 生成されたファイルのサマリー
915
+ return { generatedFiles, errors };
916
+ }
917
+
918
+ /**
919
+ * Phase Bのチェックリストとサマリーを表示
920
+ */
921
+ function displayPhaseBChecklist(
922
+ feature: string,
923
+ phaseBTypes: string[],
924
+ generatedFiles: string[],
925
+ errors: string[]
926
+ ): void {
927
+ // 生成されたファイルのサマリー
1146
928
  const testExecutionDir = join(
1147
929
  process.cwd(),
1148
930
  '.kiro',
@@ -1158,7 +940,7 @@ async function runPhaseBPhase(feature: string): Promise<PhaseRunResult> {
1158
940
  });
1159
941
  }
1160
942
 
1161
- // Step 5: チェックリスト表示
943
+ // チェックリスト表示
1162
944
  console.log('\n' + '='.repeat(60));
1163
945
  console.log('📋 リリース準備テストチェックリスト:\n');
1164
946
 
@@ -1205,10 +987,31 @@ async function runPhaseBPhase(feature: string): Promise<PhaseRunResult> {
1205
987
  errors.forEach((err) => console.log(` - ${err}`));
1206
988
  console.log('📢 エラーを修正してから再実行してください');
1207
989
  }
990
+ }
991
+
992
+ /**
993
+ * リリース準備テストフェーズを実行(Phase B)
994
+ * テスト実行ファイルを自動生成し、手動テストチェックリストを表示
995
+ */
996
+ async function runPhaseBPhase(feature: string): Promise<PhaseRunResult> {
997
+ console.log('\n🔍 Phase B: リリース準備テスト(Release Tests)');
998
+ console.log('='.repeat(60));
999
+
1000
+ // Phase B対象のテストタイプを読み込み
1001
+ const phaseBTypes = loadPhaseBTestTypes(feature);
1002
+
1003
+ // テスト実行ファイルを生成
1004
+ const { generatedFiles, errors } = await generatePhaseBTestFiles(
1005
+ feature,
1006
+ phaseBTypes,
1007
+ );
1008
+
1009
+ // チェックリストとサマリーを表示
1010
+ displayPhaseBChecklist(feature, phaseBTypes, generatedFiles, errors);
1208
1011
 
1209
1012
  return {
1210
1013
  phase: 'phase-b' as Phase,
1211
- success,
1014
+ success: errors.length === 0,
1212
1015
  confluenceCreated: false,
1213
1016
  jiraCreated: false,
1214
1017
  validationPassed: true,
@@ -1231,10 +1034,6 @@ export async function runPhase(
1231
1034
  return await runRequirementsPhase(feature);
1232
1035
  case 'design':
1233
1036
  return await runDesignPhase(feature);
1234
- case 'test-type-selection':
1235
- return await runTestTypeSelectionPhase(feature);
1236
- case 'test-spec':
1237
- return await runTestSpecPhase(feature);
1238
1037
  case 'tasks':
1239
1038
  return await runTasksPhase(feature);
1240
1039
  case 'environment-setup':
@@ -1258,12 +1057,11 @@ if (import.meta.url === `file://${process.argv[1]}`) {
1258
1057
  console.error('\nAvailable Phases:');
1259
1058
  console.error(' requirements - Phase 0.1: 要件定義');
1260
1059
  console.error(' design - Phase 0.2: 設計');
1261
- console.error(' test-type-selection- Phase 0.3: テストタイプ選択(任意)');
1262
- console.error(' test-spec - Phase 0.4: テスト仕様書作成(任意)');
1263
1060
  console.error(' tasks - Phase 0.5-0.6: タスク分割・JIRA同期');
1264
1061
  console.error(' environment-setup - Phase 1: 環境構築(任意)');
1265
1062
  console.error(' phase-a - Phase A: PR前自動テスト(任意)');
1266
1063
  console.error(' phase-b - Phase B: リリース準備テスト(任意)');
1064
+ console.error('\nNote: For test planning (Phase 0.3-0.4), use /michi:test-planning AI command');
1267
1065
  process.exit(1);
1268
1066
  }
1269
1067
 
@@ -1272,8 +1070,6 @@ if (import.meta.url === `file://${process.argv[1]}`) {
1272
1070
  const validPhases = [
1273
1071
  'requirements',
1274
1072
  'design',
1275
- 'test-type-selection',
1276
- 'test-spec',
1277
1073
  'tasks',
1278
1074
  'environment-setup',
1279
1075
  'phase-a',
@@ -1283,7 +1079,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
1283
1079
  if (!validPhases.includes(phase)) {
1284
1080
  console.error(`Invalid phase: ${phase}`);
1285
1081
  console.error(
1286
- 'Must be one of: requirements, design, test-type-selection, test-spec, tasks, environment-setup, phase-a, phase-b',
1082
+ 'Must be one of: requirements, design, tasks, environment-setup, phase-a, phase-b',
1287
1083
  );
1288
1084
  process.exit(1);
1289
1085
  }
@@ -1298,8 +1094,9 @@ if (import.meta.url === `file://${process.argv[1]}`) {
1298
1094
  process.exit(1);
1299
1095
  }
1300
1096
  })
1301
- .catch((error) => {
1302
- console.error(`\n❌ フェーズ実行エラー: ${error.message}`);
1097
+ .catch((error: unknown) => {
1098
+ const message = error instanceof Error ? error.message : String(error);
1099
+ console.error(`\n❌ フェーズ実行エラー: ${message}`);
1303
1100
  process.exit(1);
1304
1101
  });
1305
1102
  }