@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
@@ -0,0 +1,135 @@
1
+ /**
2
+ * 対話的設定の共通ヘルパー関数
3
+ */
4
+
5
+ import * as readline from 'readline';
6
+
7
+ /**
8
+ * readlineインターフェースを作成
9
+ */
10
+ export function createInterface(): readline.Interface {
11
+ return readline.createInterface({
12
+ input: process.stdin,
13
+ output: process.stdout,
14
+ });
15
+ }
16
+
17
+ /**
18
+ * 質問を表示して回答を取得
19
+ */
20
+ export function question(rl: readline.Interface, query: string): Promise<string> {
21
+ return new Promise((resolve) => {
22
+ rl.question(query, (answer) => {
23
+ resolve(answer.trim());
24
+ });
25
+ });
26
+ }
27
+
28
+ /**
29
+ * 選択肢から選択
30
+ */
31
+ export async function select(
32
+ rl: readline.Interface,
33
+ prompt: string,
34
+ choices: Array<{ value: string; label: string; description?: string }>,
35
+ defaultValue?: string,
36
+ maxRetries: number = 3,
37
+ ): Promise<string> {
38
+ if (choices.length === 0) {
39
+ throw new Error('select: choices must contain at least one option');
40
+ }
41
+
42
+ console.log(`\n${prompt}`);
43
+ choices.forEach((choice, index) => {
44
+ const defaultMark = defaultValue === choice.value ? ' (デフォルト)' : '';
45
+ const desc = choice.description ? ` - ${choice.description}` : '';
46
+ console.log(` ${index + 1}. ${choice.label}${desc}${defaultMark}`);
47
+ });
48
+
49
+ const answer = await question(
50
+ rl,
51
+ `\n選択してください [1-${choices.length}]: `,
52
+ );
53
+
54
+ if (!answer && defaultValue) {
55
+ return defaultValue;
56
+ }
57
+
58
+ const index = parseInt(answer, 10) - 1;
59
+ if (index >= 0 && index < choices.length) {
60
+ return choices[index].value;
61
+ }
62
+
63
+ if (defaultValue) {
64
+ return defaultValue;
65
+ }
66
+
67
+ // 無効な入力の場合は再試行(最大試行回数まで)
68
+ if (maxRetries > 0) {
69
+ console.log(
70
+ `⚠️ 無効な選択です。もう一度入力してください(残り試行回数: ${maxRetries})。`,
71
+ );
72
+ return select(rl, prompt, choices, defaultValue, maxRetries - 1);
73
+ }
74
+
75
+ // 最大試行回数に達した場合はデフォルト値または最初の選択肢を返す
76
+ // 注: choices.length > 0 は関数冒頭で保証されている
77
+ const fallbackValue = defaultValue || choices[0].value;
78
+ console.log(
79
+ `⚠️ 最大試行回数に達しました。デフォルト値を使用します: ${fallbackValue}`,
80
+ );
81
+ return fallbackValue;
82
+ }
83
+
84
+ /**
85
+ * Yes/No質問
86
+ */
87
+ export async function confirm(
88
+ rl: readline.Interface,
89
+ prompt: string,
90
+ defaultValue: boolean = true,
91
+ ): Promise<boolean> {
92
+ const defaultText = defaultValue ? '[Y/n]' : '[y/N]';
93
+ const answer = await question(rl, `${prompt} ${defaultText}: `);
94
+
95
+ if (!answer) {
96
+ return defaultValue;
97
+ }
98
+
99
+ return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
100
+ }
101
+
102
+ /**
103
+ * 複数選択
104
+ */
105
+ export async function multiSelect(
106
+ rl: readline.Interface,
107
+ prompt: string,
108
+ choices: Array<{ value: string; label: string }>,
109
+ defaults: string[] = [],
110
+ ): Promise<string[]> {
111
+ console.log(`\n${prompt}`);
112
+ choices.forEach((choice, index) => {
113
+ const checked = defaults.includes(choice.value) ? '[x]' : '[ ]';
114
+ console.log(` ${checked} ${index + 1}. ${choice.label}`);
115
+ });
116
+
117
+ const answer = await question(
118
+ rl,
119
+ '\n選択してください(カンマ区切り、例: 1,2,3): ',
120
+ );
121
+
122
+ if (!answer && defaults.length > 0) {
123
+ return defaults;
124
+ }
125
+
126
+ if (!answer) {
127
+ return [];
128
+ }
129
+
130
+ const indices = answer
131
+ .split(',')
132
+ .map((s) => parseInt(s.trim(), 10) - 1)
133
+ .filter((i) => i >= 0 && i < choices.length);
134
+ return indices.map((i) => choices[i].value);
135
+ }
@@ -100,38 +100,46 @@ export async function getProjectIssueTypes(
100
100
  });
