@sk8metal/michi-cli 0.0.1 → 0.0.2

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 (47) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +24 -24
  3. package/dist/scripts/__tests__/validate-phase.test.d.ts +5 -0
  4. package/dist/scripts/__tests__/validate-phase.test.d.ts.map +1 -0
  5. package/dist/scripts/__tests__/validate-phase.test.js +162 -0
  6. package/dist/scripts/__tests__/validate-phase.test.js.map +1 -0
  7. package/dist/scripts/utils/__tests__/config-validator.test.d.ts +5 -0
  8. package/dist/scripts/utils/__tests__/config-validator.test.d.ts.map +1 -0
  9. package/dist/scripts/utils/__tests__/config-validator.test.js +247 -0
  10. package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -0
  11. package/dist/scripts/utils/__tests__/feature-name-validator.test.d.ts +5 -0
  12. package/dist/scripts/utils/__tests__/feature-name-validator.test.d.ts.map +1 -0
  13. package/dist/scripts/utils/__tests__/feature-name-validator.test.js +106 -0
  14. package/dist/scripts/utils/__tests__/feature-name-validator.test.js.map +1 -0
  15. package/dist/src/__tests__/cli.test.d.ts +5 -0
  16. package/dist/src/__tests__/cli.test.d.ts.map +1 -0
  17. package/dist/src/__tests__/cli.test.js +58 -0
  18. package/dist/src/__tests__/cli.test.js.map +1 -0
  19. package/docs/setup.md +1 -1
  20. package/package.json +5 -3
  21. package/scripts/__tests__/README.md +101 -0
  22. package/scripts/__tests__/validate-phase.test.ts +185 -0
  23. package/scripts/config/config-schema.ts +130 -0
  24. package/scripts/config/default-config.json +57 -0
  25. package/scripts/config-interactive.ts +494 -0
  26. package/scripts/confluence-sync.ts +503 -0
  27. package/scripts/create-project.ts +293 -0
  28. package/scripts/jira-sync.ts +644 -0
  29. package/scripts/list-projects.ts +85 -0
  30. package/scripts/markdown-to-confluence.ts +161 -0
  31. package/scripts/multi-project-estimate.ts +255 -0
  32. package/scripts/phase-runner.ts +303 -0
  33. package/scripts/pr-automation.ts +67 -0
  34. package/scripts/pre-flight-check.ts +285 -0
  35. package/scripts/resource-dashboard.ts +124 -0
  36. package/scripts/setup-env.sh +52 -0
  37. package/scripts/setup-existing-project.ts +381 -0
  38. package/scripts/setup-existing.sh +145 -0
  39. package/scripts/utils/__tests__/config-validator.test.ts +302 -0
  40. package/scripts/utils/__tests__/feature-name-validator.test.ts +129 -0
  41. package/scripts/utils/config-loader.ts +326 -0
  42. package/scripts/utils/config-validator.ts +347 -0
  43. package/scripts/utils/confluence-hierarchy.ts +854 -0
  44. package/scripts/utils/feature-name-validator.ts +135 -0
  45. package/scripts/utils/project-meta.ts +69 -0
  46. package/scripts/validate-phase.ts +279 -0
  47. package/scripts/workflow-orchestrator.ts +178 -0
