@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.
- package/CHANGELOG.md +43 -0
- package/dist/scripts/config-global.d.ts +10 -0
- package/dist/scripts/config-global.d.ts.map +1 -0
- package/dist/scripts/config-global.js +111 -0
- package/dist/scripts/config-global.js.map +1 -0
- package/dist/scripts/confluence-sync.d.ts +22 -4
- package/dist/scripts/confluence-sync.d.ts.map +1 -1
- package/dist/scripts/confluence-sync.js +22 -12
- package/dist/scripts/confluence-sync.js.map +1 -1
- package/dist/scripts/jira-sync.d.ts.map +1 -1
- package/dist/scripts/jira-sync.js +201 -167
- package/dist/scripts/jira-sync.js.map +1 -1
- package/dist/scripts/list-projects.js.map +1 -1
- package/dist/scripts/multi-project-estimate.js.map +1 -1
- package/dist/scripts/phase-runner.d.ts +1 -1
- package/dist/scripts/phase-runner.d.ts.map +1 -1
- package/dist/scripts/phase-runner.js +295 -522
- package/dist/scripts/phase-runner.js.map +1 -1
- package/dist/scripts/pre-flight-check.d.ts.map +1 -1
- package/dist/scripts/pre-flight-check.js +10 -6
- package/dist/scripts/pre-flight-check.js.map +1 -1
- package/dist/scripts/resource-dashboard.js.map +1 -1
- package/dist/scripts/spec-impl-workflow.js +1 -1
- package/dist/scripts/spec-impl-workflow.js.map +1 -1
- package/dist/scripts/template/renderer.d.ts +1 -1
- package/dist/scripts/template/renderer.d.ts.map +1 -1
- package/dist/scripts/test-interactive.d.ts.map +1 -1
- package/dist/scripts/test-interactive.js +0 -15
- package/dist/scripts/test-interactive.js.map +1 -1
- package/dist/scripts/test-new-features.js +6 -3
- package/dist/scripts/test-new-features.js.map +1 -1
- package/dist/scripts/test-spec-generator.d.ts.map +1 -1
- package/dist/scripts/test-spec-generator.js +1 -2
- package/dist/scripts/test-spec-generator.js.map +1 -1
- package/dist/scripts/utils/config-loader.d.ts +7 -2
- package/dist/scripts/utils/config-loader.d.ts.map +1 -1
- package/dist/scripts/utils/config-loader.js +79 -8
- package/dist/scripts/utils/config-loader.js.map +1 -1
- package/dist/scripts/utils/config-sections.d.ts +54 -0
- package/dist/scripts/utils/config-sections.d.ts.map +1 -0
- package/dist/scripts/utils/config-sections.js +178 -0
- package/dist/scripts/utils/config-sections.js.map +1 -0
- package/dist/scripts/utils/config-validator.d.ts +4 -0
- package/dist/scripts/utils/config-validator.d.ts.map +1 -1
- package/dist/scripts/utils/config-validator.js +57 -1
- package/dist/scripts/utils/config-validator.js.map +1 -1
- package/dist/scripts/utils/confluence-approval.d.ts.map +1 -1
- package/dist/scripts/utils/confluence-approval.js +5 -3
- package/dist/scripts/utils/confluence-approval.js.map +1 -1
- package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
- package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
- package/dist/scripts/utils/interactive-helpers.d.ts +32 -0
- package/dist/scripts/utils/interactive-helpers.d.ts.map +1 -0
- package/dist/scripts/utils/interactive-helpers.js +92 -0
- package/dist/scripts/utils/interactive-helpers.js.map +1 -0
- package/dist/scripts/utils/jira-issue-type-fetcher.d.ts.map +1 -1
- package/dist/scripts/utils/jira-issue-type-fetcher.js +27 -18
- package/dist/scripts/utils/jira-issue-type-fetcher.js.map +1 -1
- package/dist/scripts/utils/release-notes-generator.d.ts.map +1 -1
- package/dist/scripts/utils/release-notes-generator.js +2 -1
- package/dist/scripts/utils/release-notes-generator.js.map +1 -1
- package/dist/scripts/utils/spec-updater.d.ts +19 -0
- package/dist/scripts/utils/spec-updater.d.ts.map +1 -1
- package/dist/scripts/utils/spec-updater.js.map +1 -1
- package/dist/scripts/utils/tasks-converter.d.ts.map +1 -1
- package/dist/scripts/utils/tasks-converter.js +2 -2
- package/dist/scripts/utils/tasks-converter.js.map +1 -1
- package/dist/scripts/utils/tasks-format-validator.d.ts.map +1 -1
- package/dist/scripts/utils/tasks-format-validator.js +0 -12
- package/dist/scripts/utils/tasks-format-validator.js.map +1 -1
- package/dist/scripts/utils/test-runner.d.ts.map +1 -1
- package/dist/scripts/utils/test-runner.js +3 -2
- package/dist/scripts/utils/test-runner.js.map +1 -1
- package/dist/scripts/validate-phase.d.ts +1 -1
- package/dist/scripts/validate-phase.d.ts.map +1 -1
- package/dist/scripts/validate-phase.js +12 -62
- package/dist/scripts/validate-phase.js.map +1 -1
- package/dist/scripts/workflow-orchestrator.d.ts.map +1 -1
- package/dist/scripts/workflow-orchestrator.js +11 -16
- package/dist/scripts/workflow-orchestrator.js.map +1 -1
- package/dist/src/__tests__/integration/setup/init.test.d.ts +5 -0
- package/dist/src/__tests__/integration/setup/init.test.d.ts.map +1 -0
- package/dist/src/__tests__/integration/setup/init.test.js +352 -0
- package/dist/src/__tests__/integration/setup/init.test.js.map +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +28 -20
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/init.d.ts +28 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +490 -0
- package/dist/src/commands/init.js.map +1 -0
- package/docs/user-guide/getting-started/setup.md +31 -3
- package/docs/user-guide/guides/customization.md +64 -11
- package/docs/user-guide/guides/workflow.md +35 -21
- package/docs/user-guide/reference/config.md +30 -5
- package/docs/user-guide/reference/quick-reference.md +68 -74
- package/docs/user-guide/testing/test-planning-flow.md +4 -0
- package/package.json +2 -4
- package/scripts/config-global.ts +160 -0
- package/scripts/confluence-sync.ts +91 -27
- package/scripts/jira-sync.ts +284 -218
- package/scripts/list-projects.ts +2 -2
- package/scripts/multi-project-estimate.ts +3 -3
- package/scripts/phase-runner.ts +391 -594
- package/scripts/pre-flight-check.ts +20 -9
- package/scripts/pre-publish-check.sh +3 -34
- package/scripts/resource-dashboard.ts +4 -4
- package/scripts/spec-impl-workflow.ts +1 -1
- package/scripts/template/renderer.ts +1 -1
- package/scripts/test-interactive.ts +0 -19
- package/scripts/test-new-features.ts +10 -7
- package/scripts/test-npm-package.sh +3 -34
- package/scripts/test-spec-generator.ts +3 -7
- package/scripts/utils/config-loader.ts +107 -26
- package/scripts/utils/config-sections.ts +316 -0
- package/scripts/utils/config-validator.ts +66 -1
- package/scripts/utils/confluence-approval.ts +8 -6
- package/scripts/utils/confluence-hierarchy.ts +27 -27
- package/scripts/utils/interactive-helpers.ts +135 -0
- package/scripts/utils/jira-issue-type-fetcher.ts +29 -21
- package/scripts/utils/release-notes-generator.ts +3 -2
- package/scripts/utils/spec-updater.ts +37 -15
- package/scripts/utils/tasks-converter.ts +4 -6
- package/scripts/utils/tasks-format-validator.ts +0 -13
- package/scripts/utils/test-runner.ts +4 -3
- package/scripts/validate-phase.ts +21 -80
- package/scripts/workflow-orchestrator.ts +16 -25
- package/templates/claude/commands/kiro/kiro-spec-impl.md +4 -0
- package/templates/claude/commands/kiro/kiro-spec-tasks.md +3 -1
- package/templates/claude/commands/michi/confluence-sync.md +8 -2
- package/templates/claude/commands/michi/design-review.md +4 -0
- package/templates/claude/commands/michi/e2e-plan.md +4 -0
- package/templates/claude/commands/michi/license-check.md +4 -0
- package/templates/claude/commands/michi/pr-resolve.md +4 -0
- package/templates/claude/commands/michi/project-switch.md +8 -2
- package/templates/claude/commands/michi/spec-design.md +78 -0
- package/templates/claude/commands/michi/spec-impl.md +716 -0
- package/templates/claude/commands/michi/test-planning.md +174 -0
- package/templates/claude/commands/michi/validate-design.md +58 -0
- package/templates/claude/commands/michi/version-audit.md +4 -0
- package/templates/michi/cc-sdd-overrides/README.md +8 -0
- package/templates/michi/cc-sdd-overrides/settings/rules/design-review-michi.md +53 -0
- package/dist/scripts/config-interactive.d.ts +0 -10
- package/dist/scripts/config-interactive.d.ts.map +0 -1
- package/dist/scripts/config-interactive.js +0 -372
- package/dist/scripts/config-interactive.js.map +0 -1
- package/dist/scripts/setup-existing-project.d.ts +0 -15
- package/dist/scripts/setup-existing-project.d.ts.map +0 -1
- package/dist/scripts/setup-existing-project.js +0 -455
- package/dist/scripts/setup-existing-project.js.map +0 -1
- package/dist/scripts/setup-interactive.d.ts +0 -10
- package/dist/scripts/setup-interactive.d.ts.map +0 -1
- package/dist/scripts/setup-interactive.js +0 -413
- package/dist/scripts/setup-interactive.js.map +0 -1
- package/scripts/config-interactive.ts +0 -550
- package/scripts/setup-existing-project.ts +0 -585
- package/scripts/setup-interactive.ts +0 -565
package/scripts/phase-runner.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 各フェーズを実行し、Confluence/JIRA作成を確実に実行
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { existsSync, writeFileSync, readFileSync
|
|
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:
|
|
78
|
-
|
|
79
|
-
|
|
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:
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
282
|
+
' AI-DLC形式のままではJIRA連携が正常に動作しない可能性があります。',
|
|
283
|
+
);
|
|
284
|
+
errors.push(
|
|
285
|
+
'tasks.mdがAI-DLC形式のため、フォーマット検証をスキップしました',
|
|
268
286
|
);
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
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:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
//
|
|
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:
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
const validation = validatePhase(feature, 'tasks');
|
|
364
|
-
errors.push(...validation.errors);
|
|
350
|
+
return { jiraCreated, errors };
|
|
351
|
+
}
|
|
365
352
|
|
|
366
|
-
|
|
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
|
-
*
|
|
627
|
-
* 自動生成: test-type-selectionから選択されたテストタイプの仕様書を生成
|
|
378
|
+
* タスク分割フェーズを実行
|
|
628
379
|
*/
|
|
629
|
-
async function
|
|
630
|
-
console.log('\n📝 Phase
|
|
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
|
-
//
|
|
636
|
-
const
|
|
637
|
-
|
|
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: '
|
|
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
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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: '
|
|
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
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
-
|
|
743
|
-
|
|
425
|
+
// サマリー表示
|
|
426
|
+
displayTasksPhaseSummary(jiraCreated, validation);
|
|
744
427
|
|
|
745
428
|
return {
|
|
746
|
-
phase: '
|
|
747
|
-
success:
|
|
429
|
+
phase: 'tasks',
|
|
430
|
+
success: validation.valid && jiraCreated,
|
|
748
431
|
confluenceCreated: false,
|
|
749
|
-
jiraCreated
|
|
750
|
-
validationPassed:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
892
|
-
|
|
893
|
-
|
|
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:
|
|
902
|
-
|
|
903
|
-
|
|
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:
|
|
913
|
-
|
|
914
|
-
|
|
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
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
-
|
|
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
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1070
|
-
* テスト実行ファイルを自動生成し、手動テストチェックリストを表示
|
|
830
|
+
* Phase B対象のテストタイプを読み込み
|
|
1071
831
|
*/
|
|
1072
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
1119
|
-
|
|
872
|
+
return phaseBTypes;
|
|
873
|
+
}
|
|
1120
874
|
|
|
1121
|
-
|
|
1122
|
-
|
|
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
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
console.
|
|
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
|
-
|
|
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
|
-
//
|
|
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,
|
|
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
|
-
|
|
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
|
}
|