101
101
 
102
102
  const issueTypes = response.data.issueTypes || [];
103
-
104
- return issueTypes.map((it: any) => ({
103
+
104
+ return issueTypes.map((it: Partial<IssueTypeInfo> & { id: string; name: string }) => ({
105
105
  id: it.id,
106
106
  name: it.name,
107
107
  description: it.description || undefined,
108
108
  iconUrl: it.iconUrl || undefined,
109
109
  subtask: it.subtask || false
110
110
  }));
111
- } catch (error: any) {
111
+ } catch (error: unknown) {
112
112
  // エラーをログに記録(デバッグ用)
113
- if (error.response) {
114
- // HTTPエラー(4xx, 5xx)
115
- const status = error.response.status;
116
- const statusText = error.response.statusText;
117
-
118
- if (status === 401) {
119
- console.error('❌ JIRA認証に失敗しました。認証情報を確認してください。');
120
- } else if (status === 403) {
121
- console.error('❌ JIRAへのアクセス権限がありません。');
122
- } else if (status === 404) {
123
- console.error(`❌ JIRAプロジェクト "${projectKey}" が見つかりません。`);
113
+ // axios.isAxiosError()またはerror.response/requestの存在で判定
114
+ if (axios.isAxiosError(error) || (typeof error === 'object' && error !== null && ('response' in error || 'request' in error))) {
115
+ const axiosError = error as { response?: { status: number; statusText: string }; request?: unknown; message?: string };
116
+ if (axiosError.response) {
117
+ // HTTPエラー(4xx, 5xx)
118
+ const status = axiosError.response.status;
119
+ const statusText = axiosError.response.statusText;
120
+
121
+ if (status === 401) {
122
+ console.error('❌ JIRA認証に失敗しました。認証情報を確認してください。');
123
+ } else if (status === 403) {
124
+ console.error('❌ JIRAへのアクセス権限がありません。');
125
+ } else if (status === 404) {
126
+ console.error(`❌ JIRAプロジェクト "${projectKey}" が見つかりません。`);
127
+ } else {
128
+ console.error(`❌ JIRA APIエラー: ${status} ${statusText}`);
129
+ }
130
+ } else if (axiosError.request) {
131
+ // ネットワークエラー
132
+ console.error('❌ JIRA APIへの接続に失敗しました。ネットワークを確認してください。');
124
133
  } else {
125
- console.error(`❌ JIRA APIエラー: ${status} ${statusText}`);
134
+ // その他のエラー
135
+ const message = axiosError.message || String(error);
136
+ console.error(`❌ エラー: ${message}`);
126
137
  }
127
- } else if (error.request) {
128
- // ネットワークエラー
129
- console.error('❌ JIRA APIへの接続に失敗しました。ネットワークを確認してください。');
130
138
  } else {
131
- // その他のエラー
132
- console.error(`❌ エラー: ${error.message}`);
139
+ const message = error instanceof Error ? error.message : String(error);
140
+ console.error(`❌ エラー: ${message}`);
133
141
  }
134
-
142
+
135
143
  return null;
136
144
  }
137
145
  }
@@ -101,8 +101,9 @@ export async function getCommits(
101
101
  }
102
102
 
103
103
  return commits;
104
- } catch (error: any) {
105
- console.error('Failed to get commits:', error.message);
104
+ } catch (error: unknown) {
105
+ const message = error instanceof Error ? error.message : String(error);
106
+ console.error('Failed to get commits:', message);
106
107
  return [];
107
108
  }
108
109
  }
@@ -29,12 +29,24 @@ export interface SpecJson {
29
29
  url?: string;
30
30
  title?: string;
31
31
  };
32
+ // 旧形式フィールド(後方互換性のため)
33
+ requirementsPageId?: string;
34
+ requirementsUrl?: string;
35
+ designPageId?: string;
36
+ designUrl?: string;
37
+ tasksPageId?: string;
38
+ tasksUrl?: string;
32
39
  };