@@ -0,0 +1,135 @@
1
+ /**
2
+ * 機能名(feature)のバリデーション
3
+ * kebab-case形式を強制
4
+ */
5
+
6
+ export interface ValidationResult {
7
+ valid: boolean;
8
+ errors: string[];
9
+ }
10
+
11
+ /**
12
+ * kebab-case形式の正規表現
13
+ * - 小文字の英数字で始まる
14
+ * - 小文字の英数字とハイフンのみ
15
+ * - 連続したハイフン不可
16
+ * - 先頭・末尾のハイフン不可
17
+ */
18
+ const KEBAB_CASE_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
19
+
20
+ /**
21
+ * feature名がkebab-case形式か検証
22
+ */
23
+ export function validateFeatureName(featureName: string): ValidationResult {
24
+ const errors: string[] = [];
25
+
26
+ // 空文字チェック
27
+ if (!featureName || featureName.trim().length === 0) {
28
+ errors.push('❌ feature名が空です');
29
+ return { valid: false, errors };
30
+ }
31
+
32
+ const trimmed = featureName.trim();
33
+
34
+ // kebab-case形式チェック
35
+ if (!KEBAB_CASE_PATTERN.test(trimmed)) {
36
+ errors.push(`❌ feature名が無効な形式です: "${trimmed}"`);
37
+ errors.push(' 必須形式: 英語、kebab-case(ハイフン区切り)、小文字のみ');
38
+
39
+ // 具体的な問題を特定
40
+ if (/[A-Z]/.test(trimmed)) {
41
+ errors.push(' → 大文字が含まれています(小文字に変換してください)');
42
+ }
43
+ if (/[ぁ-んァ-ヶー一-龯]/.test(trimmed)) {
44
+ errors.push(' → 日本語が含まれています(英語に変換してください)');
45
+ }
46
+ if (/_/.test(trimmed)) {
47
+ errors.push(' → アンダースコア(_)が含まれています(ハイフン(-)を使用してください)');
48
+ }
49
+ if (/\s/.test(trimmed)) {
50
+ errors.push(' → スペースが含まれています(ハイフン(-)を使用してください)');
51
+ }
52
+ if (trimmed.startsWith('-') || trimmed.endsWith('-')) {
53
+ errors.push(' → 先頭または末尾にハイフンがあります(削除してください)');
54
+ }
55
+ if (/--/.test(trimmed)) {
56
+ errors.push(' → 連続したハイフンがあります(1つに減らしてください)');
57
+ }
58
+ if (/[^a-z0-9-]/.test(trimmed)) {
59
+ const invalidChars = trimmed.match(/[^a-z0-9-]/g);
60
+ errors.push(` → 使用できない文字が含まれています: ${[...new Set(invalidChars)].join(', ')}`);
61
+ }
62
+
63
+ // 修正案を提示
64
+ const suggestion = suggestFeatureName(trimmed);
65
+ if (suggestion && suggestion !== trimmed) {
66
+ errors.push(` 💡 修正案: "${suggestion}"`);
67
+ }
68
+ }
69
+
70
+ // 長さチェック(推奨)
71
+ if (trimmed.length > 50) {
72
+ errors.push(`⚠️ feature名が長すぎます(${trimmed.length}文字)。50文字以内を推奨`);
73
+ }
74
+
75
+ // 単語数チェック(推奨)
76
+ const wordCount = trimmed.split('-').length;
77
+ if (wordCount > 5) {
78
+ errors.push(`⚠️ 単語数が多すぎます(${wordCount}単語)。2-4単語を推奨`);
79
+ }
80
+
81
+ return {
82
+ valid: errors.length === 0,
83
+ errors
84
+ };
85
+ }
86
+
87
+ /**
88
+ * 不正なfeature名を修正案に変換
89
+ */
90
+ export function suggestFeatureName(input: string): string {
91
+ return input
92
+ .toLowerCase() // 小文字に変換
93
+ .replace(/[ぁ-んァ-ヶー一-龯]/g, '') // 日本語削除
94
+ .replace(/\s+/g, '-') // スペース→ハイフン
95
+ .replace(/_+/g, '-') // アンダースコア→ハイフン
96
+ .replace(/[^a-z0-9-]/g, '') // 無効文字削除
97
+ .replace(/--+/g, '-') // 連続ハイフン→1つ
98
+ .replace(/^-+|-+$/g, ''); // 先頭・末尾ハイフン削除
99
+ }
100
+
101
+ /**
102
+ * feature名をバリデートし、エラー時は例外をスロー
103
+ */
104
+ export function validateFeatureNameOrThrow(featureName: string): void {
105
+ const result = validateFeatureName(featureName);
106
+
107
+ if (!result.valid) {
108
+ const errorMessage = [
109
+ `Invalid feature name: "${featureName}"`,
110
+ '',
111
+ ...result.errors,
112
+ '',
113
+ 'ヘルプ: README.md#機能名(feature)の命名規則 を参照してください'
114
+ ].join('\n');
115
+
116
+ throw new Error(errorMessage);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * feature名をバリデートし、警告を表示(続行可能)
122
+ */
123
+ export function validateFeatureNameWithWarning(featureName: string): boolean {
124
+ const result = validateFeatureName(featureName);
125
+
126
+ if (!result.valid) {
127
+ console.error('\n⚠️ Feature name validation failed:');
128
+ result.errors.forEach(err => console.error(err));
129
+ console.error('\nヘルプ: README.md#機能名(feature)の命名規則 を参照してください\n');
130
+ return false;
131
+ }
132
+
133
+ return true;
134
+ }
135
+
@@ -0,0 +1,69 @@
1
+ /**
2
+ * プロジェクトメタデータ読み込みユーティリティ
3
+ */
4
+
5
+ import { readFileSync, existsSync } from 'fs';
6
+ import { resolve } from 'path';
7
+
8
+ export interface ProjectMetadata {
9
+ projectId: string;
10
+ projectName: string;
11
+ jiraProjectKey: string;
12
+ confluenceLabels: string[];
13
+ status: 'active' | 'inactive' | 'completed';
14
+ team: string[];
15
+ stakeholders: string[];
16
+ repository: string;
17
+ description?: string;
18
+ }
19
+
20
+ /**
21
+ * .kiro/project.json を読み込む
22
+ */
23
+ export function loadProjectMeta(projectRoot: string = process.cwd()): ProjectMetadata {
24
+ const projectJsonPath = resolve(projectRoot, '.kiro/project.json');
25
+
26
+ if (!existsSync(projectJsonPath)) {
27
+ throw new Error(`Project metadata not found: ${projectJsonPath}`);
28
+ }
29
+
30
+ try {
31
+ const content = readFileSync(projectJsonPath, 'utf-8');
32
+ const meta = JSON.parse(content) as ProjectMetadata;
33
+
34
+ // 必須フィールドのバリデーション
35
+ const requiredFields: (keyof ProjectMetadata)[] = [
36
+ 'projectId',
37
+ 'projectName',
38
+ 'jiraProjectKey',
39
+ 'confluenceLabels'
40
+ ];
41
+
42
+ for (const field of requiredFields) {
43
+ if (!meta[field]) {
44
+ throw new Error(`Required field missing in project.json: ${field}`);
45
+ }
46
+ }
47
+
48
+ return meta;
49
+ } catch (error) {
50
+ if (error instanceof SyntaxError) {
51
+ throw new Error(`Invalid JSON in ${projectJsonPath}: ${error.message}`);
52
+ }
53
+ throw error;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * プロジェクトメタデータを表示用にフォーマット
59
+ */
60
+ export function formatProjectInfo(meta: ProjectMetadata): string {
61
+ return `
62
+ Project: ${meta.projectName} (${meta.projectId})
63
+ JIRA: ${meta.jiraProjectKey}
64
+ Labels: ${meta.confluenceLabels.join(', ')}
65
+ Status: ${meta.status}
66
+ Team: ${meta.team.join(', ')}
67
+ `.trim();
68
+ }
69
+
@@ -0,0 +1,279 @@
1
+ /**
2
+ * フェーズ完了バリデーションスクリプト
3
+ * 各フェーズで必須項目が完了しているかチェック
4
+ */
5
+
6
+ import { existsSync, readFileSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { loadProjectMeta } from './utils/project-meta.js';
9
+ import { validateFeatureName } from './utils/feature-name-validator.js';
10
+
11
+ type Phase = 'requirements' | 'design' | 'tasks';
12
+
13
+ interface ValidationResult {
14
+ phase: Phase;
15
+ valid: boolean;
16
+ errors: string[];
17
+ warnings: string[];
18
+ }
19
+
20
+ /**
21
+ * spec.jsonを読み込み
22
+ */
23
+ function loadSpecJson(feature: string): any {
24
+ const specPath = join(process.cwd(), '.kiro', 'specs', feature, 'spec.json');
25
+
26
+ if (!existsSync(specPath)) {
27
+ throw new Error(`spec.json not found: ${specPath}`);
28
+ }
29
+
30
+ return JSON.parse(readFileSync(specPath, 'utf-8'));
31
+ }
32
+
33
+ /**
34
+ * 要件定義フェーズのバリデーション
35
+ */
36
+ function validateRequirements(feature: string): ValidationResult {
37
+ const errors: string[] = [];
38
+ const warnings: string[] = [];
39
+
40
+ // 0. feature名のバリデーション
41
+ const nameValidation = validateFeatureName(feature);
42
+ if (!nameValidation.valid) {
43
+ errors.push(...nameValidation.errors);
44
+ }
45
+
46
+ // 1. requirements.md存在チェック
47
+ const requirementsPath = join(process.cwd(), '.kiro', 'specs', feature, 'requirements.md');
48
+ if (!existsSync(requirementsPath)) {
49
+ errors.push('❌ requirements.md が作成されていません');
50
+ }
51
+
52
+ // 2. spec.json読み込み
53
+ let spec: any;
54
+ try {
55
+ spec = loadSpecJson(feature);
56
+ } catch (error: any) {
57
+ errors.push(`❌ spec.json読み込みエラー: ${error.message}`);
58
+ return { phase: 'requirements', valid: false, errors, warnings };
59
+ }
60
+
61
+ // 3. Confluenceページ作成チェック(必須)
62
+ if (!spec.confluence?.requirementsPageId) {
63
+ errors.push('❌ Confluenceページ(要件定義)が作成されていません');
64
+ errors.push(' → 実行: npm run confluence:sync <feature> requirements');
65
+ }
66
+
67
+ // 4. spec.jsonのconfluence情報チェック
68
+ if (!spec.confluence?.spaceKey) {
69
+ errors.push('❌ spec.jsonにconfluence.spaceKeyが記録されていません');
70
+ }
71
+
72
+ // 5. マイルストーン更新チェック
73
+ if (!spec.milestones?.requirements?.completed) {
74
+ warnings.push('⚠️ spec.jsonのmilestones.requirements.completedがfalseです');
75
+ }
76
+
77
+ return {
78
+ phase: 'requirements',
79
+ valid: errors.length === 0,
80
+ errors,
81
+ warnings
82
+ };
83
+ }
84
+
85
+ /**
86
+ * 設計フェーズのバリデーション
87
+ */
88
+ function validateDesign(feature: string): ValidationResult {
89
+ const errors: string[] = [];
90
+ const warnings: string[] = [];
91
+
92
+ // 0. feature名のバリデーション
93
+ const nameValidation = validateFeatureName(feature);
94
+ if (!nameValidation.valid) {
95
+ errors.push(...nameValidation.errors);
96
+ }
97
+
98
+ // 1. design.md存在チェック
99
+ const designPath = join(process.cwd(), '.kiro', 'specs', feature, 'design.md');
100
+ if (!existsSync(designPath)) {
101
+ errors.push('❌ design.md が作成されていません');
102
+ }
103
+
104
+ // 2. spec.json読み込み
105
+ let spec: any;
106
+ try {
107
+ spec = loadSpecJson(feature);
108
+ } catch (error: any) {
109
+ errors.push(`❌ spec.json読み込みエラー: ${error.message}`);
110
+ return { phase: 'design', valid: false, errors, warnings };
111
+ }
112
+
113
+ // 3. 前提: 要件定義完了チェック
114
+ if (!spec.milestones?.requirements?.completed) {
115
+ errors.push('❌ 要件定義が完了していません(前提条件)');
116
+ }
117
+
118
+ // 4. Confluenceページ作成チェック(必須)
119
+ if (!spec.confluence?.designPageId) {
120
+ errors.push('❌ Confluenceページ(設計書)が作成されていません');
121
+ errors.push(' → 実行: npm run confluence:sync <feature> design');
122
+ }
123
+
124
+ // 5. マイルストーン更新チェック
125
+ if (!spec.milestones?.design?.completed) {
126
+ warnings.push('⚠️ spec.jsonのmilestones.design.completedがfalseです');
127
+ }
128
+
129
+ return {
130
+ phase: 'design',
131
+ valid: errors.length === 0,
132
+ errors,
133
+ warnings
134
+ };
135
+ }
136
+
137
+ /**
138
+ * タスク分割フェーズのバリデーション
139
+ */
140
+ function validateTasks(feature: string): ValidationResult {
141
+ const errors: string[] = [];
142
+ const warnings: string[] = [];
143
+
144
+ // 0. feature名のバリデーション
145
+ const nameValidation = validateFeatureName(feature);
146
+ if (!nameValidation.valid) {
147
+ errors.push(...nameValidation.errors);
148
+ }
149
+
150
+ // 1. tasks.md存在チェック
151
+ const tasksPath = join(process.cwd(), '.kiro', 'specs', feature, 'tasks.md');
152
+ if (!existsSync(tasksPath)) {
153
+ errors.push('❌ tasks.md が作成されていません');
154
+ } else {
155
+ // 営業日表記チェック
156
+ const tasksContent = readFileSync(tasksPath, 'utf-8');
157
+ if (!tasksContent.includes('(月)') && !tasksContent.includes('(火)')) {
158
+ warnings.push('⚠️ tasks.mdに曜日表記(月、火、水...)が含まれていません');
159
+ }
160
+ if (!tasksContent.includes('Day 1') && !tasksContent.includes('Day1')) {
161
+ warnings.push('⚠️ tasks.mdに営業日カウント(Day 1, Day 2...)が含まれていません');
162
+ }
163
+ if (!tasksContent.includes('土日')) {
164
+ warnings.push('⚠️ tasks.mdに土日休みの明記がありません');
165
+ }
166
+ }
167
+
168
+ // 2. spec.json読み込み
169
+ let spec: any;
170
+ try {
171
+ spec = loadSpecJson(feature);
172
+ } catch (error: any) {
173
+ errors.push(`❌ spec.json読み込みエラー: ${error.message}`);
174
+ return { phase: 'tasks', valid: false, errors, warnings };
175
+ }
176
+
177
+ // 3. 前提: 設計完了チェック
178
+ if (!spec.milestones?.design?.completed) {
179
+ errors.push('❌ 設計が完了していません(前提条件)');
180
+ }
181
+
182
+ // 4. JIRA Epic作成チェック(必須)
183
+ if (!spec.jira?.epicKey) {
184
+ errors.push('❌ JIRA Epicが作成されていません');
185
+ errors.push(' → 実行: npm run jira:sync <feature>');
186
+ }
187
+
188
+ // 5. JIRA Story作成チェック(必須)
189
+ if (!spec.jira?.stories || spec.jira.stories.created === 0) {
190
+ errors.push('❌ JIRA Storyが1つも作成されていません');
191
+ errors.push(' → 実行: npm run jira:sync <feature>');
192
+ } else if (spec.jira.stories.created < spec.jira.stories.total) {
193
+ warnings.push(`⚠️ JIRA Storyが一部未作成: ${spec.jira.stories.created}/${spec.jira.stories.total}`);
194
+ }
195
+
196
+ // 6. マイルストーン更新チェック
197
+ if (!spec.milestones?.tasks?.completed) {
198
+ warnings.push('⚠️ spec.jsonのmilestones.tasks.completedがfalseです');
199
+ }
200
+
201
+ return {
202
+ phase: 'tasks',
203
+ valid: errors.length === 0,
204
+ errors,
205
+ warnings
206
+ };
207
+ }
208
+
209
+ /**
210
+ * フェーズをバリデート
211
+ */
212
+ export function validatePhase(feature: string, phase: Phase): ValidationResult {
213
+ console.log(`\n🔍 Validating phase: ${phase} for feature: ${feature}`);
214
+
215
+ let result: ValidationResult;
216
+
217
+ switch (phase) {
218
+ case 'requirements':
219
+ result = validateRequirements(feature);
220
+ break;
221
+ case 'design':
222
+ result = validateDesign(feature);
223
+ break;
224
+ case 'tasks':
225
+ result = validateTasks(feature);
226
+ break;
227
+ default:
228
+ throw new Error(`Unknown phase: ${phase}`);
229
+ }
230
+
231
+ // 結果表示
232
+ console.log('\n📊 Validation Result:');
233
+
234
+ if (result.errors.length > 0) {
235
+ console.log('\n❌ エラー:');
236
+ result.errors.forEach(err => console.log(` ${err}`));
237
+ }
238
+
239
+ if (result.warnings.length > 0) {
240
+ console.log('\n⚠️ 警告:');
241
+ result.warnings.forEach(warn => console.log(` ${warn}`));
242
+ }
243
+
244
+ if (result.valid) {
245
+ console.log('\n✅ バリデーション成功: すべての必須項目が完了しています');
246
+ } else {
247
+ console.log('\n❌ バリデーション失敗: 上記のエラーを修正してください');
248
+ }
249
+
250
+ return result;
251
+ }
252
+
253
+ // CLI実行
254
+ if (import.meta.url === `file://${process.argv[1]}`) {
255
+ const args = process.argv.slice(2);
256
+
257
+ if (args.length < 2) {
258
+ console.error('Usage: npm run validate:phase <feature> <phase>');
259
+ console.error('Example: npm run validate:phase calculator-app requirements');
260
+ console.error('Phases: requirements, design, tasks');
261
+ process.exit(1);
262
+ }
263
+
264
+ const [feature, phase] = args;
265
+
266
+ if (!['requirements', 'design', 'tasks'].includes(phase)) {
267
+ console.error('Invalid phase. Must be: requirements, design, or tasks');
268
+ process.exit(1);
269
+ }
270
+
271
+ try {
272
+ const result = validatePhase(feature, phase as Phase);
273
+ process.exit(result.valid ? 0 : 1);
274
+ } catch (error: any) {
275
+ console.error(`\n❌ Validation error: ${error.message}`);
276
+ process.exit(1);
277
+ }
278
+ }
279
+
@@ -0,0 +1,178 @@
1
+ /**
2
+ * ワークフローオーケストレーター
3
+ * AI開発フロー全体を統合実行
4
+ */
5
+
6
+ import { config } from 'dotenv';
7
+ import { loadProjectMeta } from './utils/project-meta.js';
8
+ import { syncToConfluence } from './confluence-sync.js';
9
+ import { syncTasksToJIRA } from './jira-sync.js';
10
+ import { createPR } from './pr-automation.js';
11
+
12
+ config();
13
+
14
+ export interface WorkflowConfig {
15
+ feature: string;
16
+ stages: WorkflowStage[];
17
+ approvalGates?: {
18
+ requirements?: string[];
19
+ design?: string[];
20
+ release?: string[];
21
+ };
22
+ }
23
+
24
+ export type WorkflowStage =
25
+ | 'requirements'
26
+ | 'design'
27
+ | 'tasks'
28
+ | 'implement'
29
+ | 'test'
30
+ | 'release';
31
+
32
+ export class WorkflowOrchestrator {
33
+ private config: WorkflowConfig;
34
+
35
+ constructor(config: WorkflowConfig) {
36
+ this.config = config;
37
+ }
38
+
39
+ /**
40
+ * ワークフロー全体を実行
41
+ */
42
+ async run(): Promise<void> {
43
+ console.log(`🚀 Starting workflow for: ${this.config.feature}`);
44
+ console.log(`Stages: ${this.config.stages.join(' → ')}`);
45
+
46
+ const projectMeta = loadProjectMeta();
47
+ console.log(`Project: ${projectMeta.projectName}`);
48
+
49
+ for (const stage of this.config.stages) {
50
+ console.log(`\n📋 Stage: ${stage}`);
51
+
52
+ try {
53
+ await this.executeStage(stage);
54
+
55
+ // 承認ゲートチェック
56
+ if (this.hasApprovalGate(stage)) {
57
+ await this.waitForApproval(stage);
58
+ }
59
+
60
+ console.log(`✅ Stage completed: ${stage}`);
61
+ } catch (error: any) {
62
+ console.error(`❌ Stage failed: ${stage}`, error.message);
63
+ throw error;
64
+ }
65
+ }
66
+
67
+ console.log('\n🎉 Workflow completed successfully!');
68
+ }
69
+
70
+ /**
71
+ * 各ステージを実行
72
+ */
73
+ private async executeStage(stage: WorkflowStage): Promise<void> {
74
+ switch (stage) {
75
+ case 'requirements':
76
+ console.log(' Syncing requirements to Confluence...');
77
+ await syncToConfluence(this.config.feature, 'requirements');
78
+ break;
79
+
80
+ case 'design':
81
+ console.log(' Syncing design to Confluence...');
82
+ await syncToConfluence(this.config.feature, 'design');
83
+ break;
84
+
85
+ case 'tasks':
86
+ console.log(' Creating JIRA tasks...');
87
+ await syncTasksToJIRA(this.config.feature);
88
+ break;
89
+
90
+ case 'implement':
91
+ console.log(' Implementation phase - manual step');
92
+ console.log(' Use: /kiro:spec-impl <feature> <tasks>');
93
+ break;
94
+
95
+ case 'test':
96
+ console.log(' Test phase - execute tests');
97
+ // TODO: テスト実行とレポート生成
98
+ break;
99
+
100
+ case 'release':
101
+ console.log(' Release preparation');
102
+ // TODO: リリースノート生成とJIRA Release作成
103
+ break;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 承認ゲートがあるかチェック
109
+ */
110
+ private hasApprovalGate(stage: WorkflowStage): boolean {
111
+ const gates = this.config.approvalGates;
112
+ if (!gates) return false;
113
+
114
+ const gateList =
115
+ stage === 'requirements' ? gates.requirements :
116
+ stage === 'design' ? gates.design :
117
+ stage === 'release' ? gates.release :
118
+ undefined;
119
+
120
+ return Array.isArray(gateList) && gateList.length > 0;
121
+ }
122
+
123
+ /**
124
+ * 承認を待つ
125
+ */
126
+ private async waitForApproval(stage: WorkflowStage): Promise<void> {
127
+ console.log(`\n⏸️ Approval required for: ${stage}`);
128
+
129
+ const approvers = this.config.approvalGates?.[stage as keyof typeof this.config.approvalGates];
130
+ if (approvers) {
131
+ console.log(` Approvers: ${approvers.join(', ')}`);
132
+ }
133
+
134
+ console.log(' ✅ Confluence で承認してください');
135
+ console.log(' ⏳ 承認完了後、次のステージに進みます');
136
+
137
+ // TODO: Confluence APIで承認状態をポーリング
138
+ // 現在は手動確認
139
+ console.log(' (手動で承認を確認してください)');
140
+ }
141
+ }
142
+
143
+ // CLI実行
144
+ if (import.meta.url === `file://${process.argv[1]}`) {
145
+ const args = process.argv.slice(2);
146
+
147
+ if (args.length === 0) {
148
+ console.error('Usage: npm run workflow:run -- --feature <feature_name>');
149
+ process.exit(1);
150
+ }
151
+
152
+ const featureIndex = args.indexOf('--feature');
153
+ const feature = featureIndex >= 0 ? args[featureIndex + 1] : undefined;
154
+
155
+ if (featureIndex === -1 || !feature) {
156
+ console.error('Usage: npm run workflow:run -- --feature <feature_name>');
157
+ process.exit(1);
158
+ }
159
+
160
+ const workflowConfig: WorkflowConfig = {
161
+ feature,
162
+ stages: ['requirements', 'design', 'tasks', 'implement', 'test', 'release'],
163
+ approvalGates: {
164
+ requirements: ['企画', '部長'],
165
+ design: ['アーキテクト', '部長'],
166
+ release: ['SM', '部長']
167
+ }
168
+ };
169
+
170
+ const orchestrator = new WorkflowOrchestrator(workflowConfig);
171
+
172
+ orchestrator.run()
173
+ .then(() => process.exit(0))
174
+ .catch((error) => {
175
+ console.error('❌ Workflow failed:', error.message);
176
+ process.exit(1);
177
+ });
178
+ }