@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
|
@@ -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:
|
|
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:
|
|
111
|
+
} catch (error: unknown) {
|
|
112
112
|
// エラーをログに記録(デバッグ用)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
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(`❌ エラー: ${
|
|
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:
|
|
105
|
-
|
|
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
|
-
|
|
148
|
-
|
|
169
|
+
spec.confluence.requirementsPageId = pageInfo.pageId;
|
|
170
|
+
spec.confluence.requirementsUrl = pageInfo.url;
|
|
149
171
|
} else if (docType === 'design') {
|
|
150
|
-
|
|
151
|
-
|
|
172
|
+
spec.confluence.designPageId = pageInfo.pageId;
|
|
173
|
+
spec.confluence.designUrl = pageInfo.url;
|
|
152
174
|
} else if (docType === 'tasks') {
|
|
153
|
-
|
|
154
|
-
|
|
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 (!
|
|
167
|
-
|
|
188
|
+
if (!spec.milestones.requirements) {
|
|
189
|
+
spec.milestones.requirements = {};
|
|
168
190
|
}
|
|
169
|
-
|
|
191
|
+
spec.milestones.requirements.completed = true;
|
|
170
192
|
} else if (docType === 'design') {
|
|
171
193
|
spec.milestones.designCompleted = true;
|
|
172
194
|
// 旧形式(後方互換性のため併記)
|
|
173
|
-
if (!
|
|
174
|
-
|
|
195
|
+
if (!spec.milestones.design) {
|
|
196
|
+
spec.milestones.design = {};
|
|
175
197
|
}
|
|
176
|
-
|
|
198
|
+
spec.milestones.design.completed = true;
|
|
177
199
|
} else if (docType === 'tasks') {
|
|
178
200
|
spec.milestones.tasksCompleted = true;
|
|
179
201
|
// 旧形式(後方互換性のため併記)
|
|
180
|
-
if (!
|
|
181
|
-
|
|
202
|
+
if (!spec.milestones.tasks) {
|
|
203
|
+
spec.milestones.tasks = {};
|
|
182
204
|
}
|
|
183
|
-
|
|
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
|
|
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
|
-
|
|
81
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
64
|
-
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):
|
|
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:
|
|
60
|
+
let spec: SpecJson;
|
|
62
61
|
try {
|
|
63
62
|
spec = loadSpecJson(feature);
|
|
64
|
-
} catch (error:
|
|
65
|
-
|
|
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:
|
|
113
|
+
let spec: SpecJson;
|
|
114
114
|
try {
|
|
115
115
|
spec = loadSpecJson(feature);
|
|
116
|
-
} catch (error:
|
|
117
|
-
|
|
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:
|
|
195
|
+
let spec: SpecJson;
|
|
195
196
|
try {
|
|
196
197
|
spec = loadSpecJson(feature);
|
|
197
|
-
} catch (error:
|
|
198
|
-
|
|
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,
|
|
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:
|
|
470
|
-
|
|
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
|
}
|