33
40
  jira?: {
34
41
  projectKey?: string;
35
42
  epicKey?: string;
36
43
  epicUrl?: string;
37
44
  storyKeys?: string[];
45
+ // 旧形式フィールド(後方互換性のため)
46
+ stories?: {
47
+ created?: number;
48
+ total?: number;
49
+ };
38
50
  };
39
51
  environmentSetup?: {
40
52
  completed?: boolean;
@@ -48,6 +60,16 @@ export interface SpecJson {
48
60
  designCompleted?: boolean;
49
61
  tasksCompleted?: boolean;
50
62
  jiraSyncCompleted?: boolean;
63
+ // 旧形式フィールド(後方互換性のため)
64
+ requirements?: {
65
+ completed?: boolean;
66
+ };
67
+ design?: {
68
+ completed?: boolean;
69
+ };
70
+ tasks?: {
71
+ completed?: boolean;
72
+ };
51
73
  };
52
74
  lastUpdated?: string;
53
75
  }
@@ -144,14 +166,14 @@ export function updateSpecJsonAfterConfluenceSync(
144
166
 
145
167
  // 旧形式(後方互換性のため併記)
146
168
  if (docType === 'requirements') {
147
- (spec.confluence as any).requirementsPageId = pageInfo.pageId;
148
- (spec.confluence as any).requirementsUrl = pageInfo.url;
169
+ spec.confluence.requirementsPageId = pageInfo.pageId;
170
+ spec.confluence.requirementsUrl = pageInfo.url;
149
171
  } else if (docType === 'design') {
150
- (spec.confluence as any).designPageId = pageInfo.pageId;
151
- (spec.confluence as any).designUrl = pageInfo.url;
172
+ spec.confluence.designPageId = pageInfo.pageId;
173
+ spec.confluence.designUrl = pageInfo.url;
152
174
  } else if (docType === 'tasks') {
153
- (spec.confluence as any).tasksPageId = pageInfo.pageId;
154
- (spec.confluence as any).tasksUrl = pageInfo.url;
175
+ spec.confluence.tasksPageId = pageInfo.pageId;
176
+ spec.confluence.tasksUrl = pageInfo.url;
155
177
  }
156
178
 
157
179
  // マイルストーンを更新
@@ -163,24 +185,24 @@ export function updateSpecJsonAfterConfluenceSync(
163
185
  if (docType === 'requirements') {
164
186
  spec.milestones.requirementsCompleted = true;
165
187
  // 旧形式(後方互換性のため併記)
166
- if (!(spec.milestones as any).requirements) {
167
- (spec.milestones as any).requirements = {};
188
+ if (!spec.milestones.requirements) {
189
+ spec.milestones.requirements = {};
168
190
  }
169
- (spec.milestones as any).requirements.completed = true;
191
+ spec.milestones.requirements.completed = true;
170
192
  } else if (docType === 'design') {
171
193
  spec.milestones.designCompleted = true;
172
194
  // 旧形式(後方互換性のため併記)
173
- if (!(spec.milestones as any).design) {
174
- (spec.milestones as any).design = {};
195
+ if (!spec.milestones.design) {
196
+ spec.milestones.design = {};
175
197
  }
176
- (spec.milestones as any).design.completed = true;
198
+ spec.milestones.design.completed = true;
177
199
  } else if (docType === 'tasks') {
178
200
  spec.milestones.tasksCompleted = true;
179
201
  // 旧形式(後方互換性のため併記)
180
- if (!(spec.milestones as any).tasks) {
181
- (spec.milestones as any).tasks = {};
202
+ if (!spec.milestones.tasks) {
203
+ spec.milestones.tasks = {};
182
204
  }
183
- (spec.milestones as any).tasks.completed = true;
205
+ spec.milestones.tasks.completed = true;
184
206
  }
185
207
 
186
208
  saveSpecJson(featureName, spec, projectRoot);
@@ -4,8 +4,7 @@
4
4
  * AI-DLC形式のtasks.mdをMichiが期待するPhase構造に変換
5
5
  */
6
6
 
7
- import { writeFileSync, copyFileSync, existsSync } from 'fs';
8
- import { join, dirname } from 'path';
7
+ import { writeFileSync, copyFileSync } from 'fs';
9
8
  import type {
10
9
  AIDLCDocument,
11
10
  AIDLCCategory,
@@ -20,7 +19,6 @@ import { readFileSync } from 'fs';
20
19
  import {
21
20
  getWeekdayNotation,
22
21
  getWeekdayRangeNotation,
23
- getWeekNumber,
24
22
  ensureBusinessDay,
25
23
  } from './business-days.js';
26
24
 
@@ -77,8 +75,8 @@ interface PhaseMapping {
77
75
  */
78
76
  function mapCategoryToPhase(
79
77
  category: AIDLCCategory,
80
- index: number,
81
- totalCategories: number,
78
+ _index: number,
79
+ _totalCategories: number,
82
80
  ): PhaseMapping {
83
81
  const title = category.title.toLowerCase();
84
82
 
@@ -364,7 +362,7 @@ export function convertToMichiFormat(
364
362
  const group = phaseGroups.get(mapping.phaseId)!;
365
363
 
366
364
  // タスクをStoryに変換
367
- category.tasks.forEach((task, taskIndex) => {
365
+ category.tasks.forEach(task => {
368
366
  const storyContent = convertTaskToStory(
369
367
  task,
370
368
  mapping.phaseId,
@@ -41,19 +41,6 @@ export function validateTasksFormat(tasksPath: string): void {
41
41
 
42
42
  // 2. フェーズ構造の検証(新旧両方をサポート)
43
43
 
44
- // 新ワークフロー構造(推奨)
45
- const newWorkflowPhases = [
46
- 'Phase 0.1:', // 要件定義
47
- 'Phase 0.2:', // 設計
48
- 'Phase 1:', // 環境構築(任意)
49
- 'Phase 2:', // TDD実装
50
- 'Phase A:', // PR前自動テスト(任意)
51
- 'Phase 3:', // 追加QA(任意)
52
- 'Phase B:', // リリース準備テスト(任意)
53
- 'Phase 4:', // リリース準備
54
- 'Phase 5:', // リリース
55
- ];
56
-
57
44
  // 旧6-Phase構造(互換性のためサポート)
58
45
  const legacyPhases = [
59
46
  'Phase 0: 要件定義(Requirements)',
@@ -53,15 +53,16 @@ export async function executeTests(
53
53
  duration,
54
54
  timestamp
55
55
  };
56
- } catch (error: any) {
56
+ } catch (error: unknown) {
57
57
  const duration = (Date.now() - startTime) / 1000;
58
+ const errorObj = error as { stdout?: string; message?: string };
58
59
 
59
60
  return {
60
61
  success: false,
61
62
  language,
62
63
  command,
63
- output: error.stdout || '',
64
- error: error.message || String(error),
64
+ output: errorObj.stdout || '',
65
+ error: errorObj.message || String(error),
65
66
  duration,
66
67
  timestamp
67
68
  };
@@ -7,12 +7,11 @@ import { existsSync, readFileSync } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { validateFeatureName } from './utils/feature-name-validator.js';
9
9
  import { loadConfig } from './utils/config-loader.js';
10
+ import { type SpecJson } from './utils/spec-updater.js';
10
11
 
11
12
  type Phase =
12
13
  | 'requirements'
13
14
  | 'design'
14
- | 'test-type-selection'
15
- | 'test-spec'
16
15
  | 'tasks'
17
16
  | 'environment-setup'
18
17
  | 'phase-a'
@@ -28,7 +27,7 @@ interface ValidationResult {
28
27
  /**
29
28
  * spec.jsonを読み込み
30
29
  */
31
- function loadSpecJson(feature: string): any {
30
+ function loadSpecJson(feature: string): SpecJson {
32
31
  const specPath = join(process.cwd(), '.kiro', 'specs', feature, 'spec.json');
33
32
 
34
33
  if (!existsSync(specPath)) {
@@ -58,11 +57,12 @@ function validateRequirements(feature: string): ValidationResult {
58
57
  }
59
58
 
60
59
  // 2. spec.json読み込み
61
- let spec: any;
60
+ let spec: SpecJson;
62
61
  try {
63
62
  spec = loadSpecJson(feature);
64
- } catch (error: any) {
65
- errors.push(`❌ spec.json読み込みエラー: ${error.message}`);
63
+ } catch (error: unknown) {
64
+ const message = error instanceof Error ? error.message : String(error);
65
+ errors.push(`❌ spec.json読み込みエラー: ${message}`);
66
66
  return { phase: 'requirements', valid: false, errors, warnings };
67
67
  }
68
68
 
@@ -110,11 +110,12 @@ function validateDesign(feature: string): ValidationResult {
110
110
  }
111
111
 
112
112
  // 2. spec.json読み込み
113
- let spec: any;
113
+ let spec: SpecJson;
114
114
  try {
115
115
  spec = loadSpecJson(feature);
116
- } catch (error: any) {
117
- errors.push(`❌ spec.json読み込みエラー: ${error.message}`);
116
+ } catch (error: unknown) {
117
+ const message = error instanceof Error ? error.message : String(error);
118
+ errors.push(`❌ spec.json読み込みエラー: ${message}`);
118
119
  return { phase: 'design', valid: false, errors, warnings };
119
120
  }
120
121
 
@@ -191,11 +192,12 @@ function validateTasks(feature: string): ValidationResult {
191
192
  }
192
193
 
193
194
  // 2. spec.json読み込み
194
- let spec: any;
195
+ let spec: SpecJson;
195
196
  try {
196
197
  spec = loadSpecJson(feature);
197
- } catch (error: any) {
198
- errors.push(`❌ spec.json読み込みエラー: ${error.message}`);
198
+ } catch (error: unknown) {
199
+ const message = error instanceof Error ? error.message : String(error);
200
+ errors.push(`❌ spec.json読み込みエラー: ${message}`);
199
201
  return { phase: 'tasks', valid: false, errors, warnings };
200
202
  }
201
203
 
@@ -214,7 +216,7 @@ function validateTasks(feature: string): ValidationResult {
214
216
  // spec.jira.storyKeys 配列をチェック(新フォーマット)
215
217
  // または spec.jira.stories.created(旧フォーマット)をチェック
216
218
  const hasStories = spec.jira?.storyKeys && Array.isArray(spec.jira.storyKeys) && spec.jira.storyKeys.length > 0;
217
- const hasLegacyStories = spec.jira?.stories && spec.jira.stories.created > 0;
219
+ const hasLegacyStories = spec.jira?.stories && spec.jira.stories.created !== undefined && spec.jira.stories.created > 0;
218
220
 
219
221
  if (!hasStories && !hasLegacyStories) {
220
222
  errors.push('❌ JIRA Storyが1つも作成されていません');
@@ -222,7 +224,7 @@ function validateTasks(feature: string): ValidationResult {
222
224
  } else if (spec.jira?.storyKeys && spec.jira.storyKeys.length > 0) {
223
225
  // 新フォーマット: storyKeys配列が存在する場合
224
226
  // 成功として扱う(警告なし)
225
- } else if (spec.jira?.stories && spec.jira.stories.created < spec.jira.stories.total) {
227
+ } else if (spec.jira?.stories && spec.jira.stories.created !== undefined && spec.jira.stories.total !== undefined && spec.jira.stories.created < spec.jira.stories.total) {
226
228
  // 旧フォーマット: 一部未作成の場合のみ警告
227
229
  warnings.push(`⚠️ JIRA Storyが一部未作成: ${spec.jira.stories.created}/${spec.jira.stories.total}`);
228
230
  }
@@ -240,59 +242,6 @@ function validateTasks(feature: string): ValidationResult {
240
242
  };
241
243
  }
242
244
 
243
- /**
244
- * テストタイプ選択フェーズのバリデーション(Phase 0.3)
245
- * マニュアル対応フェーズ - バリデーション不要(常に成功)
246
- */
247
- function validateTestTypeSelection(feature: string): ValidationResult {
248
- const errors: string[] = [];
249
- const warnings: string[] = [];
250
-
251
- // feature名のバリデーション
252
- const nameValidation = validateFeatureName(feature);
253
- if (!nameValidation.valid) {
254
- errors.push(...nameValidation.errors);
255
- }
256
-
257
- warnings.push('⚠️ このフェーズはマニュアル対応です。ガイダンスに従ってテストタイプを選択してください');
258
-
259
- return {
260
- phase: 'test-type-selection',
261
- valid: errors.length === 0,
262
- errors,
263
- warnings
264
- };
265
- }
266
-
267
- /**
268
- * テスト仕様書作成フェーズのバリデーション(Phase 0.4)
269
- * マニュアル対応フェーズ - バリデーション不要(常に成功)
270
- */
271
- function validateTestSpec(feature: string): ValidationResult {
272
- const errors: string[] = [];
273
- const warnings: string[] = [];
274
-
275
- // feature名のバリデーション
276
- const nameValidation = validateFeatureName(feature);
277
- if (!nameValidation.valid) {
278
- errors.push(...nameValidation.errors);
279
- }
280
-
281
- // テスト仕様書ディレクトリの存在チェック(任意)
282
- const testSpecDir = join(process.cwd(), 'docs', 'testing', 'specs', feature);
283
- if (!existsSync(testSpecDir)) {
284
- warnings.push('⚠️ テスト仕様書ディレクトリがありません: docs/testing/specs/' + feature);
285
- }
286
-
287
- warnings.push('⚠️ このフェーズはマニュアル対応です。テンプレートを使用してテスト仕様書を作成してください');
288
-
289
- return {
290
- phase: 'test-spec',
291
- valid: errors.length === 0,
292
- errors,
293
- warnings
294
- };
295
- }
296
245
 
297
246
  /**
298
247
  * 環境構築フェーズのバリデーション(Phase 1)
@@ -381,12 +330,6 @@ export function validatePhase(feature: string, phase: Phase): ValidationResult {
381
330
  case 'design':
382
331
  result = validateDesign(feature);
383
332
  break;
384
- case 'test-type-selection':
385
- result = validateTestTypeSelection(feature);
386
- break;
387
- case 'test-spec':
388
- result = validateTestSpec(feature);
389
- break;
390
333
  case 'tasks':
391
334
  result = validateTasks(feature);
392
335
  break;
@@ -435,12 +378,11 @@ if (import.meta.url === `file://${process.argv[1]}`) {
435
378
  console.error('\nAvailable Phases:');
436
379
  console.error(' requirements - Phase 0.1: 要件定義');
437
380
  console.error(' design - Phase 0.2: 設計');
438
- console.error(' test-type-selection- Phase 0.3: テストタイプ選択(任意)');
439
- console.error(' test-spec - Phase 0.4: テスト仕様書作成(任意)');
440
381
  console.error(' tasks - Phase 0.5-0.6: タスク分割・JIRA同期');
441
382
  console.error(' environment-setup - Phase 1: 環境構築(任意)');
442
383
  console.error(' phase-a - Phase A: PR前自動テスト(任意)');
443
384
  console.error(' phase-b - Phase B: リリース準備テスト(任意)');
385
+ console.error('\nNote: For test planning (Phase 0.3-0.4), use /michi:test-planning AI command');
444
386
  process.exit(1);
445
387
  }
446
388
 
@@ -449,8 +391,6 @@ if (import.meta.url === `file://${process.argv[1]}`) {
449
391
  const validPhases = [
450
392
  'requirements',
451
393
  'design',
452
- 'test-type-selection',
453
- 'test-spec',
454
394
  'tasks',
455
395
  'environment-setup',
456
396
  'phase-a',
@@ -459,15 +399,16 @@ if (import.meta.url === `file://${process.argv[1]}`) {
459
399
 
460
400
  if (!validPhases.includes(phase)) {
461
401
  console.error(`Invalid phase: ${phase}`);
462
- console.error('Must be one of: requirements, design, test-type-selection, test-spec, tasks, environment-setup, phase-a, phase-b');
402
+ console.error('Must be one of: requirements, design, tasks, environment-setup, phase-a, phase-b');
463
403
  process.exit(1);
464
404
  }
465
405
 
466
406
  try {
467
407
  const result = validatePhase(feature, phase as Phase);
468
408
  process.exit(result.valid ? 0 : 1);
469
- } catch (error: any) {
470
- console.error(`\n❌ Validation error: ${error.message}`);
409
+ } catch (error: unknown) {
410
+ const message = error instanceof Error ? error.message : String(error);
411
+ console.error(`\n❌ Validation error: ${message}`);
471
412
  process.exit(1);
472
413
  }
473
414
  }